Business fixes

This commit is contained in:
Isaac 2024-02-28 19:22:43 +04:00
parent cbb9d42744
commit 8446fd3ab1
35 changed files with 448 additions and 171 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<ChatLocationContextHolder?>(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)

View file

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

View file

@ -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: ""),

View file

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

View file

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

View file

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

View file

@ -510,7 +510,7 @@ private enum DeviceContactInfoEntry: ItemListNodeEntry {
} else {
arguments.openAddress(value)
}
}, longTapAction: {
}, longTapAction: { _, _ in
if selected == nil {
arguments.displayCopyContextMenu(.info(index), string)
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<Empty>] = []
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<AutomaticBusinessMessageSetupScreenInitialData, NoError> in
let (accountPeer, greetingMessage, awayMessage) = data
let (accountPeer, greetingMessage, awayMessage, businessHours) = data
var additionalPeerIds = Set<EnginePeer.Id>()
if let greetingMessage {
@ -1505,7 +1575,8 @@ public final class AutomaticBusinessMessageSetupScreen: ViewControllerComponentC
shortcutMessageList: shortcutMessageList,
greetingMessage: greetingMessage,
awayMessage: awayMessage,
additionalPeers: additionalPeers
additionalPeers: additionalPeers,
businessHours: businessHours
)
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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