diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 0d2e423195..f25770021e 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9048,7 +9048,7 @@ Sorry for the inconvenience."; "Login.Email.CantAccess" = "Can't access this email?"; "Login.Email.ResetTitle" = "Reset Email"; -"Login.Email.ResetText" = "You can change your login email if you are logged into Telegram from another device. Otherwise, if you don't have access to email %@, you can reset this email with an SMS code in 7 days."; +"Login.Email.ResetText" = "You can change your login email if you are logged into Telegram from another device. Otherwise, if you don't have access to email %1$@, you can reset this email with an **SMS** code **%2$@**."; "Login.Email.Reset" = "Reset"; "Login.Email.ResetNowViaSMS" = "Reset now via SMS"; "Login.Email.WillBeResetIn" = "Email will be reset in %@"; @@ -9061,3 +9061,5 @@ Sorry for the inconvenience."; "Conversation.ForwardOptions.RecipientsMessageForwardHidden" = "Recipients won't see that it was forwarded"; "Conversation.ForwardOptions.RecipientsMessagesForwardVisible" = "Recipients will see they were forwarded"; "Conversation.ForwardOptions.RecipientsMessagesForwardHidden" = "Recipients won't see they were forwarded"; + +"Conversation.SendMessage.SendWhenOnline" = "Send When Online"; diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 61a80885d7..c059f09abb 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -667,4 +667,5 @@ public enum FileMediaResourceMediaStatus: Equatable { public protocol ChatMessageItemNodeProtocol: ListViewItemNode { func targetReactionView(value: MessageReaction.Reaction) -> UIView? + func contentFrame() -> CGRect } diff --git a/submodules/AccountContext/Sources/OpenChatMessage.swift b/submodules/AccountContext/Sources/OpenChatMessage.swift index 65403b14a1..df44668e5a 100644 --- a/submodules/AccountContext/Sources/OpenChatMessage.swift +++ b/submodules/AccountContext/Sources/OpenChatMessage.swift @@ -45,6 +45,7 @@ public final class OpenChatMessageParams { public let playlistLocation: PeerMessagesPlaylistLocation? public let gallerySource: GalleryControllerItemSource? public let centralItemUpdated: ((MessageId) -> Void)? + public let getSourceRect: (() -> CGRect?)? public init( context: AccountContext, @@ -72,7 +73,8 @@ public final class OpenChatMessageParams { actionInteraction: GalleryControllerActionInteraction? = nil, playlistLocation: PeerMessagesPlaylistLocation? = nil, gallerySource: GalleryControllerItemSource? = nil, - centralItemUpdated: ((MessageId) -> Void)? = nil + centralItemUpdated: ((MessageId) -> Void)? = nil, + getSourceRect: (() -> CGRect?)? = nil ) { self.context = context self.updatedPresentationData = updatedPresentationData @@ -100,5 +102,6 @@ public final class OpenChatMessageParams { self.playlistLocation = playlistLocation self.gallerySource = gallerySource self.centralItemUpdated = centralItemUpdated + self.getSourceRect = getSourceRect } } diff --git a/submodules/AccountContext/Sources/PeerSelectionController.swift b/submodules/AccountContext/Sources/PeerSelectionController.swift index d00ec3d92f..963dea8a81 100644 --- a/submodules/AccountContext/Sources/PeerSelectionController.swift +++ b/submodules/AccountContext/Sources/PeerSelectionController.swift @@ -83,6 +83,7 @@ public enum AttachmentTextInputPanelSendMode { case generic case silent case schedule + case whenOnline } public protocol PeerSelectionController: ViewController { diff --git a/submodules/AlertUI/BUILD b/submodules/AlertUI/BUILD index 9f7f33fd96..2d9a5628f8 100644 --- a/submodules/AlertUI/BUILD +++ b/submodules/AlertUI/BUILD @@ -10,7 +10,10 @@ swift_library( "-warnings-as-errors", ], deps = [ + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", + "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", + "//submodules/TelegramUI/Components/TextNodeWithEntities:TextNodeWithEntities", ], visibility = [ "//visibility:public", diff --git a/submodules/AlertUI/Sources/TextAlertWithEntitiesController.swift b/submodules/AlertUI/Sources/TextAlertWithEntitiesController.swift new file mode 100644 index 0000000000..a9e506b81c --- /dev/null +++ b/submodules/AlertUI/Sources/TextAlertWithEntitiesController.swift @@ -0,0 +1,355 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import SwiftSignalKit +import TextNodeWithEntities + +private let alertWidth: CGFloat = 270.0 + +final class TextAlertWithEntitiesContentNode: AlertContentNode { + private var theme: AlertControllerTheme + private let actionLayout: TextAlertContentActionLayout + + private let titleNode: ImmediateTextNode? + private let textNode: ImmediateTextNodeWithEntities + + private let actionNodesSeparator: ASDisplayNode + private let actionNodes: [TextAlertContentActionNode] + private let actionVerticalSeparators: [ASDisplayNode] + + private var validLayout: CGSize? + + private let _dismissOnOutsideTap: Bool + override public var dismissOnOutsideTap: Bool { + return self._dismissOnOutsideTap + } + + private var highlightedItemIndex: Int? = nil + + var textAttributeAction: (NSAttributedString.Key, (Any) -> Void)? { + didSet { + if let (attribute, textAttributeAction) = self.textAttributeAction { + self.textNode.highlightAttributeAction = { attributes in + if let _ = attributes[attribute] { + return attribute + } else { + return nil + } + } + self.textNode.tapAttributeAction = { attributes, _ in + if let value = attributes[attribute] { + textAttributeAction(value) + } + } + self.textNode.linkHighlightColor = self.theme.accentColor.withAlphaComponent(0.5) + } else { + self.textNode.highlightAttributeAction = nil + self.textNode.tapAttributeAction = nil + } + } + } + + init(theme: AlertControllerTheme, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout, dismissOnOutsideTap: Bool) { + self.theme = theme + self.actionLayout = actionLayout + self._dismissOnOutsideTap = dismissOnOutsideTap + if let title = title { + let titleNode = ImmediateTextNode() + titleNode.attributedText = title + titleNode.displaysAsynchronously = false + titleNode.isUserInteractionEnabled = false + titleNode.maximumNumberOfLines = 4 + titleNode.truncationType = .end + titleNode.isAccessibilityElement = true + titleNode.accessibilityLabel = title.string + self.titleNode = titleNode + } else { + self.titleNode = nil + } + + self.textNode = ImmediateTextNodeWithEntities() + self.textNode.maximumNumberOfLines = 0 + self.textNode.attributedText = text + self.textNode.displaysAsynchronously = false + self.textNode.isLayerBacked = false + self.textNode.isAccessibilityElement = true + self.textNode.accessibilityLabel = text.string + self.textNode.insets = UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0) + if text.length != 0 { + if let paragraphStyle = text.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle { + self.textNode.textAlignment = paragraphStyle.alignment + } + } + self.textNode.spoilerColor = theme.secondaryColor + + self.actionNodesSeparator = ASDisplayNode() + self.actionNodesSeparator.isLayerBacked = true + self.actionNodesSeparator.backgroundColor = theme.separatorColor + + self.actionNodes = actions.map { action -> TextAlertContentActionNode in + return TextAlertContentActionNode(theme: theme, action: action) + } + + var actionVerticalSeparators: [ASDisplayNode] = [] + if actions.count > 1 { + for _ in 0 ..< actions.count - 1 { + let separatorNode = ASDisplayNode() + separatorNode.isLayerBacked = true + separatorNode.backgroundColor = theme.separatorColor + actionVerticalSeparators.append(separatorNode) + } + } + self.actionVerticalSeparators = actionVerticalSeparators + + super.init() + + if let titleNode = self.titleNode { + self.addSubnode(titleNode) + } + self.addSubnode(self.textNode) + + self.addSubnode(self.actionNodesSeparator) + + var i = 0 + for actionNode in self.actionNodes { + self.addSubnode(actionNode) + + let index = i + actionNode.highlightedUpdated = { [weak self] highlighted in + if highlighted { + self?.highlightedItemIndex = index + } + } + i += 1 + } + + for separatorNode in self.actionVerticalSeparators { + self.addSubnode(separatorNode) + } + } + + func setHighlightedItemIndex(_ index: Int?, update: Bool = false) { + self.highlightedItemIndex = index + + if update { + var i = 0 + for actionNode in self.actionNodes { + if i == index { + actionNode.setHighlighted(true, animated: false) + } else { + actionNode.setHighlighted(false, animated: false) + } + i += 1 + } + } + } + + override func decreaseHighlightedIndex() { + let currentHighlightedIndex = self.highlightedItemIndex ?? 0 + + self.setHighlightedItemIndex(max(0, currentHighlightedIndex - 1), update: true) + } + + override func increaseHighlightedIndex() { + let currentHighlightedIndex = self.highlightedItemIndex ?? -1 + + self.setHighlightedItemIndex(min(self.actionNodes.count - 1, currentHighlightedIndex + 1), update: true) + } + + override func performHighlightedAction() { + guard let highlightedItemIndex = self.highlightedItemIndex else { + return + } + + var i = 0 + for itemNode in self.actionNodes { + if i == highlightedItemIndex { + itemNode.performAction() + return + } + i += 1 + } + } + + override func updateTheme(_ theme: AlertControllerTheme) { + self.theme = theme + + if let titleNode = self.titleNode, let attributedText = titleNode.attributedText { + let updatedText = NSMutableAttributedString(attributedString: attributedText) + updatedText.addAttribute(NSAttributedString.Key.foregroundColor, value: theme.primaryColor, range: NSRange(location: 0, length: updatedText.length)) + titleNode.attributedText = updatedText + } + if let attributedText = self.textNode.attributedText { + let updatedText = NSMutableAttributedString(attributedString: attributedText) + updatedText.addAttribute(NSAttributedString.Key.foregroundColor, value: theme.primaryColor, range: NSRange(location: 0, length: updatedText.length)) + self.textNode.attributedText = updatedText + } + self.textNode.spoilerColor = theme.secondaryColor + + self.actionNodesSeparator.backgroundColor = theme.separatorColor + for actionNode in self.actionNodes { + actionNode.updateTheme(theme) + } + for separatorNode in self.actionVerticalSeparators { + separatorNode.backgroundColor = theme.separatorColor + } + + if let size = self.validLayout { + _ = self.updateLayout(size: size, transition: .immediate) + } + } + + override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + self.validLayout = size + + let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) + + var size = size + size.width = min(size.width, alertWidth) + + var titleSize: CGSize? + if let titleNode = self.titleNode { + titleSize = titleNode.updateLayout(CGSize(width: size.width - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude)) + } + let textSize = self.textNode.updateLayout(CGSize(width: size.width - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude)) + + let actionButtonHeight: CGFloat = 44.0 + + var minActionsWidth: CGFloat = 0.0 + let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) + let actionTitleInsets: CGFloat = 8.0 + + var effectiveActionLayout = self.actionLayout + for actionNode in self.actionNodes { + let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) + if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { + effectiveActionLayout = .vertical + } + switch effectiveActionLayout { + case .horizontal: + minActionsWidth += actionTitleSize.width + actionTitleInsets + case .vertical: + minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) + } + } + + let resultSize: CGSize + + var actionsHeight: CGFloat = 0.0 + switch effectiveActionLayout { + case .horizontal: + actionsHeight = actionButtonHeight + case .vertical: + actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) + } + + let contentWidth = alertWidth - insets.left - insets.right + if let titleNode = self.titleNode, let titleSize = titleSize { + let spacing: CGFloat = 6.0 + let titleFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - titleSize.width) / 2.0), y: insets.top), size: titleSize) + transition.updateFrame(node: titleNode, frame: titleFrame) + + let textFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - textSize.width) / 2.0), y: titleFrame.maxY + spacing), size: textSize) + transition.updateFrame(node: self.textNode, frame: textFrame.offsetBy(dx: -1.0, dy: -1.0)) + + resultSize = CGSize(width: contentWidth + insets.left + insets.right, height: titleSize.height + spacing + textSize.height + actionsHeight + insets.top + insets.bottom) + } else { + let textFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - textSize.width) / 2.0), y: insets.top), size: textSize) + transition.updateFrame(node: self.textNode, frame: textFrame) + + resultSize = CGSize(width: contentWidth + insets.left + insets.right, height: textSize.height + actionsHeight + insets.top + insets.bottom) + } + + self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)) + + var actionOffset: CGFloat = 0.0 + let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) + var separatorIndex = -1 + var nodeIndex = 0 + for actionNode in self.actionNodes { + if separatorIndex >= 0 { + let separatorNode = self.actionVerticalSeparators[separatorIndex] + switch effectiveActionLayout { + case .horizontal: + transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) + case .vertical: + transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + } + } + separatorIndex += 1 + + let currentActionWidth: CGFloat + switch effectiveActionLayout { + case .horizontal: + if nodeIndex == self.actionNodes.count - 1 { + currentActionWidth = resultSize.width - actionOffset + } else { + currentActionWidth = actionWidth + } + case .vertical: + currentActionWidth = resultSize.width + } + + let actionNodeFrame: CGRect + switch effectiveActionLayout { + case .horizontal: + actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) + actionOffset += currentActionWidth + case .vertical: + actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) + actionOffset += actionButtonHeight + } + + transition.updateFrame(node: actionNode, frame: actionNodeFrame) + + nodeIndex += 1 + } + + return resultSize + } +} + +public func textWithEntitiesAlertController(theme: AlertControllerTheme, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissAutomatically: Bool = true) -> AlertController { + var dismissImpl: (() -> Void)? + let controller = AlertController(theme: theme, contentNode: TextAlertWithEntitiesContentNode(theme: theme, title: title, text: text, actions: actions.map { action in + return TextAlertAction(type: action.type, title: action.title, action: { + if dismissAutomatically { + dismissImpl?() + } + action.action() + }) + }, actionLayout: actionLayout, dismissOnOutsideTap: true), allowInputInset: allowInputInset) + dismissImpl = { [weak controller] in + controller?.dismissAnimated() + } + + return controller +} + + +public func textWithEntitiesAlertController(alertContext: AlertControllerContext, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissAutomatically: Bool = true) -> AlertController { + let theme = alertContext.theme + + var dismissImpl: (() -> Void)? + let controller = AlertController(theme: theme, contentNode: TextAlertContentNode(theme: theme, title: title, text: text, actions: actions.map { action in + return TextAlertAction(type: action.type, title: action.title, action: { + if dismissAutomatically { + dismissImpl?() + } + action.action() + }) + }, actionLayout: actionLayout, dismissOnOutsideTap: true), allowInputInset: allowInputInset) + dismissImpl = { [weak controller] in + controller?.dismissAnimated() + } + + let presentationDataDisposable = alertContext.themeSignal.start(next: { [weak controller] theme in + controller?.theme = theme + }) + controller.dismissed = { _ in + presentationDataDisposable.dispose() + } + + return controller +} diff --git a/submodules/AlertUI/Sources/ThemedTextAlertController.swift b/submodules/AlertUI/Sources/ThemedTextAlertController.swift index fcc13819ae..c7ef5ddff0 100644 --- a/submodules/AlertUI/Sources/ThemedTextAlertController.swift +++ b/submodules/AlertUI/Sources/ThemedTextAlertController.swift @@ -1,5 +1,6 @@ import Foundation import UIKit +import AsyncDisplayKit import Display import SwiftSignalKit diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index 4518152426..dd710e2c5e 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -117,6 +117,12 @@ public extension AttachmentContainable { } public enum AttachmentMediaPickerSendMode { + case generic + case silently + case whenOnline +} + +public enum AttachmentMediaPickerAttachmentMode { case media case files } @@ -131,7 +137,7 @@ public protocol AttachmentMediaPickerContext { func mainButtonAction() func setCaption(_ caption: NSAttributedString) - func send(silently: Bool, mode: AttachmentMediaPickerSendMode) + func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) func schedule() } @@ -380,12 +386,14 @@ public class AttachmentController: ViewController { self.panel.sendMessagePressed = { [weak self] mode in if let strongSelf = self { switch mode { - case .generic: - strongSelf.mediaPickerContext?.send(silently: false, mode: .media) - case .silent: - strongSelf.mediaPickerContext?.send(silently: true, mode: .media) - case .schedule: - strongSelf.mediaPickerContext?.schedule() + case .generic: + strongSelf.mediaPickerContext?.send(mode: .generic, attachmentMode: .media) + case .silent: + strongSelf.mediaPickerContext?.send(mode: .silently, attachmentMode: .media) + case .schedule: + strongSelf.mediaPickerContext?.schedule() + case .whenOnline: + strongSelf.mediaPickerContext?.send(mode: .whenOnline, attachmentMode: .media) } } } diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 7e594790c2..305e1df444 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -925,7 +925,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { return } textInputPanelNode.loadTextInputNodeIfNeeded() - guard let textInputNode = textInputPanelNode.textInputNode else { + guard let textInputNode = textInputPanelNode.textInputNode, let peerId = chatLocation.peerId else { return } @@ -933,15 +933,36 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { if case .media = strongSelf.presentationInterfaceState.inputMode { hasEntityKeyboard = true } - - let controller = ChatSendMessageActionSheetController(context: strongSelf.context, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, attachment: true, completion: { - }, sendMessage: { [weak textInputPanelNode] silently in - textInputPanelNode?.sendMessage(silently ? .silent : .generic) - }, schedule: { [weak textInputPanelNode] in - textInputPanelNode?.sendMessage(.schedule) + let _ = (strongSelf.context.account.viewTracker.peerView(peerId) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] peerView in + guard let strongSelf = self, let peer = peerViewMainPeer(peerView) else { + return + } + var sendWhenOnlineAvailable = false + if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case .present = presence.status { + sendWhenOnlineAvailable = true + } + if peer.id.namespace == Namespaces.Peer.CloudUser && peer.id.id._internalGetInt64Value() == 777000 { + sendWhenOnlineAvailable = false + } + + let controller = ChatSendMessageActionSheetController(context: strongSelf.context, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, attachment: true, canSendWhenOnline: sendWhenOnlineAvailable, completion: { + }, sendMessage: { [weak textInputPanelNode] mode in + switch mode { + case .generic: + textInputPanelNode?.sendMessage(.generic) + case .silently: + textInputPanelNode?.sendMessage(.silent) + case .whenOnline: + textInputPanelNode?.sendMessage(.whenOnline) + } + }, schedule: { [weak textInputPanelNode] in + textInputPanelNode?.sendMessage(.schedule) + }) + controller.emojiViewProvider = textInputPanelNode.emojiViewProvider + strongSelf.presentInGlobalOverlay(controller) }) - controller.emojiViewProvider = textInputPanelNode.emojiViewProvider - strongSelf.presentInGlobalOverlay(controller) }, openScheduledMessages: { }, openPeersNearby: { }, displaySearchResultsTooltip: { _, _ in diff --git a/submodules/AuthorizationUI/BUILD b/submodules/AuthorizationUI/BUILD index e03bd64443..42b6200c8b 100644 --- a/submodules/AuthorizationUI/BUILD +++ b/submodules/AuthorizationUI/BUILD @@ -38,6 +38,8 @@ swift_library( "//submodules/InvisibleInkDustNode:InvisibleInkDustNode", "//submodules/AuthorizationUtils:AuthorizationUtils", "//submodules/ManagedAnimationNode:ManagedAnimationNode", + "//submodules/AlertUI:AlertUI", + "//submodules/TelegramUI/Components/TextNodeWithEntities:TextNodeWithEntities", ], visibility = [ "//visibility:public", diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift index b06c675684..aecd5a27dc 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift @@ -12,9 +12,9 @@ import PhoneNumberFormat import AnimatedStickerNode import TelegramAnimatedStickerNode import SolidRoundedButtonNode -import InvisibleInkDustNode import AuthorizationUtils import TelegramStringFormatting +import TextNodeWithEntities final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextFieldDelegate { private let strings: PresentationStrings @@ -24,9 +24,8 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF private let titleNode: ImmediateTextNode private let titleActivateAreaNode: AccessibilityAreaNode private let titleIconNode: ASImageNode - private let currentOptionNode: ImmediateTextNode + private let currentOptionNode: ImmediateTextNodeWithEntities private let currentOptionActivateAreaNode: AccessibilityAreaNode - private var dustNode: InvisibleInkDustNode? private let currentOptionInfoNode: ASTextNode private let currentOptionInfoActivateAreaNode: AccessibilityAreaNode @@ -105,11 +104,12 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.titleIconNode.displayWithoutProcessing = true self.titleIconNode.displaysAsynchronously = false - self.currentOptionNode = ImmediateTextNode() + self.currentOptionNode = ImmediateTextNodeWithEntities() self.currentOptionNode.isUserInteractionEnabled = false self.currentOptionNode.displaysAsynchronously = false self.currentOptionNode.lineSpacing = 0.1 self.currentOptionNode.maximumNumberOfLines = 0 + self.currentOptionNode.spoilerColor = self.theme.list.itemSecondaryTextColor self.currentOptionActivateAreaNode = AccessibilityAreaNode() self.currentOptionActivateAreaNode.accessibilityTraits = .staticText @@ -570,23 +570,6 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - additionalBottomInset)), items: items, transition: transition, failIfDoesNotFit: false) - if let textLayout = self.currentOptionNode.cachedLayout, !textLayout.spoilers.isEmpty { - if self.dustNode == nil { - let dustNode = InvisibleInkDustNode(textNode: nil, enableAnimations: true) - self.dustNode = dustNode - self.currentOptionNode.supernode?.insertSubnode(dustNode, aboveSubnode: self.currentOptionNode) - - } - if let dustNode = self.dustNode { - let textFrame = self.currentOptionNode.frame - dustNode.update(size: textFrame.size, color: self.theme.list.itemSecondaryTextColor, textColor: self.theme.list.itemPrimaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }) - transition.updateFrame(node: dustNode, frame: textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)) - } - } else if let dustNode = self.dustNode { - self.dustNode = nil - dustNode.removeFromSupernode() - } - self.nextOptionTitleNode.frame = self.nextOptionButtonNode.bounds self.titleActivateAreaNode.frame = self.titleNode.frame diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index 34a84c65fa..d144f13af0 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -18,6 +18,8 @@ import LegacyMediaPickerUI import PasswordSetupUI import TelegramNotices import AuthenticationServices +import Markdown +import AlertUI private enum InnerState: Equatable { case state(UnauthorizedAccountStateContents) @@ -280,7 +282,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail return controller } - private func codeEntryController(number: String, email: String?, type: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) -> AuthorizationSequenceCodeEntryController { + private func codeEntryController(number: String, phoneCodeHash: String, email: String?, type: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) -> AuthorizationSequenceCodeEntryController { var currentController: AuthorizationSequenceCodeEntryController? for c in self.viewControllers { if let c = c as? AuthorizationSequenceCodeEntryController { @@ -302,8 +304,57 @@ public final class AuthorizationSequenceController: NavigationController, MFMail let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: ""))).start() }) - controller.resetEmail = { - + controller.resetEmail = { [weak self, weak controller] in + if let self, case let .email(pattern, _, resetAvailablePeriod, resetPendingDate, _, setup) = type, !setup { + if let _ = resetPendingDate { + + } else if let resetAvailablePeriod { + let pattern = pattern.replacingOccurrences(of: "*", with: "#") + let title = NSAttributedString(string: self.presentationData.strings.Login_Email_ResetTitle, font: Font.semibold(self.presentationData.listsFontSize.baseDisplaySize), textColor: self.presentationData.theme.actionSheet.primaryTextColor) + let availableIn = unmuteIntervalString(strings: self.presentationData.strings, value: resetAvailablePeriod) + let body = MarkdownAttributeSet(font: Font.regular(self.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) + let bold = MarkdownAttributeSet(font: Font.semibold(self.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) + let text = parseMarkdownIntoAttributedString(self.presentationData.strings.Login_Email_ResetText(pattern, availableIn).string, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in nil }), textAlignment: .center).mutableCopy() as! NSMutableAttributedString + if let regex = try? NSRegularExpression(pattern: "\\#", options: []) { + let matches = regex.matches(in: text.string, options: [], range: NSMakeRange(0, text.length)) + if let first = matches.first { + text.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler), value: true, range: NSRange(location: first.range.location, length: matches.count)) + } + } + + let alertController = textWithEntitiesAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: title, text: text, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Login_Email_Reset, action: { [weak self] in + guard let self else { + return + } + self.actionDisposable.set( + (resetLoginEmail(account: self.account, phoneNumber: number, phoneCodeHash: phoneCodeHash) + |> deliverOnMainQueue).start(error: { [weak self] error in + Queue.mainQueue().async { + guard let self, let controller = controller else { + return + } + controller.inProgress = false + + let text: String + switch error { + case .limitExceeded: + text = self.presentationData.strings.Login_CodeFloodError + case .generic: + text = self.presentationData.strings.Login_UnknownError + case .codeExpired: + text = self.presentationData.strings.Login_CodeExpired + let account = self.account + let _ = TelegramEngineUnauthorized(account: self.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start() + } + + controller.presentInGlobalOverlay(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})])) + } + }) + ) + })]) + controller?.present(alertController, in: .window(.root)) + } + } } controller.loginWithCode = { [weak self, weak controller] code in if let strongSelf = self { @@ -1068,7 +1119,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail controllers.append(self.phoneEntryController(countryCode: countryCode, number: number, splashController: previousSplashController)) self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty && (previousSplashController == nil || self.viewControllers.count > 2)) - case let .confirmationCodeEntry(number, type, _, timeout, nextType, _): + case let .confirmationCodeEntry(number, type, phoneCodeHash, timeout, nextType, _): var controllers: [ViewController] = [] if !self.otherAccountPhoneNumbers.1.isEmpty { controllers.append(self.splashController()) @@ -1081,7 +1132,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail if let _ = self.currentEmail { controllers.append(self.emailSetupController(number: number, appleSignInAllowed: self.appleSignInAllowed)) } - controllers.append(self.codeEntryController(number: number, email: self.currentEmail, type: type, nextType: nextType, timeout: timeout, termsOfService: nil)) + controllers.append(self.codeEntryController(number: number, phoneCodeHash: phoneCodeHash, email: self.currentEmail, type: type, nextType: nextType, timeout: timeout, termsOfService: nil)) } self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty) case let .passwordEntry(hint, _, _, suggestReset, syncContacts): diff --git a/submodules/AuthorizationUtils/Sources/AuthorizationOptionText.swift b/submodules/AuthorizationUtils/Sources/AuthorizationOptionText.swift index 48308e3d26..dc26d51911 100644 --- a/submodules/AuthorizationUtils/Sources/AuthorizationOptionText.swift +++ b/submodules/AuthorizationUtils/Sources/AuthorizationOptionText.swift @@ -29,12 +29,8 @@ public func authorizationCurrentOptionText(_ type: SentAuthorizationCodeType, ph return NSAttributedString(string: "", font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center) case let .email(emailPattern, _, _, _, _, _): let mutableString = NSAttributedString(string: strings.Login_EnterCodeEmailText(email ?? emailPattern).string, font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center).mutableCopy() as! NSMutableAttributedString - - let string = mutableString.string - let nsString = string as NSString - if let regex = try? NSRegularExpression(pattern: "\\*", options: []) { - let matches = regex.matches(in: string, options: [], range: NSMakeRange(0, nsString.length)) + let matches = regex.matches(in: mutableString.string, options: [], range: NSMakeRange(0, mutableString.length)) if let first = matches.first { mutableString.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler), value: true, range: NSRange(location: first.range.location, length: matches.count)) } diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift index 1406029bcb..cb71a621da 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift @@ -10,6 +10,11 @@ import TelegramCore import TextFormat public final class ChatSendMessageActionSheetController: ViewController { + public enum SendMode { + case generic + case silently + case whenOnline + } private var controllerNode: ChatSendMessageActionSheetControllerNode { return self.displayNode as! ChatSendMessageActionSheetControllerNode } @@ -24,8 +29,9 @@ public final class ChatSendMessageActionSheetController: ViewController { private let sourceSendButton: ASDisplayNode private let textInputNode: EditableTextNode private let attachment: Bool + private let canSendWhenOnline: Bool private let completion: () -> Void - private let sendMessage: (Bool) -> Void + private let sendMessage: (SendMode) -> Void private let schedule: () -> Void private var presentationData: PresentationData @@ -39,7 +45,7 @@ public final class ChatSendMessageActionSheetController: ViewController { public var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id?, forwardMessageIds: [EngineMessage.Id]?, hasEntityKeyboard: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputNode: EditableTextNode, attachment: Bool = false, completion: @escaping () -> Void, sendMessage: @escaping (Bool) -> Void, schedule: @escaping () -> Void) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id?, forwardMessageIds: [EngineMessage.Id]?, hasEntityKeyboard: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputNode: EditableTextNode, attachment: Bool = false, canSendWhenOnline: Bool, completion: @escaping () -> Void, sendMessage: @escaping (SendMode) -> Void, schedule: @escaping () -> Void) { self.context = context self.peerId = peerId self.forwardMessageIds = forwardMessageIds @@ -48,6 +54,7 @@ public final class ChatSendMessageActionSheetController: ViewController { self.sourceSendButton = sourceSendButton self.textInputNode = textInputNode self.attachment = attachment + self.canSendWhenOnline = canSendWhenOnline self.completion = completion self.sendMessage = sendMessage self.schedule = schedule @@ -95,11 +102,14 @@ public final class ChatSendMessageActionSheetController: ViewController { canSchedule = !isSecret } - self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, presentationData: self.presentationData, reminders: reminders, gesture: gesture, sourceSendButton: self.sourceSendButton, textInputNode: self.textInputNode, attachment: self.attachment, forwardedCount: forwardedCount, hasEntityKeyboard: self.hasEntityKeyboard, emojiViewProvider: self.emojiViewProvider, send: { [weak self] in - self?.sendMessage(false) + self.displayNode = ChatSendMessageActionSheetControllerNode(context: self.context, presentationData: self.presentationData, reminders: reminders, gesture: gesture, sourceSendButton: self.sourceSendButton, textInputNode: self.textInputNode, attachment: self.attachment, canSendWhenOnline: self.canSendWhenOnline, forwardedCount: forwardedCount, hasEntityKeyboard: self.hasEntityKeyboard, emojiViewProvider: self.emojiViewProvider, send: { [weak self] in + self?.sendMessage(.generic) self?.dismiss(cancel: false) }, sendSilently: { [weak self] in - self?.sendMessage(true) + self?.sendMessage(.silently) + self?.dismiss(cancel: false) + }, sendWhenOnline: { [weak self] in + self?.sendMessage(.whenOnline) self?.dismiss(cancel: false) }, schedule: !canSchedule ? nil : { [weak self] in self?.schedule() diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift index e90286ade0..7035561189 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift @@ -17,15 +17,18 @@ private let rightInset: CGFloat = 16.0 private enum ChatSendMessageActionIcon { case sendWithoutSound + case sendWhenOnline case schedule func image(theme: PresentationTheme) -> UIImage? { let imageName: String switch self { - case .sendWithoutSound: - imageName = "Chat/Input/Menu/SilentIcon" - case .schedule: - imageName = "Chat/Input/Menu/ScheduleIcon" + case .sendWithoutSound: + imageName = "Chat/Input/Menu/SilentIcon" + case .sendWhenOnline: + imageName = "Chat/Input/Menu/WhenOnlineIcon" + case .schedule: + imageName = "Chat/Input/Menu/ScheduleIcon" } return generateTintedImage(image: UIImage(bundleImageName: imageName), color: theme.contextMenu.primaryColor) } @@ -191,7 +194,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, private var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)? - init(context: AccountContext, presentationData: PresentationData, reminders: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputNode: EditableTextNode, attachment: Bool, forwardedCount: Int?, hasEntityKeyboard: Bool, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, send: (() -> Void)?, sendSilently: (() -> Void)?, schedule: (() -> Void)?, cancel: (() -> Void)?) { + init(context: AccountContext, presentationData: PresentationData, reminders: Bool, gesture: ContextGesture, sourceSendButton: ASDisplayNode, textInputNode: EditableTextNode, attachment: Bool, canSendWhenOnline: Bool, forwardedCount: Int?, hasEntityKeyboard: Bool, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, send: (() -> Void)?, sendSilently: (() -> Void)?, sendWhenOnline: (() -> Void)?, schedule: (() -> Void)?, cancel: (() -> Void)?) { self.context = context self.presentationData = presentationData self.sourceSendButton = sourceSendButton @@ -249,6 +252,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, contentNodes.append(ActionSheetItemNode(theme: self.presentationData.theme, title: self.presentationData.strings.Conversation_SendMessage_SendSilently, icon: .sendWithoutSound, hasSeparator: true, action: { sendSilently?() })) + if canSendWhenOnline { + contentNodes.append(ActionSheetItemNode(theme: self.presentationData.theme, title: self.presentationData.strings.Conversation_SendMessage_SendWhenOnline, icon: .sendWhenOnline, hasSeparator: true, action: { + sendWhenOnline?() + })) + } } if let _ = schedule { contentNodes.append(ActionSheetItemNode(theme: self.presentationData.theme, title: reminders ? self.presentationData.strings.Conversation_SendMessage_SetReminder: self.presentationData.strings.Conversation_SendMessage_ScheduleMessage, icon: .schedule, hasSeparator: false, action: { diff --git a/submodules/ComposePollUI/Sources/CreatePollController.swift b/submodules/ComposePollUI/Sources/CreatePollController.swift index 1f5ba3164b..b964e37bdd 100644 --- a/submodules/ComposePollUI/Sources/CreatePollController.swift +++ b/submodules/ComposePollUI/Sources/CreatePollController.swift @@ -536,7 +536,7 @@ private final class CreatePollContext: AttachmentMediaPickerContext { func setCaption(_ caption: NSAttributedString) { } - func send(silently: Bool, mode: AttachmentMediaPickerSendMode) { + func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) { } func schedule() { @@ -558,6 +558,44 @@ public class CreatePollControllerImpl: ItemListController, AttachmentContainable public var mediaPickerContext: AttachmentMediaPickerContext? { return CreatePollContext() } + + fileprivate var stateValue: Atomic? + + private var hasContent: Bool { + if let stateValue { + let state = stateValue.with { $0 } + var hasNonEmptyOptions = false + for i in 0 ..< state.options.count { + let optionText = state.options[i].item.text.trimmingCharacters(in: .whitespacesAndNewlines) + if !optionText.isEmpty { + hasNonEmptyOptions = true + } + } + if hasNonEmptyOptions || !state.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + return true + } else { + return false + } + } else { + return false + } + } + + var context: AccountContext? + public func requestDismiss(completion: @escaping () -> Void) { + if self.hasContent, let context = self.context { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + self.present(textAlertController(context: context, updatedPresentationData: nil, title: nil, text: presentationData.strings.CreatePoll_CancelConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { + completion() + })]), in: .window(.root)) + } else { + completion() + } + } + + public func shouldDismissImmediately() -> Bool { + return !self.hasContent + } } public func createPollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, isQuiz: Bool? = nil, completion: @escaping (ComposedPoll) -> Void) -> CreatePollControllerImpl { @@ -579,7 +617,6 @@ public func createPollController(context: AccountContext, updatedPresentationDat var ensureSolutionVisibleImpl: (() -> Void)? var ensureQuestionVisibleImpl: (() -> Void)? var displayQuizTooltipImpl: ((Bool) -> Void)? - var attemptNavigationImpl: (() -> Bool)? let actionsDisposable = DisposableSet() @@ -923,9 +960,7 @@ public func createPollController(context: AccountContext, updatedPresentationDat }) let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { - if let attemptNavigationImpl = attemptNavigationImpl, attemptNavigationImpl() { - dismissImpl?() - } + dismissImpl?() }) let optionIds = state.options.map { $0.item.id } @@ -966,6 +1001,8 @@ public func createPollController(context: AccountContext, updatedPresentationDat weak var currentTooltipController: TooltipController? let controller = CreatePollControllerImpl(context: context, state: signal) + controller.context = context + controller.stateValue = stateValue controller.navigationPresentation = .modal controller.visibleBottomContentOffsetChanged = { [weak controller] _ in controller?.updateTabBarAlpha(1.0, .immediate) @@ -1193,31 +1230,6 @@ public func createPollController(context: AccountContext, updatedPresentationDat return .single(didReorder) }) - attemptNavigationImpl = { - let state = stateValue.with { $0 } - var hasNonEmptyOptions = false - for i in 0 ..< state.options.count { - let optionText = state.options[i].item.text.trimmingCharacters(in: .whitespacesAndNewlines) - if !optionText.isEmpty { - hasNonEmptyOptions = true - } - } - if hasNonEmptyOptions || !state.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.CreatePoll_CancelConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { - dismissImpl?() - })]), nil) - return false - } else { - return true - } - } - controller.attemptNavigation = { _ in - if let attemptNavigationImpl = attemptNavigationImpl, attemptNavigationImpl() { - return true - } - return false - } dismissInputImpl = { [weak controller] in controller?.view.endEditing(true) } diff --git a/submodules/Display/Source/TextAlertController.swift b/submodules/Display/Source/TextAlertController.swift index 28b339166e..54437bb0f6 100644 --- a/submodules/Display/Source/TextAlertController.swift +++ b/submodules/Display/Source/TextAlertController.swift @@ -29,7 +29,7 @@ public final class TextAlertContentActionNode: HighlightableButtonNode { private let backgroundNode: ASDisplayNode - var highlightedUpdated: (Bool) -> Void = { _ in } + public var highlightedUpdated: (Bool) -> Void = { _ in } public init(theme: AlertControllerTheme, action: TextAlertAction) { self.theme = theme @@ -68,13 +68,13 @@ public final class TextAlertContentActionNode: HighlightableButtonNode { }) } - func performAction() { + public func performAction() { if self.actionEnabled { self.action.action() } } - func setHighlighted(_ highlighted: Bool, animated: Bool) { + public func setHighlighted(_ highlighted: Bool, animated: Bool) { self.highlightedUpdated(highlighted) if highlighted { if self.backgroundNode.supernode == nil { diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index df0a45bf49..7bb59da58d 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -336,6 +336,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll self.textNode = ImmediateTextNodeWithEntities() self.textNode.maximumNumberOfLines = 0 self.textNode.linkHighlightColor = UIColor(rgb: 0x5ac8fa, alpha: 0.2) + self.textNode.displaySpoilerEffect = false self.authorNameNode = ASTextNode() self.authorNameNode.maximumNumberOfLines = 1 @@ -802,6 +803,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll if let textLayout = self.textNode.cachedLayout, !textLayout.spoilers.isEmpty { if self.spoilerTextNode == nil { let spoilerTextNode = ImmediateTextNodeWithEntities() + spoilerTextNode.displaySpoilerEffect = false spoilerTextNode.attributedText = textNode.attributedText spoilerTextNode.maximumNumberOfLines = 0 spoilerTextNode.linkHighlightColor = UIColor(rgb: 0x5ac8fa, alpha: 0.2) diff --git a/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Schedule.imageset/Contents.json b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Schedule.imageset/Contents.json new file mode 100644 index 0000000000..a8b8162849 --- /dev/null +++ b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Schedule.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "schedule.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Schedule.imageset/schedule.pdf b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Schedule.imageset/schedule.pdf new file mode 100644 index 0000000000..23e1733493 --- /dev/null +++ b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Schedule.imageset/schedule.pdf @@ -0,0 +1,97 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 3.334961 3.334961 cm +0.000000 0.000000 0.000000 scn +8.000000 16.665077 m +8.000000 17.032347 8.297730 17.330078 8.665000 17.330078 c +13.450547 17.330078 17.330002 13.450625 17.330002 8.665078 c +17.330002 3.879531 13.450547 0.000076 8.665000 0.000076 c +3.879452 0.000076 0.000000 3.879531 0.000000 8.665078 c +0.000000 11.755159 1.617408 14.466341 4.050117 16.000078 c +2.165000 16.000078 l +1.797731 16.000078 1.500000 16.297810 1.500000 16.665077 c +1.500000 17.032347 1.797731 17.330078 2.165000 17.330078 c +5.665000 17.330078 l +6.032269 17.330078 6.330000 17.032347 6.330000 16.665077 c +6.330000 13.165078 l +6.330000 12.797809 6.032269 12.500078 5.665000 12.500078 c +5.297730 12.500078 5.000000 12.797809 5.000000 13.165078 c +5.000000 15.020401 l +2.805339 13.751994 1.330000 11.380217 1.330000 8.665078 c +1.330000 4.614069 4.613991 1.330078 8.665000 1.330078 c +12.716009 1.330078 16.000000 4.614069 16.000000 8.665078 c +16.000000 12.716087 12.716009 16.000078 8.665000 16.000078 c +8.297730 16.000078 8.000000 16.297810 8.000000 16.665077 c +h +8.665000 14.330078 m +9.032269 14.330078 9.330000 14.032348 9.330000 13.665078 c +9.330000 8.940530 l +12.135226 6.135303 l +12.394924 5.875606 12.394924 5.454551 12.135226 5.194852 c +11.875527 4.935153 11.454473 4.935153 11.194774 5.194852 c +8.194774 8.194853 l +8.070063 8.319564 8.000000 8.488708 8.000000 8.665078 c +8.000000 13.665078 l +8.000000 14.032348 8.297730 14.330078 8.665000 14.330078 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1511 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001601 00000 n +0000001624 00000 n +0000001797 00000 n +0000001871 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1930 +%%EOF \ No newline at end of file diff --git a/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Silently.imageset/Contents.json b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Silently.imageset/Contents.json new file mode 100644 index 0000000000..47cbacef29 --- /dev/null +++ b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Silently.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "sendwithoutsound.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Silently.imageset/sendwithoutsound.pdf b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Silently.imageset/sendwithoutsound.pdf new file mode 100644 index 0000000000..8ad58ec31b --- /dev/null +++ b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/Silently.imageset/sendwithoutsound.pdf @@ -0,0 +1,154 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 3.334961 3.205078 cm +0.000000 0.000000 0.000000 scn +1.135226 17.265186 m +0.875527 17.524885 0.454473 17.524885 0.194774 17.265186 c +-0.064925 17.005487 -0.064925 16.584435 0.194774 16.324736 c +16.194775 0.324734 l +16.454473 0.065035 16.875528 0.065035 17.135227 0.324734 c +17.394926 0.584433 17.394926 1.005487 17.135227 1.265186 c +12.830019 5.570395 l +12.830019 13.984108 l +12.830019 14.015735 l +12.830019 14.015746 l +12.830019 14.015758 l +12.830032 14.503190 12.830044 14.912979 12.802612 15.227596 c +12.776955 15.521866 12.718998 15.904351 12.465319 16.192924 c +12.149200 16.552528 11.677307 16.734966 11.201504 16.681524 c +10.819681 16.638639 10.519508 16.394609 10.302576 16.194126 c +10.070642 15.979778 9.794991 15.676547 9.467113 15.315862 c +9.467100 15.315848 l +9.445834 15.292454 l +6.911015 12.504154 l +6.870810 12.460040 l +6.811123 12.459965 l +6.165019 12.459965 l +6.142232 12.459965 l +6.073141 12.459966 6.005919 12.459967 5.940522 12.459891 c +1.135226 17.265186 l +h +7.233788 11.166624 m +11.500019 6.900394 l +11.500019 13.984108 l +11.500019 14.511556 11.499405 14.862436 11.477638 15.112073 c +11.466372 15.241299 11.451292 15.307571 11.443966 15.335192 c +11.425201 15.348718 11.403254 15.357203 11.380270 15.359817 c +11.356270 15.344307 11.300531 15.305413 11.205269 15.217373 c +11.021238 15.047297 10.784756 14.788080 10.429955 14.397799 c +7.895135 11.609499 l +7.876595 11.588985 l +7.810806 11.515999 7.711236 11.405536 7.585287 11.322461 c +7.477557 11.251403 7.358773 11.198743 7.233788 11.166624 c +h +3.310589 11.708942 m +4.251275 10.768256 l +4.114416 10.639768 4.004863 10.482624 3.931640 10.305847 c +3.894677 10.216611 3.864227 10.089064 3.847507 9.844008 c +3.830379 9.592980 3.830019 9.270005 3.830019 8.794965 c +3.830019 8.319924 3.830379 7.996948 3.847507 7.745921 c +3.864227 7.500866 3.894677 7.373319 3.931640 7.284082 c +4.067134 6.956970 4.327024 6.697080 4.654137 6.561585 c +4.743373 6.524623 4.870920 6.494172 5.115975 6.477452 c +5.367003 6.460325 5.689979 6.459965 6.165019 6.459965 c +6.811123 6.459965 l +6.838768 6.460045 l +6.838777 6.460045 l +6.937038 6.460461 7.085750 6.461090 7.231940 6.423779 c +7.357602 6.391706 7.477026 6.338876 7.585287 6.267468 c +7.711238 6.184393 7.810808 6.073929 7.876598 6.000941 c +7.895135 5.980431 l +10.429956 3.192128 l +10.784757 2.801847 11.021239 2.542631 11.205269 2.372556 c +11.300533 2.284515 11.356270 2.245622 11.380270 2.230112 c +11.403254 2.232725 11.425201 2.241211 11.443966 2.254738 c +11.451292 2.282358 11.466372 2.348630 11.477638 2.477856 c +11.498196 2.713622 11.499886 3.039693 11.500010 3.519522 c +12.789057 2.230474 l +12.756243 1.960018 12.682525 1.644088 12.465319 1.397003 c +12.149200 1.037399 11.677307 0.854963 11.201504 0.908403 c +10.819681 0.951288 10.519508 1.195318 10.302577 1.395802 c +10.070659 1.610135 9.795034 1.913337 9.467189 2.273985 c +9.467137 2.274039 l +9.467128 2.274051 l +9.445836 2.297473 l +6.911015 5.085775 l +6.870807 5.129889 l +6.811123 5.129965 l +6.165019 5.129965 l +6.142203 5.129965 l +6.142138 5.129965 l +5.695606 5.129959 5.327103 5.129954 5.025440 5.150537 c +4.712668 5.171878 4.423473 5.217547 4.145168 5.332826 c +3.492168 5.603307 2.973362 6.122113 2.702880 6.775113 c +2.587602 7.053419 2.541932 7.342614 2.520592 7.655386 c +2.500008 7.957066 2.500013 8.325591 2.500019 8.772156 c +2.500019 8.772170 l +2.500019 8.794965 l +2.500019 8.817760 l +2.500019 8.817774 l +2.500013 9.264338 2.500008 9.632866 2.520592 9.934544 c +2.541932 10.247315 2.587602 10.536510 2.702880 10.814816 c +2.843596 11.154535 3.051523 11.457933 3.310589 11.708942 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 3618 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000003708 00000 n +0000003731 00000 n +0000003904 00000 n +0000003978 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +4037 +%%EOF \ No newline at end of file diff --git a/submodules/LegacyComponents/LegacyImages.xcassets/Editor/WhenOnline.imageset/Contents.json b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/WhenOnline.imageset/Contents.json new file mode 100644 index 0000000000..9478d5c466 --- /dev/null +++ b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/WhenOnline.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "sendthenonline.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/LegacyComponents/LegacyImages.xcassets/Editor/WhenOnline.imageset/sendthenonline.pdf b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/WhenOnline.imageset/sendthenonline.pdf new file mode 100644 index 0000000000..c3c0c5df3b --- /dev/null +++ b/submodules/LegacyComponents/LegacyImages.xcassets/Editor/WhenOnline.imageset/sendthenonline.pdf @@ -0,0 +1,123 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 15.000000 4.000000 cm +0.000000 0.000000 0.000000 scn +5.000000 2.500000 m +5.000000 1.119288 3.880712 0.000000 2.500000 0.000000 c +1.119288 0.000000 0.000000 1.119288 0.000000 2.500000 c +0.000000 3.880712 1.119288 5.000000 2.500000 5.000000 c +3.880712 5.000000 5.000000 3.880712 5.000000 2.500000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 3.334961 3.334961 cm +0.000000 0.000000 0.000000 scn +1.330000 8.665078 m +1.330000 12.716087 4.613991 16.000078 8.665000 16.000078 c +12.716008 16.000078 16.000000 12.716087 16.000000 8.665078 c +16.000000 7.971566 15.903754 7.300534 15.723890 6.664610 c +16.154043 6.472708 16.542303 6.203824 16.871166 5.875466 c +17.168650 6.750837 17.330002 7.689115 17.330002 8.665078 c +17.330002 13.450625 13.450547 17.330078 8.665000 17.330078 c +3.879453 17.330078 0.000000 13.450625 0.000000 8.665078 c +0.000000 3.879531 3.879453 0.000076 8.665000 0.000076 c +9.641031 0.000076 10.579370 0.161449 11.454794 0.458973 c +11.126430 0.787828 10.857537 1.176086 10.665626 1.606233 c +10.029655 1.426340 9.358569 1.330078 8.665000 1.330078 c +6.949595 1.330078 5.371725 1.918934 4.122502 2.905533 c +4.297272 3.127586 4.536829 3.393646 4.847776 3.660172 c +5.621715 4.323548 6.847626 5.000078 8.665000 5.000078 c +9.428206 5.000078 10.087104 4.880767 10.652022 4.693223 c +10.837114 5.118156 11.097044 5.502989 11.415169 5.831069 c +10.648278 6.131837 9.735907 6.330078 8.665000 6.330078 c +6.482374 6.330078 4.958285 5.506608 3.982224 4.669984 c +3.639743 4.376429 3.365922 4.082502 3.154341 3.823979 c +2.018686 5.115683 1.330000 6.809955 1.330000 8.665078 c +h +12.678360 5.175269 m +12.317028 4.907584 12.030398 4.544794 11.855478 4.123891 c +12.090899 3.974664 12.299200 3.817050 12.482224 3.660172 c +12.793171 3.393646 13.032728 3.127586 13.207498 2.905533 c +12.806664 2.588965 12.371993 2.313348 11.909800 2.084994 c +12.111738 1.664135 12.428010 1.308554 12.818370 1.058498 c +14.273650 1.854803 15.475398 3.056577 16.271671 4.511877 c +16.021608 4.902231 15.666019 5.218495 15.245155 5.420424 c +14.958824 4.840852 14.598176 4.304554 14.175659 3.823979 c +13.964078 4.082502 13.690256 4.376430 13.347776 4.669984 c +13.147928 4.841282 12.925106 5.012029 12.678360 5.175269 c +h +6.830039 10.665039 m +6.830039 11.678482 7.651597 12.500039 8.665039 12.500039 c +9.678482 12.500039 10.500039 11.678482 10.500039 10.665039 c +10.500039 9.651596 9.678482 8.830039 8.665039 8.830039 c +7.651597 8.830039 6.830039 9.651596 6.830039 10.665039 c +h +8.665039 13.830039 m +6.917058 13.830039 5.500039 12.413020 5.500039 10.665039 c +5.500039 8.917058 6.917058 7.500039 8.665039 7.500039 c +10.413020 7.500039 11.830039 8.917058 11.830039 10.665039 c +11.830039 12.413020 10.413020 13.830039 8.665039 13.830039 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 2777 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000002867 00000 n +0000002890 00000 n +0000003063 00000 n +0000003137 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +3196 +%%EOF \ No newline at end of file diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h index 3e21657bdc..9f9dbbbeec 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h @@ -95,7 +95,7 @@ typedef enum - (UIBarButtonItem *)leftBarButtonItem; - (UIBarButtonItem *)rightBarButtonItem; -- (void)send:(bool)silently; +- (void)send:(bool)silently whenOnline:(bool)whenOnline; - (void)schedule:(bool)schedule; - (NSArray *)resultSignalsWithCurrentItem:(TGMediaAsset *)currentItem descriptionGenerator:(id (^)(id, NSAttributedString *, NSString *, NSString *))descriptionGenerator; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerSendActionSheetController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerSendActionSheetController.h index 2dea552b66..e75e0da4f1 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerSendActionSheetController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaPickerSendActionSheetController.h @@ -6,10 +6,11 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) void (^send)(void); @property (nonatomic, copy) void (^sendSilently)(void); +@property (nonatomic, copy) void (^sendWhenOnline)(void); @property (nonatomic, copy) void (^schedule)(void); @property (nonatomic, copy) void (^sendWithTimer)(void); -- (instancetype)initWithContext:(id)context isDark:(bool)isDark sendButtonFrame:(CGRect)sendButtonFrame canSendSilently:(bool)canSendSilently canSchedule:(bool)canSchedule reminder:(bool)reminder hasTimer:(bool)hasTimer; +- (instancetype)initWithContext:(id)context isDark:(bool)isDark sendButtonFrame:(CGRect)sendButtonFrame canSendSilently:(bool)canSendSilently canSendWhenOnline:(bool)canSendWhenOnline canSchedule:(bool)canSchedule reminder:(bool)reminder hasTimer:(bool)hasTimer; @end diff --git a/submodules/LegacyComponents/Sources/TGCameraController.m b/submodules/LegacyComponents/Sources/TGCameraController.m index a2319afee4..4dc4705dac 100644 --- a/submodules/LegacyComponents/Sources/TGCameraController.m +++ b/submodules/LegacyComponents/Sources/TGCameraController.m @@ -1568,7 +1568,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus } } - TGMediaPickerSendActionSheetController *controller = [[TGMediaPickerSendActionSheetController alloc] initWithContext:strongSelf->_context isDark:true sendButtonFrame:strongModel.interfaceView.doneButtonFrame canSendSilently:strongSelf->_hasSilentPosting canSchedule:effectiveHasSchedule reminder:strongSelf->_reminder hasTimer:strongSelf->_hasTimer]; + TGMediaPickerSendActionSheetController *controller = [[TGMediaPickerSendActionSheetController alloc] initWithContext:strongSelf->_context isDark:true sendButtonFrame:strongModel.interfaceView.doneButtonFrame canSendSilently:strongSelf->_hasSilentPosting canSendWhenOnline:false canSchedule:effectiveHasSchedule reminder:strongSelf->_reminder hasTimer:strongSelf->_hasTimer]; controller.send = ^{ __strong TGCameraController *strongSelf = weakSelf; __strong TGMediaPickerGalleryModel *strongModel = weakModel; @@ -1621,6 +1621,32 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus [strongSelf _dismissTransitionForResultController:strongController]; }; + controller.sendWhenOnline = ^{ + __strong TGCameraController *strongSelf = weakSelf; + __strong TGMediaPickerGalleryModel *strongModel = weakModel; + + if (strongSelf == nil || strongModel == nil) + return; + + __strong TGModernGalleryController *strongController = weakGalleryController; + if (strongController == nil) + return; + + if ([item isKindOfClass:[TGMediaPickerGalleryVideoItem class]]) + { + TGMediaPickerGalleryVideoItemView *itemView = (TGMediaPickerGalleryVideoItemView *)[strongController itemViewForItem:item]; + [itemView stop]; + [itemView setPlayButtonHidden:true animated:true]; + } + + if (strongSelf->_selectionContext.allowGrouping) + [[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"]; + + if (strongSelf.finishedWithResults != nil) + strongSelf.finishedWithResults(strongController, strongSelf->_selectionContext, strongSelf->_editingContext, item.asset, false, 0x7ffffffe); + + [strongSelf _dismissTransitionForResultController:strongController]; + }; controller.schedule = ^{ __strong TGCameraController *strongSelf = weakSelf; if (strongSelf == nil) diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m index 6b2cd0c26e..8d152ab221 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m @@ -1711,9 +1711,9 @@ } } -- (void)send:(bool)silently +- (void)send:(bool)silently whenOnline:(bool)whenOnlne { - [self completeWithCurrentItem:nil silentPosting:silently scheduleTime:0]; + [self completeWithCurrentItem:nil silentPosting:silently scheduleTime:whenOnlne ? 0x7ffffffe : 0]; } - (void)schedule:(bool)media { diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m b/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m index c81218980e..b5103bb1f8 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m @@ -165,7 +165,7 @@ } } - TGMediaPickerSendActionSheetController *controller = [[TGMediaPickerSendActionSheetController alloc] initWithContext:strongSelf->_context isDark:true sendButtonFrame:strongSelf.galleryModel.interfaceView.doneButtonFrame canSendSilently:hasSilentPosting canSchedule:effectiveHasSchedule reminder:reminder hasTimer:hasTimer]; + TGMediaPickerSendActionSheetController *controller = [[TGMediaPickerSendActionSheetController alloc] initWithContext:strongSelf->_context isDark:true sendButtonFrame:strongSelf.galleryModel.interfaceView.doneButtonFrame canSendSilently:hasSilentPosting canSendWhenOnline:true canSchedule:effectiveHasSchedule reminder:reminder hasTimer:hasTimer]; controller.send = ^{ __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; if (strongSelf == nil) @@ -186,6 +186,16 @@ if (strongSelf.completeWithItem != nil) strongSelf.completeWithItem(item, true, 0); }; + controller.sendWhenOnline = ^{ + __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + strongSelf->_galleryModel.dismiss(true, false); + + if (strongSelf.completeWithItem != nil) + strongSelf.completeWithItem(item, false, 0x7ffffffe); + }; controller.schedule = ^{ __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; if (strongSelf == nil) diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerSendActionSheetController.m b/submodules/LegacyComponents/Sources/TGMediaPickerSendActionSheetController.m index 5916515eb6..e54924dd5b 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerSendActionSheetController.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerSendActionSheetController.m @@ -105,6 +105,7 @@ bool _isDark; CGRect _sendButtonFrame; bool _canSendSilently; + bool _canSendWhenOnline; bool _canSchedule; bool _reminder; bool _hasTimer; @@ -117,6 +118,7 @@ UIView *_containerView; UIView *_separatorView; TGMediaPickerSendActionSheetItemView *_sendSilentlyButton; + TGMediaPickerSendActionSheetItemView *_sendWhenOnlineButton; TGMediaPickerSendActionSheetItemView *_scheduleButton; TGMediaPickerSendActionSheetItemView *_timerButton; } @@ -124,13 +126,14 @@ @implementation TGMediaPickerSendActionSheetController -- (instancetype)initWithContext:(id)context isDark:(bool)isDark sendButtonFrame:(CGRect)sendButtonFrame canSendSilently:(bool)canSendSilently canSchedule:(bool)canSchedule reminder:(bool)reminder hasTimer:(bool)hasTimer { +- (instancetype)initWithContext:(id)context isDark:(bool)isDark sendButtonFrame:(CGRect)sendButtonFrame canSendSilently:(bool)canSendSilently canSendWhenOnline:(bool)canSendWhenOnline canSchedule:(bool)canSchedule reminder:(bool)reminder hasTimer:(bool)hasTimer { self = [super initWithContext:context]; if (self != nil) { _context = context; _isDark = isDark; _sendButtonFrame = sendButtonFrame; _canSendSilently = canSendSilently; + _canSendWhenOnline = canSendWhenOnline; _canSchedule = canSchedule; _reminder = reminder; _hasTimer = hasTimer; @@ -163,7 +166,7 @@ __weak TGMediaPickerSendActionSheetController *weakSelf = self; if (_canSendSilently) { - _sendSilentlyButton = [[TGMediaPickerSendActionSheetItemView alloc] initWithTitle:TGLocalized(@"Conversation.SendMessage.SendSilently") icon:TGComponentsImageNamed(@"MediaMute") isDark:_isDark isLast:!_canSchedule && !_hasTimer]; + _sendSilentlyButton = [[TGMediaPickerSendActionSheetItemView alloc] initWithTitle:TGLocalized(@"Conversation.SendMessage.SendSilently") icon:TGComponentsImageNamed(@"Editor/Silently") isDark:_isDark isLast:!_canSchedule && !_hasTimer && !_canSendWhenOnline]; _sendSilentlyButton.pressed = ^{ __strong TGMediaPickerSendActionSheetController *strongSelf = weakSelf; [strongSelf sendSilentlyPressed]; @@ -171,8 +174,17 @@ [_containerView addSubview:_sendSilentlyButton]; } + if (_canSendWhenOnline) { + _sendWhenOnlineButton = [[TGMediaPickerSendActionSheetItemView alloc] initWithTitle:TGLocalized(@"Conversation.SendMessage.SendWhenOnline") icon:TGComponentsImageNamed(@"Editor/WhenOnline") isDark:_isDark isLast:!_canSchedule && !_hasTimer]; + _sendWhenOnlineButton.pressed = ^{ + __strong TGMediaPickerSendActionSheetController *strongSelf = weakSelf; + [strongSelf sendWhenOnlinePressed]; + }; + [_containerView addSubview:_sendWhenOnlineButton]; + } + if (_canSchedule) { - _scheduleButton = [[TGMediaPickerSendActionSheetItemView alloc] initWithTitle:TGLocalized(_reminder ? @"Conversation.SendMessage.SetReminder" : @"Conversation.SendMessage.ScheduleMessage") icon:TGComponentsImageNamed(@"MediaSchedule") isDark:_isDark isLast:!_hasTimer]; + _scheduleButton = [[TGMediaPickerSendActionSheetItemView alloc] initWithTitle:TGLocalized(_reminder ? @"Conversation.SendMessage.SetReminder" : @"Conversation.SendMessage.ScheduleMessage") icon:TGComponentsImageNamed(@"Editor/Schedule") isDark:_isDark isLast:!_hasTimer]; _scheduleButton.pressed = ^{ __strong TGMediaPickerSendActionSheetController *strongSelf = weakSelf; [strongSelf schedulePressed]; @@ -295,8 +307,8 @@ CGFloat itemHeight = 44.0; CGFloat containerWidth = 240.0; - CGFloat containerHeight = (_canSendSilently + _canSchedule + _hasTimer) * itemHeight; - containerWidth = MAX(containerWidth, MAX(_timerButton.buttonLabel.frame.size.width, MAX(_sendSilentlyButton.buttonLabel.frame.size.width, _scheduleButton.buttonLabel.frame.size.width)) + 84.0); + CGFloat containerHeight = (_canSendSilently + _canSchedule + _hasTimer + _canSendWhenOnline) * itemHeight; + containerWidth = MAX(containerWidth, MAX(_timerButton.buttonLabel.frame.size.width, MAX(_sendSilentlyButton.buttonLabel.frame.size.width, MAX(_sendWhenOnlineButton.buttonLabel.frame.size.width, _scheduleButton.buttonLabel.frame.size.width))) + 84.0); if (!_dismissed) { _containerView.frame = CGRectMake(CGRectGetMaxX(_sendButtonFrame) - containerWidth - 8.0, _sendButtonFrame.origin.y - containerHeight - 4.0, containerWidth, containerHeight); } @@ -305,6 +317,9 @@ _sendSilentlyButton.frame = CGRectMake(0.0, offset, containerWidth, itemHeight); offset += _sendSilentlyButton.frame.size.height; + _sendWhenOnlineButton.frame = CGRectMake(0.0, offset, containerWidth, itemHeight); + offset += _sendWhenOnlineButton.frame.size.height; + _scheduleButton.frame = CGRectMake(0.0, offset, containerWidth, itemHeight); offset += _scheduleButton.frame.size.height; @@ -327,6 +342,14 @@ self.sendSilently(); } +- (void)sendWhenOnlinePressed { + [self animateOut:false]; + + if (self.sendWhenOnline != nil) + self.sendWhenOnline(); +} + + - (void)schedulePressed { [self animateOut:false]; diff --git a/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m b/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m index e9cc6b88c6..0e62e7d2c2 100644 --- a/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m +++ b/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m @@ -776,7 +776,7 @@ typedef enum [_generator impactOccurred]; } - TGMediaPickerSendActionSheetController *controller = [[TGMediaPickerSendActionSheetController alloc] initWithContext:_context isDark:self.pallete.isDark sendButtonFrame:[_controlsView convertRect:[_controlsView frameForSendButton] toView:nil] canSendSilently:_canSendSilently canSchedule:_canSchedule reminder:_reminder hasTimer:false]; + TGMediaPickerSendActionSheetController *controller = [[TGMediaPickerSendActionSheetController alloc] initWithContext:_context isDark:self.pallete.isDark sendButtonFrame:[_controlsView convertRect:[_controlsView frameForSendButton] toView:nil] canSendSilently:_canSendSilently canSendWhenOnline:false canSchedule:_canSchedule reminder:_reminder hasTimer:false]; __weak TGVideoMessageCaptureController *weakSelf = self; controller.send = ^{ __strong TGVideoMessageCaptureController *strongSelf = weakSelf; @@ -800,6 +800,17 @@ typedef enum _automaticDismiss = true; [strongSelf dismiss:false]; }; + controller.sendWhenOnline = ^{ + __strong TGVideoMessageCaptureController *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + + [strongSelf finishWithURL:strongSelf->_url dimensions:CGSizeMake(240.0f, 240.0f) duration:strongSelf->_duration liveUploadData:strongSelf->_liveUploadData thumbnailImage:strongSelf->_thumbnailImage isSilent:false scheduleTimestamp:0x7ffffffe]; + + _automaticDismiss = true; + [strongSelf dismiss:false]; + }; controller.schedule = ^{ __strong TGVideoMessageCaptureController *strongSelf = weakSelf; if (strongSelf == nil) { diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift index 601828c1ad..373f976b4b 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift @@ -105,8 +105,8 @@ public class LegacyAssetPickerContext: AttachmentMediaPickerContext { self.controller?.editingContext.setForcedCaption(caption, skipUpdate: true) } - public func send(silently: Bool, mode: AttachmentMediaPickerSendMode) { - self.controller?.send(silently) + public func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) { + self.controller?.send(mode == .silently, whenOnline: mode == .whenOnline) } public func schedule() { diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index d70344413e..4f6cd10a7b 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -389,7 +389,7 @@ private final class LocationPickerContext: AttachmentMediaPickerContext { func setCaption(_ caption: NSAttributedString) { } - func send(silently: Bool, mode: AttachmentMediaPickerSendMode) { + func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) { } func schedule() { diff --git a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift index b135d60a43..d2e17bb4dc 100644 --- a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift +++ b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift @@ -236,52 +236,82 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?, } } } - - let legacySheetController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil) - let sheetController = TGMediaPickerSendActionSheetController(context: legacyController.context, isDark: true, sendButtonFrame: model.interfaceView.doneButtonFrame, canSendSilently: hasSilentPosting, canSchedule: effectiveHasSchedule, reminder: reminder, hasTimer: hasTimer) - let dismissImpl = { [weak model] in - model?.dismiss(true, false) - dismissAll() - } - sheetController.send = { - completed(item.asset, false, nil, { - dismissImpl() - }) - } - sheetController.sendSilently = { - completed(item.asset, true, nil, { - dismissImpl() - }) - } - sheetController.schedule = { - presentSchedulePicker(true, { time in - completed(item.asset, false, time, { - dismissImpl() - }) - }) - } - sheetController.sendWithTimer = { - presentTimerPicker { time in - var items = selectionContext.selectedItems() ?? [] - items.append(item.asset as Any) - - for case let item as TGMediaEditableItem in items { - editingContext?.setTimer(time as NSNumber, for: item) + + let sendWhenOnlineAvailable: Signal + if let peer { + sendWhenOnlineAvailable = context.account.viewTracker.peerView(peer.id) + |> take(1) + |> map { peerView -> Bool in + guard let peer = peerViewMainPeer(peerView) else { + return false } - + var sendWhenOnlineAvailable = false + if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case .present = presence.status { + sendWhenOnlineAvailable = true + } + if peer.id.namespace == Namespaces.Peer.CloudUser && peer.id.id._internalGetInt64Value() == 777000 { + sendWhenOnlineAvailable = false + } + return sendWhenOnlineAvailable + } + } else { + sendWhenOnlineAvailable = .single(false) + } + + let _ = (sendWhenOnlineAvailable + |> take(1) + |> deliverOnMainQueue).start(next: { sendWhenOnlineAvailable in + let legacySheetController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil) + let sheetController = TGMediaPickerSendActionSheetController(context: legacyController.context, isDark: true, sendButtonFrame: model.interfaceView.doneButtonFrame, canSendSilently: hasSilentPosting, canSendWhenOnline: sendWhenOnlineAvailable, canSchedule: effectiveHasSchedule, reminder: reminder, hasTimer: hasTimer) + let dismissImpl = { [weak model] in + model?.dismiss(true, false) + dismissAll() + } + sheetController.send = { completed(item.asset, false, nil, { dismissImpl() }) } - } - sheetController.customDismissBlock = { [weak legacySheetController] in - legacySheetController?.dismiss() - } - legacySheetController.bind(controller: sheetController) - present(legacySheetController, nil) - - let hapticFeedback = HapticFeedback() - hapticFeedback.impact() + sheetController.sendSilently = { + completed(item.asset, true, nil, { + dismissImpl() + }) + } + sheetController.sendWhenOnline = { + completed(item.asset, false, scheduleWhenOnlineTimestamp, { + dismissImpl() + }) + } + sheetController.schedule = { + presentSchedulePicker(true, { time in + completed(item.asset, false, time, { + dismissImpl() + }) + }) + } + sheetController.sendWithTimer = { + presentTimerPicker { time in + var items = selectionContext.selectedItems() ?? [] + items.append(item.asset as Any) + + for case let item as TGMediaEditableItem in items { + editingContext?.setTimer(time as NSNumber, for: item) + } + + completed(item.asset, false, nil, { + dismissImpl() + }) + } + } + sheetController.customDismissBlock = { [weak legacySheetController] in + legacySheetController?.dismiss() + } + legacySheetController.bind(controller: sheetController) + present(legacySheetController, nil) + + let hapticFeedback = HapticFeedback() + hapticFeedback.impact() + }) } } model.interfaceView.setThumbnailSignalForItem { item in diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 57b6f59862..5c6ca1ffd3 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -408,17 +408,26 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } if let controller = self.controller, case .assets(nil) = controller.subject { + let enableAnimations = self.controller?.context.sharedContext.energyUsageSettings.fullTranslucency ?? true + let cameraView = TGAttachmentCameraView(forSelfPortrait: false, videoModeByDefault: controller.bannedSendPhotos != nil && controller.bannedSendVideos == nil)! cameraView.clipsToBounds = true cameraView.removeCorners() - cameraView.pressed = { [weak self] in + cameraView.pressed = { [weak self, weak cameraView] in if let strongSelf = self, !strongSelf.openingMedia { strongSelf.dismissInput() strongSelf.controller?.openCamera?(strongSelf.cameraView) + + if !enableAnimations { + cameraView?.startPreview() + } } } self.cameraView = cameraView - cameraView.startPreview() + + if enableAnimations { + cameraView.startPreview() + } self.gridNode.scrollView.addSubview(cameraView) self.gridNode.addSubnode(self.cameraActivateAreaNode) @@ -1758,8 +1767,8 @@ final class MediaPickerContext: AttachmentMediaPickerContext { self.interaction?.editingState.setForcedCaption(caption, skipUpdate: true) } - func send(silently: Bool, mode: AttachmentMediaPickerSendMode) { - self.interaction?.sendSelected(nil, silently, nil, true, {}) + func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) { + self.interaction?.sendSelected(nil, mode == .silently, mode == .whenOnline ? scheduleWhenOnlineTimestamp : nil, true, {}) } func schedule() { diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift index 80bc21a037..f0fc00b5b7 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift @@ -1406,14 +1406,14 @@ public func channelAdminController(context: AccountContext, updatedPresentationD footerItem = ChannelAdminAddBotFooterItem(theme: presentationData.theme, title: state.adminRights ? presentationData.strings.Bot_AddToChat_Add_AddAsAdmin : presentationData.strings.Bot_AddToChat_Add_AddAsMember, action: { if state.adminRights { let theme = AlertControllerTheme(presentationData: presentationData) - let attributedTitle = NSAttributedString(string: presentationData.strings.Bot_AddToChat_Add_AdminAlertTitle, font: Font.medium(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) + let attributedTitle = NSAttributedString(string: presentationData.strings.Bot_AddToChat_Add_AdminAlertTitle, font: Font.semibold(presentationData.listsFontSize.baseDisplaySize), textColor: theme.primaryColor, paragraphAlignment: .center) let text = isGroup ? presentationData.strings.Bot_AddToChat_Add_AdminAlertTextGroup(peerTitle).string : presentationData.strings.Bot_AddToChat_Add_AdminAlertTextChannel(peerTitle).string - let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor) + let body = MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) + let bold = MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - + let controller = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Bot_AddToChat_Add_AdminAlertAdd, action: { rightButtonActionImpl() }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { diff --git a/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift b/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift index ab95da6445..db0e459c4d 100644 --- a/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelOwnershipTransferController.swift @@ -559,9 +559,9 @@ private func confirmChannelOwnershipTransferController(context: AccountContext, text = presentationData.strings.Channel_OwnershipTransfer_DescriptionInfo(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), EnginePeer(member).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string } - let attributedTitle = NSAttributedString(string: title, font: Font.medium(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor) + let attributedTitle = NSAttributedString(string: title, font: Font.semibold(presentationData.listsFontSize.baseDisplaySize), textColor: theme.primaryColor, paragraphAlignment: .center) + let body = MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) + let bold = MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) let controller = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Channel_OwnershipTransfer_ChangeOwner, action: { @@ -575,16 +575,17 @@ func channelOwnershipTransferController(context: AccountContext, updatedPresenta let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } let theme = AlertControllerTheme(presentationData: presentationData) - var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.medium(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center) + var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.semibold(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center) var text = presentationData.strings.OwnershipTransfer_SecurityRequirements + let textFontSize = presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0 + var isGroup = true if let channel = peer as? TelegramChannel, case .broadcast = channel.info { isGroup = false } var actions: [TextAlertAction] = [] - switch initialError { case .requestPassword: return confirmChannelOwnershipTransferController(context: context, updatedPresentationData: updatedPresentationData, peer: peer, member: member, present: present, completion: completion) @@ -618,8 +619,8 @@ func channelOwnershipTransferController(context: AccountContext, updatedPresenta actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] } - let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor) + let body = MarkdownAttributeSet(font: Font.regular(textFontSize), textColor: theme.primaryColor) + let bold = MarkdownAttributeSet(font: Font.semibold(textFontSize), textColor: theme.primaryColor) let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) return richTextAlertController(context: context, title: title, text: attributedText, actions: actions) diff --git a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift index 05415756a0..6031dda89c 100644 --- a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift @@ -966,9 +966,9 @@ public func channelPermissionsController(context: AccountContext, updatedPresent controller.navigationPresentation = .modal controller.setState(.custom(icon: .animation("BroadcastGroup"), title: presentationData.strings.BroadcastGroups_IntroTitle, subtitle: nil, text: presentationData.strings.BroadcastGroups_IntroText, buttonTitle: presentationData.strings.BroadcastGroups_Convert, secondaryButtonTitle: presentationData.strings.BroadcastGroups_Cancel, footerText: nil), animated: false) controller.proceed = { [weak controller] result in - let attributedTitle = NSAttributedString(string: presentationData.strings.BroadcastGroups_ConfirmationAlert_Title, font: Font.medium(17.0), textColor: presentationData.theme.actionSheet.primaryTextColor, paragraphAlignment: .center) - let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: presentationData.theme.actionSheet.primaryTextColor) - let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: presentationData.theme.actionSheet.primaryTextColor) + let attributedTitle = NSAttributedString(string: presentationData.strings.BroadcastGroups_ConfirmationAlert_Title, font: Font.semibold(presentationData.listsFontSize.baseDisplaySize), textColor: presentationData.theme.actionSheet.primaryTextColor, paragraphAlignment: .center) + let body = MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: presentationData.theme.actionSheet.primaryTextColor) + let bold = MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: presentationData.theme.actionSheet.primaryTextColor) let attributedText = parseMarkdownIntoAttributedString(presentationData.strings.BroadcastGroups_ConfirmationAlert_Text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) let alertController = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.BroadcastGroups_ConfirmationAlert_Convert, action: { [weak controller] in diff --git a/submodules/PremiumUI/Sources/PhoneDemoComponent.swift b/submodules/PremiumUI/Sources/PhoneDemoComponent.swift index 7e0530f6ab..fdbfa8d939 100644 --- a/submodules/PremiumUI/Sources/PhoneDemoComponent.swift +++ b/submodules/PremiumUI/Sources/PhoneDemoComponent.swift @@ -457,6 +457,7 @@ final class PhoneDemoComponent: Component { self.containerView.clipsToBounds = true self.phoneView = PhoneView(frame: CGRect(origin: .zero, size: phoneSize)) + self.phoneView.isUserInteractionEnabled = false super.init(frame: frame) diff --git a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift index bf6f407549..2ef5236446 100644 --- a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift @@ -283,15 +283,18 @@ final class DemoPagerComponent: Component { let items: [Item] let index: Int + let nextAction: ActionSlot? let updated: (CGFloat, Int) -> Void public init( items: [Item], index: Int = 0, + nextAction: ActionSlot? = nil, updated: @escaping (CGFloat, Int) -> Void ) { self.items = items self.index = index + self.nextAction = nextAction self.updated = updated } @@ -346,6 +349,17 @@ final class DemoPagerComponent: Component { func update(component: DemoPagerComponent, availableSize: CGSize, transition: Transition) -> CGSize { var validIds: [AnyHashable] = [] + component.nextAction?.connect { [weak self] in + if let self { + var nextContentOffset = self.scrollView.contentOffset + nextContentOffset.x += self.scrollView.frame.width + if nextContentOffset.x >= self.scrollView.contentSize.width { + nextContentOffset.x = 0.0 + } + self.scrollView.contentOffset = nextContentOffset + } + } + let firstTime = self.itemViews.isEmpty let contentSize = CGSize(width: availableSize.width * CGFloat(component.items.count), height: availableSize.height) @@ -787,7 +801,8 @@ private final class DemoSheetContent: CombinedComponent { content: AnyComponent( StickersCarouselComponent( context: component.context, - stickers: stickers + stickers: stickers, + tapAction: {} ) ), title: strings.Premium_Stickers, diff --git a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift index 8faff377d4..3aaffe1d37 100644 --- a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift @@ -1145,7 +1145,7 @@ private final class PremiumGiftContext: AttachmentMediaPickerContext { func setCaption(_ caption: NSAttributedString) { } - func send(silently: Bool, mode: AttachmentMediaPickerSendMode) { + func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) { } func schedule() { diff --git a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift index b85422c728..d9340ea5e4 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift @@ -618,6 +618,8 @@ public class PremiumLimitsListScreen: ViewController { var disposable: Disposable? var promoConfiguration: PremiumPromoConfiguration? + let nextAction = ActionSlot() + init(context: AccountContext, controller: PremiumLimitsListScreen, buttonTitle: String, gloss: Bool) { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -761,6 +763,8 @@ public class PremiumLimitsListScreen: ViewController { self.dim.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:)))) + self.pagerView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.pagerTapGesture(_:)))) + self.controller?.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate) } @@ -770,6 +774,12 @@ public class PremiumLimitsListScreen: ViewController { } } + @objc func pagerTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.nextAction.invoke(Void()) + } + } + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if let layout = self.currentLayout { if case .regular = layout.metrics.widthClass { @@ -1067,7 +1077,10 @@ public class PremiumLimitsListScreen: ViewController { content: AnyComponent( StickersCarouselComponent( context: context, - stickers: stickers + stickers: stickers, + tapAction: { [weak self] in + self?.nextAction.invoke(Void()) + } ) ), title: strings.Premium_Stickers, @@ -1224,6 +1237,7 @@ public class PremiumLimitsListScreen: ViewController { DemoPagerComponent( items: items, index: index, + nextAction: nextAction, updated: { [weak self] position, count in if let strongSelf = self { strongSelf.footerNode.updatePosition(position, count: count) diff --git a/submodules/PremiumUI/Sources/StickersCarouselComponent.swift b/submodules/PremiumUI/Sources/StickersCarouselComponent.swift index 1238fc5bfc..bedd056e90 100644 --- a/submodules/PremiumUI/Sources/StickersCarouselComponent.swift +++ b/submodules/PremiumUI/Sources/StickersCarouselComponent.swift @@ -19,13 +19,16 @@ final class StickersCarouselComponent: Component { let context: AccountContext let stickers: [TelegramMediaFile] + let tapAction: () -> Void public init( context: AccountContext, - stickers: [TelegramMediaFile] + stickers: [TelegramMediaFile], + tapAction: @escaping () -> Void ) { self.context = context self.stickers = stickers + self.tapAction = tapAction } public static func ==(lhs: StickersCarouselComponent, rhs: StickersCarouselComponent) -> Bool { @@ -48,7 +51,8 @@ final class StickersCarouselComponent: Component { if self.node == nil && !component.stickers.isEmpty { let node = StickersCarouselNode( context: component.context, - stickers: component.stickers + stickers: component.stickers, + tapAction: component.tapAction ) self.node = node self.addSubnode(node) @@ -278,6 +282,8 @@ private class StickerNode: ASDisplayNode { private class StickersCarouselNode: ASDisplayNode, UIScrollViewDelegate { private let context: AccountContext private let stickers: [TelegramMediaFile] + private let tapAction: () -> Void + private var itemContainerNodes: [ASDisplayNode] = [] private var itemNodes: [Int: StickerNode] = [:] private let scrollNode: ASScrollNode @@ -296,9 +302,10 @@ private class StickersCarouselNode: ASDisplayNode, UIScrollViewDelegate { private var previousInteractionTimestamp: Double = 0.0 private var timer: SwiftSignalKit.Timer? - init(context: AccountContext, stickers: [TelegramMediaFile]) { + init(context: AccountContext, stickers: [TelegramMediaFile], tapAction: @escaping () -> Void) { self.context = context self.stickers = stickers + self.tapAction = tapAction self.scrollNode = ASScrollNode() self.tapNode = ASDisplayNode() @@ -335,11 +342,17 @@ private class StickersCarouselNode: ASDisplayNode, UIScrollViewDelegate { @objc private func stickerTapped(_ gestureRecognizer: UITapGestureRecognizer) { self.previousInteractionTimestamp = CACurrentMediaTime() + 1.0 + let point = gestureRecognizer.location(in: self.view) + let size = self.bounds.size + if point.y > size.height / 3.0 && point.y < size.height - size.height / 3.0 { + self.tapAction() + return + } + guard self.animator == nil, self.scrollStartPosition == nil else { return } - let point = gestureRecognizer.location(in: self.view) guard let index = self.itemContainerNodes.firstIndex(where: { $0.frame.contains(point) }) else { return } diff --git a/submodules/PresentationDataUtils/Sources/AlertTheme.swift b/submodules/PresentationDataUtils/Sources/AlertTheme.swift index 8de1164e1c..610480dfc7 100644 --- a/submodules/PresentationDataUtils/Sources/AlertTheme.swift +++ b/submodules/PresentationDataUtils/Sources/AlertTheme.swift @@ -31,3 +31,19 @@ public func textAlertController(sharedContext: SharedAccountContext, title: Stri public func richTextAlertController(context: AccountContext, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissAutomatically: Bool = true) -> AlertController { return richTextAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: context.sharedContext.currentPresentationData.with { $0 }), themeSignal: context.sharedContext.presentationData |> map { presentationData in AlertControllerTheme(presentationData: presentationData) }), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissAutomatically: dismissAutomatically) } + +public func textWithEntitiesAlertController(context: AccountContext, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissAutomatically: Bool = true) -> AlertController { + return textWithEntitiesAlertController( + alertContext: AlertControllerContext( + theme: AlertControllerTheme(presentationData: context.sharedContext.currentPresentationData.with { $0 }), + themeSignal: context.sharedContext.presentationData |> map { presentationData in AlertControllerTheme(presentationData: presentationData) } + ), + title: title, + text: text, + actions: actions, + actionLayout: actionLayout, + allowInputInset: allowInputInset, + dismissAutomatically: dismissAutomatically + ) +} + diff --git a/submodules/SettingsUI/Sources/Themes/ThemeGridControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeGridControllerNode.swift index 0e5c284b63..279e96d0f5 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeGridControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeGridControllerNode.swift @@ -375,6 +375,16 @@ final class ThemeGridControllerNode: ASDisplayNode { sortedWallpapers = wallpapers.map(\.wallpaper) } + if let builtinIndex = sortedWallpapers.firstIndex(where: { wallpaper in + if case .builtin = wallpaper { + return true + } else { + return false + } + }) { + sortedWallpapers[builtinIndex] = defaultBuiltinWallpaper(data: .legacy, colors: legacyBuiltinWallpaperGradientColors.map(\.rgb)) + } + for wallpaper in sortedWallpapers { if case let .file(file) = wallpaper, (wallpaper.isPattern && file.settings.colors.isEmpty) { continue diff --git a/submodules/ShareController/Sources/SharePeersContainerNode.swift b/submodules/ShareController/Sources/SharePeersContainerNode.swift index 663f389ea8..a9f094be3a 100644 --- a/submodules/ShareController/Sources/SharePeersContainerNode.swift +++ b/submodules/ShareController/Sources/SharePeersContainerNode.swift @@ -697,11 +697,11 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { if node.isHidden { continue } - if let result = node.hitTest(point.offsetBy(dx: -nodeFrame.minX, dy: -nodeFrame.minY), with: event) { + if let result = node.hitTest(point.offsetBy(dx: -self.headerNode.frame.minX, dy: -self.headerNode.frame.minY).offsetBy(dx: -nodeFrame.minX, dy: -nodeFrame.minY), with: event) { return result } } - + return super.hitTest(point, with: event) } diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 3a3700f15f..21d4bc461f 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -255,9 +255,11 @@ private final class StickerPackContainer: ASDisplayNode { guard let strongSelf = self, !strongSelf.isDismissed else { return } + if let (layout, _, _, _) = strongSelf.validLayout, case .regular = layout.metrics.widthClass { + return + } let contentOffset = strongSelf.gridNode.scrollView.contentOffset let insets = strongSelf.gridNode.scrollView.contentInset - if contentOffset.y <= -insets.top - 30.0 { strongSelf.isDismissed = true DispatchQueue.main.async { @@ -1203,7 +1205,9 @@ private final class StickerPackContainer: ASDisplayNode { func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { var insets = layout.insets(options: [.statusBar]) - if case .compact = layout.metrics.widthClass, layout.size.width > layout.size.height { + if case .regular = layout.metrics.widthClass { + insets.top = 0.0 + } else if case .compact = layout.metrics.widthClass, layout.size.width > layout.size.height { insets.top = 0.0 } else { insets.top += 10.0 @@ -1271,11 +1275,14 @@ private final class StickerPackContainer: ASDisplayNode { let initialRevealedRowCount: CGFloat = 4.5 - let topInset = max(0.0, layout.size.height - floor(initialRevealedRowCount * itemWidth) - insets.top - actionAreaHeight - titleAreaInset) - + let topInset: CGFloat + if case .regular = layout.metrics.widthClass { + topInset = 0.0 + } else { + topInset = insets.top + max(0.0, layout.size.height - floor(initialRevealedRowCount * itemWidth) - insets.top - actionAreaHeight - titleAreaInset) + } let additionalGridBottomInset = max(0.0, gridFrame.size.height - actionAreaHeight - contentHeight) - - let gridInsets = UIEdgeInsets(top: insets.top + topInset, left: gridLeftInset, bottom: actionAreaHeight + additionalGridBottomInset, right: layout.size.width - fillingWidth - gridLeftInset) + let gridInsets = UIEdgeInsets(top: topInset, left: gridLeftInset, bottom: actionAreaHeight + additionalGridBottomInset, right: layout.size.width - fillingWidth - gridLeftInset) let firstTime = self.validLayout == nil self.validLayout = (layout, gridFrame, titleAreaInset, gridInsets) @@ -1354,9 +1361,16 @@ private final class StickerPackContainer: ASDisplayNode { self.titleSeparatorNode.layer.removeAllAnimations() } - let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: max(minBackgroundY, unclippedBackgroundY)), size: CGSize(width: layout.size.width, height: layout.size.height)) + var backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: max(minBackgroundY, unclippedBackgroundY)), size: CGSize(width: layout.size.width, height: layout.size.height)) + var titleContainerFrame: CGRect + if case .regular = layout.metrics.widthClass { + backgroundFrame.origin.y = min(0.0, backgroundFrame.origin.y) + titleContainerFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + floor((backgroundFrame.width) / 2.0), y: floor((56.0) / 2.0)), size: CGSize()) + } else { + titleContainerFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + floor((backgroundFrame.width) / 2.0), y: backgroundFrame.minY + floor((56.0) / 2.0)), size: CGSize()) + } transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - transition.updateFrame(node: self.titleContainer, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + floor((backgroundFrame.width) / 2.0), y: backgroundFrame.minY + floor((56.0) / 2.0)), size: CGSize())) + transition.updateFrame(node: self.titleContainer, frame: titleContainerFrame) transition.updateFrame(node: self.titleSeparatorNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY + 56.0 - UIScreenPixel), size: CGSize(width: backgroundFrame.width, height: UIScreenPixel))) transition.updateFrame(node: self.titleBackgroundnode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.width, height: 56.0))) self.titleBackgroundnode.update(size: CGSize(width: layout.size.width, height: 56.0), transition: .immediate) @@ -1427,6 +1441,8 @@ private final class StickerPackScreenNode: ViewControllerTracingNode { private let openMention: (String) -> Void private let dimNode: ASDisplayNode + private let shadowNode: ASImageNode + private let arrowNode: ASImageNode private let containerContainingNode: ASDisplayNode private var containers: [Int: StickerPackContainer] = [:] @@ -1475,12 +1491,23 @@ private final class StickerPackScreenNode: ViewControllerTracingNode { self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.25) self.dimNode.alpha = 0.0 + self.shadowNode = ASImageNode() + self.shadowNode.displaysAsynchronously = false + self.shadowNode.isUserInteractionEnabled = false + + self.arrowNode = ASImageNode() + self.arrowNode.displaysAsynchronously = false + self.arrowNode.isUserInteractionEnabled = false + self.arrowNode.image = generateArrowImage(color: self.presentationData.theme.actionSheet.opaqueItemBackgroundColor) + self.containerContainingNode = ASDisplayNode() + self.containerContainingNode.clipsToBounds = true super.init() self.addSubnode(self.dimNode) - + self.addSubnode(self.shadowNode) + self.addSubnode(self.arrowNode) self.addSubnode(self.containerContainingNode) } @@ -1494,6 +1521,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode { for (_, container) in self.containers { container.updatePresentationData(presentationData) } + self.arrowNode.image = generateArrowImage(color: presentationData.theme.actionSheet.opaqueItemBackgroundColor) } func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { @@ -1502,10 +1530,65 @@ private final class StickerPackScreenNode: ViewControllerTracingNode { self.validLayout = layout transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - transition.updateFrame(node: self.containerContainingNode, frame: CGRect(origin: CGPoint(), size: layout.size)) + + let containerContainingFrame: CGRect + let containerInsets: UIEdgeInsets + if case .regular = layout.metrics.widthClass { + self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.01) + self.containerContainingNode.cornerRadius = 10.0 + + let size = CGSize(width: 390.0, height: min(620.0, layout.size.height - 60.0)) + var contentRect: CGRect + if let sourceRect = self.controller?.getSourceRect?() { + let sideSpacing: CGFloat = 10.0 + let margin: CGFloat = 64.0 + contentRect = CGRect(origin: CGPoint(x: sourceRect.maxX + sideSpacing, y: floor(sourceRect.midY - size.height / 2.0)), size: size) + contentRect.origin.y = min(layout.size.height - margin - size.height - layout.intrinsicInsets.bottom, max(margin, contentRect.origin.y)) + + let arrowSize = CGSize(width: 23.0, height: 12.0) + let arrowFrame: CGRect + if contentRect.maxX > layout.size.width { + contentRect.origin.x = sourceRect.minX - size.width - sideSpacing + arrowFrame = CGRect(origin: CGPoint(x: contentRect.maxX - (arrowSize.width - arrowSize.height) / 2.0, y: floor(sourceRect.midY - arrowSize.height / 2.0)), size: arrowSize) + self.arrowNode.transform = CATransform3DMakeRotation(-.pi / 2.0, 0.0, 0.0, 1.0) + } else { + arrowFrame = CGRect(origin: CGPoint(x: contentRect.minX - arrowSize.width + (arrowSize.width - arrowSize.height) / 2.0, y: floor(sourceRect.midY - arrowSize.height / 2.0)), size: arrowSize) + self.arrowNode.transform = CATransform3DMakeRotation(.pi / 2.0, 0.0, 0.0, 1.0) + } + + self.arrowNode.frame = arrowFrame + self.arrowNode.isHidden = false + + } else { + let masterWidth = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0)) + let detailWidth = layout.size.width - masterWidth + contentRect = CGRect(origin: CGPoint(x: masterWidth + floor((detailWidth - size.width) / 2.0), y: floor((layout.size.height - size.height) / 2.0)), size: size) + self.arrowNode.isHidden = true + } + + containerContainingFrame = contentRect + containerInsets = .zero + + self.shadowNode.alpha = 1.0 + if self.shadowNode.image == nil { + self.shadowNode.image = generateShadowImage() + } + } else { + self.containerContainingNode.cornerRadius = 0.0 + + self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.25) + containerContainingFrame = CGRect(origin: CGPoint(), size: layout.size) + containerInsets = layout.intrinsicInsets + + self.arrowNode.isHidden = true + self.shadowNode.alpha = 0.0 + } + transition.updateFrame(node: self.containerContainingNode, frame: containerContainingFrame) + + let shadowFrame = containerContainingFrame.insetBy(dx: -60.0, dy: -60.0) + transition.updateFrame(node: self.shadowNode, frame: shadowFrame) let expandProgress: CGFloat = 1.0 - let scaledInset: CGFloat = 12.0 let scaledDistance: CGFloat = 4.0 let minScale = (layout.size.width - scaledInset * 2.0) / layout.size.width @@ -1593,11 +1676,15 @@ private final class StickerPackScreenNode: ViewControllerTracingNode { self.containers[i] = container } - let containerFrame = CGRect(origin: CGPoint(x: CGFloat(indexOffset) * layout.size.width + self.relativeToSelectedStickerPackTransition + scaledOffset, y: containerVerticalOffset), size: layout.size) + let containerFrame = CGRect(origin: CGPoint(x: CGFloat(indexOffset) * containerContainingFrame.size.width + self.relativeToSelectedStickerPackTransition + scaledOffset, y: containerVerticalOffset), size: containerContainingFrame.size) containerTransition.updateFrame(node: container, frame: containerFrame, beginWithCurrentState: true) containerTransition.updateSublayerTransformScaleAndOffset(node: container, scale: containerScale, offset: CGPoint(), beginWithCurrentState: true) + var containerLayout = layout + containerLayout.size = containerFrame.size + containerLayout.intrinsicInsets = containerInsets + if container.validLayout?.0 != layout { - container.updateLayout(layout: layout, transition: containerTransition) + container.updateLayout(layout: containerLayout, transition: containerTransition) } if wasAdded { @@ -1680,23 +1767,40 @@ private final class StickerPackScreenNode: ViewControllerTracingNode { } func animateIn() { + guard let layout = self.validLayout else { + return + } self.dimNode.alpha = 1.0 self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - let minInset: CGFloat = (self.containers.map { (_, container) -> CGFloat in container.topContentInset }).max() ?? 0.0 - self.containerContainingNode.layer.animatePosition(from: CGPoint(x: 0.0, y: self.containerContainingNode.bounds.height - minInset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + if case .regular = layout.metrics.widthClass { + + } else { + let minInset: CGFloat = (self.containers.map { (_, container) -> CGFloat in container.topContentInset }).max() ?? 0.0 + self.containerContainingNode.layer.animatePosition(from: CGPoint(x: 0.0, y: self.containerContainingNode.bounds.height - minInset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + } } func animateOut(completion: @escaping () -> Void) { + guard let layout = self.validLayout else { + return + } self.dimNode.alpha = 0.0 self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) - let minInset: CGFloat = (self.containers.map { (_, container) -> CGFloat in container.topContentInset }).max() ?? 0.0 - self.containerContainingNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.containerContainingNode.bounds.height - minInset), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { _ in - completion() - }) - - self.modalProgressUpdated(0.0, .animated(duration: 0.2, curve: .easeInOut)) + if case .regular = layout.metrics.widthClass { + self.layer.allowsGroupOpacity = true + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in + completion() + }) + } else { + let minInset: CGFloat = (self.containers.map { (_, container) -> CGFloat in container.topContentInset }).max() ?? 0.0 + self.containerContainingNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.containerContainingNode.bounds.height - minInset), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { _ in + completion() + }) + + self.modalProgressUpdated(0.0, .animated(duration: 0.2, curve: .easeInOut)) + } } func dismiss() { @@ -1782,6 +1886,8 @@ public final class StickerPackScreenImpl: ViewController { public var dismissed: (() -> Void)? public var actionPerformed: (([(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)]) -> Void)? + public var getSourceRect: (() -> CGRect?)? + private let _ready = Promise() override public var ready: Promise { return self._ready @@ -2032,8 +2138,9 @@ public func StickerPackScreen( sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil, sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)? = nil, actionPerformed: (([(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)]) -> Void)? = nil, - dismissed: (() -> Void)? = nil) -> ViewController -{ + dismissed: (() -> Void)? = nil, + getSourceRect: (() -> CGRect?)? = nil +) -> ViewController { let controller = StickerPackScreenImpl( context: context, stickerPacks: stickerPacks, @@ -2045,6 +2152,7 @@ public func StickerPackScreen( actionPerformed: actionPerformed ) controller.dismissed = dismissed + controller.getSourceRect = getSourceRect return controller } @@ -2062,3 +2170,34 @@ private final class StickerPackContextReferenceContentSource: ContextReferenceCo return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds) } } + + +private func generateShadowImage() -> UIImage? { + return generateImage(CGSize(width: 140.0, height: 140.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.saveGState() + context.setShadow(offset: CGSize(), blur: 60.0, color: UIColor(white: 0.0, alpha: 0.4).cgColor) + let path = UIBezierPath(roundedRect: CGRect(x: 60.0, y: 60.0, width: 20.0, height: 20.0), cornerRadius: 10.0).cgPath + context.addPath(path) + context.fillPath() + + context.restoreGState() + + context.setBlendMode(.clear) + context.addPath(path) + context.fillPath() + })?.stretchableImage(withLeftCapWidth: 70, topCapHeight: 70) +} + +private func generateArrowImage(color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 23.0, height: 12.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setFillColor(color.cgColor) + + context.translateBy(x: -183.0, y: -209.0) + + try? drawSvgPath(context, path: "M183.219,208.89 H206.781 C205.648,208.89 204.567,209.371 203.808,210.214 L197.23,217.523 C196.038,218.848 193.962,218.848 192.77,217.523 L186.192,210.214 C185.433,209.371 184.352,208.89 183.219,208.89 Z ") + }) +} diff --git a/submodules/TelegramCore/Sources/Authorization.swift b/submodules/TelegramCore/Sources/Authorization.swift index 6f63544a07..9b49ad9ab5 100644 --- a/submodules/TelegramCore/Sources/Authorization.swift +++ b/submodules/TelegramCore/Sources/Authorization.swift @@ -805,6 +805,62 @@ public func verifyLoginEmailSetup(account: UnauthorizedAccount, code: Authorizat |> ignoreValues } +public enum AuthorizationEmailResetError { + case generic + case limitExceeded + case codeExpired +} + +public func resetLoginEmail(account: UnauthorizedAccount, phoneNumber: String, phoneCodeHash: String) -> Signal { + return account.postbox.transaction { transaction -> Signal in + if let state = transaction.getState() as? UnauthorizedAccountState { + switch state.contents { + case let .confirmationCodeEntry(phoneNumber, _, phoneCodeHash, _, _, syncContacts): + return account.network.request(Api.functions.auth.resetLoginEmail(phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash), automaticFloodWait: false) + |> `catch` { error -> Signal in + let errorDescription = error.errorDescription ?? "" + if errorDescription.hasPrefix("FLOOD_WAIT") { + return .fail(.limitExceeded) + } else if errorDescription == "CODE_HASH_EXPIRED" || errorDescription == "PHONE_CODE_EXPIRED" { + return .fail(.codeExpired) + } else { + return .fail(.generic) + } + } + |> mapToSignal { sentCode -> Signal in + return account.postbox.transaction { transaction -> Signal in + switch sentCode { + case let .sentCode(_, type, phoneCodeHash, nextType, codeTimeout): + var parsedNextType: AuthorizationCodeNextType? + if let nextType = nextType { + parsedNextType = AuthorizationCodeNextType(apiType: nextType) + } + + transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts))) + + return .complete() + case .sentCodeSuccess: + return .complete() + } + } + |> switchToLatest + |> mapError { _ -> AuthorizationEmailResetError in + } + |> ignoreValues + } + default: + return .fail(.generic) + } + } else { + return .fail(.generic) + } + } + |> mapError { _ -> AuthorizationEmailResetError in + } + |> switchToLatest + |> ignoreValues +} + public func authorizeWithCode(accountManager: AccountManager, account: UnauthorizedAccount, code: AuthorizationCode, termsOfService: UnauthorizedAccountTermsOfService?, forcedPasswordSetupNotice: @escaping (Int32) -> (NoticeEntryKey, CodableEntry)?) -> Signal { return account.postbox.transaction { transaction -> Signal in if let state = transaction.getState() as? UnauthorizedAccountState { diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index 547edd2235..1233f97cc9 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -1049,6 +1049,10 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio ) } +public let legacyBuiltinWallpaperGradientColors: [UIColor] = [ + UIColor(rgb: 0xd6e2ee) +] + public let defaultBuiltinWallpaperGradientColors: [UIColor] = [ UIColor(rgb: 0xdbddbb), UIColor(rgb: 0x6ba587), @@ -1076,6 +1080,15 @@ public extension BuiltinWallpaperData { datacenterId: 4, fileSize: 183832 ) + static let legacy = BuiltinWallpaperData( + wallpaperId: 5911458201550716931, + wallpaperAccessHash: -5164387148619674119, + slug: "Ye7DfT2kCVIKAAAAhzXfrkdOjxs", + fileId: 5911315028815907420, + fileAccessHash: 5205407890340371688, + datacenterId: 2, + fileSize: 71715 + ) static let variant1 = BuiltinWallpaperData( wallpaperId: 5784984711902265347, wallpaperAccessHash: -7073897034484875424, @@ -1206,6 +1219,7 @@ public extension BuiltinWallpaperData { static func generate(account: Account) { let slugToName: [(String, String)] = [ ("fqv01SQemVIBAAAApND8LDRUhRU", "`default`"), + ("Ye7DfT2kCVIKAAAAhzXfrkdOjxs", "legacy"), ("RlZs2PJkSFADAAAAElGaGwgJBgU", "variant1"), ("9LW_RcoOSVACAAAAFTk3DTyXN-M", "variant2"), ("CJNyxPMgSVAEAAAAvW9sMwc51cw", "variant3"), diff --git a/submodules/TelegramPresentationData/Sources/PresentationData.swift b/submodules/TelegramPresentationData/Sources/PresentationData.swift index f38ec45828..3458593c55 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationData.swift @@ -383,7 +383,10 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager NSAttributedString.Key?)? { @@ -378,9 +382,12 @@ public class ImmediateTextNodeWithEntities: TextNode { let _ = apply() + var enableAnimations = true if let arguments = self.arguments { self.updateInlineStickers(context: arguments.context, cache: arguments.cache, renderer: arguments.renderer, textLayout: layout, placeholderColor: arguments.placeholderColor) + enableAnimations = arguments.context.sharedContext.energyUsageSettings.fullTranslucency } + self.updateSpoilers(enableAnimations: enableAnimations, textLayout: layout) if layout.numberOfLines > 1 { self.trailingLineWidth = layout.trailingLineWidth @@ -443,6 +450,25 @@ public class ImmediateTextNodeWithEntities: TextNode { } } + private func updateSpoilers(enableAnimations: Bool, textLayout: TextNodeLayout) { + if !textLayout.spoilers.isEmpty && self.displaySpoilerEffect { + if self.dustNode == nil { + let dustNode = InvisibleInkDustNode(textNode: nil, enableAnimations: enableAnimations) + self.dustNode = dustNode + self.addSubnode(dustNode) + + } + if let dustNode = self.dustNode { + let textFrame = CGRect(origin: .zero, size: textLayout.size) + dustNode.update(size: textFrame.size, color: self.spoilerColor, textColor: self.spoilerColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }) + dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0) + } + } else if let dustNode = self.dustNode { + self.dustNode = nil + dustNode.removeFromSupernode() + } + } + public func updateLayoutInfo(_ constrainedSize: CGSize) -> ImmediateTextNodeLayoutInfo { self.constrainedSize = constrainedSize diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/Contents.json index 38f0c81fc2..6e965652df 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/Contents.json @@ -1,9 +1,9 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { "provides-namespace" : true } -} \ No newline at end of file +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/Contents.json index 69aa9981a3..a8b8162849 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/Contents.json @@ -1,22 +1,12 @@ { "images" : [ { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Schedule@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Schedule@3x.png", - "scale" : "3x" + "filename" : "schedule.pdf", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/Schedule@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/Schedule@2x.png deleted file mode 100644 index 2829293819..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/Schedule@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/Schedule@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/Schedule@3x.png deleted file mode 100644 index f4eb832ef8..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/Schedule@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/schedule.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/schedule.pdf new file mode 100644 index 0000000000..23e1733493 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/ScheduleIcon.imageset/schedule.pdf @@ -0,0 +1,97 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 3.334961 3.334961 cm +0.000000 0.000000 0.000000 scn +8.000000 16.665077 m +8.000000 17.032347 8.297730 17.330078 8.665000 17.330078 c +13.450547 17.330078 17.330002 13.450625 17.330002 8.665078 c +17.330002 3.879531 13.450547 0.000076 8.665000 0.000076 c +3.879452 0.000076 0.000000 3.879531 0.000000 8.665078 c +0.000000 11.755159 1.617408 14.466341 4.050117 16.000078 c +2.165000 16.000078 l +1.797731 16.000078 1.500000 16.297810 1.500000 16.665077 c +1.500000 17.032347 1.797731 17.330078 2.165000 17.330078 c +5.665000 17.330078 l +6.032269 17.330078 6.330000 17.032347 6.330000 16.665077 c +6.330000 13.165078 l +6.330000 12.797809 6.032269 12.500078 5.665000 12.500078 c +5.297730 12.500078 5.000000 12.797809 5.000000 13.165078 c +5.000000 15.020401 l +2.805339 13.751994 1.330000 11.380217 1.330000 8.665078 c +1.330000 4.614069 4.613991 1.330078 8.665000 1.330078 c +12.716009 1.330078 16.000000 4.614069 16.000000 8.665078 c +16.000000 12.716087 12.716009 16.000078 8.665000 16.000078 c +8.297730 16.000078 8.000000 16.297810 8.000000 16.665077 c +h +8.665000 14.330078 m +9.032269 14.330078 9.330000 14.032348 9.330000 13.665078 c +9.330000 8.940530 l +12.135226 6.135303 l +12.394924 5.875606 12.394924 5.454551 12.135226 5.194852 c +11.875527 4.935153 11.454473 4.935153 11.194774 5.194852 c +8.194774 8.194853 l +8.070063 8.319564 8.000000 8.488708 8.000000 8.665078 c +8.000000 13.665078 l +8.000000 14.032348 8.297730 14.330078 8.665000 14.330078 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1511 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001601 00000 n +0000001624 00000 n +0000001797 00000 n +0000001871 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1930 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/Contents.json index 13746b9a96..47cbacef29 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/Contents.json @@ -1,22 +1,12 @@ { "images" : [ { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Mute@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Mute@3x.png", - "scale" : "3x" + "filename" : "sendwithoutsound.pdf", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/Mute@2x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/Mute@2x.png deleted file mode 100644 index 036c06a6d1..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/Mute@2x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/Mute@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/Mute@3x.png deleted file mode 100644 index a79ac3bd68..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/Mute@3x.png and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/sendwithoutsound.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/sendwithoutsound.pdf new file mode 100644 index 0000000000..8ad58ec31b --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/SilentIcon.imageset/sendwithoutsound.pdf @@ -0,0 +1,154 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 3.334961 3.205078 cm +0.000000 0.000000 0.000000 scn +1.135226 17.265186 m +0.875527 17.524885 0.454473 17.524885 0.194774 17.265186 c +-0.064925 17.005487 -0.064925 16.584435 0.194774 16.324736 c +16.194775 0.324734 l +16.454473 0.065035 16.875528 0.065035 17.135227 0.324734 c +17.394926 0.584433 17.394926 1.005487 17.135227 1.265186 c +12.830019 5.570395 l +12.830019 13.984108 l +12.830019 14.015735 l +12.830019 14.015746 l +12.830019 14.015758 l +12.830032 14.503190 12.830044 14.912979 12.802612 15.227596 c +12.776955 15.521866 12.718998 15.904351 12.465319 16.192924 c +12.149200 16.552528 11.677307 16.734966 11.201504 16.681524 c +10.819681 16.638639 10.519508 16.394609 10.302576 16.194126 c +10.070642 15.979778 9.794991 15.676547 9.467113 15.315862 c +9.467100 15.315848 l +9.445834 15.292454 l +6.911015 12.504154 l +6.870810 12.460040 l +6.811123 12.459965 l +6.165019 12.459965 l +6.142232 12.459965 l +6.073141 12.459966 6.005919 12.459967 5.940522 12.459891 c +1.135226 17.265186 l +h +7.233788 11.166624 m +11.500019 6.900394 l +11.500019 13.984108 l +11.500019 14.511556 11.499405 14.862436 11.477638 15.112073 c +11.466372 15.241299 11.451292 15.307571 11.443966 15.335192 c +11.425201 15.348718 11.403254 15.357203 11.380270 15.359817 c +11.356270 15.344307 11.300531 15.305413 11.205269 15.217373 c +11.021238 15.047297 10.784756 14.788080 10.429955 14.397799 c +7.895135 11.609499 l +7.876595 11.588985 l +7.810806 11.515999 7.711236 11.405536 7.585287 11.322461 c +7.477557 11.251403 7.358773 11.198743 7.233788 11.166624 c +h +3.310589 11.708942 m +4.251275 10.768256 l +4.114416 10.639768 4.004863 10.482624 3.931640 10.305847 c +3.894677 10.216611 3.864227 10.089064 3.847507 9.844008 c +3.830379 9.592980 3.830019 9.270005 3.830019 8.794965 c +3.830019 8.319924 3.830379 7.996948 3.847507 7.745921 c +3.864227 7.500866 3.894677 7.373319 3.931640 7.284082 c +4.067134 6.956970 4.327024 6.697080 4.654137 6.561585 c +4.743373 6.524623 4.870920 6.494172 5.115975 6.477452 c +5.367003 6.460325 5.689979 6.459965 6.165019 6.459965 c +6.811123 6.459965 l +6.838768 6.460045 l +6.838777 6.460045 l +6.937038 6.460461 7.085750 6.461090 7.231940 6.423779 c +7.357602 6.391706 7.477026 6.338876 7.585287 6.267468 c +7.711238 6.184393 7.810808 6.073929 7.876598 6.000941 c +7.895135 5.980431 l +10.429956 3.192128 l +10.784757 2.801847 11.021239 2.542631 11.205269 2.372556 c +11.300533 2.284515 11.356270 2.245622 11.380270 2.230112 c +11.403254 2.232725 11.425201 2.241211 11.443966 2.254738 c +11.451292 2.282358 11.466372 2.348630 11.477638 2.477856 c +11.498196 2.713622 11.499886 3.039693 11.500010 3.519522 c +12.789057 2.230474 l +12.756243 1.960018 12.682525 1.644088 12.465319 1.397003 c +12.149200 1.037399 11.677307 0.854963 11.201504 0.908403 c +10.819681 0.951288 10.519508 1.195318 10.302577 1.395802 c +10.070659 1.610135 9.795034 1.913337 9.467189 2.273985 c +9.467137 2.274039 l +9.467128 2.274051 l +9.445836 2.297473 l +6.911015 5.085775 l +6.870807 5.129889 l +6.811123 5.129965 l +6.165019 5.129965 l +6.142203 5.129965 l +6.142138 5.129965 l +5.695606 5.129959 5.327103 5.129954 5.025440 5.150537 c +4.712668 5.171878 4.423473 5.217547 4.145168 5.332826 c +3.492168 5.603307 2.973362 6.122113 2.702880 6.775113 c +2.587602 7.053419 2.541932 7.342614 2.520592 7.655386 c +2.500008 7.957066 2.500013 8.325591 2.500019 8.772156 c +2.500019 8.772170 l +2.500019 8.794965 l +2.500019 8.817760 l +2.500019 8.817774 l +2.500013 9.264338 2.500008 9.632866 2.520592 9.934544 c +2.541932 10.247315 2.587602 10.536510 2.702880 10.814816 c +2.843596 11.154535 3.051523 11.457933 3.310589 11.708942 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 3618 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000003708 00000 n +0000003731 00000 n +0000003904 00000 n +0000003978 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +4037 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/WhenOnlineIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/WhenOnlineIcon.imageset/Contents.json new file mode 100644 index 0000000000..9478d5c466 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/WhenOnlineIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "sendthenonline.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/WhenOnlineIcon.imageset/sendthenonline.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/WhenOnlineIcon.imageset/sendthenonline.pdf new file mode 100644 index 0000000000..c3c0c5df3b --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Menu/WhenOnlineIcon.imageset/sendthenonline.pdf @@ -0,0 +1,123 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 15.000000 4.000000 cm +0.000000 0.000000 0.000000 scn +5.000000 2.500000 m +5.000000 1.119288 3.880712 0.000000 2.500000 0.000000 c +1.119288 0.000000 0.000000 1.119288 0.000000 2.500000 c +0.000000 3.880712 1.119288 5.000000 2.500000 5.000000 c +3.880712 5.000000 5.000000 3.880712 5.000000 2.500000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 3.334961 3.334961 cm +0.000000 0.000000 0.000000 scn +1.330000 8.665078 m +1.330000 12.716087 4.613991 16.000078 8.665000 16.000078 c +12.716008 16.000078 16.000000 12.716087 16.000000 8.665078 c +16.000000 7.971566 15.903754 7.300534 15.723890 6.664610 c +16.154043 6.472708 16.542303 6.203824 16.871166 5.875466 c +17.168650 6.750837 17.330002 7.689115 17.330002 8.665078 c +17.330002 13.450625 13.450547 17.330078 8.665000 17.330078 c +3.879453 17.330078 0.000000 13.450625 0.000000 8.665078 c +0.000000 3.879531 3.879453 0.000076 8.665000 0.000076 c +9.641031 0.000076 10.579370 0.161449 11.454794 0.458973 c +11.126430 0.787828 10.857537 1.176086 10.665626 1.606233 c +10.029655 1.426340 9.358569 1.330078 8.665000 1.330078 c +6.949595 1.330078 5.371725 1.918934 4.122502 2.905533 c +4.297272 3.127586 4.536829 3.393646 4.847776 3.660172 c +5.621715 4.323548 6.847626 5.000078 8.665000 5.000078 c +9.428206 5.000078 10.087104 4.880767 10.652022 4.693223 c +10.837114 5.118156 11.097044 5.502989 11.415169 5.831069 c +10.648278 6.131837 9.735907 6.330078 8.665000 6.330078 c +6.482374 6.330078 4.958285 5.506608 3.982224 4.669984 c +3.639743 4.376429 3.365922 4.082502 3.154341 3.823979 c +2.018686 5.115683 1.330000 6.809955 1.330000 8.665078 c +h +12.678360 5.175269 m +12.317028 4.907584 12.030398 4.544794 11.855478 4.123891 c +12.090899 3.974664 12.299200 3.817050 12.482224 3.660172 c +12.793171 3.393646 13.032728 3.127586 13.207498 2.905533 c +12.806664 2.588965 12.371993 2.313348 11.909800 2.084994 c +12.111738 1.664135 12.428010 1.308554 12.818370 1.058498 c +14.273650 1.854803 15.475398 3.056577 16.271671 4.511877 c +16.021608 4.902231 15.666019 5.218495 15.245155 5.420424 c +14.958824 4.840852 14.598176 4.304554 14.175659 3.823979 c +13.964078 4.082502 13.690256 4.376430 13.347776 4.669984 c +13.147928 4.841282 12.925106 5.012029 12.678360 5.175269 c +h +6.830039 10.665039 m +6.830039 11.678482 7.651597 12.500039 8.665039 12.500039 c +9.678482 12.500039 10.500039 11.678482 10.500039 10.665039 c +10.500039 9.651596 9.678482 8.830039 8.665039 8.830039 c +7.651597 8.830039 6.830039 9.651596 6.830039 10.665039 c +h +8.665039 13.830039 m +6.917058 13.830039 5.500039 12.413020 5.500039 10.665039 c +5.500039 8.917058 6.917058 7.500039 8.665039 7.500039 c +10.413020 7.500039 11.830039 8.917058 11.830039 10.665039 c +11.830039 12.413020 10.413020 13.830039 8.665039 13.830039 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 2777 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000002867 00000 n +0000002890 00000 n +0000003063 00000 n +0000003137 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +3196 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Resources/ChatWallpaperBuiltin0.jpg b/submodules/TelegramUI/Resources/ChatWallpaperBuiltin0.jpg deleted file mode 100644 index 44f7cf5262..0000000000 Binary files a/submodules/TelegramUI/Resources/ChatWallpaperBuiltin0.jpg and /dev/null differ diff --git a/submodules/TelegramUI/Resources/Ye7DfT2kCVIKAAAAhzXfrkdOjxs.tgv b/submodules/TelegramUI/Resources/Ye7DfT2kCVIKAAAAhzXfrkdOjxs.tgv new file mode 100644 index 0000000000..1c70cda0a1 Binary files /dev/null and b/submodules/TelegramUI/Resources/Ye7DfT2kCVIKAAAAhzXfrkdOjxs.tgv differ diff --git a/submodules/TelegramUI/Sources/AttachmentFileController.swift b/submodules/TelegramUI/Sources/AttachmentFileController.swift index 7550b1a2ae..30cc6cfffc 100644 --- a/submodules/TelegramUI/Sources/AttachmentFileController.swift +++ b/submodules/TelegramUI/Sources/AttachmentFileController.swift @@ -183,7 +183,7 @@ private final class AttachmentFileContext: AttachmentMediaPickerContext { func setCaption(_ caption: NSAttributedString) { } - func send(silently: Bool, mode: AttachmentMediaPickerSendMode) { + func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) { } func schedule() { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 752a2e7bed..0e42e7860b 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -430,6 +430,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private weak var searchResultsTooltipController: TooltipController? private weak var messageTooltipController: TooltipController? private weak var videoUnmuteTooltipController: TooltipController? + private var didDisplayVideoUnmuteTooltip = false private weak var silentPostTooltipController: TooltipController? private weak var mediaRecordingModeTooltipController: TooltipController? private weak var mediaRestrictedTooltipController: TooltipController? @@ -995,78 +996,92 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } })) } - }, actionInteraction: GalleryControllerActionInteraction(openUrl: { [weak self] url, concealed in - if let strongSelf = self { - strongSelf.openUrl(url, concealed: concealed, message: nil) - } - }, openUrlIn: { [weak self] url in - if let strongSelf = self { - strongSelf.openUrlIn(url) - } - }, openPeerMention: { [weak self] mention in - if let strongSelf = self { - strongSelf.controllerInteraction?.openPeerMention(mention) - } - }, openPeer: { [weak self] peer in - if let strongSelf = self { - strongSelf.controllerInteraction?.openPeer(peer, .default, nil, .default) - } - }, openHashtag: { [weak self] peerName, hashtag in - if let strongSelf = self { - strongSelf.controllerInteraction?.openHashtag(peerName, hashtag) - } - }, openBotCommand: { [weak self] command in - if let strongSelf = self { - strongSelf.controllerInteraction?.sendBotCommand(nil, command) - } - }, addContact: { [weak self] phoneNumber in - if let strongSelf = self { - strongSelf.controllerInteraction?.addContact(phoneNumber) - } - }, storeMediaPlaybackState: { [weak self] messageId, timestamp, playbackRate in - guard let strongSelf = self else { - return - } - var storedState: MediaPlaybackStoredState? - if let timestamp = timestamp { - storedState = MediaPlaybackStoredState(timestamp: timestamp, playbackRate: AudioPlaybackRate(playbackRate)) - } - let _ = updateMediaPlaybackStoredStateInteractively(engine: strongSelf.context.engine, messageId: messageId, state: storedState).start() - }, editMedia: { [weak self] messageId, snapshots, transitionCompletion in - guard let strongSelf = self else { - return - } - - let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId)) - |> deliverOnMainQueue).start(next: { [weak self] message in - guard let strongSelf = self, let message = message else { + }, actionInteraction: GalleryControllerActionInteraction( + openUrl: { [weak self] url, concealed in + if let strongSelf = self { + strongSelf.openUrl(url, concealed: concealed, message: nil) + } + }, openUrlIn: { [weak self] url in + if let strongSelf = self { + strongSelf.openUrlIn(url) + } + }, openPeerMention: { [weak self] mention in + if let strongSelf = self { + strongSelf.controllerInteraction?.openPeerMention(mention) + } + }, openPeer: { [weak self] peer in + if let strongSelf = self { + strongSelf.controllerInteraction?.openPeer(peer, .default, nil, .default) + } + }, openHashtag: { [weak self] peerName, hashtag in + if let strongSelf = self { + strongSelf.controllerInteraction?.openHashtag(peerName, hashtag) + } + }, openBotCommand: { [weak self] command in + if let strongSelf = self { + strongSelf.controllerInteraction?.sendBotCommand(nil, command) + } + }, addContact: { [weak self] phoneNumber in + if let strongSelf = self { + strongSelf.controllerInteraction?.addContact(phoneNumber) + } + }, storeMediaPlaybackState: { [weak self] messageId, timestamp, playbackRate in + guard let strongSelf = self else { + return + } + var storedState: MediaPlaybackStoredState? + if let timestamp = timestamp { + storedState = MediaPlaybackStoredState(timestamp: timestamp, playbackRate: AudioPlaybackRate(playbackRate)) + } + let _ = updateMediaPlaybackStoredStateInteractively(engine: strongSelf.context.engine, messageId: messageId, state: storedState).start() + }, editMedia: { [weak self] messageId, snapshots, transitionCompletion in + guard let strongSelf = self else { return } - var mediaReference: AnyMediaReference? - for media in message.media { - if let image = media as? TelegramMediaImage { - mediaReference = AnyMediaReference.standalone(media: image) - } else if let file = media as? TelegramMediaFile { - mediaReference = AnyMediaReference.standalone(media: file) + let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId)) + |> deliverOnMainQueue).start(next: { [weak self] message in + guard let strongSelf = self, let message = message else { + return } - } - - if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] { - legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: { - transitionCompletion() - }, getCaptionPanelView: { [weak self] in - return self?.getCaptionPanelView() - }, sendMessagesWithSignals: { [weak self] signals, _, _ in - if let strongSelf = self { - strongSelf.enqueueMediaMessages(signals: signals, silentPosting: false) + + var mediaReference: AnyMediaReference? + for media in message.media { + if let image = media as? TelegramMediaImage { + mediaReference = AnyMediaReference.standalone(media: image) + } else if let file = media as? TelegramMediaFile { + mediaReference = AnyMediaReference.standalone(media: file) } - }, present: { [weak self] c, a in - self?.present(c, in: .window(.root), with: a) - }) + } + + if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] { + legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: { + transitionCompletion() + }, getCaptionPanelView: { [weak self] in + return self?.getCaptionPanelView() + }, sendMessagesWithSignals: { [weak self] signals, _, _ in + if let strongSelf = self { + strongSelf.enqueueMediaMessages(signals: signals, silentPosting: false) + } + }, present: { [weak self] c, a in + self?.present(c, in: .window(.root), with: a) + }) + } + }) + }), + getSourceRect: { [weak self] in + guard let strongSelf = self else { + return nil } - }) - }))) + var rect: CGRect? + strongSelf.chatDisplayNode.historyNode.forEachVisibleMessageItemNode({ itemNode in + if itemNode.item?.message.id == message.id { + rect = itemNode.view.convert(itemNode.contentFrame(), to: nil) + } + }) + return rect + } + )) }, openPeer: { [weak self] peer, navigation, fromMessage, source in self?.openPeer(peer: peer, navigation: navigation, fromMessage: fromMessage, fromReactionMessageId: source == .reaction ? fromMessage?.id : nil, expandAvatar: source == .groupParticipant) }, openPeerMention: { [weak self] name in @@ -4288,30 +4303,30 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let botAdminRights { if botAdminRights.rights.isEmpty { let stringWithRanges = strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationInviteAdminText(botName, peerName) - let formattedString = NSMutableAttributedString(string: stringWithRanges.string, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) + let formattedString = NSMutableAttributedString(string: stringWithRanges.string, font: Font.regular(strongSelf.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor, paragraphAlignment: .center) for range in stringWithRanges.ranges.prefix(2) { - formattedString.addAttribute(.font, value: Font.semibold(13.0), range: range.range) + formattedString.addAttribute(.font, value: Font.semibold(strongSelf.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), range: range.range) } attributedText = formattedString } else { let stringWithRanges = strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationInviteWithRightsText(botName, peerName, stringForAdminRights(strings: strongSelf.presentationData.strings, adminRights: botAdminRights, isChannel: isChannel)) - let formattedString = NSMutableAttributedString(string: stringWithRanges.string, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) + let formattedString = NSMutableAttributedString(string: stringWithRanges.string, font: Font.regular(strongSelf.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor, paragraphAlignment: .center) for range in stringWithRanges.ranges.prefix(2) { - formattedString.addAttribute(.font, value: Font.semibold(13.0), range: range.range) + formattedString.addAttribute(.font, value: Font.semibold(strongSelf.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), range: range.range) } attributedText = formattedString } } else { if case let .group(group) = peerType, group.botParticipant { let stringWithRanges = strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationInviteText(botName, peerName) - let formattedString = NSMutableAttributedString(string: stringWithRanges.string, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) + let formattedString = NSMutableAttributedString(string: stringWithRanges.string, font: Font.regular(strongSelf.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor, paragraphAlignment: .center) for range in stringWithRanges.ranges.prefix(2) { - formattedString.addAttribute(.font, value: Font.semibold(13.0), range: range.range) + formattedString.addAttribute(.font, value: Font.semibold(strongSelf.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), range: range.range) } attributedText = formattedString } else { attributedTitle = nil - attributedText = NSAttributedString(string: strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationTitle(peerName, botName).string, font: Font.medium(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) + attributedText = NSAttributedString(string: strongSelf.presentationData.strings.RequestPeer_SelectionConfirmationTitle(peerName, botName).string, font: Font.semibold(strongSelf.presentationData.listsFontSize.baseDisplaySize), textColor: theme.primaryColor, paragraphAlignment: .center) } } } @@ -8986,14 +9001,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } }, displayVideoUnmuteTip: { [weak self] location in - guard let strongSelf = self, let layout = strongSelf.validLayout, strongSelf.traceVisibility() && isTopmostChatController(strongSelf) else { + guard let strongSelf = self, !strongSelf.didDisplayVideoUnmuteTooltip, let layout = strongSelf.validLayout, strongSelf.traceVisibility() && isTopmostChatController(strongSelf) else { return } - if let location = location, location.y < strongSelf.navigationLayout(layout: layout).navigationFrame.maxY { return } - let icon: UIImage? if layout.deviceMetrics.hasTopNotch || layout.deviceMetrics.hasDynamicIsland { icon = UIImage(bundleImageName: "Chat/Message/VolumeButtonIconX") @@ -9001,6 +9014,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G icon = UIImage(bundleImageName: "Chat/Message/VolumeButtonIcon") } if let location = location, let icon = icon { + strongSelf.didDisplayVideoUnmuteTooltip = true strongSelf.videoUnmuteTooltipController?.dismiss() let tooltipController = TooltipController(content: .iconAndText(icon, strongSelf.presentationInterfaceState.strings.Conversation_PressVolumeButtonForSound), baseFontSize: strongSelf.presentationData.listsFontSize.baseDisplaySize, timeout: 3.5, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true) strongSelf.videoUnmuteTooltipController = tooltipController @@ -9832,7 +9846,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.window?.presentInGlobalOverlay(slowmodeTooltipController) }, displaySendMessageOptions: { [weak self] node, gesture in - if let strongSelf = self, let textInputNode = strongSelf.chatDisplayNode.textInputNode(), let layout = strongSelf.validLayout { + if let strongSelf = self, let peerId = strongSelf.chatLocation.peerId, let textInputNode = strongSelf.chatDisplayNode.textInputNode(), let layout = strongSelf.validLayout { let previousSupportedOrientations = strongSelf.supportedOrientations if layout.size.width > layout.size.height { strongSelf.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .landscape) @@ -9847,26 +9861,55 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G hasEntityKeyboard = true } - let controller = ChatSendMessageActionSheetController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, completion: { [weak self] in - if let strongSelf = self { - strongSelf.supportedOrientations = previousSupportedOrientations + let _ = (strongSelf.context.account.viewTracker.peerView(peerId) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] peerView in + guard let strongSelf = self, let peer = peerViewMainPeer(peerView) else { + return } - }, sendMessage: { [weak self] silently in - if let strongSelf = self { - strongSelf.controllerInteraction?.sendCurrentMessage(silently) + var sendWhenOnlineAvailable = false + if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case .present = presence.status { + sendWhenOnlineAvailable = true } - }, schedule: { [weak self] in - if let strongSelf = self { - strongSelf.controllerInteraction?.scheduleCurrentMessage() + if peer.id.namespace == Namespaces.Peer.CloudUser && peer.id.id._internalGetInt64Value() == 777000 { + sendWhenOnlineAvailable = false + } + + let controller = ChatSendMessageActionSheetController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, canSendWhenOnline: sendWhenOnlineAvailable, completion: { [weak self] in + if let strongSelf = self { + strongSelf.supportedOrientations = previousSupportedOrientations + } + }, sendMessage: { [weak self] mode in + if let strongSelf = self { + switch mode { + case .generic: + strongSelf.controllerInteraction?.sendCurrentMessage(false) + case .silently: + strongSelf.controllerInteraction?.sendCurrentMessage(true) + case .whenOnline: + strongSelf.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleWhenOnlineTimestamp) { [weak self] in + if let strongSelf = self { + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, saveInterfaceState: strongSelf.presentationInterfaceState.subject != .scheduledMessages, { + $0.updatedInterfaceState { $0.withUpdatedReplyMessageId(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) } + }) + strongSelf.openScheduledMessages() + } + } + } + } + }, schedule: { [weak self] in + if let strongSelf = self { + strongSelf.controllerInteraction?.scheduleCurrentMessage() + } + }) + controller.emojiViewProvider = strongSelf.chatDisplayNode.textInputPanelNode?.emojiViewProvider + strongSelf.sendMessageActionsController = controller + if layout.isNonExclusive { + strongSelf.present(controller, in: .window(.root)) + } else { + strongSelf.presentInGlobalOverlay(controller, with: nil) } }) - controller.emojiViewProvider = strongSelf.chatDisplayNode.textInputPanelNode?.emojiViewProvider - strongSelf.sendMessageActionsController = controller - if layout.isNonExclusive { - strongSelf.present(controller, in: .window(.root)) - } else { - strongSelf.presentInGlobalOverlay(controller, with: nil) - } } }, openScheduledMessages: { [weak self] in if let strongSelf = self { @@ -11056,9 +11099,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if values.contains(.convertToGigagroup) && !strongSelf.displayedConvertToGigagroupSuggestion { strongSelf.displayedConvertToGigagroupSuggestion = true - let attributedTitle = NSAttributedString(string: strongSelf.presentationData.strings.BroadcastGroups_LimitAlert_Title, font: Font.medium(17.0), textColor: strongSelf.presentationData.theme.actionSheet.primaryTextColor, paragraphAlignment: .center) - let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: strongSelf.presentationData.theme.actionSheet.primaryTextColor) - let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: strongSelf.presentationData.theme.actionSheet.primaryTextColor) + let attributedTitle = NSAttributedString(string: strongSelf.presentationData.strings.BroadcastGroups_LimitAlert_Title, font: Font.semibold(strongSelf.presentationData.listsFontSize.baseDisplaySize), textColor: strongSelf.presentationData.theme.actionSheet.primaryTextColor, paragraphAlignment: .center) + let body = MarkdownAttributeSet(font: Font.regular(strongSelf.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: strongSelf.presentationData.theme.actionSheet.primaryTextColor) + let bold = MarkdownAttributeSet(font: Font.semibold(strongSelf.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: strongSelf.presentationData.theme.actionSheet.primaryTextColor) let participantsLimit = strongSelf.context.currentLimitsConfiguration.with { $0 }.maxSupergroupMemberCount let text = strongSelf.presentationData.strings.BroadcastGroups_LimitAlert_Text(presentationStringsFormattedNumber(participantsLimit, strongSelf.presentationData.dateTimeFormat.groupingSeparator)).string @@ -11074,9 +11117,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G controller.navigationPresentation = .modal controller.setState(.custom(icon: .animation("BroadcastGroup"), title: presentationData.strings.BroadcastGroups_IntroTitle, subtitle: nil, text: presentationData.strings.BroadcastGroups_IntroText, buttonTitle: presentationData.strings.BroadcastGroups_Convert, secondaryButtonTitle: presentationData.strings.BroadcastGroups_Cancel, footerText: nil), animated: false) controller.proceed = { [weak controller] result in - let attributedTitle = NSAttributedString(string: presentationData.strings.BroadcastGroups_ConfirmationAlert_Title, font: Font.medium(17.0), textColor: presentationData.theme.actionSheet.primaryTextColor, paragraphAlignment: .center) - let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: presentationData.theme.actionSheet.primaryTextColor) - let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: presentationData.theme.actionSheet.primaryTextColor) + let attributedTitle = NSAttributedString(string: presentationData.strings.BroadcastGroups_ConfirmationAlert_Title, font: Font.semibold(presentationData.listsFontSize.baseDisplaySize), textColor: presentationData.theme.actionSheet.primaryTextColor, paragraphAlignment: .center) + let body = MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: presentationData.theme.actionSheet.primaryTextColor) + let bold = MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: presentationData.theme.actionSheet.primaryTextColor) let attributedText = parseMarkdownIntoAttributedString(presentationData.strings.BroadcastGroups_ConfirmationAlert_Text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) let alertController = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { @@ -12790,24 +12833,29 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G break case .gift: initialButton = .gift - case let .bot(botId, _, _): - for bot in attachMenuBots.reversed() { - var peerType = peerType - if bot.peer.id == peer.id { - peerType.insert(.sameBot) - peerType.remove(.bot) - } - let button: AttachmentButtonType = .app(bot.peer, bot.shortName, bot.icons) - if !bot.peerTypes.intersection(peerType).isEmpty { - buttons.insert(button, at: 1) - + default: + break + } + + for bot in attachMenuBots.reversed() { + var peerType = peerType + if bot.peer.id == peer.id { + peerType.insert(.sameBot) + peerType.remove(.bot) + } + let button: AttachmentButtonType = .app(bot.peer, bot.shortName, bot.icons) + if !bot.peerTypes.intersection(peerType).isEmpty { + buttons.insert(button, at: 1) + + if case let .bot(botId, _, _) = subject { if initialButton == nil && bot.peer.id == botId { initialButton = button } } - allButtons.insert(button, at: 1) } + allButtons.insert(button, at: 1) } + return (buttons, allButtons, initialButton) } } else { @@ -16331,6 +16379,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G commit(transformedMessages) } }) + case .whenOnline: + let transformedMessages = strongSelf.transformEnqueueMessages(result, silentPosting: false, scheduleTime: scheduleWhenOnlineTimestamp) + commit(transformedMessages) } } controller.peerSelected = { [weak self, weak controller] peer, threadId in @@ -17340,7 +17391,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G @available(iOSApplicationExtension 11.0, iOS 11.0, *) public func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) { session.loadObjects(ofClass: UIImage.self) { [weak self] imageItems in - guard let strongSelf = self else { + guard let strongSelf = self, !imageItems.isEmpty else { return } let images = imageItems as! [UIImage] diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 31ec565a7b..1efa37c56b 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -1361,6 +1361,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if (isChat && (wasPlaying || currentlyPlayingVideo)) || (!isChat && !wasPlaying && currentlyPlayingVideo) { var currentIsVisible = true + var nextIsVisible = false if let appliedPlayingMessageId = strongSelf.appliedPlayingMessageId { currentIsVisible = false strongSelf.forEachVisibleMessageItemNode({ view in @@ -1369,7 +1370,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } }) } - if currentIsVisible && currentlyPlayingVideo { + strongSelf.forEachVisibleMessageItemNode({ view in + if view.item?.message.id == currentlyPlayingMessageId.id { + nextIsVisible = true + } + }) + if currentIsVisible && nextIsVisible && currentlyPlayingVideo { updatedScrollPosition = .index(index: .message(currentlyPlayingMessageId), position: .center(.bottom), directionHint: .Up, animated: true, highlight: true, displayLink: true) scrollAnimationCurve = .Spring(duration: 0.4) } diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index 4290a1aeb6..be0009f633 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -2889,6 +2889,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { override func unreadMessageRangeUpdated() { self.updateVisibility() } + + override func contentFrame() -> CGRect { + return self.imageNode.frame + } } struct AnimatedEmojiSoundsConfiguration { diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 06698bca69..fa3012d9bc 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -4562,4 +4562,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } return false } + + override func contentFrame() -> CGRect { + return self.backgroundNode.frame + } } diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index 3b6d270005..f51c390768 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -1399,4 +1399,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } return nil } + + override func contentFrame() -> CGRect { + return self.interactiveVideoNode.frame + } } diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift index 695378d11b..e1a36e8609 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift @@ -951,4 +951,8 @@ public class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol func unreadMessageRangeUpdated() { } + + public func contentFrame() -> CGRect { + return self.bounds + } } diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 5e382ac296..5b7e8cb05f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -1833,4 +1833,8 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } return nil } + + override func contentFrame() -> CGRect { + return self.imageNode.frame + } } diff --git a/submodules/TelegramUI/Sources/ContactSelectionController.swift b/submodules/TelegramUI/Sources/ContactSelectionController.swift index bf62a19908..893009e8bc 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionController.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionController.swift @@ -444,8 +444,8 @@ final class ContactsPickerContext: AttachmentMediaPickerContext { self.controller?.caption = caption } - func send(silently: Bool, mode: AttachmentMediaPickerSendMode) { - self.controller?.contactsNode.requestMultipleAction?(silently, nil) + func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) { + self.controller?.contactsNode.requestMultipleAction?(mode == .silently, mode == .whenOnline ? scheduleWhenOnlineTimestamp : nil) } func schedule() { diff --git a/submodules/TelegramUI/Sources/OpenChatMessage.swift b/submodules/TelegramUI/Sources/OpenChatMessage.swift index 0d17a3d37d..7d73f67d1f 100644 --- a/submodules/TelegramUI/Sources/OpenChatMessage.swift +++ b/submodules/TelegramUI/Sources/OpenChatMessage.swift @@ -113,7 +113,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { })) } } - }) + }, getSourceRect: params.getSourceRect) params.dismissInput() params.present(controller, nil) return true diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index 1de8c2e2d4..a99bc539a8 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -79,7 +79,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur let addMemberImpl = { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let theme = AlertControllerTheme(presentationData: presentationData) - let attributedTitle = NSAttributedString(string: presentationData.strings.Bot_AddToChat_Add_MemberAlertTitle, font: Font.medium(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) + let attributedTitle = NSAttributedString(string: presentationData.strings.Bot_AddToChat_Add_MemberAlertTitle, font: Font.semibold(presentationData.listsFontSize.baseDisplaySize), textColor: theme.primaryColor, paragraphAlignment: .center) var isGroup: Bool = false var peerTitle: String = "" @@ -95,8 +95,8 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur let text = isGroup ? presentationData.strings.Bot_AddToChat_Add_MemberAlertTextGroup(peerTitle).string : presentationData.strings.Bot_AddToChat_Add_MemberAlertTextChannel(peerTitle).string - let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor) + let body = MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) + let bold = MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) let controller = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Bot_AddToChat_Add_MemberAlertAdd, action: { diff --git a/submodules/TelegramUI/Sources/OwnershipTransferController.swift b/submodules/TelegramUI/Sources/OwnershipTransferController.swift index 24cd7beec7..10ff585ed3 100644 --- a/submodules/TelegramUI/Sources/OwnershipTransferController.swift +++ b/submodules/TelegramUI/Sources/OwnershipTransferController.swift @@ -84,11 +84,11 @@ func ownershipTransferController(context: AccountContext, updatedPresentationDat let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } let theme = AlertControllerTheme(presentationData: presentationData) - var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.medium(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center) + var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.semibold(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center) var text = presentationData.strings.OwnershipTransfer_SecurityRequirements var actions: [TextAlertAction] = [] - + let textFontSize = presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0 switch initialError { case .requestPassword: return commitOwnershipTransferController(context: context, updatedPresentationData: updatedPresentationData, present: present, commit: commit, completion: completion) @@ -114,8 +114,8 @@ func ownershipTransferController(context: AccountContext, updatedPresentationDat actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] } - let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor) + let body = MarkdownAttributeSet(font: Font.regular(textFontSize), textColor: theme.primaryColor) + let bold = MarkdownAttributeSet(font: Font.semibold(textFontSize), textColor: theme.primaryColor) let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) return richTextAlertController(context: context, title: title, text: attributedText, actions: actions) diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index 70f0e5c6e9..aa279516d7 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -124,7 +124,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(id: PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil) - self.presentationInterfaceState = self.presentationInterfaceState.updatedInterfaceState { $0.withUpdatedForwardMessageIds(forwardedMessageIds).withUpdatedForwardOptionsState(ChatInterfaceForwardOptionsState(hideNames: false, hideCaptions: false, unhideNamesOnCaptionChange: false)) } + self.presentationInterfaceState = self.presentationInterfaceState.updatedInterfaceState { $0.withUpdatedForwardMessageIds(forwardedMessageIds) } self.presentationInterfaceStatePromise.set(self.presentationInterfaceState) if let _ = self.requestPeerType { @@ -342,9 +342,9 @@ final class PeerSelectionControllerNode: ASDisplayNode { }, forwardSelectedMessages: { }, forwardCurrentForwardMessages: { }, forwardMessages: { _ in - }, updateForwardOptionsState: { [weak self] value in + }, updateForwardOptionsState: { [weak self] f in if let strongSelf = self { - strongSelf.updateChatPresentationInterfaceState(animated: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardOptionsState($0.forwardOptionsState) }) }) + strongSelf.updateChatPresentationInterfaceState(animated: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardOptionsState(f($0.forwardOptionsState ?? ChatInterfaceForwardOptionsState(hideNames: false, hideCaptions: false, unhideNamesOnCaptionChange: false))) }) }) } }, presentForwardOptions: { [weak self] sourceNode in guard let strongSelf = self else { @@ -659,9 +659,16 @@ final class PeerSelectionControllerNode: ASDisplayNode { hasEntityKeyboard = true } - let controller = ChatSendMessageActionSheetController(context: strongSelf.context, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, completion: { - }, sendMessage: { [weak textInputPanelNode] silently in - textInputPanelNode?.sendMessage(silently ? .silent : .generic) + let controller = ChatSendMessageActionSheetController(context: strongSelf.context, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, canSendWhenOnline: false, completion: { + }, sendMessage: { [weak textInputPanelNode] mode in + switch mode { + case .generic: + textInputPanelNode?.sendMessage(.generic) + case .silently: + textInputPanelNode?.sendMessage(.silent) + case .whenOnline: + textInputPanelNode?.sendMessage(.whenOnline) + } }, schedule: { [weak textInputPanelNode] in textInputPanelNode?.sendMessage(.schedule) }) diff --git a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift index 34dd2b17ea..0186b7bd2f 100644 --- a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift @@ -11,7 +11,6 @@ import AccountContext import LocalizedPeerData import PhotoResources import TelegramStringFormatting -import InvisibleInkDustNode import TextFormat import ChatPresentationInterfaceState import TextNodeWithEntities @@ -29,7 +28,6 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { let iconNode: ASImageNode let titleNode: ImmediateTextNode let textNode: ImmediateTextNodeWithEntities - var dustNode: InvisibleInkDustNode? let imageNode: TransformImageNode private let actionArea: AccessibilityAreaNode @@ -73,6 +71,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.textNode.displaysAsynchronously = false self.textNode.insets = UIEdgeInsets(top: 3.0, left: 0.0, bottom: 3.0, right: 0.0) self.textNode.visibility = true + self.textNode.spoilerColor = self.theme.chat.inputPanel.secondaryTextColor if let animationCache = animationCache, let animationRenderer = animationRenderer { self.textNode.arguments = TextNodeWithEntities.Arguments( @@ -293,6 +292,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { if let text = self.textNode.attributedText?.string { self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: self.theme.chat.inputPanel.primaryTextColor) } + self.textNode.spoilerColor = self.theme.chat.inputPanel.secondaryTextColor if let (size, inset, interfaceState) = self.validLayout { self.updateState(size: size, inset: inset, interfaceState: interfaceState) @@ -345,21 +345,6 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { if self.textNode.supernode == self { self.textNode.frame = textFrame } - - if let textLayout = self.textNode.cachedLayout, !textLayout.spoilers.isEmpty { - if self.dustNode == nil { - let dustNode = InvisibleInkDustNode(textNode: nil, enableAnimations: self.context.sharedContext.energyUsageSettings.fullTranslucency) - self.dustNode = dustNode - self.textNode.supernode?.insertSubnode(dustNode, aboveSubnode: self.textNode) - } - if let dustNode = self.dustNode { - dustNode.update(size: textFrame.size, color: self.theme.chat.inputPanel.secondaryTextColor, textColor: self.theme.chat.inputPanel.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }) - dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0) - } - } else if let dustNode = self.dustNode { - self.dustNode = nil - dustNode.removeFromSupernode() - } } @objc func closePressed() { diff --git a/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift b/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift index c8b4fdc3c5..459906cc6a 100644 --- a/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift +++ b/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift @@ -36,7 +36,8 @@ func makeTelegramAccountAuxiliaryMethods(appDelegate: AppDelegate?) -> AccountAu } } else if let wallpaperResource = resource as? WallpaperDataResource { let builtinWallpapers: [String] = [ - "fqv01SQemVIBAAAApND8LDRUhRU" + "fqv01SQemVIBAAAApND8LDRUhRU", + "Ye7DfT2kCVIKAAAAhzXfrkdOjxs" ] if builtinWallpapers.contains(wallpaperResource.slug) { if let url = getAppBundle().url(forResource: wallpaperResource.slug, withExtension: "tgv") { @@ -67,6 +68,19 @@ func makeTelegramAccountAuxiliaryMethods(appDelegate: AppDelegate?) -> AccountAu } else { return nil } + } else if cloudDocumentMediaResource.fileId == 5911315028815907420 { + if let url = getAppBundle().url(forResource: "Ye7DfT2kCVIKAAAAhzXfrkdOjxs", withExtension: "tgv") { + return Signal { subscriber in + subscriber.putNext(.reset) + if let data = try? Data(contentsOf: url, options: .mappedRead) { + subscriber.putNext(.dataPart(resourceOffset: 0, data: data, range: 0 ..< Int64(data.count), complete: true)) + } + + return EmptyDisposable + } + } else { + return nil + } } } else if let cloudDocumentSizeMediaResource = resource as? CloudDocumentSizeMediaResource { if cloudDocumentSizeMediaResource.documentId == 5789658100176783156 && cloudDocumentSizeMediaResource.sizeSpec == "m" { diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 13027339cc..f53ef73a19 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -1059,7 +1059,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode if patternIsBlack { self.patternImageLayer.softlightMode = .never } else { - if self.useSharedAnimationPhase { + if self.useSharedAnimationPhase && file.settings.colors.count > 2 { self.patternImageLayer.softlightMode = .whileAnimating } else { self.patternImageLayer.softlightMode = .always diff --git a/submodules/WebSearchUI/Sources/WebSearchController.swift b/submodules/WebSearchUI/Sources/WebSearchController.swift index fb2b69a473..7d8bfe60a7 100644 --- a/submodules/WebSearchUI/Sources/WebSearchController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchController.swift @@ -576,8 +576,8 @@ public class WebSearchPickerContext: AttachmentMediaPickerContext { self.interaction?.editingState.setForcedCaption(caption, skipUpdate: true) } - public func send(silently: Bool, mode: AttachmentMediaPickerSendMode) { - self.interaction?.sendSelected(nil, silently, nil) + public func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) { + self.interaction?.sendSelected(nil, mode == .silently, nil) } public func schedule() { diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 5f8256c709..1b29b48576 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -1340,7 +1340,7 @@ final class WebAppPickerContext: AttachmentMediaPickerContext { func setCaption(_ caption: NSAttributedString) { } - func send(silently: Bool, mode: AttachmentMediaPickerSendMode) { + func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) { } func schedule() {