diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 8cc33fd4ad..76307b2c57 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9915,7 +9915,9 @@ Sorry for the inconvenience."; "Gallery.ViewOnceVideoTooltip" = "This video can only be viewed once."; "WebApp.DisclaimerTitle" = "Warning"; -"WebApp.DisclaimerText" = "You are about to use a mini app operated by an independent party not affiliated with Telegram. You must agree to the Terms of Use of mini apps to continue.\n\n**%@** shortcuts will be added in your attachment menu and Settings."; +"WebApp.DisclaimerText" = "You are about to use a mini app operated by an independent party not affiliated with Telegram. You must agree to the Terms of Use of mini apps to continue."; +"WebApp.DisclaimerShortcutsText" = "**%@** shortcuts will be added in your attachment menu."; +"WebApp.DisclaimerShortcutsSettingsText" = "**%@** shortcuts will be added in your attachment menu and Settings."; "WebApp.DisclaimerAgree" = "I agree to the [Terms of Use]()"; "WebApp.DisclaimerContinue" = "Continue"; "WebApp.Disclaimer_URL" = "https://telegram.org/tos/mini-apps"; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 32917f52d8..e939f3d9ae 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -869,7 +869,7 @@ public protocol SharedAccountContext: AnyObject { func makeSetupTwoFactorAuthController(context: AccountContext) -> ViewController func makeStorageManagementController(context: AccountContext) -> ViewController func makeAttachmentFileController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileController - func makeGalleryCaptionPanelView(context: AccountContext, chatLocation: ChatLocation, customEmojiAvailable: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void) -> NSObject? + func makeGalleryCaptionPanelView(context: AccountContext, chatLocation: ChatLocation, isScheduledMessages: Bool, customEmojiAvailable: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void) -> NSObject? func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String, all: Bool) -> ViewController func makeMyStoriesController(context: AccountContext, isArchive: Bool) -> ViewController func makeArchiveSettingsController(context: AccountContext) -> ViewController diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputActionButtonsNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputActionButtonsNode.swift index b52a183491..10fa313b6e 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputActionButtonsNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputActionButtonsNode.swift @@ -82,6 +82,7 @@ final class AttachmentTextInputActionButtonsNode: ASDisplayNode { super.didLoad() let gestureRecognizer = ContextGesture(target: nil, action: nil) + gestureRecognizer.isEnabled = self.sendButtonLongPressEnabled self.gestureRecognizer = gestureRecognizer self.sendButton.view.addGestureRecognizer(gestureRecognizer) gestureRecognizer.activated = { [weak self] recognizer, _ in diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift index ef2e88af01..8c29e54cc2 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift @@ -327,7 +327,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS private var maxCaptionLength: Int32? - public init(context: AccountContext, presentationInterfaceState: ChatPresentationInterfaceState, isCaption: Bool = false, isAttachment: Bool = false, presentController: @escaping (ViewController) -> Void, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) { + public init(context: AccountContext, presentationInterfaceState: ChatPresentationInterfaceState, isCaption: Bool = false, isAttachment: Bool = false, isScheduledMessages: Bool = false, presentController: @escaping (ViewController) -> Void, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) { self.context = context self.presentationInterfaceState = presentationInterfaceState self.isCaption = isCaption @@ -374,8 +374,13 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS super.init() - self.actionButtons.sendButtonLongPressed = { [weak self] node, gesture in - self?.interfaceInteraction?.displaySendMessageOptions(node, gesture) + if !isScheduledMessages { + self.actionButtons.sendButtonLongPressed = { [weak self] node, gesture in + self?.interfaceInteraction?.displaySendMessageOptions(node, gesture) + } + self.actionButtons.sendButtonLongPressEnabled = true + } else { + self.actionButtons.sendButtonLongPressEnabled = false } self.actionButtons.sendButton.addTarget(self, action: #selector(self.sendButtonPressed), forControlEvents: .touchUpInside) @@ -727,8 +732,6 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS } self.textPlaceholderNode.frame = CGRect(origin: self.textPlaceholderNode.frame.origin, size: placeholderSize) } - - self.actionButtons.sendButtonLongPressEnabled = true } let sendButtonHasApplyIcon = self.isCaption || interfaceState.interfaceState.editMessage != nil diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index 4b35a35891..04983504d4 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -179,6 +179,7 @@ public class AttachmentController: ViewController { private let context: AccountContext private let updatedPresentationData: (initial: PresentationData, signal: Signal)? private let chatLocation: ChatLocation? + private let isScheduledMessages: Bool private let buttons: [AttachmentButtonType] private let initialButton: AttachmentButtonType private let fromMenu: Bool @@ -294,7 +295,7 @@ public class AttachmentController: ViewController { self.container = AttachmentContainer() self.container.canHaveKeyboardFocus = true - self.panel = AttachmentPanel(context: controller.context, chatLocation: controller.chatLocation, updatedPresentationData: controller.updatedPresentationData, makeEntityInputView: makeEntityInputView) + self.panel = AttachmentPanel(context: controller.context, chatLocation: controller.chatLocation, isScheduledMessages: controller.isScheduledMessages, updatedPresentationData: controller.updatedPresentationData, makeEntityInputView: makeEntityInputView) self.panel.fromMenu = controller.fromMenu self.panel.isStandalone = controller.isStandalone @@ -907,10 +908,11 @@ public class AttachmentController: ViewController { public var getSourceRect: (() -> CGRect?)? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, chatLocation: ChatLocation?, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, hasTextInput: Bool = true, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil}) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, chatLocation: ChatLocation?, isScheduledMessages: Bool = false, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, hasTextInput: Bool = true, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil}) { self.context = context self.updatedPresentationData = updatedPresentationData self.chatLocation = chatLocation + self.isScheduledMessages = isScheduledMessages self.buttons = buttons self.initialButton = initialButton self.fromMenu = fromMenu diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 505cd6b429..8f55a0d317 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -725,6 +725,7 @@ private final class MainButtonNode: HighlightTrackingButtonNode { final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { private let context: AccountContext + private let isScheduledMessages: Bool private var presentationData: PresentationData private var presentationDataDisposable: Disposable? @@ -775,9 +776,10 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { var mainButtonPressed: () -> Void = { } - init(context: AccountContext, chatLocation: ChatLocation?, updatedPresentationData: (initial: PresentationData, signal: Signal)?, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) { + init(context: AccountContext, chatLocation: ChatLocation?, isScheduledMessages: Bool, updatedPresentationData: (initial: PresentationData, signal: Signal)?, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) { self.context = context self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } + self.isScheduledMessages = isScheduledMessages self.makeEntityInputView = makeEntityInputView @@ -1248,7 +1250,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { private func loadTextNodeIfNeeded() { if let _ = self.textInputPanelNode { } else { - let textInputPanelNode = AttachmentTextInputPanelNode(context: self.context, presentationInterfaceState: self.presentationInterfaceState, isAttachment: true, presentController: { [weak self] c in + let textInputPanelNode = AttachmentTextInputPanelNode(context: self.context, presentationInterfaceState: self.presentationInterfaceState, isAttachment: true, isScheduledMessages: self.isScheduledMessages, presentController: { [weak self] c in if let strongSelf = self { strongSelf.present(c) } diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift index cb71a621da..ac5f097cc3 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift @@ -22,6 +22,7 @@ public final class ChatSendMessageActionSheetController: ViewController { private let context: AccountContext private let peerId: EnginePeer.Id? + private let isScheduledMessages: Bool private let forwardMessageIds: [EngineMessage.Id]? private let hasEntityKeyboard: Bool @@ -45,9 +46,10 @@ public final class ChatSendMessageActionSheetController: ViewController { public var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id?, forwardMessageIds: [EngineMessage.Id]?, hasEntityKeyboard: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputNode: EditableTextNode, attachment: Bool = false, canSendWhenOnline: Bool, completion: @escaping () -> Void, sendMessage: @escaping (SendMode) -> Void, schedule: @escaping () -> Void) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id?, isScheduledMessages: Bool = false, forwardMessageIds: [EngineMessage.Id]?, hasEntityKeyboard: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputNode: EditableTextNode, attachment: Bool = false, canSendWhenOnline: Bool, completion: @escaping () -> Void, sendMessage: @escaping (SendMode) -> Void, schedule: @escaping () -> Void) { self.context = context self.peerId = peerId + self.isScheduledMessages = isScheduledMessages self.forwardMessageIds = forwardMessageIds self.hasEntityKeyboard = hasEntityKeyboard self.gesture = gesture @@ -101,6 +103,9 @@ public final class ChatSendMessageActionSheetController: ViewController { isSecret = peerId.namespace == Namespaces.Peer.SecretChat canSchedule = !isSecret } + if self.isScheduledMessages { + canSchedule = false + } self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, presentationData: self.presentationData, reminders: reminders, gesture: gesture, sourceSendButton: self.sourceSendButton, textInputNode: self.textInputNode, attachment: self.attachment, canSendWhenOnline: self.canSendWhenOnline, forwardedCount: forwardedCount, hasEntityKeyboard: self.hasEntityKeyboard, emojiViewProvider: self.emojiViewProvider, send: { [weak self] in self?.sendMessage(.generic) diff --git a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift index 7604afc788..5856601071 100644 --- a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift +++ b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift @@ -100,7 +100,7 @@ enum LegacyMediaPickerGallerySource { case selection(item: TGMediaSelectableItem) } -func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?, threadTitle: String?, chatLocation: ChatLocation?, presentationData: PresentationData, source: LegacyMediaPickerGallerySource, immediateThumbnail: UIImage?, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, hasSilentPosting: Bool, hasSchedule: Bool, hasTimer: Bool, updateHiddenMedia: @escaping (String?) -> Void, initialLayout: ContainerViewLayout?, transitionHostView: @escaping () -> UIView?, transitionView: @escaping (String) -> UIView?, completed: @escaping (TGMediaSelectableItem & TGMediaEditableItem, Bool, Int32?, @escaping () -> Void) -> Void, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, present: @escaping (ViewController, Any?) -> Void, finishedTransitionIn: @escaping () -> Void, willTransitionOut: @escaping () -> Void, dismissAll: @escaping () -> Void) -> TGModernGalleryController { +func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?, threadTitle: String?, chatLocation: ChatLocation?, isScheduledMessages: Bool, presentationData: PresentationData, source: LegacyMediaPickerGallerySource, immediateThumbnail: UIImage?, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, hasSilentPosting: Bool, hasSchedule: Bool, hasTimer: Bool, updateHiddenMedia: @escaping (String?) -> Void, initialLayout: ContainerViewLayout?, transitionHostView: @escaping () -> UIView?, transitionView: @escaping (String) -> UIView?, completed: @escaping (TGMediaSelectableItem & TGMediaEditableItem, Bool, Int32?, @escaping () -> Void) -> Void, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, present: @escaping (ViewController, Any?) -> Void, finishedTransitionIn: @escaping () -> Void, willTransitionOut: @escaping () -> Void, dismissAll: @escaping () -> Void) -> TGModernGalleryController { let reminder = peer?.id == context.account.peerId let hasSilentPosting = hasSilentPosting && peer?.id != context.account.peerId @@ -224,103 +224,105 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?, }) } } - model.interfaceView.doneLongPressed = { [weak selectionContext, weak editingContext, weak legacyController, weak model] item in - if let legacyController = legacyController, let item = item as? TGMediaPickerGalleryItem, let model = model, let selectionContext = selectionContext { - var effectiveHasSchedule = hasSchedule - - if let editingContext = editingContext { - if let timer = editingContext.timer(for: item.asset)?.intValue, timer > 0 { - effectiveHasSchedule = false - } - for item in selectionContext.selectedItems() { - if let editableItem = item as? TGMediaEditableItem, let timer = editingContext.timer(for: editableItem)?.intValue, timer > 0 { + if !isScheduledMessages { + model.interfaceView.doneLongPressed = { [weak selectionContext, weak editingContext, weak legacyController, weak model] item in + if let legacyController = legacyController, let item = item as? TGMediaPickerGalleryItem, let model = model, let selectionContext = selectionContext { + var effectiveHasSchedule = hasSchedule + + if let editingContext = editingContext { + if let timer = editingContext.timer(for: item.asset)?.intValue, timer > 0 { effectiveHasSchedule = false - break } - } - } - - let sendWhenOnlineAvailable: Signal - if let peer { - if case .secretChat = peer { - effectiveHasSchedule = false - } - sendWhenOnlineAvailable = context.account.viewTracker.peerView(peer.id) - |> take(1) - |> map { peerView -> Bool in - guard let peer = peerViewMainPeer(peerView) else { - return false - } - var sendWhenOnlineAvailable = false - if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case let .present(until) = presence.status { - let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) - if currentTime > until { - sendWhenOnlineAvailable = true + for item in selectionContext.selectedItems() { + if let editableItem = item as? TGMediaEditableItem, let timer = editingContext.timer(for: editableItem)?.intValue, timer > 0 { + effectiveHasSchedule = false + break } } - if peer.id.namespace == Namespaces.Peer.CloudUser && peer.id.id._internalGetInt64Value() == 777000 { - sendWhenOnlineAvailable = false + } + + let sendWhenOnlineAvailable: Signal + if let peer { + if case .secretChat = peer { + effectiveHasSchedule = false } - return sendWhenOnlineAvailable - } - } else { - sendWhenOnlineAvailable = .single(false) - } - - let _ = (sendWhenOnlineAvailable - |> take(1) - |> deliverOnMainQueue).start(next: { sendWhenOnlineAvailable in - let legacySheetController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil) - let sheetController = TGMediaPickerSendActionSheetController(context: legacyController.context, isDark: true, sendButtonFrame: model.interfaceView.doneButtonFrame, canSendSilently: hasSilentPosting, canSendWhenOnline: sendWhenOnlineAvailable && effectiveHasSchedule, canSchedule: effectiveHasSchedule, reminder: reminder, hasTimer: false) - let dismissImpl = { [weak model] in - model?.dismiss(true, false) - dismissAll() - } - sheetController.send = { - completed(item.asset, false, nil, { - dismissImpl() - }) - } - sheetController.sendSilently = { - completed(item.asset, true, nil, { - dismissImpl() - }) - } - sheetController.sendWhenOnline = { - completed(item.asset, false, scheduleWhenOnlineTimestamp, { - dismissImpl() - }) - } - sheetController.schedule = { - presentSchedulePicker(true, { time in - completed(item.asset, false, time, { - dismissImpl() - }) - }) - } - sheetController.sendWithTimer = { - presentTimerPicker { time in - var items = selectionContext.selectedItems() ?? [] - items.append(item.asset as Any) - - for case let item as TGMediaEditableItem in items { - editingContext?.setTimer(time as NSNumber, for: item) + sendWhenOnlineAvailable = context.account.viewTracker.peerView(peer.id) + |> take(1) + |> map { peerView -> Bool in + guard let peer = peerViewMainPeer(peerView) else { + return false } - + var sendWhenOnlineAvailable = false + if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case let .present(until) = presence.status { + let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) + if currentTime > until { + sendWhenOnlineAvailable = true + } + } + if peer.id.namespace == Namespaces.Peer.CloudUser && peer.id.id._internalGetInt64Value() == 777000 { + sendWhenOnlineAvailable = false + } + return sendWhenOnlineAvailable + } + } else { + sendWhenOnlineAvailable = .single(false) + } + + let _ = (sendWhenOnlineAvailable + |> take(1) + |> deliverOnMainQueue).start(next: { sendWhenOnlineAvailable in + let legacySheetController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil) + let sheetController = TGMediaPickerSendActionSheetController(context: legacyController.context, isDark: true, sendButtonFrame: model.interfaceView.doneButtonFrame, canSendSilently: hasSilentPosting, canSendWhenOnline: sendWhenOnlineAvailable && effectiveHasSchedule, canSchedule: effectiveHasSchedule, reminder: reminder, hasTimer: false) + let dismissImpl = { [weak model] in + model?.dismiss(true, false) + dismissAll() + } + sheetController.send = { completed(item.asset, false, nil, { dismissImpl() }) } - } - sheetController.customDismissBlock = { [weak legacySheetController] in - legacySheetController?.dismiss() - } - legacySheetController.bind(controller: sheetController) - present(legacySheetController, nil) - - let hapticFeedback = HapticFeedback() - hapticFeedback.impact() - }) + sheetController.sendSilently = { + completed(item.asset, true, nil, { + dismissImpl() + }) + } + sheetController.sendWhenOnline = { + completed(item.asset, false, scheduleWhenOnlineTimestamp, { + dismissImpl() + }) + } + sheetController.schedule = { + presentSchedulePicker(true, { time in + completed(item.asset, false, time, { + dismissImpl() + }) + }) + } + sheetController.sendWithTimer = { + presentTimerPicker { time in + var items = selectionContext.selectedItems() ?? [] + items.append(item.asset as Any) + + for case let item as TGMediaEditableItem in items { + editingContext?.setTimer(time as NSNumber, for: item) + } + + completed(item.asset, false, nil, { + dismissImpl() + }) + } + } + sheetController.customDismissBlock = { [weak legacySheetController] in + legacySheetController?.dismiss() + } + legacySheetController.bind(controller: sheetController) + present(legacySheetController, nil) + + let hapticFeedback = HapticFeedback() + hapticFeedback.impact() + }) + } } } model.interfaceView.setThumbnailSignalForItem { item in diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index a2a6176e57..7a13b856ff 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -169,6 +169,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { fileprivate var interaction: MediaPickerInteraction? private let peer: EnginePeer? + private let isScheduledMessages: Bool private let threadTitle: String? private let chatLocation: ChatLocation? private let bannedSendPhotos: (Int32, Bool)? @@ -970,7 +971,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.openingMedia = true - self.currentGalleryController = presentLegacyMediaPickerGallery(context: controller.context, peer: controller.peer, threadTitle: controller.threadTitle, chatLocation: controller.chatLocation, presentationData: self.presentationData, source: .fetchResult(fetchResult: fetchResult, index: index, reversed: reversed), immediateThumbnail: immediateThumbnail, selectionContext: interaction.selectionState, editingContext: interaction.editingState, hasSilentPosting: true, hasSchedule: hasSchedule, hasTimer: hasTimer, updateHiddenMedia: { [weak self] id in + self.currentGalleryController = presentLegacyMediaPickerGallery(context: controller.context, peer: controller.peer, threadTitle: controller.threadTitle, chatLocation: controller.chatLocation, isScheduledMessages: controller.isScheduledMessages, presentationData: self.presentationData, source: .fetchResult(fetchResult: fetchResult, index: index, reversed: reversed), immediateThumbnail: immediateThumbnail, selectionContext: interaction.selectionState, editingContext: interaction.editingState, hasSilentPosting: true, hasSchedule: hasSchedule, hasTimer: hasTimer, updateHiddenMedia: { [weak self] id in self?.hiddenMediaId.set(.single(id)) }, initialLayout: layout, transitionHostView: { [weak self] in return self?.gridNode.view @@ -1009,7 +1010,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } self.openingMedia = true - self.currentGalleryController = presentLegacyMediaPickerGallery(context: controller.context, peer: controller.peer, threadTitle: controller.threadTitle, chatLocation: controller.chatLocation, presentationData: self.presentationData, source: .selection(item: item), immediateThumbnail: immediateThumbnail, selectionContext: interaction.selectionState, editingContext: interaction.editingState, hasSilentPosting: true, hasSchedule: true, hasTimer: hasTimer, updateHiddenMedia: { [weak self] id in + self.currentGalleryController = presentLegacyMediaPickerGallery(context: controller.context, peer: controller.peer, threadTitle: controller.threadTitle, chatLocation: controller.chatLocation, isScheduledMessages: controller.isScheduledMessages, presentationData: self.presentationData, source: .selection(item: item), immediateThumbnail: immediateThumbnail, selectionContext: interaction.selectionState, editingContext: interaction.editingState, hasSilentPosting: true, hasSchedule: true, hasTimer: hasTimer, updateHiddenMedia: { [weak self] id in self?.hiddenMediaId.set(.single(id)) }, initialLayout: layout, transitionHostView: { [weak self] in return self?.selectionNode?.view @@ -1524,6 +1525,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { peer: EnginePeer?, threadTitle: String?, chatLocation: ChatLocation?, + isScheduledMessages: Bool = false, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, subject: Subject, @@ -1541,6 +1543,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.peer = peer self.threadTitle = threadTitle self.chatLocation = chatLocation + self.isScheduledMessages = isScheduledMessages self.bannedSendPhotos = bannedSendPhotos self.bannedSendVideos = bannedSendVideos self.subject = subject @@ -2104,7 +2107,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { var updateNavigationStackImpl: ((AttachmentContainable) -> Void)? let groupsController = MediaGroupsScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mediaAssetsContext: self.controllerNode.mediaAssetsContext, embedded: embedded, openGroup: { [weak self] collection in if let strongSelf = self { - let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, threadTitle: strongSelf.threadTitle, chatLocation: strongSelf.chatLocation, bannedSendPhotos: strongSelf.bannedSendPhotos, bannedSendVideos: strongSelf.bannedSendVideos, subject: .assets(collection, mode), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState) + let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, threadTitle: strongSelf.threadTitle, chatLocation: strongSelf.chatLocation, isScheduledMessages: strongSelf.isScheduledMessages, bannedSendPhotos: strongSelf.bannedSendPhotos, bannedSendVideos: strongSelf.bannedSendVideos, subject: .assets(collection, mode), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState) mediaPicker.presentSchedulePicker = strongSelf.presentSchedulePicker mediaPicker.presentTimerPicker = strongSelf.presentTimerPicker diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift index f792b1f52a..68a86ee647 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift @@ -191,6 +191,10 @@ public final class AttachMenuBots: Equatable, Codable { try container.encode(Int32(self.flags.rawValue), forKey: .flags) } + + func withUpdatedFlags(_ flags: Flags) -> Bot { + return Bot(peerId: self.peerId, name: self.name, icons: self.icons, peerTypes: self.peerTypes, flags: flags) + } } private enum CodingKeys: String, CodingKey { @@ -442,6 +446,23 @@ func _internal_removeBotFromAttachMenu(accountPeerId: PeerId, postbox: Postbox, |> switchToLatest } +func _internal_acceptAttachMenuBotDisclaimer(postbox: Postbox, botId: PeerId) -> Signal { + return postbox.transaction { transaction in + if let attachMenuBots = cachedAttachMenuBots(transaction: transaction) { + var updatedAttachMenuBots = attachMenuBots + if let index = attachMenuBots.bots.firstIndex(where: { $0.peerId == botId }) { + var updatedFlags = attachMenuBots.bots[index].flags + updatedFlags.remove(.showInSettingsDisclaimer) + let updatedBot = attachMenuBots.bots[index].withUpdatedFlags(updatedFlags) + var updatedBots = attachMenuBots.bots + updatedBots[index] = updatedBot + updatedAttachMenuBots = AttachMenuBots(hash: attachMenuBots.hash, bots: updatedBots) + } + setCachedAttachMenuBots(transaction: transaction, attachMenuBots: updatedAttachMenuBots) + } + } |> ignoreValues +} + public struct AttachMenuBot { public let peer: EnginePeer public let shortName: String @@ -449,7 +470,7 @@ public struct AttachMenuBot { public let peerTypes: AttachMenuBots.Bot.PeerFlags public let flags: AttachMenuBots.Bot.Flags - init(peer: EnginePeer, shortName: String, icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile], peerTypes: AttachMenuBots.Bot.PeerFlags, flags: AttachMenuBots.Bot.Flags) { + public init(peer: EnginePeer, shortName: String, icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile], peerTypes: AttachMenuBots.Bot.PeerFlags, flags: AttachMenuBots.Bot.Flags) { self.peer = peer self.shortName = shortName self.icons = icons diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index e0ba967b1b..1bc0d1fe5d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -531,6 +531,10 @@ public extension TelegramEngine { return _internal_removeBotFromAttachMenu(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, botId: botId) } + public func acceptAttachMenuBotDisclaimer(botId: PeerId) -> Signal { + return _internal_acceptAttachMenuBotDisclaimer(postbox: self.account.postbox, botId: botId) + } + public func getAttachMenuBot(botId: PeerId, cached: Bool = false) -> Signal { return _internal_getAttachMenuBot(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, botId: botId, cached: cached) } diff --git a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift index 264781343a..8492d951f9 100644 --- a/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift +++ b/submodules/TelegramUI/Components/LegacyMessageInputPanel/Sources/LegacyMessageInputPanel.swift @@ -17,6 +17,7 @@ import TooltipUI public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { private let context: AccountContext private let chatLocation: ChatLocation + private let isScheduledMessages: Bool private let present: (ViewController) -> Void private let presentInGlobalOverlay: (ViewController) -> Void @@ -35,11 +36,13 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { public init( context: AccountContext, chatLocation: ChatLocation, + isScheduledMessages: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void ) { self.context = context self.chatLocation = chatLocation + self.isScheduledMessages = isScheduledMessages self.present = present self.presentInGlobalOverlay = presentInGlobalOverlay @@ -164,7 +167,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { strings: presentationData.strings, style: .media, placeholder: .plain(presentationData.strings.MediaPicker_AddCaption), - maxLength: 1024, + maxLength: Int(self.context.userLimits.maxCaptionLength), queryTypes: [.mention], alwaysDarkWhenHasText: false, resetInputContents: resetInputContents, @@ -191,7 +194,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView { likeAction: nil, likeOptionsAction: nil, inputModeAction: nil, - timeoutAction: self.chatLocation.peerId?.namespace == Namespaces.Peer.CloudUser ? { [weak self] sourceView, gesture in + timeoutAction: self.chatLocation.peerId?.namespace == Namespaces.Peer.CloudUser && !self.isScheduledMessages ? { [weak self] sourceView, gesture in if let self { self.presentTimeoutSetup(sourceView: sourceView, gesture: gesture) } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index 4f214ffc97..55585862f8 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -2301,7 +2301,7 @@ final class StoryItemSetContainerSendMessage { return nil } //TODO:self.presentationInterfaceState.customEmojiAvailable - return component.context.sharedContext.makeGalleryCaptionPanelView(context: component.context, chatLocation: .peer(id: peer.id), customEmojiAvailable: true, present: { [weak view] c in + return component.context.sharedContext.makeGalleryCaptionPanelView(context: component.context, chatLocation: .peer(id: peer.id), isScheduledMessages: false, customEmojiAvailable: true, present: { [weak view] c in guard let view else { return } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 02f372049d..43e01dc52e 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -13012,7 +13012,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func getCaptionPanelView() -> TGCaptionPanelView? { - return self.context.sharedContext.makeGalleryCaptionPanelView(context: self.context, chatLocation: self.presentationInterfaceState.chatLocation, customEmojiAvailable: self.presentationInterfaceState.customEmojiAvailable, present: { [weak self] c in + var isScheduledMessages = false + if case .scheduledMessages = self.presentationInterfaceState.subject { + isScheduledMessages = true + } + return self.context.sharedContext.makeGalleryCaptionPanelView(context: self.context, chatLocation: self.presentationInterfaceState.chatLocation, isScheduledMessages: isScheduledMessages, customEmojiAvailable: self.presentationInterfaceState.customEmojiAvailable, present: { [weak self] c in self?.present(c, in: .window(.root)) }, presentInGlobalOverlay: { [weak self] c in guard let self else { @@ -13443,7 +13447,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.canReadHistory.set(false) - let attachmentController = AttachmentController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, chatLocation: strongSelf.chatLocation, buttons: buttons, initialButton: initialButton, makeEntityInputView: { [weak self] in + let attachmentController = AttachmentController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, chatLocation: strongSelf.chatLocation, isScheduledMessages: isScheduledMessages, buttons: buttons, initialButton: initialButton, makeEntityInputView: { [weak self] in guard let strongSelf = self else { return nil } @@ -14230,7 +14234,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let peer = self.presentationInterfaceState.renderedPeer?.peer else { return } - let controller = MediaPickerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: EnginePeer(peer), threadTitle: self.threadInfo?.title, chatLocation: self.chatLocation, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, subject: subject, saveEditedPhotos: saveEditedPhotos) + var isScheduledMessages = false + if case .scheduledMessages = self.presentationInterfaceState.subject { + isScheduledMessages = true + } + let controller = MediaPickerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: EnginePeer(peer), threadTitle: self.threadInfo?.title, chatLocation: self.chatLocation, isScheduledMessages: isScheduledMessages, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, subject: subject, saveEditedPhotos: saveEditedPhotos) let mediaPickerContext = controller.mediaPickerContext controller.openCamera = { [weak self] cameraView in self?.openCamera(cameraView: cameraView) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index ea9bac48d9..4e546d94a4 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -489,6 +489,25 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, } |> distinctUntilChanged + let botsKey = ValueBoxKey(length: 8) + botsKey.setInt64(0, value: 0) + let bots = context.engine.data.subscribe(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: Namespaces.CachedItemCollection.attachMenuBots, id: botsKey)) + |> mapToSignal { entry -> Signal<[AttachMenuBot], NoError> in + let bots: [AttachMenuBots.Bot] = entry?.get(AttachMenuBots.self)?.bots ?? [] + return context.engine.data.subscribe( + EngineDataMap(bots.map(\.peerId).map(TelegramEngine.EngineData.Item.Peer.Peer.init)) + ) + |> map { peersMap -> [AttachMenuBot] in + var result: [AttachMenuBot] = [] + for bot in bots { + if let maybePeer = peersMap[bot.peerId], let peer = maybePeer { + result.append(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes, flags: bot.flags)) + } + } + return result + } + } + return combineLatest( context.account.viewTracker.peerView(peerId, updateData: true), accountsAndPeers, @@ -512,7 +531,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, } |> distinctUntilChanged, hasStories, - context.engine.messages.attachMenuBots() + bots ) |> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences, suggestions, limits, hasPassword, isPowerSavingEnabled, hasStories, bots -> PeerInfoScreenData in let (notificationExceptions, notificationsAuthorizationStatus, notificationsWarningSuppressed) = notifications diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 298c694c2b..4874ea7a02 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -4640,10 +4640,29 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro guard let self else { return } - self.openBotAppDisposable.set(((self.context.engine.messages.requestSimpleWebView(botId: bot.peer.id, url: nil, source: .settings, themeParams: generateWebAppThemeParams(self.presentationData.theme)) + + let presentationData = self.presentationData + let progressSignal = Signal { [weak self] subscriber in + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) + self?.controller?.present(controller, in: .window(.root)) + return ActionDisposable { [weak controller] in + Queue.mainQueue().async() { + controller?.dismiss() + } + } + } + |> runOn(Queue.mainQueue()) + |> delay(0.35, queue: Queue.mainQueue()) + let progressDisposable = progressSignal.start() + + let signal: Signal = self.context.engine.messages.requestSimpleWebView(botId: bot.peer.id, url: nil, source: .settings, themeParams: generateWebAppThemeParams(self.presentationData.theme)) |> afterDisposed { -// updateProgress() - }) + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + + self.openBotAppDisposable.set((signal |> deliverOnMainQueue).start(next: { [weak self] url in guard let self else { return @@ -4670,6 +4689,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro guard let self else { return } + if bot.flags.contains(.showInSettingsDisclaimer) { + let _ = self.context.engine.messages.acceptAttachMenuBotDisclaimer(botId: bot.peer.id).start() + } if bot.flags.contains(.notActivated) { let _ = (self.context.engine.messages.addBotToAttachMenu(botId: bot.peer.id, allowWrite: allowWrite) |> deliverOnMainQueue).start(error: { _ in diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index a2d0072614..361167958b 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1627,109 +1627,14 @@ public final class SharedAccountContextImpl: SharedAccountContext { return makeAttachmentFileControllerImpl(context: context, updatedPresentationData: updatedPresentationData, bannedSendMedia: bannedSendMedia, presentGallery: presentGallery, presentFiles: presentFiles, send: send) } - public func makeGalleryCaptionPanelView(context: AccountContext, chatLocation: ChatLocation, customEmojiAvailable: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void) -> NSObject? { -// var presentationData = context.sharedContext.currentPresentationData.with { $0 } -// presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme) -// -// var presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: presentationData.chatFontSize, bubbleCorners: presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: .standard(previewing: false), chatLocation: chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil) -// -// var updateChatPresentationInterfaceStateImpl: (((ChatPresentationInterfaceState) -> ChatPresentationInterfaceState) -> Void)? -// var ensureFocusedImpl: (() -> Void)? -// -// let interfaceInteraction = ChatPanelInterfaceInteraction(updateTextInputStateAndMode: { f in -// updateChatPresentationInterfaceStateImpl?({ -// let (updatedState, updatedMode) = f($0.interfaceState.effectiveInputState, $0.inputMode) -// return $0.updatedInterfaceState { interfaceState in -// return interfaceState.withUpdatedEffectiveInputState(updatedState) -// }.updatedInputMode({ _ in updatedMode }) -// }) -// }, updateInputModeAndDismissedButtonKeyboardMessageId: { f in -// updateChatPresentationInterfaceStateImpl?({ -// let (updatedInputMode, updatedClosedButtonKeyboardMessageId) = f($0) -// return $0.updatedInputMode({ _ in return updatedInputMode }).updatedInterfaceState({ -// $0.withUpdatedMessageActionsState({ value in -// var value = value -// value.closedButtonKeyboardMessageId = updatedClosedButtonKeyboardMessageId -// return value -// }) -// }) -// }) -// }, openLinkEditing: { -// var selectionRange: Range? -// var text: NSAttributedString? -// var inputMode: ChatInputMode? -// updateChatPresentationInterfaceStateImpl?({ state in -// selectionRange = state.interfaceState.effectiveInputState.selectionRange -// if let selectionRange = selectionRange { -// text = state.interfaceState.effectiveInputState.inputText.attributedSubstring(from: NSRange(location: selectionRange.startIndex, length: selectionRange.count)) -// } -// inputMode = state.inputMode -// return state -// }) -// -// var link: String? -// if let text { -// text.enumerateAttributes(in: NSMakeRange(0, text.length)) { attributes, _, _ in -// if let linkAttribute = attributes[ChatTextInputAttributes.textUrl] as? ChatTextInputTextUrlAttribute { -// link = linkAttribute.url -// } -// } -// } -// -// let controller = chatTextLinkEditController(sharedContext: context.sharedContext, updatedPresentationData: (presentationData, .never()), account: context.account, text: text?.string ?? "", link: link, apply: { link in -// if let inputMode = inputMode, let selectionRange = selectionRange { -// if let link = link { -// updateChatPresentationInterfaceStateImpl?({ -// return $0.updatedInterfaceState({ -// $0.withUpdatedEffectiveInputState(chatTextInputAddLinkAttribute($0.effectiveInputState, selectionRange: selectionRange, url: link)) -// }) -// }) -// } -// ensureFocusedImpl?() -// updateChatPresentationInterfaceStateImpl?({ -// return $0.updatedInputMode({ _ in return inputMode }).updatedInterfaceState({ -// $0.withUpdatedEffectiveInputState(ChatTextInputState(inputText: $0.effectiveInputState.inputText, selectionRange: selectionRange.endIndex ..< selectionRange.endIndex)) -// }) -// }) -// } -// }) -// present(controller) -// }) -// -// let inputPanelNode = AttachmentTextInputPanelNode(context: context, presentationInterfaceState: presentationInterfaceState, isCaption: true, presentController: { c in -// presentInGlobalOverlay(c) -// }, makeEntityInputView: { -// return EntityInputView(context: context, isDark: true, areCustomEmojiEnabled: customEmojiAvailable) -// }) -// inputPanelNode.interfaceInteraction = interfaceInteraction -// inputPanelNode.effectivePresentationInterfaceState = { -// return presentationInterfaceState -// } -// -// updateChatPresentationInterfaceStateImpl = { [weak inputPanelNode] f in -// let updatedPresentationInterfaceState = f(presentationInterfaceState) -// let updateInputTextState = presentationInterfaceState.interfaceState.effectiveInputState != updatedPresentationInterfaceState.interfaceState.effectiveInputState -// -// presentationInterfaceState = updatedPresentationInterfaceState -// -// if let inputPanelNode = inputPanelNode, updateInputTextState { -// inputPanelNode.updateInputTextState(updatedPresentationInterfaceState.interfaceState.effectiveInputState, animated: true) -// } -// } -// -// ensureFocusedImpl = { [weak inputPanelNode] in -// inputPanelNode?.ensureFocused() -// } -// -// return inputPanelNode - + public func makeGalleryCaptionPanelView(context: AccountContext, chatLocation: ChatLocation, isScheduledMessages: Bool, customEmojiAvailable: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void) -> NSObject? { let inputPanelNode = LegacyMessageInputPanelNode( context: context, chatLocation: chatLocation, + isScheduledMessages: isScheduledMessages, present: present, presentInGlobalOverlay: presentInGlobalOverlay ) - return inputPanelNode } diff --git a/submodules/WebUI/Sources/WebAppTermsAlertController.swift b/submodules/WebUI/Sources/WebAppTermsAlertController.swift index 8fbd32ed9a..565a1e3b7e 100644 --- a/submodules/WebUI/Sources/WebAppTermsAlertController.swift +++ b/submodules/WebUI/Sources/WebAppTermsAlertController.swift @@ -25,9 +25,11 @@ private final class WebAppTermsAlertContentNode: AlertContentNode, UIGestureReco private let strings: PresentationStrings private let title: String private let text: String + private let additionalText: String? private let titleNode: ImmediateTextNode private let textNode: ImmediateTextNode + private let additionalTextNode: ImmediateTextNode private let acceptTermsCheckNode: InteractiveCheckNode private let acceptTermsLabelNode: ImmediateTextNode @@ -53,10 +55,11 @@ private final class WebAppTermsAlertContentNode: AlertContentNode, UIGestureReco var openTerms: () -> Void = {} - init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, title: String, text: String, actions: [TextAlertAction]) { + init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, title: String, text: String, additionalText: String?, actions: [TextAlertAction]) { self.strings = strings self.title = title self.text = text + self.additionalText = additionalText self.titleNode = ImmediateTextNode() self.titleNode.displaysAsynchronously = false @@ -69,6 +72,12 @@ private final class WebAppTermsAlertContentNode: AlertContentNode, UIGestureReco self.textNode.lineSpacing = 0.1 self.textNode.textAlignment = .center + self.additionalTextNode = ImmediateTextNode() + self.additionalTextNode.maximumNumberOfLines = 0 + self.additionalTextNode.displaysAsynchronously = false + self.additionalTextNode.lineSpacing = 0.1 + self.additionalTextNode.textAlignment = .center + self.acceptTermsCheckNode = InteractiveCheckNode(theme: CheckNodeTheme(backgroundColor: theme.accentColor, strokeColor: theme.contrastColor, borderColor: theme.controlBorderColor, overlayBorder: false, hasInset: false, hasShadow: false)) self.acceptTermsLabelNode = ImmediateTextNode() self.acceptTermsLabelNode.maximumNumberOfLines = 4 @@ -94,6 +103,7 @@ private final class WebAppTermsAlertContentNode: AlertContentNode, UIGestureReco self.addSubnode(self.titleNode) self.addSubnode(self.textNode) + self.addSubnode(self.additionalTextNode) self.addSubnode(self.acceptTermsCheckNode) self.addSubnode(self.acceptTermsLabelNode) @@ -179,6 +189,11 @@ private final class WebAppTermsAlertContentNode: AlertContentNode, UIGestureReco override func updateTheme(_ theme: AlertControllerTheme) { self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) self.textNode.attributedText = formattedText(self.text, fontSize: 13.0, color: theme.primaryColor, linkColor: theme.accentColor, textAlignment: .center) + if let additionalText = self.additionalText { + self.additionalTextNode.attributedText = formattedText(additionalText, fontSize: 13.0, color: theme.primaryColor, linkColor: theme.accentColor, textAlignment: .center) + } else { + self.additionalTextNode.attributedText = nil + } let attributedAgreeText = parseMarkdownIntoAttributedString( self.strings.WebApp_DisclaimerAgree, @@ -242,6 +257,14 @@ private final class WebAppTermsAlertContentNode: AlertContentNode, UIGestureReco transition.updateFrame(node: self.acceptTermsLabelNode, frame: CGRect(origin: CGPoint(x: acceptTermsOriginX + checkSize.width + spacing, y: origin.y), size: acceptTermsSize)) origin.y += acceptTermsSize.height entriesHeight += acceptTermsSize.height + origin.y += 21.0 + } + + let additionalTextSize = self.additionalTextNode.updateLayout(CGSize(width: size.width - 48.0, height: size.height)) + transition.updateFrame(node: self.additionalTextNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - additionalTextSize.width) / 2.0), y: origin.y), size: additionalTextSize)) + origin.y += additionalTextSize.height + if additionalTextSize.height > 0.0 { + entriesHeight += 20.0 } let actionButtonHeight: CGFloat = 44.0 @@ -275,7 +298,7 @@ private final class WebAppTermsAlertContentNode: AlertContentNode, UIGestureReco actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) } - let resultSize = CGSize(width: contentWidth, height: titleSize.height + textSize.height + entriesHeight + actionsHeight + 3.0 + insets.top + insets.bottom) + let resultSize = CGSize(width: contentWidth, height: titleSize.height + textSize.height + additionalTextSize.height + entriesHeight + actionsHeight + 3.0 + insets.top + insets.bottom) transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) @@ -350,9 +373,17 @@ public func webAppTermsAlertController( })] let title = presentationData.strings.WebApp_DisclaimerTitle - let text = presentationData.strings.WebApp_DisclaimerText(bot.peer.compactDisplayTitle).string + let text = presentationData.strings.WebApp_DisclaimerText + let additionalText: String? + if bot.flags.contains(.showInSettings) { + additionalText = presentationData.strings.WebApp_DisclaimerShortcutsSettingsText(bot.peer.compactDisplayTitle).string + } else if bot.flags.contains(.showInAttachMenu) { + additionalText = presentationData.strings.WebApp_DisclaimerShortcutsText(bot.peer.compactDisplayTitle).string + } else { + additionalText = nil + } - let contentNode = WebAppTermsAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, title: title, text: text, actions: actions) + let contentNode = WebAppTermsAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, title: title, text: text, additionalText: additionalText, actions: actions) contentNode.openTerms = { context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.WebApp_Disclaimer_URL, forceExternal: true, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: { })