From 9b7562c7bce29849204ae36200c88e3c60a9b855 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 24 Mar 2023 21:43:27 +0400 Subject: [PATCH 1/7] Temp --- .../Sources/OngoingCallContext.swift | 269 +++++++++++++++++- .../OngoingCallThreadLocalContext.h | 11 +- .../Sources/OngoingCallThreadLocalContext.mm | 44 ++- 3 files changed, 305 insertions(+), 19 deletions(-) diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index ddd3f5e954..92a1e664d1 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -933,22 +933,58 @@ public final class OngoingCallContext { } } - let context = OngoingCallThreadLocalContextWebrtc(version: version, queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: Data(), key: key, isOutgoing: isOutgoing, connections: filteredConnections, maxLayer: maxLayer, allowP2P: allowP2P, allowTCP: enableTCP, enableStunMarking: enableStunMarking, logPath: logPath, statsLogPath: tempStatsLogPath, sendSignalingData: { [weak callSessionManager] data in - queue.async { - guard let strongSelf = self else { - return - } - if let signalingConnectionManager = strongSelf.signalingConnectionManager { - signalingConnectionManager.with { impl in - impl.send(payloadData: data) - } - } - - if let callSessionManager = callSessionManager { - callSessionManager.sendSignalingData(internalId: internalId, data: data) + var directConnection: OngoingCallDirectConnection? + #if DEBUG + if #available(iOS 12.0, *) { + for connection in filteredConnections { + if connection.username == "reflector" && connection.reflectorId == 1 && !connection.hasTcp && connection.hasTurn { + directConnection = CallDirectConnectionImpl(host: connection.ip, port: Int(connection.port), peerTag: dataWithHexString(connection.password)) + break } } - }, videoCapturer: video?.impl, preferredVideoCodec: preferredVideoCodec, audioInputDeviceId: "", audioDevice: audioDevice?.impl) + } + #else + directConnection = nil + #endif + + let context = OngoingCallThreadLocalContextWebrtc( + version: version, + queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), + proxy: voipProxyServer, + networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), + dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), + derivedState: Data(), + key: key, + isOutgoing: isOutgoing, + connections: filteredConnections, + maxLayer: maxLayer, + allowP2P: allowP2P, + allowTCP: enableTCP, + enableStunMarking: enableStunMarking, + logPath: logPath, + statsLogPath: tempStatsLogPath, + sendSignalingData: { [weak callSessionManager] data in + queue.async { + guard let strongSelf = self else { + return + } + if let signalingConnectionManager = strongSelf.signalingConnectionManager { + signalingConnectionManager.with { impl in + impl.send(payloadData: data) + } + } + + if let callSessionManager = callSessionManager { + callSessionManager.sendSignalingData(internalId: internalId, data: data) + } + } + }, + videoCapturer: video?.impl, + preferredVideoCodec: preferredVideoCodec, + audioInputDeviceId: "", + audioDevice: audioDevice?.impl, + directConnection: directConnection + ) strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context)) context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in @@ -1287,12 +1323,204 @@ public final class OngoingCallContext { } } -private protocol CallSignalingConnection { +private protocol CallSignalingConnection: AnyObject { func start() func stop() func send(payloadData: Data) } +@available(iOS 13.0, *) +private class CustomWrapperProtocol: NWProtocolFramerImplementation { + static var label: String = "CustomWrapperProtocol" + + static let definition = NWProtocolFramer.Definition(implementation: CustomWrapperProtocol.self) + + required init(framer: NWProtocolFramer.Instance) { + + } + + func start(framer: NWProtocolFramer.Instance) -> NWProtocolFramer.StartResult { + return .ready + } + + func handleInput(framer: NWProtocolFramer.Instance) -> Int { + preconditionFailure() + } + + func handleOutput(framer: NWProtocolFramer.Instance, message: NWProtocolFramer.Message, messageLength: Int, isComplete: Bool) { + preconditionFailure() + } + + func wakeup(framer: NWProtocolFramer.Instance) { + } + + func stop(framer: NWProtocolFramer.Instance) -> Bool { + return true + } + + func cleanup(framer: NWProtocolFramer.Instance) { + } +} + +@available(iOS 12.0, *) +private final class CallDirectConnectionImpl: NSObject, OngoingCallDirectConnection { + private final class Impl { + private let queue: Queue + private let peerTag: Data + + private var connection: NWConnection? + + var incomingDataHandler: ((Data) -> Void)? + + init(queue: Queue, host: String, port: Int, peerTag: Data) { + self.queue = queue + + var peerTag = peerTag + peerTag.withUnsafeMutableBytes { buffer in + let bytes = buffer.baseAddress!.assumingMemoryBound(to: UInt8.self) + for i in (buffer.count - 4) ..< buffer.count { + bytes.advanced(by: i).pointee = 1 + } + } + self.peerTag = peerTag + + if let port = NWEndpoint.Port(rawValue: UInt16(clamping: port)) { + self.connection = NWConnection(host: NWEndpoint.Host(host), port: port, using: .udp) + } + + self.connection?.stateUpdateHandler = { newState in + switch newState { + case .ready: + print("CallDirectConnection: State: Ready") + case .setup: + print("CallDirectConnection: State: Setup") + case .cancelled: + print("CallDirectConnection: State: Cancelled") + case .preparing: + print("CallDirectConnection: State: Preparing") + case let .waiting(error): + print("CallDirectConnection: State: Waiting (\(error))") + case let .failed(error): + print("CallDirectConnection: State: Error (\(error))") + @unknown default: + print("CallDirectConnection: State: Unknown") + } + } + + self.connection?.start(queue: self.queue.queue) + self.receive() + } + + deinit { + + } + + private func receive() { + let queue = self.queue + self.connection?.receiveMessage(completion: { [weak self] data, _, _, error in + assert(queue.isCurrent()) + + guard let self else { + return + } + + if let data { + if data.count >= 16 { + var unwrappedData = Data(count: data.count - 16) + unwrappedData.withUnsafeMutableBytes { destBuffer -> Void in + data.withUnsafeBytes { sourceBuffer -> Void in + sourceBuffer.copyBytes(to: destBuffer, from: 16 ..< sourceBuffer.count) + } + } + + self.incomingDataHandler?(unwrappedData) + } else { + print("Invalid data size") + } + } + if error == nil { + self.receive() + } + }) + } + + func send(data: Data) { + var wrappedData = Data() + wrappedData.append(self.peerTag) + wrappedData.append(data) + + self.connection?.send(content: wrappedData, completion: .contentProcessed({ error in + if let error { + print("Send error: \(error)") + } + })) + } + } + + private static let sharedQueue = Queue(name: "CallDirectConnectionImpl") + + private let queue: Queue + private let impl: QueueLocalObject + + private let incomingDataHandlers = Atomic Void>>(value: Bag()) + + init(host: String, port: Int, peerTag: Data) { + let queue = CallDirectConnectionImpl.sharedQueue + self.queue = queue + self.impl = QueueLocalObject(queue: queue, generate: { + return Impl(queue: queue, host: host, port: port, peerTag: peerTag) + }) + + let incomingDataHandlers = self.incomingDataHandlers + self.impl.with { [weak incomingDataHandlers] impl in + impl.incomingDataHandler = { data in + guard let incomingDataHandlers else { + return + } + for f in incomingDataHandlers.with({ return $0.copyItems() }) { + f(data) + } + } + } + } + + func add(onIncomingPacket addOnIncomingPacket: @escaping (Data) -> Void) -> Data { + var token = self.incomingDataHandlers.with { bag -> Int32 in + return Int32(bag.add(addOnIncomingPacket)) + } + return withUnsafeBytes(of: &token, { buffer -> Data in + let bytes = buffer.baseAddress!.assumingMemoryBound(to: UInt8.self) + return Data(bytes: bytes, count: 4) + }) + } + + func remove(onIncomingPacket token: Data) { + if token.count != 4 { + return + } + + var tokenValue: Int32 = 0 + withUnsafeMutableBytes(of: &tokenValue, { tokenBuffer in + let tokenBytes = tokenBuffer.baseAddress!.assumingMemoryBound(to: UInt8.self) + + token.withUnsafeBytes { sourceBuffer in + let sourceBytes = sourceBuffer.baseAddress!.assumingMemoryBound(to: UInt8.self) + memcpy(tokenBytes, sourceBytes, 4) + } + }) + + self.incomingDataHandlers.with { bag in + bag.remove(Int(tokenValue)) + } + } + + func sendPacket(_ packet: Data) { + self.impl.with { impl in + impl.send(data: packet) + } + } +} + @available(iOS 12.0, *) private final class CallSignalingConnectionImpl: CallSignalingConnection { private let queue: Queue @@ -1316,7 +1544,18 @@ private final class CallSignalingConnectionImpl: CallSignalingConnection { self.peerTag = peerTag self.dataReceived = dataReceived self.isClosed = isClosed + + #if DEBUG + if #available(iOS 15.0, *) { + let parameters = NWParameters.quic(alpn: ["tgcalls"]) + parameters.defaultProtocolStack.internetProtocol = NWProtocolFramer.Options(definition: CustomWrapperProtocol.definition) + self.connection = NWConnection(host: self.host, port: self.port, using: parameters) + } else { + preconditionFailure() + } + #else self.connection = NWConnection(host: self.host, port: self.port, using: .tcp) + #endif self.connection.stateUpdateHandler = { [weak self] state in queue.async { diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h index 734f600161..daf0875498 100644 --- a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h +++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h @@ -215,6 +215,14 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) { @end +@protocol OngoingCallDirectConnection + +- (NSData * _Nonnull)addOnIncomingPacket:(void (^_Nonnull)(NSData * _Nonnull))addOnIncomingPacket; +- (void)removeOnIncomingPacket:(NSData * _Nonnull)token; +- (void)sendPacket:(NSData * _Nonnull)packet; + +@end + @interface OngoingCallThreadLocalContextWebrtc : NSObject + (void)logMessage:(NSString * _Nonnull)string; @@ -245,7 +253,8 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) { sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec audioInputDeviceId:(NSString * _Nonnull)audioInputDeviceId - audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice; + audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice + directConnection:(id _Nullable)directConnection; - (void)setManualAudioSessionIsActive:(bool)isAudioSessionActive; diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index 907e01cd87..110536a4ed 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -134,7 +134,7 @@ private: self = [super init]; if (self != nil) { _audioDeviceModule.reset(new tgcalls::ThreadLocalObject(tgcalls::StaticThreads::getThreads()->getWorkerThread(), [disableRecording]() mutable { - return (tgcalls::SharedAudioDeviceModule *)(new SharedAudioDeviceModuleImpl(disableRecording)); + return std::static_pointer_cast(std::make_shared(disableRecording)); })); } return self; @@ -535,6 +535,37 @@ private: void (^_frameReceived)(webrtc::VideoFrame const &); }; +class DirectConnectionChannelImpl : public tgcalls::DirectConnectionChannel { +public: + DirectConnectionChannelImpl(id _Nonnull impl) { + _impl = impl; + } + + virtual ~DirectConnectionChannelImpl() { + } + + virtual std::vector addOnIncomingPacket(std::function>)> &&handler) override { + __block auto localHandler = std::move(handler); + + NSData *token = [_impl addOnIncomingPacket:^(NSData * _Nonnull data) { + std::shared_ptr> mappedData = std::make_shared>((uint8_t const *)data.bytes, (uint8_t const *)data.bytes + data.length); + localHandler(mappedData); + }]; + return std::vector((uint8_t * const)token.bytes, (uint8_t * const)token.bytes + token.length); + } + + virtual void removeOnIncomingPacket(std::vector &token) override { + [_impl removeOnIncomingPacket:[[NSData alloc] initWithBytes:token.data() length:token.size()]]; + } + + virtual void sendPacket(std::unique_ptr> &&packet) override { + [_impl sendPacket:[[NSData alloc] initWithBytes:packet->data() length:packet->size()]]; + } + +private: + id _impl; +}; + } @interface GroupCallVideoSink : NSObject { @@ -1024,7 +1055,8 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec audioInputDeviceId:(NSString * _Nonnull)audioInputDeviceId - audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice { + audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice + directConnection:(id _Nullable)directConnection { self = [super init]; if (self != nil) { _version = version; @@ -1149,6 +1181,11 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; audioDeviceModule = [_audioDevice getAudioDeviceModule]; } + std::shared_ptr directConnectionChannel; + if (directConnection) { + directConnectionChannel = std::static_pointer_cast(std::make_shared(directConnection)); + } + __weak OngoingCallThreadLocalContextWebrtc *weakSelf = self; _tgVoip = tgcalls::Meta::Create([version UTF8String], (tgcalls::Descriptor){ .version = [version UTF8String], @@ -1288,7 +1325,8 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; }]; return resultModule; } - } + }, + .directConnectionChannel = directConnectionChannel }); _state = OngoingCallStateInitializing; _signalBars = 4; From fc5fe12730f4c6720b3341b9e032a0a4bf58a535 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Mon, 27 Mar 2023 21:19:39 +0400 Subject: [PATCH 2/7] Update tgcalls --- submodules/TgVoipWebrtc/tgcalls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 35d5d408ce..a04d4e182a 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 35d5d408cee8c69b61a32bc96dbfccc37980a5ae +Subproject commit a04d4e182a77977b30b82f648a589d77b63ce74c From 8fd43ef1d67f3c98d7f307fe0f1970518e314368 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Wed, 12 Apr 2023 20:48:28 +0400 Subject: [PATCH 3/7] Update build system --- build-system/Make/ProjectGeneration.py | 2 ++ build-system/bazel-rules/rules_xcodeproj | 2 +- submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh | 2 +- third-party/yasm/BUILD | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build-system/Make/ProjectGeneration.py b/build-system/Make/ProjectGeneration.py index 046ee4d9bd..05a326f441 100644 --- a/build-system/Make/ProjectGeneration.py +++ b/build-system/Make/ProjectGeneration.py @@ -24,6 +24,7 @@ def generate_xcodeproj(build_environment: BuildEnvironment, disable_extensions, bazel_generate_arguments += ['--override_repository=build_configuration={}'.format(configuration_path)] if disable_extensions: bazel_generate_arguments += ['--//{}:disableExtensions'.format(app_target)] + bazel_generate_arguments += ['--//{}:disableStripping'.format('Telegram')] project_bazel_arguments = [] for argument in bazel_app_arguments: @@ -31,6 +32,7 @@ def generate_xcodeproj(build_environment: BuildEnvironment, disable_extensions, project_bazel_arguments += ['--override_repository=build_configuration={}'.format(configuration_path)] if disable_extensions: project_bazel_arguments += ['--//{}:disableExtensions'.format(app_target)] + project_bazel_arguments += ['--//{}:disableStripping'.format('Telegram')] xcodeproj_bazelrc = os.path.join(build_environment.base_path, 'xcodeproj.bazelrc') if os.path.isfile(xcodeproj_bazelrc): diff --git a/build-system/bazel-rules/rules_xcodeproj b/build-system/bazel-rules/rules_xcodeproj index dc226d129a..7f600ddd7c 160000 --- a/build-system/bazel-rules/rules_xcodeproj +++ b/build-system/bazel-rules/rules_xcodeproj @@ -1 +1 @@ -Subproject commit dc226d129aca2237982b98a95c80ed1ccc74f0c5 +Subproject commit 7f600ddd7cb3ebc59c696a4639b84b93267c7b6e diff --git a/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh b/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh index 50f4990e21..95404823f2 100755 --- a/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh +++ b/submodules/ffmpeg/Sources/FFMpeg/build-ffmpeg-bazel.sh @@ -181,7 +181,7 @@ then echo "$CONFIGURE_FLAGS" > "$CONFIGURED_MARKER" fi - CORE_COUNT=`sysctl -n hw.logicalcpu` + CORE_COUNT=`PATH="$PATH:/usr/sbin" sysctl -n hw.logicalcpu` make -j$CORE_COUNT install $EXPORT || exit 1 popd diff --git a/third-party/yasm/BUILD b/third-party/yasm/BUILD index 38efc12b07..bc405ece85 100644 --- a/third-party/yasm/BUILD +++ b/third-party/yasm/BUILD @@ -9,7 +9,7 @@ genrule( cmd_bash = """ set -x - core_count="`sysctl -n hw.logicalcpu`" + core_count=`PATH="$$PATH:/usr/sbin" sysctl -n hw.logicalcpu` BUILD_DIR="$(RULEDIR)/build" rm -rf "$$BUILD_DIR" mkdir -p "$$BUILD_DIR" From ed13fd8134f3815f524f3cee64781eb5580a7329 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Wed, 12 Apr 2023 22:06:24 +0400 Subject: [PATCH 4/7] Use 9.0.0 as a marker for the direct connection experiment --- .../Sources/OngoingCallContext.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index 92a1e664d1..bb60b75c2b 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -934,18 +934,18 @@ public final class OngoingCallContext { } var directConnection: OngoingCallDirectConnection? - #if DEBUG - if #available(iOS 12.0, *) { - for connection in filteredConnections { - if connection.username == "reflector" && connection.reflectorId == 1 && !connection.hasTcp && connection.hasTurn { - directConnection = CallDirectConnectionImpl(host: connection.ip, port: Int(connection.port), peerTag: dataWithHexString(connection.password)) - break + if version == "9.0.0" { + if #available(iOS 12.0, *) { + for connection in filteredConnections { + if connection.username == "reflector" && connection.reflectorId == 1 && !connection.hasTcp && connection.hasTurn { + directConnection = CallDirectConnectionImpl(host: connection.ip, port: Int(connection.port), peerTag: dataWithHexString(connection.password)) + break + } } } + } else { + directConnection = nil } - #else - directConnection = nil - #endif let context = OngoingCallThreadLocalContextWebrtc( version: version, From 1d6d5d53a8235f653fa8152a38d85f2d8a651456 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Wed, 12 Apr 2023 22:10:42 +0400 Subject: [PATCH 5/7] Update tgcalls --- submodules/TgVoipWebrtc/tgcalls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index a04d4e182a..1e528141ed 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit a04d4e182a77977b30b82f648a589d77b63ce74c +Subproject commit 1e528141ed28f188b9b6dc721c8b630541dfb1b0 From c7803123e8f19cd52bf51501ff02cbf45b41eb57 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Wed, 12 Apr 2023 22:58:18 +0400 Subject: [PATCH 6/7] Update localization --- Telegram/Telegram-iOS/en.lproj/Localizable.strings | 4 ++++ submodules/ChatListUI/Sources/ChatListController.swift | 3 +-- .../ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift | 5 ++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index d3c26358da..4a7d0d41da 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9323,3 +9323,7 @@ Sorry for the inconvenience."; "Conversation.Theme.PreviewDarkShort" = "Tap to view this theme in the night mode."; "Conversation.Theme.PreviewLightShort" = "Tap to view this theme in the day mode."; + +"ChatList.EmptyListContactsHeader" = "YOUR CONTACTS ON TELEGRAM"; +"ChatList.EmptyListContactsHeaderHide" = "hide"; +"ChatList.EmptyListTooltip" = "Send a message or\nstart a group here."; diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index a571f13603..087002b3b6 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2016,8 +2016,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let componentView = self.headerContentView.view as? ChatListHeaderComponent.View { if let rightButtonView = componentView.rightButtonView { let absoluteFrame = rightButtonView.convert(rightButtonView.bounds, to: self.view) - //TODO:localize - let text: String = "Send a message or\nstart a group here." + let text: String = self.presentationData.strings.ChatList_EmptyListTooltip let tooltipController = TooltipController(content: .text(text), baseFontSize: self.presentationData.listsFontSize.baseDisplaySize, timeout: 30.0, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true, padding: 6.0, innerPadding: UIEdgeInsets(top: 2.0, left: 3.0, bottom: 2.0, right: 3.0)) self.present(tooltipController, in: .current, with: TooltipControllerPresentationArguments(sourceNodeAndRect: { [weak self] in diff --git a/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift b/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift index 78f28d1400..e7068be412 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift @@ -251,10 +251,9 @@ class ChatListSectionHeaderNode: ListViewItemNode { strongSelf.addSubnode(headerNode) } - //TODO:localize - headerNode.title = "YOUR CONTACTS ON TELEGRAM" + headerNode.title = item.strings.ChatList_EmptyListContactsHeader if item.hide != nil { - headerNode.action = "hide" + headerNode.action = item.strings.ChatList_EmptyListContactsHeaderHide headerNode.actionType = .generic headerNode.activateAction = { guard let self else { From 32245285db0fe0b4031e836b124a2b4ad9586b65 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Wed, 12 Apr 2023 23:29:33 +0400 Subject: [PATCH 7/7] Fix ad to same chat navigation --- .../TelegramUI/Sources/ChatController.swift | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index cd6de0c56d..45f00f07b9 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -4298,22 +4298,28 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch adAttribute.target { case let .peer(id, messageId, startParam): - let navigationData: ChatControllerInteractionNavigateToPeer - if let bot = message.author as? TelegramUser, bot.botInfo != nil, let startParam = startParam { - navigationData = .withBotStartPayload(ChatControllerInitialBotStart(payload: startParam, behavior: .interactive)) + if case let .peer(currentPeerId) = self.chatLocation, currentPeerId == id { + if let messageId { + self.navigateToMessage(from: nil, to: .id(messageId, nil), rememberInStack: false) + } } else { - var subject: ChatControllerSubject? - if let messageId = messageId { - subject = .message(id: .id(messageId), highlight: true, timecode: nil) + let navigationData: ChatControllerInteractionNavigateToPeer + if let bot = message.author as? TelegramUser, bot.botInfo != nil, let startParam = startParam { + navigationData = .withBotStartPayload(ChatControllerInitialBotStart(payload: startParam, behavior: .interactive)) + } else { + var subject: ChatControllerSubject? + if let messageId = messageId { + subject = .message(id: .id(messageId), highlight: true, timecode: nil) + } + navigationData = .chat(textInputState: nil, subject: subject, peekData: nil) } - navigationData = .chat(textInputState: nil, subject: subject, peekData: nil) + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + if let self, let peer = peer { + self.openPeer(peer: peer, navigation: navigationData, fromMessage: nil) + } + }) } - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id)) - |> deliverOnMainQueue).start(next: { [weak self] peer in - if let self, let peer = peer { - self.openPeer(peer: peer, navigation: navigationData, fromMessage: nil) - } - }) case let .join(_, joinHash): self.controllerInteraction?.openJoinLink(joinHash) }