mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-09 10:13:18 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/TelegramUI
This commit is contained in:
commit
3d4bb0f742
@ -254,6 +254,7 @@
|
||||
D056CD781FF2A6EE00880D28 /* ChatMessageSwipeToReplyNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D056CD771FF2A6EE00880D28 /* ChatMessageSwipeToReplyNode.swift */; };
|
||||
D056CD7A1FF3CC2A00880D28 /* ListMessagePlaybackOverlayNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D056CD791FF3CC2A00880D28 /* ListMessagePlaybackOverlayNode.swift */; };
|
||||
D056CD7C1FF3E92C00880D28 /* DirectionalPanGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D056CD7B1FF3E92C00880D28 /* DirectionalPanGestureRecognizer.swift */; };
|
||||
D05B077421BFC38600B1D27C /* FFMpeg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05B077321BFC38600B1D27C /* FFMpeg.framework */; };
|
||||
D05D8B3A2192FC460064586F /* LocalizationListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05D8B392192FC460064586F /* LocalizationListController.swift */; };
|
||||
D05D8B3F2192FC6E0064586F /* LocalizationListControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05D8B3E2192FC6E0064586F /* LocalizationListControllerNode.swift */; };
|
||||
D05D8B412192FC8A0064586F /* LocalizationListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05D8B402192FC8A0064586F /* LocalizationListItem.swift */; };
|
||||
@ -702,7 +703,6 @@
|
||||
D0EC6D1A1EB9F58800EBF1C3 /* FFMpegMediaFrameSourceContextHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F69D161D6B87D30046BCD6 /* FFMpegMediaFrameSourceContextHelpers.swift */; };
|
||||
D0EC6D1B1EB9F58800EBF1C3 /* FFMpegMediaVideoFrameDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F69D871D6B87EC0046BCD6 /* FFMpegMediaVideoFrameDecoder.swift */; };
|
||||
D0EC6D1C1EB9F58800EBF1C3 /* FFMpegMediaPassthroughVideoFrameDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F69D6F1D6B87DE0046BCD6 /* FFMpegMediaPassthroughVideoFrameDecoder.swift */; };
|
||||
D0EC6D1D1EB9F58800EBF1C3 /* FFMpegPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F69D171D6B87D30046BCD6 /* FFMpegPacket.swift */; };
|
||||
D0EC6D1E1EB9F58800EBF1C3 /* MediaPlayerScrubbingNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03922A61DF70E3F000F2CE9 /* MediaPlayerScrubbingNode.swift */; };
|
||||
D0EC6D1F1EB9F58800EBF1C3 /* MediaPlayerTimeTextNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0177B7F1DFAE18500A5083A /* MediaPlayerTimeTextNode.swift */; };
|
||||
D0EC6D201EB9F58800EBF1C3 /* PeerAvatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F69CDE1D6B87D30046BCD6 /* PeerAvatar.swift */; };
|
||||
@ -1053,10 +1053,6 @@
|
||||
D0EC6E921EB9F5B200EBF1C3 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D08D452C1D5E340300A7428A /* SwiftSignalKit.framework */; };
|
||||
D0EC6E931EB9F5B200EBF1C3 /* TelegramCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D08D452D1D5E340300A7428A /* TelegramCore.framework */; };
|
||||
D0EC6E961EB9F5B300EBF1C3 /* MtProtoKitDynamic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0EC6E941EB9F5B300EBF1C3 /* MtProtoKitDynamic.framework */; };
|
||||
D0EC6E981EB9F5D000EBF1C3 /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F69EA81D6B9BCB0046BCD6 /* libavcodec.a */; };
|
||||
D0EC6E991EB9F5D000EBF1C3 /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F69EA91D6B9BCB0046BCD6 /* libavformat.a */; };
|
||||
D0EC6E9A1EB9F5D000EBF1C3 /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F69EAA1D6B9BCB0046BCD6 /* libavutil.a */; };
|
||||
D0EC6E9B1EB9F5D000EBF1C3 /* libswresample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F69EAB1D6B9BCB0046BCD6 /* libswresample.a */; };
|
||||
D0EC6E9C1EB9F5E600EBF1C3 /* libwebp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F69EA61D6B9BBC0046BCD6 /* libwebp.a */; };
|
||||
D0EC6EA21EB9FAFA00EBF1C3 /* libopus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D03B251DECB26D00220C46 /* libopus.a */; };
|
||||
D0EC6EA31EB9FB7A00EBF1C3 /* SSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D075518E1DDA4F9E0073E051 /* SSignalKit.framework */; };
|
||||
@ -1577,6 +1573,8 @@
|
||||
D05A32E91E6F143C002760B4 /* RecentSessionsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentSessionsController.swift; sourceTree = "<group>"; };
|
||||
D05A32EB1E6F1462002760B4 /* BlockedPeersController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockedPeersController.swift; sourceTree = "<group>"; };
|
||||
D05A32ED1E6F25A0002760B4 /* ItemListRecentSessionItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListRecentSessionItem.swift; sourceTree = "<group>"; };
|
||||
D05B077121BFB9F600B1D27C /* FFMpeg.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FFMpeg.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D05B077321BFC38600B1D27C /* FFMpeg.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FFMpeg.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D05B724C1E720393000BD3AD /* SelectivePrivacySettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectivePrivacySettingsController.swift; sourceTree = "<group>"; };
|
||||
D05B724F1E720597000BD3AD /* PresentationData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationData.swift; sourceTree = "<group>"; };
|
||||
D05BFB5E1EAA22F900909D38 /* PresentationResourceKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationResourceKey.swift; sourceTree = "<group>"; };
|
||||
@ -2153,7 +2151,6 @@
|
||||
D0F69CFB1D6B87D30046BCD6 /* TouchDownGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchDownGestureRecognizer.swift; sourceTree = "<group>"; };
|
||||
D0F69D021D6B87D30046BCD6 /* MediaPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPlayer.swift; sourceTree = "<group>"; };
|
||||
D0F69D161D6B87D30046BCD6 /* FFMpegMediaFrameSourceContextHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FFMpegMediaFrameSourceContextHelpers.swift; sourceTree = "<group>"; };
|
||||
D0F69D171D6B87D30046BCD6 /* FFMpegPacket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FFMpegPacket.swift; sourceTree = "<group>"; };
|
||||
D0F69D1D1D6B87D30046BCD6 /* MediaTrackDecodableFrame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaTrackDecodableFrame.swift; sourceTree = "<group>"; };
|
||||
D0F69D6F1D6B87DE0046BCD6 /* FFMpegMediaPassthroughVideoFrameDecoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FFMpegMediaPassthroughVideoFrameDecoder.swift; sourceTree = "<group>"; };
|
||||
D0F69D701D6B87DE0046BCD6 /* MediaTrackFrameBuffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaTrackFrameBuffer.swift; sourceTree = "<group>"; };
|
||||
@ -2278,6 +2275,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D05B077421BFC38600B1D27C /* FFMpeg.framework in Frameworks */,
|
||||
D045549A21B2F173007A6DD9 /* libturbojpeg.a in Frameworks */,
|
||||
091BEAB3214552D9003AEA30 /* Vision.framework in Frameworks */,
|
||||
D0C45E9F213FFAFD00988156 /* Lottie.framework in Frameworks */,
|
||||
@ -2294,10 +2292,6 @@
|
||||
D0EC6EA31EB9FB7A00EBF1C3 /* SSignalKit.framework in Frameworks */,
|
||||
D0EC6EA21EB9FAFA00EBF1C3 /* libopus.a in Frameworks */,
|
||||
D0EC6E9C1EB9F5E600EBF1C3 /* libwebp.a in Frameworks */,
|
||||
D0EC6E981EB9F5D000EBF1C3 /* libavcodec.a in Frameworks */,
|
||||
D0EC6E991EB9F5D000EBF1C3 /* libavformat.a in Frameworks */,
|
||||
D0EC6E9A1EB9F5D000EBF1C3 /* libavutil.a in Frameworks */,
|
||||
D0EC6E9B1EB9F5D000EBF1C3 /* libswresample.a in Frameworks */,
|
||||
D0EC6E961EB9F5B300EBF1C3 /* MtProtoKitDynamic.framework in Frameworks */,
|
||||
D0EC6E8F1EB9F5B200EBF1C3 /* AsyncDisplayKit.framework in Frameworks */,
|
||||
D0EC6E901EB9F5B200EBF1C3 /* Display.framework in Frameworks */,
|
||||
@ -3327,6 +3321,8 @@
|
||||
D08D45281D5E340200A7428A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D05B077321BFC38600B1D27C /* FFMpeg.framework */,
|
||||
D05B077121BFB9F600B1D27C /* FFMpeg.framework */,
|
||||
D045549921B2F173007A6DD9 /* libturbojpeg.a */,
|
||||
D0C45E9E213FFAFD00988156 /* Lottie.framework */,
|
||||
D02DADBE2138D76F00116225 /* Vision.framework */,
|
||||
@ -4165,7 +4161,6 @@
|
||||
D0F69D161D6B87D30046BCD6 /* FFMpegMediaFrameSourceContextHelpers.swift */,
|
||||
D0F69D871D6B87EC0046BCD6 /* FFMpegMediaVideoFrameDecoder.swift */,
|
||||
D0F69D6F1D6B87DE0046BCD6 /* FFMpegMediaPassthroughVideoFrameDecoder.swift */,
|
||||
D0F69D171D6B87D30046BCD6 /* FFMpegPacket.swift */,
|
||||
D03922A61DF70E3F000F2CE9 /* MediaPlayerScrubbingNode.swift */,
|
||||
D0177B7F1DFAE18500A5083A /* MediaPlayerTimeTextNode.swift */,
|
||||
);
|
||||
@ -5222,7 +5217,6 @@
|
||||
D01C06AF1FBB461E001561AB /* JoinLinkPreviewController.swift in Sources */,
|
||||
D0EC6D1C1EB9F58800EBF1C3 /* FFMpegMediaPassthroughVideoFrameDecoder.swift in Sources */,
|
||||
D0D9DE0D20EFEA2E00F20B06 /* InstantPageMediaPlaylist.swift in Sources */,
|
||||
D0EC6D1D1EB9F58800EBF1C3 /* FFMpegPacket.swift in Sources */,
|
||||
D01C06B11FBB4643001561AB /* JoinLinkPreviewControllerNode.swift in Sources */,
|
||||
D0EC6D1E1EB9F58800EBF1C3 /* MediaPlayerScrubbingNode.swift in Sources */,
|
||||
D0C0B59B1EE019E5000F4D2C /* ChatSearchNavigationContentNode.swift in Sources */,
|
||||
@ -5948,7 +5942,7 @@
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
D021D510219CB2240064BEBA /* Debug Fork */ = {
|
||||
D021D510219CB2240064BEBA /* DebugFork */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6009,9 +6003,9 @@
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = "Debug Fork";
|
||||
name = DebugFork;
|
||||
};
|
||||
D021D511219CB2240064BEBA /* Debug Fork */ = {
|
||||
D021D511219CB2240064BEBA /* DebugFork */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6025,9 +6019,9 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = "Debug Fork";
|
||||
name = DebugFork;
|
||||
};
|
||||
D021D512219CB2240064BEBA /* Debug Fork */ = {
|
||||
D021D512219CB2240064BEBA /* DebugFork */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
@ -6065,9 +6059,9 @@
|
||||
SWIFT_VERSION = 4.0;
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include";
|
||||
};
|
||||
name = "Debug Fork";
|
||||
name = DebugFork;
|
||||
};
|
||||
D0400EDB1D5B900A007931CE /* Release AppStore */ = {
|
||||
D0400EDB1D5B900A007931CE /* ReleaseAppStore */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6121,9 +6115,9 @@
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = "Release AppStore";
|
||||
name = ReleaseAppStore;
|
||||
};
|
||||
D0400EDD1D5B900A007931CE /* Release AppStore */ = {
|
||||
D0400EDD1D5B900A007931CE /* ReleaseAppStore */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6138,9 +6132,9 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = "Release AppStore";
|
||||
name = ReleaseAppStore;
|
||||
};
|
||||
D079FD261F06BEF70038FADE /* Debug AppStore */ = {
|
||||
D079FD261F06BEF70038FADE /* DebugAppStore */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6201,9 +6195,9 @@
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = "Debug AppStore";
|
||||
name = DebugAppStore;
|
||||
};
|
||||
D079FD271F06BEF70038FADE /* Debug AppStore */ = {
|
||||
D079FD271F06BEF70038FADE /* DebugAppStore */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6217,9 +6211,9 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = "Debug AppStore";
|
||||
name = DebugAppStore;
|
||||
};
|
||||
D079FD281F06BEF70038FADE /* Debug AppStore */ = {
|
||||
D079FD281F06BEF70038FADE /* DebugAppStore */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
@ -6257,9 +6251,9 @@
|
||||
SWIFT_VERSION = 4.0;
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include";
|
||||
};
|
||||
name = "Debug AppStore";
|
||||
name = DebugAppStore;
|
||||
};
|
||||
D0924FEE1FE52C29003F693F /* Release Hockeyapp Internal */ = {
|
||||
D0924FEE1FE52C29003F693F /* ReleaseHockeyappInternal */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6313,9 +6307,9 @@
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = "Release Hockeyapp Internal";
|
||||
name = ReleaseHockeyappInternal;
|
||||
};
|
||||
D0924FEF1FE52C29003F693F /* Release Hockeyapp Internal */ = {
|
||||
D0924FEF1FE52C29003F693F /* ReleaseHockeyappInternal */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6330,9 +6324,9 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = "Release Hockeyapp Internal";
|
||||
name = ReleaseHockeyappInternal;
|
||||
};
|
||||
D0924FF01FE52C29003F693F /* Release Hockeyapp Internal */ = {
|
||||
D0924FF01FE52C29003F693F /* ReleaseHockeyappInternal */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
@ -6370,9 +6364,9 @@
|
||||
SWIFT_VERSION = 4.0;
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include";
|
||||
};
|
||||
name = "Release Hockeyapp Internal";
|
||||
name = ReleaseHockeyappInternal;
|
||||
};
|
||||
D0ADF948212B3B0000310BBC /* Debug AppStore LLC */ = {
|
||||
D0ADF948212B3B0000310BBC /* DebugAppStoreLLC */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6433,9 +6427,9 @@
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = "Debug AppStore LLC";
|
||||
name = DebugAppStoreLLC;
|
||||
};
|
||||
D0ADF949212B3B0000310BBC /* Debug AppStore LLC */ = {
|
||||
D0ADF949212B3B0000310BBC /* DebugAppStoreLLC */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6449,9 +6443,9 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = "Debug AppStore LLC";
|
||||
name = DebugAppStoreLLC;
|
||||
};
|
||||
D0ADF94A212B3B0000310BBC /* Debug AppStore LLC */ = {
|
||||
D0ADF94A212B3B0000310BBC /* DebugAppStoreLLC */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
@ -6489,9 +6483,9 @@
|
||||
SWIFT_VERSION = 4.0;
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include";
|
||||
};
|
||||
name = "Debug AppStore LLC";
|
||||
name = DebugAppStoreLLC;
|
||||
};
|
||||
D0CE6F02213DC32300BCD44B /* Release AppStore LLC */ = {
|
||||
D0CE6F02213DC32300BCD44B /* ReleaseAppStoreLLC */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6545,9 +6539,9 @@
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = "Release AppStore LLC";
|
||||
name = ReleaseAppStoreLLC;
|
||||
};
|
||||
D0CE6F03213DC32300BCD44B /* Release AppStore LLC */ = {
|
||||
D0CE6F03213DC32300BCD44B /* ReleaseAppStoreLLC */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6562,9 +6556,9 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = "Release AppStore LLC";
|
||||
name = ReleaseAppStoreLLC;
|
||||
};
|
||||
D0CE6F04213DC32300BCD44B /* Release AppStore LLC */ = {
|
||||
D0CE6F04213DC32300BCD44B /* ReleaseAppStoreLLC */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
@ -6600,9 +6594,9 @@
|
||||
SWIFT_VERSION = 4.0;
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include";
|
||||
};
|
||||
name = "Release AppStore LLC";
|
||||
name = ReleaseAppStoreLLC;
|
||||
};
|
||||
D0EC6E9E1EB9F79800EBF1C3 /* Debug Hockeyapp */ = {
|
||||
D0EC6E9E1EB9F79800EBF1C3 /* DebugHockeyapp */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
@ -6640,9 +6634,9 @@
|
||||
SWIFT_VERSION = 4.0;
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include";
|
||||
};
|
||||
name = "Debug Hockeyapp";
|
||||
name = DebugHockeyapp;
|
||||
};
|
||||
D0EC6E9F1EB9F79800EBF1C3 /* Release Hockeyapp */ = {
|
||||
D0EC6E9F1EB9F79800EBF1C3 /* ReleaseHockeyapp */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
@ -6680,9 +6674,9 @@
|
||||
SWIFT_VERSION = 4.0;
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include";
|
||||
};
|
||||
name = "Release Hockeyapp";
|
||||
name = ReleaseHockeyapp;
|
||||
};
|
||||
D0EC6EA01EB9F79800EBF1C3 /* Release AppStore */ = {
|
||||
D0EC6EA01EB9F79800EBF1C3 /* ReleaseAppStore */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
@ -6718,9 +6712,9 @@
|
||||
SWIFT_VERSION = 4.0;
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include";
|
||||
};
|
||||
name = "Release AppStore";
|
||||
name = ReleaseAppStore;
|
||||
};
|
||||
D0FC40911D5B8E7500261D9D /* Debug Hockeyapp */ = {
|
||||
D0FC40911D5B8E7500261D9D /* DebugHockeyapp */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6781,9 +6775,9 @@
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = "Debug Hockeyapp";
|
||||
name = DebugHockeyapp;
|
||||
};
|
||||
D0FC40921D5B8E7500261D9D /* Release Hockeyapp */ = {
|
||||
D0FC40921D5B8E7500261D9D /* ReleaseHockeyapp */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6837,9 +6831,9 @@
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = "Release Hockeyapp";
|
||||
name = ReleaseHockeyapp;
|
||||
};
|
||||
D0FC40971D5B8E7500261D9D /* Debug Hockeyapp */ = {
|
||||
D0FC40971D5B8E7500261D9D /* DebugHockeyapp */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6853,9 +6847,9 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = "Debug Hockeyapp";
|
||||
name = DebugHockeyapp;
|
||||
};
|
||||
D0FC40981D5B8E7500261D9D /* Release Hockeyapp */ = {
|
||||
D0FC40981D5B8E7500261D9D /* ReleaseHockeyapp */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */;
|
||||
buildSettings = {
|
||||
@ -6870,7 +6864,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = "Release Hockeyapp";
|
||||
name = ReleaseHockeyapp;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
@ -6878,47 +6872,47 @@
|
||||
D0EC6EA11EB9F79800EBF1C3 /* Build configuration list for PBXNativeTarget "TelegramUI" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D0EC6E9E1EB9F79800EBF1C3 /* Debug Hockeyapp */,
|
||||
D021D512219CB2240064BEBA /* Debug Fork */,
|
||||
D079FD281F06BEF70038FADE /* Debug AppStore */,
|
||||
D0ADF94A212B3B0000310BBC /* Debug AppStore LLC */,
|
||||
D0EC6E9F1EB9F79800EBF1C3 /* Release Hockeyapp */,
|
||||
D0924FF01FE52C29003F693F /* Release Hockeyapp Internal */,
|
||||
D0EC6EA01EB9F79800EBF1C3 /* Release AppStore */,
|
||||
D0CE6F04213DC32300BCD44B /* Release AppStore LLC */,
|
||||
D0EC6E9E1EB9F79800EBF1C3 /* DebugHockeyapp */,
|
||||
D021D512219CB2240064BEBA /* DebugFork */,
|
||||
D079FD281F06BEF70038FADE /* DebugAppStore */,
|
||||
D0ADF94A212B3B0000310BBC /* DebugAppStoreLLC */,
|
||||
D0EC6E9F1EB9F79800EBF1C3 /* ReleaseHockeyapp */,
|
||||
D0924FF01FE52C29003F693F /* ReleaseHockeyappInternal */,
|
||||
D0EC6EA01EB9F79800EBF1C3 /* ReleaseAppStore */,
|
||||
D0CE6F04213DC32300BCD44B /* ReleaseAppStoreLLC */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = "Release Hockeyapp";
|
||||
defaultConfigurationName = ReleaseHockeyapp;
|
||||
};
|
||||
D0FC40791D5B8E7400261D9D /* Build configuration list for PBXProject "TelegramUI" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D0FC40911D5B8E7500261D9D /* Debug Hockeyapp */,
|
||||
D021D510219CB2240064BEBA /* Debug Fork */,
|
||||
D079FD261F06BEF70038FADE /* Debug AppStore */,
|
||||
D0ADF948212B3B0000310BBC /* Debug AppStore LLC */,
|
||||
D0FC40921D5B8E7500261D9D /* Release Hockeyapp */,
|
||||
D0924FEE1FE52C29003F693F /* Release Hockeyapp Internal */,
|
||||
D0400EDB1D5B900A007931CE /* Release AppStore */,
|
||||
D0CE6F02213DC32300BCD44B /* Release AppStore LLC */,
|
||||
D0FC40911D5B8E7500261D9D /* DebugHockeyapp */,
|
||||
D021D510219CB2240064BEBA /* DebugFork */,
|
||||
D079FD261F06BEF70038FADE /* DebugAppStore */,
|
||||
D0ADF948212B3B0000310BBC /* DebugAppStoreLLC */,
|
||||
D0FC40921D5B8E7500261D9D /* ReleaseHockeyapp */,
|
||||
D0924FEE1FE52C29003F693F /* ReleaseHockeyappInternal */,
|
||||
D0400EDB1D5B900A007931CE /* ReleaseAppStore */,
|
||||
D0CE6F02213DC32300BCD44B /* ReleaseAppStoreLLC */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = "Release Hockeyapp";
|
||||
defaultConfigurationName = ReleaseHockeyapp;
|
||||
};
|
||||
D0FC40961D5B8E7500261D9D /* Build configuration list for PBXNativeTarget "TelegramUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D0FC40971D5B8E7500261D9D /* Debug Hockeyapp */,
|
||||
D021D511219CB2240064BEBA /* Debug Fork */,
|
||||
D079FD271F06BEF70038FADE /* Debug AppStore */,
|
||||
D0ADF949212B3B0000310BBC /* Debug AppStore LLC */,
|
||||
D0FC40981D5B8E7500261D9D /* Release Hockeyapp */,
|
||||
D0924FEF1FE52C29003F693F /* Release Hockeyapp Internal */,
|
||||
D0400EDD1D5B900A007931CE /* Release AppStore */,
|
||||
D0CE6F03213DC32300BCD44B /* Release AppStore LLC */,
|
||||
D0FC40971D5B8E7500261D9D /* DebugHockeyapp */,
|
||||
D021D511219CB2240064BEBA /* DebugFork */,
|
||||
D079FD271F06BEF70038FADE /* DebugAppStore */,
|
||||
D0ADF949212B3B0000310BBC /* DebugAppStoreLLC */,
|
||||
D0FC40981D5B8E7500261D9D /* ReleaseHockeyapp */,
|
||||
D0924FEF1FE52C29003F693F /* ReleaseHockeyappInternal */,
|
||||
D0400EDD1D5B900A007931CE /* ReleaseAppStore */,
|
||||
D0CE6F03213DC32300BCD44B /* ReleaseAppStoreLLC */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = "Release Hockeyapp";
|
||||
defaultConfigurationName = ReleaseHockeyapp;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
|
||||
@ -245,9 +245,17 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.layoutArguments = (layout, navigationBarHeight)
|
||||
|
||||
var insets = layout.insets(options: [.input])
|
||||
var insets = layout.insets(options: [])
|
||||
insets.top = navigationBarHeight
|
||||
|
||||
if let inputHeight = layout.inputHeight {
|
||||
if inputHeight.isEqual(to: layout.standardInputHeight - 44.0) {
|
||||
insets.bottom += layout.standardInputHeight
|
||||
} else {
|
||||
insets.bottom += inputHeight
|
||||
}
|
||||
}
|
||||
|
||||
if max(layout.size.width, layout.size.height) > 1023.0 {
|
||||
if let codeType = self.codeType, case .otherSession = codeType {
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_CheckOtherSessionMessages, font: Font.medium(32.0), textColor: self.theme.primaryColor)
|
||||
|
||||
@ -34,8 +34,23 @@ public final class AuthorizationSequenceController: NavigationController {
|
||||
super.init(mode: .single, theme: NavigationControllerTheme(navigationBar: AuthorizationSequenceController.navigationBarTheme(theme), emptyAreaColor: .black, emptyDetailIcon: nil))
|
||||
|
||||
self.stateDisposable = (account.postbox.stateView()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] view in
|
||||
self?.updateState(state: view.state ?? UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty))
|
||||
|> filter { view in
|
||||
if view.state is UnauthorizedAccountState || view.state == nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> map { view -> UnauthorizedAccountStateContents in
|
||||
if let state = view.state as? UnauthorizedAccountState {
|
||||
return state.contents
|
||||
} else {
|
||||
return .empty
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
self?.updateState(state: state)
|
||||
})
|
||||
}
|
||||
|
||||
@ -145,7 +160,7 @@ public final class AuthorizationSequenceController: NavigationController {
|
||||
}
|
||||
}
|
||||
}
|
||||
controller.updateData(countryCode: countryCode, number: number)
|
||||
controller.updateData(countryCode: countryCode, countryName: nil, number: number)
|
||||
return controller
|
||||
}
|
||||
|
||||
@ -606,28 +621,25 @@ public final class AuthorizationSequenceController: NavigationController {
|
||||
return controller
|
||||
}
|
||||
|
||||
private func updateState(state: PostboxCoding?) {
|
||||
if let state = state as? UnauthorizedAccountState {
|
||||
switch state.contents {
|
||||
case .empty:
|
||||
if let _ = self.viewControllers.last as? AuthorizationSequenceSplashController {
|
||||
} else {
|
||||
self.setViewControllers([self.splashController()], animated: !self.viewControllers.isEmpty)
|
||||
}
|
||||
case let .phoneEntry(countryCode, number):
|
||||
self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: countryCode, number: number)], animated: !self.viewControllers.isEmpty)
|
||||
case let .confirmationCodeEntry(number, type, _, timeout, nextType, termsOfService):
|
||||
self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: defaultCountryCode(), number: ""), self.codeEntryController(number: number, type: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty)
|
||||
case let .passwordEntry(hint, _, _):
|
||||
self.setViewControllers([self.splashController(), self.passwordEntryController(hint: hint)], animated: !self.viewControllers.isEmpty)
|
||||
case let .passwordRecovery(_, _, _, emailPattern):
|
||||
self.setViewControllers([self.splashController(), self.passwordRecoveryController(emailPattern: emailPattern)], animated: !self.viewControllers.isEmpty)
|
||||
case let .awaitingAccountReset(protectedUntil, number):
|
||||
self.setViewControllers([self.splashController(), self.awaitingAccountResetController(protectedUntil: protectedUntil, number: number)], animated: !self.viewControllers.isEmpty)
|
||||
case let .signUp(_, _, _, firstName, lastName, termsOfService):
|
||||
self.setViewControllers([self.splashController(), self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty)
|
||||
}
|
||||
} else if let _ = state as? AuthorizedAccountState {
|
||||
private func updateState(state: UnauthorizedAccountStateContents) {
|
||||
switch state {
|
||||
case .empty:
|
||||
if let _ = self.viewControllers.last as? AuthorizationSequenceSplashController {
|
||||
} else {
|
||||
self.setViewControllers([self.splashController()], animated: !self.viewControllers.isEmpty)
|
||||
}
|
||||
case let .phoneEntry(countryCode, number):
|
||||
self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: countryCode, number: number)], animated: !self.viewControllers.isEmpty)
|
||||
case let .confirmationCodeEntry(number, type, _, timeout, nextType, termsOfService):
|
||||
self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: defaultCountryCode(), number: ""), self.codeEntryController(number: number, type: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty)
|
||||
case let .passwordEntry(hint, _, _):
|
||||
self.setViewControllers([self.splashController(), self.passwordEntryController(hint: hint)], animated: !self.viewControllers.isEmpty)
|
||||
case let .passwordRecovery(_, _, _, emailPattern):
|
||||
self.setViewControllers([self.splashController(), self.passwordRecoveryController(emailPattern: emailPattern)], animated: !self.viewControllers.isEmpty)
|
||||
case let .awaitingAccountReset(protectedUntil, number):
|
||||
self.setViewControllers([self.splashController(), self.awaitingAccountResetController(protectedUntil: protectedUntil, number: number)], animated: !self.viewControllers.isEmpty)
|
||||
case let .signUp(_, _, _, firstName, lastName, termsOfService):
|
||||
self.setViewControllers([self.splashController(), self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
private let theme: AuthorizationTheme
|
||||
private let openUrl: (String) -> Void
|
||||
|
||||
private var currentData: (Int32, String)?
|
||||
private var currentData: (Int32, String?, String)?
|
||||
|
||||
var inProgress: Bool = false {
|
||||
didSet {
|
||||
@ -58,25 +58,25 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
self.termsDisposable.dispose()
|
||||
}
|
||||
|
||||
func updateData(countryCode: Int32, number: String) {
|
||||
self.currentData = (countryCode, number)
|
||||
func updateData(countryCode: Int32, countryName: String?, number: String) {
|
||||
self.currentData = (countryCode, countryName, number)
|
||||
if self.isNodeLoaded {
|
||||
self.controllerNode.codeAndNumber = (countryCode, number)
|
||||
self.controllerNode.codeAndNumber = (countryCode, countryName, number)
|
||||
}
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = AuthorizationSequencePhoneEntryControllerNode(strings: self.strings, theme: self.theme)
|
||||
if let (code, number) = self.currentData {
|
||||
self.controllerNode.codeAndNumber = (code, number)
|
||||
if let (code, name, number) = self.currentData {
|
||||
self.controllerNode.codeAndNumber = (code, name, number)
|
||||
}
|
||||
self.displayNodeDidLoad()
|
||||
self.controllerNode.selectCountryCode = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let controller = AuthorizationSequenceCountrySelectionController(strings: strongSelf.strings, theme: AuthorizationSequenceCountrySelectionTheme(authorizationTheme: strongSelf.theme))
|
||||
controller.completeWithCountryCode = { code, _ in
|
||||
controller.completeWithCountryCode = { code, name in
|
||||
if let strongSelf = self, let currentData = strongSelf.currentData {
|
||||
strongSelf.updateData(countryCode: Int32(code), number: currentData.1)
|
||||
strongSelf.updateData(countryCode: Int32(code), countryName: name, number: currentData.2)
|
||||
strongSelf.controllerNode.activateInput()
|
||||
}
|
||||
}
|
||||
@ -111,7 +111,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
}
|
||||
|
||||
@objc func nextPressed() {
|
||||
let (_, number) = self.controllerNode.codeAndNumber
|
||||
let (_, _, number) = self.controllerNode.codeAndNumber
|
||||
if !number.isEmpty {
|
||||
self.loginWithNumber?(self.controllerNode.currentNumber)
|
||||
} else {
|
||||
|
||||
@ -117,9 +117,13 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
||||
|
||||
self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.phoneInputNode.countryCodeUpdated = { [weak self] code in
|
||||
self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in
|
||||
if let strongSelf = self {
|
||||
if let code = Int(code), let (countryId, countryName) = countryCodeToIdAndName[code] {
|
||||
if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
|
||||
let flagString = emojiFlagForISOCountryCode(name as NSString)
|
||||
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.strings) ?? countryName
|
||||
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.primaryColor, for: [])
|
||||
} else if let code = Int(code), let (countryId, countryName) = countryCodeToIdAndName[code] {
|
||||
let flagString = emojiFlagForISOCountryCode(countryId as NSString)
|
||||
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(countryId, strings: strongSelf.strings) ?? countryName
|
||||
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.primaryColor, for: [])
|
||||
@ -171,7 +175,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
return self.phoneAndCountryNode.phoneInputNode.number
|
||||
}
|
||||
|
||||
var codeAndNumber: (Int32?, String) {
|
||||
var codeAndNumber: (Int32?, String?, String) {
|
||||
get {
|
||||
return self.phoneAndCountryNode.phoneInputNode.codeAndNumber
|
||||
} set(value) {
|
||||
@ -246,7 +250,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
self.termsOfServiceNode.tapAttributeAction = { [weak self] attributes in
|
||||
self.termsOfServiceNode.tapAttributeAction = { attributes in
|
||||
if let _ = attributes[NSAttributedStringKey(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||
}
|
||||
}
|
||||
@ -254,9 +258,17 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
var insets = layout.insets(options: [.input])
|
||||
var insets = layout.insets(options: [])
|
||||
insets.top = navigationBarHeight
|
||||
|
||||
if let inputHeight = layout.inputHeight {
|
||||
if inputHeight.isEqual(to: layout.standardInputHeight - 44.0) {
|
||||
insets.bottom += layout.standardInputHeight
|
||||
} else {
|
||||
insets.bottom += inputHeight
|
||||
}
|
||||
}
|
||||
|
||||
if max(layout.size.width, layout.size.height) > 1023.0 {
|
||||
self.titleNode.attributedText = NSAttributedString(string: strings.Login_PhoneTitle, font: Font.light(40.0), textColor: self.theme.primaryColor)
|
||||
} else {
|
||||
@ -265,13 +277,11 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
|
||||
let titleSize = self.titleNode.measure(CGSize(width: layout.size.width, height: CGFloat.greatestFiniteMagnitude))
|
||||
let noticeSize = self.noticeNode.measure(CGSize(width: min(274.0, layout.size.width - 28.0), height: CGFloat.greatestFiniteMagnitude))
|
||||
let termsOfServiceSize = self.termsOfServiceNode.updateLayout(CGSize(width: layout.size.width, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
var items: [AuthorizationLayoutItem] = [
|
||||
AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)),
|
||||
AuthorizationLayoutItem(node: self.noticeNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)),
|
||||
AuthorizationLayoutItem(node: self.phoneAndCountryNode, size: CGSize(width: layout.size.width, height: 115.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 44.0, maxValue: 44.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)),
|
||||
//AuthorizationLayoutItem(node: self.termsOfServiceNode, size: termsOfServiceSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 90.0, maxValue: 90.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)),
|
||||
AuthorizationLayoutItem(node: self.phoneAndCountryNode, size: CGSize(width: layout.size.width, height: 115.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 44.0, maxValue: 44.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))
|
||||
]
|
||||
|
||||
if layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 10.0)), items: items, transition: transition, failIfDoesNotFit: true) {
|
||||
|
||||
@ -33,7 +33,7 @@ private enum BlockedPeersEntryStableId: Hashable {
|
||||
|
||||
private enum BlockedPeersEntry: ItemListNodeEntry {
|
||||
case add(PresentationTheme, String)
|
||||
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, ItemListPeerItemEditing, Bool)
|
||||
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, ItemListPeerItemEditing, Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -48,7 +48,7 @@ private enum BlockedPeersEntry: ItemListNodeEntry {
|
||||
switch self {
|
||||
case .add:
|
||||
return .add
|
||||
case let .peerItem(_, _, _, _, peer, _, _):
|
||||
case let .peerItem(_, _, _, _, _, peer, _, _):
|
||||
return .peer(peer.id)
|
||||
}
|
||||
}
|
||||
@ -61,8 +61,8 @@ private enum BlockedPeersEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsEditing, lhsEnabled):
|
||||
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsEditing, rhsEnabled) = rhs {
|
||||
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer, lhsEditing, lhsEnabled):
|
||||
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer, rhsEditing, rhsEnabled) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -75,6 +75,9 @@ private enum BlockedPeersEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameOrder != rhsNameOrder {
|
||||
return false
|
||||
}
|
||||
if !lhsPeer.isEqual(rhsPeer) {
|
||||
return false
|
||||
}
|
||||
@ -99,11 +102,11 @@ private enum BlockedPeersEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peerItem(index, _, _, _, _, _, _):
|
||||
case let .peerItem(index, _, _, _, _, _, _, _):
|
||||
switch rhs {
|
||||
case .add:
|
||||
return false
|
||||
case let .peerItem(rhsIndex, _, _, _, _, _, _):
|
||||
case let .peerItem(rhsIndex, _, _, _, _, _, _, _):
|
||||
return index < rhsIndex
|
||||
}
|
||||
}
|
||||
@ -115,8 +118,8 @@ private enum BlockedPeersEntry: ItemListNodeEntry {
|
||||
return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, sectionId: self.section, editing: false, action: {
|
||||
arguments.addPeer()
|
||||
})
|
||||
case let .peerItem(_, theme, strings, dateTimeFormat, peer, editing, enabled):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: {
|
||||
case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, editing, enabled):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: {
|
||||
arguments.openPeer(peer)
|
||||
}, setPeerIdWithRevealedOptions: { previousId, id in
|
||||
arguments.setPeerIdWithRevealedOptions(previousId, id)
|
||||
@ -179,7 +182,7 @@ private func blockedPeersControllerEntries(presentationData: PresentationData, s
|
||||
|
||||
var index: Int32 = 0
|
||||
for peer in peers {
|
||||
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != peer.id))
|
||||
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != peer.id))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,7 +146,7 @@ public final class CallController: ViewController {
|
||||
})
|
||||
])
|
||||
])
|
||||
strongSelf.present(actionSheet, in: .window(.root))
|
||||
strongSelf.present(actionSheet, in: .window(.calls))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -203,6 +203,7 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true)
|
||||
|
||||
self.listNode = ListView()
|
||||
self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
|
||||
|
||||
self.emptyTextNode = ASTextNode()
|
||||
self.emptyTextNode.alpha = 0.0
|
||||
|
||||
@ -11,7 +11,7 @@ final class ChangePhoneNumberController: ViewController {
|
||||
|
||||
private let account: Account
|
||||
|
||||
private var currentData: (Int32, String)?
|
||||
private var currentData: (Int32, String?, String)?
|
||||
private let requestDisposable = MetaDisposable()
|
||||
|
||||
var inProgress: Bool = false {
|
||||
@ -52,11 +52,11 @@ final class ChangePhoneNumberController: ViewController {
|
||||
self.requestDisposable.dispose()
|
||||
}
|
||||
|
||||
func updateData(countryCode: Int32, number: String) {
|
||||
if self.currentData == nil || self.currentData! != (countryCode, number) {
|
||||
self.currentData = (countryCode, number)
|
||||
func updateData(countryCode: Int32, countryName: String, number: String) {
|
||||
if self.currentData == nil || self.currentData! != (countryCode, countryName, number) {
|
||||
self.currentData = (countryCode, countryName, number)
|
||||
if self.isNodeLoaded {
|
||||
self.controllerNode.codeAndNumber = (countryCode, number)
|
||||
self.controllerNode.codeAndNumber = (countryCode, countryName, number)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,9 +67,9 @@ final class ChangePhoneNumberController: ViewController {
|
||||
self.controllerNode.selectCountryCode = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let controller = AuthorizationSequenceCountrySelectionController(strings: strongSelf.presentationData.strings, theme: AuthorizationSequenceCountrySelectionTheme(presentationTheme: strongSelf.presentationData.theme))
|
||||
controller.completeWithCountryCode = { code, _ in
|
||||
controller.completeWithCountryCode = { code, name in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateData(countryCode: Int32(code), number: strongSelf.controllerNode.codeAndNumber.1)
|
||||
strongSelf.updateData(countryCode: Int32(code), countryName: name, number: strongSelf.controllerNode.codeAndNumber.2)
|
||||
strongSelf.controllerNode.activateInput()
|
||||
}
|
||||
}
|
||||
@ -98,7 +98,7 @@ final class ChangePhoneNumberController: ViewController {
|
||||
}
|
||||
|
||||
@objc func nextPressed() {
|
||||
let (code, number) = self.controllerNode.codeAndNumber
|
||||
let (code, _, number) = self.controllerNode.codeAndNumber
|
||||
var phoneNumber = number
|
||||
if let code = code {
|
||||
phoneNumber = "\(code)\(phoneNumber)"
|
||||
|
||||
@ -78,7 +78,7 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode {
|
||||
return self.phoneInputNode.number
|
||||
}
|
||||
|
||||
var codeAndNumber: (Int32?, String) {
|
||||
var codeAndNumber: (Int32?, String?, String) {
|
||||
get {
|
||||
return self.phoneInputNode.codeAndNumber
|
||||
} set(value) {
|
||||
@ -148,9 +148,12 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode {
|
||||
|
||||
self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.phoneInputNode.countryCodeUpdated = { [weak self] code in
|
||||
self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in
|
||||
if let strongSelf = self {
|
||||
if let code = Int(code), let (_, countryName) = countryCodeToIdAndName[code] {
|
||||
if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
|
||||
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.presentationData.strings) ?? countryName
|
||||
strongSelf.countryButton.setTitle(localizedName, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: [])
|
||||
} else if let code = Int(code), let (_, countryName) = countryCodeToIdAndName[code] {
|
||||
strongSelf.countryButton.setTitle(countryName, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: [])
|
||||
} else {
|
||||
strongSelf.countryButton.setTitle(strongSelf.presentationData.strings.Login_CountryCode, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: [])
|
||||
|
||||
@ -67,7 +67,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
||||
case administrationInfo(PresentationTheme, String)
|
||||
|
||||
case adminsHeader(PresentationTheme, String)
|
||||
case adminPeerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Bool, Int32, RenderedChannelParticipant, ItemListPeerItemEditing, Bool)
|
||||
case adminPeerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Bool, Int32, RenderedChannelParticipant, ItemListPeerItemEditing, Bool)
|
||||
case addAdmin(PresentationTheme, String, Bool)
|
||||
case adminsInfo(PresentationTheme, String)
|
||||
|
||||
@ -94,7 +94,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
||||
return .index(4)
|
||||
case .adminsInfo:
|
||||
return .index(5)
|
||||
case let .adminPeerItem(_, _, _, _, _, participant, _, _):
|
||||
case let .adminPeerItem(_, _, _, _, _, _, participant, _, _):
|
||||
return .peer(participant.peer.id)
|
||||
}
|
||||
}
|
||||
@ -125,8 +125,8 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .adminPeerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsIsGroup, lhsIndex, lhsParticipant, lhsEditing, lhsEnabled):
|
||||
if case let .adminPeerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsIsGroup, rhsIndex, rhsParticipant, rhsEditing, rhsEnabled) = rhs {
|
||||
case let .adminPeerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsIsGroup, lhsIndex, lhsParticipant, lhsEditing, lhsEnabled):
|
||||
if case let .adminPeerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsIsGroup, rhsIndex, rhsParticipant, rhsEditing, rhsEnabled) = rhs {
|
||||
if lhsTheme !== rhsTheme {
|
||||
return false
|
||||
}
|
||||
@ -136,6 +136,9 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameOrder != rhsNameOrder {
|
||||
return false
|
||||
}
|
||||
if lhsIsGroup != rhsIsGroup {
|
||||
return false
|
||||
}
|
||||
@ -195,11 +198,11 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case let .adminPeerItem(_, _, _, _, index, _, _, _):
|
||||
case let .adminPeerItem(_, _, _, _, _, index, _, _, _):
|
||||
switch rhs {
|
||||
case .recentActions, .administrationType, .administrationInfo, .adminsHeader, .addAdmin:
|
||||
return false
|
||||
case let .adminPeerItem(_, _, _, _, rhsIndex, _, _, _):
|
||||
case let .adminPeerItem(_, _, _, _, _, rhsIndex, _, _, _):
|
||||
return index < rhsIndex
|
||||
default:
|
||||
return true
|
||||
@ -230,7 +233,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
case let .adminsHeader(theme, title):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: title, sectionId: self.section)
|
||||
case let .adminPeerItem(theme, strings, dateTimeFormat, isGroup, _, participant, editing, enabled):
|
||||
case let .adminPeerItem(theme, strings, dateTimeFormat, nameDisplayOrder, _, _, participant, editing, enabled):
|
||||
let peerText: String
|
||||
let action: (() -> Void)?
|
||||
switch participant.participant {
|
||||
@ -251,7 +254,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
||||
arguments.openAdmin(participant.participant)
|
||||
}
|
||||
}
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: participant.peer, presence: nil, text: .text(peerText), label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: action, setPeerIdWithRevealedOptions: { previousId, id in
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: participant.peer, presence: nil, text: .text(peerText), label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: action, setPeerIdWithRevealedOptions: { previousId, id in
|
||||
arguments.setPeerIdWithRevealedOptions(previousId, id)
|
||||
}, removePeer: { peerId in
|
||||
arguments.removeAdmin(peerId)
|
||||
@ -356,6 +359,10 @@ private struct ChannelAdminsControllerState: Equatable {
|
||||
}
|
||||
|
||||
private func channelAdminsControllerEntries(presentationData: PresentationData, accountPeerId: PeerId, view: PeerView, state: ChannelAdminsControllerState, participants: [RenderedChannelParticipant]?) -> [ChannelAdminsEntry] {
|
||||
if participants == nil || participants?.count == nil {
|
||||
return []
|
||||
}
|
||||
|
||||
var entries: [ChannelAdminsEntry] = []
|
||||
|
||||
if let peer = view.peers[view.peerId] as? TelegramChannel {
|
||||
@ -450,7 +457,7 @@ private func channelAdminsControllerEntries(presentationData: PresentationData,
|
||||
editable = false
|
||||
}
|
||||
}
|
||||
entries.append(.adminPeerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, isGroup, index, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), existingParticipantIds.contains(participant.peer.id)))
|
||||
entries.append(.adminPeerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, isGroup, index, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), existingParticipantIds.contains(participant.peer.id)))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
@ -473,7 +480,7 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon
|
||||
}
|
||||
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
|
||||
let actionsDisposable = DisposableSet()
|
||||
|
||||
@ -669,11 +676,18 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon
|
||||
presentControllerImpl?(channelAdminController(account: account, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in
|
||||
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
}, present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
})
|
||||
}
|
||||
|
||||
var emptyStateItem: ItemListControllerEmptyStateItem?
|
||||
if admins == nil || admins?.count == 0 {
|
||||
emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(isGroup ? presentationData.strings.ChatAdmins_Title : presentationData.strings.Channel_Management_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||
let listState = ItemListNodeState(entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: account.peerId, view: view, state: state, participants: admins), style: .blocks, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count)
|
||||
let listState = ItemListNodeState(entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: account.peerId, view: view, state: state, participants: admins), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
} |> afterDisposed {
|
||||
|
||||
@ -42,7 +42,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
|
||||
case add(PresentationTheme, String)
|
||||
case restrictedHeader(PresentationTheme, String)
|
||||
case bannedHeader(PresentationTheme, String)
|
||||
case peerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Int32, ChannelBlacklistPeerCategory, RenderedChannelParticipant, ItemListPeerItemEditing, Bool, Bool)
|
||||
case peerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Int32, ChannelBlacklistPeerCategory, RenderedChannelParticipant, ItemListPeerItemEditing, Bool, Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -52,7 +52,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
|
||||
return ChannelBlacklistSection.restricted.rawValue
|
||||
case .bannedHeader:
|
||||
return ChannelBlacklistSection.banned.rawValue
|
||||
case let .peerItem(_, _, _, _, category, _, _, _, _):
|
||||
case let .peerItem(_, _, _, _, _, category, _, _, _, _):
|
||||
switch category {
|
||||
case .restricted:
|
||||
return ChannelBlacklistSection.restricted.rawValue
|
||||
@ -70,7 +70,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
|
||||
return .index(1)
|
||||
case .bannedHeader:
|
||||
return .index(2)
|
||||
case let .peerItem(_, _, _, _, _, participant, _, _, _):
|
||||
case let .peerItem(_, _, _, _, _, _, participant, _, _, _):
|
||||
return .peer(participant.peer.id)
|
||||
}
|
||||
}
|
||||
@ -95,8 +95,8 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsIndex, lhsCategory, lhsParticipant, lhsEditing, lhsEnabled, lhsCanOpen):
|
||||
if case let .peerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsIndex, rhsCategory, rhsParticipant, rhsEditing, rhsEnabled, rhsCanOpen) = rhs {
|
||||
case let .peerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsIndex, lhsCategory, lhsParticipant, lhsEditing, lhsEnabled, lhsCanOpen):
|
||||
if case let .peerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsIndex, rhsCategory, rhsParticipant, rhsEditing, rhsEnabled, rhsCanOpen) = rhs {
|
||||
if lhsTheme !== rhsTheme {
|
||||
return false
|
||||
}
|
||||
@ -106,6 +106,9 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameOrder != rhsNameOrder {
|
||||
return false
|
||||
}
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -151,7 +154,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
|
||||
switch rhs {
|
||||
case .add, .restrictedHeader, .bannedHeader:
|
||||
return false
|
||||
case let .peerItem(_, _, _, _, category, _, _, _, _):
|
||||
case let .peerItem(_, _, _, _, _, category, _, _, _, _):
|
||||
switch category {
|
||||
case .restricted:
|
||||
return false
|
||||
@ -159,7 +162,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case let .peerItem(_, _, _, index, category, _, _, _, _):
|
||||
case let .peerItem(_, _, _, _, index, category, _, _, _, _):
|
||||
switch rhs {
|
||||
case .add, .restrictedHeader:
|
||||
return false
|
||||
@ -170,7 +173,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
|
||||
case .banned:
|
||||
return false
|
||||
}
|
||||
case let .peerItem(_, _, _, rhsIndex, _, _, _, _, _):
|
||||
case let .peerItem(_, _, _, _, rhsIndex, _, _, _, _, _):
|
||||
return index < rhsIndex
|
||||
}
|
||||
}
|
||||
@ -186,7 +189,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .bannedHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .peerItem(theme, strings, dateTimeFormat, _, _, participant, editing, enabled, canOpen):
|
||||
case let .peerItem(theme, strings, dateTimeFormat, nameDisplayOrder, _, _, participant, editing, enabled, canOpen):
|
||||
var text: ItemListPeerItemText = .none
|
||||
switch participant.participant {
|
||||
case let .member(_, _, _, banInfo):
|
||||
@ -196,7 +199,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
|
||||
default:
|
||||
break
|
||||
}
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: participant.peer, presence: nil, text: text, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: canOpen ? {
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: participant.peer, presence: nil, text: text, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: canOpen ? {
|
||||
arguments.openPeer(participant.participant)
|
||||
} : {
|
||||
arguments.openPeerInfo(participant.peer)
|
||||
@ -285,7 +288,7 @@ private func channelBlacklistControllerEntries(presentationData: PresentationDat
|
||||
if case .creator = participant.participant {
|
||||
editable = false
|
||||
}
|
||||
entries.append(.peerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, index, .restricted, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, canOpen))
|
||||
entries.append(.peerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index, .restricted, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, canOpen))
|
||||
index += 1
|
||||
}
|
||||
if !blacklist.banned.isEmpty {
|
||||
@ -296,7 +299,7 @@ private func channelBlacklistControllerEntries(presentationData: PresentationDat
|
||||
if case .creator = participant.participant {
|
||||
editable = false
|
||||
}
|
||||
entries.append(.peerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, index, .banned, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, canOpen))
|
||||
entries.append(.peerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index, .banned, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, canOpen))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
@ -311,7 +314,7 @@ public func channelBlacklistController(account: Account, peerId: PeerId) -> View
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
|
||||
let actionsDisposable = DisposableSet()
|
||||
@ -477,6 +480,8 @@ public func channelBlacklistController(account: Account, peerId: PeerId) -> View
|
||||
arguments.openPeerInfo(rendered.peer)
|
||||
}
|
||||
}
|
||||
}, present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -379,9 +379,29 @@ private final class ChannelMemberSingleCategoryListContext: ChannelMemberCategor
|
||||
}
|
||||
}
|
||||
case let .recentSearch(query):
|
||||
break
|
||||
default:
|
||||
break
|
||||
if let updated = updated, isParticipantMember(updated.participant), updated.peer.indexName.matchesByTokens(query) {
|
||||
var found = false
|
||||
loop: for i in 0 ..< list.count {
|
||||
if list[i].peer.id == updated.peer.id {
|
||||
list[i] = updated
|
||||
found = true
|
||||
updatedList = true
|
||||
break loop
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
list.insert(updated, at: 0)
|
||||
updatedList = true
|
||||
}
|
||||
} else if let previous = previous, isParticipantMember(previous) {
|
||||
loop: for i in 0 ..< list.count {
|
||||
if list[i].peer.id == previous.peerId {
|
||||
list.remove(at: i)
|
||||
updatedList = true
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if updatedList {
|
||||
|
||||
@ -62,7 +62,7 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
||||
case addMember(PresentationTheme, String)
|
||||
case addMemberInfo(PresentationTheme, String)
|
||||
case inviteLink(PresentationTheme, String)
|
||||
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, RenderedChannelParticipant, ItemListPeerItemEditing, Bool)
|
||||
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, RenderedChannelParticipant, ItemListPeerItemEditing, Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -81,7 +81,7 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
||||
return .index(1)
|
||||
case .inviteLink:
|
||||
return .index(2)
|
||||
case let .peerItem(_, _, _, _, participant, _, _):
|
||||
case let .peerItem(_, _, _, _, _, participant, _, _):
|
||||
return .peer(participant.peer.id)
|
||||
}
|
||||
}
|
||||
@ -106,8 +106,8 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsParticipant, lhsEditing, lhsEnabled):
|
||||
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsParticipant, rhsEditing, rhsEnabled) = rhs {
|
||||
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsParticipant, lhsEditing, lhsEnabled):
|
||||
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsParticipant, rhsEditing, rhsEnabled) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -120,6 +120,9 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameOrder != rhsNameOrder {
|
||||
return false
|
||||
}
|
||||
if lhsParticipant != rhsParticipant {
|
||||
return false
|
||||
}
|
||||
@ -155,9 +158,9 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
||||
return true
|
||||
}
|
||||
|
||||
case let .peerItem(index, _, _, _, _, _, _):
|
||||
case let .peerItem(index, _, _, _, _, _, _, _):
|
||||
switch rhs {
|
||||
case let .peerItem(rhsIndex, _, _, _, _, _, _):
|
||||
case let .peerItem(rhsIndex, _, _, _, _, _, _, _):
|
||||
return index < rhsIndex
|
||||
case .addMember, .addMemberInfo, .inviteLink:
|
||||
return false
|
||||
@ -177,9 +180,8 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
||||
})
|
||||
case let .addMemberInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
case let .peerItem(_, theme, strings, dateTimeFormat, participant, editing, enabled):
|
||||
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: participant.peer, presence: participant.presences[participant.peer.id], text: .presence, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: {
|
||||
case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, participant, editing, enabled):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: participant.peer, presence: participant.presences[participant.peer.id], text: .presence, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: {
|
||||
arguments.openPeer(participant.peer)
|
||||
}, setPeerIdWithRevealedOptions: { previousId, id in
|
||||
arguments.setPeerIdWithRevealedOptions(previousId, id)
|
||||
@ -244,6 +246,10 @@ private struct ChannelMembersControllerState: Equatable {
|
||||
}
|
||||
|
||||
private func ChannelMembersControllerEntries(account: Account, presentationData: PresentationData, view: PeerView, state: ChannelMembersControllerState, participants: [RenderedChannelParticipant]?) -> [ChannelMembersEntry] {
|
||||
if participants == nil || participants?.count == nil {
|
||||
return []
|
||||
}
|
||||
|
||||
var entries: [ChannelMembersEntry] = []
|
||||
|
||||
if let participants = participants {
|
||||
@ -295,7 +301,7 @@ private func ChannelMembersControllerEntries(account: Account, presentationData:
|
||||
editable = canEditMembers
|
||||
}
|
||||
}
|
||||
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id))
|
||||
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
@ -310,7 +316,7 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
|
||||
let actionsDisposable = DisposableSet()
|
||||
@ -444,11 +450,13 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo
|
||||
pushControllerImpl?(infoController)
|
||||
// arguments.pushController(infoController)
|
||||
}
|
||||
}, present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
})
|
||||
}
|
||||
|
||||
var emptyStateItem: ItemListControllerEmptyStateItem?
|
||||
if peers == nil {
|
||||
if peers == nil || peers?.count == 0 {
|
||||
emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
|
||||
}
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ private enum ChannelMembersSearchSection {
|
||||
|
||||
private enum ChannelMembersSearchContent: Equatable {
|
||||
case peer(Peer)
|
||||
case participant(participant: RenderedChannelParticipant, label: String?, revealActions: [ParticipantRevealAction], enabled: Bool)
|
||||
case participant(participant: RenderedChannelParticipant, label: String?, revealActions: [ParticipantRevealAction], revealed: Bool, enabled: Bool)
|
||||
|
||||
static func ==(lhs: ChannelMembersSearchContent, rhs: ChannelMembersSearchContent) -> Bool {
|
||||
switch lhs {
|
||||
@ -45,8 +45,8 @@ private enum ChannelMembersSearchContent: Equatable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .participant(participant, label, revealActions, enabled):
|
||||
if case .participant(participant, label, revealActions, enabled) = rhs {
|
||||
case let .participant(participant, label, revealActions, revealed, enabled):
|
||||
if case .participant(participant, label, revealActions, revealed, enabled) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -58,12 +58,28 @@ private enum ChannelMembersSearchContent: Equatable {
|
||||
switch self {
|
||||
case let .peer(peer):
|
||||
return peer.id
|
||||
case let .participant(participant, _, _, _):
|
||||
case let .participant(participant, _, _, _, _):
|
||||
return participant.peer.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChannelMembersSearchContainerInteraction {
|
||||
let peerSelected: (Peer, RenderedChannelParticipant?) -> Void
|
||||
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
|
||||
let promotePeer: (RenderedChannelParticipant) -> Void
|
||||
let restrictPeer: (RenderedChannelParticipant) -> Void
|
||||
let removePeer: (PeerId) -> Void
|
||||
|
||||
init(peerSelected: @escaping (Peer, RenderedChannelParticipant?) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, promotePeer: @escaping (RenderedChannelParticipant) -> Void, restrictPeer: @escaping (RenderedChannelParticipant) -> Void, removePeer: @escaping (PeerId) -> Void) {
|
||||
self.peerSelected = peerSelected
|
||||
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
||||
self.promotePeer = promotePeer
|
||||
self.restrictPeer = restrictPeer
|
||||
self.removePeer = removePeer
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChannelMembersSearchEntry: Comparable, Identifiable {
|
||||
let index: Int
|
||||
let content: ChannelMembersSearchContent
|
||||
@ -87,31 +103,40 @@ private final class ChannelMembersSearchEntry: Comparable, Identifiable {
|
||||
return lhs.index < rhs.index
|
||||
}
|
||||
|
||||
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, peerSelected: @escaping (Peer, RenderedChannelParticipant?) -> Void) -> ListViewItem {
|
||||
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, interaction: ChannelMembersSearchContainerInteraction) -> ListViewItem {
|
||||
switch self.content {
|
||||
case let .peer(peer):
|
||||
return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: .peer(peer: peer, chatPeer: peer), status: .none, enabled: true, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: theme, strings: strings, actionTitle: nil, action: nil) }), action: { _ in
|
||||
peerSelected(peer, nil)
|
||||
interaction.peerSelected(peer, nil)
|
||||
})
|
||||
case let .participant(participant, label, revealActions, enabled):
|
||||
case let .participant(participant, label, revealActions, revealed, enabled):
|
||||
let status: ContactsPeerItemStatus
|
||||
if let label = label {
|
||||
status = .custom(label)
|
||||
} else {
|
||||
status = .none
|
||||
}
|
||||
|
||||
var options: [ItemListPeerItemRevealOption] = []
|
||||
for action in revealActions {
|
||||
options.append(ItemListPeerItemRevealOption(type: action.type, title: action.title, action: {
|
||||
switch action.action {
|
||||
case .promote:
|
||||
//arguments.promotePeer(participant)
|
||||
interaction.promotePeer(participant)
|
||||
break
|
||||
case .restrict:
|
||||
//arguments.restrictPeer(participant)
|
||||
interaction.restrictPeer(participant)
|
||||
break
|
||||
case .remove:
|
||||
//arguments.removePeer(peer.id)
|
||||
interaction.removePeer(participant.peer.id)
|
||||
break
|
||||
}
|
||||
}))
|
||||
}
|
||||
return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: .peer(peer: participant.peer, chatPeer: participant.peer), status: .none, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: theme, strings: strings, actionTitle: nil, action: nil) }), action: { _ in
|
||||
peerSelected(participant.peer, participant)
|
||||
return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: .peer(peer: participant.peer, chatPeer: participant.peer), status: status, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: revealed), options: options, index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: theme, strings: strings, actionTitle: nil, action: nil) }), action: { _ in
|
||||
interaction.peerSelected(participant.peer, participant)
|
||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -123,16 +148,21 @@ struct ChannelMembersSearchContainerTransition {
|
||||
let isSearching: Bool
|
||||
}
|
||||
|
||||
private func channelMembersSearchContainerPreparedRecentTransition(from fromEntries: [ChannelMembersSearchEntry], to toEntries: [ChannelMembersSearchEntry], isSearching: Bool, account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, peerSelected: @escaping (Peer, RenderedChannelParticipant?) -> Void) -> ChannelMembersSearchContainerTransition {
|
||||
private func channelMembersSearchContainerPreparedRecentTransition(from fromEntries: [ChannelMembersSearchEntry], to toEntries: [ChannelMembersSearchEntry], isSearching: Bool, account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, interaction: ChannelMembersSearchContainerInteraction) -> ChannelMembersSearchContainerTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, peerSelected: peerSelected), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, peerSelected: peerSelected), directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, interaction: interaction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, interaction: interaction), directionHint: nil) }
|
||||
|
||||
return ChannelMembersSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching)
|
||||
}
|
||||
|
||||
private struct ChannelMembersSearchContainerState: Equatable {
|
||||
var revealedPeerId: PeerId?
|
||||
var removingParticipantIds = Set<PeerId>()
|
||||
}
|
||||
|
||||
final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
private let account: Account
|
||||
private let openPeer: (Peer, RenderedChannelParticipant?) -> Void
|
||||
@ -150,9 +180,11 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
private let removeMemberDisposable = MetaDisposable()
|
||||
|
||||
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder)>
|
||||
|
||||
init(account: Account, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, updateActivity: @escaping(Bool)->Void) {
|
||||
init(account: Account, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, updateActivity: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
self.account = account
|
||||
self.openPeer = openPeer
|
||||
self.mode = mode
|
||||
@ -173,194 +205,323 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
|
||||
self.addSubnode(self.dimNode)
|
||||
self.addSubnode(self.listNode)
|
||||
|
||||
let themeAndStringsPromise = self.themeAndStringsPromise
|
||||
let foundItems = searchQuery.get()
|
||||
|> mapToSignal { query -> Signal<[ChannelMembersSearchEntry]?, NoError> in
|
||||
updateActivity(true)
|
||||
if let query = query, !query.isEmpty {
|
||||
let foundGroupMembers: Signal<[RenderedChannelParticipant], NoError>
|
||||
let foundMembers: Signal<[RenderedChannelParticipant], NoError>
|
||||
|
||||
switch mode {
|
||||
case .searchMembers, .banAndPromoteActions:
|
||||
foundGroupMembers = Signal { subscriber in
|
||||
let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.recent(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in
|
||||
if case .ready = state.loadingState {
|
||||
subscriber.putNext(state.list)
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
})
|
||||
return disposable
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
foundMembers = .single([])
|
||||
case .inviteActions:
|
||||
foundGroupMembers = .single([])
|
||||
foundMembers = channelMembers(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, category: .recent(.search(query)))
|
||||
|> map { $0 ?? [] }
|
||||
case .searchAdmins:
|
||||
foundGroupMembers = Signal { subscriber in
|
||||
let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.admins(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in
|
||||
if case .ready = state.loadingState {
|
||||
subscriber.putNext(state.list)
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
})
|
||||
return disposable
|
||||
} |> runOn(Queue.mainQueue())
|
||||
foundMembers = .single([])
|
||||
case .searchBanned:
|
||||
foundGroupMembers = Signal { subscriber in
|
||||
let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.restrictedAndBanned(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in
|
||||
if case .ready = state.loadingState {
|
||||
subscriber.putNext(state.list)
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
})
|
||||
return disposable
|
||||
} |> runOn(Queue.mainQueue())
|
||||
foundMembers = .single([])
|
||||
let statePromise = ValuePromise(ChannelMembersSearchContainerState(), ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: ChannelMembersSearchContainerState())
|
||||
let updateState: ((ChannelMembersSearchContainerState) -> ChannelMembersSearchContainerState) -> Void = { f in
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
||||
let removeMemberDisposable = self.removeMemberDisposable
|
||||
let interaction = ChannelMembersSearchContainerInteraction(peerSelected: { peer, participant in
|
||||
openPeer(peer, participant)
|
||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
updateState { state in
|
||||
var state = state
|
||||
if (peerId == nil && fromPeerId == state.revealedPeerId) || (peerId != nil && fromPeerId == nil) {
|
||||
state.revealedPeerId = peerId
|
||||
}
|
||||
return state
|
||||
}
|
||||
}, promotePeer: { participant in
|
||||
present(channelAdminController(account: account, peerId: peerId, adminId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
|
||||
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, restrictPeer: { participant in
|
||||
present(channelBannedMemberController(account: account, peerId: peerId, memberId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
|
||||
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, removePeer: { memberId in
|
||||
let signal = account.postbox.loadedPeerWithId(memberId)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { peer -> Signal<Bool, NoError> in
|
||||
let result = ValuePromise<Bool>()
|
||||
result.set(true)
|
||||
return result.get()
|
||||
}
|
||||
|> mapToSignal { value -> Signal<Void, NoError> in
|
||||
if value {
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.removingParticipantIds.insert(memberId)
|
||||
return state
|
||||
}
|
||||
|
||||
let foundContacts: Signal<([Peer], [PeerId: PeerPresence]), NoError>
|
||||
let foundRemotePeers: Signal<([FoundPeer], [FoundPeer]), NoError>
|
||||
switch mode {
|
||||
case .inviteActions, .banAndPromoteActions:
|
||||
foundContacts = account.postbox.searchContacts(query: query.lowercased())
|
||||
foundRemotePeers = .single(([], [])) |> then(searchPeers(account: account, query: query)
|
||||
|> delay(0.2, queue: Queue.concurrentDefaultQueue()))
|
||||
case .searchMembers, .searchBanned, .searchAdmins:
|
||||
foundContacts = .single(([], [:]))
|
||||
foundRemotePeers = .single(([], []))
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
return account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: account, peerId: peerId, memberId: memberId, bannedRights: TelegramChannelBannedRights(flags: [.banReadMessages], untilDate: Int32.max))
|
||||
|> afterDisposed {
|
||||
Queue.mainQueue().async {
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.removingParticipantIds.remove(memberId)
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return combineLatest(foundGroupMembers, foundMembers, foundContacts, foundRemotePeers, themeAndStringsPromise.get())
|
||||
|> map { foundGroupMembers, foundMembers, foundContacts, foundRemotePeers, themeAndStrings -> [ChannelMembersSearchEntry]? in
|
||||
var entries: [ChannelMembersSearchEntry] = []
|
||||
|
||||
var existingPeerIds = Set<PeerId>()
|
||||
for filter in filters {
|
||||
switch filter {
|
||||
case let .exclude(ids):
|
||||
existingPeerIds = existingPeerIds.union(ids)
|
||||
case .disable:
|
||||
break
|
||||
}
|
||||
}
|
||||
switch mode {
|
||||
case .inviteActions, .banAndPromoteActions:
|
||||
existingPeerIds.insert(account.peerId)
|
||||
case .searchMembers, .searchAdmins, .searchBanned:
|
||||
break
|
||||
}
|
||||
|
||||
var index = 0
|
||||
|
||||
for participant in foundGroupMembers {
|
||||
if !existingPeerIds.contains(participant.peer.id) {
|
||||
existingPeerIds.insert(participant.peer.id)
|
||||
let section: ChannelMembersSearchSection
|
||||
switch mode {
|
||||
case .inviteActions, .banAndPromoteActions:
|
||||
section = .members
|
||||
case .searchMembers, .searchBanned, .searchAdmins:
|
||||
section = .none
|
||||
}
|
||||
|
||||
var label: String?
|
||||
var enabled = true
|
||||
if case .banAndPromoteActions = mode {
|
||||
if case .creator = participant.participant {
|
||||
label = themeAndStrings.1.Channel_Management_LabelCreator
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
switch mode {
|
||||
case .searchAdmins:
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
label = themeAndStrings.1.Channel_Management_LabelCreator
|
||||
case let .member(_, _, adminInfo, _):
|
||||
if let adminInfo = adminInfo {
|
||||
if let peer = participant.peers[adminInfo.promotedBy] {
|
||||
label = themeAndStrings.1.Channel_Management_PromotedBy(peer.displayTitle).0
|
||||
}
|
||||
}
|
||||
}
|
||||
case .searchBanned:
|
||||
switch participant.participant {
|
||||
case let .member(_, _, _, banInfo):
|
||||
if let banInfo = banInfo, let peer = participant.peers[banInfo.restrictedBy] {
|
||||
label = themeAndStrings.1.Channel_Management_RestrictedBy(peer.displayTitle).0
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: [], enabled: enabled), section: section))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
for participant in foundMembers {
|
||||
if !existingPeerIds.contains(participant.peer.id) {
|
||||
existingPeerIds.insert(participant.peer.id)
|
||||
let section: ChannelMembersSearchSection
|
||||
switch mode {
|
||||
case .inviteActions, .banAndPromoteActions:
|
||||
section = .members
|
||||
case .searchMembers, .searchBanned, .searchAdmins:
|
||||
section = .none
|
||||
}
|
||||
|
||||
var label: String?
|
||||
var enabled = true
|
||||
if case .banAndPromoteActions = mode {
|
||||
if case .creator = participant.participant {
|
||||
label = themeAndStrings.1.Channel_Management_LabelCreator
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: [], enabled: enabled), section: section))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
for peer in foundContacts.0 {
|
||||
if !existingPeerIds.contains(peer.id) {
|
||||
existingPeerIds.insert(peer.id)
|
||||
entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .contacts))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
for foundPeer in foundRemotePeers.0 {
|
||||
let peer = foundPeer.peer
|
||||
if !existingPeerIds.contains(peer.id) && peer is TelegramUser {
|
||||
existingPeerIds.insert(peer.id)
|
||||
entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .global))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
for foundPeer in foundRemotePeers.1 {
|
||||
let peer = foundPeer.peer
|
||||
if !existingPeerIds.contains(peer.id) && peer is TelegramUser {
|
||||
existingPeerIds.insert(peer.id)
|
||||
entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .global))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
return entries
|
||||
return removePeerMember(account: account, peerId: peerId, memberId: memberId)
|
||||
|> deliverOnMainQueue
|
||||
|> afterDisposed {
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.removingParticipantIds.remove(memberId)
|
||||
return state
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
removeMemberDisposable.set(signal.start())
|
||||
})
|
||||
|
||||
let themeAndStringsPromise = self.themeAndStringsPromise
|
||||
let foundItems = combineLatest(searchQuery.get(), account.postbox.multiplePeersView([peerId]) |> take(1))
|
||||
|> mapToSignal { query, peerView -> Signal<[ChannelMembersSearchEntry]?, NoError> in
|
||||
guard let channel = peerView.peers[peerId] as? TelegramChannel else {
|
||||
return .single(nil)
|
||||
}
|
||||
updateActivity(true)
|
||||
if let query = query, !query.isEmpty {
|
||||
let foundGroupMembers: Signal<[RenderedChannelParticipant], NoError>
|
||||
let foundMembers: Signal<[RenderedChannelParticipant], NoError>
|
||||
|
||||
switch mode {
|
||||
case .searchMembers, .banAndPromoteActions:
|
||||
foundGroupMembers = Signal { subscriber in
|
||||
let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.recent(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in
|
||||
if case .ready = state.loadingState {
|
||||
subscriber.putNext(state.list)
|
||||
}
|
||||
})
|
||||
return disposable
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
foundMembers = .single([])
|
||||
case .inviteActions:
|
||||
foundGroupMembers = .single([])
|
||||
foundMembers = channelMembers(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, category: .recent(.search(query)))
|
||||
|> map { $0 ?? [] }
|
||||
case .searchAdmins:
|
||||
foundGroupMembers = Signal { subscriber in
|
||||
let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.admins(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in
|
||||
if case .ready = state.loadingState {
|
||||
subscriber.putNext(state.list)
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
})
|
||||
return disposable
|
||||
} |> runOn(Queue.mainQueue())
|
||||
foundMembers = .single([])
|
||||
case .searchBanned:
|
||||
foundGroupMembers = Signal { subscriber in
|
||||
let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.restrictedAndBanned(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in
|
||||
if case .ready = state.loadingState {
|
||||
subscriber.putNext(state.list)
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
})
|
||||
return disposable
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
foundMembers = .single([])
|
||||
}
|
||||
|
||||
let foundContacts: Signal<([Peer], [PeerId: PeerPresence]), NoError>
|
||||
let foundRemotePeers: Signal<([FoundPeer], [FoundPeer]), NoError>
|
||||
switch mode {
|
||||
case .inviteActions, .banAndPromoteActions:
|
||||
foundContacts = account.postbox.searchContacts(query: query.lowercased())
|
||||
foundRemotePeers = .single(([], [])) |> then(searchPeers(account: account, query: query)
|
||||
|> delay(0.2, queue: Queue.concurrentDefaultQueue()))
|
||||
case .searchMembers, .searchBanned, .searchAdmins:
|
||||
foundContacts = .single(([], [:]))
|
||||
foundRemotePeers = .single(([], []))
|
||||
}
|
||||
|
||||
return combineLatest(foundGroupMembers, foundMembers, foundContacts, foundRemotePeers, themeAndStringsPromise.get(), statePromise.get())
|
||||
|> map { foundGroupMembers, foundMembers, foundContacts, foundRemotePeers, themeAndStrings, state -> [ChannelMembersSearchEntry]? in
|
||||
var entries: [ChannelMembersSearchEntry] = []
|
||||
|
||||
var existingPeerIds = Set<PeerId>()
|
||||
for filter in filters {
|
||||
switch filter {
|
||||
case let .exclude(ids):
|
||||
existingPeerIds = existingPeerIds.union(ids)
|
||||
case .disable:
|
||||
break
|
||||
}
|
||||
}
|
||||
switch mode {
|
||||
case .inviteActions, .banAndPromoteActions:
|
||||
existingPeerIds.insert(account.peerId)
|
||||
case .searchMembers, .searchAdmins, .searchBanned:
|
||||
break
|
||||
}
|
||||
|
||||
var index = 0
|
||||
|
||||
for participant in foundGroupMembers {
|
||||
if !existingPeerIds.contains(participant.peer.id) {
|
||||
existingPeerIds.insert(participant.peer.id)
|
||||
let section: ChannelMembersSearchSection
|
||||
switch mode {
|
||||
case .inviteActions, .banAndPromoteActions:
|
||||
section = .members
|
||||
case .searchMembers, .searchBanned, .searchAdmins:
|
||||
section = .none
|
||||
}
|
||||
|
||||
var canPromote: Bool
|
||||
var canRestrict: Bool
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
canPromote = false
|
||||
canRestrict = false
|
||||
case let .member(_, _, adminRights, bannedRights):
|
||||
if channel.hasAdminRights([.canAddAdmins]) {
|
||||
canPromote = true
|
||||
} else {
|
||||
canPromote = false
|
||||
}
|
||||
if channel.hasAdminRights([.canBanUsers]) {
|
||||
canRestrict = true
|
||||
} else {
|
||||
canRestrict = false
|
||||
}
|
||||
if canPromote {
|
||||
if let bannedRights = bannedRights {
|
||||
if bannedRights.restrictedBy != account.peerId && !channel.flags.contains(.isCreator) {
|
||||
canPromote = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if canRestrict {
|
||||
if let adminRights = adminRights {
|
||||
if adminRights.promotedBy != account.peerId && !channel.flags.contains(.isCreator) {
|
||||
canRestrict = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var label: String?
|
||||
var enabled = true
|
||||
if case .banAndPromoteActions = mode {
|
||||
if case .creator = participant.participant {
|
||||
label = themeAndStrings.1.Channel_Management_LabelCreator
|
||||
enabled = false
|
||||
}
|
||||
} else if case .searchMembers = mode {
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
label = themeAndStrings.1.Channel_Management_LabelCreator
|
||||
case let .member(member):
|
||||
if member.adminInfo != nil {
|
||||
label = themeAndStrings.1.Channel_Management_LabelEditor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if state.removingParticipantIds.contains(participant.peer.id) {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
var peerActions: [ParticipantRevealAction] = []
|
||||
if case .searchMembers = mode {
|
||||
if canPromote {
|
||||
peerActions.append(ParticipantRevealAction(type: .neutral, title: themeAndStrings.1.GroupInfo_ActionPromote, action: .promote))
|
||||
}
|
||||
if canRestrict {
|
||||
peerActions.append(ParticipantRevealAction(type: .warning, title: themeAndStrings.1.GroupInfo_ActionRestrict, action: .restrict))
|
||||
peerActions.append(ParticipantRevealAction(type: .destructive, title: themeAndStrings.1.Common_Delete, action: .remove))
|
||||
}
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .searchAdmins:
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
label = themeAndStrings.1.Channel_Management_LabelCreator
|
||||
case let .member(_, _, adminInfo, _):
|
||||
if let adminInfo = adminInfo {
|
||||
if let peer = participant.peers[adminInfo.promotedBy] {
|
||||
label = themeAndStrings.1.Channel_Management_PromotedBy(peer.displayTitle).0
|
||||
}
|
||||
}
|
||||
}
|
||||
case .searchBanned:
|
||||
switch participant.participant {
|
||||
case let .member(_, _, _, banInfo):
|
||||
if let banInfo = banInfo, let peer = participant.peers[banInfo.restrictedBy] {
|
||||
label = themeAndStrings.1.Channel_Management_RestrictedBy(peer.displayTitle).0
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: peerActions, revealed: state.revealedPeerId == participant.peer.id, enabled: enabled), section: section))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
for participant in foundMembers {
|
||||
if !existingPeerIds.contains(participant.peer.id) {
|
||||
existingPeerIds.insert(participant.peer.id)
|
||||
let section: ChannelMembersSearchSection
|
||||
switch mode {
|
||||
case .inviteActions, .banAndPromoteActions:
|
||||
section = .members
|
||||
case .searchMembers, .searchBanned, .searchAdmins:
|
||||
section = .none
|
||||
}
|
||||
|
||||
var label: String?
|
||||
var enabled = true
|
||||
if case .banAndPromoteActions = mode {
|
||||
if case .creator = participant.participant {
|
||||
label = themeAndStrings.1.Channel_Management_LabelCreator
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: [], revealed: false, enabled: enabled), section: section))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
for peer in foundContacts.0 {
|
||||
if !existingPeerIds.contains(peer.id) {
|
||||
existingPeerIds.insert(peer.id)
|
||||
entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .contacts))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
for foundPeer in foundRemotePeers.0 {
|
||||
let peer = foundPeer.peer
|
||||
if !existingPeerIds.contains(peer.id) && peer is TelegramUser {
|
||||
existingPeerIds.insert(peer.id)
|
||||
entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .global))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
for foundPeer in foundRemotePeers.1 {
|
||||
let peer = foundPeer.peer
|
||||
if !existingPeerIds.contains(peer.id) && peer is TelegramUser {
|
||||
existingPeerIds.insert(peer.id)
|
||||
entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .global))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
|
||||
let previousSearchItems = Atomic<[ChannelMembersSearchEntry]?>(value: nil)
|
||||
@ -371,24 +532,24 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
|
||||
let previousEntries = previousSearchItems.swap(entries)
|
||||
updateActivity(false)
|
||||
let firstTime = previousEntries == nil
|
||||
let transition = channelMembersSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries ?? [], isSearching: entries != nil, account: account, theme: themeAndStrings.0, strings: themeAndStrings.1, nameSortOrder: themeAndStrings.2, nameDisplayOrder: themeAndStrings.3, peerSelected: openPeer)
|
||||
let transition = channelMembersSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries ?? [], isSearching: entries != nil, account: account, theme: themeAndStrings.0, strings: themeAndStrings.1, nameSortOrder: themeAndStrings.2, nameDisplayOrder: themeAndStrings.3, interaction: interaction)
|
||||
strongSelf.enqueueTransition(transition, firstTime: firstTime)
|
||||
}
|
||||
}))
|
||||
|
||||
self.presentationDataDisposable = (account.telegramApplicationContext.presentationData
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
let previousTheme = strongSelf.presentationData.theme
|
||||
let previousStrings = strongSelf.presentationData.strings
|
||||
|
||||
strongSelf.presentationData = presentationData
|
||||
|
||||
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
|
||||
strongSelf.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
let previousTheme = strongSelf.presentationData.theme
|
||||
let previousStrings = strongSelf.presentationData.strings
|
||||
|
||||
strongSelf.presentationData = presentationData
|
||||
|
||||
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
|
||||
strongSelf.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
self.listNode.beganInteractiveDragging = { [weak self] in
|
||||
self?.dismissInput?()
|
||||
@ -398,6 +559,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
|
||||
deinit {
|
||||
self.searchDisposable.dispose()
|
||||
self.presentationDataDisposable?.dispose()
|
||||
self.removeMemberDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
|
||||
@ -70,6 +70,9 @@ final class ChannelMembersSearchController: ViewController {
|
||||
self?.dismiss()
|
||||
self?.openPeer(peer, participant)
|
||||
}
|
||||
self.controllerNode.present = { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
@ -121,6 +121,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
||||
var requestActivateSearch: (() -> Void)?
|
||||
var requestDeactivateSearch: (() -> Void)?
|
||||
var requestOpenPeerFromSearch: ((Peer, RenderedChannelParticipant?) -> Void)?
|
||||
var present: ((ViewController, Any?) -> Void)?
|
||||
|
||||
var themeAndStrings: (PresentationTheme, PresentationStrings)
|
||||
|
||||
@ -292,6 +293,8 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
||||
self?.requestOpenPeerFromSearch?(peer, participant)
|
||||
}, updateActivity: { value in
|
||||
|
||||
}, present: { [weak self] c, a in
|
||||
self?.present?(c, a)
|
||||
}), cancel: { [weak self] in
|
||||
if let requestDeactivateSearch = self?.requestDeactivateSearch {
|
||||
requestDeactivateSearch()
|
||||
|
||||
@ -67,7 +67,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
||||
case publicLinkStatus(PresentationTheme, String, AddressNameValidationStatus)
|
||||
|
||||
case existingLinksInfo(PresentationTheme, String)
|
||||
case existingLinkPeerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, ItemListPeerItemEditing, Bool)
|
||||
case existingLinkPeerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, ItemListPeerItemEditing, Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -113,7 +113,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
||||
return 12
|
||||
case .existingLinksInfo:
|
||||
return 13
|
||||
case let .existingLinkPeerItem(index, _, _, _, _, _, _):
|
||||
case let .existingLinkPeerItem(index, _, _, _, _, _, _, _):
|
||||
return 14 + index
|
||||
}
|
||||
}
|
||||
@ -204,8 +204,8 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .existingLinkPeerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsEditing, lhsEnabled):
|
||||
if case let .existingLinkPeerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsEditing, rhsEnabled) = rhs {
|
||||
case let .existingLinkPeerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer, lhsEditing, lhsEnabled):
|
||||
if case let .existingLinkPeerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer, rhsEditing, rhsEnabled) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -218,6 +218,9 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameOrder != rhsNameOrder {
|
||||
return false
|
||||
}
|
||||
if !lhsPeer.isEqual(rhsPeer) {
|
||||
return false
|
||||
}
|
||||
@ -307,12 +310,12 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
|
||||
return ItemListActivityTextItem(displayActivity: displayActivity, theme: theme, text: NSAttributedString(string: text, textColor: color), sectionId: self.section)
|
||||
case let .existingLinksInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
case let .existingLinkPeerItem(_, theme, strings, dateTimeFormat, peer, editing, enabled):
|
||||
case let .existingLinkPeerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, editing, enabled):
|
||||
var label = ""
|
||||
if let addressName = peer.addressName {
|
||||
label = "t.me/" + addressName
|
||||
}
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .text(label), label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { previousId, id in
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .text(label), label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { previousId, id in
|
||||
arguments.setPeerIdWithRevealedOptions(previousId, id)
|
||||
}, removePeer: { peerId in
|
||||
arguments.revokePeerId(peerId)
|
||||
@ -491,7 +494,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
||||
}
|
||||
return lhsDate > rhsDate
|
||||
}) {
|
||||
entries.append(.existingLinkPeerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, ItemListPeerItemEditing(editable: true, editing: true, revealed: state.revealedRevokePeerId == peer.id), state.revokingPeerId == nil))
|
||||
entries.append(.existingLinkPeerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: true, revealed: state.revealedRevokePeerId == peer.id), state.revokingPeerId == nil))
|
||||
index += 1
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -236,7 +236,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
self.presentationData = (account.applicationContext as! TelegramApplicationContext).currentPresentationData.with { $0 }
|
||||
self.automaticMediaDownloadSettings = (account.applicationContext as! TelegramApplicationContext).currentAutomaticMediaDownloadSettings.with { $0 }
|
||||
|
||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, fontSize: self.presentationData.fontSize, accountPeerId: account.peerId, mode: mode, chatLocation: chatLocation)
|
||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, fontSize: self.presentationData.fontSize, accountPeerId: account.peerId, mode: mode, chatLocation: chatLocation)
|
||||
|
||||
var mediaAccessoryPanelVisibility = MediaAccessoryPanelVisibility.none
|
||||
if case .standard = mode {
|
||||
@ -1102,7 +1102,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
|
||||
self.controllerInteraction = controllerInteraction
|
||||
|
||||
self.chatTitleView = ChatTitleView(account: self.account, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat)
|
||||
self.chatTitleView = ChatTitleView(account: self.account, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder)
|
||||
self.navigationItem.titleView = self.chatTitleView
|
||||
self.chatTitleView?.pressed = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
|
||||
@ -128,10 +128,6 @@ class ChatDocumentGalleryItemNode: GalleryItemNode, WKNavigationDelegate {
|
||||
|
||||
super.init()
|
||||
|
||||
if let webView = self.webView as? WKWebView {
|
||||
//webView.navigationDelegate = self
|
||||
}
|
||||
|
||||
self.view.addSubview(self.webView)
|
||||
|
||||
self.statusNodeContainer.addSubnode(self.statusNode)
|
||||
@ -170,6 +166,11 @@ class ChatDocumentGalleryItemNode: GalleryItemNode, WKNavigationDelegate {
|
||||
let updateFile = self.accountAndFile?.1.media != fileReference.media
|
||||
self.accountAndFile = (account, fileReference)
|
||||
if updateFile {
|
||||
if fileReference.media.mimeType.hasPrefix("image/") {
|
||||
if let webView = self.webView as? WKWebView {
|
||||
webView.backgroundColor = .black
|
||||
}
|
||||
}
|
||||
self.maybeLoadContent()
|
||||
self.setupStatus(account: account, resource: fileReference.media.resource)
|
||||
}
|
||||
|
||||
@ -246,22 +246,18 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode {
|
||||
|
||||
super.init()
|
||||
|
||||
// self.chatPresentationDataPromise.set(.single(()))
|
||||
|
||||
|
||||
self.chatPresentationDataPromise.set(account.telegramApplicationContext.presentationData |> map { presentationData in
|
||||
return ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: presentationData.disableAnimations)
|
||||
return ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations)
|
||||
})
|
||||
|
||||
self.floatingSections = true
|
||||
//self.preloadPages = false
|
||||
|
||||
let messageViewQueue = self.messageViewQueue
|
||||
|
||||
let historyViewUpdate = self.chatHistoryLocation
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { location in
|
||||
return chatHistoryViewForLocation(location, account: account, chatLocation: .peer(peerId), fixedCombinedReadStates: nil, tagMask: tagMask, additionalData: [], orderStatistics: [.locationWithinMonth])
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { location in
|
||||
return chatHistoryViewForLocation(location, account: account, chatLocation: .peer(peerId), fixedCombinedReadStates: nil, tagMask: tagMask, additionalData: [], orderStatistics: [.locationWithinMonth])
|
||||
}
|
||||
|
||||
let previousView = Atomic<ChatHistoryView?>(value: nil)
|
||||
|
||||
@ -356,7 +356,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
self.currentPresentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.currentPresentationData.theme, wallpaper: self.currentPresentationData.chatWallpaper), fontSize: self.currentPresentationData.fontSize, strings: self.currentPresentationData.strings, dateTimeFormat: self.currentPresentationData.dateTimeFormat, disableAnimations: self.currentPresentationData.disableAnimations))
|
||||
self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.currentPresentationData.theme, wallpaper: self.currentPresentationData.chatWallpaper), fontSize: self.currentPresentationData.fontSize, strings: self.currentPresentationData.strings, dateTimeFormat: self.currentPresentationData.dateTimeFormat, nameDisplayOrder: self.currentPresentationData.nameDisplayOrder, disableAnimations: self.currentPresentationData.disableAnimations))
|
||||
|
||||
super.init()
|
||||
|
||||
@ -680,7 +680,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings || previousWallpaper != presentationData.chatWallpaper || previousDisableAnimations != presentationData.disableAnimations {
|
||||
let themeData = ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper)
|
||||
let chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: presentationData.disableAnimations)
|
||||
let chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations)
|
||||
|
||||
strongSelf.dynamicBounceEnabled = !presentationData.disableAnimations
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS
|
||||
editPanelNode.updateThemeAndStrings(theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings)
|
||||
return editPanelNode
|
||||
} else {
|
||||
let panelNode = EditAccessoryPanelNode(account: account, messageId: editMessage.messageId, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings)
|
||||
let panelNode = EditAccessoryPanelNode(account: account, messageId: editMessage.messageId, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder)
|
||||
panelNode.interfaceInteraction = interfaceInteraction
|
||||
return panelNode
|
||||
}
|
||||
@ -60,7 +60,7 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS
|
||||
replyPanelNode.updateThemeAndStrings(theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings)
|
||||
return replyPanelNode
|
||||
} else {
|
||||
let panelNode = ReplyAccessoryPanelNode(account: account, messageId: replyMessageId, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings)
|
||||
let panelNode = ReplyAccessoryPanelNode(account: account, messageId: replyMessageId, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder)
|
||||
panelNode.interfaceInteraction = interfaceInteraction
|
||||
return panelNode
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ private func updatedContextQueryResultStateForQuery(account: Account, peer: Peer
|
||||
return result == .orderedAscending
|
||||
}))
|
||||
sortedPeers = sortedPeers.filter { peer in
|
||||
return !peer.displayTitle.isEmpty
|
||||
return !peer.debugDisplayTitle.isEmpty
|
||||
}
|
||||
return { _ in return .mentions(sortedPeers) }
|
||||
}
|
||||
|
||||
@ -557,7 +557,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
let presentationData = strongSelf.account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
var generalMessageContentKind: MessageContentKind?
|
||||
for message in messages {
|
||||
let currentKind = messageContentKind(message, strings: presentationData.strings, accountPeerId: strongSelf.account.peerId)
|
||||
let currentKind = messageContentKind(message, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: strongSelf.account.peerId)
|
||||
if generalMessageContentKind == nil || generalMessageContentKind == currentKind {
|
||||
generalMessageContentKind = currentKind
|
||||
} else {
|
||||
@ -683,7 +683,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
||||
let presentationData = strongSelf.account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
var generalMessageContentKind: MessageContentKind?
|
||||
for message in messages {
|
||||
let currentKind = messageContentKind(message, strings: presentationData.strings, accountPeerId: strongSelf.account.peerId)
|
||||
let currentKind = messageContentKind(message, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: strongSelf.account.peerId)
|
||||
if generalMessageContentKind == nil || generalMessageContentKind == currentKind {
|
||||
generalMessageContentKind = currentKind
|
||||
} else {
|
||||
|
||||
@ -520,7 +520,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let leftInset: CGFloat = params.leftInset + 78.0
|
||||
|
||||
let (peer, initialHideAuthor, messageText) = chatListItemStrings(strings: item.presentationData.strings, message: message, chatPeer: itemPeer, accountPeerId: item.account.peerId)
|
||||
let (peer, initialHideAuthor, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, message: message, chatPeer: itemPeer, accountPeerId: item.account.peerId)
|
||||
var hideAuthor = initialHideAuthor
|
||||
if isPeerGroup {
|
||||
hideAuthor = false
|
||||
@ -540,11 +540,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
if let author = message.author as? TelegramUser, let peer = peer, !(peer is TelegramUser) {
|
||||
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
|
||||
} else {
|
||||
peerText = author.id == account.peerId ? item.presentationData.strings.DialogList_You : author.displayTitle(strings: item.presentationData.strings)
|
||||
peerText = author.id == account.peerId ? item.presentationData.strings.DialogList_You : author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
}
|
||||
} else if case .groupReference = item.content {
|
||||
if let messagePeer = itemPeer.chatMainPeer {
|
||||
peerText = messagePeer.displayTitle(strings: item.presentationData.strings)
|
||||
peerText = messagePeer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
}
|
||||
}
|
||||
|
||||
@ -559,7 +559,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
case .peer:
|
||||
if peer?.id == item.account.peerId {
|
||||
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleFont, textColor: theme.titleColor)
|
||||
} else if let displayTitle = peer?.displayTitle(strings: item.presentationData.strings) {
|
||||
} else if let displayTitle = peer?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) {
|
||||
titleAttributedString = NSAttributedString(string: displayTitle, font: titleFont, textColor: item.index.messageIndex.id.peerId.namespace == Namespaces.Peer.SecretChat ? theme.secretTitleColor : theme.titleColor)
|
||||
}
|
||||
case .groupReference:
|
||||
|
||||
@ -2,7 +2,7 @@ import Foundation
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
|
||||
public func chatListItemStrings(strings: PresentationStrings, message: Message?, chatPeer: RenderedPeer, accountPeerId: PeerId) -> (peer: Peer?, hideAuthor: Bool, messageText: String) {
|
||||
public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, message: Message?, chatPeer: RenderedPeer, accountPeerId: PeerId) -> (peer: Peer?, hideAuthor: Bool, messageText: String) {
|
||||
let peer: Peer?
|
||||
|
||||
var hideAuthor = false
|
||||
@ -130,12 +130,12 @@ public func chatListItemStrings(strings: PresentationStrings, message: Message?,
|
||||
}
|
||||
}
|
||||
default:
|
||||
if let text = plainServiceMessageString(strings: strings, message: message, accountPeerId: accountPeerId) {
|
||||
if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId) {
|
||||
messageText = text
|
||||
}
|
||||
}
|
||||
case _ as TelegramMediaExpiredContent:
|
||||
if let text = plainServiceMessageString(strings: strings, message: message, accountPeerId: accountPeerId) {
|
||||
if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId) {
|
||||
messageText = text
|
||||
}
|
||||
default:
|
||||
|
||||
@ -22,15 +22,15 @@ private func peerMentionsAttributes(primaryTextColor: UIColor, peerIds: [(Int, P
|
||||
return result
|
||||
}
|
||||
|
||||
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, message: Message, accountPeerId: PeerId) -> NSAttributedString? {
|
||||
return universalServiceMessageString(theme: theme, strings: strings, message: message, accountPeerId: accountPeerId)
|
||||
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, message: Message, accountPeerId: PeerId) -> NSAttributedString? {
|
||||
return universalServiceMessageString(theme: theme, strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId)
|
||||
}
|
||||
|
||||
func plainServiceMessageString(strings: PresentationStrings, message: Message, accountPeerId: PeerId) -> String? {
|
||||
return universalServiceMessageString(theme: nil, strings: strings, message: message, accountPeerId: accountPeerId)?.string
|
||||
func plainServiceMessageString(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, message: Message, accountPeerId: PeerId) -> String? {
|
||||
return universalServiceMessageString(theme: nil, strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId)?.string
|
||||
}
|
||||
|
||||
private func universalServiceMessageString(theme: ChatPresentationThemeData?, strings: PresentationStrings, message: Message, accountPeerId: PeerId) -> NSAttributedString? {
|
||||
private func universalServiceMessageString(theme: ChatPresentationThemeData?, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, message: Message, accountPeerId: PeerId) -> NSAttributedString? {
|
||||
var attributedString: NSAttributedString?
|
||||
|
||||
let primaryTextColor: UIColor
|
||||
@ -44,7 +44,7 @@ private func universalServiceMessageString(theme: ChatPresentationThemeData?, st
|
||||
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
let authorName = message.author?.displayTitle ?? ""
|
||||
let authorName = message.author?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? ""
|
||||
|
||||
var isChannel = false
|
||||
if message.id.peerId.namespace == Namespaces.Peer.CloudChannel, let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
||||
@ -70,7 +70,7 @@ private func universalServiceMessageString(theme: ChatPresentationThemeData?, st
|
||||
if peerIds.count == 1 {
|
||||
attributePeerIds.append((1, peerIds.first))
|
||||
}
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_Invited(authorName, peerDisplayTitles(peerIds, message.peers)), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_Invited(authorName, peerDebugDisplayTitles(peerIds, message.peers)), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
|
||||
}
|
||||
case let .removedMembers(peerIds):
|
||||
if peerIds.first == message.author?.id {
|
||||
@ -84,7 +84,7 @@ private func universalServiceMessageString(theme: ChatPresentationThemeData?, st
|
||||
if peerIds.count == 1 {
|
||||
attributePeerIds.append((1, peerIds.first))
|
||||
}
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_Kicked(authorName, peerDisplayTitles(peerIds, message.peers)), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_Kicked(authorName, peerDebugDisplayTitles(peerIds, message.peers)), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
|
||||
}
|
||||
case let .photoUpdated(image):
|
||||
if authorName.isEmpty || isChannel {
|
||||
@ -510,7 +510,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 0.0, hidesBackground: .always, forceFullCorners: false, forceAlignment: .center)
|
||||
|
||||
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
|
||||
let attributedString = attributedServiceMessageString(theme: item.presentationData.theme, strings: item.presentationData.strings, message: item.message, accountPeerId: item.account.peerId)
|
||||
let attributedString = attributedServiceMessageString(theme: item.presentationData.theme, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, message: item.message, accountPeerId: item.account.peerId)
|
||||
|
||||
var image: TelegramMediaImage?
|
||||
for media in item.message.media {
|
||||
|
||||
@ -295,7 +295,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
// }
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings, format: .minimal)
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .minimal)
|
||||
|
||||
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.presentationData.theme, item.presentationData.strings, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
@ -305,7 +305,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
for attribute in item.message.attributes {
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||
let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - imageSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left)
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
if let currentReplyBackgroundNode = currentReplyBackgroundNode {
|
||||
updatedReplyBackgroundNode = currentReplyBackgroundNode
|
||||
|
||||
@ -304,7 +304,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
sentViaBot = true
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: presentationData.dateTimeFormat, strings: presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings)
|
||||
|
||||
var webpageGalleryMediaCount: Int?
|
||||
for media in message.media {
|
||||
|
||||
@ -604,10 +604,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
|
||||
if initialDisplayHeader && displayAuthorInfo {
|
||||
if let peer = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
||||
authorNameString = peer.displayTitle
|
||||
authorNameString = peer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
authorNameColor = chatMessagePeerIdColors[Int(peer.id.id % 7)]
|
||||
} else if let effectiveAuthor = effectiveAuthor {
|
||||
authorNameString = effectiveAuthor.displayTitle
|
||||
authorNameString = effectiveAuthor.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
authorNameColor = chatMessagePeerIdColors[Int(effectiveAuthor.id.id % 7)]
|
||||
}
|
||||
if let rawAuthorNameColor = authorNameColor {
|
||||
@ -686,7 +686,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
sentViaBot = true
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType
|
||||
if message.effectivelyIncoming(item.account.peerId) {
|
||||
@ -774,7 +774,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
if let authorSignature = forwardInfo.authorSignature {
|
||||
forwardAuthorSignature = authorSignature
|
||||
} else if forwardInfo.author.id != source.id {
|
||||
forwardAuthorSignature = forwardInfo.author.displayTitle
|
||||
forwardAuthorSignature = forwardInfo.author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
} else {
|
||||
forwardAuthorSignature = nil
|
||||
}
|
||||
@ -782,7 +782,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
forwardSource = forwardInfo.author
|
||||
forwardAuthorSignature = nil
|
||||
}
|
||||
let sizeAndApply = forwardInfoLayout(item.presentationData.theme, item.presentationData.strings, .bubble(incoming: incoming), forwardSource, forwardAuthorSignature, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude))
|
||||
let sizeAndApply = forwardInfoLayout(item.presentationData, item.presentationData.strings, .bubble(incoming: incoming), forwardSource, forwardAuthorSignature, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude))
|
||||
forwardInfoSizeApply = (sizeAndApply.0, { sizeAndApply.1() })
|
||||
|
||||
forwardInfoOriginY = headerSize.height
|
||||
@ -796,7 +796,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
} else {
|
||||
headerSize.height += 2.0
|
||||
}
|
||||
let sizeAndApply = replyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .bubble(incoming: incoming), replyMessage, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude))
|
||||
let sizeAndApply = replyInfoLayout(item.presentationData, item.presentationData.strings, item.account, .bubble(incoming: incoming), replyMessage, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude))
|
||||
replyInfoSizeApply = (sizeAndApply.0, { sizeAndApply.1() })
|
||||
|
||||
replyInfoOriginY = headerSize.height
|
||||
|
||||
@ -132,7 +132,7 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
buttonImage = PresentationResourcesChat.chatBubbleOutgoingCallButtonImage(item.presentationData.theme.theme)
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
|
||||
let statusText: String
|
||||
if let callDuration = callDuration, callDuration > 1 {
|
||||
|
||||
@ -151,7 +151,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
sentViaBot = true
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
switch position {
|
||||
|
||||
@ -18,15 +18,15 @@ class ChatMessageForwardInfoNode: ASDisplayNode {
|
||||
super.init()
|
||||
}
|
||||
|
||||
class func asyncLayout(_ maybeNode: ChatMessageForwardInfoNode?) -> (_ theme: ChatPresentationThemeData, _ strings: PresentationStrings, _ type: ChatMessageForwardInfoType, _ peer: Peer, _ authorName: String?, _ constrainedSize: CGSize) -> (CGSize, () -> ChatMessageForwardInfoNode) {
|
||||
class func asyncLayout(_ maybeNode: ChatMessageForwardInfoNode?) -> (_ presentationData: ChatPresentationData, _ strings: PresentationStrings, _ type: ChatMessageForwardInfoType, _ peer: Peer, _ authorName: String?, _ constrainedSize: CGSize) -> (CGSize, () -> ChatMessageForwardInfoNode) {
|
||||
let textNodeLayout = TextNode.asyncLayout(maybeNode?.textNode)
|
||||
|
||||
return { theme, strings, type, peer, authorName, constrainedSize in
|
||||
return { presentationData, strings, type, peer, authorName, constrainedSize in
|
||||
let peerString: String
|
||||
if let authorName = authorName {
|
||||
peerString = "\(peer.displayTitle(strings: strings)) (\(authorName))"
|
||||
peerString = "\(peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)) (\(authorName))"
|
||||
} else {
|
||||
peerString = peer.displayTitle(strings: strings)
|
||||
peerString = peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
}
|
||||
|
||||
let titleColor: UIColor
|
||||
@ -34,10 +34,10 @@ class ChatMessageForwardInfoNode: ASDisplayNode {
|
||||
|
||||
switch type {
|
||||
case let .bubble(incoming):
|
||||
titleColor = incoming ? theme.theme.chat.bubble.incomingAccentTextColor : theme.theme.chat.bubble.outgoingAccentTextColor
|
||||
titleColor = incoming ? presentationData.theme.theme.chat.bubble.incomingAccentTextColor : presentationData.theme.theme.chat.bubble.outgoingAccentTextColor
|
||||
completeSourceString = strings.Message_ForwardedMessage(peerString)
|
||||
case .standalone:
|
||||
let serviceColor = serviceMessageColorComponents(theme: theme.theme, wallpaper: theme.wallpaper)
|
||||
let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
|
||||
titleColor = serviceColor.primaryText
|
||||
completeSourceString = strings.Message_ForwardedMessageShort(peerString)
|
||||
}
|
||||
|
||||
@ -168,7 +168,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
} else if let attribute = attribute as? InlineBotMessageAttribute {
|
||||
if let peerId = attribute.peerId, let bot = item.message.peers[peerId] as? TelegramUser {
|
||||
inlineBotNameString = bot.username
|
||||
@ -205,7 +205,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
if let authorSignature = forwardInfo.authorSignature {
|
||||
forwardAuthorSignature = authorSignature
|
||||
} else if forwardInfo.author.id != source.id {
|
||||
forwardAuthorSignature = forwardInfo.author.displayTitle
|
||||
forwardAuthorSignature = forwardInfo.author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
} else {
|
||||
forwardAuthorSignature = nil
|
||||
}
|
||||
@ -214,7 +214,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
forwardAuthorSignature = nil
|
||||
}
|
||||
let availableWidth = max(60.0, availableContentWidth - videoLayout.contentSize.width + 6.0)
|
||||
forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData.theme, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
if let currentForwardBackgroundNode = currentForwardBackgroundNode {
|
||||
updatedForwardBackgroundNode = currentForwardBackgroundNode
|
||||
|
||||
@ -17,6 +17,9 @@ private let durationFont = Font.regular(11.0)
|
||||
final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
private let titleNode: TextNode
|
||||
private let descriptionNode: TextNode
|
||||
private let descriptionMeasuringNode: TextNode
|
||||
private let fetchingTextNode: ImmediateTextNode
|
||||
private let fetchingCompactTextNode: ImmediateTextNode
|
||||
private let waveformNode: AudioWaveformNode
|
||||
private let waveformForegroundNode: AudioWaveformNode
|
||||
private var waveformScrubbingNode: MediaPlayerScrubbingNode?
|
||||
@ -57,6 +60,24 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
self.descriptionNode.displaysAsynchronously = true
|
||||
self.descriptionNode.isUserInteractionEnabled = false
|
||||
|
||||
self.descriptionMeasuringNode = TextNode()
|
||||
|
||||
self.fetchingTextNode = ImmediateTextNode()
|
||||
self.fetchingTextNode.displaysAsynchronously = true
|
||||
self.fetchingTextNode.isUserInteractionEnabled = false
|
||||
self.fetchingTextNode.maximumNumberOfLines = 1
|
||||
self.fetchingTextNode.contentMode = .left
|
||||
self.fetchingTextNode.contentsScale = UIScreenScale
|
||||
self.fetchingTextNode.isHidden = true
|
||||
|
||||
self.fetchingCompactTextNode = ImmediateTextNode()
|
||||
self.fetchingCompactTextNode.displaysAsynchronously = true
|
||||
self.fetchingCompactTextNode.isUserInteractionEnabled = false
|
||||
self.fetchingCompactTextNode.maximumNumberOfLines = 1
|
||||
self.fetchingCompactTextNode.contentMode = .left
|
||||
self.fetchingCompactTextNode.contentsScale = UIScreenScale
|
||||
self.fetchingCompactTextNode.isHidden = true
|
||||
|
||||
self.waveformNode = AudioWaveformNode()
|
||||
self.waveformNode.isLayerBacked = true
|
||||
self.waveformForegroundNode = AudioWaveformNode()
|
||||
@ -70,6 +91,8 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.descriptionNode)
|
||||
self.addSubnode(self.fetchingTextNode)
|
||||
self.addSubnode(self.fetchingCompactTextNode)
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -147,19 +170,13 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
|
||||
let titleAsyncLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let descriptionAsyncLayout = TextNode.asyncLayout(self.descriptionNode)
|
||||
let descriptionMeasuringAsyncLayout = TextNode.asyncLayout(self.descriptionMeasuringNode)
|
||||
let statusLayout = self.dateAndStatusNode.asyncLayout()
|
||||
|
||||
let currentMessage = self.message
|
||||
let currentTheme = self.themeAndStrings?.0
|
||||
let currentResourceStatus = self.resourceStatus
|
||||
|
||||
return { account, presentationData, message, file, automaticDownload, incoming, isRecentActions, dateAndStatusType, constrainedSize in
|
||||
var updatedTheme: ChatPresentationThemeData?
|
||||
|
||||
if presentationData.theme != currentTheme {
|
||||
updatedTheme = presentationData.theme
|
||||
}
|
||||
|
||||
return (CGFloat.greatestFiniteMagnitude, { constrainedSize in
|
||||
var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
|
||||
var updatedStatusSignal: Signal<FileMediaResourceStatus, NoError>?
|
||||
@ -233,7 +250,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
sentViaBot = true
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: presentationData.dateTimeFormat, strings: presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings)
|
||||
|
||||
let (size, apply) = statusLayout(presentationData.theme, presentationData.strings, edited && !sentViaBot, viewCount, dateText, statusType, constrainedSize)
|
||||
statusSize = size
|
||||
@ -273,7 +290,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
if voice {
|
||||
isVoice = true
|
||||
let durationString = stringForDuration(audioDuration)
|
||||
candidateDescriptionString = NSAttributedString(string: durationString, font: durationFont, textColor:incoming ? bubbleTheme.incomingFileDurationColor : bubbleTheme.outgoingFileDurationColor)
|
||||
candidateDescriptionString = NSAttributedString(string: durationString, font: durationFont, textColor: incoming ? bubbleTheme.incomingFileDurationColor : bubbleTheme.outgoingFileDurationColor)
|
||||
if let waveform = waveform {
|
||||
waveform.withDataNoCopy { data in
|
||||
audioWaveform = AudioWaveform(bitstream: data, bitsPerSample: 5)
|
||||
@ -334,6 +351,16 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
let (titleLayout, titleApply) = titleAsyncLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (descriptionLayout, descriptionApply) = descriptionAsyncLayout(TextNodeLayoutArguments(attributedString: descriptionString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let fileSizeString: String
|
||||
if let _ = file.size {
|
||||
fileSizeString = "000.0 MB"
|
||||
} else {
|
||||
fileSizeString = ""
|
||||
}
|
||||
let (descriptionMeasuringLayout, descriptionMeasuringApply) = descriptionMeasuringAsyncLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(fileSizeString) / \(fileSizeString)", font: descriptionFont, textColor: .black), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let descriptionMaxWidth = max(descriptionLayout.size.width, descriptionMeasuringLayout.size.width)
|
||||
|
||||
let minVoiceWidth: CGFloat = 120.0
|
||||
let maxVoiceWidth = constrainedSize.width
|
||||
let maxVoiceLength: CGFloat = 30.0
|
||||
@ -341,12 +368,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
|
||||
var minLayoutWidth: CGFloat
|
||||
if hasThumbnail {
|
||||
minLayoutWidth = max(titleLayout.size.width, descriptionLayout.size.width) + 86.0
|
||||
minLayoutWidth = max(titleLayout.size.width, descriptionMaxWidth) + 86.0
|
||||
} else if isVoice {
|
||||
let calcDuration = max(minVoiceLength, min(maxVoiceLength, CGFloat(audioDuration)))
|
||||
minLayoutWidth = minVoiceWidth + (maxVoiceWidth - minVoiceWidth) * (calcDuration - minVoiceLength) / (maxVoiceLength - minVoiceLength)
|
||||
} else {
|
||||
minLayoutWidth = max(titleLayout.size.width, descriptionLayout.size.width) + 44.0 + 8.0
|
||||
minLayoutWidth = max(titleLayout.size.width, descriptionMaxWidth) + 44.0 + 8.0
|
||||
}
|
||||
|
||||
if let statusSize = statusSize {
|
||||
@ -445,9 +472,11 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
|
||||
let _ = titleApply()
|
||||
let _ = descriptionApply()
|
||||
let _ = descriptionMeasuringApply()
|
||||
|
||||
strongSelf.titleNode.frame = titleFrame
|
||||
strongSelf.descriptionNode.frame = descriptionFrame
|
||||
strongSelf.descriptionMeasuringNode.frame = CGRect(origin: CGPoint(), size: descriptionMeasuringLayout.size)
|
||||
|
||||
if let consumableContentIcon = consumableContentIcon {
|
||||
if strongSelf.consumableContentNode.supernode == nil {
|
||||
@ -625,6 +654,25 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
|
||||
let isSending = message.flags.isSending
|
||||
|
||||
var downloadingStrings: (String, String)?
|
||||
|
||||
if !isAudio {
|
||||
switch resourceStatus.mediaStatus {
|
||||
case let .fetchStatus(fetchStatus):
|
||||
switch fetchStatus {
|
||||
case let .Fetching(_, progress):
|
||||
if let size = file.size {
|
||||
let compactString = dataSizeString(Int(Float(size) * progress), forceDecimal: true)
|
||||
downloadingStrings = ("\(compactString) / \(dataSizeString(size, forceDecimal: true))", compactString)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isAudio && !isVoice && !isSending {
|
||||
let streamingStatusForegroundColor: UIColor = incoming ? bubbleTheme.incomingAccentControlColor : bubbleTheme.outgoingAccentControlColor
|
||||
let streamingStatusBackgroundColor: UIColor = incoming ? bubbleTheme.incomingMediaInactiveControlColor : bubbleTheme.outgoingMediaInactiveControlColor
|
||||
@ -743,6 +791,36 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if let (expandedString, compactString) = downloadingStrings {
|
||||
self.fetchingTextNode.attributedText = NSAttributedString(string: expandedString, font: descriptionFont, textColor: incoming ? bubbleTheme.incomingFileDurationColor : bubbleTheme.outgoingFileDurationColor)
|
||||
self.fetchingCompactTextNode.attributedText = NSAttributedString(string: compactString, font: descriptionFont, textColor: incoming ? bubbleTheme.incomingFileDurationColor : bubbleTheme.outgoingFileDurationColor)
|
||||
} else {
|
||||
self.fetchingTextNode.attributedText = nil
|
||||
self.fetchingCompactTextNode.attributedText = nil
|
||||
}
|
||||
|
||||
let maxFetchingStatusWidth = max(self.titleNode.frame.width, self.descriptionMeasuringNode.frame.width) + 2.0
|
||||
let fetchingInfo = self.fetchingTextNode.updateLayoutInfo(CGSize(width: maxFetchingStatusWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
let fetchingCompactSize = self.fetchingCompactTextNode.updateLayout(CGSize(width: maxFetchingStatusWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
if downloadingStrings != nil {
|
||||
self.descriptionNode.isHidden = true
|
||||
if fetchingInfo.truncated {
|
||||
self.fetchingTextNode.isHidden = true
|
||||
self.fetchingCompactTextNode.isHidden = false
|
||||
} else {
|
||||
self.fetchingTextNode.isHidden = false
|
||||
self.fetchingCompactTextNode.isHidden = true
|
||||
}
|
||||
} else {
|
||||
self.descriptionNode.isHidden = false
|
||||
self.fetchingTextNode.isHidden = true
|
||||
self.fetchingCompactTextNode.isHidden = true
|
||||
}
|
||||
|
||||
self.fetchingTextNode.frame = CGRect(origin: self.descriptionNode.frame.origin, size: fetchingInfo.size)
|
||||
self.fetchingCompactTextNode.frame = CGRect(origin: self.descriptionNode.frame.origin, size: fetchingCompactSize)
|
||||
}
|
||||
|
||||
static func asyncLayout(_ node: ChatMessageInteractiveFileNode?) -> (_ account: Account, _ presentationData: ChatPresentationData, _ message: Message, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, () -> ChatMessageInteractiveFileNode))) {
|
||||
|
||||
@ -244,7 +244,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
// sentViaBot = true
|
||||
// }
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings, format: .regular)
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular)
|
||||
|
||||
let maxDateAndStatusWidth: CGFloat
|
||||
if case .bubble = statusDisplayType {
|
||||
|
||||
@ -188,7 +188,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
switch position {
|
||||
|
||||
@ -119,7 +119,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
sentViaBot = true
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
switch position {
|
||||
|
||||
@ -8,6 +8,7 @@ import SwiftSignalKit
|
||||
public final class ChatMessageNotificationItem: NotificationItem {
|
||||
let account: Account
|
||||
let strings: PresentationStrings
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let messages: [Message]
|
||||
let tapAction: () -> Bool
|
||||
let expandAction: (@escaping () -> (ASDisplayNode?, () -> Void)) -> Void
|
||||
@ -16,9 +17,10 @@ public final class ChatMessageNotificationItem: NotificationItem {
|
||||
return messages.first?.id.peerId
|
||||
}
|
||||
|
||||
public init(account: Account, strings: PresentationStrings, messages: [Message], tapAction: @escaping () -> Bool, expandAction: @escaping (() -> (ASDisplayNode?, () -> Void)) -> Void) {
|
||||
public init(account: Account, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, messages: [Message], tapAction: @escaping () -> Bool, expandAction: @escaping (() -> (ASDisplayNode?, () -> Void)) -> Void) {
|
||||
self.account = account
|
||||
self.strings = strings
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.messages = messages
|
||||
self.tapAction = tapAction
|
||||
self.expandAction = expandAction
|
||||
@ -101,11 +103,11 @@ final class ChatMessageNotificationItemNode: NotificationItemNode {
|
||||
self.avatarNode.setPeer(account: item.account, peer: peer, emptyColor: presentationData.theme.list.mediaPlaceholderColor)
|
||||
|
||||
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
title = peer.displayTitle
|
||||
title = peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder)
|
||||
} else if let author = firstMessage.author, author.id != peer.id {
|
||||
title = author.displayTitle + "@" + peer.displayTitle
|
||||
title = author.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) + "@" + peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder)
|
||||
} else {
|
||||
title = peer.displayTitle
|
||||
title = peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder)
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +140,7 @@ final class ChatMessageNotificationItemNode: NotificationItemNode {
|
||||
if message.containsSecretMedia {
|
||||
imageDimensions = nil
|
||||
}
|
||||
messageText = descriptionStringForMessage(message, strings: item.strings, accountPeerId: item.account.peerId).0
|
||||
messageText = descriptionStringForMessage(message, strings: item.strings, nameDisplayOrder: item.nameDisplayOrder, accountPeerId: item.account.peerId).0
|
||||
} else if item.messages.count > 1, let peer = item.messages[0].peers[item.messages[0].id.peerId] {
|
||||
var displayAuthor = true
|
||||
if let channel = peer as? TelegramChannel {
|
||||
@ -155,15 +157,15 @@ final class ChatMessageNotificationItemNode: NotificationItemNode {
|
||||
if item.messages[0].forwardInfo != nil {
|
||||
if let author = item.messages[0].author, displayAuthor {
|
||||
title = nil
|
||||
messageText = presentationData.strings.CHAT_MESSAGE_FWDS(author.compactDisplayTitle, peer.displayTitle, "\(item.messages.count)").0
|
||||
messageText = presentationData.strings.CHAT_MESSAGE_FWDS(author.compactDisplayTitle, peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0
|
||||
} else {
|
||||
title = nil
|
||||
messageText = presentationData.strings.MESSAGE_FWDS(peer.displayTitle, "\(item.messages.count)").0
|
||||
messageText = presentationData.strings.MESSAGE_FWDS(peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0
|
||||
}
|
||||
} else if item.messages[0].groupingKey != nil {
|
||||
var kind = messageContentKind(item.messages[0], strings: presentationData.strings, accountPeerId: item.account.peerId).key
|
||||
var kind = messageContentKind(item.messages[0], strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: item.account.peerId).key
|
||||
for i in 1 ..< item.messages.count {
|
||||
let nextKind = messageContentKind(item.messages[i], strings: presentationData.strings, accountPeerId: item.account.peerId)
|
||||
let nextKind = messageContentKind(item.messages[i], strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: item.account.peerId)
|
||||
if kind != nextKind.key {
|
||||
kind = .text
|
||||
break
|
||||
@ -191,16 +193,16 @@ final class ChatMessageNotificationItemNode: NotificationItemNode {
|
||||
} else if isGroup, let author = item.messages[0].author {
|
||||
switch kind {
|
||||
case .image:
|
||||
messageText = presentationData.strings.CHAT_MESSAGE_PHOTOS(author.compactDisplayTitle, peer.displayTitle, "\(item.messages.count)").0
|
||||
messageText = presentationData.strings.CHAT_MESSAGE_PHOTOS(author.compactDisplayTitle, peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0
|
||||
default:
|
||||
messageText = presentationData.strings.CHAT_MESSAGES(author.compactDisplayTitle, peer.displayTitle, "\(item.messages.count)").0
|
||||
messageText = presentationData.strings.CHAT_MESSAGES(author.compactDisplayTitle, peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0
|
||||
}
|
||||
} else {
|
||||
switch kind {
|
||||
case .image:
|
||||
messageText = presentationData.strings.MESSAGE_PHOTOS(peer.displayTitle, "\(item.messages.count)").0
|
||||
messageText = presentationData.strings.MESSAGE_PHOTOS(peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0
|
||||
default:
|
||||
messageText = presentationData.strings.MESSAGES(peer.displayTitle, "\(item.messages.count)").0
|
||||
messageText = presentationData.strings.MESSAGES(peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -39,35 +39,35 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
self.contentNode.addSubnode(self.lineNode)
|
||||
}
|
||||
|
||||
class func asyncLayout(_ maybeNode: ChatMessageReplyInfoNode?) -> (_ theme: ChatPresentationThemeData, _ strings: PresentationStrings, _ account: Account, _ type: ChatMessageReplyInfoType, _ message: Message, _ constrainedSize: CGSize) -> (CGSize, () -> ChatMessageReplyInfoNode) {
|
||||
class func asyncLayout(_ maybeNode: ChatMessageReplyInfoNode?) -> (_ theme: ChatPresentationData, _ strings: PresentationStrings, _ account: Account, _ type: ChatMessageReplyInfoType, _ message: Message, _ constrainedSize: CGSize) -> (CGSize, () -> ChatMessageReplyInfoNode) {
|
||||
|
||||
let titleNodeLayout = TextNode.asyncLayout(maybeNode?.titleNode)
|
||||
let textNodeLayout = TextNode.asyncLayout(maybeNode?.textNode)
|
||||
let imageNodeLayout = TransformImageNode.asyncLayout(maybeNode?.imageNode)
|
||||
let previousMediaReference = maybeNode?.previousMediaReference
|
||||
|
||||
return { theme, strings, account, type, message, constrainedSize in
|
||||
let titleString = message.author?.displayTitle(strings: strings) ?? strings.User_DeletedAccount
|
||||
let (textString, isMedia) = descriptionStringForMessage(message, strings: strings, accountPeerId: account.peerId)
|
||||
return { presentationData, strings, account, type, message, constrainedSize in
|
||||
let titleString = message.author?.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) ?? strings.User_DeletedAccount
|
||||
let (textString, isMedia) = descriptionStringForMessage(message, strings: strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: account.peerId)
|
||||
|
||||
let placeholderColor: UIColor = message.effectivelyIncoming(account.peerId) ? theme.theme.chat.bubble.incomingMediaPlaceholderColor : theme.theme.chat.bubble.outgoingMediaPlaceholderColor
|
||||
let placeholderColor: UIColor = message.effectivelyIncoming(account.peerId) ? presentationData.theme.theme.chat.bubble.incomingMediaPlaceholderColor : presentationData.theme.theme.chat.bubble.outgoingMediaPlaceholderColor
|
||||
let titleColor: UIColor
|
||||
let lineImage: UIImage?
|
||||
let textColor: UIColor
|
||||
|
||||
switch type {
|
||||
case let .bubble(incoming):
|
||||
titleColor = incoming ? theme.theme.chat.bubble.incomingAccentTextColor : theme.theme.chat.bubble.outgoingAccentTextColor
|
||||
lineImage = incoming ? PresentationResourcesChat.chatBubbleVerticalLineIncomingImage(theme.theme) : PresentationResourcesChat.chatBubbleVerticalLineOutgoingImage(theme.theme)
|
||||
titleColor = incoming ? presentationData.theme.theme.chat.bubble.incomingAccentTextColor : presentationData.theme.theme.chat.bubble.outgoingAccentTextColor
|
||||
lineImage = incoming ? PresentationResourcesChat.chatBubbleVerticalLineIncomingImage(presentationData.theme.theme) : PresentationResourcesChat.chatBubbleVerticalLineOutgoingImage(presentationData.theme.theme)
|
||||
if isMedia {
|
||||
textColor = incoming ? theme.theme.chat.bubble.incomingSecondaryTextColor : theme.theme.chat.bubble.outgoingSecondaryTextColor
|
||||
textColor = incoming ? presentationData.theme.theme.chat.bubble.incomingSecondaryTextColor : presentationData.theme.theme.chat.bubble.outgoingSecondaryTextColor
|
||||
} else {
|
||||
textColor = incoming ? theme.theme.chat.bubble.incomingPrimaryTextColor : theme.theme.chat.bubble.outgoingPrimaryTextColor
|
||||
textColor = incoming ? presentationData.theme.theme.chat.bubble.incomingPrimaryTextColor : presentationData.theme.theme.chat.bubble.outgoingPrimaryTextColor
|
||||
}
|
||||
case .standalone:
|
||||
let serviceColor = serviceMessageColorComponents(theme: theme.theme, wallpaper: theme.wallpaper)
|
||||
let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
|
||||
titleColor = serviceColor.primaryText
|
||||
lineImage = PresentationResourcesChat.chatServiceVerticalLineImage(theme.theme)
|
||||
lineImage = PresentationResourcesChat.chatServiceVerticalLineImage(presentationData.theme.theme)
|
||||
textColor = titleColor
|
||||
}
|
||||
|
||||
|
||||
@ -234,7 +234,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
// }
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings, format: .minimal)
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .minimal)
|
||||
|
||||
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.presentationData.theme, item.presentationData.strings, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
@ -267,7 +267,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty {
|
||||
replyMarkup = attribute
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
sentViaBot = true
|
||||
}
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings)
|
||||
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
switch position {
|
||||
|
||||
@ -115,7 +115,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.currentMessage = interfaceState.pinnedMessage
|
||||
|
||||
if let currentMessage = currentMessage, let currentLayout = self.currentLayout {
|
||||
self.enqueueTransition(width: currentLayout.0, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: .immediate, message: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, accountPeerId: self.account.peerId, firstTime: previousMessageWasNil)
|
||||
self.enqueueTransition(width: currentLayout.0, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: .immediate, message: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: self.account.peerId, firstTime: previousMessageWasNil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,14 +134,14 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
self.currentLayout = (width, leftInset, rightInset)
|
||||
|
||||
if let currentMessage = self.currentMessage {
|
||||
self.enqueueTransition(width: width, leftInset: leftInset, rightInset: rightInset, transition: .immediate, message: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, accountPeerId: interfaceState.accountPeerId, firstTime: true)
|
||||
self.enqueueTransition(width: width, leftInset: leftInset, rightInset: rightInset, transition: .immediate, message: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: interfaceState.accountPeerId, firstTime: true)
|
||||
}
|
||||
}
|
||||
|
||||
return panelHeight
|
||||
}
|
||||
|
||||
private func enqueueTransition(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, message: Message, theme: PresentationTheme, strings: PresentationStrings, accountPeerId: PeerId, firstTime: Bool) {
|
||||
private func enqueueTransition(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, message: Message, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId, firstTime: Bool) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeTextLayout = TextNode.asyncLayout(self.textNode)
|
||||
let imageNodeLayout = self.imageNode.asyncLayout()
|
||||
@ -214,9 +214,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: strings.Conversation_PinnedMessage, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0)))
|
||||
|
||||
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: descriptionStringForMessage(message, strings: strings, accountPeerId: accountPeerId).0, font: Font.regular(15.0), textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0)))
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: descriptionStringForMessage(message, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: accountPeerId).0, font: Font.regular(15.0), textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0)))
|
||||
|
||||
Queue.mainQueue().async {
|
||||
if let strongSelf = self {
|
||||
|
||||
@ -61,6 +61,7 @@ public final class ChatPresentationData {
|
||||
let fontSize: PresentationFontSize
|
||||
let strings: PresentationStrings
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let disableAnimations: Bool
|
||||
|
||||
let messageFont: UIFont
|
||||
@ -68,11 +69,12 @@ public final class ChatPresentationData {
|
||||
let messageItalicFont: UIFont
|
||||
let messageFixedFont: UIFont
|
||||
|
||||
init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, disableAnimations: Bool) {
|
||||
init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
|
||||
self.theme = theme
|
||||
self.fontSize = fontSize
|
||||
self.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.disableAnimations = disableAnimations
|
||||
|
||||
let baseFontSize = fontSize.baseDisplaySize
|
||||
|
||||
@ -337,11 +337,12 @@ final class ChatPresentationInterfaceState: Equatable {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let fontSize: PresentationFontSize
|
||||
let accountPeerId: PeerId
|
||||
let mode: ChatControllerPresentationMode
|
||||
|
||||
init(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode, chatLocation: ChatLocation) {
|
||||
init(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode, chatLocation: ChatLocation) {
|
||||
self.interfaceState = ChatInterfaceState()
|
||||
self.inputTextPanelState = ChatTextInputPanelState()
|
||||
self.editMessageState = nil
|
||||
@ -373,12 +374,13 @@ final class ChatPresentationInterfaceState: Equatable {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.fontSize = fontSize
|
||||
self.accountPeerId = accountPeerId
|
||||
self.mode = mode
|
||||
}
|
||||
|
||||
init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, isContact: Bool, hasBots: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: Message?, peerIsBlocked: Bool, peerIsMuted: Bool, canReportPeer: Bool, callsAvailable: Bool, callsPrivate: Bool, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode) {
|
||||
init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, isContact: Bool, hasBots: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: Message?, peerIsBlocked: Bool, peerIsMuted: Bool, canReportPeer: Bool, callsAvailable: Bool, callsPrivate: Bool, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode) {
|
||||
self.interfaceState = interfaceState
|
||||
self.chatLocation = chatLocation
|
||||
self.renderedPeer = renderedPeer
|
||||
@ -410,6 +412,7 @@ final class ChatPresentationInterfaceState: Equatable {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.fontSize = fontSize
|
||||
self.accountPeerId = accountPeerId
|
||||
self.mode = mode
|
||||
@ -575,27 +578,27 @@ final class ChatPresentationInterfaceState: Equatable {
|
||||
}
|
||||
|
||||
func updatedInterfaceState(_ f: (ChatInterfaceState) -> ChatInterfaceState) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedPeer(_ f: (RenderedPeer?) -> RenderedPeer?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedIsNotAccessible(_ isNotAccessible: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedExplicitelyCanPinMessages(_ explicitelyCanPinMessages: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedIsContact(_ isContact: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedHasBots(_ hasBots: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedInputQueryResult(queryKind: ChatPresentationInputQueryKind, _ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState {
|
||||
@ -606,103 +609,103 @@ final class ChatPresentationInterfaceState: Equatable {
|
||||
} else {
|
||||
inputQueryResults.removeValue(forKey: queryKind)
|
||||
}
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedInputTextPanelState(_ f: (ChatTextInputPanelState) -> ChatTextInputPanelState) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: f(self.inputTextPanelState), editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: f(self.inputTextPanelState), editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedEditMessageState(_ editMessageState: ChatEditInterfaceMessageState?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedRecordedMediaPreview(_ recordedMediaPreview: ChatRecordedMediaPreview?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedInputMode(_ f: (ChatInputMode) -> ChatInputMode) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: f(self.inputMode), titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: f(self.inputMode), titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedTitlePanelContext(_ f: ([ChatTitlePanelContext]) -> [ChatTitlePanelContext]) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: f(self.titlePanelContexts), keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: f(self.titlePanelContexts), keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedKeyboardButtonsMessage(_ message: Message?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: message, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: message, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedPinnedMessage(_ pinnedMessage: Message?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedPeerIsBlocked(_ peerIsBlocked: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedPeerIsMuted(_ peerIsMuted: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedCanReportPeer(_ canReportPeer: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedCallsAvailable(_ callsAvailable: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedCallsPrivate(_ callsPrivate: Bool) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedBotStartPayload(_ botStartPayload: String?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedChatHistoryState(_ chatHistoryState: ChatHistoryNodeHistoryState?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedUrlPreview(_ urlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedEditingUrlPreview(_ editingUrlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedSearch(_ search: ChatSearchData?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedSearchQuerySuggestionResult(_ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: f(self.searchQuerySuggestionResult), chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: f(self.searchQuerySuggestionResult), chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedMode(_ mode: ChatControllerPresentationMode) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: mode)
|
||||
}
|
||||
|
||||
func updatedTheme(_ theme: PresentationTheme) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedStrings(_ strings: PresentationStrings) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedDateTimeFormat(_ dateTimeFormat: PresentationDateTimeFormat) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
|
||||
func updatedChatWallpaper(_ chatWallpaper: TelegramWallpaper) -> ChatPresentationInterfaceState {
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -97,7 +97,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
|
||||
self.state = ChatRecentActionsControllerState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, fontSize: self.presentationData.fontSize)
|
||||
|
||||
self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, disableAnimations: self.presentationData.disableAnimations))
|
||||
self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations))
|
||||
|
||||
self.context = ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: self.peer.id)
|
||||
|
||||
|
||||
@ -64,7 +64,7 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry {
|
||||
|
||||
case adminsTitle(PresentationTheme, String)
|
||||
case allAdmins(PresentationTheme, String, Bool)
|
||||
case adminPeerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Int32, RenderedChannelParticipant, Bool)
|
||||
case adminPeerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Int32, RenderedChannelParticipant, Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -87,7 +87,7 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry {
|
||||
return .index(200)
|
||||
case .allAdmins:
|
||||
return .index(201)
|
||||
case let .adminPeerItem(_, _, _, _, participant, _):
|
||||
case let .adminPeerItem(_, _, _, _, _, participant, _):
|
||||
return .peer(participant.peer.id)
|
||||
}
|
||||
}
|
||||
@ -124,8 +124,8 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .adminPeerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsIndex, lhsParticipant, lhsChecked):
|
||||
if case let .adminPeerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsIndex, rhsParticipant, rhsChecked) = rhs {
|
||||
case let .adminPeerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameDisplayOrder, lhsIndex, lhsParticipant, lhsChecked):
|
||||
if case let .adminPeerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameDisplayOrder, rhsIndex, rhsParticipant, rhsChecked) = rhs {
|
||||
if lhsTheme !== rhsTheme {
|
||||
return false
|
||||
}
|
||||
@ -135,6 +135,9 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameDisplayOrder != rhsNameDisplayOrder {
|
||||
return false
|
||||
}
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -185,9 +188,9 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case let .adminPeerItem(_, _, _, lhsIndex, _, _):
|
||||
case let .adminPeerItem(_, _, _, _, lhsIndex, _, _):
|
||||
switch rhs {
|
||||
case let .adminPeerItem(_, _, _, rhsIndex, _, _):
|
||||
case let .adminPeerItem(_, _, _, _, rhsIndex, _, _):
|
||||
return lhsIndex < rhsIndex
|
||||
default:
|
||||
return false
|
||||
@ -213,7 +216,7 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry {
|
||||
return ItemListSwitchItem(theme: theme, title: text, value: value, enabled: true, sectionId: self.section, style: .blocks, updated: { _ in
|
||||
arguments.toggleAllAdmins()
|
||||
})
|
||||
case let .adminPeerItem(theme, strings, dateTimeFormat, _, participant, checked):
|
||||
case let .adminPeerItem(theme, strings, dateTimeFormat, nameDisplayOrder, _, participant, checked):
|
||||
let peerText: String
|
||||
switch participant.participant {
|
||||
case .creator:
|
||||
@ -221,7 +224,7 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry {
|
||||
case .member:
|
||||
peerText = strings.ChatAdmins_AdminLabel.capitalized
|
||||
}
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: participant.peer, presence: nil, text: .text(peerText), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: checked, style: .check), enabled: true, sectionId: self.section, action: {
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: participant.peer, presence: nil, text: .text(peerText), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: checked, style: .check), enabled: true, sectionId: self.section, action: {
|
||||
arguments.toggleAdmin(participant.peer.id)
|
||||
}, setPeerIdWithRevealedOptions: { _, _ in
|
||||
}, removePeer: { _ in })
|
||||
@ -345,7 +348,7 @@ private func channelRecentActionsFilterControllerEntries(presentationData: Prese
|
||||
} else {
|
||||
adminSelected = true
|
||||
}
|
||||
entries.append(.adminPeerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, index, participant, adminSelected))
|
||||
entries.append(.adminPeerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index, participant, adminSelected))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,6 +78,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
private var theme: PresentationTheme
|
||||
private var strings: PresentationStrings
|
||||
private var dateTimeFormat: PresentationDateTimeFormat
|
||||
private var nameDisplayOrder: PresentationPersonNameOrder
|
||||
|
||||
private let contentContainer: ASDisplayNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
@ -255,7 +256,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
if peerView.peerId == self.account.peerId {
|
||||
string = NSAttributedString(string: self.strings.Conversation_SavedMessages, font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
||||
} else {
|
||||
string = NSAttributedString(string: peer.displayTitle(strings: self.strings), font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
||||
string = NSAttributedString(string: peer.displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder), font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor)
|
||||
}
|
||||
}
|
||||
if peerView.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
@ -446,11 +447,12 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
}
|
||||
}
|
||||
|
||||
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) {
|
||||
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) {
|
||||
self.account = account
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
|
||||
self.contentContainer = ASDisplayNode()
|
||||
|
||||
|
||||
@ -304,44 +304,64 @@ private extension PeerIndexNameRepresentation {
|
||||
func isLessThan(other: PeerIndexNameRepresentation, ordering: PresentationPersonNameOrder) -> ComparisonResult {
|
||||
switch self {
|
||||
case let .title(lhsTitle, _):
|
||||
let rhsString: String
|
||||
switch other {
|
||||
case let .title(title, _):
|
||||
return lhsTitle.compare(title)
|
||||
case let .personName(_, last, _, _):
|
||||
let lastResult = lhsTitle.compare(last)
|
||||
if lastResult == .orderedSame {
|
||||
return .orderedAscending
|
||||
} else {
|
||||
return lastResult
|
||||
}
|
||||
}
|
||||
case let .personName(lhsFirst, lhsLast, _, _):
|
||||
switch other {
|
||||
case let .title(title, _):
|
||||
let lastResult = lhsFirst.compare(title)
|
||||
if lastResult == .orderedSame {
|
||||
return .orderedDescending
|
||||
} else {
|
||||
return lastResult
|
||||
}
|
||||
rhsString = title
|
||||
case let .personName(first, last, _, _):
|
||||
switch ordering {
|
||||
case .firstLast:
|
||||
let firstResult = lhsFirst.compare(first)
|
||||
if firstResult == .orderedSame {
|
||||
return lhsLast.compare(last)
|
||||
if first.isEmpty {
|
||||
rhsString = last
|
||||
} else {
|
||||
return firstResult
|
||||
rhsString = first + last
|
||||
}
|
||||
case .lastFirst:
|
||||
let lastResult = lhsLast.compare(last)
|
||||
if lastResult == .orderedSame {
|
||||
return lhsFirst.compare(first)
|
||||
if last.isEmpty {
|
||||
rhsString = first
|
||||
} else {
|
||||
return lastResult
|
||||
rhsString = last + first
|
||||
}
|
||||
}
|
||||
}
|
||||
return lhsTitle.caseInsensitiveCompare(rhsString)
|
||||
case let .personName(lhsFirst, lhsLast, _, _):
|
||||
let lhsString: String
|
||||
switch ordering {
|
||||
case .firstLast:
|
||||
if lhsFirst.isEmpty {
|
||||
lhsString = lhsLast
|
||||
} else {
|
||||
lhsString = lhsFirst + lhsLast
|
||||
}
|
||||
case .lastFirst:
|
||||
if lhsLast.isEmpty {
|
||||
lhsString = lhsFirst
|
||||
} else {
|
||||
lhsString = lhsLast + lhsFirst
|
||||
}
|
||||
}
|
||||
let rhsString: String
|
||||
switch other {
|
||||
case let .title(title, _):
|
||||
rhsString = title
|
||||
case let .personName(first, last, _, _):
|
||||
switch ordering {
|
||||
case .firstLast:
|
||||
if first.isEmpty {
|
||||
rhsString = last
|
||||
} else {
|
||||
rhsString = first + last
|
||||
}
|
||||
case .lastFirst:
|
||||
if last.isEmpty {
|
||||
rhsString = first
|
||||
} else {
|
||||
rhsString = last + first
|
||||
}
|
||||
}
|
||||
}
|
||||
return lhsString.caseInsensitiveCompare(rhsString)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -424,21 +444,21 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer]
|
||||
var indexHeader: unichar = 35
|
||||
switch peer.indexName {
|
||||
case let .title(title, _):
|
||||
if let c = title.utf16.first {
|
||||
if let c = title.uppercased().utf16.first {
|
||||
indexHeader = c
|
||||
}
|
||||
case let .personName(first, last, _, _):
|
||||
switch sortOrder {
|
||||
case .firstLast:
|
||||
if let c = first.utf16.first {
|
||||
if let c = first.uppercased().utf16.first {
|
||||
indexHeader = c
|
||||
} else if let c = last.utf16.first {
|
||||
} else if let c = last.uppercased().utf16.first {
|
||||
indexHeader = c
|
||||
}
|
||||
case .lastFirst:
|
||||
if let c = last.utf16.first {
|
||||
if let c = last.uppercased().utf16.first {
|
||||
indexHeader = c
|
||||
} else if let c = first.utf16.first {
|
||||
} else if let c = first.uppercased().utf16.first {
|
||||
indexHeader = c
|
||||
}
|
||||
}
|
||||
@ -511,20 +531,45 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer]
|
||||
return entries
|
||||
}
|
||||
|
||||
private func preparedContactListNodeTransition(account: Account, from fromEntries: [ContactListNodeEntry], to toEntries: [ContactListNodeEntry], interaction: ContactListNodeInteraction, firstTime: Bool, isEmpty: Bool, animated: Bool) -> ContactsListNodeTransition {
|
||||
private func preparedContactListNodeTransition(account: Account, from fromEntries: [ContactListNodeEntry], to toEntries: [ContactListNodeEntry], interaction: ContactListNodeInteraction, firstTime: Bool, isEmpty: Bool, generateIndexSections: Bool, animated: Bool) -> ContactsListNodeTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) }
|
||||
|
||||
return ContactsListNodeTransition(deletions: deletions, insertions: insertions, updates: updates, firstTime: firstTime, isEmpty: isEmpty, animated: animated)
|
||||
var indexSections: [String] = []
|
||||
if generateIndexSections {
|
||||
var existingSections = Set<unichar>()
|
||||
for entry in toEntries {
|
||||
switch entry {
|
||||
case .search:
|
||||
//indexSections.apend(CollectionIndexNode.searchIndex)
|
||||
break
|
||||
case let .peer(_, _, _, header, _, _, _, _, _, _, _):
|
||||
if let header = header as? ContactListNameIndexHeader {
|
||||
if !existingSections.contains(header.letter) {
|
||||
existingSections.insert(header.letter)
|
||||
if let scalar = UnicodeScalar(header.letter) {
|
||||
let title = "\(Character(scalar))"
|
||||
indexSections.append(title)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ContactsListNodeTransition(deletions: deletions, insertions: insertions, updates: updates, indexSections: indexSections, firstTime: firstTime, isEmpty: isEmpty, animated: animated)
|
||||
}
|
||||
|
||||
private struct ContactsListNodeTransition {
|
||||
let deletions: [ListViewDeleteItem]
|
||||
let insertions: [ListViewInsertItem]
|
||||
let updates: [ListViewUpdateItem]
|
||||
let indexSections: [String]
|
||||
let firstTime: Bool
|
||||
let isEmpty: Bool
|
||||
let animated: Bool
|
||||
@ -584,9 +629,11 @@ final class ContactListNode: ASDisplayNode {
|
||||
private let filters: [ContactListFilter]
|
||||
|
||||
let listNode: ListView
|
||||
private var indexNode: CollectionIndexNode?
|
||||
private var indexSections: [String]?
|
||||
|
||||
private var queuedTransitions: [ContactsListNodeTransition] = []
|
||||
private var hasValidLayout = false
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private var _ready = ValuePromise<Bool>()
|
||||
var ready: Signal<Bool, NoError> {
|
||||
@ -647,6 +694,14 @@ final class ContactListNode: ASDisplayNode {
|
||||
self.listNode = ListView()
|
||||
self.listNode.dynamicBounceEnabled = !self.presentationData.disableAnimations
|
||||
|
||||
var generateSections = false
|
||||
if case .natural = presentation {
|
||||
generateSections = true
|
||||
self.indexNode = CollectionIndexNode()
|
||||
} else {
|
||||
self.indexNode = nil
|
||||
}
|
||||
|
||||
self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings, self.presentationData.dateTimeFormat, self.presentationData.nameSortOrder, self.presentationData.nameDisplayOrder, self.presentationData.disableAnimations))
|
||||
|
||||
self.authorizationPromise = Promise(AccessType.allowed)
|
||||
@ -664,12 +719,15 @@ final class ContactListNode: ASDisplayNode {
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
|
||||
self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
|
||||
if self.indexNode == nil {
|
||||
self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
|
||||
}
|
||||
|
||||
self.selectionStateValue = selectionState
|
||||
self.selectionStatePromise.set(.single(selectionState))
|
||||
|
||||
self.addSubnode(self.listNode)
|
||||
self.indexNode.flatMap(self.addSubnode)
|
||||
self.addSubnode(self.authorizationNode)
|
||||
|
||||
let processingQueue = Queue()
|
||||
@ -683,6 +741,37 @@ final class ContactListNode: ASDisplayNode {
|
||||
self?.openPeer?(peer)
|
||||
})
|
||||
|
||||
self.indexNode?.indexSelected = { [weak self] section in
|
||||
guard let strongSelf = self, let entries = previousEntries.with({ $0 }) else {
|
||||
return
|
||||
}
|
||||
var index = 0
|
||||
var peerIndex = 0
|
||||
loop: for entry in entries {
|
||||
switch entry {
|
||||
case .search:
|
||||
if section == CollectionIndexNode.searchIndex {
|
||||
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.PreferSynchronousDrawing, .PreferSynchronousResourceLoading], scrollToItem: ListViewScrollToItem(index: index, position: .top(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down), additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
break loop
|
||||
}
|
||||
case let .peer(_, _, _, header, _, _, _, _, _, _, _):
|
||||
if let header = header as? ContactListNameIndexHeader {
|
||||
if let scalar = UnicodeScalar(header.letter) {
|
||||
let title = "\(Character(scalar))"
|
||||
if title == section {
|
||||
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.PreferSynchronousDrawing, .PreferSynchronousResourceLoading], scrollToItem: ListViewScrollToItem(index: peerIndex == 0 ? 0 : index, position: .top(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down), additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
peerIndex += 1
|
||||
default:
|
||||
break
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
let account = self.account
|
||||
var firstTime: Int32 = 1
|
||||
let selectionStateSignal = self.selectionStatePromise.get()
|
||||
@ -795,7 +884,7 @@ final class ContactListNode: ASDisplayNode {
|
||||
|
||||
let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: localPeersAndStatuses.1, presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, dateTimeFormat: themeAndStrings.2, sortOrder: themeAndStrings.3, displayOrder: themeAndStrings.4, disabledPeerIds: disabledPeerIds, authorizationStatus: .allowed)
|
||||
let previous = previousEntries.swap(entries)
|
||||
return .single(preparedContactListNodeTransition(account: account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: false, animated: false))
|
||||
return .single(preparedContactListNodeTransition(account: account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: false, generateIndexSections: generateSections, animated: false))
|
||||
}
|
||||
|
||||
if OSAtomicCompareAndSwap32(1, 0, &firstTime) {
|
||||
@ -844,7 +933,7 @@ final class ContactListNode: ASDisplayNode {
|
||||
} else {
|
||||
animated = false
|
||||
}
|
||||
return .single(preparedContactListNodeTransition(account: account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: isEmpty, animated: animated))
|
||||
return .single(preparedContactListNodeTransition(account: account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: isEmpty, generateIndexSections: generateSections, animated: animated))
|
||||
}
|
||||
|
||||
if OSAtomicCompareAndSwap32(1, 0, &firstTime) {
|
||||
@ -953,6 +1042,9 @@ final class ContactListNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
let hadValidLayout = self.validLayout != nil
|
||||
self.validLayout = layout
|
||||
|
||||
var insets = layout.insets(options: [.input])
|
||||
insets.left += layout.safeInsets.left
|
||||
insets.right += layout.safeInsets.right
|
||||
@ -985,14 +1077,18 @@ final class ContactListNode: ASDisplayNode {
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve)
|
||||
|
||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
if let indexNode = self.indexNode, let indexSections = self.indexSections {
|
||||
let indexNodeFrame = CGRect(origin: CGPoint(x: layout.size.width - insets.right - 20.0, y: insets.top), size: CGSize(width: 20.0, height: layout.size.height - insets.top - insets.bottom))
|
||||
transition.updateFrame(node: indexNode, frame: indexNodeFrame)
|
||||
indexNode.update(size: indexNodeFrame.size, color: self.presentationData.theme.list.itemAccentColor, sections: indexSections, transition: transition)
|
||||
}
|
||||
|
||||
//if let authorizationNode = self.authorizationNode {
|
||||
authorizationNode.updateLayout(size: layout.size, insets: insets, transition: transition)
|
||||
transition.updateFrame(node: authorizationNode, frame: self.bounds)
|
||||
//}
|
||||
|
||||
if !self.hasValidLayout {
|
||||
self.hasValidLayout = true
|
||||
if !hadValidLayout {
|
||||
self.dequeueTransitions()
|
||||
}
|
||||
}
|
||||
@ -1000,13 +1096,13 @@ final class ContactListNode: ASDisplayNode {
|
||||
private func enqueueTransition(_ transition: ContactsListNodeTransition) {
|
||||
self.queuedTransitions.append(transition)
|
||||
|
||||
if self.hasValidLayout {
|
||||
if self.validLayout != nil {
|
||||
self.dequeueTransitions()
|
||||
}
|
||||
}
|
||||
|
||||
private func dequeueTransitions() {
|
||||
if self.hasValidLayout {
|
||||
if self.validLayout != nil {
|
||||
while !self.queuedTransitions.isEmpty {
|
||||
let transition = self.queuedTransitions.removeFirst()
|
||||
|
||||
@ -1019,6 +1115,15 @@ final class ContactListNode: ASDisplayNode {
|
||||
options.insert(.AnimateCrossfade)
|
||||
}
|
||||
}
|
||||
if let indexNode = self.indexNode, let layout = self.validLayout {
|
||||
self.indexSections = transition.indexSections
|
||||
|
||||
var insets = layout.insets(options: [.input])
|
||||
insets.left += layout.safeInsets.left
|
||||
insets.right += layout.safeInsets.right
|
||||
|
||||
indexNode.update(size: CGSize(width: 20.0, height: layout.size.height - insets.top - insets.bottom), color: self.presentationData.theme.list.itemAccentColor, sections: transition.indexSections, transition: .immediate)
|
||||
}
|
||||
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateOpaqueState: nil, completion: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
if !strongSelf.didSetReady {
|
||||
|
||||
@ -4,21 +4,25 @@ import SwiftSignalKit
|
||||
|
||||
public struct ContactSynchronizationSettings: Equatable, PreferencesEntry {
|
||||
public var synchronizeDeviceContacts: Bool
|
||||
public var nameDisplayOrder: PresentationPersonNameOrder
|
||||
|
||||
public static var defaultSettings: ContactSynchronizationSettings {
|
||||
return ContactSynchronizationSettings(synchronizeDeviceContacts: true)
|
||||
return ContactSynchronizationSettings(synchronizeDeviceContacts: true, nameDisplayOrder: .firstLast)
|
||||
}
|
||||
|
||||
public init(synchronizeDeviceContacts: Bool) {
|
||||
public init(synchronizeDeviceContacts: Bool, nameDisplayOrder: PresentationPersonNameOrder) {
|
||||
self.synchronizeDeviceContacts = synchronizeDeviceContacts
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.synchronizeDeviceContacts = decoder.decodeInt32ForKey("synchronizeDeviceContacts", orElse: 0) != 0
|
||||
self.nameDisplayOrder = PresentationPersonNameOrder(rawValue: decoder.decodeInt32ForKey("nameDisplayOrder", orElse: 0)) ?? .firstLast
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeInt32(self.synchronizeDeviceContacts ? 1 : 0, forKey: "synchronizeDeviceContacts")
|
||||
encoder.encodeInt32(self.nameDisplayOrder.rawValue, forKey: "synchronizeDeviceContacts")
|
||||
}
|
||||
|
||||
public func isEqual(to: PreferencesEntry) -> Bool {
|
||||
@ -30,7 +34,7 @@ public struct ContactSynchronizationSettings: Equatable, PreferencesEntry {
|
||||
}
|
||||
}
|
||||
|
||||
func updateContactSynchronizationSettingsInteractively(postbox: Postbox, _ f: @escaping (ContactSynchronizationSettings) -> ContactSynchronizationSettings) -> Signal<Void, NoError> {
|
||||
func updateContactSettingsInteractively(postbox: Postbox, _ f: @escaping (ContactSynchronizationSettings) -> ContactSynchronizationSettings) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.contactSynchronizationSettings, { entry in
|
||||
let currentSettings: ContactSynchronizationSettings
|
||||
|
||||
@ -115,6 +115,7 @@ class ContactsPeerItem: ListViewItem {
|
||||
let enabled: Bool
|
||||
let selection: ContactsPeerItemSelection
|
||||
let editing: ContactsPeerItemEditing
|
||||
let options: [ItemListPeerItemRevealOption]
|
||||
let action: (ContactsPeerItemPeer) -> Void
|
||||
let setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)?
|
||||
let deletePeer: ((PeerId) -> Void)?
|
||||
@ -125,7 +126,7 @@ class ContactsPeerItem: ListViewItem {
|
||||
|
||||
let header: ListViewItemHeader?
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, account: Account, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil) {
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, account: Account, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.sortOrder = sortOrder
|
||||
@ -138,6 +139,7 @@ class ContactsPeerItem: ListViewItem {
|
||||
self.enabled = enabled
|
||||
self.selection = selection
|
||||
self.editing = editing
|
||||
self.options = options
|
||||
self.action = action
|
||||
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
||||
self.deletePeer = deletePeer
|
||||
@ -451,9 +453,16 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
titleAttributedString = NSAttributedString(string: item.strings.DialogList_SavedMessages, font: titleBoldFont, textColor: textColor)
|
||||
} else if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty {
|
||||
let string = NSMutableAttributedString()
|
||||
string.append(NSAttributedString(string: firstName, font: item.sortOrder == .firstLast ? titleBoldFont : titleFont, textColor: textColor))
|
||||
string.append(NSAttributedString(string: " ", font: titleFont, textColor: textColor))
|
||||
string.append(NSAttributedString(string: lastName, font: item.sortOrder == .firstLast ? titleFont : titleBoldFont, textColor: textColor))
|
||||
switch item.displayOrder {
|
||||
case .firstLast:
|
||||
string.append(NSAttributedString(string: firstName, font: item.sortOrder == .firstLast ? titleBoldFont : titleFont, textColor: textColor))
|
||||
string.append(NSAttributedString(string: " ", font: titleFont, textColor: textColor))
|
||||
string.append(NSAttributedString(string: lastName, font: item.sortOrder == .firstLast ? titleFont : titleBoldFont, textColor: textColor))
|
||||
case .lastFirst:
|
||||
string.append(NSAttributedString(string: lastName, font: item.sortOrder == .firstLast ? titleFont : titleBoldFont, textColor: textColor))
|
||||
string.append(NSAttributedString(string: " ", font: titleFont, textColor: textColor))
|
||||
string.append(NSAttributedString(string: firstName, font: item.sortOrder == .firstLast ? titleBoldFont : titleFont, textColor: textColor))
|
||||
}
|
||||
titleAttributedString = string
|
||||
} else if let firstName = user.firstName, !firstName.isEmpty {
|
||||
titleAttributedString = NSAttributedString(string: firstName, font: titleBoldFont, textColor: textColor)
|
||||
@ -562,6 +571,32 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
titleFrame = CGRect(origin: CGPoint(x: leftInset, y: 13.0), size: titleLayout.size)
|
||||
}
|
||||
|
||||
let peerRevealOptions: [ItemListRevealOption]
|
||||
if item.enabled {
|
||||
var mappedOptions: [ItemListRevealOption] = []
|
||||
var index: Int32 = 0
|
||||
for option in item.options {
|
||||
let color: UIColor
|
||||
let textColor: UIColor
|
||||
switch option.type {
|
||||
case .neutral:
|
||||
color = item.theme.list.itemDisclosureActions.constructive.fillColor
|
||||
textColor = item.theme.list.itemDisclosureActions.constructive.foregroundColor
|
||||
case .warning:
|
||||
color = item.theme.list.itemDisclosureActions.warning.fillColor
|
||||
textColor = item.theme.list.itemDisclosureActions.warning.foregroundColor
|
||||
case .destructive:
|
||||
color = item.theme.list.itemDisclosureActions.destructive.fillColor
|
||||
textColor = item.theme.list.itemDisclosureActions.destructive.foregroundColor
|
||||
}
|
||||
mappedOptions.append(ItemListRevealOption(key: index, title: option.title, icon: .none, color: color, textColor: textColor))
|
||||
index += 1
|
||||
}
|
||||
peerRevealOptions = mappedOptions
|
||||
} else {
|
||||
peerRevealOptions = []
|
||||
}
|
||||
|
||||
return (nodeLayout, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
return (.complete(), { [weak strongSelf] animated, synchronousLoads in
|
||||
@ -710,12 +745,12 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
strongSelf.updateLayout(size: nodeLayout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset)
|
||||
|
||||
|
||||
if item.editing.editable {
|
||||
strongSelf.setRevealOptions((left: [], right: [ItemListRevealOption(key: 0, title: item.strings.Common_Delete, icon: .none, color: item.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.theme.list.itemDisclosureActions.destructive.foregroundColor)]))
|
||||
strongSelf.setRevealOptionsOpened(item.editing.revealed, animated: animated)
|
||||
} else {
|
||||
strongSelf.setRevealOptions((left: [], right: []))
|
||||
strongSelf.setRevealOptions((left: [], right: peerRevealOptions))
|
||||
strongSelf.setRevealOptionsOpened(item.editing.revealed, animated: animated)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -800,13 +835,17 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
override func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) {
|
||||
if let item = self.item {
|
||||
switch item.peer {
|
||||
case let .peer(peer, chatPeer):
|
||||
if let peer = chatPeer ?? peer {
|
||||
item.deletePeer?(peer.id)
|
||||
}
|
||||
case .deviceContact:
|
||||
break
|
||||
if item.editing.editable {
|
||||
switch item.peer {
|
||||
case let .peer(peer, chatPeer):
|
||||
if let peer = chatPeer ?? peer {
|
||||
item.deletePeer?(peer.id)
|
||||
}
|
||||
case .deviceContact:
|
||||
break
|
||||
}
|
||||
} else {
|
||||
item.options[Int(option.key)].action()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -73,3 +73,16 @@ let countryCodeToIdAndName: [Int: (String, String)] = {
|
||||
}
|
||||
return dict
|
||||
}()
|
||||
|
||||
struct CountryCodeAndId: Hashable {
|
||||
let code: Int
|
||||
let id: String
|
||||
}
|
||||
|
||||
let countryCodeAndIdToName: [CountryCodeAndId: String] = {
|
||||
var dict: [CountryCodeAndId: String] = [:]
|
||||
for (code, id, name) in phoneCountriesInfo {
|
||||
dict[CountryCodeAndId(code: code, id: id)] = name
|
||||
}
|
||||
return dict
|
||||
}()
|
||||
|
||||
@ -42,7 +42,7 @@ private enum CreateGroupEntry: ItemListNodeEntry {
|
||||
case groupInfo(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer?, ItemListAvatarAndNameInfoItemState, ItemListAvatarAndNameInfoItemUpdatingAvatar?)
|
||||
case setProfilePhoto(PresentationTheme, String)
|
||||
|
||||
case member(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, PeerPresence?)
|
||||
case member(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, PeerPresence?)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -59,7 +59,7 @@ private enum CreateGroupEntry: ItemListNodeEntry {
|
||||
return 0
|
||||
case .setProfilePhoto:
|
||||
return 1
|
||||
case let .member(index, _, _, _, _, _):
|
||||
case let .member(index, _, _, _, _, _, _):
|
||||
return 2 + index
|
||||
}
|
||||
}
|
||||
@ -100,8 +100,8 @@ private enum CreateGroupEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .member(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsPresence):
|
||||
if case let .member(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsPresence) = rhs {
|
||||
case let .member(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameDisplayOrder, lhsPeer, lhsPresence):
|
||||
if case let .member(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameDisplayOrder, rhsPeer, rhsPresence) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -114,6 +114,9 @@ private enum CreateGroupEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameDisplayOrder != rhsNameDisplayOrder {
|
||||
return false
|
||||
}
|
||||
if !lhsPeer.isEqual(rhsPeer) {
|
||||
return false
|
||||
}
|
||||
@ -146,8 +149,8 @@ private enum CreateGroupEntry: ItemListNodeEntry {
|
||||
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||
arguments.changeProfilePhoto()
|
||||
})
|
||||
case let .member(_, theme, strings, dateTimeFormat, peer, presence):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: presence, text: .presence, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in })
|
||||
case let .member(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, presence):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: presence, text: .presence, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in })
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -210,7 +213,7 @@ private func createGroupEntries(presentationData: PresentationData, state: Creat
|
||||
})
|
||||
|
||||
for i in 0 ..< peers.count {
|
||||
entries.append(.member(Int32(i), presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peers[i], view.presences[peers[i].id]))
|
||||
entries.append(.member(Int32(i), presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peers[i], view.presences[peers[i].id]))
|
||||
}
|
||||
|
||||
return entries
|
||||
|
||||
@ -394,7 +394,7 @@ public func dataPrivacyController(account: Account) -> ViewController {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = updateContactSynchronizationSettingsInteractively(postbox: account.postbox, { settings in
|
||||
let _ = updateContactSettingsInteractively(postbox: account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.synchronizeDeviceContacts = false
|
||||
return settings
|
||||
@ -413,7 +413,7 @@ public func dataPrivacyController(account: Account) -> ViewController {
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})]))
|
||||
}
|
||||
}, updateSyncContacts: { value in
|
||||
let _ = updateContactSynchronizationSettingsInteractively(postbox: account.postbox, { settings in
|
||||
let _ = updateContactSettingsInteractively(postbox: account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.synchronizeDeviceContacts = value
|
||||
return settings
|
||||
|
||||
@ -7,6 +7,7 @@ import AddressBook
|
||||
public typealias DeviceContactStableId = String
|
||||
|
||||
private protocol DeviceContactDataContext {
|
||||
func personNameDisplayOrder() -> PresentationPersonNameOrder
|
||||
func getExtendedContactData(stableId: DeviceContactStableId) -> DeviceContactExtendedData?
|
||||
func appendContactData(_ contactData: DeviceContactExtendedData, to stableId: DeviceContactStableId) -> DeviceContactExtendedData?
|
||||
func createContactWithData(_ contactData: DeviceContactExtendedData) -> (DeviceContactStableId, DeviceContactExtendedData)?
|
||||
@ -64,6 +65,15 @@ private final class DeviceContactDataModernContext: DeviceContactDataContext {
|
||||
return (contact.identifier, DeviceContactBasicData(firstName: contact.givenName, lastName: contact.familyName, phoneNumbers: phoneNumbers))
|
||||
}
|
||||
|
||||
func personNameDisplayOrder() -> PresentationPersonNameOrder {
|
||||
switch CNContactFormatter.nameOrder(for: CNContact()) {
|
||||
case .givenNameFirst:
|
||||
return .firstLast
|
||||
default:
|
||||
return .lastFirst
|
||||
}
|
||||
}
|
||||
|
||||
func getExtendedContactData(stableId: DeviceContactStableId) -> DeviceContactExtendedData? {
|
||||
let keysToFetch: [CNKeyDescriptor] = [
|
||||
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
|
||||
@ -318,6 +328,14 @@ private final class DeviceContactDataLegacyContext: DeviceContactDataContext {
|
||||
return (stableId, DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: phoneNumbers))
|
||||
}
|
||||
|
||||
func personNameDisplayOrder() -> PresentationPersonNameOrder {
|
||||
if ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst {
|
||||
return .firstLast
|
||||
} else {
|
||||
return .lastFirst
|
||||
}
|
||||
}
|
||||
|
||||
func getExtendedContactData(stableId: DeviceContactStableId) -> DeviceContactExtendedData? {
|
||||
if let contact = self.getContactById(stableId: stableId) {
|
||||
let basicData = DeviceContactDataLegacyContext.parseContact(contact).1
|
||||
@ -378,6 +396,7 @@ private final class DeviceContactDataManagerImpl {
|
||||
private var accessInitialized = false
|
||||
|
||||
private var dataContext: DeviceContactDataContext?
|
||||
let personNameDisplayOrder = ValuePromise<PresentationPersonNameOrder>()
|
||||
private var extendedContexts: [DeviceContactStableId: ExtendedContactDataContext] = [:]
|
||||
|
||||
private var stableIdToBasicContactData: [DeviceContactStableId: DeviceContactBasicData] = [:]
|
||||
@ -402,27 +421,24 @@ private final class DeviceContactDataManagerImpl {
|
||||
strongSelf.accessInitialized = true
|
||||
if authorizationStatus {
|
||||
if #available(iOSApplicationExtension 9.0, *) {
|
||||
strongSelf.dataContext = DeviceContactDataModernContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in
|
||||
let dataContext = DeviceContactDataModernContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateAll(stableIdToBasicContactData)
|
||||
})
|
||||
strongSelf.dataContext = dataContext
|
||||
strongSelf.personNameDisplayOrder.set(dataContext.personNameDisplayOrder())
|
||||
} else {
|
||||
strongSelf.dataContext = DeviceContactDataLegacyContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in
|
||||
let dataContext = DeviceContactDataLegacyContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateAll(stableIdToBasicContactData)
|
||||
})
|
||||
strongSelf.dataContext = dataContext
|
||||
strongSelf.personNameDisplayOrder.set(dataContext.personNameDisplayOrder())
|
||||
}
|
||||
|
||||
/*strongSelf.dataContext = DeviceContactDataLegacyContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateAll(stableIdToBasicContactData)
|
||||
})*/
|
||||
} else {
|
||||
strongSelf.updateAll([:])
|
||||
}
|
||||
@ -626,6 +642,18 @@ public final class DeviceContactDataManager {
|
||||
})
|
||||
}
|
||||
|
||||
public func personNameDisplayOrder() -> Signal<PresentationPersonNameOrder, NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with({ impl in
|
||||
disposable.set(impl.personNameDisplayOrder.get().start(next: { value in
|
||||
subscriber.putNext(value)
|
||||
}))
|
||||
})
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
public func basicData() -> Signal<[DeviceContactStableId: DeviceContactBasicData], NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
@ -51,12 +51,14 @@ final class EditAccessoryPanelNode: AccessoryPanelNode {
|
||||
private let account: Account
|
||||
var theme: PresentationTheme
|
||||
var strings: PresentationStrings
|
||||
var nameDisplayOrder: PresentationPersonNameOrder
|
||||
|
||||
init(account: Account, messageId: MessageId, theme: PresentationTheme, strings: PresentationStrings) {
|
||||
init(account: Account, messageId: MessageId, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) {
|
||||
self.account = account
|
||||
self.messageId = messageId
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
|
||||
self.closeButton = ASButtonNode()
|
||||
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(theme), for: [])
|
||||
@ -127,7 +129,7 @@ final class EditAccessoryPanelNode: AccessoryPanelNode {
|
||||
if let currentEditMediaReference = self.currentEditMediaReference {
|
||||
effectiveMessage = effectiveMessage.withUpdatedMedia([currentEditMediaReference.media])
|
||||
}
|
||||
(text, _) = descriptionStringForMessage(effectiveMessage, strings: self.strings, accountPeerId: self.account.peerId)
|
||||
(text, _) = descriptionStringForMessage(effectiveMessage, strings: self.strings, nameDisplayOrder: self.nameDisplayOrder, accountPeerId: self.account.peerId)
|
||||
}
|
||||
|
||||
var updatedMediaReference: AnyMediaReference?
|
||||
@ -196,7 +198,7 @@ final class EditAccessoryPanelNode: AccessoryPanelNode {
|
||||
if let currentEditMediaReference = self.currentEditMediaReference {
|
||||
effectiveMessage = effectiveMessage.withUpdatedMedia([currentEditMediaReference.media])
|
||||
}
|
||||
switch messageContentKind(effectiveMessage, strings: strings, accountPeerId: self.account.peerId) {
|
||||
switch messageContentKind(effectiveMessage, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: self.account.peerId) {
|
||||
case .text:
|
||||
isMedia = false
|
||||
default:
|
||||
|
||||
@ -1,33 +1,25 @@
|
||||
import Foundation
|
||||
import TelegramUIPrivateModule
|
||||
import CoreMedia
|
||||
import FFMpeg
|
||||
|
||||
final class FFMpegAudioFrameDecoder: MediaTrackFrameDecoder {
|
||||
private let codecContext: UnsafeMutablePointer<AVCodecContext>
|
||||
private let swrContext: FFMpegSwResample
|
||||
private let codecContext: FFMpegAVCodecContext
|
||||
private let swrContext: FFMpegSWResample
|
||||
|
||||
private let audioFrame: UnsafeMutablePointer<AVFrame>
|
||||
private let audioFrame: FFMpegAVFrame
|
||||
private var resetDecoderOnNextFrame = true
|
||||
|
||||
init(codecContext: UnsafeMutablePointer<AVCodecContext>) {
|
||||
init(codecContext: FFMpegAVCodecContext) {
|
||||
self.codecContext = codecContext
|
||||
self.audioFrame = av_frame_alloc()
|
||||
self.audioFrame = FFMpegAVFrame()
|
||||
|
||||
self.swrContext = FFMpegSwResample(sourceChannelCount: Int(codecContext.pointee.channels), sourceSampleRate: Int(codecContext.pointee.sample_rate), sourceSampleFormat: codecContext.pointee.sample_fmt, destinationChannelCount: 2, destinationSampleRate: 44100, destinationSampleFormat: AV_SAMPLE_FMT_S16)
|
||||
}
|
||||
|
||||
deinit {
|
||||
av_frame_unref(self.audioFrame)
|
||||
|
||||
var codecContextRef: UnsafeMutablePointer<AVCodecContext>? = codecContext
|
||||
avcodec_free_context(&codecContextRef)
|
||||
self.swrContext = FFMpegSWResample(sourceChannelCount: Int(codecContext.channels()), sourceSampleRate: Int(codecContext.sampleRate()), sourceSampleFormat: codecContext.sampleFormat(), destinationChannelCount: 2, destinationSampleRate: 44100, destinationSampleFormat: FFMPEG_AV_SAMPLE_FMT_S16)
|
||||
}
|
||||
|
||||
func decode(frame: MediaTrackDecodableFrame) -> MediaTrackFrame? {
|
||||
var status = frame.packet.sendToDecoder(self.codecContext)
|
||||
let status = frame.packet.send(toDecoder: self.codecContext)
|
||||
if status == 0 {
|
||||
status = avcodec_receive_frame(self.codecContext, self.audioFrame)
|
||||
if status == 0 {
|
||||
if self.codecContext.receive(into: self.audioFrame) {
|
||||
return convertAudioFrame(self.audioFrame, pts: frame.pts, duration: frame.duration)
|
||||
}
|
||||
}
|
||||
@ -39,7 +31,7 @@ final class FFMpegAudioFrameDecoder: MediaTrackFrameDecoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func convertAudioFrame(_ frame: UnsafeMutablePointer<AVFrame>, pts: CMTime, duration: CMTime) -> MediaTrackFrame? {
|
||||
private func convertAudioFrame(_ frame: FFMpegAVFrame, pts: CMTime, duration: CMTime) -> MediaTrackFrame? {
|
||||
guard let data = self.swrContext.resample(frame) else {
|
||||
return nil
|
||||
}
|
||||
@ -67,7 +59,7 @@ final class FFMpegAudioFrameDecoder: MediaTrackFrameDecoder {
|
||||
}
|
||||
|
||||
func reset() {
|
||||
avcodec_flush_buffers(self.codecContext)
|
||||
self.codecContext.flushBuffers()
|
||||
self.resetDecoderOnNextFrame = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,6 +69,7 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource {
|
||||
private let queue: Queue
|
||||
private let postbox: Postbox
|
||||
private let resourceReference: MediaResourceReference
|
||||
private let tempFilePath: String?
|
||||
private let streamable: Bool
|
||||
private let video: Bool
|
||||
private let preferSoftwareDecoding: Bool
|
||||
@ -91,10 +92,11 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource {
|
||||
}
|
||||
}
|
||||
|
||||
init(queue: Queue, postbox: Postbox, resourceReference: MediaResourceReference, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, fetchAutomatically: Bool) {
|
||||
init(queue: Queue, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, fetchAutomatically: Bool) {
|
||||
self.queue = queue
|
||||
self.postbox = postbox
|
||||
self.resourceReference = resourceReference
|
||||
self.tempFilePath = tempFilePath
|
||||
self.streamable = streamable
|
||||
self.video = video
|
||||
self.preferSoftwareDecoding = preferSoftwareDecoding
|
||||
@ -146,6 +148,7 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource {
|
||||
|
||||
let postbox = self.postbox
|
||||
let resourceReference = self.resourceReference
|
||||
let tempFilePath = self.tempFilePath
|
||||
let queue = self.queue
|
||||
let streamable = self.streamable
|
||||
let video = self.video
|
||||
@ -153,7 +156,7 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource {
|
||||
let fetchAutomatically = self.fetchAutomatically
|
||||
|
||||
self.performWithContext { [weak self] context in
|
||||
context.initializeState(postbox: postbox, resourceReference: resourceReference, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, fetchAutomatically: fetchAutomatically)
|
||||
context.initializeState(postbox: postbox, resourceReference: resourceReference, tempFilePath: tempFilePath, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, fetchAutomatically: fetchAutomatically)
|
||||
|
||||
let (frames, endOfStream) = context.takeFrames(until: timestamp)
|
||||
|
||||
@ -195,13 +198,14 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource {
|
||||
let queue = self.queue
|
||||
let postbox = self.postbox
|
||||
let resourceReference = self.resourceReference
|
||||
let tempFilePath = self.tempFilePath
|
||||
let streamable = self.streamable
|
||||
let video = self.video
|
||||
let preferSoftwareDecoding = self.preferSoftwareDecoding
|
||||
let fetchAutomatically = self.fetchAutomatically
|
||||
|
||||
self.performWithContext { [weak self] context in
|
||||
context.initializeState(postbox: postbox, resourceReference: resourceReference, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, fetchAutomatically: fetchAutomatically)
|
||||
context.initializeState(postbox: postbox, resourceReference: resourceReference, tempFilePath: tempFilePath, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, fetchAutomatically: fetchAutomatically)
|
||||
|
||||
context.seek(timestamp: timestamp, completed: { streamDescriptions, timestamp in
|
||||
queue.async {
|
||||
|
||||
@ -4,10 +4,11 @@ import Postbox
|
||||
import CoreMedia
|
||||
import TelegramUIPrivateModule
|
||||
import TelegramCore
|
||||
import FFMpeg
|
||||
|
||||
private struct StreamContext {
|
||||
let index: Int
|
||||
let codecContext: UnsafeMutablePointer<AVCodecContext>?
|
||||
let codecContext: FFMpegAVCodecContext?
|
||||
let fps: CMTime
|
||||
let timebase: CMTime
|
||||
let duration: CMTime
|
||||
@ -30,29 +31,18 @@ struct FFMpegMediaFrameSourceDescriptionSet {
|
||||
}
|
||||
|
||||
private final class InitializedState {
|
||||
fileprivate let avIoContext: UnsafeMutablePointer<AVIOContext>
|
||||
fileprivate let avFormatContext: UnsafeMutablePointer<AVFormatContext>
|
||||
fileprivate let avIoContext: FFMpegAVIOContext
|
||||
fileprivate let avFormatContext: FFMpegAVFormatContext
|
||||
|
||||
fileprivate let audioStream: StreamContext?
|
||||
fileprivate let videoStream: StreamContext?
|
||||
|
||||
init(avIoContext: UnsafeMutablePointer<AVIOContext>, avFormatContext: UnsafeMutablePointer<AVFormatContext>, audioStream: StreamContext?, videoStream: StreamContext?) {
|
||||
init(avIoContext: FFMpegAVIOContext, avFormatContext: FFMpegAVFormatContext, audioStream: StreamContext?, videoStream: StreamContext?) {
|
||||
self.avIoContext = avIoContext
|
||||
self.avFormatContext = avFormatContext
|
||||
self.audioStream = audioStream
|
||||
self.videoStream = videoStream
|
||||
}
|
||||
|
||||
deinit {
|
||||
let avIoContext = Optional(self.avIoContext)
|
||||
if self.avIoContext.pointee.buffer != nil {
|
||||
av_free(self.avIoContext.pointee.buffer)
|
||||
}
|
||||
av_free(avIoContext)
|
||||
|
||||
var avFormatContext = Optional(self.avFormatContext)
|
||||
avformat_close_input(&avFormatContext)
|
||||
}
|
||||
}
|
||||
|
||||
struct FFMpegMediaFrameSourceStreamContextInfo {
|
||||
@ -81,44 +71,66 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa
|
||||
let readCount = min(resourceSize - context.readingOffset, Int(bufferSize))
|
||||
let requestRange: Range<Int> = context.readingOffset ..< (context.readingOffset + readCount)
|
||||
data = postbox.mediaBox.resourceData(resourceReference.resource, size: resourceSize, in: requestRange, mode: .complete)
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
if readCount == 0 {
|
||||
fetchedData = Data()
|
||||
} else {
|
||||
let disposable = data.start(next: { data in
|
||||
if data.count == readCount {
|
||||
fetchedData = data
|
||||
if let tempFilePath = context.tempFilePath, let fileData = (try? Data(contentsOf: URL(fileURLWithPath: tempFilePath), options: .mappedRead))?.subdata(in: requestRange) {
|
||||
fetchedData = fileData
|
||||
} else {
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let disposable = data.start(next: { data in
|
||||
if data.count == readCount {
|
||||
fetchedData = data
|
||||
semaphore.signal()
|
||||
}
|
||||
})
|
||||
semaphore.wait()
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let tempFilePath = context.tempFilePath, let fileSize = fileSize(tempFilePath) {
|
||||
let fd = open(tempFilePath, O_RDONLY, S_IRUSR)
|
||||
if fd >= 0 {
|
||||
let readingOffset = context.readingOffset
|
||||
let readCount = max(0, min(fileSize - readingOffset, Int(bufferSize)))
|
||||
let range = readingOffset ..< (readingOffset + readCount)
|
||||
|
||||
lseek(fd, off_t(range.lowerBound), SEEK_SET)
|
||||
var data = Data(count: readCount)
|
||||
data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
|
||||
let readBytes = read(fd, bytes, readCount)
|
||||
assert(readBytes <= readCount)
|
||||
}
|
||||
fetchedData = data
|
||||
close(fd)
|
||||
}
|
||||
} else {
|
||||
let data = postbox.mediaBox.resourceData(resourceReference.resource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false))
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let readingOffset = context.readingOffset
|
||||
let disposable = data.start(next: { next in
|
||||
if next.complete {
|
||||
let readCount = max(0, min(next.size - readingOffset, Int(bufferSize)))
|
||||
let range = readingOffset ..< (readingOffset + readCount)
|
||||
|
||||
let fd = open(next.path, O_RDONLY, S_IRUSR)
|
||||
if fd >= 0 {
|
||||
lseek(fd, off_t(range.lowerBound), SEEK_SET)
|
||||
var data = Data(count: readCount)
|
||||
data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
|
||||
let readBytes = read(fd, bytes, readCount)
|
||||
assert(readBytes <= readCount)
|
||||
}
|
||||
fetchedData = data
|
||||
close(fd)
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
})
|
||||
semaphore.wait()
|
||||
disposable.dispose()
|
||||
}
|
||||
} else {
|
||||
let data = postbox.mediaBox.resourceData(resourceReference.resource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false))
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let readingOffset = context.readingOffset
|
||||
let disposable = data.start(next: { next in
|
||||
if next.complete {
|
||||
let readCount = max(0, min(next.size - readingOffset, Int(bufferSize)))
|
||||
let range = readingOffset ..< (readingOffset + readCount)
|
||||
|
||||
let fd = open(next.path, O_RDONLY, S_IRUSR)
|
||||
if fd >= 0 {
|
||||
lseek(fd, off_t(range.lowerBound), SEEK_SET)
|
||||
var data = Data(count: readCount)
|
||||
data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
|
||||
let readBytes = read(fd, bytes, readCount)
|
||||
assert(readBytes <= readCount)
|
||||
}
|
||||
fetchedData = data
|
||||
close(fd)
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
})
|
||||
semaphore.wait()
|
||||
disposable.dispose()
|
||||
}
|
||||
if let fetchedData = fetchedData {
|
||||
fetchedData.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||
@ -144,24 +156,28 @@ private func seekCallback(userData: UnsafeMutableRawPointer?, offset: Int64, whe
|
||||
resourceSize = size
|
||||
} else {
|
||||
if !streamable {
|
||||
var resultSize: Int = Int(Int32.max - 1)
|
||||
let data = postbox.mediaBox.resourceData(resourceReference.resource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false))
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let disposable = data.start(next: { next in
|
||||
if next.complete {
|
||||
resultSize = Int(next.size)
|
||||
semaphore.signal()
|
||||
}
|
||||
})
|
||||
semaphore.wait()
|
||||
disposable.dispose()
|
||||
resourceSize = resultSize
|
||||
if let tempFilePath = context.tempFilePath, let fileSize = fileSize(tempFilePath) {
|
||||
resourceSize = fileSize
|
||||
} else {
|
||||
var resultSize: Int = Int(Int32.max - 1)
|
||||
let data = postbox.mediaBox.resourceData(resourceReference.resource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false))
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let disposable = data.start(next: { next in
|
||||
if next.complete {
|
||||
resultSize = Int(next.size)
|
||||
semaphore.signal()
|
||||
}
|
||||
})
|
||||
semaphore.wait()
|
||||
disposable.dispose()
|
||||
resourceSize = resultSize
|
||||
}
|
||||
} else {
|
||||
resourceSize = Int(Int32.max - 1)
|
||||
}
|
||||
}
|
||||
|
||||
if (whence & AVSEEK_SIZE) != 0 {
|
||||
if (whence & FFMPEG_AVSEEK_SIZE) != 0 {
|
||||
result = Int64(resourceSize == Int(Int32.max - 1) ? -1 : resourceSize)
|
||||
} else {
|
||||
context.readingOffset = Int(min(Int64(resourceSize), offset))
|
||||
@ -173,11 +189,15 @@ private func seekCallback(userData: UnsafeMutableRawPointer?, offset: Int64, whe
|
||||
context.fetchedDataDisposable.set(nil)
|
||||
} else {
|
||||
if streamable {
|
||||
let fetchRange: Range<Int> = context.readingOffset ..< Int(Int32.max)
|
||||
context.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (fetchRange, .elevated), statsCategory: statsCategory, preferBackgroundReferenceRevalidation: streamable).start())
|
||||
if context.tempFilePath == nil {
|
||||
let fetchRange: Range<Int> = context.readingOffset ..< Int(Int32.max)
|
||||
context.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (fetchRange, .elevated), statsCategory: statsCategory, preferBackgroundReferenceRevalidation: streamable).start())
|
||||
}
|
||||
} else if !context.requestedCompleteFetch && context.fetchAutomatically {
|
||||
context.requestedCompleteFetch = true
|
||||
context.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, statsCategory: statsCategory, preferBackgroundReferenceRevalidation: streamable).start())
|
||||
if context.tempFilePath == nil {
|
||||
context.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, statsCategory: statsCategory, preferBackgroundReferenceRevalidation: streamable).start())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,6 +213,7 @@ final class FFMpegMediaFrameSourceContext: NSObject {
|
||||
|
||||
fileprivate var postbox: Postbox?
|
||||
fileprivate var resourceReference: MediaResourceReference?
|
||||
fileprivate var tempFilePath: String?
|
||||
fileprivate var streamable: Bool?
|
||||
fileprivate var statsCategory: MediaResourceStatsCategory?
|
||||
|
||||
@ -223,7 +244,7 @@ final class FFMpegMediaFrameSourceContext: NSObject {
|
||||
self.fetchedFullDataDisposable.dispose()
|
||||
}
|
||||
|
||||
func initializeState(postbox: Postbox, resourceReference: MediaResourceReference, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, fetchAutomatically: Bool) {
|
||||
func initializeState(postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, fetchAutomatically: Bool) {
|
||||
if self.readingError || self.initializedState != nil {
|
||||
return
|
||||
}
|
||||
@ -232,173 +253,119 @@ final class FFMpegMediaFrameSourceContext: NSObject {
|
||||
|
||||
self.postbox = postbox
|
||||
self.resourceReference = resourceReference
|
||||
self.tempFilePath = tempFilePath
|
||||
self.streamable = streamable
|
||||
self.statsCategory = video ? .video : .audio
|
||||
self.preferSoftwareDecoding = preferSoftwareDecoding
|
||||
self.fetchAutomatically = fetchAutomatically
|
||||
|
||||
var preferSoftwareAudioDecoding = false
|
||||
if case let .media(media, _) = resourceReference, let file = media.media as? TelegramMediaFile {
|
||||
if file.isInstantVideo {
|
||||
preferSoftwareAudioDecoding = true
|
||||
}
|
||||
}
|
||||
|
||||
if streamable {
|
||||
self.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (0 ..< Int(Int32.max), .elevated), statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start())
|
||||
if self.tempFilePath == nil {
|
||||
self.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (0 ..< Int(Int32.max), .elevated), statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start())
|
||||
}
|
||||
} else if !self.requestedCompleteFetch && self.fetchAutomatically {
|
||||
self.requestedCompleteFetch = true
|
||||
self.fetchedFullDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start())
|
||||
if self.tempFilePath == nil {
|
||||
self.fetchedFullDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start())
|
||||
}
|
||||
}
|
||||
|
||||
var avFormatContextRef = avformat_alloc_context()
|
||||
guard let avFormatContext = avFormatContextRef else {
|
||||
let avFormatContext = FFMpegAVFormatContext()
|
||||
|
||||
guard let avIoContext = FFMpegAVIOContext(bufferSize: Int32(self.ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, seek: seekCallback) else {
|
||||
self.readingError = true
|
||||
return
|
||||
}
|
||||
|
||||
let avIoBuffer = av_malloc(self.ioBufferSize)!
|
||||
let avIoContextRef = avio_alloc_context(avIoBuffer.assumingMemoryBound(to: UInt8.self), Int32(self.ioBufferSize), 0, Unmanaged.passUnretained(self).toOpaque(), readPacketCallback, nil, seekCallback)
|
||||
avFormatContext.setIO(avIoContext)
|
||||
|
||||
guard let avIoContext = avIoContextRef else {
|
||||
if !avFormatContext.openInput() {
|
||||
self.readingError = true
|
||||
return
|
||||
}
|
||||
|
||||
avFormatContext.pointee.pb = avIoContext
|
||||
|
||||
//avFormatContext.pointee.flags |= AVFMT_FLAG_FAST_SEEK
|
||||
//print(String.init(cString: avutil_configuration()))
|
||||
|
||||
var options: OpaquePointer?
|
||||
av_dict_set(&options, "usetoc", "1", 0)
|
||||
|
||||
guard avformat_open_input(&avFormatContextRef, nil, nil, &options) >= 0 else {
|
||||
self.readingError = true
|
||||
return
|
||||
}
|
||||
|
||||
av_dict_free(&options)
|
||||
|
||||
guard avformat_find_stream_info(avFormatContext, nil) >= 0 else {
|
||||
self.readingError = true
|
||||
if !avFormatContext.findStreamInfo() {
|
||||
self.readingError = true;
|
||||
return
|
||||
}
|
||||
|
||||
var videoStream: StreamContext?
|
||||
var audioStream: StreamContext?
|
||||
|
||||
for streamIndex in FFMpegMediaFrameSourceContextHelpers.streamIndices(formatContext: avFormatContext, codecType: AVMEDIA_TYPE_VIDEO) {
|
||||
if (avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.disposition & Int32(AV_DISPOSITION_ATTACHED_PIC)) == 0 {
|
||||
|
||||
let codecPar = avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.codecpar!
|
||||
|
||||
if self.preferSoftwareDecoding {
|
||||
if let codec = avcodec_find_decoder(codecPar.pointee.codec_id) {
|
||||
if let codecContext = avcodec_alloc_context3(codec) {
|
||||
if avcodec_parameters_to_context(codecContext, avFormatContext.pointee.streams[streamIndex]!.pointee.codecpar) >= 0 {
|
||||
if avcodec_open2(codecContext, codec, nil) >= 0 {
|
||||
let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 24))
|
||||
|
||||
let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale)
|
||||
|
||||
var rotationAngle: Double = 0.0
|
||||
if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value {
|
||||
if strcmp(value, "0") != 0 {
|
||||
if let angle = Double(String(cString: value)) {
|
||||
rotationAngle = angle * Double.pi / 180.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height)
|
||||
|
||||
videoStream = StreamContext(index: streamIndex, codecContext: codecContext, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaVideoFrameDecoder(codecContext: codecContext), rotationAngle: rotationAngle, aspect: aspect)
|
||||
break
|
||||
} else {
|
||||
var codecContextRef: UnsafeMutablePointer<AVCodecContext>? = codecContext
|
||||
avcodec_free_context(&codecContextRef)
|
||||
}
|
||||
} else {
|
||||
var codecContextRef: UnsafeMutablePointer<AVCodecContext>? = codecContext
|
||||
avcodec_free_context(&codecContextRef)
|
||||
}
|
||||
for streamIndexNumber in avFormatContext.streamIndices(for: FFMpegAVFormatStreamTypeVideo) {
|
||||
let streamIndex = streamIndexNumber.int32Value
|
||||
if avFormatContext.isAttachedPic(atStreamIndex: streamIndex) {
|
||||
continue
|
||||
}
|
||||
|
||||
let codecId = avFormatContext.codecId(atStreamIndex: streamIndex)
|
||||
|
||||
let fpsAndTimebase = avFormatContext.fpsAndTimebase(forStreamIndex: streamIndex, defaultTimeBase: CMTimeMake(1, 40000))
|
||||
let (fps, timebase) = (fpsAndTimebase.fps, fpsAndTimebase.timebase)
|
||||
|
||||
let duration = CMTimeMake(avFormatContext.duration(atStreamIndex: streamIndex), timebase.timescale)
|
||||
|
||||
let metrics = avFormatContext.metricsForStream(at: streamIndex)
|
||||
|
||||
let rotationAngle: Double = metrics.rotationAngle
|
||||
let aspect = Double(metrics.width) / Double(metrics.height)
|
||||
|
||||
if self.preferSoftwareDecoding {
|
||||
if let codec = FFMpegAVCodec.find(forId: codecId) {
|
||||
let codecContext = FFMpegAVCodecContext(codec: codec)
|
||||
if avFormatContext.codecParams(atStreamIndex: streamIndex, to: codecContext) {
|
||||
if codecContext.open() {
|
||||
videoStream = StreamContext(index: Int(streamIndex), codecContext: codecContext, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaVideoFrameDecoder(codecContext: codecContext), rotationAngle: rotationAngle, aspect: aspect)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if codecPar.pointee.codec_id == AV_CODEC_ID_MPEG4 {
|
||||
if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromMpeg4CodecData(UInt32(kCMVideoCodecType_MPEG4Video), codecPar.pointee.width, codecPar.pointee.height, codecPar.pointee.extradata, codecPar.pointee.extradata_size) {
|
||||
let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 1000))
|
||||
|
||||
let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale)
|
||||
|
||||
var rotationAngle: Double = 0.0
|
||||
if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value {
|
||||
if strcmp(value, "0") != 0 {
|
||||
if let angle = Double(String(cString: value)) {
|
||||
rotationAngle = angle * Double.pi / 180.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height)
|
||||
|
||||
videoStream = StreamContext(index: streamIndex, codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect)
|
||||
break
|
||||
}
|
||||
} else if codecPar.pointee.codec_id == AV_CODEC_ID_H264 {
|
||||
if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromAVCCodecData(UInt32(kCMVideoCodecType_H264), codecPar.pointee.width, codecPar.pointee.height, codecPar.pointee.extradata, codecPar.pointee.extradata_size) {
|
||||
let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 1000))
|
||||
|
||||
let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale)
|
||||
|
||||
var rotationAngle: Double = 0.0
|
||||
if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value {
|
||||
if strcmp(value, "0") != 0 {
|
||||
if let angle = Double(String(cString: value)) {
|
||||
rotationAngle = angle * Double.pi / 180.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height)
|
||||
|
||||
videoStream = StreamContext(index: streamIndex, codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect)
|
||||
break
|
||||
}
|
||||
} else if codecPar.pointee.codec_id == AV_CODEC_ID_HEVC {
|
||||
if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromHEVCCodecData(UInt32(kCMVideoCodecType_HEVC), codecPar.pointee.width, codecPar.pointee.height, codecPar.pointee.extradata, codecPar.pointee.extradata_size) {
|
||||
let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 1000))
|
||||
|
||||
let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale)
|
||||
|
||||
var rotationAngle: Double = 0.0
|
||||
if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value {
|
||||
if strcmp(value, "0") != 0 {
|
||||
if let angle = Double(String(cString: value)) {
|
||||
rotationAngle = angle * Double.pi / 180.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height)
|
||||
|
||||
videoStream = StreamContext(index: streamIndex, codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if codecId == FFMpegCodecIdMPEG4 {
|
||||
if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromMpeg4CodecData(UInt32(kCMVideoCodecType_MPEG4Video), metrics.width, metrics.height, metrics.extradata, metrics.extradataSize) {
|
||||
videoStream = StreamContext(index: Int(streamIndex), codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect)
|
||||
break
|
||||
}
|
||||
} else if codecId == FFMpegCodecIdH264 {
|
||||
if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromAVCCodecData(UInt32(kCMVideoCodecType_H264), metrics.width, metrics.height, metrics.extradata, metrics.extradataSize) {
|
||||
videoStream = StreamContext(index: Int(streamIndex), codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect)
|
||||
break
|
||||
}
|
||||
} else if codecId == FFMpegCodecIdHEVC {
|
||||
if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromHEVCCodecData(UInt32(kCMVideoCodecType_HEVC), metrics.width, metrics.height, metrics.extradata, metrics.extradataSize) {
|
||||
videoStream = StreamContext(index: Int(streamIndex), codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for streamIndex in FFMpegMediaFrameSourceContextHelpers.streamIndices(formatContext: avFormatContext, codecType: AVMEDIA_TYPE_AUDIO) {
|
||||
if let codec = avcodec_find_decoder(avFormatContext.pointee.streams[streamIndex]!.pointee.codecpar.pointee.codec_id) {
|
||||
if let codecContext = avcodec_alloc_context3(codec) {
|
||||
if avcodec_parameters_to_context(codecContext, avFormatContext.pointee.streams[streamIndex]!.pointee.codecpar) >= 0 {
|
||||
if avcodec_open2(codecContext, codec, nil) >= 0 {
|
||||
let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 40000))
|
||||
|
||||
let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale)
|
||||
|
||||
audioStream = StreamContext(index: streamIndex, codecContext: codecContext, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegAudioFrameDecoder(codecContext: codecContext), rotationAngle: 0.0, aspect: 1.0)
|
||||
} else {
|
||||
var codecContextRef: UnsafeMutablePointer<AVCodecContext>? = codecContext
|
||||
avcodec_free_context(&codecContextRef)
|
||||
}
|
||||
} else {
|
||||
var codecContextRef: UnsafeMutablePointer<AVCodecContext>? = codecContext
|
||||
avcodec_free_context(&codecContextRef)
|
||||
for streamIndexNumber in avFormatContext.streamIndices(for: FFMpegAVFormatStreamTypeAudio) {
|
||||
let streamIndex = streamIndexNumber.int32Value
|
||||
let codecId = avFormatContext.codecId(atStreamIndex: streamIndex)
|
||||
|
||||
var codec: FFMpegAVCodec?
|
||||
|
||||
if codec == nil {
|
||||
codec = FFMpegAVCodec.find(forId: codecId)
|
||||
}
|
||||
|
||||
if let codec = codec {
|
||||
let codecContext = FFMpegAVCodecContext(codec: codec)
|
||||
if avFormatContext.codecParams(atStreamIndex: streamIndex, to: codecContext) {
|
||||
if codecContext.open() {
|
||||
let fpsAndTimebase = avFormatContext.fpsAndTimebase(forStreamIndex: streamIndex, defaultTimeBase: CMTimeMake(1, 40000))
|
||||
let (fps, timebase) = (fpsAndTimebase.fps, fpsAndTimebase.timebase)
|
||||
|
||||
let duration = CMTimeMake(avFormatContext.duration(atStreamIndex: streamIndex), timebase.timescale)
|
||||
|
||||
audioStream = StreamContext(index: Int(streamIndex), codecContext: codecContext, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegAudioFrameDecoder(codecContext: codecContext), rotationAngle: 0.0, aspect: 1.0)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -407,7 +374,9 @@ final class FFMpegMediaFrameSourceContext: NSObject {
|
||||
self.initializedState = InitializedState(avIoContext: avIoContext, avFormatContext: avFormatContext, audioStream: audioStream, videoStream: videoStream)
|
||||
|
||||
if streamable {
|
||||
self.fetchedFullDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (0 ..< Int(Int32.max), .default), statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start())
|
||||
if self.tempFilePath == nil {
|
||||
self.fetchedFullDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (0 ..< Int(Int32.max), .default), statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start())
|
||||
}
|
||||
self.requestedCompleteFetch = true
|
||||
}
|
||||
}
|
||||
@ -426,10 +395,10 @@ final class FFMpegMediaFrameSourceContext: NSObject {
|
||||
}
|
||||
|
||||
let packet = FFMpegPacket()
|
||||
if av_read_frame(initializedState.avFormatContext, &packet.packet) < 0 {
|
||||
return nil
|
||||
} else {
|
||||
if initializedState.avFormatContext.readFrame(into: packet) {
|
||||
return packet
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -457,24 +426,22 @@ final class FFMpegMediaFrameSourceContext: NSObject {
|
||||
|
||||
while !self.readingError && ((videoTimestamp == nil || videoTimestamp!.isLess(than: until)) || (audioTimestamp == nil || audioTimestamp!.isLess(than: until))) {
|
||||
if let packet = self.readPacket() {
|
||||
if let videoStream = initializedState.videoStream, Int(packet.packet.stream_index) == videoStream.index {
|
||||
if let videoStream = initializedState.videoStream, Int(packet.streamIndex) == videoStream.index {
|
||||
let frame = videoFrameFromPacket(packet, videoStream: videoStream)
|
||||
frames.append(frame)
|
||||
|
||||
if videoTimestamp == nil || videoTimestamp! < CMTimeGetSeconds(frame.pts) {
|
||||
videoTimestamp = CMTimeGetSeconds(frame.pts)
|
||||
}
|
||||
} else if let audioStream = initializedState.audioStream, Int(packet.packet.stream_index) == audioStream.index {
|
||||
let avNoPtsRawValue: UInt64 = 0x8000000000000000
|
||||
let avNoPtsValue = Int64(bitPattern: avNoPtsRawValue)
|
||||
let packetPts = packet.packet.pts == avNoPtsValue ? packet.packet.dts : packet.packet.pts
|
||||
} else if let audioStream = initializedState.audioStream, Int(packet.streamIndex) == audioStream.index {
|
||||
let packetPts = packet.pts
|
||||
|
||||
let pts = CMTimeMake(packetPts, audioStream.timebase.timescale)
|
||||
let dts = CMTimeMake(packet.packet.dts, audioStream.timebase.timescale)
|
||||
let dts = CMTimeMake(packet.dts, audioStream.timebase.timescale)
|
||||
|
||||
let duration: CMTime
|
||||
|
||||
let frameDuration = packet.packet.duration
|
||||
let frameDuration = packet.duration
|
||||
if frameDuration != 0 {
|
||||
duration = CMTimeMake(frameDuration * audioStream.timebase.value, audioStream.timebase.timescale)
|
||||
} else {
|
||||
@ -522,7 +489,7 @@ final class FFMpegMediaFrameSourceContext: NSObject {
|
||||
for stream in [initializedState.videoStream, initializedState.audioStream] {
|
||||
if let stream = stream {
|
||||
let pts = CMTimeMakeWithSeconds(timestamp, stream.timebase.timescale)
|
||||
av_seek_frame(initializedState.avFormatContext, Int32(stream.index), pts.value, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME)
|
||||
initializedState.avFormatContext.seekFrame(forStreamIndex: Int32(stream.index), pts: pts.value)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -543,12 +510,12 @@ final class FFMpegMediaFrameSourceContext: NSObject {
|
||||
if timestamp.isZero || initializedState.videoStream == nil {
|
||||
for _ in 0 ..< 24 {
|
||||
if let packet = self.readPacketInternal() {
|
||||
if let videoStream = initializedState.videoStream, Int(packet.packet.stream_index) == videoStream.index {
|
||||
if let videoStream = initializedState.videoStream, Int(packet.streamIndex) == videoStream.index {
|
||||
self.packetQueue.append(packet)
|
||||
let pts = CMTimeMake(packet.pts, videoStream.timebase.timescale)
|
||||
actualPts = pts
|
||||
break
|
||||
} else if let audioStream = initializedState.audioStream, Int(packet.packet.stream_index) == audioStream.index {
|
||||
} else if let audioStream = initializedState.audioStream, Int(packet.streamIndex) == audioStream.index {
|
||||
self.packetQueue.append(packet)
|
||||
let pts = CMTimeMake(packet.pts, audioStream.timebase.timescale)
|
||||
actualPts = pts
|
||||
@ -564,14 +531,14 @@ final class FFMpegMediaFrameSourceContext: NSObject {
|
||||
var audioPackets: [FFMpegPacket] = []
|
||||
while !self.readingError {
|
||||
if let packet = self.readPacket() {
|
||||
if let videoStream = initializedState.videoStream, Int(packet.packet.stream_index) == videoStream.index {
|
||||
if let videoStream = initializedState.videoStream, Int(packet.streamIndex) == videoStream.index {
|
||||
let frame = videoFrameFromPacket(packet, videoStream: videoStream)
|
||||
extraVideoFrames.append(frame)
|
||||
|
||||
if CMTimeCompare(frame.dts, limitPts) >= 0 && CMTimeCompare(frame.pts, limitPts) >= 0 {
|
||||
break
|
||||
}
|
||||
} else if let audioStream = initializedState.audioStream, Int(packet.packet.stream_index) == audioStream.index {
|
||||
} else if let audioStream = initializedState.audioStream, Int(packet.streamIndex) == audioStream.index {
|
||||
audioPackets.append(packet)
|
||||
}
|
||||
} else {
|
||||
@ -613,16 +580,14 @@ final class FFMpegMediaFrameSourceContext: NSObject {
|
||||
}
|
||||
|
||||
private func videoFrameFromPacket(_ packet: FFMpegPacket, videoStream: StreamContext) -> MediaTrackDecodableFrame {
|
||||
let avNoPtsRawValue: UInt64 = 0x8000000000000000
|
||||
let avNoPtsValue = Int64(bitPattern: avNoPtsRawValue)
|
||||
let packetPts = packet.packet.pts == avNoPtsValue ? packet.packet.dts : packet.packet.pts
|
||||
let packetPts = packet.pts
|
||||
|
||||
let pts = CMTimeMake(packetPts, videoStream.timebase.timescale)
|
||||
let dts = CMTimeMake(packet.packet.dts, videoStream.timebase.timescale)
|
||||
let dts = CMTimeMake(packet.dts, videoStream.timebase.timescale)
|
||||
|
||||
let duration: CMTime
|
||||
|
||||
let frameDuration = packet.packet.duration
|
||||
let frameDuration = packet.duration
|
||||
if frameDuration != 0 {
|
||||
duration = CMTimeMake(frameDuration * videoStream.timebase.value, videoStream.timebase.timescale)
|
||||
} else {
|
||||
|
||||
@ -1,15 +1,10 @@
|
||||
import Foundation
|
||||
import CoreMedia
|
||||
import TelegramUIPrivateModule
|
||||
import FFMpeg
|
||||
|
||||
final class FFMpegMediaFrameSourceContextHelpers {
|
||||
static let registerFFMpegGlobals: Void = {
|
||||
#if DEBUG
|
||||
av_log_set_level(AV_LOG_ERROR)
|
||||
#else
|
||||
av_log_set_level(AV_LOG_QUIET)
|
||||
#endif
|
||||
av_register_all()
|
||||
FFMpegGlobals.initializeGlobals()
|
||||
return
|
||||
}()
|
||||
|
||||
@ -95,37 +90,4 @@ final class FFMpegMediaFrameSourceContextHelpers {
|
||||
|
||||
return formatDescription
|
||||
}
|
||||
|
||||
static func streamIndices(formatContext: UnsafeMutablePointer<AVFormatContext>, codecType: AVMediaType) -> [Int] {
|
||||
var indices: [Int] = []
|
||||
for i in 0 ..< Int(formatContext.pointee.nb_streams) {
|
||||
if codecType == formatContext.pointee.streams.advanced(by: i).pointee!.pointee.codecpar!.pointee.codec_type {
|
||||
indices.append(i)
|
||||
}
|
||||
}
|
||||
return indices
|
||||
}
|
||||
|
||||
static func streamFpsAndTimeBase(stream: UnsafePointer<AVStream>, defaultTimeBase: CMTime) -> (fps: CMTime, timebase: CMTime) {
|
||||
let timebase: CMTime
|
||||
var fps: CMTime
|
||||
|
||||
if stream.pointee.time_base.den != 0 && stream.pointee.time_base.num != 0 {
|
||||
timebase = CMTimeMake(Int64(stream.pointee.time_base.num), stream.pointee.time_base.den)
|
||||
} else if stream.pointee.codec.pointee.time_base.den != 0 && stream.pointee.codec.pointee.time_base.num != 0 {
|
||||
timebase = CMTimeMake(Int64(stream.pointee.codec.pointee.time_base.num), stream.pointee.codec.pointee.time_base.den)
|
||||
} else {
|
||||
timebase = defaultTimeBase
|
||||
}
|
||||
|
||||
if stream.pointee.avg_frame_rate.den != 0 && stream.pointee.avg_frame_rate.num != 0 {
|
||||
fps = CMTimeMake(Int64(stream.pointee.avg_frame_rate.num), stream.pointee.avg_frame_rate.den)
|
||||
} else if stream.pointee.r_frame_rate.den != 0 && stream.pointee.r_frame_rate.num != 0 {
|
||||
fps = CMTimeMake(Int64(stream.pointee.r_frame_rate.num), stream.pointee.r_frame_rate.den)
|
||||
} else {
|
||||
fps = CMTimeMake(1, 24)
|
||||
}
|
||||
|
||||
return (fps, timebase)
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,16 +13,16 @@ final class FFMpegMediaPassthroughVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
func decode(frame: MediaTrackDecodableFrame) -> MediaTrackFrame? {
|
||||
var blockBuffer: CMBlockBuffer?
|
||||
|
||||
let bytes = malloc(Int(frame.packet.packet.size))!
|
||||
memcpy(bytes, frame.packet.packet.data, Int(frame.packet.packet.size))
|
||||
guard CMBlockBufferCreateWithMemoryBlock(nil, bytes, Int(frame.packet.packet.size), nil, nil, 0, Int(frame.packet.packet.size), 0, &blockBuffer) == noErr else {
|
||||
let bytes = malloc(Int(frame.packet.size))!
|
||||
memcpy(bytes, frame.packet.data, Int(frame.packet.size))
|
||||
guard CMBlockBufferCreateWithMemoryBlock(nil, bytes, Int(frame.packet.size), nil, nil, 0, Int(frame.packet.size), 0, &blockBuffer) == noErr else {
|
||||
free(bytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
var timingInfo = CMSampleTimingInfo(duration: frame.duration, presentationTimeStamp: frame.pts, decodeTimeStamp: frame.dts)
|
||||
var sampleBuffer: CMSampleBuffer?
|
||||
var sampleSize = Int(frame.packet.packet.size)
|
||||
var sampleSize = Int(frame.packet.size)
|
||||
guard CMSampleBufferCreate(nil, blockBuffer, true, nil, nil, self.videoFormat, 1, 1, &timingInfo, 1, &sampleSize, &sampleBuffer) == noErr else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
import TelegramUIPrivateModule
|
||||
import CoreMedia
|
||||
import Accelerate
|
||||
import FFMpeg
|
||||
|
||||
private let bufferCount = 32
|
||||
|
||||
final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
private let codecContext: UnsafeMutablePointer<AVCodecContext>
|
||||
private let codecContext: FFMpegAVCodecContext
|
||||
|
||||
private let videoFrame: UnsafeMutablePointer<AVFrame>
|
||||
private let videoFrame: FFMpegAVFrame
|
||||
private var resetDecoderOnNextFrame = true
|
||||
|
||||
private var pixelBufferPool: CVPixelBufferPool?
|
||||
|
||||
private var delayedFrames: [MediaTrackFrame] = []
|
||||
|
||||
init(codecContext: UnsafeMutablePointer<AVCodecContext>) {
|
||||
init(codecContext: FFMpegAVCodecContext) {
|
||||
self.codecContext = codecContext
|
||||
self.videoFrame = av_frame_alloc()
|
||||
self.videoFrame = FFMpegAVFrame()
|
||||
|
||||
/*var sourcePixelBufferOptions: [String: Any] = [:]
|
||||
sourcePixelBufferOptions[kCVPixelBufferPixelFormatTypeKey as String] = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange as NSNumber
|
||||
@ -40,13 +40,6 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
self.pixelBufferPool = pixelBufferPool*/
|
||||
}
|
||||
|
||||
deinit {
|
||||
av_frame_unref(self.videoFrame)
|
||||
|
||||
var codecContextRef: UnsafeMutablePointer<AVCodecContext>? = codecContext
|
||||
avcodec_free_context(&codecContextRef)
|
||||
}
|
||||
|
||||
func decodeInternal(frame: MediaTrackDecodableFrame) {
|
||||
|
||||
}
|
||||
@ -56,11 +49,10 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
}
|
||||
|
||||
func decode(frame: MediaTrackDecodableFrame, ptsOffset: CMTime?) -> MediaTrackFrame? {
|
||||
var status = frame.packet.sendToDecoder(self.codecContext)
|
||||
var status = frame.packet.send(toDecoder: self.codecContext)
|
||||
if status == 0 {
|
||||
status = avcodec_receive_frame(self.codecContext, self.videoFrame)
|
||||
if status == 0 {
|
||||
var pts = CMTimeMake(self.videoFrame.pointee.pts, frame.pts.timescale)
|
||||
if self.codecContext.receive(into: self.videoFrame) {
|
||||
var pts = CMTimeMake(self.videoFrame.pts, frame.pts.timescale)
|
||||
if let ptsOffset = ptsOffset {
|
||||
pts = CMTimeAdd(pts, ptsOffset)
|
||||
}
|
||||
@ -87,11 +79,11 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
private func convertVideoFrame(_ frame: UnsafeMutablePointer<AVFrame>, pts: CMTime, dts: CMTime, duration: CMTime) -> MediaTrackFrame? {
|
||||
if frame.pointee.data.0 == nil {
|
||||
private func convertVideoFrame(_ frame: FFMpegAVFrame, pts: CMTime, dts: CMTime, duration: CMTime) -> MediaTrackFrame? {
|
||||
if frame.data[0] == nil {
|
||||
return nil
|
||||
}
|
||||
if frame.pointee.linesize.1 != frame.pointee.linesize.2 {
|
||||
if frame.lineSize[1] != frame.lineSize[2] {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -107,15 +99,15 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
let ioSurfaceProperties = NSMutableDictionary()
|
||||
ioSurfaceProperties["IOSurfaceIsGlobal"] = true as NSNumber
|
||||
|
||||
var options: [String: Any] = [kCVPixelBufferBytesPerRowAlignmentKey as String: frame.pointee.linesize.0 as NSNumber]
|
||||
var options: [String: Any] = [kCVPixelBufferBytesPerRowAlignmentKey as String: frame.lineSize[0] as NSNumber]
|
||||
/*if #available(iOSApplicationExtension 9.0, *) {
|
||||
options[kCVPixelBufferOpenGLESTextureCacheCompatibilityKey as String] = true as NSNumber
|
||||
}*/
|
||||
options[kCVPixelBufferIOSurfacePropertiesKey as String] = ioSurfaceProperties
|
||||
|
||||
CVPixelBufferCreate(kCFAllocatorDefault,
|
||||
Int(frame.pointee.width),
|
||||
Int(frame.pointee.height),
|
||||
Int(frame.width),
|
||||
Int(frame.height),
|
||||
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
|
||||
options as CFDictionary,
|
||||
&pixelBufferRef)
|
||||
@ -125,7 +117,7 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
let srcPlaneSize = Int(frame.pointee.linesize.1) * Int(frame.pointee.height / 2)
|
||||
let srcPlaneSize = Int(frame.lineSize[1]) * Int(frame.height / 2)
|
||||
let dstPlaneSize = srcPlaneSize * 2
|
||||
|
||||
let dstPlane = malloc(dstPlaneSize)!.assumingMemoryBound(to: UInt8.self)
|
||||
@ -134,8 +126,8 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
}
|
||||
|
||||
for i in 0 ..< srcPlaneSize {
|
||||
dstPlane[2 * i] = frame.pointee.data.1![i]
|
||||
dstPlane[2 * i + 1] = frame.pointee.data.2![i]
|
||||
dstPlane[2 * i] = frame.data[1]![i]
|
||||
dstPlane[2 * i + 1] = frame.data[2]![i]
|
||||
}
|
||||
|
||||
let status = CVPixelBufferLockBaseAddress(pixelBuffer, [])
|
||||
@ -148,13 +140,13 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
let bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1)
|
||||
|
||||
var base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)!
|
||||
if bytePerRowY == frame.pointee.linesize.0 {
|
||||
memcpy(base, frame.pointee.data.0!, bytePerRowY * Int(frame.pointee.height))
|
||||
if bytePerRowY == frame.lineSize[0] {
|
||||
memcpy(base, frame.data[0]!, bytePerRowY * Int(frame.height))
|
||||
} else {
|
||||
var dest = base
|
||||
var src = frame.pointee.data.0!
|
||||
let linesize = Int(frame.pointee.linesize.0)
|
||||
for _ in 0 ..< Int(frame.pointee.height) {
|
||||
var src = frame.data[0]!
|
||||
let linesize = Int(frame.lineSize[0])
|
||||
for _ in 0 ..< Int(frame.height) {
|
||||
memcpy(dest, src, linesize)
|
||||
dest = dest.advanced(by: bytePerRowY)
|
||||
src = src.advanced(by: linesize)
|
||||
@ -162,13 +154,13 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
}
|
||||
|
||||
base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)!
|
||||
if bytesPerRowUV == frame.pointee.linesize.1 * 2 {
|
||||
memcpy(base, dstPlane, bytesPerRowUV * Int(frame.pointee.height) / 2)
|
||||
if bytesPerRowUV == frame.lineSize[1] * 2 {
|
||||
memcpy(base, dstPlane, bytesPerRowUV * Int(frame.height) / 2)
|
||||
} else {
|
||||
var dest = base
|
||||
var src = dstPlane
|
||||
let linesize = Int(frame.pointee.linesize.1) * 2
|
||||
for _ in 0 ..< Int(frame.pointee.height) / 2 {
|
||||
let linesize = Int(frame.lineSize[1]) * 2
|
||||
for _ in 0 ..< Int(frame.height) / 2 {
|
||||
memcpy(dest, src, linesize)
|
||||
dest = dest.advanced(by: bytesPerRowUV)
|
||||
src = src.advanced(by: linesize)
|
||||
@ -229,7 +221,7 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
}
|
||||
|
||||
func reset() {
|
||||
avcodec_flush_buffers(self.codecContext)
|
||||
self.codecContext.flushBuffers()
|
||||
self.resetDecoderOnNextFrame = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
import Foundation
|
||||
import TelegramUIPrivateModule
|
||||
|
||||
final class FFMpegPacket {
|
||||
var packet = AVPacket()
|
||||
|
||||
init() {
|
||||
av_init_packet(&self.packet)
|
||||
}
|
||||
|
||||
deinit {
|
||||
av_packet_unref(&self.packet)
|
||||
}
|
||||
|
||||
var pts: Int64 {
|
||||
let avNoPtsRawValue: UInt64 = 0x8000000000000000
|
||||
let avNoPtsValue = Int64(bitPattern: avNoPtsRawValue)
|
||||
let packetPts = self.packet.pts == avNoPtsValue ? self.packet.dts : self.packet.pts
|
||||
|
||||
return packetPts
|
||||
}
|
||||
|
||||
func sendToDecoder(_ codecContext: UnsafeMutablePointer<AVCodecContext>) -> Int32 {
|
||||
return avcodec_send_packet(codecContext, &self.packet)
|
||||
}
|
||||
}
|
||||
@ -80,7 +80,7 @@ private enum FeedGroupingSection: ItemListSectionId {
|
||||
|
||||
private enum FeedGroupingEntry: ItemListNodeEntry {
|
||||
case groupHeader(PresentationTheme, String)
|
||||
case peer(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Int, Peer, Bool)
|
||||
case peer(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Int, Peer, Bool)
|
||||
case ungroup(PresentationTheme, String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
@ -96,7 +96,7 @@ private enum FeedGroupingEntry: ItemListNodeEntry {
|
||||
switch self {
|
||||
case .groupHeader:
|
||||
return .index(0)
|
||||
case let .peer(_, _, _, _, peer, _):
|
||||
case let .peer(_, _, _, _, _, peer, _):
|
||||
return .peer(peer.id)
|
||||
case .ungroup:
|
||||
return .index(1)
|
||||
@ -111,8 +111,8 @@ private enum FeedGroupingEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peer(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsIndex, lhsPeer, lhsValue):
|
||||
if case let .peer(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsIndex, rhsPeer, rhsValue) = rhs {
|
||||
case let .peer(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsIndex, lhsPeer, lhsValue):
|
||||
if case let .peer(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsIndex, rhsPeer, rhsValue) = rhs {
|
||||
if lhsTheme !== rhsTheme {
|
||||
return false
|
||||
}
|
||||
@ -122,6 +122,9 @@ private enum FeedGroupingEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameOrder != rhsNameOrder {
|
||||
return false
|
||||
}
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -148,11 +151,11 @@ private enum FeedGroupingEntry: ItemListNodeEntry {
|
||||
switch lhs {
|
||||
case .groupHeader:
|
||||
return true
|
||||
case let .peer(_, _, _, index, _, _):
|
||||
case let .peer(_, _, _, _, index, _, _):
|
||||
switch rhs {
|
||||
case .groupHeader:
|
||||
return false
|
||||
case let .peer(_, _, _, rhsIndex, _, _):
|
||||
case let .peer(_, _, _, _, rhsIndex, _, _):
|
||||
return index < rhsIndex
|
||||
default:
|
||||
return true
|
||||
@ -166,8 +169,8 @@ private enum FeedGroupingEntry: ItemListNodeEntry {
|
||||
switch self {
|
||||
case let .groupHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .peer(theme, strings, dateTimeFormat, _, peer, value):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: value, style: .standard), enabled: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: { value in
|
||||
case let .peer(theme, strings, dateTimeFormat, nameDisplayOrder, _, peer, value):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: value, style: .standard), enabled: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: { value in
|
||||
arguments.togglePeer(peer, value)
|
||||
})
|
||||
case let .ungroup(theme, text):
|
||||
@ -200,17 +203,19 @@ private final class FeedGroupingState {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let peers: [FeedGroupingPeerState]
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, peers: [FeedGroupingPeerState]) {
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, peers: [FeedGroupingPeerState]) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.peers = peers
|
||||
}
|
||||
|
||||
func withUpdatedPeers(_ peers: [FeedGroupingPeerState]) -> FeedGroupingState {
|
||||
return FeedGroupingState(theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, peers: peers)
|
||||
return FeedGroupingState(theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, peers: peers)
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,7 +225,7 @@ private func entriesStateFromState(_ state: FeedGroupingState) -> FeedGroupingEn
|
||||
entries.append(.groupHeader(state.theme, "GROUP CHANNELS"))
|
||||
var index = 0
|
||||
for peer in state.peers {
|
||||
entries.append(.peer(state.theme, state.strings, state.dateTimeFormat, index, peer.peer, peer.included))
|
||||
entries.append(.peer(state.theme, state.strings, state.dateTimeFormat, state.nameDisplayOrder, index, peer.peer, peer.included))
|
||||
index += 1
|
||||
}
|
||||
entries.append(.ungroup(state.theme, "Ungroup All Channels"))
|
||||
@ -284,7 +289,7 @@ final class FeedGroupingControllerNode: ViewControllerTracingNode {
|
||||
self.blockingOverlay = ASDisplayNode()
|
||||
self.blockingOverlay.backgroundColor = self.presentationData.theme.list.blocksBackgroundColor
|
||||
|
||||
self._stateValue = FeedGroupingState(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, peers: [])
|
||||
self._stateValue = FeedGroupingState(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, peers: [])
|
||||
self.statePromise.set(.single(self._stateValue))
|
||||
|
||||
super.init()
|
||||
|
||||
@ -7,7 +7,7 @@ import TelegramUIPrivateModule
|
||||
import Display
|
||||
import UIKit
|
||||
import VideoToolbox
|
||||
|
||||
/*
|
||||
private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: UnsafeMutablePointer<UInt8>?, bufferSize: Int32) -> Int32 {
|
||||
guard let buffer = buffer else {
|
||||
return 0
|
||||
@ -404,3 +404,4 @@ func streamingVideoThumbnail(postbox: Postbox, fileReference: FileMediaReference
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@ -131,7 +131,7 @@ private func galleryMessageCaptionText(_ message: Message) -> String {
|
||||
return message.text
|
||||
}
|
||||
|
||||
func galleryItemForEntry(account: Account, presentationData: PresentationData, entry: MessageHistoryEntry, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }) -> GalleryItem? {
|
||||
func galleryItemForEntry(account: Account, presentationData: PresentationData, entry: MessageHistoryEntry, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }) -> GalleryItem? {
|
||||
switch entry {
|
||||
case let .MessageEntry(message, _, location, _):
|
||||
if let (media, mediaImage) = mediaForMessage(message: message) {
|
||||
@ -141,10 +141,10 @@ func galleryItemForEntry(account: Account, presentationData: PresentationData, e
|
||||
if file.isVideo {
|
||||
let content: UniversalVideoContent
|
||||
if file.isAnimated {
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId + 1, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: false, loopVideo: true, enableSound: false)
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId + 1, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: false, loopVideo: true, enableSound: false, tempFilePath: tempFilePath)
|
||||
} else {
|
||||
if true || (file.mimeType == "video/mpeg4" || file.mimeType == "video/mov" || file.mimeType == "video/mp4") {
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: true, loopVideo: loopVideos)
|
||||
content = NativeVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: true, loopVideo: loopVideos, tempFilePath: tempFilePath)
|
||||
} else {
|
||||
content = PlatformVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), streamVideo: streamVideos, loopVideo: loopVideos)
|
||||
}
|
||||
@ -161,7 +161,7 @@ func galleryItemForEntry(account: Account, presentationData: PresentationData, e
|
||||
return UniversalVideoGalleryItem(account: account, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.author?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: caption, hideControls: hideControls, playbackCompleted: playbackCompleted, performAction: performAction, openActionOptions: openActionOptions)
|
||||
} else {
|
||||
if file.mimeType.hasPrefix("image/") && file.mimeType != "image/gif" {
|
||||
if file.size == nil || file.size! < 5 * 1024 * 1024 {
|
||||
if file.size == nil || file.size! < 2 * 1024 * 1024 {
|
||||
return ChatImageGalleryItem(account: account, presentationData: presentationData, message: message, location: location, performAction: performAction, openActionOptions: openActionOptions)
|
||||
} else {
|
||||
return ChatDocumentGalleryItem(account: account, presentationData: presentationData, message: message, location: location)
|
||||
|
||||
@ -56,7 +56,7 @@ private enum GroupAdminsEntryStableId: Hashable {
|
||||
private enum GroupAdminsEntry: ItemListNodeEntry {
|
||||
case allAdmins(PresentationTheme, String, Bool)
|
||||
case allAdminsInfo(PresentationTheme, String)
|
||||
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, String, Bool, Bool)
|
||||
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, String, Bool, Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -73,7 +73,7 @@ private enum GroupAdminsEntry: ItemListNodeEntry {
|
||||
return .index(0)
|
||||
case .allAdminsInfo:
|
||||
return .index(1)
|
||||
case let .peerItem(_, _, _, _, peer, _, _, _):
|
||||
case let .peerItem(_, _, _, _, _, peer, _, _, _):
|
||||
return .peer(peer.id)
|
||||
}
|
||||
}
|
||||
@ -92,8 +92,8 @@ private enum GroupAdminsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsLabel, lhsToggled, lhsEnabled):
|
||||
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsLabel, rhsToggled, rhsEnabled) = rhs {
|
||||
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer, lhsLabel, lhsToggled, lhsEnabled):
|
||||
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer, rhsLabel, rhsToggled, rhsEnabled) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -106,6 +106,9 @@ private enum GroupAdminsEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameOrder != rhsNameOrder {
|
||||
return false
|
||||
}
|
||||
if !lhsPeer.isEqual(rhsPeer) {
|
||||
return false
|
||||
}
|
||||
@ -136,9 +139,9 @@ private enum GroupAdminsEntry: ItemListNodeEntry {
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case let .peerItem(index, _, _, _, _, _, _, _):
|
||||
case let .peerItem(index, _, _, _, _, _, _, _, _):
|
||||
switch rhs {
|
||||
case let .peerItem(rhsIndex, _, _, _, _, _, _, _):
|
||||
case let .peerItem(rhsIndex, _, _, _, _, _, _, _, _):
|
||||
return index < rhsIndex
|
||||
case .allAdmins, .allAdminsInfo:
|
||||
return false
|
||||
@ -154,8 +157,8 @@ private enum GroupAdminsEntry: ItemListNodeEntry {
|
||||
})
|
||||
case let .allAdminsInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
case let .peerItem(_, theme, strings, dateTimeFormat, peer, label, toggled, enabled):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: toggled, style: .standard), enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: { value in
|
||||
case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, label, toggled, enabled):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: toggled, style: .standard), enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: { value in
|
||||
arguments.updatePeerIsAdmin(peer.id, value)
|
||||
})
|
||||
}
|
||||
@ -276,7 +279,7 @@ private func groupAdminsControllerEntries(account: Account, presentationData: Pr
|
||||
}
|
||||
}
|
||||
}
|
||||
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, label, isAdmin, isEnabled))
|
||||
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, label, isAdmin, isEnabled))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,7 +149,7 @@ private enum GroupInfoEntry: ItemListNodeEntry {
|
||||
case membersAdmins(PresentationTheme, String, String)
|
||||
case membersBlacklist(PresentationTheme, String, String)
|
||||
case addMember(PresentationTheme, String, editing: Bool)
|
||||
case member(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, index: Int, peerId: PeerId, peer: Peer, participant: RenderedChannelParticipant?, presence: PeerPresence?, memberStatus: GroupInfoMemberStatus, editing: ItemListPeerItemEditing, revealActions: [ParticipantRevealAction], enabled: Bool)
|
||||
case member(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, index: Int, peerId: PeerId, peer: Peer, participant: RenderedChannelParticipant?, presence: PeerPresence?, memberStatus: GroupInfoMemberStatus, editing: ItemListPeerItemEditing, revealActions: [ParticipantRevealAction], enabled: Bool)
|
||||
case convertToSupergroup(PresentationTheme, String)
|
||||
case leave(PresentationTheme, String)
|
||||
|
||||
@ -343,8 +343,8 @@ private enum GroupInfoEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .member(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsIndex, lhsPeerId, lhsPeer, lhsParticipant, lhsPresence, lhsMemberStatus, lhsEditing, lhsActions, lhsEnabled):
|
||||
if case let .member(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsIndex, rhsPeerId, rhsPeer, rhsParticipant, rhsPresence, rhsMemberStatus, rhsEditing, rhsActions, rhsEnabled) = rhs {
|
||||
case let .member(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameDisplayOrder, lhsIndex, lhsPeerId, lhsPeer, lhsParticipant, lhsPresence, lhsMemberStatus, lhsEditing, lhsActions, lhsEnabled):
|
||||
if case let .member(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameDisplayOrder, rhsIndex, rhsPeerId, rhsPeer, rhsParticipant, rhsPresence, rhsMemberStatus, rhsEditing, rhsActions, rhsEnabled) = rhs {
|
||||
if lhsTheme !== rhsTheme {
|
||||
return false
|
||||
}
|
||||
@ -354,6 +354,9 @@ private enum GroupInfoEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameDisplayOrder != rhsNameDisplayOrder {
|
||||
return false
|
||||
}
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -394,7 +397,7 @@ private enum GroupInfoEntry: ItemListNodeEntry {
|
||||
|
||||
var stableId: GroupEntryStableId {
|
||||
switch self {
|
||||
case let .member(_, _, _, _, peerId, _, _, _, _, _, _, _):
|
||||
case let .member(_, _, _, _, _, peerId, _, _, _, _, _, _, _):
|
||||
return .peer(peerId)
|
||||
default:
|
||||
return .index(self.sortIndex)
|
||||
@ -439,7 +442,7 @@ private enum GroupInfoEntry: ItemListNodeEntry {
|
||||
return 16
|
||||
case .addMember:
|
||||
return 17
|
||||
case let .member(_, _, _, index, _, _, _, _, _, _, _, _):
|
||||
case let .member(_, _, _, _, index, _, _, _, _, _, _, _, _):
|
||||
return 20 + index
|
||||
case .convertToSupergroup:
|
||||
return 100000
|
||||
@ -526,7 +529,7 @@ private enum GroupInfoEntry: ItemListNodeEntry {
|
||||
return ItemListDisclosureItem(theme: theme, title: title, label: text, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.pushController(channelBlacklistController(account: arguments.account, peerId: arguments.peerId))
|
||||
})
|
||||
case let .member(theme, strings, dateTimeFormat, _, _, peer, participant, presence, memberStatus, editing, actions, enabled):
|
||||
case let .member(theme, strings, dateTimeFormat, nameDisplayOrder, _, _, peer, participant, presence, memberStatus, editing, actions, enabled):
|
||||
let label: String?
|
||||
switch memberStatus {
|
||||
case .admin:
|
||||
@ -551,7 +554,7 @@ private enum GroupInfoEntry: ItemListNodeEntry {
|
||||
}
|
||||
}))
|
||||
}
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: presence, text: .presence, label: label == nil ? .none : .text(label!), editing: editing, revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: enabled, sectionId: self.section, action: {
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: presence, text: .presence, label: label == nil ? .none : .text(label!), editing: editing, revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: enabled, sectionId: self.section, action: {
|
||||
if let infoController = peerInfoController(account: arguments.account, peer: peer) {
|
||||
arguments.pushController(infoController)
|
||||
}
|
||||
@ -957,7 +960,7 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
|
||||
} else {
|
||||
memberStatus = .member
|
||||
}
|
||||
entries.append(GroupInfoEntry.member(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, index: i, peerId: peer.id, peer: peer, participant: nil, presence: peerPresences[peer.id], memberStatus: memberStatus, editing: ItemListPeerItemEditing(editable: canRemoveParticipant(account: account, isAdmin: canEditMembers, participantId: peer.id, invitedBy: sortedParticipants[i].invitedBy), editing: state.editingState != nil && canRemoveAnyMember, revealed: state.peerIdWithRevealedOptions == peer.id), revealActions: [ParticipantRevealAction(type: .destructive, title: presentationData.strings.Common_Delete, action: .remove)], enabled: !disabledPeerIds.contains(peer.id)))
|
||||
entries.append(GroupInfoEntry.member(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index: i, peerId: peer.id, peer: peer, participant: nil, presence: peerPresences[peer.id], memberStatus: memberStatus, editing: ItemListPeerItemEditing(editable: canRemoveParticipant(account: account, isAdmin: canEditMembers, participantId: peer.id, invitedBy: sortedParticipants[i].invitedBy), editing: state.editingState != nil && canRemoveAnyMember, revealed: state.peerIdWithRevealedOptions == peer.id), revealActions: [ParticipantRevealAction(type: .destructive, title: presentationData.strings.Common_Delete, action: .remove)], enabled: !disabledPeerIds.contains(peer.id)))
|
||||
}
|
||||
}
|
||||
} else if let channel = view.peers[view.peerId] as? TelegramChannel, let cachedChannelData = view.cachedData as? CachedChannelData, let memberCount = cachedChannelData.participantsSummary.memberCount {
|
||||
@ -1079,7 +1082,7 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
|
||||
peerActions.append(ParticipantRevealAction(type: .destructive, title: presentationData.strings.Common_Delete, action: .remove))
|
||||
}
|
||||
|
||||
entries.append(GroupInfoEntry.member(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, index: i, peerId: participant.peer.id, peer: participant.peer, participant: participant, presence: participant.presences[participant.peer.id], memberStatus: memberStatus, editing: ItemListPeerItemEditing(editable: !peerActions.isEmpty, editing: state.editingState != nil && canRemoveAnyMember, revealed: state.peerIdWithRevealedOptions == participant.peer.id), revealActions: peerActions, enabled: !disabledPeerIds.contains(participant.peer.id)))
|
||||
entries.append(GroupInfoEntry.member(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index: i, peerId: participant.peer.id, peer: participant.peer, participant: participant, presence: participant.presences[participant.peer.id], memberStatus: memberStatus, editing: ItemListPeerItemEditing(editable: !peerActions.isEmpty, editing: state.editingState != nil && canRemoveAnyMember, revealed: state.peerIdWithRevealedOptions == participant.peer.id), revealActions: peerActions, enabled: !disabledPeerIds.contains(participant.peer.id)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1780,6 +1783,8 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl
|
||||
if let infoController = peerInfoController(account: account, peer: peer) {
|
||||
arguments.pushController(infoController)
|
||||
}
|
||||
}, present: { c, a in
|
||||
presentControllerImpl?(c, a)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -10,15 +10,19 @@ final class ChannelMembersSearchItem: ItemListControllerSearch {
|
||||
let peerId: PeerId
|
||||
let cancel: () -> Void
|
||||
let openPeer: (Peer, RenderedChannelParticipant?) -> Void
|
||||
let present: (ViewController, Any?) -> Void
|
||||
let searchMode: ChannelMembersSearchMode
|
||||
|
||||
private var updateActivity: ((Bool) -> Void)?
|
||||
private var activity: ValuePromise<Bool> = ValuePromise(ignoreRepeated: false)
|
||||
private let activityDisposable = MetaDisposable()
|
||||
init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode = .searchMembers, cancel: @escaping () -> Void, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) {
|
||||
|
||||
init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode = .searchMembers, cancel: @escaping () -> Void, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
self.account = account
|
||||
self.peerId = peerId
|
||||
self.cancel = cancel
|
||||
self.openPeer = openPeer
|
||||
self.present = present
|
||||
self.searchMode = searchMode
|
||||
activityDisposable.set((activity.get() |> mapToSignal { value -> Signal<Bool, NoError> in
|
||||
if value {
|
||||
@ -32,7 +36,7 @@ final class ChannelMembersSearchItem: ItemListControllerSearch {
|
||||
}
|
||||
|
||||
deinit {
|
||||
activityDisposable.dispose()
|
||||
self.activityDisposable.dispose()
|
||||
}
|
||||
|
||||
func isEqual(to: ItemListControllerSearch) -> Bool {
|
||||
@ -63,6 +67,8 @@ final class ChannelMembersSearchItem: ItemListControllerSearch {
|
||||
func node(current: ItemListControllerSearchNode?) -> ItemListControllerSearchNode {
|
||||
return ChannelMembersSearchItemNode(account: self.account, peerId: self.peerId, searchMode: self.searchMode, openPeer: self.openPeer, cancel: self.cancel, updateActivity: { [weak self] value in
|
||||
self?.activity.set(value)
|
||||
}, present: { [weak self] c, a in
|
||||
self?.present(c, a)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -70,10 +76,10 @@ final class ChannelMembersSearchItem: ItemListControllerSearch {
|
||||
private final class ChannelMembersSearchItemNode: ItemListControllerSearchNode {
|
||||
private let containerNode: ChannelMembersSearchContainerNode
|
||||
|
||||
init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(Bool)->Void) {
|
||||
init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||
self.containerNode = ChannelMembersSearchContainerNode(account: account, peerId: peerId, mode: searchMode, filters: [], openPeer: { peer, participant in
|
||||
openPeer(peer, participant)
|
||||
}, updateActivity: updateActivity)
|
||||
}, updateActivity: updateActivity, present: present)
|
||||
self.containerNode.cancel = {
|
||||
cancel()
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ private enum GroupsInCommonEntryStableId: Hashable {
|
||||
}
|
||||
|
||||
private enum GroupsInCommonEntry: ItemListNodeEntry {
|
||||
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer)
|
||||
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -53,15 +53,15 @@ private enum GroupsInCommonEntry: ItemListNodeEntry {
|
||||
|
||||
var stableId: GroupsInCommonEntryStableId {
|
||||
switch self {
|
||||
case let .peerItem(_, _, _, _, peer):
|
||||
case let .peerItem(_, _, _, _, _, peer):
|
||||
return .peer(peer.id)
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: GroupsInCommonEntry, rhs: GroupsInCommonEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer):
|
||||
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer) = rhs {
|
||||
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer):
|
||||
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -77,6 +77,9 @@ private enum GroupsInCommonEntry: ItemListNodeEntry {
|
||||
if !lhsPeer.isEqual(rhsPeer) {
|
||||
return false
|
||||
}
|
||||
if lhsNameOrder != rhsNameOrder {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -86,9 +89,9 @@ private enum GroupsInCommonEntry: ItemListNodeEntry {
|
||||
|
||||
static func <(lhs: GroupsInCommonEntry, rhs: GroupsInCommonEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .peerItem(index, _, _, _, _):
|
||||
case let .peerItem(index, _, _, _, _, _):
|
||||
switch rhs {
|
||||
case let .peerItem(rhsIndex, _, _, _, _):
|
||||
case let .peerItem(rhsIndex, _, _, _, _, _):
|
||||
return index < rhsIndex
|
||||
}
|
||||
}
|
||||
@ -96,8 +99,8 @@ private enum GroupsInCommonEntry: ItemListNodeEntry {
|
||||
|
||||
func item(_ arguments: GroupsInCommonControllerArguments) -> ListViewItem {
|
||||
switch self {
|
||||
case let .peerItem(_, theme, strings, dateTimeFormat, peer):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: {
|
||||
case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: {
|
||||
arguments.openPeer(peer.id)
|
||||
}, setPeerIdWithRevealedOptions: { _, _ in
|
||||
}, removePeer: { _ in
|
||||
@ -118,7 +121,7 @@ private func groupsInCommonControllerEntries(presentationData: PresentationData,
|
||||
if let peers = peers {
|
||||
var index: Int32 = 0
|
||||
for peer in peers {
|
||||
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer))
|
||||
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,6 +63,7 @@ final class ItemListPeerItem: ListViewItem, ItemListItem {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let account: Account
|
||||
let peer: Peer
|
||||
let aliasHandling: ItemListPeerItemAliasHandling
|
||||
@ -82,10 +83,11 @@ final class ItemListPeerItem: ListViewItem, ItemListItem {
|
||||
let hasTopStripe: Bool
|
||||
let hasTopGroupInset: Bool
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, account: Account, peer: Peer, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true) {
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, account: Account, peer: Peer, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.account = account
|
||||
self.peer = peer
|
||||
self.aliasHandling = aliasHandling
|
||||
@ -332,9 +334,16 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
} else if let user = item.peer as? TelegramUser {
|
||||
if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty {
|
||||
let string = NSMutableAttributedString()
|
||||
string.append(NSAttributedString(string: firstName, font: titleFont, textColor: titleColor))
|
||||
string.append(NSAttributedString(string: " ", font: titleFont, textColor: titleColor))
|
||||
string.append(NSAttributedString(string: lastName, font: titleBoldFont, textColor: titleColor))
|
||||
switch item.nameDisplayOrder {
|
||||
case .firstLast:
|
||||
string.append(NSAttributedString(string: firstName, font: titleFont, textColor: titleColor))
|
||||
string.append(NSAttributedString(string: " ", font: titleFont, textColor: titleColor))
|
||||
string.append(NSAttributedString(string: lastName, font: titleBoldFont, textColor: titleColor))
|
||||
case .lastFirst:
|
||||
string.append(NSAttributedString(string: lastName, font: titleBoldFont, textColor: titleColor))
|
||||
string.append(NSAttributedString(string: " ", font: titleFont, textColor: titleColor))
|
||||
string.append(NSAttributedString(string: firstName, font: titleFont, textColor: titleColor))
|
||||
}
|
||||
titleAttributedString = string
|
||||
} else if let firstName = user.firstName, !firstName.isEmpty {
|
||||
titleAttributedString = NSAttributedString(string: firstName, font: titleBoldFont, textColor: titleColor)
|
||||
|
||||
@ -24,6 +24,7 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
let website: WebAuthorization
|
||||
let peer: Peer?
|
||||
let enabled: Bool
|
||||
@ -33,10 +34,11 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem {
|
||||
let setSessionIdWithRevealedOptions: (Int64?, Int64?) -> Void
|
||||
let removeSession: (Int64) -> Void
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void) {
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.website = website
|
||||
self.peer = peer
|
||||
self.enabled = enabled
|
||||
@ -174,7 +176,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode {
|
||||
let rightInset: CGFloat = params.rightInset
|
||||
|
||||
if let user = item.peer as? TelegramUser {
|
||||
titleAttributedString = NSAttributedString(string: user.displayTitle, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor)
|
||||
titleAttributedString = NSAttributedString(string: user.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), font: titleFont, textColor: item.theme.list.itemPrimaryTextColor)
|
||||
}
|
||||
|
||||
var appString = ""
|
||||
|
||||
@ -144,6 +144,7 @@ final class ListMessageFileItemNode: ListMessageNode {
|
||||
|
||||
private let titleNode: TextNode
|
||||
private let descriptionNode: TextNode
|
||||
private let descriptionProgressNode: ImmediateTextNode
|
||||
|
||||
private let extensionIconNode: ASImageNode
|
||||
private let extensionIconText: TextNode
|
||||
@ -171,6 +172,7 @@ final class ListMessageFileItemNode: ListMessageNode {
|
||||
|
||||
private var appliedItem: ListMessageItem?
|
||||
private var layoutParams: ListViewItemLayoutParams?
|
||||
private var contentSizeValue: CGSize?
|
||||
private var currentLeftOffet: CGFloat = 0.0
|
||||
|
||||
override var canBeLongTapped: Bool {
|
||||
@ -191,6 +193,10 @@ final class ListMessageFileItemNode: ListMessageNode {
|
||||
self.descriptionNode = TextNode()
|
||||
self.descriptionNode.isUserInteractionEnabled = false
|
||||
|
||||
self.descriptionProgressNode = ImmediateTextNode()
|
||||
self.descriptionProgressNode.isUserInteractionEnabled = false
|
||||
self.descriptionProgressNode.maximumNumberOfLines = 1
|
||||
|
||||
self.extensionIconNode = ASImageNode()
|
||||
self.extensionIconNode.isLayerBacked = true
|
||||
self.extensionIconNode.displaysAsynchronously = false
|
||||
@ -223,6 +229,7 @@ final class ListMessageFileItemNode: ListMessageNode {
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.descriptionNode)
|
||||
self.addSubnode(self.descriptionProgressNode)
|
||||
self.addSubnode(self.extensionIconNode)
|
||||
self.addSubnode(self.extensionIconText)
|
||||
self.addSubnode(self.statusNode)
|
||||
@ -492,6 +499,7 @@ final class ListMessageFileItemNode: ListMessageNode {
|
||||
strongSelf.account = item.account
|
||||
strongSelf.appliedItem = item
|
||||
strongSelf.layoutParams = params
|
||||
strongSelf.contentSizeValue = nodeLayout.contentSize
|
||||
strongSelf.currentLeftOffet = leftOffset
|
||||
|
||||
if let _ = updatedTheme {
|
||||
@ -604,96 +612,105 @@ final class ListMessageFileItemNode: ListMessageNode {
|
||||
if let updatedStatusSignal = updatedStatusSignal {
|
||||
strongSelf.statusDisposable.set((updatedStatusSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak strongSelf] fileStatus in
|
||||
let status = fileStatus.mediaStatus
|
||||
displayLinkDispatcher.dispatch {
|
||||
if let strongSelf = strongSelf {
|
||||
strongSelf.fetchStatus = fileStatus.fetchStatus
|
||||
strongSelf.resourceStatus = status
|
||||
var musicIsPlaying: Bool?
|
||||
var statusState: RadialStatusNodeState = .none
|
||||
if !isAudio {
|
||||
if let layoutParams = strongSelf.layoutParams {
|
||||
strongSelf.updateProgressFrame(size: nodeLayout.contentSize, leftInset: layoutParams.leftInset, rightInset: layoutParams.rightInset, transition: .immediate)
|
||||
}
|
||||
} else {
|
||||
switch fileStatus.fetchStatus {
|
||||
case let .Fetching(isActive, progress):
|
||||
var adjustedProgress = progress
|
||||
if isActive {
|
||||
adjustedProgress = max(adjustedProgress, 0.027)
|
||||
}
|
||||
statusState = .cloudProgress(color: item.theme.list.itemAccentColor, strokeBackgroundColor: item.theme.list.itemAccentColor.withAlphaComponent(0.5), lineWidth: 2.0, value: CGFloat(adjustedProgress))
|
||||
case .Local:
|
||||
break
|
||||
case .Remote:
|
||||
if let image = PresentationResourcesItemList.cloudFetchIcon(item.theme) {
|
||||
statusState = .customIcon(image)
|
||||
}
|
||||
}
|
||||
strongSelf.statusNode.transitionToState(statusState, completion: {})
|
||||
strongSelf.statusButtonNode.isUserInteractionEnabled = statusState != .none
|
||||
|
||||
switch status {
|
||||
case let .fetchStatus(fetchStatus):
|
||||
switch fetchStatus {
|
||||
case let .Fetching(isActive, progress):
|
||||
var adjustedProgress = progress
|
||||
if isActive {
|
||||
adjustedProgress = max(adjustedProgress, 0.027)
|
||||
}
|
||||
strongSelf.progressNode.state = .Fetching(progress: adjustedProgress)
|
||||
case .Local:
|
||||
if isAudio {
|
||||
strongSelf.progressNode.state = .Play
|
||||
} else {
|
||||
strongSelf.progressNode.state = .Icon
|
||||
}
|
||||
case .Remote:
|
||||
if isAudio {
|
||||
strongSelf.progressNode.state = .Play
|
||||
} else {
|
||||
strongSelf.progressNode.state = .Remote
|
||||
}
|
||||
}
|
||||
case let .playbackStatus(playbackStatus):
|
||||
switch playbackStatus {
|
||||
case .playing:
|
||||
musicIsPlaying = true
|
||||
strongSelf.progressNode.state = .Pause
|
||||
case .paused:
|
||||
musicIsPlaying = false
|
||||
strongSelf.progressNode.state = .Play
|
||||
}
|
||||
}
|
||||
}
|
||||
if let musicIsPlaying = musicIsPlaying {
|
||||
if strongSelf.playbackOverlayNode == nil {
|
||||
let playbackOverlayNode = ListMessagePlaybackOverlayNode()
|
||||
playbackOverlayNode.frame = strongSelf.iconImageNode.frame
|
||||
strongSelf.playbackOverlayNode = playbackOverlayNode
|
||||
strongSelf.addSubnode(playbackOverlayNode)
|
||||
}
|
||||
strongSelf.playbackOverlayNode?.isPlaying = musicIsPlaying
|
||||
} else if let playbackOverlayNode = strongSelf.playbackOverlayNode {
|
||||
strongSelf.playbackOverlayNode = nil
|
||||
playbackOverlayNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
if let strongSelf = strongSelf {
|
||||
strongSelf.fetchStatus = fileStatus.fetchStatus
|
||||
strongSelf.resourceStatus = fileStatus.mediaStatus
|
||||
strongSelf.updateStatus(transition: .immediate)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
strongSelf.updateProgressFrame(size: CGSize(width: params.width, height: 52.0), leftInset: params.leftInset, rightInset: params.rightInset, transition: transition)
|
||||
transition.updateFrame(node: strongSelf.downloadStatusIconNode, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset, y: 31.0), size: CGSize(width: 11.0, height: 11.0)))
|
||||
|
||||
if let updatedFetchControls = updatedFetchControls {
|
||||
let _ = strongSelf.fetchControls.swap(updatedFetchControls)
|
||||
}
|
||||
|
||||
strongSelf.updateStatus(transition: transition)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func updateStatus(transition: ContainedViewLayoutTransition) {
|
||||
guard let item = self.item, let media = self.currentMedia, let fetchStatus = self.fetchStatus, let status = self.resourceStatus, let layoutParams = self.layoutParams, let contentSize = self.contentSizeValue else {
|
||||
return
|
||||
}
|
||||
|
||||
var isAudio = false
|
||||
if let file = media as? TelegramMediaFile {
|
||||
isAudio = file.isMusic || file.isVoice
|
||||
}
|
||||
|
||||
var musicIsPlaying: Bool?
|
||||
var statusState: RadialStatusNodeState = .none
|
||||
if !isAudio {
|
||||
self.updateProgressFrame(size: contentSize, leftInset: layoutParams.leftInset, rightInset: layoutParams.rightInset, transition: .immediate)
|
||||
} else {
|
||||
switch fetchStatus {
|
||||
case let .Fetching(isActive, progress):
|
||||
var adjustedProgress = progress
|
||||
if isActive {
|
||||
adjustedProgress = max(adjustedProgress, 0.027)
|
||||
}
|
||||
statusState = .cloudProgress(color: item.theme.list.itemAccentColor, strokeBackgroundColor: item.theme.list.itemAccentColor.withAlphaComponent(0.5), lineWidth: 2.0, value: CGFloat(adjustedProgress))
|
||||
case .Local:
|
||||
break
|
||||
case .Remote:
|
||||
if let image = PresentationResourcesItemList.cloudFetchIcon(item.theme) {
|
||||
statusState = .customIcon(image)
|
||||
}
|
||||
}
|
||||
self.statusNode.transitionToState(statusState, completion: {})
|
||||
self.statusButtonNode.isUserInteractionEnabled = statusState != .none
|
||||
|
||||
switch status {
|
||||
case let .fetchStatus(fetchStatus):
|
||||
switch fetchStatus {
|
||||
case let .Fetching(isActive, progress):
|
||||
var adjustedProgress = progress
|
||||
if isActive {
|
||||
adjustedProgress = max(adjustedProgress, 0.027)
|
||||
}
|
||||
self.progressNode.state = .Fetching(progress: adjustedProgress)
|
||||
case .Local:
|
||||
if isAudio {
|
||||
self.progressNode.state = .Play
|
||||
} else {
|
||||
self.progressNode.state = .Icon
|
||||
}
|
||||
case .Remote:
|
||||
if isAudio {
|
||||
self.progressNode.state = .Play
|
||||
} else {
|
||||
self.progressNode.state = .Remote
|
||||
}
|
||||
}
|
||||
case let .playbackStatus(playbackStatus):
|
||||
switch playbackStatus {
|
||||
case .playing:
|
||||
musicIsPlaying = true
|
||||
self.progressNode.state = .Pause
|
||||
case .paused:
|
||||
musicIsPlaying = false
|
||||
self.progressNode.state = .Play
|
||||
}
|
||||
}
|
||||
}
|
||||
if let musicIsPlaying = musicIsPlaying {
|
||||
if self.playbackOverlayNode == nil {
|
||||
let playbackOverlayNode = ListMessagePlaybackOverlayNode()
|
||||
playbackOverlayNode.frame = self.iconImageNode.frame
|
||||
self.playbackOverlayNode = playbackOverlayNode
|
||||
self.addSubnode(playbackOverlayNode)
|
||||
}
|
||||
self.playbackOverlayNode?.isPlaying = musicIsPlaying
|
||||
} else if let playbackOverlayNode = self.playbackOverlayNode {
|
||||
self.playbackOverlayNode = nil
|
||||
playbackOverlayNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) {
|
||||
super.setHighlighted(highlighted, at: point, animated: animated)
|
||||
|
||||
@ -742,9 +759,13 @@ final class ListMessageFileItemNode: ListMessageNode {
|
||||
}
|
||||
|
||||
private func updateProgressFrame(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
guard let item = self.appliedItem else {
|
||||
return
|
||||
}
|
||||
var descriptionOffset: CGFloat = 0.0
|
||||
|
||||
if let resourceStatus = self.resourceStatus, let item = self.appliedItem {
|
||||
var downloadingString: String?
|
||||
if let resourceStatus = self.resourceStatus {
|
||||
var maybeFetchStatus: MediaResourceStatus = .Local
|
||||
switch resourceStatus {
|
||||
case .playbackStatus:
|
||||
@ -752,7 +773,12 @@ final class ListMessageFileItemNode: ListMessageNode {
|
||||
case let .fetchStatus(fetchStatus):
|
||||
maybeFetchStatus = fetchStatus
|
||||
switch fetchStatus {
|
||||
case .Remote, .Fetching:
|
||||
case let .Fetching(_, progress):
|
||||
if let file = self.currentMedia as? TelegramMediaFile, let size = file.size {
|
||||
downloadingString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true)) / \(dataSizeString(size, forceDecimal: true))"
|
||||
}
|
||||
descriptionOffset = 14.0
|
||||
case .Remote:
|
||||
descriptionOffset = 14.0
|
||||
case .Local:
|
||||
break
|
||||
@ -802,6 +828,18 @@ final class ListMessageFileItemNode: ListMessageNode {
|
||||
descriptionFrame.origin.x = originX
|
||||
transition.updateFrame(node: self.descriptionNode, frame: descriptionFrame)
|
||||
}
|
||||
|
||||
if downloadingString != nil {
|
||||
self.descriptionProgressNode.isHidden = false
|
||||
self.descriptionNode.isHidden = true
|
||||
} else {
|
||||
self.descriptionProgressNode.isHidden = true
|
||||
self.descriptionNode.isHidden = false
|
||||
}
|
||||
self.descriptionProgressNode.attributedText = NSAttributedString(string: downloadingString ?? "", font: descriptionFont, textColor: item.theme.list.itemSecondaryTextColor)
|
||||
let descriptionSize = self.descriptionProgressNode.updateLayout(CGSize(width: size.width - 14.0, height: size.height))
|
||||
transition.updateFrame(node: self.descriptionProgressNode, frame: CGRect(origin: self.descriptionNode.frame.origin, size: descriptionSize))
|
||||
|
||||
}
|
||||
|
||||
func activateMedia() {
|
||||
|
||||
@ -63,6 +63,7 @@ private final class MediaPlayerContext {
|
||||
|
||||
private let postbox: Postbox
|
||||
private let resourceReference: MediaResourceReference
|
||||
private let tempFilePath: String?
|
||||
private let streamable: Bool
|
||||
private let video: Bool
|
||||
private let preferSoftwareDecoding: Bool
|
||||
@ -88,7 +89,7 @@ private final class MediaPlayerContext {
|
||||
|
||||
private var stoppedAtEnd = false
|
||||
|
||||
init(queue: Queue, audioSessionManager: ManagedAudioSession, playerStatus: Promise<MediaPlayerStatus>, postbox: Postbox, resourceReference: MediaResourceReference, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, playAndRecord: Bool, keepAudioSessionWhilePaused: Bool) {
|
||||
init(queue: Queue, audioSessionManager: ManagedAudioSession, playerStatus: Promise<MediaPlayerStatus>, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, playAndRecord: Bool, keepAudioSessionWhilePaused: Bool) {
|
||||
assert(queue.isCurrent())
|
||||
|
||||
self.queue = queue
|
||||
@ -96,6 +97,7 @@ private final class MediaPlayerContext {
|
||||
self.playerStatus = playerStatus
|
||||
self.postbox = postbox
|
||||
self.resourceReference = resourceReference
|
||||
self.tempFilePath = tempFilePath
|
||||
self.streamable = streamable
|
||||
self.video = video
|
||||
self.preferSoftwareDecoding = preferSoftwareDecoding
|
||||
@ -253,7 +255,7 @@ private final class MediaPlayerContext {
|
||||
self.playerStatus.set(.single(status))
|
||||
}
|
||||
|
||||
let frameSource = FFMpegMediaFrameSource(queue: self.queue, postbox: self.postbox, resourceReference: self.resourceReference, streamable: self.streamable, video: self.video, preferSoftwareDecoding: self.preferSoftwareDecoding, fetchAutomatically: self.fetchAutomatically)
|
||||
let frameSource = FFMpegMediaFrameSource(queue: self.queue, postbox: self.postbox, resourceReference: self.resourceReference, tempFilePath: self.tempFilePath, streamable: self.streamable, video: self.video, preferSoftwareDecoding: self.preferSoftwareDecoding, fetchAutomatically: self.fetchAutomatically)
|
||||
let disposable = MetaDisposable()
|
||||
let updatedSeekState: MediaPlayerSeekState?
|
||||
if let loadedDuration = loadedDuration {
|
||||
@ -827,9 +829,9 @@ final class MediaPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
init(audioSessionManager: ManagedAudioSession, postbox: Postbox, resourceReference: MediaResourceReference, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool = false, enableSound: Bool, baseRate: Double = 1.0, fetchAutomatically: Bool, playAndRecord: Bool = false, keepAudioSessionWhilePaused: Bool = true) {
|
||||
init(audioSessionManager: ManagedAudioSession, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String? = nil, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool = false, enableSound: Bool, baseRate: Double = 1.0, fetchAutomatically: Bool, playAndRecord: Bool = false, keepAudioSessionWhilePaused: Bool = true) {
|
||||
self.queue.async {
|
||||
let context = MediaPlayerContext(queue: self.queue, audioSessionManager: audioSessionManager, playerStatus: self.statusValue, postbox: postbox, resourceReference: resourceReference, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, playAutomatically: playAutomatically, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically, playAndRecord: playAndRecord, keepAudioSessionWhilePaused: keepAudioSessionWhilePaused)
|
||||
let context = MediaPlayerContext(queue: self.queue, audioSessionManager: audioSessionManager, playerStatus: self.statusValue, postbox: postbox, resourceReference: resourceReference, tempFilePath: tempFilePath, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, playAutomatically: playAutomatically, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically, playAndRecord: playAndRecord, keepAudioSessionWhilePaused: keepAudioSessionWhilePaused)
|
||||
self.contextRef = Unmanaged.passRetained(context)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
import CoreMedia
|
||||
import TelegramUIPrivateModule
|
||||
import FFMpeg
|
||||
|
||||
enum MediaTrackFrameType {
|
||||
case video
|
||||
|
||||
@ -128,8 +128,6 @@ final class MentionChatInputPanelItemNode: ListViewItemNode {
|
||||
let previousItem = self.item
|
||||
|
||||
return { [weak self] item, params, mergedTop, mergedBottom in
|
||||
let baseWidth = params.width - params.leftInset - params.rightInset
|
||||
|
||||
let leftInset: CGFloat = 55.0 + params.leftInset
|
||||
let rightInset: CGFloat = 10.0 + params.rightInset
|
||||
|
||||
@ -139,7 +137,7 @@ final class MentionChatInputPanelItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
let string = NSMutableAttributedString()
|
||||
string.append(NSAttributedString(string: item.peer.displayTitle, font: primaryFont, textColor: item.theme.list.itemPrimaryTextColor))
|
||||
string.append(NSAttributedString(string: item.peer.debugDisplayTitle, font: primaryFont, textColor: item.theme.list.itemPrimaryTextColor))
|
||||
if let addressName = item.peer.addressName, !addressName.isEmpty {
|
||||
string.append(NSAttributedString(string: " @\(addressName)", font: secondaryFont, textColor: item.theme.list.itemSecondaryTextColor))
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ public enum MessageContentKind: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public func messageContentKind(_ message: Message, strings: PresentationStrings, accountPeerId: PeerId) -> MessageContentKind {
|
||||
public func messageContentKind(_ message: Message, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId) -> MessageContentKind {
|
||||
for media in message.media {
|
||||
switch media {
|
||||
case let expiredMedia as TelegramMediaExpiredContent:
|
||||
@ -127,7 +127,7 @@ public func messageContentKind(_ message: Message, strings: PresentationStrings,
|
||||
return .location
|
||||
}
|
||||
case _ as TelegramMediaAction:
|
||||
return .text(plainServiceMessageString(strings: strings, message: message, accountPeerId: accountPeerId) ?? "")
|
||||
return .text(plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId) ?? "")
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -135,11 +135,11 @@ public func messageContentKind(_ message: Message, strings: PresentationStrings,
|
||||
return .text(message.text)
|
||||
}
|
||||
|
||||
func descriptionStringForMessage(_ message: Message, strings: PresentationStrings, accountPeerId: PeerId) -> (String, Bool) {
|
||||
func descriptionStringForMessage(_ message: Message, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId) -> (String, Bool) {
|
||||
if !message.text.isEmpty {
|
||||
return (message.text, false)
|
||||
}
|
||||
switch messageContentKind(message, strings: strings, accountPeerId: accountPeerId) {
|
||||
switch messageContentKind(message, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: accountPeerId) {
|
||||
case let .text(text):
|
||||
return (text, false)
|
||||
case .image:
|
||||
|
||||
@ -223,61 +223,56 @@ final class MultiplexedVideoNode: UIScrollView, UIScrollViewDelegate {
|
||||
let progressSize = CGSize(width: 24.0, height: 24.0)
|
||||
let progressFrame = CGRect(origin: CGPoint(x: item.frame.midX - progressSize.width / 2.0, y: item.frame.midY - progressSize.height / 2.0), size: progressSize)
|
||||
|
||||
let updatedStatusSignal = account.postbox.mediaBox.resourceStatus(item.fileReference.media.resource)
|
||||
|
||||
let statusDisposable: MetaDisposable
|
||||
if let disposable = self.statusDisposable[item.fileReference.media.fileId] {
|
||||
statusDisposable = disposable
|
||||
} else {
|
||||
statusDisposable = MetaDisposable()
|
||||
self.statusDisposable[item.fileReference.media.fileId] = statusDisposable
|
||||
if item.frame.maxY < minVisibleY {
|
||||
continue
|
||||
}
|
||||
if item.frame.minY > maxVisibleY {
|
||||
continue
|
||||
}
|
||||
|
||||
statusDisposable.set((updatedStatusSignal |> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
displayLinkDispatcher.dispatch {
|
||||
if self.statusDisposable[item.fileReference.media.fileId] == nil {
|
||||
let statusDisposable = MetaDisposable()
|
||||
let updatedStatusSignal = account.postbox.mediaBox.resourceStatus(item.fileReference.media.resource)
|
||||
self.statusDisposable[item.fileReference.media.fileId] = statusDisposable
|
||||
statusDisposable.set((updatedStatusSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
guard let `self` = self else {return}
|
||||
|
||||
let state: RadialStatusNodeState
|
||||
|
||||
switch status {
|
||||
case let .Fetching(_, progress):
|
||||
state = .progress(color: .white, lineWidth: nil, value: CGFloat(max(progress, 0.2)), cancelEnabled: false)
|
||||
case .Remote:
|
||||
state = .progress(color: .white, lineWidth: nil, value: 0, cancelEnabled: false)
|
||||
case .Local:
|
||||
state = .none
|
||||
case let .Fetching(_, progress):
|
||||
state = .progress(color: .white, lineWidth: nil, value: CGFloat(max(progress, 0.2)), cancelEnabled: false)
|
||||
case .Remote:
|
||||
state = .progress(color: .white, lineWidth: nil, value: 0, cancelEnabled: false)
|
||||
case .Local:
|
||||
state = .none
|
||||
}
|
||||
|
||||
if state == .none {
|
||||
if let statusNode = self.visibleProgressNodes[item.fileReference.media.fileId] {
|
||||
if let statusNode = self.visibleProgressNodes[item.fileReference.media.fileId] {
|
||||
if state == .none {
|
||||
self.visibleProgressNodes.removeValue(forKey: item.fileReference.media.fileId)
|
||||
statusNode.transitionToState(state, completion: { [weak statusNode] in
|
||||
statusNode?.removeFromSupernode()
|
||||
statusNode?.isHidden = true
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if let visibleProgressNode = self.visibleProgressNodes[item.fileReference.media.fileId] {
|
||||
if ensureFrames {
|
||||
visibleProgressNode.frame = progressFrame
|
||||
}
|
||||
} else {
|
||||
let visibleProgressNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5))
|
||||
visibleProgressNode.frame = progressFrame
|
||||
self.visibleProgressNodes[item.fileReference.media.fileId] = visibleProgressNode
|
||||
statusNode.isHidden = false
|
||||
statusNode.transitionToState(state, completion: {})
|
||||
}
|
||||
|
||||
let statusNode = self.visibleProgressNodes[item.fileReference.media.fileId]!
|
||||
statusNode.transitionToState(state, completion: {})
|
||||
self.addSubnode(statusNode)
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
if item.frame.maxY < minVisibleY {
|
||||
continue;
|
||||
}))
|
||||
}
|
||||
if item.frame.minY > maxVisibleY {
|
||||
continue;
|
||||
|
||||
if let visibleProgressNode = self.visibleProgressNodes[item.fileReference.media.fileId] {
|
||||
if ensureFrames {
|
||||
visibleProgressNode.frame = progressFrame
|
||||
}
|
||||
} else {
|
||||
let statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5))
|
||||
statusNode.isHidden = true
|
||||
statusNode.frame = progressFrame
|
||||
self.visibleProgressNodes[item.fileReference.media.fileId] = statusNode
|
||||
self.addSubnode(statusNode)
|
||||
}
|
||||
|
||||
visibleIds.insert(item.fileReference.media.fileId)
|
||||
@ -315,6 +310,13 @@ final class MultiplexedVideoNode: UIScrollView, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
var removeProgressIds: [MediaId] = []
|
||||
for id in self.visibleProgressNodes.keys {
|
||||
if !visibleIds.contains(id) {
|
||||
removeProgressIds.append(id)
|
||||
}
|
||||
}
|
||||
|
||||
for id in removeIds {
|
||||
let (_, layerHolder) = self.visibleLayers[id]!
|
||||
layerHolder.layer.removeFromSuperlayer()
|
||||
@ -326,6 +328,13 @@ final class MultiplexedVideoNode: UIScrollView, UIScrollViewDelegate {
|
||||
thumbnailLayer.removeFromSuperlayer()
|
||||
self.visibleThumbnailLayers.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
for id in removeProgressIds {
|
||||
let progressNode = self.visibleProgressNodes[id]!
|
||||
progressNode.removeFromSupernode()
|
||||
self.visibleProgressNodes.removeValue(forKey: id)
|
||||
self.statusDisposable.removeValue(forKey: id)?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateVisibleItems(transition: ContainedViewLayoutTransition = .immediate) {
|
||||
|
||||
@ -49,8 +49,9 @@ final class NativeVideoContent: UniversalVideoContent {
|
||||
let baseRate: Double
|
||||
let fetchAutomatically: Bool
|
||||
let placeholderColor: UIColor
|
||||
let tempFilePath: String?
|
||||
|
||||
init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: Bool = false, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, placeholderColor: UIColor = .white) {
|
||||
init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: Bool = false, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, placeholderColor: UIColor = .white, tempFilePath: String? = nil) {
|
||||
self.id = id
|
||||
self.nativeId = id
|
||||
self.fileReference = fileReference
|
||||
@ -75,10 +76,11 @@ final class NativeVideoContent: UniversalVideoContent {
|
||||
self.baseRate = baseRate
|
||||
self.fetchAutomatically = fetchAutomatically
|
||||
self.placeholderColor = placeholderColor
|
||||
self.tempFilePath = tempFilePath
|
||||
}
|
||||
|
||||
func makeContentNode(postbox: Postbox, audioSession: ManagedAudioSession) -> UniversalVideoContentNode & ASDisplayNode {
|
||||
return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, placeholderColor: self.placeholderColor)
|
||||
return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, placeholderColor: self.placeholderColor, tempFilePath: self.tempFilePath)
|
||||
}
|
||||
|
||||
func isEqual(to other: UniversalVideoContent) -> Bool {
|
||||
@ -128,14 +130,14 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: Bool, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, placeholderColor: UIColor) {
|
||||
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: Bool, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, placeholderColor: UIColor, tempFilePath: String?) {
|
||||
self.postbox = postbox
|
||||
self.fileReference = fileReference
|
||||
self.placeholderColor = placeholderColor
|
||||
|
||||
self.imageNode = TransformImageNode()
|
||||
|
||||
self.player = MediaPlayer(audioSessionManager: audioSessionManager, postbox: postbox, resourceReference: fileReference.resourceReference(fileReference.media.resource), streamable: streamVideo, video: true, preferSoftwareDecoding: false, playAutomatically: false, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically)
|
||||
self.player = MediaPlayer(audioSessionManager: audioSessionManager, postbox: postbox, resourceReference: fileReference.resourceReference(fileReference.media.resource), tempFilePath: tempFilePath, streamable: streamVideo, video: true, preferSoftwareDecoding: false, playAutomatically: false, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically)
|
||||
var actionAtEndImpl: (() -> Void)?
|
||||
if enableSound && !loopVideo {
|
||||
self.player.actionAtEnd = .action({
|
||||
@ -214,7 +216,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
||||
}
|
||||
|
||||
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.playerNode.frame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -0.0002, dy: -0.0002)
|
||||
self.playerNode.frame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0)
|
||||
}
|
||||
|
||||
func play() {
|
||||
|
||||
@ -5,15 +5,12 @@ import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
|
||||
|
||||
|
||||
|
||||
private final class NotificationExceptionState : Equatable {
|
||||
|
||||
let mode:NotificationExceptionMode
|
||||
let isSearchMode: Bool
|
||||
let revealedPeerId: PeerId?
|
||||
let editing: Bool
|
||||
|
||||
init(mode: NotificationExceptionMode, isSearchMode: Bool = false, revealedPeerId: PeerId? = nil, editing: Bool = false) {
|
||||
self.mode = mode
|
||||
self.isSearchMode = isSearchMode
|
||||
@ -294,7 +291,7 @@ private func notificationsExceptionEntries(presentationData: PresentationData, s
|
||||
let soundName = localizedPeerNotificationSoundString(strings: presentationData.strings, sound: value.settings.messageSound)
|
||||
title += (title.isEmpty ? presentationData.strings.Notification_Exceptions_Sound(soundName).0 : ", \(presentationData.strings.Notification_Exceptions_Sound(soundName).0)")
|
||||
}
|
||||
entries.append(.peer(index: index, peer: value.peer, theme: presentationData.theme, strings: presentationData.strings, dateFormat: presentationData.dateTimeFormat, description: title, notificationSettings: value.settings, revealed: state.revealedPeerId == value.peer.id, editing: state.editing))
|
||||
entries.append(.peer(index: index, peer: value.peer, theme: presentationData.theme, strings: presentationData.strings, dateFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, description: title, notificationSettings: value.settings, revealed: state.revealedPeerId == value.peer.id, editing: state.editing))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
@ -324,14 +321,15 @@ private enum NotificationExceptionEntryId: Hashable {
|
||||
case search
|
||||
case peerId(Int64)
|
||||
case addException
|
||||
|
||||
var hashValue: Int {
|
||||
switch self {
|
||||
case .search:
|
||||
return 0
|
||||
case .addException:
|
||||
return 1
|
||||
case let .peerId(peerId):
|
||||
return peerId.hashValue
|
||||
case .search:
|
||||
return 0
|
||||
case .addException:
|
||||
return 1
|
||||
case let .peerId(peerId):
|
||||
return peerId.hashValue
|
||||
}
|
||||
}
|
||||
|
||||
@ -380,7 +378,7 @@ private enum NotificationExceptionEntry : ItemListNodeEntry {
|
||||
typealias ItemGenerationArguments = NotificationExceptionArguments
|
||||
|
||||
case search(PresentationTheme, PresentationStrings)
|
||||
case peer(index: Int, peer: Peer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, description: String, notificationSettings: TelegramPeerNotificationSettings, revealed: Bool, editing: Bool)
|
||||
case peer(index: Int, peer: Peer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, description: String, notificationSettings: TelegramPeerNotificationSettings, revealed: Bool, editing: Bool)
|
||||
case addException(PresentationTheme, PresentationStrings, Bool)
|
||||
|
||||
func item(_ arguments: NotificationExceptionArguments) -> ListViewItem {
|
||||
@ -393,8 +391,8 @@ private enum NotificationExceptionEntry : ItemListNodeEntry {
|
||||
return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.addExceptionIcon(theme), title: strings.Notification_Exceptions_AddException, sectionId: self.section, editing: editing, action: {
|
||||
arguments.selectPeer()
|
||||
})
|
||||
case let .peer(_, peer, theme, strings, dateTimeFormat, value, _, revealed, editing):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .text(value), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: editing, revealed: revealed), switchValue: nil, enabled: true, sectionId: self.section, action: {
|
||||
case let .peer(_, peer, theme, strings, dateTimeFormat, nameDisplayOrder, value, _, revealed, editing):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .text(value), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: editing, revealed: revealed), switchValue: nil, enabled: true, sectionId: self.section, action: {
|
||||
arguments.openPeer(peer)
|
||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
arguments.updateRevealedPeerId(peerId)
|
||||
@ -410,7 +408,7 @@ private enum NotificationExceptionEntry : ItemListNodeEntry {
|
||||
return .search
|
||||
case .addException:
|
||||
return .addException
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _):
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, _):
|
||||
return .peerId(peer.id.toInt64())
|
||||
}
|
||||
}
|
||||
@ -431,10 +429,10 @@ private enum NotificationExceptionEntry : ItemListNodeEntry {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case let .peer(lhsIndex, lhsPeer, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsValue, lhsSettings, lhsRevealed, lhsEditing):
|
||||
case let .peer(lhsIndex, lhsPeer, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsValue, lhsSettings, lhsRevealed, lhsEditing):
|
||||
switch rhs {
|
||||
case let .peer(rhsIndex, rhsPeer, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsValue, rhsSettings, rhsRevealed, rhsEditing):
|
||||
return lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsDateTimeFormat == rhsDateTimeFormat && lhsIndex == rhsIndex && lhsPeer.isEqual(rhsPeer) && lhsValue == rhsValue && lhsSettings == rhsSettings && lhsRevealed == rhsRevealed && lhsEditing == rhsEditing
|
||||
case let .peer(rhsIndex, rhsPeer, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsValue, rhsSettings, rhsRevealed, rhsEditing):
|
||||
return lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsDateTimeFormat == rhsDateTimeFormat && lhsNameOrder == rhsNameOrder && lhsIndex == rhsIndex && lhsPeer.isEqual(rhsPeer) && lhsValue == rhsValue && lhsSettings == rhsSettings && lhsRevealed == rhsRevealed && lhsEditing == rhsEditing
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@ -452,11 +450,11 @@ private enum NotificationExceptionEntry : ItemListNodeEntry {
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case let .peer(lhsIndex, _, _, _, _, _, _, _ , _):
|
||||
case let .peer(lhsIndex, _, _, _, _, _, _, _, _, _):
|
||||
switch rhs {
|
||||
case .search, .addException:
|
||||
return false
|
||||
case let .peer(rhsIndex, _, _, _, _, _, _, _, _):
|
||||
case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _):
|
||||
return lhsIndex < rhsIndex
|
||||
}
|
||||
}
|
||||
|
||||
@ -813,7 +813,7 @@ public func notificationsAndSoundsController(account: Account, exceptionsList: N
|
||||
var channels:[PeerId : NotificationExceptionWrapper] = [:]
|
||||
if let list = list {
|
||||
for (key, value) in list.settings {
|
||||
if let peer = list.peers[key], !peer.displayTitle.isEmpty, peer.id != account.peerId {
|
||||
if let peer = list.peers[key], !peer.debugDisplayTitle.isEmpty, peer.id != account.peerId {
|
||||
switch value.muteState {
|
||||
case .default:
|
||||
switch value.messageSound {
|
||||
|
||||
@ -541,9 +541,11 @@ public func passcodeEntryController(account: Account, animateIn: Bool = true, co
|
||||
completion(value != nil)
|
||||
dismissImpl?()
|
||||
})!
|
||||
controller.touchIdCompletion = {
|
||||
completion(true)
|
||||
dismissImpl?()
|
||||
if passcodeSettings?.enableBiometrics ?? false {
|
||||
controller.touchIdCompletion = {
|
||||
completion(true)
|
||||
dismissImpl?()
|
||||
}
|
||||
}
|
||||
controller.checkCurrentPasscode = { value in
|
||||
if let value = value {
|
||||
@ -578,7 +580,9 @@ public func passcodeEntryController(account: Account, animateIn: Bool = true, co
|
||||
}).start()
|
||||
}
|
||||
legacyController.presentationCompleted = { [weak controller] in
|
||||
controller?.refreshTouchId()
|
||||
if passcodeSettings?.enableBiometrics ?? false {
|
||||
controller?.refreshTouchId()
|
||||
}
|
||||
}
|
||||
legacyController.bind(controller: controller)
|
||||
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
|
||||
|
||||
@ -10,12 +10,20 @@ struct PeerMediaCollectionMessageForGallery {
|
||||
let fromSearchResults: Bool
|
||||
}
|
||||
|
||||
private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, account: Account, peerId: PeerId, messageId: MessageId?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal<Set<MessageId>?, NoError>) -> ChatHistoryNode & ASDisplayNode {
|
||||
private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, account: Account, theme: PresentationTheme, peerId: PeerId, messageId: MessageId?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal<Set<MessageId>?, NoError>) -> ChatHistoryNode & ASDisplayNode {
|
||||
switch mode {
|
||||
case .photoOrVideo:
|
||||
return ChatHistoryGridNode(account: account, peerId: peerId, messageId: messageId, tagMask: .photoOrVideo, controllerInteraction: controllerInteraction)
|
||||
let node = ChatHistoryGridNode(account: account, peerId: peerId, messageId: messageId, tagMask: .photoOrVideo, controllerInteraction: controllerInteraction)
|
||||
node.showVerticalScrollIndicator = true
|
||||
if theme.list.plainBackgroundColor.argb == 0xffffffff {
|
||||
node.indicatorStyle = .default
|
||||
} else {
|
||||
node.indicatorStyle = .white
|
||||
}
|
||||
return node
|
||||
case .file:
|
||||
let node = ChatHistoryListNode(account: account, chatLocation: .peer(peerId), tagMask: .file, messageId: messageId, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false))
|
||||
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
||||
node.didEndScrolling = { [weak node] in
|
||||
guard let node = node else {
|
||||
return
|
||||
@ -26,6 +34,7 @@ private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, account: Ac
|
||||
return node
|
||||
case .music:
|
||||
let node = ChatHistoryListNode(account: account, chatLocation: .peer(peerId), tagMask: .music, messageId: messageId, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false))
|
||||
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
||||
node.didEndScrolling = { [weak node] in
|
||||
guard let node = node else {
|
||||
return
|
||||
@ -36,6 +45,7 @@ private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, account: Ac
|
||||
return node
|
||||
case .webpage:
|
||||
let node = ChatHistoryListNode(account: account, chatLocation: .peer(peerId), tagMask: .webPage, messageId: messageId, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false))
|
||||
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
||||
node.didEndScrolling = { [weak node] in
|
||||
guard let node = node else {
|
||||
return
|
||||
@ -134,11 +144,11 @@ class PeerMediaCollectionControllerNode: ASDisplayNode {
|
||||
|
||||
self.sectionsNode = PeerMediaCollectionSectionsNode(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||
|
||||
self.historyNode = historyNodeImplForMode(self.mediaCollectionInterfaceState.mode, account: account, peerId: peerId, messageId: messageId, controllerInteraction: controllerInteraction, selectedMessages: self.selectedMessagesPromise.get())
|
||||
self.historyNode = historyNodeImplForMode(self.mediaCollectionInterfaceState.mode, account: account, theme: self.presentationData.theme, peerId: peerId, messageId: messageId, controllerInteraction: controllerInteraction, selectedMessages: self.selectedMessagesPromise.get())
|
||||
self.historyEmptyNode = PeerMediaCollectionEmptyNode(mode: self.mediaCollectionInterfaceState.mode, theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||
self.historyEmptyNode.isHidden = true
|
||||
|
||||
self.chatPresentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, fontSize: self.presentationData.fontSize, accountPeerId: account.peerId, mode: .standard(previewing: false), chatLocation: .peer(self.peerId))
|
||||
self.chatPresentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, fontSize: self.presentationData.fontSize, accountPeerId: account.peerId, mode: .standard(previewing: false), chatLocation: .peer(self.peerId))
|
||||
|
||||
super.init()
|
||||
|
||||
@ -397,7 +407,7 @@ class PeerMediaCollectionControllerNode: ASDisplayNode {
|
||||
if self.mediaCollectionInterfaceState.mode != mediaCollectionInterfaceState.mode {
|
||||
let previousMode = self.mediaCollectionInterfaceState.mode
|
||||
if let containerLayout = self.containerLayout, self.candidateHistoryNode == nil || self.candidateHistoryNode!.1 != mediaCollectionInterfaceState.mode {
|
||||
let node = historyNodeImplForMode(mediaCollectionInterfaceState.mode, account: self.account, peerId: self.peerId, messageId: nil, controllerInteraction: self.controllerInteraction, selectedMessages: self.selectedMessagesPromise.get())
|
||||
let node = historyNodeImplForMode(mediaCollectionInterfaceState.mode, account: self.account, theme: self.presentationData.theme, peerId: self.peerId, messageId: nil, controllerInteraction: self.controllerInteraction, selectedMessages: self.selectedMessagesPromise.get())
|
||||
node.backgroundColor = mediaCollectionInterfaceState.theme.list.plainBackgroundColor
|
||||
self.candidateHistoryNode = (node, mediaCollectionInterfaceState.mode)
|
||||
|
||||
|
||||
@ -3,15 +3,16 @@ import TelegramCore
|
||||
import Postbox
|
||||
|
||||
extension Peer {
|
||||
func displayTitle(strings: PresentationStrings) -> String {
|
||||
func displayTitle(strings: PresentationStrings, displayOrder: PresentationPersonNameOrder) -> String {
|
||||
switch self {
|
||||
case let user as TelegramUser:
|
||||
if let firstName = user.firstName {
|
||||
if let lastName = user.lastName {
|
||||
if strings.lc == 0x6b6f {
|
||||
return "\(lastName) \(firstName)"
|
||||
} else {
|
||||
return "\(firstName) \(lastName)"
|
||||
switch displayOrder {
|
||||
case .firstLast:
|
||||
return "\(firstName) \(lastName)"
|
||||
case .lastFirst:
|
||||
return "\(lastName) \(firstName)"
|
||||
}
|
||||
} else {
|
||||
return firstName
|
||||
|
||||
@ -113,23 +113,35 @@ final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
var codeAndNumber: (Int32?, String) {
|
||||
private var countryNameForCode: (Int32, String)?
|
||||
|
||||
var codeAndNumber: (Int32?, String?, String) {
|
||||
get {
|
||||
var code: Int32?
|
||||
if let text = self.countryCodeField.textField.text, text.count <= 4, let number = Int(removePlus(text)) {
|
||||
code = Int32(number)
|
||||
return (code, cleanPhoneNumber(self.numberField.textField.text))
|
||||
var countryName: String?
|
||||
if self.countryNameForCode?.0 == code {
|
||||
countryName = self.countryNameForCode?.1
|
||||
}
|
||||
return (code, countryName, cleanPhoneNumber(self.numberField.textField.text))
|
||||
} else if let text = self.countryCodeField.textField.text {
|
||||
return (nil, cleanPhoneNumber(text + (self.numberField.textField.text ?? "")))
|
||||
return (nil, nil, cleanPhoneNumber(text + (self.numberField.textField.text ?? "")))
|
||||
} else {
|
||||
return (nil, "")
|
||||
return (nil, nil, "")
|
||||
}
|
||||
} set(value) {
|
||||
self.updateNumber("+" + (value.0 == nil ? "" : "\(value.0!)") + value.1)
|
||||
let updatedCountryName = self.countryNameForCode?.0 != value.0 || self.countryNameForCode?.1 != value.1
|
||||
if let code = value.0, let name = value.1 {
|
||||
self.countryNameForCode = (code, name)
|
||||
} else {
|
||||
self.countryNameForCode = nil
|
||||
}
|
||||
self.updateNumber("+" + (value.0 == nil ? "" : "\(value.0!)") + value.2, forceNotifyCountryCodeUpdated: updatedCountryName)
|
||||
}
|
||||
}
|
||||
|
||||
var countryCodeUpdated: ((String) -> Void)?
|
||||
var countryCodeUpdated: ((String, String?) -> Void)?
|
||||
|
||||
var countryCodeTextUpdated: ((String) -> Void)?
|
||||
var numberTextUpdated: ((String) -> Void)?
|
||||
@ -204,7 +216,7 @@ final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
self.updateNumber(inputText)
|
||||
}
|
||||
|
||||
private func updateNumber(_ inputText: String, tryRestoringInputPosition: Bool = true) {
|
||||
private func updateNumber(_ inputText: String, tryRestoringInputPosition: Bool = true, forceNotifyCountryCodeUpdated: Bool = false) {
|
||||
let (regionPrefix, text) = self.phoneFormatter.updateText(inputText)
|
||||
var realRegionPrefix: String
|
||||
let numberText: String
|
||||
@ -226,10 +238,14 @@ final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
if realRegionPrefix != self.countryCodeField.textField.text {
|
||||
self.countryCodeField.textField.text = realRegionPrefix
|
||||
}
|
||||
if self.previousCountryCodeText != realRegionPrefix {
|
||||
if self.previousCountryCodeText != realRegionPrefix || forceNotifyCountryCodeUpdated {
|
||||
self.previousCountryCodeText = realRegionPrefix
|
||||
let code = removePlus(realRegionPrefix).trimmingCharacters(in: CharacterSet.whitespaces)
|
||||
self.countryCodeUpdated?(code)
|
||||
var countryName: String?
|
||||
if self.countryNameForCode?.0 == Int32(code) {
|
||||
countryName = self.countryNameForCode?.1
|
||||
}
|
||||
self.countryCodeUpdated?(code, countryName)
|
||||
}
|
||||
self.countryCodeTextUpdated?(realRegionPrefix)
|
||||
|
||||
|
||||
@ -88,9 +88,13 @@ private func chatMessagePhotoDatas(postbox: Postbox, photoReference: ImageMediaR
|
||||
|
||||
return thumbnail
|
||||
|> mapToSignal { thumbnailData in
|
||||
return fullSizeData
|
||||
|> map { (fullSizeData, complete) in
|
||||
return (thumbnailData, fullSizeData, complete)
|
||||
if let thumbnailData = thumbnailData {
|
||||
return fullSizeData
|
||||
|> map { (fullSizeData, complete) in
|
||||
return (thumbnailData, fullSizeData, complete)
|
||||
}
|
||||
} else {
|
||||
return .single((thumbnailData, nil, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,9 +31,9 @@ public enum PresentationDateFormat {
|
||||
case dayFirst
|
||||
}
|
||||
|
||||
public enum PresentationPersonNameOrder {
|
||||
case firstLast
|
||||
case lastFirst
|
||||
public enum PresentationPersonNameOrder: Int32 {
|
||||
case firstLast = 0
|
||||
case lastFirst = 1
|
||||
}
|
||||
|
||||
extension PresentationStrings : Equatable {
|
||||
@ -41,12 +41,6 @@ extension PresentationStrings : Equatable {
|
||||
return lhs === rhs
|
||||
}
|
||||
}
|
||||
//
|
||||
//extension PresentationTheme : Equatable {
|
||||
// public static func ==(lhs: PresentationTheme, rhs: PresentationTheme) -> Bool {
|
||||
// return lhs === rhs
|
||||
// }
|
||||
//}
|
||||
|
||||
public final class PresentationData: Equatable {
|
||||
public let strings: PresentationStrings
|
||||
@ -167,23 +161,6 @@ private func currentPersonNameSortOrder() -> PresentationPersonNameOrder {
|
||||
}
|
||||
}
|
||||
|
||||
private func currentPersonNameDisplayOrder() -> PresentationPersonNameOrder {
|
||||
if #available(iOSApplicationExtension 9.0, *) {
|
||||
switch CNContactFormatter.nameOrder(for: CNContact()) {
|
||||
case .givenNameFirst:
|
||||
return .firstLast
|
||||
default:
|
||||
return .lastFirst
|
||||
}
|
||||
} else {
|
||||
if ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst {
|
||||
return .firstLast
|
||||
} else {
|
||||
return .lastFirst
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class InitialPresentationDataAndSettings {
|
||||
public let presentationData: PresentationData
|
||||
public let automaticMediaDownloadSettings: AutomaticMediaDownloadSettings
|
||||
@ -203,7 +180,7 @@ public final class InitialPresentationDataAndSettings {
|
||||
}
|
||||
|
||||
public func currentPresentationDataAndSettings(postbox: Postbox) -> Signal<InitialPresentationDataAndSettings, NoError> {
|
||||
return postbox.transaction { transaction -> (PresentationThemeSettings, LocalizationSettings?, AutomaticMediaDownloadSettings, CallListSettings, InAppNotificationSettings, MediaInputSettings, ExperimentalUISettings) in
|
||||
return postbox.transaction { transaction -> (PresentationThemeSettings, LocalizationSettings?, AutomaticMediaDownloadSettings, CallListSettings, InAppNotificationSettings, MediaInputSettings, ExperimentalUISettings, ContactSynchronizationSettings) in
|
||||
let themeSettings: PresentationThemeSettings
|
||||
if let current = transaction.getPreferencesEntry(key: ApplicationSpecificPreferencesKeys.presentationThemeSettings) as? PresentationThemeSettings {
|
||||
themeSettings = current
|
||||
@ -248,9 +225,11 @@ public func currentPresentationDataAndSettings(postbox: Postbox) -> Signal<Initi
|
||||
|
||||
let experimentalUISettings: ExperimentalUISettings = (transaction.getPreferencesEntry(key: ApplicationSpecificPreferencesKeys.experimentalUISettings) as? ExperimentalUISettings) ?? ExperimentalUISettings.defaultSettings
|
||||
|
||||
return (themeSettings, localizationSettings, automaticMediaDownloadSettings, callListSettings, inAppNotificationSettings, mediaInputSettings, experimentalUISettings)
|
||||
let contactSettings: ContactSynchronizationSettings = (transaction.getPreferencesEntry(key: ApplicationSpecificPreferencesKeys.contactSynchronizationSettings) as? ContactSynchronizationSettings) ?? ContactSynchronizationSettings.defaultSettings
|
||||
|
||||
return (themeSettings, localizationSettings, automaticMediaDownloadSettings, callListSettings, inAppNotificationSettings, mediaInputSettings, experimentalUISettings, contactSettings)
|
||||
}
|
||||
|> map { (themeSettings, localizationSettings, automaticMediaDownloadSettings, callListSettings, inAppNotificationSettings, mediaInputSettings, experimentalUISettings) -> InitialPresentationDataAndSettings in
|
||||
|> map { (themeSettings, localizationSettings, automaticMediaDownloadSettings, callListSettings, inAppNotificationSettings, mediaInputSettings, experimentalUISettings, contactSettings) -> InitialPresentationDataAndSettings in
|
||||
let themeValue: PresentationTheme
|
||||
|
||||
let effectiveTheme: PresentationThemeReference
|
||||
@ -295,7 +274,7 @@ public func currentPresentationDataAndSettings(postbox: Postbox) -> Signal<Initi
|
||||
stringsValue = defaultPresentationStrings
|
||||
}
|
||||
let dateTimeFormat = currentDateTimeFormat()
|
||||
let nameDisplayOrder = currentPersonNameDisplayOrder()
|
||||
let nameDisplayOrder = contactSettings.nameDisplayOrder
|
||||
let nameSortOrder = currentPersonNameSortOrder()
|
||||
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, volumeControlStatusBarIcons: volumeControlStatusBarIcons(), fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations), automaticMediaDownloadSettings: automaticMediaDownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings)
|
||||
}
|
||||
@ -367,7 +346,7 @@ private func automaticThemeShouldSwitch(_ settings: AutomaticThemeSwitchSetting,
|
||||
}
|
||||
|
||||
public func updatedPresentationData(postbox: Postbox, applicationBindings: TelegramApplicationBindings) -> Signal<PresentationData, NoError> {
|
||||
let preferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.presentationThemeSettings, PreferencesKeys.localizationSettings]))
|
||||
let preferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.presentationThemeSettings, PreferencesKeys.localizationSettings, ApplicationSpecificPreferencesKeys.contactSynchronizationSettings]))
|
||||
return postbox.combinedView(keys: [preferencesKey])
|
||||
|> mapToSignal { view -> Signal<PresentationData, NoError> in
|
||||
let themeSettings: PresentationThemeSettings
|
||||
@ -377,6 +356,8 @@ public func updatedPresentationData(postbox: Postbox, applicationBindings: Teleg
|
||||
themeSettings = PresentationThemeSettings.defaultSettings
|
||||
}
|
||||
|
||||
let contactSettings: ContactSynchronizationSettings = (view.views[preferencesKey] as! PreferencesView).values[ApplicationSpecificPreferencesKeys.contactSynchronizationSettings] as? ContactSynchronizationSettings ?? ContactSynchronizationSettings.defaultSettings
|
||||
|
||||
return applicationBindings.applicationInForeground
|
||||
|> mapToSignal({ inForeground -> Signal<PresentationData, NoError> in
|
||||
if inForeground {
|
||||
@ -433,9 +414,9 @@ public func updatedPresentationData(postbox: Postbox, applicationBindings: Teleg
|
||||
}
|
||||
|
||||
let dateTimeFormat = currentDateTimeFormat()
|
||||
let nameDisplayOrder = currentPersonNameDisplayOrder()
|
||||
let nameDisplayOrder = contactSettings.nameDisplayOrder
|
||||
let nameSortOrder = currentPersonNameSortOrder()
|
||||
|
||||
|
||||
return PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, volumeControlStatusBarIcons: volumeControlStatusBarIcons(), fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations)
|
||||
}
|
||||
} else {
|
||||
@ -447,7 +428,7 @@ public func updatedPresentationData(postbox: Postbox, applicationBindings: Teleg
|
||||
|
||||
public func defaultPresentationData() -> PresentationData {
|
||||
let dateTimeFormat = currentDateTimeFormat()
|
||||
let nameDisplayOrder = currentPersonNameDisplayOrder()
|
||||
let nameDisplayOrder: PresentationPersonNameOrder = .firstLast
|
||||
let nameSortOrder = currentPersonNameSortOrder()
|
||||
|
||||
let themeSettings = PresentationThemeSettings.defaultSettings
|
||||
|
||||
@ -78,7 +78,7 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
||||
case pendingSessionsInfo(PresentationTheme, String)
|
||||
case otherSessionsHeader(PresentationTheme, String)
|
||||
case session(index: Int32, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, session: RecentAccountSession, enabled: Bool, editing: Bool, revealed: Bool)
|
||||
case website(index: Int32, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool)
|
||||
case website(index: Int32, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -113,7 +113,7 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
||||
return .index(7)
|
||||
case let .session(_, _, _, _, session, _, _, _):
|
||||
return .session(session.hash)
|
||||
case let .website(_, _, _, _, website, _, _, _, _):
|
||||
case let .website(_, _, _, _, _, website, _, _, _, _):
|
||||
return .session(website.hash)
|
||||
}
|
||||
}
|
||||
@ -180,8 +180,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .website(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsWebsite, lhsPeer, lhsEnabled, lhsEditing, lhsRevealed):
|
||||
if case let .website(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsWebsite, rhsPeer, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsWebsite == rhsWebsite, arePeersEqual(lhsPeer, rhsPeer), lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
||||
case let .website(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsWebsite, lhsPeer, lhsEnabled, lhsEditing, lhsRevealed):
|
||||
if case let .website(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsWebsite, rhsPeer, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsNameOrder == rhsNameOrder, lhsWebsite == rhsWebsite, arePeersEqual(lhsPeer, rhsPeer), lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -221,8 +221,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
||||
return false
|
||||
}
|
||||
}
|
||||
case let .website(lhsIndex, _, _, _, _, _, _, _, _):
|
||||
if case let .website(rhsIndex, _, _, _, _, _, _, _, _) = rhs {
|
||||
case let .website(lhsIndex, _, _, _, _, _, _, _, _, _):
|
||||
if case let .website(rhsIndex, _, _, _, _, _, _, _, _, _) = rhs {
|
||||
return lhsIndex <= rhsIndex
|
||||
} else {
|
||||
return false
|
||||
@ -269,8 +269,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
||||
}, removeSession: { id in
|
||||
arguments.removeSession(id)
|
||||
})
|
||||
case let .website(_, theme, strings, dateTimeFormat, website, peer, enabled, editing, revealed):
|
||||
return ItemListWebsiteItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, website: website, peer: peer, enabled: enabled, editing: editing, revealed: revealed, sectionId: self.section, setSessionIdWithRevealedOptions: { previousId, id in
|
||||
case let .website(_, theme, strings, dateTimeFormat, nameDisplayOrder, website, peer, enabled, editing, revealed):
|
||||
return ItemListWebsiteItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, website: website, peer: peer, enabled: enabled, editing: editing, revealed: revealed, sectionId: self.section, setSessionIdWithRevealedOptions: { previousId, id in
|
||||
arguments.setSessionIdWithRevealedOptions(previousId, id)
|
||||
}, removeSession: { id in
|
||||
arguments.removeWebSession(id)
|
||||
@ -397,7 +397,7 @@ private func recentSessionsControllerEntries(presentationData: PresentationData,
|
||||
let website = websites[i]
|
||||
if !existingSessionIds.contains(website.hash) {
|
||||
existingSessionIds.insert(website.hash)
|
||||
entries.append(.website(index: Int32(i), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, website: website, peer: peers[website.botId], enabled: state.removingSessionId != website.hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == website.hash))
|
||||
entries.append(.website(index: Int32(i), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, website: website, peer: peers[website.botId], enabled: state.removingSessionId != website.hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == website.hash))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
||||
|
||||
var theme: PresentationTheme
|
||||
|
||||
init(account: Account, messageId: MessageId, theme: PresentationTheme, strings: PresentationStrings) {
|
||||
init(account: Account, messageId: MessageId, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) {
|
||||
self.messageId = messageId
|
||||
|
||||
self.theme = theme
|
||||
@ -62,10 +62,10 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
||||
var authorName = ""
|
||||
var text = ""
|
||||
if let author = message?.author {
|
||||
authorName = author.displayTitle
|
||||
authorName = author.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||
}
|
||||
if let message = message {
|
||||
(text, _) = descriptionStringForMessage(message, strings: strings, accountPeerId: account.peerId)
|
||||
(text, _) = descriptionStringForMessage(message, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: account.peerId)
|
||||
}
|
||||
|
||||
var updatedMediaReference: AnyMediaReference?
|
||||
@ -131,7 +131,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
||||
|
||||
let isMedia: Bool
|
||||
if let message = message {
|
||||
switch messageContentKind(message, strings: strings, accountPeerId: account.peerId) {
|
||||
switch messageContentKind(message, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: account.peerId) {
|
||||
case .text:
|
||||
isMedia = false
|
||||
default:
|
||||
|
||||
@ -130,6 +130,8 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
|
||||
private var messageView: MessageView?
|
||||
private var currentNodeMessageId: MessageId?
|
||||
private var currentNodeMessageIsVideo = false
|
||||
private var tempFile: TempBoxFile?
|
||||
|
||||
private let _hiddenMedia = Promise<(MessageId, Media)?>(nil)
|
||||
private var hiddenMediaManagerIndex: Int?
|
||||
@ -149,6 +151,8 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
|
||||
self.statusBar.statusBarStyle = .White
|
||||
|
||||
|
||||
|
||||
self.disposable.set((account.postbox.messageView(messageId) |> deliverOnMainQueue).start(next: { [weak self] view in
|
||||
if let strongSelf = self {
|
||||
strongSelf.messageView = view
|
||||
@ -192,6 +196,9 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaManagerIndex)
|
||||
}
|
||||
self.screenCaptureEventsDisposable?.dispose()
|
||||
if let tempFile = self.tempFile {
|
||||
TempBox.shared.dispose(tempFile)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func donePressed() {
|
||||
@ -205,7 +212,7 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
}
|
||||
}, dismissController: { [weak self] in
|
||||
self?.dismiss(forceAway: true)
|
||||
}, replaceRootController: { [weak self] _, _ in
|
||||
}, replaceRootController: { _, _ in
|
||||
})
|
||||
self.displayNode = SecretMediaPreviewControllerNode(controllerInteraction: controllerInteraction)
|
||||
self.displayNodeDidLoad()
|
||||
@ -215,7 +222,7 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
|
||||
self.controllerNode.transitionDataForCentralItem = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if let centralItemNode = strongSelf.controllerNode.pager.centralItemNode(), let presentationArguments = strongSelf.presentationArguments as? GalleryControllerPresentationArguments {
|
||||
if let _ = strongSelf.controllerNode.pager.centralItemNode(), let presentationArguments = strongSelf.presentationArguments as? GalleryControllerPresentationArguments {
|
||||
if let message = strongSelf.messageView?.message {
|
||||
if let media = mediaForMessage(message: message), let transitionArguments = presentationArguments.transitionArguments(message.id, media) {
|
||||
return (transitionArguments.transitionNode, transitionArguments.addToTransitionSurface)
|
||||
@ -234,7 +241,7 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
if let strongSelf = self {
|
||||
strongSelf._hiddenMedia.set(.single(nil))
|
||||
|
||||
var animatedOutNode = true
|
||||
let animatedOutNode = true
|
||||
var animatedOutInterface = false
|
||||
|
||||
let completion = {
|
||||
@ -261,10 +268,20 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
if let _ = index {
|
||||
if let message = strongSelf.messageView?.message, let media = mediaForMessage(message: message) {
|
||||
var beginTimeAndTimeout: (Double, Double)?
|
||||
var videoDuration: Int32?
|
||||
for media in message.media {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
videoDuration = file.duration
|
||||
}
|
||||
}
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? AutoremoveTimeoutMessageAttribute {
|
||||
if let countdownBeginTime = attribute.countdownBeginTime {
|
||||
beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout))
|
||||
if let videoDuration = videoDuration {
|
||||
beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, Double(videoDuration))
|
||||
} else {
|
||||
beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout))
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
@ -302,7 +319,7 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.SecretImage_NotViewedYet(peerTitle).0
|
||||
}
|
||||
contentNode.setText(text)
|
||||
contentNode.setText(text)
|
||||
strongSelf.controllerNode.updatePresentationState({
|
||||
$0.withUpdatedFooterContentNode(contentNode)
|
||||
}, transition: .immediate)
|
||||
@ -328,7 +345,6 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
var nodeAnimatesItself = false
|
||||
|
||||
if let centralItemNode = self.controllerNode.pager.centralItemNode(), let message = self.messageView?.message {
|
||||
|
||||
if let media = mediaForMessage(message: message) {
|
||||
if let presentationArguments = self.presentationArguments as? GalleryControllerPresentationArguments, let transitionArguments = presentationArguments.transitionArguments(message.id, media) {
|
||||
nodeAnimatesItself = true
|
||||
@ -394,7 +410,20 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
if let message = message {
|
||||
if self.currentNodeMessageId != message.id {
|
||||
self.currentNodeMessageId = message.id
|
||||
guard let item = galleryItemForEntry(account: account, presentationData: self.presentationData, entry: .MessageEntry(message, false, nil, nil), streamVideos: false, hideControls: true, playbackCompleted: { [weak self] in
|
||||
var tempFilePath: String?
|
||||
for media in message.media {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
if let path = self.account.postbox.mediaBox.completedResourcePath(file.resource) {
|
||||
let tempFile = TempBox.shared.file(path: path, fileName: file.fileName ?? "file")
|
||||
self.tempFile = tempFile
|
||||
tempFilePath = tempFile.path
|
||||
self.currentNodeMessageIsVideo = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
guard let item = galleryItemForEntry(account: self.account, presentationData: self.presentationData, entry: .MessageEntry(message, false, nil, nil), streamVideos: false, hideControls: true, tempFilePath: tempFilePath, playbackCompleted: { [weak self] in
|
||||
self?.dismiss(forceAway: false)
|
||||
}) else {
|
||||
self._ready.set(.single(true))
|
||||
@ -409,10 +438,20 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
self.markMessageAsConsumedDisposable.set(markMessageContentAsConsumedInteractively(postbox: self.account.postbox, messageId: message.id).start())
|
||||
} else {
|
||||
var beginTimeAndTimeout: (Double, Double)?
|
||||
var videoDuration: Int32?
|
||||
for media in message.media {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
videoDuration = file.duration
|
||||
}
|
||||
}
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? AutoremoveTimeoutMessageAttribute {
|
||||
if let countdownBeginTime = attribute.countdownBeginTime {
|
||||
beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout))
|
||||
if let videoDuration = videoDuration {
|
||||
beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, Double(videoDuration))
|
||||
} else {
|
||||
beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout))
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
@ -428,7 +467,9 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
if !self.didSetReady {
|
||||
self._ready.set(.single(true))
|
||||
}
|
||||
self.dismiss()
|
||||
if !self.currentNodeMessageIsVideo {
|
||||
self.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ private enum SelectivePrivacyPeersEntryStableId: Hashable {
|
||||
}
|
||||
|
||||
private enum SelectivePrivacyPeersEntry: ItemListNodeEntry {
|
||||
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, ItemListPeerItemEditing, Bool)
|
||||
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, ItemListPeerItemEditing, Bool)
|
||||
case addItem(PresentationTheme, String, Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
@ -70,7 +70,7 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry {
|
||||
|
||||
var stableId: SelectivePrivacyPeersEntryStableId {
|
||||
switch self {
|
||||
case let .peerItem(_, _, _, _, peer, _, _):
|
||||
case let .peerItem(_, _, _, _, _, peer, _, _):
|
||||
return .peer(peer.id)
|
||||
case .addItem:
|
||||
return .add
|
||||
@ -79,8 +79,8 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry {
|
||||
|
||||
static func ==(lhs: SelectivePrivacyPeersEntry, rhs: SelectivePrivacyPeersEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsEditing, lhsEnabled):
|
||||
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsEditing, rhsEnabled) = rhs {
|
||||
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer, lhsEditing, lhsEnabled):
|
||||
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer, rhsEditing, rhsEnabled) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -96,6 +96,9 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameOrder != rhsNameOrder {
|
||||
return false
|
||||
}
|
||||
if lhsEditing != rhsEditing {
|
||||
return false
|
||||
}
|
||||
@ -117,9 +120,9 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry {
|
||||
|
||||
static func <(lhs: SelectivePrivacyPeersEntry, rhs: SelectivePrivacyPeersEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .peerItem(index, _, _, _, _, _, _):
|
||||
case let .peerItem(index, _, _, _, _, _, _, _):
|
||||
switch rhs {
|
||||
case let .peerItem(rhsIndex, _, _, _, _, _, _):
|
||||
case let .peerItem(rhsIndex, _, _, _, _, _, _, _):
|
||||
return index < rhsIndex
|
||||
case .addItem:
|
||||
return true
|
||||
@ -131,8 +134,8 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry {
|
||||
|
||||
func item(_ arguments: SelectivePrivacyPeersControllerArguments) -> ListViewItem {
|
||||
switch self {
|
||||
case let .peerItem(_, theme, strings, dateTimeFormat, peer, editing, enabled):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { previousId, id in
|
||||
case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, editing, enabled):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { previousId, id in
|
||||
arguments.setPeerIdWithRevealedOptions(previousId, id)
|
||||
}, removePeer: { peerId in
|
||||
arguments.removePeer(peerId)
|
||||
@ -183,7 +186,7 @@ private func selectivePrivacyPeersControllerEntries(presentationData: Presentati
|
||||
|
||||
var index: Int32 = 0
|
||||
for peer in peers {
|
||||
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.id == state.peerIdWithRevealedOptions), true))
|
||||
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.id == state.peerIdWithRevealedOptions), true))
|
||||
index += 1
|
||||
}
|
||||
|
||||
|
||||
@ -81,11 +81,9 @@ final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate {
|
||||
|
||||
self.textInputNode = EditableTextNode()
|
||||
let textColor: UIColor = theme.textColor
|
||||
let keyboardAppearance: UIKeyboardAppearance = UIKeyboardAppearance.default
|
||||
self.textInputNode.typingAttributes = [NSAttributedStringKey.font.rawValue: Font.regular(17.0), NSAttributedStringKey.foregroundColor.rawValue: textColor]
|
||||
self.textInputNode.clipsToBounds = true
|
||||
self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0)
|
||||
self.textInputNode.keyboardAppearance = keyboardAppearance
|
||||
self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0)
|
||||
self.textInputNode.keyboardAppearance = theme.keyboardAppearance
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
import CoreMedia
|
||||
import TelegramUIPrivateModule
|
||||
import SwiftSignalKit
|
||||
import FFMpeg
|
||||
|
||||
private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: UnsafeMutablePointer<UInt8>?, bufferSize: Int32) -> Int32 {
|
||||
let context = Unmanaged<SoftwareVideoSource>.fromOpaque(userData!).takeUnretainedValue()
|
||||
@ -14,7 +14,7 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa
|
||||
private func seekCallback(userData: UnsafeMutableRawPointer?, offset: Int64, whence: Int32) -> Int64 {
|
||||
let context = Unmanaged<SoftwareVideoSource>.fromOpaque(userData!).takeUnretainedValue()
|
||||
if let fd = context.fd {
|
||||
if (whence & AVSEEK_SIZE) != 0 {
|
||||
if (whence & FFMPEG_AVSEEK_SIZE) != 0 {
|
||||
return Int64(context.size)
|
||||
} else {
|
||||
lseek(fd, off_t(offset), SEEK_SET)
|
||||
@ -47,8 +47,8 @@ private final class SoftwareVideoStream {
|
||||
final class SoftwareVideoSource {
|
||||
private var readingError = false
|
||||
private var videoStream: SoftwareVideoStream?
|
||||
private var avIoContext: UnsafeMutablePointer<AVIOContext>?
|
||||
private var avFormatContext: UnsafeMutablePointer<AVFormatContext>?
|
||||
private var avIoContext: FFMpegAVIOContext?
|
||||
private var avFormatContext: FFMpegAVFormatContext?
|
||||
private let path: String
|
||||
fileprivate let fd: Int32?
|
||||
fileprivate let size: Int32
|
||||
@ -69,25 +69,21 @@ final class SoftwareVideoSource {
|
||||
|
||||
self.path = path
|
||||
|
||||
var avFormatContextRef = avformat_alloc_context()
|
||||
guard let avFormatContext = avFormatContextRef else {
|
||||
self.readingError = true
|
||||
return
|
||||
}
|
||||
let avFormatContext = FFMpegAVFormatContext()
|
||||
|
||||
let ioBufferSize = 64 * 1024
|
||||
let avIoBuffer = av_malloc(ioBufferSize)!
|
||||
let avIoContextRef = avio_alloc_context(avIoBuffer.assumingMemoryBound(to: UInt8.self), Int32(ioBufferSize), 0, Unmanaged.passUnretained(self).toOpaque(), readPacketCallback, nil, seekCallback)
|
||||
self.avIoContext = avIoContextRef
|
||||
|
||||
avFormatContext.pointee.pb = self.avIoContext
|
||||
let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, seek: seekCallback)
|
||||
self.avIoContext = avIoContext
|
||||
|
||||
guard avformat_open_input(&avFormatContextRef, nil, nil, nil) >= 0 else {
|
||||
avFormatContext.setIO(self.avIoContext!)
|
||||
|
||||
if !avFormatContext.openInput() {
|
||||
self.readingError = true
|
||||
return
|
||||
}
|
||||
|
||||
guard avformat_find_stream_info(avFormatContext, nil) >= 0 else {
|
||||
if !avFormatContext.findStreamInfo() {
|
||||
self.readingError = true
|
||||
return
|
||||
}
|
||||
@ -96,40 +92,30 @@ final class SoftwareVideoSource {
|
||||
|
||||
var videoStream: SoftwareVideoStream?
|
||||
|
||||
for streamIndex in FFMpegMediaFrameSourceContextHelpers.streamIndices(formatContext: avFormatContext, codecType: AVMEDIA_TYPE_VIDEO) {
|
||||
if (avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.disposition & Int32(AV_DISPOSITION_ATTACHED_PIC)) == 0 {
|
||||
|
||||
let codecPar = avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.codecpar!
|
||||
|
||||
if let codec = avcodec_find_decoder(codecPar.pointee.codec_id) {
|
||||
if let codecContext = avcodec_alloc_context3(codec) {
|
||||
if avcodec_parameters_to_context(codecContext, avFormatContext.pointee.streams[streamIndex]!.pointee.codecpar) >= 0 {
|
||||
if avcodec_open2(codecContext, codec, nil) >= 0 {
|
||||
let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 24))
|
||||
|
||||
let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale)
|
||||
|
||||
var rotationAngle: Double = 0.0
|
||||
if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value {
|
||||
if strcmp(value, "0") != 0 {
|
||||
if let angle = Double(String(cString: value)) {
|
||||
rotationAngle = angle * Double.pi / 180.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height)
|
||||
|
||||
videoStream = SoftwareVideoStream(index: streamIndex, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaVideoFrameDecoder(codecContext: codecContext), rotationAngle: rotationAngle, aspect: aspect)
|
||||
break
|
||||
} else {
|
||||
var codecContextRef: UnsafeMutablePointer<AVCodecContext>? = codecContext
|
||||
avcodec_free_context(&codecContextRef)
|
||||
}
|
||||
} else {
|
||||
var codecContextRef: UnsafeMutablePointer<AVCodecContext>? = codecContext
|
||||
avcodec_free_context(&codecContextRef)
|
||||
}
|
||||
for streamIndexNumber in avFormatContext.streamIndices(for: FFMpegAVFormatStreamTypeVideo) {
|
||||
let streamIndex = streamIndexNumber.int32Value
|
||||
if avFormatContext.isAttachedPic(atStreamIndex: streamIndex) {
|
||||
continue
|
||||
}
|
||||
|
||||
let codecId = avFormatContext.codecId(atStreamIndex: streamIndex)
|
||||
|
||||
let fpsAndTimebase = avFormatContext.fpsAndTimebase(forStreamIndex: streamIndex, defaultTimeBase: CMTimeMake(1, 40000))
|
||||
let (fps, timebase) = (fpsAndTimebase.fps, fpsAndTimebase.timebase)
|
||||
|
||||
let duration = CMTimeMake(avFormatContext.duration(atStreamIndex: streamIndex), timebase.timescale)
|
||||
|
||||
let metrics = avFormatContext.metricsForStream(at: streamIndex)
|
||||
|
||||
let rotationAngle: Double = metrics.rotationAngle
|
||||
let aspect = Double(metrics.width) / Double(metrics.height)
|
||||
|
||||
if let codec = FFMpegAVCodec.find(forId: codecId) {
|
||||
let codecContext = FFMpegAVCodecContext(codec: codec)
|
||||
if avFormatContext.codecParams(atStreamIndex: streamIndex, to: codecContext) {
|
||||
if codecContext.open() {
|
||||
videoStream = SoftwareVideoStream(index: Int(streamIndex), fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaVideoFrameDecoder(codecContext: codecContext), rotationAngle: rotationAngle, aspect: aspect)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,15 +125,6 @@ final class SoftwareVideoSource {
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let avIoContext = self.avIoContext {
|
||||
if avIoContext.pointee.buffer != nil {
|
||||
av_free(avIoContext.pointee.buffer)
|
||||
}
|
||||
av_free(avIoContext)
|
||||
}
|
||||
if let avFormatContext = self.avFormatContext {
|
||||
avformat_free_context(avFormatContext)
|
||||
}
|
||||
if let fd = self.fd {
|
||||
close(fd)
|
||||
}
|
||||
@ -159,10 +136,10 @@ final class SoftwareVideoSource {
|
||||
}
|
||||
|
||||
let packet = FFMpegPacket()
|
||||
if av_read_frame(avFormatContext, &packet.packet) < 0 {
|
||||
return nil
|
||||
} else {
|
||||
if avFormatContext.readFrame(into: packet) {
|
||||
return packet
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,17 +149,15 @@ final class SoftwareVideoSource {
|
||||
|
||||
while !self.readingError && frames.isEmpty {
|
||||
if let packet = self.readPacketInternal() {
|
||||
if let videoStream = videoStream, Int(packet.packet.stream_index) == videoStream.index {
|
||||
let avNoPtsRawValue: UInt64 = 0x8000000000000000
|
||||
let avNoPtsValue = Int64(bitPattern: avNoPtsRawValue)
|
||||
let packetPts = packet.packet.pts == avNoPtsValue ? packet.packet.dts : packet.packet.pts
|
||||
if let videoStream = videoStream, Int(packet.streamIndex) == videoStream.index {
|
||||
let packetPts = packet.pts
|
||||
|
||||
let pts = CMTimeMake(packetPts, videoStream.timebase.timescale)
|
||||
let dts = CMTimeMake(packet.packet.dts, videoStream.timebase.timescale)
|
||||
let dts = CMTimeMake(packet.dts, videoStream.timebase.timescale)
|
||||
|
||||
let duration: CMTime
|
||||
|
||||
let frameDuration = packet.packet.duration
|
||||
let frameDuration = packet.duration
|
||||
if frameDuration != 0 {
|
||||
duration = CMTimeMake(frameDuration * videoStream.timebase.value, videoStream.timebase.timescale)
|
||||
} else {
|
||||
@ -198,7 +173,7 @@ final class SoftwareVideoSource {
|
||||
} else {
|
||||
if let avFormatContext = self.avFormatContext, let videoStream = self.videoStream {
|
||||
endOfStream = true
|
||||
av_seek_frame(avFormatContext, Int32(videoStream.index), 0, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME)
|
||||
avFormatContext.seekFrame(forStreamIndex: Int32(videoStream.index), pts: 0)
|
||||
} else {
|
||||
endOfStream = true
|
||||
break
|
||||
|
||||
@ -36,7 +36,7 @@ private enum StorageUsageEntry: ItemListNodeEntry {
|
||||
case clearAll(PresentationTheme, String, String, Bool)
|
||||
|
||||
case peersHeader(PresentationTheme, String)
|
||||
case peer(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, Peer?, String)
|
||||
case peer(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, Peer?, String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -65,7 +65,7 @@ private enum StorageUsageEntry: ItemListNodeEntry {
|
||||
return 4
|
||||
case .peersHeader:
|
||||
return 5
|
||||
case let .peer(index, _, _, _, _, _, _):
|
||||
case let .peer(index, _, _, _, _, _, _, _):
|
||||
return 6 + index
|
||||
}
|
||||
}
|
||||
@ -108,8 +108,8 @@ private enum StorageUsageEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peer(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsChatPeer, lhsValue):
|
||||
if case let .peer(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsChatPeer, rhsValue) = rhs {
|
||||
case let .peer(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer, lhsChatPeer, lhsValue):
|
||||
if case let .peer(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer, rhsChatPeer, rhsValue) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -122,6 +122,9 @@ private enum StorageUsageEntry: ItemListNodeEntry {
|
||||
if lhsDateTimeFormat != rhsDateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhsNameOrder != rhsNameOrder {
|
||||
return false
|
||||
}
|
||||
if !arePeersEqual(lhsPeer, rhsPeer) {
|
||||
return false
|
||||
}
|
||||
@ -160,8 +163,8 @@ private enum StorageUsageEntry: ItemListNodeEntry {
|
||||
return ItemListDisclosureItem(theme: theme, icon: nil, title: text, kind: enabled ? .generic : .disabled, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||
arguments.openClearAll()
|
||||
})
|
||||
case let .peer(_, theme, strings, dateTimeFormat, peer, chatPeer, value):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, aliasHandling: .threatSelfAsSaved, nameColor: chatPeer == nil ? .primary : .secret, presence: nil, text: .none, label: .disclosure(value), editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: {
|
||||
case let .peer(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, chatPeer, value):
|
||||
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, aliasHandling: .threatSelfAsSaved, nameColor: chatPeer == nil ? .primary : .secret, presence: nil, text: .none, label: .disclosure(value), editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: {
|
||||
let resolvedPeer = chatPeer ?? peer
|
||||
arguments.openPeerMedia(resolvedPeer.id)
|
||||
}, setPeerIdWithRevealedOptions: { previousId, id in
|
||||
@ -223,7 +226,7 @@ private func storageUsageControllerEntries(presentationData: PresentationData, c
|
||||
chatPeer = mainPeer
|
||||
mainPeer = associatedPeer
|
||||
}
|
||||
entries.append(.peer(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, mainPeer, chatPeer, dataSizeString(size)))
|
||||
entries.append(.peer(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, mainPeer, chatPeer, dataSizeString(size)))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,13 +7,13 @@ enum MessageTimestampStatusFormat {
|
||||
case minimal
|
||||
}
|
||||
|
||||
func stringForMessageTimestampStatus(message: Message, dateTimeFormat: PresentationDateTimeFormat, strings: PresentationStrings, format: MessageTimestampStatusFormat = .regular) -> String {
|
||||
func stringForMessageTimestampStatus(message: Message, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, strings: PresentationStrings, format: MessageTimestampStatusFormat = .regular) -> String {
|
||||
var dateText = stringForMessageTimestamp(timestamp: message.timestamp, dateTimeFormat: dateTimeFormat)
|
||||
|
||||
var authorTitle: String?
|
||||
if let author = message.author as? TelegramUser {
|
||||
if let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
||||
authorTitle = author.displayTitle
|
||||
authorTitle = author.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||
}
|
||||
} else {
|
||||
if let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
||||
|
||||
@ -229,6 +229,14 @@ public final class TelegramApplicationContext {
|
||||
let _ = immediateExperimentalUISettingsValue.swap(settings)
|
||||
}
|
||||
})
|
||||
|
||||
let _ = self.contactDataManager.personNameDisplayOrder().start(next: { order in
|
||||
let _ = updateContactSettingsInteractively(postbox: postbox, { settings in
|
||||
var settings = settings
|
||||
settings.nameDisplayOrder = order
|
||||
return settings
|
||||
}).start()
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
module TelegramUIPrivateModule {
|
||||
private header "../../third-party/FFmpeg-iOS/include/libavcodec/avcodec.h"
|
||||
private header "../../third-party/FFmpeg-iOS/include/libavformat/avformat.h"
|
||||
private header "../../third-party/FFmpeg-iOS/include/libavformat/avio.h"
|
||||
private header "../../third-party/FFmpeg-iOS/include/libavutil/avutil.h"
|
||||
private header "../../third-party/FFmpeg-iOS/include/libavutil/pixdesc.h"
|
||||
private header "../../third-party/FFmpeg-iOS/include/libswresample/swresample.h"
|
||||
//private header "../../third-party/FFmpeg-iOS/include/libavcodec/avcodec.h"
|
||||
//private header "../../third-party/FFmpeg-iOS/include/libavformat/avformat.h"
|
||||
//private header "../../third-party/FFmpeg-iOS/include/libavformat/avio.h"
|
||||
//private header "../../third-party/FFmpeg-iOS/include/libavutil/avutil.h"
|
||||
//private header "../../third-party/FFmpeg-iOS/include/libavutil/pixdesc.h"
|
||||
//private header "../../third-party/FFmpeg-iOS/include/libswresample/swresample.h"
|
||||
|
||||
header "../../third-party/opusenc/opusenc.h"
|
||||
header "../TGDataItem.h"
|
||||
header "../FFMpegSwResample.h"
|
||||
|
||||
@ -14,8 +14,9 @@ class ThemeSettingsChatPreviewItem: ListViewItem, ItemListItem {
|
||||
let fontSize: PresentationFontSize
|
||||
let wallpaper: TelegramWallpaper
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
|
||||
init(account: Account, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat) {
|
||||
init(account: Account, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) {
|
||||
self.account = account
|
||||
self.theme = theme
|
||||
self.componentTheme = componentTheme
|
||||
@ -24,6 +25,7 @@ class ThemeSettingsChatPreviewItem: ListViewItem, ItemListItem {
|
||||
self.fontSize = fontSize
|
||||
self.wallpaper = wallpaper
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
}
|
||||
|
||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
@ -152,7 +154,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3)
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: item.strings.Appearance_PreviewReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
|
||||
let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: item.componentTheme, wallpaper: item.wallpaper), fontSize: item.fontSize, strings: item.strings, dateTimeFormat: item.dateTimeFormat, disableAnimations: false)
|
||||
let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: item.componentTheme, wallpaper: item.wallpaper), fontSize: item.fontSize, strings: item.strings, dateTimeFormat: item.dateTimeFormat, nameDisplayOrder: item.nameDisplayOrder, disableAnimations: false)
|
||||
|
||||
let item2: ChatMessageItem = ChatMessageItem(presentationData: chatPresentationData, account: item.account, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: item.strings.Appearance_PreviewIncomingText, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, isAdmin: false), disableDate: true)
|
||||
let item1: ChatMessageItem = ChatMessageItem(presentationData: chatPresentationData, account: item.account, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: TelegramUser(id: item.account.peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []), text: item.strings.Appearance_PreviewOutgoingText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, isAdmin: false), disableDate: true)
|
||||
|
||||
@ -35,7 +35,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
||||
case fontSizeHeader(PresentationTheme, String)
|
||||
case fontSize(PresentationTheme, PresentationFontSize)
|
||||
case chatPreviewHeader(PresentationTheme, String)
|
||||
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat)
|
||||
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder)
|
||||
case wallpaper(PresentationTheme, String)
|
||||
case accentColor(PresentationTheme, String, Int32)
|
||||
case autoNightTheme(PresentationTheme, String, String)
|
||||
@ -95,8 +95,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat):
|
||||
if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat {
|
||||
case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder):
|
||||
if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -178,8 +178,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
case let .chatPreviewHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat):
|
||||
return ThemeSettingsChatPreviewItem(account: arguments.account, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat)
|
||||
case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder):
|
||||
return ThemeSettingsChatPreviewItem(account: arguments.account, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder)
|
||||
case let .wallpaper(theme, text):
|
||||
return ItemListDisclosureItem(theme: theme, title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openWallpaperSettings()
|
||||
@ -216,7 +216,7 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
|
||||
entries.append(.fontSizeHeader(presentationData.theme, strings.Appearance_TextSize))
|
||||
entries.append(.fontSize(presentationData.theme, fontSize))
|
||||
entries.append(.chatPreviewHeader(presentationData.theme, strings.Appearance_Preview))
|
||||
entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat))
|
||||
entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder))
|
||||
entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground))
|
||||
if theme.name == .builtin(.day) {
|
||||
entries.append(.accentColor(presentationData.theme, strings.Appearance_AccentColor, themeAccentColor ?? defaultDayAccentColor))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user