Web app improvements

This commit is contained in:
Ilya Laktyushin 2023-09-08 15:52:11 +04:00
parent 3881bdff70
commit ada47a4be5
18 changed files with 248 additions and 215 deletions

View File

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

View File

@ -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<PresentationData, NoError>)?, 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

View File

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

View File

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

View File

@ -179,6 +179,7 @@ public class AttachmentController: ViewController {
private let context: AccountContext
private let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
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<PresentationData, NoError>)? = 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<PresentationData, NoError>)? = 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

View File

@ -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<PresentationData, NoError>)?, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) {
init(context: AccountContext, chatLocation: ChatLocation?, isScheduledMessages: Bool, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, 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)
}

View File

@ -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<PresentationData, NoError>)? = 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<PresentationData, NoError>)? = 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)

View File

@ -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<Bool, NoError>
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<Bool, NoError>
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

View File

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

View File

@ -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<Never, NoError> {
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

View File

@ -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<Never, NoError> {
return _internal_acceptAttachMenuBotDisclaimer(postbox: self.account.postbox, botId: botId)
}
public func getAttachMenuBot(botId: PeerId, cached: Bool = false) -> Signal<AttachMenuBot, GetAttachMenuBotError> {
return _internal_getAttachMenuBot(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, botId: botId, cached: cached)
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Never, NoError> { [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<String, RequestSimpleWebViewError> = 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

View File

@ -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<Int>?
// 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
}

View File

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