From 8fece7c49aad53deeea793f829523003b6796ed4 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 9 Apr 2025 15:01:57 +0400 Subject: [PATCH 1/5] Various fixes --- .../Sources/QuickShareToastScreen.swift | 4 ++-- .../Sources/StoryComposeLayer.swift | 2 -- .../TelegramUI/Sources/ChatController.swift | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/submodules/TelegramUI/Components/Chat/QuickShareScreen/Sources/QuickShareToastScreen.swift b/submodules/TelegramUI/Components/Chat/QuickShareScreen/Sources/QuickShareToastScreen.swift index 8864d62ac6..e1c71a1a22 100644 --- a/submodules/TelegramUI/Components/Chat/QuickShareScreen/Sources/QuickShareToastScreen.swift +++ b/submodules/TelegramUI/Components/Chat/QuickShareScreen/Sources/QuickShareToastScreen.swift @@ -237,7 +237,7 @@ private final class QuickShareToastScreenComponent: Component { transition: .immediate, component: AnyComponent(PlainButtonComponent( content: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: "Undo", font: Font.regular(17.0), textColor: environment.theme.list.itemAccentColor.withMultiplied(hue: 0.933, saturation: 0.61, brightness: 1.0))) + text: .plain(NSAttributedString(string: component.peer.id != component.context.account.peerId ? environment.strings.Undo_Undo : "", font: Font.regular(17.0), textColor: environment.theme.list.itemAccentColor.withMultiplied(hue: 0.933, saturation: 0.61, brightness: 1.0))) )), effectAlignment: .center, contentInsets: UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0), @@ -267,7 +267,7 @@ private final class QuickShareToastScreenComponent: Component { linkAttribute: { _ in return nil }) ))), environment: {}, - containerSize: CGSize(width: availableContentSize.width - contentInsets.left - contentInsets.right - spacing - iconSize.width - 16.0, height: availableContentSize.height) + containerSize: CGSize(width: availableContentSize.width - contentInsets.left - contentInsets.right - spacing - iconSize.width - actionButtonSize.width - 16.0 - 4.0, height: availableContentSize.height) ) var contentHeight: CGFloat = 0.0 diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryComposeLayer.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryComposeLayer.swift index 5ff4735024..9a533f8c6e 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryComposeLayer.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryComposeLayer.swift @@ -28,8 +28,6 @@ func metalLibrary(device: MTLDevice) -> MTLLibrary? { return library } - - final class StoryBlobLayer: MetalEngineSubjectLayer, MetalEngineSubject { var internalData: MetalEngineSubjectInternalData? diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f3d3d2ec17..f09798f46f 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -5754,6 +5754,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else { displayedPeerVerification = .single(false) } + + let globalPrivacySettings = context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.GlobalPrivacy()) self.peerDisposable.set(combineLatest( queue: Queue.mainQueue(), @@ -5769,8 +5771,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G isPremiumRequiredForMessaging, managingBot, adMessage, - displayedPeerVerification - ).startStrict(next: { [weak self] peerView, globalNotificationSettings, onlineMemberCount, hasScheduledMessages, peerReportNotice, pinnedCount, threadInfo, hasSearchTags, hasSavedChats, isPremiumRequiredForMessaging, managingBot, adMessage, displayedPeerVerification in + displayedPeerVerification, + globalPrivacySettings + ).startStrict(next: { [weak self] peerView, globalNotificationSettings, onlineMemberCount, hasScheduledMessages, peerReportNotice, pinnedCount, threadInfo, hasSearchTags, hasSavedChats, isPremiumRequiredForMessaging, managingBot, adMessage, displayedPeerVerification, globalPrivacySettings in if let strongSelf = self { if strongSelf.peerView === peerView && strongSelf.reportIrrelvantGeoNotice == peerReportNotice && strongSelf.hasScheduledMessages == hasScheduledMessages && strongSelf.threadInfo == threadInfo && strongSelf.presentationInterfaceState.hasSearchTags == hasSearchTags && strongSelf.presentationInterfaceState.hasSavedChats == hasSavedChats && strongSelf.presentationInterfaceState.isPremiumRequiredForMessaging == isPremiumRequiredForMessaging && managingBot == strongSelf.presentationInterfaceState.contactStatus?.managingBot && adMessage?.id == strongSelf.presentationInterfaceState.adMessage?.id { return @@ -5869,6 +5872,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var contactStatus: ChatContactStatus? var businessIntro: TelegramBusinessIntro? var sendPaidMessageStars: StarsAmount? + var alwaysShowGiftButton = false + var disallowedGifts: TelegramDisallowedGifts? if let peer = peerView.peers[peerView.peerId] { if let cachedData = peerView.cachedData as? CachedUserData { contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: nil, managingBot: managingBot) @@ -5878,6 +5883,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if case let .peer(peerId) = chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { } else { sendPaidMessageStars = cachedData.sendPaidMessageStars + if cachedData.disallowedGifts != .All { + alwaysShowGiftButton = globalPrivacySettings.displayGiftButton || cachedData.flags.contains(.displayGiftButton) + } + disallowedGifts = cachedData.disallowedGifts } } else if let cachedData = peerView.cachedData as? CachedGroupData { var invitedBy: Peer? @@ -6113,6 +6122,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G .updatedHasSearchTags(hasSearchTags) .updatedIsPremiumRequiredForMessaging(isPremiumRequiredForMessaging) .updatedSendPaidMessageStars(sendPaidMessageStars) + .updatedAlwaysShowGiftButton(alwaysShowGiftButton) + .updatedDisallowedGifts(disallowedGifts) .updatedHasSavedChats(hasSavedChats) .updatedAppliedBoosts(appliedBoosts) .updatedBoostsToUnrestrict(boostsToUnrestrict) From cd30285d8717477d391cac9b504ee9965bed9879 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 9 Apr 2025 17:16:30 +0400 Subject: [PATCH 2/5] Conference updates --- .../Sources/CallListController.swift | 15 +++-- .../Sources/CallListControllerNode.swift | 44 +++++-------- .../Sources/CallListNodeEntries.swift | 28 ++++----- .../Sources/PresentationGroupCall.swift | 63 ++++++++++++++++--- .../TelegramEngine/Calls/GroupCalls.swift | 29 ++++++--- .../Calls/TelegramEngineCalls.swift | 2 +- 6 files changed, 117 insertions(+), 64 deletions(-) diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 1acf78752c..89191fcbf6 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -108,7 +108,8 @@ public final class CallListController: TelegramBaseController { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style if case .tab = self.mode { - self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)) + //self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)) + self.navigationItem.rightBarButtonItem = nil let icon: UIImage? if useSpecialTabBarIcons() { @@ -191,7 +192,7 @@ public final class CallListController: TelegramBaseController { } } - self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)) + //self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)) case .navigation: if self.editingMode { self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed)) @@ -383,7 +384,8 @@ public final class CallListController: TelegramBaseController { }) } else { strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed)), animated: true) - strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(strongSelf.presentationData.theme), style: .plain, target: self, action: #selector(strongSelf.callPressed)), animated: true) + //strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(strongSelf.presentationData.theme), style: .plain, target: self, action: #selector(strongSelf.callPressed)), animated: true) + strongSelf.navigationItem.setRightBarButton(nil, animated: true) } case .navigation: if strongSelf.editingMode { @@ -399,9 +401,9 @@ public final class CallListController: TelegramBaseController { } } } - }, createGroupCall: { [weak self] in + }, openNewCall: { [weak self] in if let strongSelf = self { - strongSelf.createGroupCall(peerIds: [], isVideo: false) + strongSelf.callPressed() } }) @@ -652,7 +654,8 @@ public final class CallListController: TelegramBaseController { switch self.mode { case .tab: self.navigationItem.setLeftBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)), animated: true) - self.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)), animated: true) + self.navigationItem.setRightBarButton(nil, animated: true) + //self.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)), animated: true) case .navigation: self.navigationItem.setLeftBarButton(nil, animated: true) self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)), animated: true) diff --git a/submodules/CallListUI/Sources/CallListControllerNode.swift b/submodules/CallListUI/Sources/CallListControllerNode.swift index 60be775f9b..05fe89d854 100644 --- a/submodules/CallListUI/Sources/CallListControllerNode.swift +++ b/submodules/CallListUI/Sources/CallListControllerNode.swift @@ -67,16 +67,16 @@ final class CallListNodeInteraction { let delete: ([EngineMessage.Id]) -> Void let updateShowCallsTab: (Bool) -> Void let openGroupCall: (EnginePeer.Id) -> Void - let createGroupCall: () -> Void + let openNewCall: () -> Void - init(setMessageIdWithRevealedOptions: @escaping (EngineMessage.Id?, EngineMessage.Id?) -> Void, call: @escaping (EngineMessage) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, delete: @escaping ([EngineMessage.Id]) -> Void, updateShowCallsTab: @escaping (Bool) -> Void, openGroupCall: @escaping (EnginePeer.Id) -> Void, createGroupCall: @escaping () -> Void) { + init(setMessageIdWithRevealedOptions: @escaping (EngineMessage.Id?, EngineMessage.Id?) -> Void, call: @escaping (EngineMessage) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, delete: @escaping ([EngineMessage.Id]) -> Void, updateShowCallsTab: @escaping (Bool) -> Void, openGroupCall: @escaping (EnginePeer.Id) -> Void, openNewCall: @escaping () -> Void) { self.setMessageIdWithRevealedOptions = setMessageIdWithRevealedOptions self.call = call self.openInfo = openInfo self.delete = delete self.updateShowCallsTab = updateShowCallsTab self.openGroupCall = openGroupCall - self.createGroupCall = createGroupCall + self.openNewCall = openNewCall } } @@ -125,10 +125,10 @@ private func mappedInsertEntries(context: AccountContext, presentationData: Item }), directionHint: entry.directionHint) case let .displayTabInfo(_, text): return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: 0), directionHint: entry.directionHint) - case .createGroupCall: + case .openNewCall: //TODO:localize - let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: PresentationResourcesItemList.linkIcon(presentationData.theme), title: "New Call Link", hasSeparator: false, sectionId: 1, noInsets: true, editing: false, action: { - nodeInteraction.createGroupCall() + let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: .none, title: "New Call", hasSeparator: false, sectionId: 1, height: .generic, noInsets: true, editing: false, action: { + nodeInteraction.openNewCall() }) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .groupCall(peer, _, isActive): @@ -150,10 +150,10 @@ private func mappedUpdateEntries(context: AccountContext, presentationData: Item }), directionHint: entry.directionHint) case let .displayTabInfo(_, text): return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: 0), directionHint: entry.directionHint) - case .createGroupCall: + case .openNewCall: //TODO:localize - let item = ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.linkIcon(presentationData.theme), title: "New Call Link", sectionId: 1, noInsets: true, editing: false, action: { - nodeInteraction.createGroupCall() + let item = ItemListPeerActionItem(presentationData: presentationData, icon: .none, title: "New Call", sectionId: 1, height: .generic, noInsets: true, editing: false, action: { + nodeInteraction.openNewCall() }) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .groupCall(peer, _, isActive): @@ -224,7 +224,7 @@ final class CallListControllerNode: ASDisplayNode { private let call: (EngineMessage) -> Void private let joinGroupCall: (EnginePeer.Id, EngineGroupCallDescription) -> Void - private let createGroupCall: () -> Void + private let openNewCall: () -> Void private let openInfo: (EnginePeer.Id, [EngineMessage]) -> Void private let emptyStateUpdated: (Bool) -> Void private let emptyStatePromise = Promise() @@ -234,7 +234,7 @@ final class CallListControllerNode: ASDisplayNode { private var previousContentOffset: ListViewVisibleContentOffset? - init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EngineMessage) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void, createGroupCall: @escaping () -> Void) { + init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EngineMessage) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void, openNewCall: @escaping () -> Void) { self.controller = controller self.context = context self.mode = mode @@ -243,7 +243,7 @@ final class CallListControllerNode: ASDisplayNode { self.joinGroupCall = joinGroupCall self.openInfo = openInfo self.emptyStateUpdated = emptyStateUpdated - self.createGroupCall = createGroupCall + self.openNewCall = openNewCall self.currentState = CallListNodeState(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: true, editing: false, messageIdWithRevealedOptions: nil) self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true) @@ -447,11 +447,11 @@ final class CallListControllerNode: ASDisplayNode { strongSelf.joinGroupCall(peerId, activeCall) } })) - }, createGroupCall: { [weak self] in + }, openNewCall: { [weak self] in guard let strongSelf = self else { return } - strongSelf.createGroupCall() + strongSelf.openNewCall() }) let viewProcessingQueue = self.viewProcessingQueue @@ -516,28 +516,18 @@ final class CallListControllerNode: ASDisplayNode { }) } |> distinctUntilChanged - - let canCreateGroupCall = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.App()) - |> map { configuration -> Bool in - var isConferencePossible = true - if let data = configuration.data, let value = data["ios_enable_conference"] as? Double { - isConferencePossible = value != 0.0 - } - return isConferencePossible - } let callListNodeViewTransition = combineLatest( callListViewUpdate, self.statePromise.get(), groupCalls, showCallsTab, - currentGroupCallPeerId, - canCreateGroupCall + currentGroupCallPeerId ) - |> mapToQueue { (updateAndType, state, groupCalls, showCallsTab, currentGroupCallPeerId, canCreateGroupCall) -> Signal in + |> mapToQueue { (updateAndType, state, groupCalls, showCallsTab, currentGroupCallPeerId) -> Signal in let (update, type) = updateAndType - let processedView = CallListNodeView(originalView: update.view, filteredEntries: callListNodeEntriesForView(view: update.view, canCreateGroupCall: canCreateGroupCall && mode == .tab, groupCalls: groupCalls, state: state, showSettings: showSettings, showCallsTab: showCallsTab, isRecentCalls: type == .all, currentGroupCallPeerId: currentGroupCallPeerId), presentationData: state.presentationData) + let processedView = CallListNodeView(originalView: update.view, filteredEntries: callListNodeEntriesForView(view: update.view, displayOpenNewCall: mode == .tab, groupCalls: groupCalls, state: state, showSettings: showSettings, showCallsTab: showCallsTab, isRecentCalls: type == .all, currentGroupCallPeerId: currentGroupCallPeerId), presentationData: state.presentationData) let previous = previousView.swap(processedView) let previousType = previousType.swap(type) diff --git a/submodules/CallListUI/Sources/CallListNodeEntries.swift b/submodules/CallListUI/Sources/CallListNodeEntries.swift index 22356ea608..37d7733834 100644 --- a/submodules/CallListUI/Sources/CallListNodeEntries.swift +++ b/submodules/CallListUI/Sources/CallListNodeEntries.swift @@ -25,7 +25,7 @@ enum CallListNodeEntry: Comparable, Identifiable { enum SortIndex: Comparable { case displayTab case displayTabInfo - case createGroupCall + case openNewCall case groupCall(EnginePeer.Id, String) case message(EngineMessage.Index) case hole(EngineMessage.Index) @@ -41,7 +41,7 @@ enum CallListNodeEntry: Comparable, Identifiable { default: return false } - case .createGroupCall: + case .openNewCall: switch rhs { case .displayTab, .displayTabInfo: return false @@ -50,7 +50,7 @@ enum CallListNodeEntry: Comparable, Identifiable { } case let .groupCall(lhsPeerId, lhsTitle): switch rhs { - case .displayTab, .displayTabInfo, .createGroupCall: + case .displayTab, .displayTabInfo, .openNewCall: return false case let .groupCall(rhsPeerId, rhsTitle): if lhsTitle == rhsTitle { @@ -63,7 +63,7 @@ enum CallListNodeEntry: Comparable, Identifiable { } case let .hole(lhsIndex): switch rhs { - case .displayTab, .displayTabInfo, .groupCall, .createGroupCall: + case .displayTab, .displayTabInfo, .groupCall, .openNewCall: return false case let .hole(rhsIndex): return lhsIndex < rhsIndex @@ -72,7 +72,7 @@ enum CallListNodeEntry: Comparable, Identifiable { } case let .message(lhsIndex): switch rhs { - case .displayTab, .displayTabInfo, .groupCall, .createGroupCall: + case .displayTab, .displayTabInfo, .groupCall, .openNewCall: return false case let .hole(rhsIndex): return lhsIndex < rhsIndex @@ -86,7 +86,7 @@ enum CallListNodeEntry: Comparable, Identifiable { case displayTab(PresentationTheme, String, Bool) case displayTabInfo(PresentationTheme, String) - case createGroupCall + case openNewCall case groupCall(peer: EnginePeer, editing: Bool, isActive: Bool) case messageEntry(topMessage: EngineMessage, messages: [EngineMessage], theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, editing: Bool, hasActiveRevealControls: Bool, displayHeader: Bool, missed: Bool) case holeEntry(index: EngineMessage.Index, theme: PresentationTheme) @@ -97,8 +97,8 @@ enum CallListNodeEntry: Comparable, Identifiable { return .displayTab case .displayTabInfo: return .displayTabInfo - case .createGroupCall: - return .createGroupCall + case .openNewCall: + return .openNewCall case let .groupCall(peer, _, _): return .groupCall(peer.id, peer.compactDisplayTitle) case let .messageEntry(message, _, _, _, _, _, _, _, _): @@ -114,7 +114,7 @@ enum CallListNodeEntry: Comparable, Identifiable { return .setting(0) case .displayTabInfo: return .setting(1) - case .createGroupCall: + case .openNewCall: return .setting(2) case let .groupCall(peer, _, _): return .groupCall(peer.id) @@ -143,8 +143,8 @@ enum CallListNodeEntry: Comparable, Identifiable { } else { return false } - case .createGroupCall: - if case .createGroupCall = rhs { + case .openNewCall: + if case .openNewCall = rhs { return true } else { return false @@ -212,7 +212,7 @@ enum CallListNodeEntry: Comparable, Identifiable { } } -func callListNodeEntriesForView(view: EngineCallList, canCreateGroupCall: Bool, groupCalls: [EnginePeer], state: CallListNodeState, showSettings: Bool, showCallsTab: Bool, isRecentCalls: Bool, currentGroupCallPeerId: EnginePeer.Id?) -> [CallListNodeEntry] { +func callListNodeEntriesForView(view: EngineCallList, displayOpenNewCall: Bool, groupCalls: [EnginePeer], state: CallListNodeState, showSettings: Bool, showCallsTab: Bool, isRecentCalls: Bool, currentGroupCallPeerId: EnginePeer.Id?) -> [CallListNodeEntry] { var result: [CallListNodeEntry] = [] for entry in view.items { switch entry { @@ -237,8 +237,8 @@ func callListNodeEntriesForView(view: EngineCallList, canCreateGroupCall: Bool, } } - if canCreateGroupCall { - result.append(.createGroupCall) + if displayOpenNewCall { + result.append(.openNewCall) } if showSettings { diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 4caf389cbc..08fb8bee08 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -210,6 +210,11 @@ private final class PendingConferenceInvitationContext { case ringing } + enum InvitationError { + case generic + case privacy(peer: EnginePeer?) + } + private let engine: TelegramEngine private var requestDisposable: Disposable? private var stateDisposable: Disposable? @@ -218,19 +223,12 @@ private final class PendingConferenceInvitationContext { private var hadMessage: Bool = false private var didNotifyEnded: Bool = false - init(engine: TelegramEngine, reference: InternalGroupCallReference, peerId: PeerId, isVideo: Bool, onStateUpdated: @escaping (State) -> Void, onEnded: @escaping (Bool) -> Void) { + init(engine: TelegramEngine, reference: InternalGroupCallReference, peerId: PeerId, isVideo: Bool, onStateUpdated: @escaping (State) -> Void, onEnded: @escaping (Bool) -> Void, onError: @escaping (InvitationError) -> Void) { self.engine = engine - self.requestDisposable = (engine.calls.inviteConferenceCallParticipant(reference: reference, peerId: peerId, isVideo: isVideo).startStrict(next: { [weak self] messageId in + self.requestDisposable = ((engine.calls.inviteConferenceCallParticipant(reference: reference, peerId: peerId, isVideo: isVideo) |> deliverOnMainQueue).startStrict(next: { [weak self] messageId in guard let self else { return } - guard let messageId else { - if !self.didNotifyEnded { - self.didNotifyEnded = true - onEnded(false) - } - return - } self.messageId = messageId onStateUpdated(.ringing) @@ -296,6 +294,24 @@ private final class PendingConferenceInvitationContext { } } }) + }, error: { [weak self] error in + guard let self else { + return + } + + if !self.didNotifyEnded { + self.didNotifyEnded = true + onEnded(false) + } + + let mappedError: InvitationError + switch error { + case .privacy(let peer): + mappedError = .privacy(peer: peer) + default: + mappedError = .generic + } + onError(mappedError) })) } @@ -792,6 +808,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { private let e2eContext: ConferenceCallE2EContext? + private var lastErrorAlertTimestamp: Double = 0.0 + init( accountContext: AccountContext, audioSession: ManagedAudioSession, @@ -3536,6 +3554,33 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { onEnded: { success in didEndAlready = true onEnded?(success) + }, + onError: { [weak self] error in + guard let self else { + return + } + + let timestamp = CACurrentMediaTime() + if self.lastErrorAlertTimestamp > timestamp - 1.0 { + return + } + self.lastErrorAlertTimestamp = timestamp + + let presentationData = self.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme) + + //TODO:localize + var errorText = "An error occurred" + switch error { + case let .privacy(peer): + if let peer { + errorText = presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).string + } + default: + break + } + self.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {}) + ]), on: .root, blockInteraction: false, completion: {}) } ) if !didEndAlready { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift index 3310d935b2..46b0773315 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift @@ -882,13 +882,19 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, } } -func _internal_inviteConferenceCallParticipant(account: Account, reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal { +public enum InviteConferenceCallParticipantError { + case generic + case privacy(peer: EnginePeer?) +} + +func _internal_inviteConferenceCallParticipant(account: Account, reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal { return account.postbox.transaction { transaction -> Api.InputUser? in return transaction.getPeer(peerId).flatMap(apiInputUser) } - |> mapToSignal { inputPeer -> Signal in + |> castError(InviteConferenceCallParticipantError.self) + |> mapToSignal { inputPeer -> Signal in guard let inputPeer else { - return .complete() + return .fail(.generic) } var flags: Int32 = 0 @@ -897,17 +903,26 @@ func _internal_inviteConferenceCallParticipant(account: Account, reference: Inte } return account.network.request(Api.functions.phone.inviteConferenceCallParticipant(flags: flags, call: reference.apiInputGroupCall, userId: inputPeer)) |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) + |> `catch` { error -> Signal in + if error.errorDescription == "USER_PRIVACY_RESTRICTED" { + return account.postbox.transaction { transaction -> InviteConferenceCallParticipantError in + return .privacy(peer: transaction.getPeer(peerId).flatMap(EnginePeer.init)) + } + |> castError(InviteConferenceCallParticipantError.self) + |> mapToSignal { error -> Signal in + return .fail(error) + } + } + return .fail(.generic) } - |> mapToSignal { result -> Signal in + |> mapToSignal { result -> Signal in if let result { account.stateManager.addUpdates(result) if let message = result.messageIds.first { return .single(message) } } - return .single(nil) + return .fail(.generic) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift index bc107740dd..77fc5f7ffc 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift @@ -109,7 +109,7 @@ public extension TelegramEngine { return _internal_sendConferenceCallBroadcast(account: self.account, callId: callId, accessHash: accessHash, block: block) } - public func inviteConferenceCallParticipant(reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal { + public func inviteConferenceCallParticipant(reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal { return _internal_inviteConferenceCallParticipant(account: self.account, reference: reference, peerId: peerId, isVideo: isVideo) } From 700a71f3930cf1d926d77915656c3de6fd27cd48 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 9 Apr 2025 18:21:11 +0400 Subject: [PATCH 3/5] Update localization --- .../Telegram-iOS/en.lproj/Localizable.strings | 65 +++++++++++++++++++ .../CallListUI/Sources/CallListCallItem.swift | 6 +- .../Sources/CallListController.swift | 9 +-- .../Sources/CallListControllerNode.swift | 6 +- .../Sources/Node/ChatListItemStrings.swift | 22 ++++++- .../Sources/ContextActionsContainerNode.swift | 3 +- .../Sources/InviteLinkInviteController.swift | 8 +-- .../ItemListPermanentInviteLinkItem.swift | 3 +- .../Sources/JoinLinkPreviewController.swift | 4 +- .../QrCodeUI/Sources/QrCodeScreen.swift | 3 +- .../Sources/CallController.swift | 7 +- .../Sources/PresentationCall.swift | 4 +- .../Sources/PresentationGroupCall.swift | 3 +- .../VideoChatEncryptionKeyComponent.swift | 7 +- .../VideoChatParticipantsComponent.swift | 16 +---- .../Sources/VideoChatScreen.swift | 6 +- .../VideoChatScreenInviteMembers.swift | 3 - .../Sources/VoiceChatController.swift | 3 - .../State/ConferenceCallE2EContext.swift | 2 - .../Sources/ServiceMessageStrings.swift | 24 ++++++- .../ChatMessageCallBubbleContentNode.swift | 16 ++--- .../ChatMessageWebpageBubbleContentNode.swift | 3 +- .../Sources/JoinSubjectScreen.swift | 23 +++---- .../TelegramUI/Sources/AppDelegate.swift | 18 +++-- .../TelegramUI/Sources/ChatController.swift | 3 +- .../ContactMultiselectionControllerNode.swift | 10 ++- .../TelegramUI/Sources/OpenResolvedUrl.swift | 3 +- .../Sources/SharedAccountContext.swift | 14 ++-- 28 files changed, 173 insertions(+), 121 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 2857455b4d..f6e9611518 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -14115,3 +14115,68 @@ Sorry for the inconvenience."; "WebApp.ImportData.AccountHeader" = "ACCOUNT TO IMPORT DATA FROM"; "WebApp.ImportData.CreatedOn" = "created on %@"; "WebApp.ImportData.Import" = "Import"; + +"CallList.ToastCallLinkCopied.Text" = "Call link copied"; +"CallList.ToastCallLinkCopied.Action" = "View Call"; +"CallList.NewCall" = "New Call"; +"CallList.NewCallLink" = "New Call Link"; + +"Chat.SendStarsToBecomeTopInfo" = "Send %@ or more to highlight your profile"; + +"VideoChat.RevokeLink" = "Revoke Link"; + +"InviteLink.GroupCallLinkHelp" = "Anyone on Telegram can join your call by following the link below."; +"InviteLink.CallLinkTitle" = "Call Link"; +"InviteLink.CreatedGroupCallFooter" = "Be the first to join the call and add people from there. [Open Call >](open_call)"; +"InviteLink.QRCode.InfoGroupCall" = "Everyone on Telegram can scan this code to join your group call."; + +"Call.GenericGroupCallTitle" = "Group Call"; + +"VideoChat.EncryptionKeyLabel" = "End-to-end encrypted"; +"VideoChat.EncryptionKeyText" = "These four emojis represent the call's encryption key. They must match for all participants and change when someone joins or leaves."; +"VideoChat.EncryptionKeyDone" = "Close"; +"VideoChat.InviteMember" = "Add Member"; + +"VideoChat.GroupCallTitle" = "Group Call"; + +"Chat.ViewGroupCall" = "JOIN GROUP CALL"; + +"NewCall.SearchPlaceholder" = "Search for contacts or usernames"; +"NewCall.VideoOption" = "Call with video enabled"; +"NewCall.ActionCallSingle" = "Call %@"; +"NewCall.ActionCallMultiple" = "Call"; + +"Chat.ToastCallLinkExpired.Text" = "This link is no longer active"; + +"Chat.CallMessage.GroupCallParticipantCount_1" = "1 person"; +"Chat.CallMessage.GroupCallParticipantCount_any" = "%d people"; + +"Chat.CallMessage.DeclinedGroupCall" = "Declined Group Call"; +"Chat.CallMessage.MissedGroupCall" = "Missed Group Call"; +"Chat.CallMessage.CancelledGroupCall" = "Cancelled Group Call"; +"Chat.CallMessage.IncomingGroupCall" = "Incoming Group Call"; +"Chat.CallMessage.OutgoingGroupCall" = "Outgoing Group Call"; + +"Invitation.GroupCall" = "Group Call"; +"Invitation.JoinGroupCall" = "Join Group Call"; +"Invitation.PublicGroup" = "public group"; +"Invitation.PrivateGroup" = "private group"; + +"Invitation.GroupCall.Text" = "You are invited to join a group call."; + +"Invitation.Group.AlreadyJoinedSingle" = "**%@** already joined this group."; +"Invitation.Group.AlreadyJoinedMultiple" = "%@ already joined this group."; +"Invitation.Group.AlreadyJoinedMultipleWithCount_1" = "{} and **%d** other person already joined this group."; +"Invitation.Group.AlreadyJoinedMultipleWithCount_any" = "{} and **%d** other people already joined this group."; + +"Invitation.GroupCall.AlreadyJoinedSingle" = "**%@** already joined this call."; +"Invitation.GroupCall.AlreadyJoinedMultiple" = "%@ already joined this call."; +"Invitation.GroupCall.AlreadyJoinedMultipleWithCount_1" = "{} and **%d** other person already joined this call."; +"Invitation.GroupCall.AlreadyJoinedMultipleWithCount_any" = "{} and **%d** other people already joined this call."; + +"Call.ShareLink" = "Share Call Link"; +"Call.AddMemberTitle" = "Add Member"; + +"Call.IncomingGroupCallTitle.Single" = "%@"; +"Call.IncomingGroupCallTitle.Multiple_1" = "{} and 1 other"; +"Call.IncomingGroupCallTitle.Multiple_any" = "{} and %d others"; \ No newline at end of file diff --git a/submodules/CallListUI/Sources/CallListCallItem.swift b/submodules/CallListUI/Sources/CallListCallItem.swift index a408a63cbc..17ae28f7d7 100644 --- a/submodules/CallListUI/Sources/CallListCallItem.swift +++ b/submodules/CallListUI/Sources/CallListCallItem.swift @@ -425,9 +425,8 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { isVideo = conferenceCall.flags.contains(.isVideo) if message.flags.contains(.Incoming) { hasIncoming = true - //TODO:localize let missedTimeout: Int32 - #if DEBUG + #if DEBUG && false missedTimeout = 5 #else missedTimeout = 30 @@ -463,8 +462,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { peersString.append(", ") } if peer.id == item.context.account.peerId { - //TODO:localize - peersString += "You" + peersString += item.presentationData.strings.DialogList_You } else { peersString += peer.compactDisplayTitle } diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 89191fcbf6..246fe5d891 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -297,9 +297,8 @@ public final class CallListController: TelegramBaseController { if let result { switch result { case .linkCopied: - //TODO:localize let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - self.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: "Call link copied.", customUndoText: "View Call", timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in + self.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: presentationData.strings.CallList_ToastCallLinkCopied_Text, customUndoText: presentationData.strings.CallList_ToastCallLinkCopied_Action, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in if case .undo = action { openCall() } @@ -517,8 +516,7 @@ public final class CallListController: TelegramBaseController { return } - //TODO:localize - let options = [ContactListAdditionalOption(title: "New Call Link", icon: .generic(PresentationResourcesItemList.linkIcon(presentationData.theme)!), action: { [weak self] in + let options = [ContactListAdditionalOption(title: self.presentationData.strings.CallList_NewCallLink, icon: .generic(PresentationResourcesItemList.linkIcon(presentationData.theme)!), action: { [weak self] in guard let self else { return } @@ -741,8 +739,7 @@ public final class CallListController: TelegramBaseController { self.context.sharedContext.openCreateGroupCallUI(context: self.context, peerIds: conferenceCall.otherParticipants, parentController: self) default: let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - //TODO:localize - self.present(textAlertController(context: self.context, title: nil, text: "An error occurred", actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.present(textAlertController(context: self.context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } }) } diff --git a/submodules/CallListUI/Sources/CallListControllerNode.swift b/submodules/CallListUI/Sources/CallListControllerNode.swift index 05fe89d854..74b1c7280a 100644 --- a/submodules/CallListUI/Sources/CallListControllerNode.swift +++ b/submodules/CallListUI/Sources/CallListControllerNode.swift @@ -126,8 +126,7 @@ private func mappedInsertEntries(context: AccountContext, presentationData: Item case let .displayTabInfo(_, text): return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: 0), directionHint: entry.directionHint) case .openNewCall: - //TODO:localize - let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: .none, title: "New Call", hasSeparator: false, sectionId: 1, height: .generic, noInsets: true, editing: false, action: { + let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: .none, title: presentationData.strings.CallList_NewCall, hasSeparator: false, sectionId: 1, height: .generic, noInsets: true, editing: false, action: { nodeInteraction.openNewCall() }) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) @@ -151,8 +150,7 @@ private func mappedUpdateEntries(context: AccountContext, presentationData: Item case let .displayTabInfo(_, text): return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: 0), directionHint: entry.directionHint) case .openNewCall: - //TODO:localize - let item = ItemListPeerActionItem(presentationData: presentationData, icon: .none, title: "New Call", sectionId: 1, height: .generic, noInsets: true, editing: false, action: { + let item = ItemListPeerActionItem(presentationData: presentationData, icon: .none, title: presentationData.strings.CallList_NewCall, sectionId: 1, height: .generic, noInsets: true, editing: false, action: { nodeInteraction.openNewCall() }) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) diff --git a/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift b/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift index d989d3e693..1e7012154e 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift @@ -298,9 +298,25 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: messageText = invoice.title case let action as TelegramMediaAction: switch action.action { - case .conferenceCall: - //TODO:localize - messageText = "Group call" + case let .conferenceCall(conferenceCall): + let incoming = message.flags.contains(.Incoming) + + let missedTimeout: Int32 = 30 + let currentTime = Int32(Date().timeIntervalSince1970) + + if conferenceCall.flags.contains(.isMissed) { + messageText = strings.Chat_CallMessage_DeclinedGroupCall + } else if message.timestamp < currentTime - missedTimeout { + messageText = strings.Chat_CallMessage_MissedGroupCall + } else if conferenceCall.duration != nil { + messageText = strings.Chat_CallMessage_CancelledGroupCall + } else { + if incoming { + messageText = strings.Chat_CallMessage_IncomingGroupCall + } else { + messageText = strings.Chat_CallMessage_OutgoingGroupCall + } + } case let .phoneCall(_, discardReason, _, isVideo): hideAuthor = !isPeerGroup let incoming = message.flags.contains(.Incoming) diff --git a/submodules/ContextUI/Sources/ContextActionsContainerNode.swift b/submodules/ContextUI/Sources/ContextActionsContainerNode.swift index 8641e4d77c..e4f72eda92 100644 --- a/submodules/ContextUI/Sources/ContextActionsContainerNode.swift +++ b/submodules/ContextUI/Sources/ContextActionsContainerNode.swift @@ -438,9 +438,8 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode { icon = nil isUserInteractionEnabled = action != nil case let .starsReactions(topCount): - //TODO:localize self.action = nil - self.text = "Send \(topCount) or more to highlight your profile" + self.text = self.presentationData.strings.Chat_SendStarsToBecomeTopInfo("\(topCount)").string self.targetSelectionIndex = nil icon = nil isUserInteractionEnabled = action != nil diff --git a/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift b/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift index cf901db89a..153bcc6d57 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift @@ -492,10 +492,9 @@ public final class InviteLinkInviteController: ViewController { let dismissAction: () -> Void = { [weak controller] in controller?.dismissAnimated() } - //TODO:localize controller.setItemGroups([ ActionSheetItemGroup(items: [ - ActionSheetTextItem(title: "Revoke Link"), + ActionSheetTextItem(title: presentationData.strings.VideoChat_RevokeLink), ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: { [weak self] in dismissAction() @@ -674,9 +673,8 @@ public final class InviteLinkInviteController: ViewController { } var entries: [InviteLinkInviteEntry] = [] - //TODO:localize - let helpText: String = "Anyone on Telegram can join your call by following the link below." - entries.append(.header(title: "Call Link", text: helpText)) + let helpText: String = presentationData.strings.InviteLink_GroupCallLinkHelp + entries.append(.header(title: presentationData.strings.InviteLink_CallLinkTitle, text: helpText)) let mainInvite: ExportedInvitation = .link(link: mainInvite?.link ?? "", title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: self.context.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil) diff --git a/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift b/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift index e64aa14847..de6d03c3a7 100644 --- a/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift @@ -369,8 +369,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem return (TelegramTextAttributes.URL, contents) } ) - //TODO:localize - let justCreatedCallTextAttributedString = parseMarkdownIntoAttributedString("Be the first to join the call and add people from there. [Open Call >](open_call)", attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString + let justCreatedCallTextAttributedString = parseMarkdownIntoAttributedString(item.presentationData.strings.InviteLink_CreatedGroupCallFooter, attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString if let range = justCreatedCallTextAttributedString.string.range(of: ">"), let chevronImage { justCreatedCallTextAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: justCreatedCallTextAttributedString.string)) } diff --git a/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewController.swift b/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewController.swift index 91c373bc47..5b23c18ed4 100644 --- a/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewController.swift +++ b/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewController.swift @@ -193,9 +193,7 @@ public func JoinLinkPreviewController( ) -> ViewController { if let data = context.currentAppConfiguration.with({ $0 }).data, data["ios_killswitch_legacy_join_link"] != nil { return LegacyJoinLinkPreviewController(context: context, link: link, navigateToPeer: navigateToPeer, parentNavigationController: parentNavigationController, resolvedState: resolvedState) - } else if case let .invite(invite) = resolvedState, !invite.flags.requestNeeded, !invite.flags.isBroadcast, !invite.flags.canRefulfillSubscription { - //TODO:release - + } else if case let .invite(invite) = resolvedState, !invite.flags.requestNeeded, !invite.flags.isBroadcast, !invite.flags.canRefulfillSubscription { var verificationStatus: JoinSubjectScreenMode.Group.VerificationStatus? if invite.flags.isFake { verificationStatus = .fake diff --git a/submodules/QrCodeUI/Sources/QrCodeScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScreen.swift index 69e11b3c2e..c5630feda9 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScreen.swift @@ -253,8 +253,7 @@ public final class QrCodeScreen: ViewController { case .channel: text = self.presentationData.strings.InviteLink_QRCode_InfoChannel case .groupCall: - //TODO:localize - text = "Everyone on Telegram can scan this code to join your group call." + text = self.presentationData.strings.InviteLink_QRCode_InfoGroupCall } case .chatFolder: title = self.presentationData.strings.InviteLink_QRCodeFolder_Title diff --git a/submodules/TelegramCallsUI/Sources/CallController.swift b/submodules/TelegramCallsUI/Sources/CallController.swift index c667d45901..37fa2064c5 100644 --- a/submodules/TelegramCallsUI/Sources/CallController.swift +++ b/submodules/TelegramCallsUI/Sources/CallController.swift @@ -498,14 +498,12 @@ public final class CallController: ViewController { } static func openConferenceAddParticipant(context: AccountContext, disablePeerIds: [EnginePeer.Id], shareLink: (() -> Void)?, completion: @escaping ([(id: EnginePeer.Id, isVideo: Bool)]) -> Void) -> ViewController { - //TODO:localize let presentationData = context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme) var options: [ContactListAdditionalOption] = [] var openShareLinkImpl: (() -> Void)? if shareLink != nil { - //TODO:localize - options.append(ContactListAdditionalOption(title: "Share Call Link", icon: .generic(UIImage(bundleImageName: "Contact List/LinkActionIcon")!), action: { + options.append(ContactListAdditionalOption(title: presentationData.strings.Call_ShareLink, icon: .generic(UIImage(bundleImageName: "Contact List/LinkActionIcon")!), action: { openShareLinkImpl?() }, clearHighlightAutomatically: false)) } @@ -515,8 +513,7 @@ public final class CallController: ViewController { updatedPresentationData: (initial: presentationData, signal: .single(presentationData)), mode: .generic, title: { strings in - //TODO:localize - return "Add Member" + return strings.Call_AddMemberTitle }, options: .single(options), displayCallIcons: true, diff --git a/submodules/TelegramCallsUI/Sources/PresentationCall.swift b/submodules/TelegramCallsUI/Sources/PresentationCall.swift index 41555bd8e4..c6b182ac82 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCall.swift @@ -765,8 +765,8 @@ public final class PresentationCallImpl: PresentationCall { self.localVideoEndpointId = nil self.remoteVideoEndpointId = nil - //TODO:localize - self.callKitIntegration?.updateCallIsConference(uuid: self.internalId, title: self.conferenceTitle ?? "Group Call") + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + self.callKitIntegration?.updateCallIsConference(uuid: self.internalId, title: self.conferenceTitle ?? presentationData.strings.Call_GenericGroupCallTitle) } func internal_markAsCanBeRemoved() { diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 08fb8bee08..781dda7de0 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -3568,8 +3568,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { let presentationData = self.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme) - //TODO:localize - var errorText = "An error occurred" + var errorText = presentationData.strings.Login_UnknownError switch error { case let .privacy(peer): if let peer { diff --git a/submodules/TelegramCallsUI/Sources/VideoChatEncryptionKeyComponent.swift b/submodules/TelegramCallsUI/Sources/VideoChatEncryptionKeyComponent.swift index 58d84e2d1b..02cfbd82ca 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatEncryptionKeyComponent.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatEncryptionKeyComponent.swift @@ -614,11 +614,10 @@ final class VideoChatEncryptionKeyComponent: Component { ) } - //TODO:localize let collapsedTextSize = self.collapsedText.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: "End-to-end encrypted", font: Font.semibold(12.0), textColor: component.theme.list.itemPrimaryTextColor)) + text: .plain(NSAttributedString(string: component.strings.VideoChat_EncryptionKeyLabel, font: Font.semibold(12.0), textColor: component.theme.list.itemPrimaryTextColor)) )), environment: {}, containerSize: CGSize(width: 1000.0, height: 1000.0) @@ -627,7 +626,7 @@ final class VideoChatEncryptionKeyComponent: Component { let expandedTextSize = self.expandedText.update( transition: .immediate, component: AnyComponent(BalancedTextComponent( - text: .plain(NSAttributedString(string: "These four emojis represent the call's encryption key. They must match for all participants and change when someone joins or leaves.", font: Font.regular(12.0), textColor: component.theme.list.itemPrimaryTextColor)), + text: .plain(NSAttributedString(string: component.strings.VideoChat_EncryptionKeyText, font: Font.regular(12.0), textColor: component.theme.list.itemPrimaryTextColor)), maximumNumberOfLines: 0, lineSpacing: 0.3 )), @@ -638,7 +637,7 @@ final class VideoChatEncryptionKeyComponent: Component { let expandedButtonTextSize = self.expandedButtonText.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: "Close", font: Font.regular(17.0), textColor: component.theme.list.itemPrimaryTextColor)) + text: .plain(NSAttributedString(string: component.strings.VideoChat_EncryptionKeyDone, font: Font.regular(17.0), textColor: component.theme.list.itemPrimaryTextColor)) )), environment: {}, containerSize: CGSize(width: availableSize.width - expandedSideInset * 2.0, height: 1000.0) diff --git a/submodules/TelegramCallsUI/Sources/VideoChatParticipantsComponent.swift b/submodules/TelegramCallsUI/Sources/VideoChatParticipantsComponent.swift index de122e01a9..a354a1c543 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatParticipantsComponent.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatParticipantsComponent.swift @@ -1282,18 +1282,7 @@ final class VideoChatParticipantsComponent: Component { let invitedPeer = component.invitedPeers[i - self.listParticipants.count] participantPeerId = invitedPeer.peer.id - let subtitle: PeerListItemComponent.Subtitle - //TODO:localize - switch invitedPeer.state { - case .none: - subtitle = PeerListItemComponent.Subtitle(text: component.strings.VoiceChat_StatusInvited, color: .neutral) - case .connecting: - subtitle = PeerListItemComponent.Subtitle(text: "connecting...", color: .neutral) - case .requesting: - subtitle = PeerListItemComponent.Subtitle(text: "requesting...", color: .neutral) - case .ringing: - subtitle = PeerListItemComponent.Subtitle(text: "invited", color: .neutral) - } + let subtitle: PeerListItemComponent.Subtitle = PeerListItemComponent.Subtitle(text: component.strings.VoiceChat_StatusInvited, color: .neutral) let rightAccessoryComponent: AnyComponent = AnyComponent(VideoChatParticipantInvitedStatusComponent( theme: component.theme @@ -1861,11 +1850,10 @@ final class VideoChatParticipantsComponent: Component { let iconType: VideoChatListInviteComponent.Icon switch inviteOption.type { case let .invite(isMultiple): - //TODO:localize if isMultiple { inviteText = component.strings.VoiceChat_InviteMember } else { - inviteText = "Add Member" + inviteText = component.strings.VideoChat_InviteMember } iconType = .addUser case .shareLink: diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift index 787ec7d39c..cc82d746a5 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift @@ -685,9 +685,8 @@ final class VideoChatScreenComponent: Component { if let result { switch result { case .linkCopied: - //TODO:localize let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: "Call link copied.", customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in + self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: presentationData.strings.CallList_ToastCallLinkCopied_Text, customUndoText: presentationData.strings.CallList_ToastCallLinkCopied_Action, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in return false }), in: .current) case .openCall: @@ -2038,11 +2037,10 @@ final class VideoChatScreenComponent: Component { maxTitleWidth -= 110.0 } - //TODO:localize let titleSize = self.title.update( transition: transition, component: AnyComponent(VideoChatTitleComponent( - title: self.callState?.title ?? self.peer?.debugDisplayTitle ?? "Group Call", + title: self.callState?.title ?? self.peer?.debugDisplayTitle ?? environment.strings.VideoChat_GroupCallTitle, status: idleTitleStatusText, isRecording: self.callState?.recordingStartTimestamp != nil, strings: environment.strings, diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreenInviteMembers.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreenInviteMembers.swift index 1fd32412b3..4d97fef81e 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScreenInviteMembers.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScreenInviteMembers.swift @@ -129,7 +129,6 @@ extension VideoChatScreenComponent.View { if let participant { dismissController?() - //TODO:release if groupCall.invitePeer(participant.peer.id, isVideo: false) { let text: String if case let .channel(channel) = self.peer, case .broadcast = channel.info { @@ -242,7 +241,6 @@ extension VideoChatScreenComponent.View { } dismissController?() - //TODO:release if groupCall.invitePeer(peer.id, isVideo: false) { let text: String if case let .channel(channel) = self.peer, case .broadcast = channel.info { @@ -315,7 +313,6 @@ extension VideoChatScreenComponent.View { } dismissController?() - //TODO:release if groupCall.invitePeer(peer.id, isVideo: false) { let text: String if case let .channel(channel) = self.peer, case .broadcast = channel.info { diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 60cefcd87c..d3c7626b85 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -1256,7 +1256,6 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController { if let participant = participant { dismissController?() - //TODO:release if strongSelf.call.invitePeer(participant.peer.id, isVideo: false) { let text: String if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info { @@ -1365,7 +1364,6 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController { } dismissController?() - //TODO:release if strongSelf.call.invitePeer(peer.id, isVideo: false) { let text: String if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info { @@ -1434,7 +1432,6 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController { } dismissController?() - //TODO:release if strongSelf.call.invitePeer(peer.id, isVideo: false) { let text: String if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info { diff --git a/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift b/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift index 31d81d6c05..07471dfedb 100644 --- a/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift +++ b/submodules/TelegramCore/Sources/State/ConferenceCallE2EContext.swift @@ -182,7 +182,6 @@ public final class ConferenceCallE2EContext { self.e2eEncryptionKeyHashValue.set(outEmoji.isEmpty ? nil : outEmoji) for outBlock in outBlocks { - //TODO:release queue let _ = self.engine.calls.sendConferenceCallBroadcast(callId: self.callId, accessHash: self.accessHash, block: outBlock).startStandalone() } } @@ -400,7 +399,6 @@ public final class ConferenceCallE2EContext { } func kickPeer(id: EnginePeer.Id) { - //TODO:release if !self.pendingKickPeers.contains(id) { self.pendingKickPeers.append(id) diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index b013f04d16..328d44f08a 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -616,9 +616,27 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, } } attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor) - case .conferenceCall: - //TODO:localize - let titleString = "Group call" + case let .conferenceCall(conferenceCall): + var titleString: String + let incoming = message.flags.contains(.Incoming) + + let missedTimeout: Int32 = 30 + let currentTime = Int32(Date().timeIntervalSince1970) + + if conferenceCall.flags.contains(.isMissed) { + titleString = strings.Chat_CallMessage_DeclinedGroupCall + } else if message.timestamp < currentTime - missedTimeout { + titleString = strings.Chat_CallMessage_MissedGroupCall + } else if conferenceCall.duration != nil { + titleString = strings.Chat_CallMessage_CancelledGroupCall + } else { + if incoming { + titleString = strings.Chat_CallMessage_IncomingGroupCall + } else { + titleString = strings.Chat_CallMessage_OutgoingGroupCall + } + } + attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor) case let .groupPhoneCall(_, _, scheduleDate, duration): if let scheduleDate = scheduleDate { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/Sources/ChatMessageCallBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/Sources/ChatMessageCallBubbleContentNode.swift index 03136f8bde..2bebc550ab 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/Sources/ChatMessageCallBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/Sources/ChatMessageCallBubbleContentNode.swift @@ -153,8 +153,7 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { callDuration = conferenceCall.duration if conferenceCall.otherParticipants.count > 0 { - //TODO:localize - peopleTextString = "\(conferenceCall.otherParticipants.count + 1) people" + peopleTextString = item.presentationData.strings.Chat_CallMessage_GroupCallParticipantCount(Int32(conferenceCall.otherParticipants.count + 1)) if let peer = item.message.author { peopleAvatars.append(peer) } @@ -165,9 +164,8 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { } } - //TODO:localize let missedTimeout: Int32 - #if DEBUG + #if DEBUG && false missedTimeout = 5 #else missedTimeout = 30 @@ -175,16 +173,16 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { let currentTime = Int32(Date().timeIntervalSince1970) if conferenceCall.flags.contains(.isMissed) { - titleString = "Declined Group Call" + titleString = item.presentationData.strings.Chat_CallMessage_DeclinedGroupCall } else if item.message.timestamp < currentTime - missedTimeout { - titleString = "Missed Group Call" + titleString = item.presentationData.strings.Chat_CallMessage_MissedGroupCall } else if conferenceCall.duration != nil { - titleString = "Cancelled Group Call" + titleString = item.presentationData.strings.Chat_CallMessage_CancelledGroupCall } else { if incoming { - titleString = "Incoming Group Call" + titleString = item.presentationData.strings.Chat_CallMessage_IncomingGroupCall } else { - titleString = "Outgoing Group Call" + titleString = item.presentationData.strings.Chat_CallMessage_OutgoingGroupCall } updateConferenceTimerEndTimeout = (item.message.timestamp + missedTimeout) - currentTime } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift index bc905b9149..70025dff94 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -475,8 +475,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent text = nil entities = nil case "telegram_call": - //TODO:localize - actionTitle = "JOIN GROUP CALL" + actionTitle = item.presentationData.strings.Chat_ViewGroupCall default: break } diff --git a/submodules/TelegramUI/Components/JoinSubjectScreen/Sources/JoinSubjectScreen.swift b/submodules/TelegramUI/Components/JoinSubjectScreen/Sources/JoinSubjectScreen.swift index 5f03c035fe..042be113c0 100644 --- a/submodules/TelegramUI/Components/JoinSubjectScreen/Sources/JoinSubjectScreen.swift +++ b/submodules/TelegramUI/Components/JoinSubjectScreen/Sources/JoinSubjectScreen.swift @@ -490,7 +490,7 @@ private final class JoinSubjectScreenComponent: Component { contentHeight += 31.0 titleString = group.title - subtitleString = group.isPublic ? "public group" : "private group" + subtitleString = group.isPublic ? environment.strings.Invitation_PublicGroup : environment.strings.Invitation_PrivateGroup descriptionTextString = group.about previewPeers = group.members @@ -528,10 +528,9 @@ private final class JoinSubjectScreenComponent: Component { } contentHeight += peerAvatarSize.height + 21.0 case let .groupCall(groupCall): - //TODO:localize - titleString = "Group Call" + titleString = environment.strings.Invitation_GroupCall subtitleString = nil - descriptionTextString = "You are invited to join a group call." + descriptionTextString = environment.strings.Invitation_GroupCall_Text previewPeers = groupCall.members totalMemberCount = groupCall.totalMemberCount @@ -691,12 +690,11 @@ private final class JoinSubjectScreenComponent: Component { if !previewPeers.isEmpty { contentHeight += 11.0 - //TODO:localize let previewPeersString: String switch component.mode { case .group: if previewPeers.count == 1 { - previewPeersString = "**\(previewPeers[0].compactDisplayTitle)** already joined this group." + previewPeersString = environment.strings.Invitation_Group_AlreadyJoinedSingle(previewPeers[0].compactDisplayTitle).string } else { let firstPeers = previewPeers.prefix(upTo: 2) let peersTextArray = firstPeers.map { "**\($0.compactDisplayTitle)**" } @@ -717,14 +715,14 @@ private final class JoinSubjectScreenComponent: Component { } } if totalMemberCount > firstPeers.count { - previewPeersString = "\(peersText) and **\(totalMemberCount - firstPeers.count)** other people already joined this group." + previewPeersString = environment.strings.Invitation_Group_AlreadyJoinedMultipleWithCount(Int32(totalMemberCount - firstPeers.count)).replacingOccurrences(of: "{}", with: peersText) } else { - previewPeersString = "\(peersText) already joined this group." + previewPeersString = environment.strings.Invitation_Group_AlreadyJoinedMultiple(peersText).string } } case .groupCall: if previewPeers.count == 1 { - previewPeersString = "**\(previewPeers[0].compactDisplayTitle)** already joined this call." + previewPeersString = environment.strings.Invitation_GroupCall_AlreadyJoinedSingle(previewPeers[0].compactDisplayTitle).string } else { let firstPeers = previewPeers.prefix(upTo: 2) let peersTextArray = firstPeers.map { "**\($0.compactDisplayTitle)**" } @@ -745,9 +743,9 @@ private final class JoinSubjectScreenComponent: Component { } } if totalMemberCount > firstPeers.count { - previewPeersString = "\(peersText) and **\(totalMemberCount - firstPeers.count)** other people already joined this call." + previewPeersString = environment.strings.Invitation_GroupCall_AlreadyJoinedMultipleWithCount(Int32(totalMemberCount - firstPeers.count)).replacingOccurrences(of: "{}", with: peersText) } else { - previewPeersString = "\(peersText) already joined this call." + previewPeersString = environment.strings.Invitation_GroupCall_AlreadyJoinedMultiple(peersText).string } } } @@ -854,8 +852,7 @@ private final class JoinSubjectScreenComponent: Component { case .group: actionButtonTitle = environment.strings.Invitation_JoinGroup case .groupCall: - //TODO:localize - actionButtonTitle = "Join Group Call" + actionButtonTitle = environment.strings.Invitation_JoinGroupCall } let actionButtonSize = self.actionButton.update( transition: transition, diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 5184a47089..75b293b704 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -2164,14 +2164,18 @@ private func extractAccountManagerState(records: AccountRecordsView take(1) + |> deliverOnMainQueue).start(next: { sharedApplicationContext in + strings = sharedApplicationContext.sharedContext.currentPresentationData.with { $0.strings } + }) + + let displayTitle: String if let memberCountString = payloadJson["member_count"] as? String, let memberCount = Int(memberCountString) { - if memberCount == 1 { - displayTitle.append(" and 1 other") - } else { - displayTitle.append(" and \(memberCount) others") - } + displayTitle = strings.Call_IncomingGroupCallTitle_Multiple(Int32(memberCount)).replacingOccurrences(of: "{}", with: fromTitle) + } else { + displayTitle = strings.Call_IncomingGroupCallTitle_Single(fromTitle).string } callKitIntegration.reportIncomingCall( diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f09798f46f..c89712de75 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -2932,8 +2932,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.context.sharedContext.openCreateGroupCallUI(context: self.context, peerIds: conferenceCall.otherParticipants, parentController: self) default: let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - //TODO:localize - self.present(textAlertController(context: self.context, title: nil, text: "An error occurred", actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.present(textAlertController(context: self.context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } }) }, longTap: { [weak self] action, params in diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift index 58fa17ca74..3a2d3bc9cd 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift @@ -131,11 +131,10 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { }, checkOptionTitle: nil) case let .groupCreation(isCall): if isCall { - //TODO:localize - placeholder = "Search for contacts or usernames" + placeholder = self.presentationData.strings.NewCall_SearchPlaceholder self.footerPanelNode = FooterPanelNode(theme: self.presentationData.theme, strings: self.presentationData.strings, action: { proceedImpl?() - }, checkOptionTitle: isCall ? "Call with video enabled" : nil) + }, checkOptionTitle: self.presentationData.strings.NewCall_VideoOption) } else { placeholder = self.presentationData.strings.Compose_TokenListPlaceholder self.footerPanelNode = nil @@ -483,15 +482,14 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { count = contactListNode.selectionState?.selectedPeerIndices.count ?? 0 } if case let .groupCreation(isCall) = self.mode, isCall { - //TODO:localize if count == 0 { // Don't set anything to prevent state update } else if count <= 1 { let callTitle: String if case let .contacts(contactListNode) = self.contentNode, let peer = contactListNode.selectedPeers.first, case let .peer(peer, _, _) = peer { - callTitle = "Call \(EnginePeer(peer).compactDisplayTitle)" + callTitle = self.presentationData.strings.NewCall_ActionCallSingle(EnginePeer(peer).compactDisplayTitle).string } else { - callTitle = "Call" + callTitle = self.presentationData.strings.NewCall_ActionCallMultiple } footerPanelNode.content = FooterPanelNode.Content(title: callTitle, badge: "") } else { diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index bcb6f012d2..b313960db0 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -407,8 +407,7 @@ func openResolvedUrlImpl( if case .chat = urlContext { elevatedLayout = false } - //TODO:localize - present(UndoOverlayController(presentationData: presentationData, content: .linkRevoked(text: "This link is no longer active"), elevatedLayout: elevatedLayout, animateInAsReplacement: false, action: { _ in + present(UndoOverlayController(presentationData: presentationData, content: .linkRevoked(text: presentationData.strings.Chat_ToastCallLinkExpired_Text), elevatedLayout: elevatedLayout, animateInAsReplacement: false, action: { _ in return true }), nil) }) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index d3bd0f15bf..6103230f8d 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1962,10 +1962,11 @@ public final class SharedAccountContextImpl: SharedAccountContext { return } + let isVideo = controller?.isCallVideoOptionSelected ?? false + if peerIds.count == 1 { - //TODO:release isVideo controller?.dismiss() - self.performCall(context: context, parentController: parentController, peerId: peerIds[0], isVideo: false, began: { + self.performCall(context: context, parentController: parentController, peerId: peerIds[0], isVideo: isVideo, began: { let _ = (context.sharedContext.hasOngoingCall.get() |> filter { $0 } |> timeout(1.0, queue: Queue.mainQueue(), alternate: .single(true)) @@ -1980,7 +1981,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { }) }) } else { - self.createGroupCall(context: context, parentController: parentController, peerIds: peerIds, completion: { + self.createGroupCall(context: context, parentController: parentController, peerIds: peerIds, isVideo: isVideo, completion: { controller?.dismiss() }) } @@ -2011,7 +2012,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { }) } - private func createGroupCall(context: AccountContext, parentController: ViewController, peerIds: [EnginePeer.Id], completion: (() -> Void)? = nil) { + private func createGroupCall(context: AccountContext, parentController: ViewController, peerIds: [EnginePeer.Id], isVideo: Bool, completion: (() -> Void)? = nil) { parentController.view.endEditing(true) var cancelImpl: (() -> Void)? @@ -2057,7 +2058,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { isStream: false ), reference: .id(id: call.callInfo.id, accessHash: call.callInfo.accessHash), - beginWithVideo: false, + beginWithVideo: isVideo, invitePeerIds: peerIds ) completion?() @@ -2079,9 +2080,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { if let result { switch result { case .linkCopied: - //TODO:localize let presentationData = context.sharedContext.currentPresentationData.with { $0 } - parentController.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: "Call link copied.", customUndoText: "View Call", timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in + parentController.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: presentationData.strings.CallList_ToastCallLinkCopied_Text, customUndoText: presentationData.strings.CallList_ToastCallLinkCopied_Action, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in if case .undo = action { openCall() } From 8b0030ca9b2105d9a74a723578ebaa66b7d95188 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 9 Apr 2025 18:43:25 +0400 Subject: [PATCH 4/5] Fix phone number discovery option --- .../SelectivePrivacySettingsController.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift index bd4a4d56b3..a79f736255 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift @@ -1047,11 +1047,12 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present if let settingInfoText = settingInfoText { entries.append(.settingInfo(presentationData.theme, settingInfoText, settingInfoLink)) } - - entries.append(.phoneDiscoveryHeader(presentationData.theme, presentationData.strings.PrivacyPhoneNumberSettings_DiscoveryHeader)) - entries.append(.phoneDiscoveryEverybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.phoneDiscoveryEnabled != false)) - entries.append(.phoneDiscoveryMyContacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.phoneDiscoveryEnabled == false)) - entries.append(.phoneDiscoveryInfo(presentationData.theme, state.phoneDiscoveryEnabled != false ? presentationData.strings.PrivacyPhoneNumberSettings_CustomPublicLink("+\(phoneNumber)").string : presentationData.strings.PrivacyPhoneNumberSettings_CustomDisabledHelp, phoneLink)) + if case .phoneNumber = kind { + entries.append(.phoneDiscoveryHeader(presentationData.theme, presentationData.strings.PrivacyPhoneNumberSettings_DiscoveryHeader)) + entries.append(.phoneDiscoveryEverybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.phoneDiscoveryEnabled != false)) + entries.append(.phoneDiscoveryMyContacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.phoneDiscoveryEnabled == false)) + entries.append(.phoneDiscoveryInfo(presentationData.theme, state.phoneDiscoveryEnabled != false ? presentationData.strings.PrivacyPhoneNumberSettings_CustomPublicLink("+\(phoneNumber)").string : presentationData.strings.PrivacyPhoneNumberSettings_CustomDisabledHelp, phoneLink)) + } if case .voiceMessages = kind, !isPremium { From fec3db13d59ec31fea5e0a9d8b3c31b70c4f1549 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 10 Apr 2025 15:16:03 +0400 Subject: [PATCH 5/5] Fix gift reorder option availability --- .../PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index ebd3998789..fabc13e616 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -11118,7 +11118,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro giftsContext?.updateSorting(sorting == .date ? .value : .date) }))) - if hasPinnedGifts { + if hasPinnedGifts && hasVisibility { items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Reorder, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) }, action: { _, f in