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

This commit is contained in:
Ilya Laktyushin 2018-12-11 23:52:08 +04:00
commit 3d4bb0f742
104 changed files with 1917 additions and 1372 deletions

View File

@ -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 */
};

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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
}
}

View File

@ -146,7 +146,7 @@ public final class CallController: ViewController {
})
])
])
strongSelf.present(actionSheet, in: .window(.root))
strongSelf.present(actionSheet, in: .window(.calls))
}
}

View File

@ -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

View File

@ -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)"

View File

@ -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: [])

View File

@ -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 {

View File

@ -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)
})
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -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() {

View File

@ -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()
}

View File

@ -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()

View File

@ -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 {

View File

@ -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 {

View File

@ -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)
}

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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) }
}

View File

@ -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 {

View File

@ -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:

View File

@ -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:

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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 {

View File

@ -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)
}

View File

@ -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

View File

@ -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))) {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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()

View File

@ -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 {

View File

@ -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

View File

@ -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()
}
}

View File

@ -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
}()

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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:

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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()

View File

@ -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
}
}
}
*/

View File

@ -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)

View File

@ -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
}
}

View File

@ -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)
})
}

View File

@ -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()
}

View File

@ -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
}
}

View File

@ -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)

View File

@ -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 = ""

View File

@ -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() {

View File

@ -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)
}
}

View File

@ -1,6 +1,6 @@
import Foundation
import CoreMedia
import TelegramUIPrivateModule
import FFMpeg
enum MediaTrackFrameType {
case video

View File

@ -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))
}

View File

@ -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:

View File

@ -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) {

View File

@ -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() {

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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))
}
}
}

View File

@ -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

View File

@ -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))
}
}
}

View File

@ -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:

View File

@ -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()
}
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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"

View File

@ -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)

View File

@ -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