From 8446fd3ab1ddcda1e2fa51ab13c197f287cc9bd8 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 28 Feb 2024 19:22:43 +0400 Subject: [PATCH] Business fixes --- .../BrowserUI/Sources/BrowserScreen.swift | 1 + .../ChatListFilterPresetListItem.swift | 11 +- .../Sources/Node/ChatListItem.swift | 16 ++- .../Sources/ViewControllerComponent.swift | 7 + .../DrawingUI/Sources/DrawingScreen.swift | 1 + .../GalleryData/Sources/GalleryData.swift | 5 + .../ChatItemGalleryFooterContentNode.swift | 2 +- .../GalleryUI/Sources/GalleryController.swift | 15 +- .../Sources/ItemListAddressItem.swift | 9 +- .../Sources/LegacyAttachmentMenu.swift | 31 ++-- .../Sources/LegacyMediaPickerGallery.swift | 6 +- .../Sources/DeviceContactInfoController.swift | 2 +- .../Sources/ReplaceBoostScreen.swift | 1 + .../Sources/DateFormat.swift | 16 ++- .../CameraScreen/Sources/CameraScreen.swift | 1 + .../LegacyCamera/Sources/LegacyCamera.swift | 19 +-- .../Sources/MediaCutoutScreen.swift | 1 + .../Sources/MediaEditorScreen.swift | 1 + .../Sources/MediaToolsScreen.swift | 1 + .../Sources/SaveProgressScreen.swift | 1 + .../ListItems/PeerInfoScreenAddressItem.swift | 16 ++- .../PeerInfoScreenBusinessHoursItem.swift | 75 ++++++++-- .../Sources/PeerInfoScreen.swift | 39 ++++- ...aticBusinessMessageSetupChatContents.swift | 1 + .../AutomaticBusinessMessageSetupScreen.swift | 81 ++++++++++- .../QuickReplyEmptyStateComponent.swift | 6 +- .../Sources/QuickReplySetupScreen.swift | 9 +- .../Sources/BusinessHoursSetupScreen.swift | 4 +- .../QuickReplyNameAlertController.swift | 15 +- .../Sources/VideoMessageCameraScreen.swift | 1 + .../TelegramUI/Sources/ChatController.swift | 80 ++++++----- .../Sources/ChatControllerEditChat.swift | 1 + .../ChatControllerOpenAttachmentMenu.swift | 136 ++++++++++-------- .../ChatInterfaceStateContextQueries.swift | 7 +- .../TranslateUI/Sources/TranslateScreen.swift | 1 + 35 files changed, 448 insertions(+), 171 deletions(-) diff --git a/submodules/BrowserUI/Sources/BrowserScreen.swift b/submodules/BrowserUI/Sources/BrowserScreen.swift index 95e06060e3..1bfe441e3b 100644 --- a/submodules/BrowserUI/Sources/BrowserScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserScreen.swift @@ -618,6 +618,7 @@ public class BrowserScreen: ViewController { bottom: layout.intrinsicInsets.bottom + layout.safeInsets.bottom, right: layout.safeInsets.right ), + additionalInsets: layout.additionalInsets, inputHeight: layout.inputHeight ?? 0.0, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift index 70d2b0d5df..fc88aafcbb 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift @@ -411,13 +411,17 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN strongSelf.arrowNode.isHidden = item.isAllChats if let sharedIconImage = strongSelf.sharedIconNode.image { - strongSelf.sharedIconNode.frame = CGRect(origin: CGPoint(x: strongSelf.arrowNode.frame.minX + 2.0 - sharedIconImage.size.width, y: floorToScreenPixels((layout.contentSize.height - sharedIconImage.size.height) / 2.0) + 1.0), size: sharedIconImage.size) + var sharedIconFrame = CGRect(origin: CGPoint(x: strongSelf.arrowNode.frame.minX + 2.0 - sharedIconImage.size.width, y: floorToScreenPixels((layout.contentSize.height - sharedIconImage.size.height) / 2.0) + 1.0), size: sharedIconImage.size) + if item.tagColor != nil { + sharedIconFrame.origin.x -= 34.0 + } + strongSelf.sharedIconNode.frame = sharedIconFrame } var isShared = false if case let .filter(_, _, _, data) = item.preset, data.isShared { isShared = true } - strongSelf.sharedIconNode.isHidden = !isShared || item.tagColor != nil + strongSelf.sharedIconNode.isHidden = !isShared if let tagColor = item.tagColor { let tagIconView: UIImageView @@ -534,6 +538,9 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN var sharedIconFrame = self.sharedIconNode.frame sharedIconFrame.origin.x = arrowFrame.minX + 2.0 - sharedIconFrame.width + if self.item?.tagColor != nil { + sharedIconFrame.origin.x -= 34.0 + } transition.updateFrame(node: self.sharedIconNode, frame: sharedIconFrame) if let tagIconView = self.tagIconView { diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 19c729f770..f61565f5ca 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -2270,11 +2270,19 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } } else if inlineAuthorPrefix == nil, let draftState = draftState { hasDraft = true - authorAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Draft, font: textFont, textColor: theme.messageDraftTextColor) - let draftText = stringWithAppliedEntities(draftState.text, entities: draftState.entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, message: nil) - attributedText = foldLineBreaks(draftText) + if !itemTags.isEmpty { + let tempAttributedText = foldLineBreaks(draftText) + let attributedTextWithDraft = NSMutableAttributedString() + attributedTextWithDraft.append(NSAttributedString(string: item.presentationData.strings.DialogList_Draft + ": ", font: textFont, textColor: theme.messageDraftTextColor)) + attributedTextWithDraft.append(tempAttributedText) + attributedText = attributedTextWithDraft + } else { + authorAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Draft, font: textFont, textColor: theme.messageDraftTextColor) + + attributedText = foldLineBreaks(draftText) + } } else if let message = messages.first { var composedString: NSMutableAttributedString @@ -3856,7 +3864,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var badgeFrame: CGRect if textLayout.numberOfLines > 1 { - badgeFrame = CGRect(origin: CGPoint(x: textLayout.trailingLineWidth, y: textNodeFrame.height - 3.0 - badgeSize.height), size: badgeSize) + badgeFrame = CGRect(origin: CGPoint(x: textLayout.trailingLineWidth + 4.0, y: textNodeFrame.height - 3.0 - badgeSize.height), size: badgeSize) } else { let firstLineFrame = textLayout.linesRects().first ?? CGRect(origin: CGPoint(), size: textNodeFrame.size) badgeFrame = CGRect(origin: CGPoint(x: 0.0, y: firstLineFrame.height + 5.0), size: badgeSize) diff --git a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift index aedc76242e..d86b99280a 100644 --- a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift +++ b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift @@ -46,6 +46,7 @@ open class ViewControllerComponentContainer: ViewController { public let statusBarHeight: CGFloat public let navigationHeight: CGFloat public let safeInsets: UIEdgeInsets + public let additionalInsets: UIEdgeInsets public let inputHeight: CGFloat public let metrics: LayoutMetrics public let deviceMetrics: DeviceMetrics @@ -60,6 +61,7 @@ open class ViewControllerComponentContainer: ViewController { statusBarHeight: CGFloat, navigationHeight: CGFloat, safeInsets: UIEdgeInsets, + additionalInsets: UIEdgeInsets, inputHeight: CGFloat, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, @@ -73,6 +75,7 @@ open class ViewControllerComponentContainer: ViewController { self.statusBarHeight = statusBarHeight self.navigationHeight = navigationHeight self.safeInsets = safeInsets + self.additionalInsets = additionalInsets self.inputHeight = inputHeight self.metrics = metrics self.deviceMetrics = deviceMetrics @@ -98,6 +101,9 @@ open class ViewControllerComponentContainer: ViewController { if lhs.safeInsets != rhs.safeInsets { return false } + if lhs.additionalInsets != rhs.additionalInsets { + return false + } if lhs.inputHeight != rhs.inputHeight { return false } @@ -167,6 +173,7 @@ open class ViewControllerComponentContainer: ViewController { statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, safeInsets: UIEdgeInsets(top: layout.intrinsicInsets.top + layout.safeInsets.top, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom + layout.safeInsets.bottom, right: layout.safeInsets.right), + additionalInsets: layout.additionalInsets, inputHeight: layout.inputHeight ?? 0.0, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index b996d290e1..8ce1998d79 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -2634,6 +2634,7 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U bottom: layout.intrinsicInsets.bottom + layout.safeInsets.bottom, right: layout.safeInsets.right ), + additionalInsets: layout.additionalInsets, inputHeight: layout.inputHeight ?? 0.0, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, diff --git a/submodules/GalleryData/Sources/GalleryData.swift b/submodules/GalleryData/Sources/GalleryData.swift index 916c791d54..26087254b0 100644 --- a/submodules/GalleryData/Sources/GalleryData.swift +++ b/submodules/GalleryData/Sources/GalleryData.swift @@ -236,6 +236,11 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati }*/ } + var source = source + if standalone { + source = .standaloneMessage(message) + } + if internalDocumentItemSupportsMimeType(file.mimeType, fileName: file.fileName ?? "file") { let gallery = GalleryController(context: context, source: source ?? .peerMessagesAtId(messageId: message.id, chatLocation: chatLocation ?? .peer(id: message.id.peerId), customTag: chatFilterTag, chatLocationContextHolder: chatLocationContextHolder ?? Atomic(value: nil)), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in navigationController?.replaceTopController(controller, animated: false, ready: ready) diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index 62da3c1a45..d4fd0ad64c 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -822,7 +822,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll } var canDelete: Bool - var canShare = !message.containsSecretMedia + var canShare = !message.containsSecretMedia && !Namespaces.Message.allNonRegular.contains(message.id.namespace) var canFullscreen = false diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 131a2319b4..9b4817a56d 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -248,13 +248,18 @@ public func galleryItemForEntry( if let result = addLocallyGeneratedEntities(text, enabledTypes: [.timecode], entities: entities, mediaDuration: file.duration.flatMap(Double.init)) { entities = result } + + var originData = GalleryItemOriginData(title: message.effectiveAuthor.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), timestamp: message.timestamp) + if Namespaces.Message.allNonRegular.contains(message.id.namespace) { + originData = GalleryItemOriginData(title: nil, timestamp: nil) + } let caption = galleryCaptionStringWithAppliedEntities(context: context, text: text, entities: entities, message: message) return UniversalVideoGalleryItem( context: context, presentationData: presentationData, content: content, - originData: GalleryItemOriginData(title: message.effectiveAuthor.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), timestamp: message.timestamp), + originData: originData, indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: caption, @@ -348,11 +353,17 @@ public func galleryItemForEntry( } description = galleryCaptionStringWithAppliedEntities(context: context, text: descriptionText, entities: entities, message: message) } + + var originData = GalleryItemOriginData(title: message.effectiveAuthor.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), timestamp: message.timestamp) + if Namespaces.Message.allNonRegular.contains(message.id.namespace) { + originData = GalleryItemOriginData(title: nil, timestamp: nil) + } + return UniversalVideoGalleryItem( context: context, presentationData: presentationData, content: content, - originData: GalleryItemOriginData(title: message.effectiveAuthor.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), timestamp: message.timestamp), + originData: originData, indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), diff --git a/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift b/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift index 537134acd2..950b799e93 100644 --- a/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift +++ b/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift @@ -20,12 +20,12 @@ public final class ItemListAddressItem: ListViewItem, ItemListItem { let style: ItemListStyle let displayDecorations: Bool let action: (() -> Void)? - let longTapAction: (() -> Void)? + let longTapAction: ((ASDisplayNode, String) -> Void)? let linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)? public let tag: Any? - public init(theme: PresentationTheme, label: String, text: String, imageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?, selected: Bool? = nil, sectionId: ItemListSectionId, style: ItemListStyle, displayDecorations: Bool = true, action: (() -> Void)?, longTapAction: (() -> Void)? = nil, linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)? = nil, tag: Any? = nil) { + public init(theme: PresentationTheme, label: String, text: String, imageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?, selected: Bool? = nil, sectionId: ItemListSectionId, style: ItemListStyle, displayDecorations: Bool = true, action: (() -> Void)?, longTapAction: ((ASDisplayNode, String) -> Void)? = nil, linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)? = nil, tag: Any? = nil) { self.theme = theme self.label = label self.text = text @@ -396,7 +396,10 @@ public class ItemListAddressItemNode: ListViewItemNode { } override public func longTapped() { - self.item?.longTapAction?() + guard let item = self.item else { + return + } + item.longTapAction?(self, item.text) } public var tag: Any? { diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift index e6101f8cc4..6f668dfad3 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift @@ -222,7 +222,7 @@ public func legacyMediaEditor(context: AccountContext, peer: Peer, threadTitle: }) } -public func legacyAttachmentMenu(context: AccountContext, peer: Peer, threadTitle: String?, chatLocation: ChatLocation, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, hasSchedule: Bool, canSendPolls: Bool, updatedPresentationData: (initial: PresentationData, signal: Signal), parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: NSAttributedString, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, presentJpegConversionAlert: @escaping (@escaping (Bool) -> Void) -> Void, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32, ((String) -> UIView?)?, @escaping () -> Void) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, present: @escaping (ViewController, Any?) -> Void) -> TGMenuSheetController { +public func legacyAttachmentMenu(context: AccountContext, peer: Peer?, threadTitle: String?, chatLocation: ChatLocation, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, hasSchedule: Bool, canSendPolls: Bool, updatedPresentationData: (initial: PresentationData, signal: Signal), parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: NSAttributedString, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, presentJpegConversionAlert: @escaping (@escaping (Bool) -> Void) -> Void, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32, ((String) -> UIView?)?, @escaping () -> Void) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, present: @escaping (ViewController, Any?) -> Void) -> TGMenuSheetController { let defaultVideoPreset = defaultVideoPresetForContext(context) UserDefaults.standard.set(defaultVideoPreset.rawValue as NSNumber, forKey: "TG_preferredVideoPreset_v0") @@ -230,18 +230,20 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, threadTitl let recipientName: String if let threadTitle { recipientName = threadTitle - } else { + } else if let peer { if peer.id == context.account.peerId { recipientName = presentationData.strings.DialogList_SavedMessages } else { recipientName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) } + } else { + recipientName = "" } let actionSheetTheme = ActionSheetControllerTheme(presentationData: presentationData) let fontSize = floor(actionSheetTheme.baseFontSize * 20.0 / 17.0) - let isSecretChat = peer.id.namespace == Namespaces.Peer.SecretChat + let isSecretChat = peer?.id.namespace == Namespaces.Peer.SecretChat let controller = TGMenuSheetController(context: parentController.context, dark: false)! controller.dismissesByOutsideTap = true @@ -314,14 +316,14 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, threadTitl carouselItem.selectionLimitExceeded = { presentSelectionLimitExceeded() } - if peer.id != context.account.peerId { + if let peer, peer.id != context.account.peerId { if peer is TelegramUser { carouselItem.hasTimer = hasSchedule } carouselItem.hasSilentPosting = true } carouselItem.hasSchedule = hasSchedule - carouselItem.reminder = peer.id == context.account.peerId + carouselItem.reminder = peer?.id == context.account.peerId carouselItem.presentScheduleController = { media, done in presentSchedulePicker(media, { time in done?(time) @@ -449,7 +451,12 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, threadTitl navigationController.setNavigationBarHidden(true, animated: false) legacyController.bind(controller: navigationController) - let recipientName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + let recipientName: String + if let peer { + recipientName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + } else { + recipientName = "" + } legacyController.enableSizeClassSignal = true @@ -489,12 +496,14 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, threadTitl itemViews.append(locationItem) var peerSupportsPolls = false - if peer is TelegramGroup || peer is TelegramChannel { - peerSupportsPolls = true - } else if let user = peer as? TelegramUser, let _ = user.botInfo { - peerSupportsPolls = true + if let peer { + if peer is TelegramGroup || peer is TelegramChannel { + peerSupportsPolls = true + } else if let user = peer as? TelegramUser, let _ = user.botInfo { + peerSupportsPolls = true + } } - if peerSupportsPolls && canSendMessagesToPeer(peer) && canSendPolls { + if let peer, peerSupportsPolls, canSendMessagesToPeer(peer) && canSendPolls { let pollItem = TGMenuSheetButtonItemView(title: presentationData.strings.AttachmentMenu_Poll, type: TGMenuSheetButtonTypeDefault, fontSize: fontSize, action: { [weak controller] in controller?.dismiss(animated: true) openPoll() diff --git a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift index a2495febd0..d1a9f17cae 100644 --- a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift +++ b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift @@ -224,7 +224,7 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?, }) } } - if !isScheduledMessages { + if !isScheduledMessages && peer != nil { 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 @@ -269,8 +269,8 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?, } let _ = (sendWhenOnlineAvailable - |> take(1) - |> deliverOnMainQueue).start(next: { sendWhenOnlineAvailable in + |> 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: hasTimer) let dismissImpl = { [weak model] in diff --git a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift index 777edd846f..67827a24f1 100644 --- a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift +++ b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift @@ -510,7 +510,7 @@ private enum DeviceContactInfoEntry: ItemListNodeEntry { } else { arguments.openAddress(value) } - }, longTapAction: { + }, longTapAction: { _, _ in if selected == nil { arguments.displayCopyContextMenu(.info(index), string) } diff --git a/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift b/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift index 973e4d3fb4..3f596f9ca9 100644 --- a/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift +++ b/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift @@ -477,6 +477,7 @@ public class ReplaceBoostScreen: ViewController { statusBarHeight: 0.0, navigationHeight: navigationHeight, safeInsets: UIEdgeInsets(top: layout.intrinsicInsets.top + layout.safeInsets.top, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom + layout.safeInsets.bottom, right: layout.safeInsets.right), + additionalInsets: layout.additionalInsets, inputHeight: layout.inputHeight ?? 0.0, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, diff --git a/submodules/TelegramStringFormatting/Sources/DateFormat.swift b/submodules/TelegramStringFormatting/Sources/DateFormat.swift index 54759b97c7..fc3e439fa5 100644 --- a/submodules/TelegramStringFormatting/Sources/DateFormat.swift +++ b/submodules/TelegramStringFormatting/Sources/DateFormat.swift @@ -2,7 +2,7 @@ import Foundation import TelegramPresentationData import TelegramUIPreferences -public func stringForShortTimestamp(hours: Int32, minutes: Int32, dateTimeFormat: PresentationDateTimeFormat) -> String { +public func stringForShortTimestamp(hours: Int32, minutes: Int32, dateTimeFormat: PresentationDateTimeFormat, formatAsPlainText: Bool = false) -> String { switch dateTimeFormat.timeFormat { case .regular: let hourString: String @@ -20,10 +20,18 @@ public func stringForShortTimestamp(hours: Int32, minutes: Int32, dateTimeFormat } else { periodString = "AM" } - if minutes >= 10 { - return "\(hourString):\(minutes)\u{00a0}\(periodString)" + + let spaceCharacter: String + if formatAsPlainText { + spaceCharacter = " " } else { - return "\(hourString):0\(minutes)\u{00a0}\(periodString)" + spaceCharacter = "\u{00a0}" + } + + if minutes >= 10 { + return "\(hourString):\(minutes)\(spaceCharacter)\(periodString)" + } else { + return "\(hourString):0\(minutes)\(spaceCharacter)\(periodString)" } case .military: return String(format: "%02d:%02d", arguments: [Int(hours), Int(minutes)]) diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index 84de31e778..0eb3f6033e 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -2384,6 +2384,7 @@ public class CameraScreen: ViewController { bottom: bottomInset, right: layout.safeInsets.right ), + additionalInsets: layout.additionalInsets, inputHeight: layout.inputHeight ?? 0.0, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, diff --git a/submodules/TelegramUI/Components/LegacyCamera/Sources/LegacyCamera.swift b/submodules/TelegramUI/Components/LegacyCamera/Sources/LegacyCamera.swift index a9c14e92e5..17eccfc3ea 100644 --- a/submodules/TelegramUI/Components/LegacyCamera/Sources/LegacyCamera.swift +++ b/submodules/TelegramUI/Components/LegacyCamera/Sources/LegacyCamera.swift @@ -10,7 +10,7 @@ import ShareController import LegacyUI import LegacyMediaPickerUI -public func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocation: ChatLocation, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, attachmentController: ViewController? = nil, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: NSAttributedString, hasSchedule: Bool, enablePhoto: Bool, enableVideo: Bool, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, dismissedWithResult: @escaping () -> Void = {}, finishedTransitionIn: @escaping () -> Void = {}) { +public func presentedLegacyCamera(context: AccountContext, peer: Peer?, chatLocation: ChatLocation, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, attachmentController: ViewController? = nil, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: NSAttributedString, hasSchedule: Bool, enablePhoto: Bool, enableVideo: Bool, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, dismissedWithResult: @escaping () -> Void = {}, finishedTransitionIn: @escaping () -> Void = {}) { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme) legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait) @@ -18,7 +18,7 @@ public func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocat legacyController.deferScreenEdgeGestures = [.top] - let isSecretChat = peer.id.namespace == Namespaces.Peer.SecretChat + let isSecretChat = peer?.id.namespace == Namespaces.Peer.SecretChat let controller: TGCameraController if let cameraView = cameraView, let previewView = cameraView.previewView() { @@ -87,15 +87,18 @@ public func presentedLegacyCamera(context: AccountContext, peer: Peer, chatLocat controller.allowCaptionEntities = true controller.allowGrouping = mediaGrouping controller.inhibitDocumentCaptions = false - controller.recipientName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - if peer.id != context.account.peerId { - if peer is TelegramUser { - controller.hasTimer = hasSchedule + + if let peer { + controller.recipientName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + if peer.id != context.account.peerId { + if peer is TelegramUser { + controller.hasTimer = hasSchedule + } + controller.hasSilentPosting = true } - controller.hasSilentPosting = true } controller.hasSchedule = hasSchedule - controller.reminder = peer.id == context.account.peerId + controller.reminder = peer?.id == context.account.peerId let screenSize = parentController.view.bounds.size var startFrame = CGRect(x: 0, y: screenSize.height, width: screenSize.width, height: screenSize.height) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift index 2e411f9cf7..714d247480 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift @@ -267,6 +267,7 @@ public final class MediaCutoutScreen: ViewController { bottom: bottomInset, right: layout.safeInsets.right ), + additionalInsets: layout.additionalInsets, inputHeight: layout.inputHeight ?? 0.0, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 2f078dad8e..0c82521387 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -3998,6 +3998,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate bottom: bottomInset, right: layout.safeInsets.right ), + additionalInsets: layout.additionalInsets, inputHeight: layoutInputHeight, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaToolsScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaToolsScreen.swift index 250f26efe5..11f3086dcb 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaToolsScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaToolsScreen.swift @@ -1031,6 +1031,7 @@ public final class MediaToolsScreen: ViewController { bottom: bottomInset, right: layout.safeInsets.right ), + additionalInsets: layout.additionalInsets, inputHeight: layout.inputHeight ?? 0.0, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/SaveProgressScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/SaveProgressScreen.swift index 7de762ec79..2aeb3f1dfa 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/SaveProgressScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/SaveProgressScreen.swift @@ -448,6 +448,7 @@ public final class SaveProgressScreen: ViewController { bottom: topInset, right: layout.safeInsets.right ), + additionalInsets: layout.additionalInsets, inputHeight: layout.inputHeight ?? 0.0, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenAddressItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenAddressItem.swift index de95db7203..c604282113 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenAddressItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenAddressItem.swift @@ -13,7 +13,7 @@ final class PeerInfoScreenAddressItem: PeerInfoScreenItem { let text: String let imageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? let action: (() -> Void)? - let longTapAction: (() -> Void)? + let longTapAction: ((ASDisplayNode, String) -> Void)? let linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)? init( @@ -22,7 +22,7 @@ final class PeerInfoScreenAddressItem: PeerInfoScreenItem { text: String, imageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?, action: (() -> Void)?, - longTapAction: (() -> Void)? = nil, + longTapAction: ((ASDisplayNode, String) -> Void)? = nil, linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)? = nil ) { self.id = id @@ -66,6 +66,16 @@ private final class PeerInfoScreenAddressItemNode: PeerInfoScreenItemNode { self.addSubnode(self.bottomSeparatorNode) self.addSubnode(self.selectionNode) self.addSubnode(self.maskNode) + + self.view.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.longPressGesture(_:)))) + } + + @objc private func longPressGesture(_ recognizer: UILongPressGestureRecognizer) { + if case .began = recognizer.state { + if let item = self.item { + item.longTapAction?(self, item.text) + } + } } override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, hasCorners: Bool, transition: ContainedViewLayoutTransition) -> CGFloat { @@ -81,7 +91,7 @@ private final class PeerInfoScreenAddressItemNode: PeerInfoScreenItemNode { self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor - let addressItem = ItemListAddressItem(theme: presentationData.theme, label: item.label, text: item.text, imageSignal: item.imageSignal, sectionId: 0, style: .blocks, displayDecorations: false, action: nil, longTapAction: item.longTapAction, linkItemAction: item.linkItemAction) + let addressItem = ItemListAddressItem(theme: presentationData.theme, label: item.label, text: item.text, imageSignal: item.imageSignal, sectionId: 0, style: .blocks, displayDecorations: false, action: nil, longTapAction: nil, linkItemAction: item.linkItemAction) let params = ListViewItemLayoutParams(width: width, leftInset: safeInsets.left, rightInset: safeInsets.right, availableHeight: 1000.0) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift index efdb3850fa..166c39df8f 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift @@ -13,7 +13,7 @@ import MultilineTextComponent import BundleIconComponent import PlainButtonComponent -private func dayBusinessHoursText(_ day: TelegramBusinessHours.WeekDay, offsetMinutes: Int) -> String { +private func dayBusinessHoursText(presentationData: PresentationData, day: TelegramBusinessHours.WeekDay, offsetMinutes: Int, formatAsPlainText: Bool = false) -> String { var businessHoursText: String = "" switch day { case .open: @@ -30,14 +30,18 @@ private func dayBusinessHoursText(_ day: TelegramBusinessHours.WeekDay, offsetMi let range = TelegramBusinessHours.WorkingTimeInterval(startMinute: range.startMinute + offsetMinutes, endMinute: range.endMinute + offsetMinutes) if !resultText.isEmpty { - resultText.append("\n") + if formatAsPlainText { + resultText.append(", ") + } else { + resultText.append("\n") + } } let startHours = clipMinutes(range.startMinute) / 60 let startMinutes = clipMinutes(range.startMinute) % 60 - let startText = stringForShortTimestamp(hours: Int32(startHours), minutes: Int32(startMinutes), dateTimeFormat: PresentationDateTimeFormat()) + let startText = stringForShortTimestamp(hours: Int32(startHours), minutes: Int32(startMinutes), dateTimeFormat: presentationData.dateTimeFormat, formatAsPlainText: formatAsPlainText) let endHours = clipMinutes(range.endMinute) / 60 let endMinutes = clipMinutes(range.endMinute) % 60 - let endText = stringForShortTimestamp(hours: Int32(endHours), minutes: Int32(endMinutes), dateTimeFormat: PresentationDateTimeFormat()) + let endText = stringForShortTimestamp(hours: Int32(endHours), minutes: Int32(endMinutes), dateTimeFormat: presentationData.dateTimeFormat, formatAsPlainText: formatAsPlainText) resultText.append("\(startText) - \(endText)") } businessHoursText += resultText @@ -51,17 +55,20 @@ final class PeerInfoScreenBusinessHoursItem: PeerInfoScreenItem { let label: String let businessHours: TelegramBusinessHours let requestLayout: (Bool) -> Void + let longTapAction: (ASDisplayNode, String) -> Void init( id: AnyHashable, label: String, businessHours: TelegramBusinessHours, - requestLayout: @escaping (Bool) -> Void + requestLayout: @escaping (Bool) -> Void, + longTapAction: @escaping (ASDisplayNode, String) -> Void ) { self.id = id self.label = label self.businessHours = businessHours self.requestLayout = requestLayout + self.longTapAction = longTapAction } func node() -> PeerInfoScreenItemNode { @@ -92,6 +99,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode private let activateArea: AccessibilityAreaNode private var item: PeerInfoScreenBusinessHoursItem? + private var presentationData: PresentationData? private var theme: PresentationTheme? private var currentTimezone: TimeZone @@ -168,8 +176,8 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode super.didLoad() let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:))) - recognizer.tapActionAtPoint = { point in - return .keepWithSingleTap + recognizer.tapActionAtPoint = { _ in + return .waitForSingleTap } recognizer.highlight = { [weak self] point in guard let strongSelf = self else { @@ -185,9 +193,55 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode case .ended: if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation { switch gesture { - case .tap, .longTap: + case .tap: self.isExpanded = !self.isExpanded self.item?.requestLayout(true) + case .longTap: + if let item = self.item, let presentationData = self.presentationData { + var text = "" + + var timezoneOffsetMinutes: Int = 0 + if self.displayLocalTimezone { + var currentCalendar = Calendar(identifier: .gregorian) + currentCalendar.timeZone = TimeZone(identifier: item.businessHours.timezoneId) ?? TimeZone.current + + timezoneOffsetMinutes = (self.currentTimezone.secondsFromGMT() - currentCalendar.timeZone.secondsFromGMT()) / 60 + } + + let businessDays: [TelegramBusinessHours.WeekDay] = self.cachedDays + + for i in 0 ..< businessDays.count { + let dayTitleValue: String + //TODO:localize + switch i { + case 0: + dayTitleValue = "Monday" + case 1: + dayTitleValue = "Tuesday" + case 2: + dayTitleValue = "Wednesday" + case 3: + dayTitleValue = "Thursday" + case 4: + dayTitleValue = "Friday" + case 5: + dayTitleValue = "Saturday" + case 6: + dayTitleValue = "Sunday" + default: + dayTitleValue = " " + } + + let businessHoursText = dayBusinessHoursText(presentationData: presentationData, day: businessDays[i], offsetMinutes: timezoneOffsetMinutes, formatAsPlainText: true) + + if !text.isEmpty { + text.append("\n") + } + text.append("\(dayTitleValue): \(businessHoursText)") + } + + item.longTapAction(self, text) + } default: break } @@ -212,6 +266,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode } self.item = item + self.presentationData = presentationData self.theme = presentationData.theme let sideInset: CGFloat = 16.0 + safeInsets.left @@ -272,7 +327,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode //TODO:localize let openStatusText = isOpen ? "Open" : "Closed" - var currentDayStatusText = currentDayIndex >= 0 && currentDayIndex < businessDays.count ? dayBusinessHoursText(businessDays[currentDayIndex], offsetMinutes: timezoneOffsetMinutes) : " " + var currentDayStatusText = currentDayIndex >= 0 && currentDayIndex < businessDays.count ? dayBusinessHoursText(presentationData: presentationData, day: businessDays[currentDayIndex], offsetMinutes: timezoneOffsetMinutes) : " " if !isOpen { for range in self.cachedWeekMinuteSet.rangeView { @@ -465,7 +520,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode dayTitleValue = " " } - let businessHoursText = dayBusinessHoursText(businessDays[i], offsetMinutes: timezoneOffsetMinutes) + let businessHoursText = dayBusinessHoursText(presentationData: presentationData, day: businessDays[i], offsetMinutes: timezoneOffsetMinutes) let dayTitleSize = dayTitle.update( transition: .immediate, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 76c9930e79..1db0cbd1eb 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -481,6 +481,8 @@ private enum PeerInfoContextSubject { case bio case phone(String) case link(customLink: String?) + case businessHours(String) + case businessLocation(String) } private enum PeerInfoSettingsSection { @@ -1168,6 +1170,10 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese //TODO:localize items[.peerInfo]!.append(PeerInfoScreenBusinessHoursItem(id: 300, label: "business hours", businessHours: businessHours, requestLayout: { animated in interaction.requestLayout(animated) + }, longTapAction: { sourceNode, text in + if !text.isEmpty { + interaction.openPeerInfoContextMenu(.businessHours(text), sourceNode, nil) + } })) } @@ -1182,6 +1188,11 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese imageSignal: imageSignal, action: { interaction.openLocation() + }, + longTapAction: { sourceNode, text in + if !text.isEmpty { + interaction.openPeerInfoContextMenu(.businessLocation(text), sourceNode, nil) + } } )) } else { @@ -1190,7 +1201,12 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese label: "location", text: businessLocation.address, imageSignal: nil, - action: nil + action: nil, + longTapAction: { sourceNode, text in + if !text.isEmpty { + interaction.openPeerInfoContextMenu(.businessLocation(text), sourceNode, nil) + } + } )) } } @@ -7879,6 +7895,27 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return nil } })) + case .businessHours(let text), .businessLocation(let text): + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + let actions: [ContextMenuAction] = [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in + UIPasteboard.general.string = text + + self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + })] + + let contextMenuController = makeContextMenuController(actions: actions) + controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in + if let controller = self?.controller, let sourceNode = sourceNode { + var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0) + if let sourceRect = sourceRect { + rect = sourceRect.insetBy(dx: 0.0, dy: 2.0) + } + return (sourceNode, rect, controller.displayNode, controller.view.bounds) + } else { + return nil + } + })) } } diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupChatContents.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupChatContents.swift index d4d24fd7e1..3fae6b81b8 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupChatContents.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupChatContents.swift @@ -154,6 +154,7 @@ final class AutomaticBusinessMessageSetupChatContents: ChatCustomContentsProtoco } func quickReplyUpdateShortcut(value: String) { + self.shortcut = value if let shortcutId = self.shortcutId { self.context.engine.accountData.editMessageShortcut(id: shortcutId, shortcut: value) } diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift index 37ec12c4b4..de02d36ed0 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift @@ -188,6 +188,53 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { return true } + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + + if self.isOn { + if self.hasAccessToAllChatsByDefault && self.additionalPeerList.categories.isEmpty && self.additionalPeerList.peers.isEmpty { + //TODO:localize + self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: "No recipients selected. Reset?", actions: [ + TextAlertAction(type: .genericAction, title: "Cancel", action: { + }), + TextAlertAction(type: .defaultAction, title: "Reset", action: { + complete() + }) + ]), in: .window(.root)) + + return false + } + + if case .away = component.mode, case .custom = self.schedule { + //TODO:localize + var errorText: String? + if let customScheduleStart = self.customScheduleStart, let customScheduleEnd = self.customScheduleEnd { + if customScheduleStart >= customScheduleEnd { + errorText = "Custom schedule end time must be larger than start time." + } + } else { + if self.customScheduleStart == nil && self.customScheduleEnd == nil { + errorText = "Custom schedule time is missing." + } else if self.customScheduleStart == nil { + errorText = "Custom schedule start time is missing." + } else { + errorText = "Custom schedule end time is missing." + } + } + + if let errorText { + //TODO:localize + self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [ + TextAlertAction(type: .genericAction, title: "Cancel", action: { + }), + TextAlertAction(type: .defaultAction, title: "Reset", action: { + complete() + }) + ]), in: .window(.root)) + return false + } + } + } + var mappedCategories: TelegramBusinessRecipients.Categories = [] if self.additionalPeerList.categories.contains(.existingChats) { mappedCategories.insert(.existingChats) @@ -555,6 +602,21 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { if let awayMessage = component.initialData.awayMessage { self.isOn = true initialRecipients = awayMessage.recipients + + switch awayMessage.schedule { + case .always: + self.schedule = .always + case let .custom(beginTimestamp, endTimestamp): + self.schedule = .custom + self.customScheduleStart = Date(timeIntervalSince1970: Double(beginTimestamp)) + self.customScheduleEnd = Date(timeIntervalSince1970: Double(endTimestamp)) + case .outsideWorkingHours: + if component.initialData.businessHours != nil { + self.schedule = .outsideBusinessHours + } else { + self.schedule = .always + } + } } } @@ -839,7 +901,7 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { if case .away = component.mode { //TODO:localize var scheduleSectionItems: [AnyComponentWithIdentity] = [] - for i in 0 ..< 3 { + optionLoop: for i in 0 ..< 3 { let title: String let schedule: Schedule switch i { @@ -847,6 +909,10 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { title = "Always Send" schedule = .always case 1: + if component.initialData.businessHours == nil { + continue optionLoop + } + title = "Outside of Business Hours" schedule = .outsideBusinessHours default: @@ -1396,19 +1462,22 @@ public final class AutomaticBusinessMessageSetupScreen: ViewControllerComponentC fileprivate let greetingMessage: TelegramBusinessGreetingMessage? fileprivate let awayMessage: TelegramBusinessAwayMessage? fileprivate let additionalPeers: [EnginePeer.Id: AutomaticBusinessMessageSetupScreenComponent.AdditionalPeerList.Peer] + fileprivate let businessHours: TelegramBusinessHours? fileprivate init( accountPeer: EnginePeer?, shortcutMessageList: ShortcutMessageList, greetingMessage: TelegramBusinessGreetingMessage?, awayMessage: TelegramBusinessAwayMessage?, - additionalPeers: [EnginePeer.Id: AutomaticBusinessMessageSetupScreenComponent.AdditionalPeerList.Peer] + additionalPeers: [EnginePeer.Id: AutomaticBusinessMessageSetupScreenComponent.AdditionalPeerList.Peer], + businessHours: TelegramBusinessHours? ) { self.accountPeer = accountPeer self.shortcutMessageList = shortcutMessageList self.greetingMessage = greetingMessage self.awayMessage = awayMessage self.additionalPeers = additionalPeers + self.businessHours = businessHours } } @@ -1468,13 +1537,14 @@ public final class AutomaticBusinessMessageSetupScreen: ViewControllerComponentC context.engine.data.get( TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId), TelegramEngine.EngineData.Item.Peer.BusinessGreetingMessage(id: context.account.peerId), - TelegramEngine.EngineData.Item.Peer.BusinessAwayMessage(id: context.account.peerId) + TelegramEngine.EngineData.Item.Peer.BusinessAwayMessage(id: context.account.peerId), + TelegramEngine.EngineData.Item.Peer.BusinessHours(id: context.account.peerId) ), context.engine.accountData.shortcutMessageList() |> take(1) ) |> mapToSignal { data, shortcutMessageList -> Signal in - let (accountPeer, greetingMessage, awayMessage) = data + let (accountPeer, greetingMessage, awayMessage, businessHours) = data var additionalPeerIds = Set() if let greetingMessage { @@ -1505,7 +1575,8 @@ public final class AutomaticBusinessMessageSetupScreen: ViewControllerComponentC shortcutMessageList: shortcutMessageList, greetingMessage: greetingMessage, awayMessage: awayMessage, - additionalPeers: additionalPeers + additionalPeers: additionalPeers, + businessHours: businessHours ) } } diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplyEmptyStateComponent.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplyEmptyStateComponent.swift index f0a9d10fca..41372475cf 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplyEmptyStateComponent.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplyEmptyStateComponent.swift @@ -109,7 +109,7 @@ final class QuickReplyEmptyStateComponent: Component { transition: .immediate, component: AnyComponent(LottieComponent( content: LottieComponent.AppBundleContent(name: "WriteEmoji"), - loop: true + loop: false )), environment: {}, containerSize: CGSize(width: 120.0, height: 120.0) @@ -144,9 +144,11 @@ final class QuickReplyEmptyStateComponent: Component { var centralContentsY: CGFloat = topInset + floor((buttonFrame.minY - topInset - centralContentsHeight) * 0.426) let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: centralContentsY), size: iconSize) - if let iconView = self.icon.view { + + if let iconView = self.icon.view as? LottieComponent.View { if iconView.superview == nil { self.addSubview(iconView) + iconView.playOnce() } transition.setFrame(view: iconView, frame: iconFrame) } diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift index 3dbc6c29a1..d205f89469 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift @@ -590,6 +590,7 @@ final class QuickReplySetupScreenComponent: Component { return } + alertController?.view.endEditing(true) alertController?.dismissAnimated() self.openQuickReplyChat(shortcut: value, shortcutId: nil) } @@ -638,6 +639,7 @@ final class QuickReplySetupScreenComponent: Component { } else { component.context.engine.accountData.editMessageShortcut(id: id, shortcut: value) + alertController?.view.endEditing(true) alertController?.dismissAnimated() } } @@ -923,7 +925,7 @@ final class QuickReplySetupScreenComponent: Component { component: AnyComponent(QuickReplyEmptyStateComponent( theme: environment.theme, strings: environment.strings, - insets: UIEdgeInsets(top: environment.navigationHeight, left: environment.safeInsets.left, bottom: environment.safeInsets.bottom, right: environment.safeInsets.right), + insets: UIEdgeInsets(top: environment.navigationHeight, left: environment.safeInsets.left, bottom: environment.safeInsets.bottom + environment.additionalInsets.bottom, right: environment.safeInsets.right), action: { [weak self] in guard let self else { return @@ -955,7 +957,7 @@ final class QuickReplySetupScreenComponent: Component { statusBarHeight = max(statusBarHeight, 1.0) } - var listBottomInset = environment.safeInsets.bottom + var listBottomInset = environment.safeInsets.bottom + environment.additionalInsets.bottom let navigationHeight = self.updateNavigationBar( component: component, theme: environment.theme, @@ -1090,12 +1092,13 @@ final class QuickReplySetupScreenComponent: Component { animateScale: false, animateContents: false ))), - insets: UIEdgeInsets(top: 4.0, left: environment.safeInsets.left, bottom: environment.safeInsets.bottom, right: environment.safeInsets.right) + insets: UIEdgeInsets(top: 4.0, left: environment.safeInsets.left, bottom: environment.safeInsets.bottom + environment.additionalInsets.bottom, right: environment.safeInsets.right) )), environment: {}, containerSize: availableSize ) let selectionPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - selectionPanelSize.height), size: selectionPanelSize) + print("selectionPanelFrame: \(selectionPanelFrame.minY)") listBottomInset = selectionPanelSize.height if let selectionPanelView = selectionPanel.view { var animateIn = false diff --git a/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift b/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift index 334ac82839..7db651f4f2 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift @@ -304,9 +304,7 @@ final class BusinessHoursSetupScreenComponent: Component { let businessHours = try self.daysState.asBusinessHours() let _ = component.context.engine.accountData.updateAccountBusinessHours(businessHours: businessHours).startStandalone() return true - } catch let error { - let _ = error - + } catch _ { let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } //TODO:localize self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: "Business hours are intersecting. Reset?", actions: [ diff --git a/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift b/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift index 6779c39757..1d3e282c89 100644 --- a/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift +++ b/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift @@ -225,11 +225,7 @@ public final class QuickReplyNameAlertContentNode: AlertContentNode { private let hapticFeedback = HapticFeedback() - var complete: (() -> Void)? { - didSet { - self.inputFieldNode.complete = self.complete - } - } + var complete: (() -> Void)? override public var dismissOnOutsideTap: Bool { return self.isUserInteractionEnabled @@ -295,6 +291,15 @@ public final class QuickReplyNameAlertContentNode: AlertContentNode { } self.updateTheme(theme) + + self.inputFieldNode.complete = { [weak self] in + guard let self else { + return + } + if let lastNode = self.actionNodes.last, lastNode.actionEnabled { + self.complete?() + } + } } deinit { diff --git a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift index 850d7d4883..7f8af9e065 100644 --- a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift +++ b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift @@ -1094,6 +1094,7 @@ public class VideoMessageCameraScreen: ViewController { bottom: 44.0, right: layout.safeInsets.right ), + additionalInsets: layout.additionalInsets, inputHeight: layout.inputHeight ?? 0.0, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 0ed8b8b15d..e40ab50ba2 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -9494,15 +9494,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } + self.updateChatPresentationInterfaceState(animated: true, interactive: false, { + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))).withUpdatedComposeDisableUrlPreviews([]) } + }) + if !self.presentationInterfaceState.isPremium { let controller = PremiumIntroScreen(context: self.context, source: .settings) self.push(controller) return } - self.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))).withUpdatedComposeDisableUrlPreviews([]) } - }) self.context.engine.accountData.sendMessageShortcut(peerId: peerId, id: shortcutId) /*self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in @@ -9906,37 +9907,36 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } - guard let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { - return - } var bannedMediaInput = false - if let channel = peer as? TelegramChannel { - if channel.hasBannedPermission(.banSendVoice) != nil && channel.hasBannedPermission(.banSendInstantVideos) != nil { - bannedMediaInput = true - } else if channel.hasBannedPermission(.banSendVoice) != nil { - if channel.hasBannedPermission(.banSendInstantVideos) == nil { - strongSelf.displayMediaRecordingTooltip() - return + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { + if let channel = peer as? TelegramChannel { + if channel.hasBannedPermission(.banSendVoice) != nil && channel.hasBannedPermission(.banSendInstantVideos) != nil { + bannedMediaInput = true + } else if channel.hasBannedPermission(.banSendVoice) != nil { + if channel.hasBannedPermission(.banSendInstantVideos) == nil { + strongSelf.displayMediaRecordingTooltip() + return + } + } else if channel.hasBannedPermission(.banSendInstantVideos) != nil { + if channel.hasBannedPermission(.banSendVoice) == nil { + strongSelf.displayMediaRecordingTooltip() + return + } } - } else if channel.hasBannedPermission(.banSendInstantVideos) != nil { - if channel.hasBannedPermission(.banSendVoice) == nil { - strongSelf.displayMediaRecordingTooltip() - return - } - } - } else if let group = peer as? TelegramGroup { - if group.hasBannedPermission(.banSendVoice) && group.hasBannedPermission(.banSendInstantVideos) { - bannedMediaInput = true - } else if group.hasBannedPermission(.banSendVoice) { - if !group.hasBannedPermission(.banSendInstantVideos) { - strongSelf.displayMediaRecordingTooltip() - return - } - } else if group.hasBannedPermission(.banSendInstantVideos) { - if !group.hasBannedPermission(.banSendVoice) { - strongSelf.displayMediaRecordingTooltip() - return + } else if let group = peer as? TelegramGroup { + if group.hasBannedPermission(.banSendVoice) && group.hasBannedPermission(.banSendInstantVideos) { + bannedMediaInput = true + } else if group.hasBannedPermission(.banSendVoice) { + if !group.hasBannedPermission(.banSendInstantVideos) { + strongSelf.displayMediaRecordingTooltip() + return + } + } else if group.hasBannedPermission(.banSendInstantVideos) { + if !group.hasBannedPermission(.banSendVoice) { + strongSelf.displayMediaRecordingTooltip() + return + } } } } @@ -14202,10 +14202,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } func requestVideoRecorder() { - guard let peerId = self.chatLocation.peerId else { - return - } - if self.videoRecorderValue == nil { if let currentInputPanelFrame = self.chatDisplayNode.currentInputPanelFrame() { if self.recorderFeedback == nil { @@ -14219,6 +14215,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } var isBot = false + + var allowLiveUpload = false + var viewOnceAvailable = false + if let peerId = self.chatLocation.peerId { + allowLiveUpload = peerId.namespace != Namespaces.Peer.SecretChat + viewOnceAvailable = !isScheduledMessages && peerId.namespace == Namespaces.Peer.CloudUser && peerId != self.context.account.peerId && !isBot + } else if case .customChatContents = self.chatLocation { + allowLiveUpload = true + } + if let user = self.presentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.botInfo != nil { isBot = true } @@ -14226,8 +14232,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let controller = VideoMessageCameraScreen( context: self.context, updatedPresentationData: self.updatedPresentationData, - allowLiveUpload: peerId.namespace != Namespaces.Peer.SecretChat, - viewOnceAvailable: !isScheduledMessages && peerId.namespace == Namespaces.Peer.CloudUser && peerId != self.context.account.peerId && !isBot, + allowLiveUpload: allowLiveUpload, + viewOnceAvailable: viewOnceAvailable, inputPanelFrame: (currentInputPanelFrame, self.chatDisplayNode.inputNode != nil), chatNode: self.chatDisplayNode.historyNode, completion: { [weak self] message, silentPosting, scheduleTime in diff --git a/submodules/TelegramUI/Sources/ChatControllerEditChat.swift b/submodules/TelegramUI/Sources/ChatControllerEditChat.swift index a9c0c525e2..2df490c037 100644 --- a/submodules/TelegramUI/Sources/ChatControllerEditChat.swift +++ b/submodules/TelegramUI/Sources/ChatControllerEditChat.swift @@ -49,6 +49,7 @@ extension ChatControllerImpl { } } else { self.chatTitleView?.titleContent = .custom("\(value)", nil, false) + alertController?.view.endEditing(true) alertController?.dismissAnimated() if case let .customChatContents(customChatContents) = self.subject { diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift index 73b19737f5..f4aafd30c8 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift @@ -320,16 +320,12 @@ extension ChatControllerImpl { attachmentController?.dismiss(animated: true) self?.presentICloudFileGallery() }, send: { [weak self] mediaReference in - guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId else { + guard let strongSelf = self else { return } + let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: mediaReference, threadId: strongSelf.chatLocation.threadId, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []) - let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: strongSelf.transformEnqueueMessages([message])) - |> deliverOnMainQueue).startStandalone(next: { [weak self] _ in - if let strongSelf = self, strongSelf.presentationInterfaceState.subject != .scheduledMessages { - strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory() - } - }) + strongSelf.sendMessages([message], media: true) }) if let controller = controller as? AttachmentFileControllerImpl { let _ = currentFilesController.swap(controller) @@ -684,26 +680,28 @@ extension ChatControllerImpl { return entry ?? GeneratedMediaStoreSettings.defaultSettings } |> deliverOnMainQueue).startStandalone(next: { [weak self] settings in - guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { + guard let strongSelf = self else { return } strongSelf.chatDisplayNode.dismissInput() var bannedSendMedia: (Int32, Bool)? var canSendPolls = true - if let channel = peer as? TelegramChannel { - if let value = channel.hasBannedPermission(.banSendMedia) { - bannedSendMedia = value - } - if channel.hasBannedPermission(.banSendPolls) != nil { - canSendPolls = false - } - } else if let group = peer as? TelegramGroup { - if group.hasBannedPermission(.banSendMedia) { - bannedSendMedia = (Int32.max, false) - } - if group.hasBannedPermission(.banSendPolls) { - canSendPolls = false + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { + if let channel = peer as? TelegramChannel { + if let value = channel.hasBannedPermission(.banSendMedia) { + bannedSendMedia = value + } + if channel.hasBannedPermission(.banSendPolls) != nil { + canSendPolls = false + } + } else if let group = peer as? TelegramGroup { + if group.hasBannedPermission(.banSendMedia) { + bannedSendMedia = (Int32.max, false) + } + if group.hasBannedPermission(.banSendPolls) { + canSendPolls = false + } } } @@ -771,11 +769,15 @@ extension ChatControllerImpl { } var slowModeEnabled = false - if let channel = peer as? TelegramChannel, channel.isRestrictedBySlowmode { - slowModeEnabled = true + var hasSchedule = false + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { + if let channel = peer as? TelegramChannel, channel.isRestrictedBySlowmode { + slowModeEnabled = true + } + hasSchedule = strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat } - let controller = legacyAttachmentMenu(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, chatLocation: strongSelf.chatLocation, editMediaOptions: menuEditMediaOptions, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, canSendPolls: canSendPolls, updatedPresentationData: strongSelf.updatedPresentationData, parentController: legacyController, recentlyUsedInlineBots: strongSelf.recentlyUsedInlineBotsValue, initialCaption: inputText, openGallery: { + let controller = legacyAttachmentMenu(context: strongSelf.context, peer: strongSelf.presentationInterfaceState.renderedPeer?.peer, threadTitle: strongSelf.threadInfo?.title, chatLocation: strongSelf.chatLocation, editMediaOptions: menuEditMediaOptions, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, hasSchedule: hasSchedule, canSendPolls: canSendPolls, updatedPresentationData: strongSelf.updatedPresentationData, parentController: legacyController, recentlyUsedInlineBots: strongSelf.recentlyUsedInlineBotsValue, initialCaption: inputText, openGallery: { self?.presentOldMediaPicker(fileMode: false, editingMedia: editMediaOptions != nil, completion: { signals, silentPosting, scheduleTime in if !inputText.string.isEmpty { strongSelf.clearInputText() @@ -787,7 +789,7 @@ extension ChatControllerImpl { } }) }, openCamera: { [weak self] cameraView, menuController in - if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { + if let strongSelf = self { var enablePhoto = true var enableVideo = true @@ -798,19 +800,21 @@ extension ChatControllerImpl { var bannedSendPhotos: (Int32, Bool)? var bannedSendVideos: (Int32, Bool)? - if let channel = peer as? TelegramChannel { - if let value = channel.hasBannedPermission(.banSendPhotos) { - bannedSendPhotos = value - } - if let value = channel.hasBannedPermission(.banSendVideos) { - bannedSendVideos = value - } - } else if let group = peer as? TelegramGroup { - if group.hasBannedPermission(.banSendPhotos) { - bannedSendPhotos = (Int32.max, false) - } - if group.hasBannedPermission(.banSendVideos) { - bannedSendVideos = (Int32.max, false) + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { + if let channel = peer as? TelegramChannel { + if let value = channel.hasBannedPermission(.banSendPhotos) { + bannedSendPhotos = value + } + if let value = channel.hasBannedPermission(.banSendVideos) { + bannedSendVideos = value + } + } else if let group = peer as? TelegramGroup { + if group.hasBannedPermission(.banSendPhotos) { + bannedSendPhotos = (Int32.max, false) + } + if group.hasBannedPermission(.banSendVideos) { + bannedSendVideos = (Int32.max, false) + } } } @@ -821,7 +825,15 @@ extension ChatControllerImpl { enableVideo = false } - presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: peer.id.namespace != Namespaces.Peer.SecretChat, mediaGrouping: true, initialCaption: inputText, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, enablePhoto: enablePhoto, enableVideo: enableVideo, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in + var storeCapturedPhotos = false + var hasSchedule = false + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { + storeCapturedPhotos = peer.id.namespace != Namespaces.Peer.SecretChat + + hasSchedule = strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat + } + + presentedLegacyCamera(context: strongSelf.context, peer: strongSelf.presentationInterfaceState.renderedPeer?.peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: storeCapturedPhotos, mediaGrouping: true, initialCaption: inputText, hasSchedule: hasSchedule, enablePhoto: enablePhoto, enableVideo: enableVideo, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in if let strongSelf = self { if editMediaOptions != nil { strongSelf.editMessageMediaWithLegacySignals(signals!) @@ -979,7 +991,7 @@ extension ChatControllerImpl { func presentICloudFileGallery(editingMessage: Bool = false) { let _ = (self.context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId), + TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId), TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false), TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true) ) @@ -1610,17 +1622,12 @@ extension ChatControllerImpl { } func openCamera(cameraView: TGAttachmentCameraView? = nil) { - guard let peer = self.presentationInterfaceState.renderedPeer?.peer else { - return - } - let _ = peer - let _ = (self.context.sharedContext.accountManager.transaction { transaction -> GeneratedMediaStoreSettings in let entry = transaction.getSharedData(ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings)?.get(GeneratedMediaStoreSettings.self) return entry ?? GeneratedMediaStoreSettings.defaultSettings } |> deliverOnMainQueue).startStandalone(next: { [weak self] settings in - guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { + guard let strongSelf = self else { return } @@ -1634,19 +1641,21 @@ extension ChatControllerImpl { var bannedSendPhotos: (Int32, Bool)? var bannedSendVideos: (Int32, Bool)? - if let channel = peer as? TelegramChannel { - if let value = channel.hasBannedPermission(.banSendPhotos) { - bannedSendPhotos = value - } - if let value = channel.hasBannedPermission(.banSendVideos) { - bannedSendVideos = value - } - } else if let group = peer as? TelegramGroup { - if group.hasBannedPermission(.banSendPhotos) { - bannedSendPhotos = (Int32.max, false) - } - if group.hasBannedPermission(.banSendVideos) { - bannedSendVideos = (Int32.max, false) + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { + if let channel = peer as? TelegramChannel { + if let value = channel.hasBannedPermission(.banSendPhotos) { + bannedSendPhotos = value + } + if let value = channel.hasBannedPermission(.banSendVideos) { + bannedSendVideos = value + } + } else if let group = peer as? TelegramGroup { + if group.hasBannedPermission(.banSendPhotos) { + bannedSendPhotos = (Int32.max, false) + } + if group.hasBannedPermission(.banSendVideos) { + bannedSendVideos = (Int32.max, false) + } } } @@ -1657,10 +1666,15 @@ extension ChatControllerImpl { enableVideo = false } - let storeCapturedMedia = peer.id.namespace != Namespaces.Peer.SecretChat + var storeCapturedMedia = false + var hasSchedule = false + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { + storeCapturedMedia = peer.id.namespace != Namespaces.Peer.SecretChat + hasSchedule = strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat + } let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText - presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: nil, parentController: strongSelf, attachmentController: self?.attachmentController, editingMedia: false, saveCapturedPhotos: storeCapturedMedia, mediaGrouping: true, initialCaption: inputText, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, enablePhoto: enablePhoto, enableVideo: enableVideo, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in + presentedLegacyCamera(context: strongSelf.context, peer: strongSelf.presentationInterfaceState.renderedPeer?.peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: nil, parentController: strongSelf, attachmentController: self?.attachmentController, editingMedia: false, saveCapturedPhotos: storeCapturedMedia, mediaGrouping: true, initialCaption: inputText, hasSchedule: hasSchedule, enablePhoto: enablePhoto, enableVideo: enableVideo, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in if let strongSelf = self { strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil) if !inputText.string.isEmpty { diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift index fb7f548a16..4c2cf03d08 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift @@ -243,8 +243,11 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee return false } var sortedCommands = filteredCommands.map(ChatInputTextCommand.command) - for shortcut in shortcuts { - sortedCommands.append(.shortcut(shortcut)) + if !shortcuts.isEmpty { + sortedCommands.removeAll() + for shortcut in shortcuts { + sortedCommands.append(.shortcut(shortcut)) + } } return { _ in return .commands(ChatInputQueryCommandsResult( commands: sortedCommands, diff --git a/submodules/TranslateUI/Sources/TranslateScreen.swift b/submodules/TranslateUI/Sources/TranslateScreen.swift index ba0f4d1ba7..94f61b42ae 100644 --- a/submodules/TranslateUI/Sources/TranslateScreen.swift +++ b/submodules/TranslateUI/Sources/TranslateScreen.swift @@ -720,6 +720,7 @@ public class TranslateScreen: ViewController { statusBarHeight: 0.0, navigationHeight: navigationHeight, safeInsets: UIEdgeInsets(top: layout.intrinsicInsets.top + layout.safeInsets.top, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom + layout.safeInsets.bottom, right: layout.safeInsets.right), + additionalInsets: layout.additionalInsets, inputHeight: layout.inputHeight ?? 0.0, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics,