mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-08-19 04:17:14 +02:00
chore: migrate to new version + fixed several critical bugs
- Migrated project to latest Telegram iOS base (v12.3.2+) - Fixed circular dependency between GhostModeManager and MiscSettingsManager - Fixed multiple Bazel build configuration errors (select() default conditions) - Fixed duplicate type definitions in PeerInfoScreen - Fixed swiftmodule directory resolution in build scripts - Added Ghostgram Settings tab in main Settings menu with all 5 features - Cleared sensitive credentials from config.json (template-only now) - Excluded bazel-cache from version control
This commit is contained in:
@@ -124,6 +124,9 @@ import AdsInfoScreen
|
||||
import PostSuggestionsSettingsScreen
|
||||
import ChatSendStarsScreen
|
||||
import ChatSendAsContextMenu
|
||||
import GlobalControlPanelsContext
|
||||
import ComponentFlow
|
||||
import ComponentDisplayAdapters
|
||||
|
||||
extension ChatControllerImpl {
|
||||
func reloadChatLocation(chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, historyNode: ChatHistoryListNodeImpl, apply: @escaping ((ContainedViewLayoutTransition?) -> Void) -> Void) {
|
||||
@@ -228,17 +231,6 @@ extension ChatControllerImpl {
|
||||
}
|
||||
self.navigationBar?.userInfo = contentData.state.navigationUserInfo
|
||||
|
||||
if self.chatTitleView?.titleContent != contentData.state.chatTitleContent {
|
||||
var animateTitleContents = false
|
||||
if !synchronous, case let .messageOptions(_, _, info) = self.subject, case .reply = info {
|
||||
animateTitleContents = true
|
||||
}
|
||||
if animateTitleContents && self.chatTitleView?.titleContent != nil {
|
||||
self.chatTitleView?.animateLayoutTransition()
|
||||
}
|
||||
self.chatTitleView?.titleContent = contentData.state.chatTitleContent
|
||||
}
|
||||
|
||||
if let infoAvatar = contentData.state.infoAvatar {
|
||||
switch infoAvatar {
|
||||
case let .peer(peer, imageOverride, contextActionIsEnabled, accessibilityLabel):
|
||||
@@ -381,6 +373,13 @@ extension ChatControllerImpl {
|
||||
if previousState.pinnedMessage != contentData.state.pinnedMessage {
|
||||
animated = true
|
||||
}
|
||||
if previousState.translationState?.isEnabled != contentData.state.translationState?.isEnabled {
|
||||
animated = true
|
||||
}
|
||||
if previousState.chatTitleContent != contentData.state.chatTitleContent {
|
||||
animated = true
|
||||
}
|
||||
|
||||
var transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .spring) : .immediate
|
||||
if let forceAnimationTransition {
|
||||
transition = forceAnimationTransition
|
||||
@@ -389,6 +388,22 @@ extension ChatControllerImpl {
|
||||
transition = .immediate
|
||||
}
|
||||
|
||||
if let chatTitleContent = contentData.state.chatTitleContent {
|
||||
var titleTransition = ComponentTransition(transition)
|
||||
if case .messageOptions = self.subject {
|
||||
titleTransition = titleTransition.withAnimation(.none)
|
||||
}
|
||||
self.chatTitleView?.update(
|
||||
context: self.context,
|
||||
theme: self.presentationData.theme,
|
||||
strings: self.presentationData.strings,
|
||||
dateTimeFormat: self.presentationData.dateTimeFormat,
|
||||
nameDisplayOrder: self.presentationData.nameDisplayOrder,
|
||||
content: chatTitleContent,
|
||||
transition: titleTransition
|
||||
)
|
||||
}
|
||||
|
||||
self.updateChatPresentationInterfaceState(transition: transition, interactive: false, { presentationInterfaceState in
|
||||
var presentationInterfaceState = presentationInterfaceState
|
||||
presentationInterfaceState = presentationInterfaceState.updatedPeer({ _ in
|
||||
@@ -677,7 +692,7 @@ extension ChatControllerImpl {
|
||||
|
||||
self.displayNode = ChatControllerNode(context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, subject: self.subject, controllerInteraction: self.controllerInteraction!, chatPresentationInterfaceState: self.presentationInterfaceState, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, navigationBar: self.navigationBar, statusBar: self.statusBar, backgroundNode: self.chatBackgroundNode, controller: self)
|
||||
|
||||
if let currentItem = self.tempVoicePlaylistCurrentItem {
|
||||
if let currentItem = self.globalControlPanelsContext?.tempVoicePlaylistCurrentItem {
|
||||
self.chatDisplayNode.historyNode.voicePlaylistItemChanged(nil, currentItem)
|
||||
}
|
||||
|
||||
@@ -988,7 +1003,15 @@ extension ChatControllerImpl {
|
||||
}
|
||||
|
||||
if let errorText = errorText {
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
let alertController = textAlertController(
|
||||
context: strongSelf.context,
|
||||
title: nil,
|
||||
text: errorText,
|
||||
actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
|
||||
]
|
||||
)
|
||||
strongSelf.present(alertController, in: .window(.root))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1092,10 +1115,15 @@ extension ChatControllerImpl {
|
||||
strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory()
|
||||
case let .businessLinkSetup(link):
|
||||
if messages.count > 1 {
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.BusinessLink_AlertTextLimitText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
|
||||
]), in: .window(.root))
|
||||
|
||||
let alertController = textAlertController(
|
||||
context: strongSelf.context,
|
||||
title: nil,
|
||||
text: strongSelf.presentationData.strings.BusinessLink_AlertTextLimitText,
|
||||
actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
|
||||
]
|
||||
)
|
||||
strongSelf.present(alertController, in: .window(.root))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1871,8 +1899,9 @@ extension ChatControllerImpl {
|
||||
titleString = self.presentationData.strings.Chat_DeletePaidMessageTon_Title
|
||||
textString = self.presentationData.strings.Chat_DeletePaidMessageTon_Text
|
||||
}
|
||||
self.present(standardTextAlertController(
|
||||
theme: AlertControllerTheme(presentationData: self.presentationData),
|
||||
|
||||
let alertController = textAlertController(
|
||||
context: self.context,
|
||||
title: titleString,
|
||||
text: textString,
|
||||
actions: [
|
||||
@@ -1884,9 +1913,9 @@ extension ChatControllerImpl {
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Cancel, action: {})
|
||||
],
|
||||
actionLayout: .vertical,
|
||||
parseMarkdown: true
|
||||
), in: .window(.root))
|
||||
actionLayout: .vertical
|
||||
)
|
||||
self.present(alertController, in: .window(.root))
|
||||
}
|
||||
if let contextController {
|
||||
contextController.dismiss(completion: commit)
|
||||
@@ -3638,7 +3667,7 @@ extension ChatControllerImpl {
|
||||
}
|
||||
}
|
||||
|
||||
let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: strongSelf.updatedPresentationData, account: strongSelf.context.account, text: text?.string ?? "", link: link, allowEmpty: true, apply: { [weak self] link in
|
||||
let controller = chatTextLinkEditController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, text: text?.string ?? "", link: link, apply: { [weak self] link in
|
||||
if let strongSelf = self, let inputMode = inputMode, let selectionRange = selectionRange {
|
||||
if let link {
|
||||
if !link.isEmpty {
|
||||
@@ -4590,6 +4619,44 @@ extension ChatControllerImpl {
|
||||
})
|
||||
}
|
||||
|
||||
var mediaPlayback = false
|
||||
var liveLocationMode: GlobalControlPanelsContext.LiveLocationMode?
|
||||
if case .standard = self.mode {
|
||||
//mediaAccessoryPanelVisibility = .specific(size: .compact)
|
||||
mediaPlayback = true
|
||||
liveLocationMode = self.chatLocation.peerId.flatMap(GlobalControlPanelsContext.LiveLocationMode.peer)
|
||||
}
|
||||
|
||||
var groupCallPanelSource: EnginePeer.Id?
|
||||
switch self.chatLocation {
|
||||
case let .peer(peerId):
|
||||
switch self.subject {
|
||||
case .message, .none:
|
||||
groupCallPanelSource = peerId
|
||||
default:
|
||||
break
|
||||
}
|
||||
case .replyThread, .customChatContents:
|
||||
break
|
||||
}
|
||||
|
||||
let globalControlPanelsContext = GlobalControlPanelsContext(
|
||||
context: self.context,
|
||||
mediaPlayback: mediaPlayback,
|
||||
liveLocationMode: liveLocationMode,
|
||||
groupCalls: groupCallPanelSource,
|
||||
chatListNotices: false
|
||||
)
|
||||
self.globalControlPanelsContext = globalControlPanelsContext
|
||||
self.globalControlPanelsContextStateDisposable = (globalControlPanelsContext.state
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] state in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.globalControlPanelsContextState = state
|
||||
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
|
||||
})
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
@@ -4745,7 +4812,18 @@ extension ChatControllerImpl {
|
||||
return true
|
||||
}
|
||||
})
|
||||
strongSelf.chatTitleView?.inputActivities = (peerId, displayActivities)
|
||||
strongSelf.chatTitleView?.updateActivities(
|
||||
activities: ChatTitleComponent.Activities(
|
||||
peerId: peerId,
|
||||
items: displayActivities.map { item -> ChatTitleComponent.Activities.Item in
|
||||
return ChatTitleComponent.Activities.Item(
|
||||
peer: EnginePeer(item.0),
|
||||
activity: item.1
|
||||
)
|
||||
}
|
||||
),
|
||||
transition: .spring(duration: 0.4)
|
||||
)
|
||||
|
||||
strongSelf.peerInputActivitiesPromise.set(.single(activities))
|
||||
|
||||
|
||||
@@ -361,49 +361,54 @@ extension ChatControllerImpl {
|
||||
strongSelf.recorderDataDisposable.set(nil)
|
||||
} else {
|
||||
let randomId = Int64.random(in: Int64.min ... Int64.max)
|
||||
|
||||
let resource = LocalFileMediaResource(fileId: randomId)
|
||||
strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data.compressedData)
|
||||
|
||||
let originalData = data.compressedData
|
||||
let duration = data.duration
|
||||
let waveformBuffer: Data? = data.waveform
|
||||
|
||||
let correlationId = Int64.random(in: 0 ..< Int64.max)
|
||||
var usedCorrelationId = false
|
||||
// GHOSTGRAM: Check if Voice Morpher needs processing
|
||||
let needsProcessing = VoiceMorpherManager.shared.isEnabled && VoiceMorpherManager.shared.selectedPreset != .disabled
|
||||
|
||||
var shouldAnimateMessageTransition = strongSelf.chatDisplayNode.shouldAnimateMessageTransition
|
||||
if strongSelf.chatLocation.threadId == nil, let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = strongSelf.presentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.manageDirect) {
|
||||
shouldAnimateMessageTransition = false
|
||||
}
|
||||
|
||||
if shouldAnimateMessageTransition, let textInputPanelNode = strongSelf.chatDisplayNode.textInputPanelNode, let micButton = textInputPanelNode.micButton {
|
||||
usedCorrelationId = true
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .audioMicInput(ChatMessageTransitionNodeImpl.Source.AudioMicInput(micButton: micButton)), initiated: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
if needsProcessing {
|
||||
// Show processing indicator
|
||||
let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: nil))
|
||||
statusController.statusBar.statusBarStyle = strongSelf.presentationData.theme.rootController.statusBarStyle.style
|
||||
strongSelf.present(statusController, in: .window(.root))
|
||||
|
||||
// Process async
|
||||
VoiceMorpherEngine.shared.processOggData(originalData) { [weak self, weak statusController] result in
|
||||
DispatchQueue.main.async {
|
||||
statusController?.dismiss()
|
||||
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
let processedData: Data
|
||||
switch result {
|
||||
case .success(let data):
|
||||
processedData = data
|
||||
case .failure:
|
||||
processedData = originalData // fallback to original on error
|
||||
}
|
||||
|
||||
strongSelf.finishSendingVoiceMessage(
|
||||
randomId: randomId,
|
||||
processedData: processedData,
|
||||
duration: duration,
|
||||
waveformBuffer: waveformBuffer,
|
||||
viewOnce: viewOnce
|
||||
)
|
||||
}
|
||||
strongSelf.audioRecorder.set(.single(nil))
|
||||
})
|
||||
} else {
|
||||
strongSelf.audioRecorder.set(.single(nil))
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatDisplayNode.collapseInput()
|
||||
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil) }
|
||||
})
|
||||
}
|
||||
}, usedCorrelationId ? correlationId : nil)
|
||||
|
||||
var attributes: [MessageAttribute] = []
|
||||
if viewOnce {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: viewOnceTimeout, countdownBeginTime: nil))
|
||||
} else {
|
||||
// No processing needed, send directly
|
||||
strongSelf.finishSendingVoiceMessage(
|
||||
randomId: randomId,
|
||||
processedData: originalData,
|
||||
duration: duration,
|
||||
waveformBuffer: waveformBuffer,
|
||||
viewOnce: viewOnce
|
||||
)
|
||||
}
|
||||
|
||||
strongSelf.sendMessages([.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(data.compressedData.count), attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)], alternativeRepresentations: [])), threadId: strongSelf.chatLocation.threadId, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: [])])
|
||||
|
||||
strongSelf.recorderFeedback?.tap()
|
||||
strongSelf.recorderFeedback = nil
|
||||
strongSelf.recorderDataDisposable.set(nil)
|
||||
@@ -774,4 +779,50 @@ extension ChatControllerImpl {
|
||||
self.videoRecorderValue?.sendVideoRecording(silentPosting: silentPosting, scheduleTime: scheduleTime, messageEffect: messageEffect)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - GHOSTGRAM: Voice Morpher Helper
|
||||
|
||||
private func finishSendingVoiceMessage(
|
||||
randomId: Int64,
|
||||
processedData: Data,
|
||||
duration: Double,
|
||||
waveformBuffer: Data?,
|
||||
viewOnce: Bool
|
||||
) {
|
||||
let resource = LocalFileMediaResource(fileId: randomId)
|
||||
self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: processedData)
|
||||
|
||||
let correlationId = Int64.random(in: 0 ..< Int64.max)
|
||||
var usedCorrelationId = false
|
||||
|
||||
var shouldAnimateMessageTransition = self.chatDisplayNode.shouldAnimateMessageTransition
|
||||
if self.chatLocation.threadId == nil, let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = self.presentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.manageDirect) {
|
||||
shouldAnimateMessageTransition = false
|
||||
}
|
||||
|
||||
if shouldAnimateMessageTransition, let textInputPanelNode = self.chatDisplayNode.textInputPanelNode, let micButton = textInputPanelNode.micButton {
|
||||
usedCorrelationId = true
|
||||
self.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .audioMicInput(ChatMessageTransitionNodeImpl.Source.AudioMicInput(micButton: micButton)), initiated: { [weak self] in
|
||||
self?.audioRecorder.set(.single(nil))
|
||||
})
|
||||
} else {
|
||||
self.audioRecorder.set(.single(nil))
|
||||
}
|
||||
|
||||
self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatDisplayNode.collapseInput()
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil) }
|
||||
})
|
||||
}
|
||||
}, usedCorrelationId ? correlationId : nil)
|
||||
|
||||
var attributes: [MessageAttribute] = []
|
||||
if viewOnce {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: viewOnceTimeout, countdownBeginTime: nil))
|
||||
}
|
||||
|
||||
self.sendMessages([.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(processedData.count), attributes: [.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: waveformBuffer)], alternativeRepresentations: [])), threadId: self.chatLocation.threadId, replyToMessageId: self.presentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: [])])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,13 +280,19 @@ extension ChatControllerImpl {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
|
||||
beginClear(.scheduledMessages)
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
let alertController = textAlertController(
|
||||
context: strongSelf.context,
|
||||
title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle,
|
||||
text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText,
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
|
||||
beginClear(.scheduledMessages)
|
||||
})
|
||||
]
|
||||
)
|
||||
strongSelf.present(alertController, in: .window(.root))
|
||||
}))
|
||||
} else {
|
||||
if let _ = canClearForMyself ?? canClearForEveryone {
|
||||
@@ -310,13 +316,19 @@ extension ChatControllerImpl {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: confirmationText, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: {
|
||||
beginClear(.forEveryone)
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
let alertController = textAlertController(
|
||||
context: strongSelf.context,
|
||||
title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle,
|
||||
text: confirmationText,
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: {
|
||||
beginClear(.forEveryone)
|
||||
})
|
||||
]
|
||||
)
|
||||
strongSelf.present(alertController, in: .window(.root))
|
||||
}))
|
||||
}
|
||||
if let canClearForMyself = canClearForMyself {
|
||||
@@ -330,13 +342,19 @@ extension ChatControllerImpl {
|
||||
items.append(ActionSheetButtonItem(title: text, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if mainPeer.id == context.account.peerId, let strongSelf = self {
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
|
||||
beginClear(.forLocalPeer)
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
let alertController = textAlertController(
|
||||
context: strongSelf.context,
|
||||
title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle,
|
||||
text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText,
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
|
||||
beginClear(.forLocalPeer)
|
||||
})
|
||||
]
|
||||
)
|
||||
strongSelf.present(alertController, in: .window(.root))
|
||||
} else {
|
||||
beginClear(.forLocalPeer)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import PremiumUI
|
||||
import TooltipUI
|
||||
import TopMessageReactions
|
||||
import TelegramNotices
|
||||
import PresentationDataUtils
|
||||
|
||||
extension ChatControllerImpl {
|
||||
func openMessageContextMenu(message: Message, selectAll: Bool, node: ASDisplayNode, frame: CGRect, anyRecognizer: UIGestureRecognizer?, location: CGPoint?) -> Void {
|
||||
@@ -433,9 +434,15 @@ extension ChatControllerImpl {
|
||||
|
||||
if case let .known(reactionSettings) = reactionSettings, let starsAllowed = reactionSettings.starsAllowed, !starsAllowed {
|
||||
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer {
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Chat_ToastStarsReactionsDisabled(peer.debugDisplayTitle).string, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
|
||||
]), in: .window(.root))
|
||||
let alertController = textAlertController(
|
||||
context: strongSelf.context,
|
||||
title: nil,
|
||||
text: strongSelf.presentationData.strings.Chat_ToastStarsReactionsDisabled(peer.debugDisplayTitle).string,
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
|
||||
]
|
||||
)
|
||||
strongSelf.present(alertController, in: .window(.root))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -16,7 +16,12 @@ extension ChatControllerImpl {
|
||||
break
|
||||
}
|
||||
}
|
||||
let controller = factCheckAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, value: currentText, entities: currentEntities, apply: { [weak self] text, entities in
|
||||
let controller = factCheckAlertController(
|
||||
context: self.context,
|
||||
updatedPresentationData: self.updatedPresentationData,
|
||||
value: currentText,
|
||||
entities: currentEntities,
|
||||
apply: { [weak self] text, entities in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -357,7 +357,9 @@ extension ChatControllerImpl {
|
||||
})))
|
||||
}
|
||||
|
||||
items.append(.separator)
|
||||
if !items.isEmpty {
|
||||
items.append(.separator)
|
||||
}
|
||||
items.append(.action(ContextMenuActionItem(text: strings.Conversation_Search, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] action in
|
||||
|
||||
@@ -14,6 +14,7 @@ import PresentationDataUtils
|
||||
import TelegramCallsUI
|
||||
import AttachmentUI
|
||||
import WebUI
|
||||
import LegacyChatHeaderPanelComponent
|
||||
|
||||
func updateChatPresentationInterfaceStateImpl(
|
||||
selfController: ChatControllerImpl,
|
||||
|
||||
Reference in New Issue
Block a user