diff --git a/Telegram/Telegram-iOS/Resources/TestFireworks.tgs b/Telegram/Telegram-iOS/Resources/TestFireworks.tgs deleted file mode 100644 index c0ca7bf61f..0000000000 Binary files a/Telegram/Telegram-iOS/Resources/TestFireworks.tgs and /dev/null differ diff --git a/Telegram/Telegram-iOS/Resources/TestHearts.tgs b/Telegram/Telegram-iOS/Resources/TestHearts.tgs deleted file mode 100644 index 3addc12a20..0000000000 Binary files a/Telegram/Telegram-iOS/Resources/TestHearts.tgs and /dev/null differ diff --git a/Telegram/Telegram-iOS/Resources/TestHearts2.tgs b/Telegram/Telegram-iOS/Resources/TestHearts2.tgs deleted file mode 100644 index 15e6b66305..0000000000 Binary files a/Telegram/Telegram-iOS/Resources/TestHearts2.tgs and /dev/null differ diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 3c4b9cc151..ff955dabf5 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7125,3 +7125,9 @@ Sorry for the inconvenience."; "Login.EnterMissingDigits" = "Enter the missing digits"; "Channel.AdminLogFilter.EventsSentMessages" = "Sent Messages"; + +"Contacts.AddContact" = "Add Contact"; + +"Conversation.LargeEmojiDisabledInfo" = "You have disabled large emoji, so they appear small and have no effects in chat."; +"Conversation.LargeEmojiEnable" = "Enable Large Emoji"; +"Conversation.LargeEmojiEnabled" = "Large emoji enabled."; diff --git a/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift b/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift index 63d63bc84a..ff89bba3a6 100644 --- a/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift +++ b/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift @@ -500,19 +500,24 @@ private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScr transition.updateAlpha(node: self.textNode, alpha: controlsAlpha) transition.updateAlpha(node: self.errorTextNode, alpha: controlsAlpha) transition.updateAlpha(node: self.torchButtonNode, alpha: controlsAlpha) + for view in self.highlightViews { + transition.updateAlpha(layer: view.layer, alpha: controlsAlpha) + } let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: layout.size.height)) let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: layout.size.height)) let errorTextSize = self.errorTextNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: layout.size.height)) - let textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: min(dimHeight - textSize.height - titleSpacing, navigationHeight + floorToScreenPixels((dimHeight - navigationHeight - textSize.height) / 2.0) + 5.0)), size: textSize) + var textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: max(dimHeight - textSize.height - titleSpacing, navigationHeight + floorToScreenPixels((dimHeight - navigationHeight - textSize.height) / 2.0) + 5.0)), size: textSize) let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: textFrame.minY - 18.0 - titleSize.height), size: titleSize) - var errorTextFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - errorTextSize.width) / 2.0), y: dimHeight + frameSide + 48.0), size: errorTextSize) - errorTextFrame.origin.y += floor(additionalTorchOffset / 2.0) if titleFrame.minY < navigationHeight { transition.updateAlpha(node: self.titleNode, alpha: 0.0) + textFrame = textFrame.offsetBy(dx: 0.0, dy: -5.0) } else { transition.updateAlpha(node: self.titleNode, alpha: controlsAlpha) } + var errorTextFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - errorTextSize.width) / 2.0), y: dimHeight + frameSide + 48.0), size: errorTextSize) + errorTextFrame.origin.y += floor(additionalTorchOffset / 2.0) + transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame) transition.updateFrameAdditive(node: self.textNode, frame: textFrame) transition.updateFrameAdditive(node: self.errorTextNode, frame: errorTextFrame) diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 2552a15c4b..7cc6946621 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -99,6 +99,8 @@ public final class CallListController: TelegramBaseController { super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .none, locationBroadcastPanelSource: .none, groupCallPanelSource: .none) + self.tabBarItemContextActionType = .always + self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style if case .tab = self.mode { @@ -394,7 +396,9 @@ public final class CallListController: TelegramBaseController { }) } })) - (self.navigationController as? NavigationController)?.pushViewController(controller) + if let navigationController = self.context.sharedContext.mainWindow?.viewController as? NavigationController { + navigationController.pushViewController(controller) + } } @objc func editPressed() { @@ -464,4 +468,43 @@ public final class CallListController: TelegramBaseController { } })) } + + override public func tabBarItemContextAction(sourceNode: ContextExtractedContentContainingNode, gesture: ContextGesture) { + var items: [ContextMenuItem] = [] + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Calls_StartNewCall, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, f in + c.dismiss(completion: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.callPressed() + }) + }))) + + let controller = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(CallListTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(items: items)), recognizer: nil, gesture: gesture) + self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) + } +} + +private final class CallListTabBarContextExtractedContentSource: ContextExtractedContentSource { + let keepInPlace: Bool = true + let ignoreContentTouches: Bool = true + let blurBackground: Bool = true + + private let controller: ViewController + private let sourceNode: ContextExtractedContentContainingNode + + init(controller: ViewController, sourceNode: ContextExtractedContentContainingNode) { + self.controller = controller + self.sourceNode = sourceNode + } + + func takeView() -> ContextControllerTakeViewInfo? { + return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds) + } + + func putBack() -> ContextControllerPutBackViewInfo? { + return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) + } } diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index 1a09f5b662..94b4324e24 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -17,6 +17,7 @@ import SearchUI import TelegramPermissionsUI import AppBundle import StickerResources +import ContextUI private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBarSearchContentNode) -> Bool { if searchNode.expansionProgress > 0.0 && searchNode.expansionProgress < 1.0 { @@ -100,6 +101,8 @@ public class ContactsController: ViewController { super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + self.tabBarItemContextActionType = .always + self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style self.title = self.presentationData.strings.Contacts_Title @@ -311,11 +314,13 @@ public class ContactsController: ViewController { let presentPeersNearby = { let controller = strongSelf.context.sharedContext.makePeersNearbyController(context: strongSelf.context) controller.navigationPresentation = .master - (strongSelf.navigationController as? NavigationController)?.pushViewController(controller, animated: true, completion: { [weak self] in - if let strongSelf = self { - strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true) - } - }) + if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController { + navigationController.pushViewController(controller, animated: true, completion: { [weak self] in + if let strongSelf = self { + strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true) + } + }) + } } switch status { @@ -332,11 +337,13 @@ public class ContactsController: ViewController { let _ = (strongSelf.navigationController as? NavigationController)?.popViewController(animated: true) } } - (strongSelf.navigationController as? NavigationController)?.pushViewController(controller, completion: { [weak self] in - if let strongSelf = self { - strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true) - } - }) + if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController { + navigationController.pushViewController(controller, completion: { [weak self] in + if let strongSelf = self { + strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true) + } + }) + } } }) } @@ -480,20 +487,26 @@ public class ContactsController: ViewController { switch status { case .allowed: let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: "", lastName: "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!!$_", value: "+")]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "") - (strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in - guard let strongSelf = self else { - return - } - if let peer = peer { - DispatchQueue.main.async { - if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { - (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) + if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController { + navigationController.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in + guard let strongSelf = self else { + return + } + if let peer = peer { + DispatchQueue.main.async { + if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController { + navigationController.pushViewController(infoController) + } + } + } + } else { + if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController { + navigationController.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil)) } } - } else { - (strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil)) - } - }), completed: nil, cancelled: nil)) + }), completed: nil, cancelled: nil)) + } case .notDetermined: DeviceAccess.authorizeAccess(to: .contacts) default: @@ -504,4 +517,54 @@ public class ContactsController: ViewController { } }) } + + override public func tabBarItemContextAction(sourceNode: ContextExtractedContentContainingNode, gesture: ContextGesture) { + var items: [ContextMenuItem] = [] + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Contacts_AddContact, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, f in + c.dismiss(completion: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.addPressed() + }) + }))) + + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Contacts_AddPeopleNearby, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Contact List/Context Menu/PeopleNearby"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, f in + c.dismiss(completion: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.contactsNode.openPeopleNearby?() + }) + }))) + + let controller = ContextController(account: self.context.account, presentationData: self.presentationData, source: .extracted(ContactsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(items: items)), recognizer: nil, gesture: gesture) + self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) + } +} + +private final class ContactsTabBarContextExtractedContentSource: ContextExtractedContentSource { + let keepInPlace: Bool = true + let ignoreContentTouches: Bool = true + let blurBackground: Bool = true + + private let controller: ViewController + private let sourceNode: ContextExtractedContentContainingNode + + init(controller: ViewController, sourceNode: ContextExtractedContentContainingNode) { + self.controller = controller + self.sourceNode = sourceNode + } + + func takeView() -> ContextControllerTakeViewInfo? { + return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds) + } + + func putBack() -> ContextControllerPutBackViewInfo? { + return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) + } } diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 0e228969c3..ede32ea4d5 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -550,6 +550,7 @@ open class NavigationBar: ASDisplayNode { if self.badgeNode.text != actualText { self.badgeNode.text = actualText self.badgeNode.isHidden = actualText.isEmpty + self.backButtonNode.manualAlpha = self.badgeNode.isHidden ? 1.0 : 0.0 self.invalidateCalculatedLayout() self.requestLayout() @@ -835,7 +836,7 @@ open class NavigationBar: ASDisplayNode { self.titleNode.accessibilityTraits = .header self.backButtonNode = NavigationButtonNode() - self.badgeNode = NavigationBarBadgeNode(fillColor: self.presentationData.theme.badgeBackgroundColor, strokeColor: self.presentationData.theme.badgeStrokeColor, textColor: self.presentationData.theme.badgeTextColor) + self.badgeNode = NavigationBarBadgeNode(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) self.badgeNode.isUserInteractionEnabled = false self.badgeNode.isHidden = true self.backButtonArrow = ASImageNode() @@ -890,6 +891,7 @@ open class NavigationBar: ASDisplayNode { self.backButtonNode.highlightChanged = { [weak self] index, highlighted in if let strongSelf = self, index == 0 { strongSelf.backButtonArrow.alpha = (highlighted ? 0.4 : 1.0) + strongSelf.badgeNode.alpha = (highlighted ? 0.4 : 1.0) } } self.backButtonNode.pressed = { [weak self] index in @@ -957,7 +959,7 @@ open class NavigationBar: ASDisplayNode { } self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor - self.badgeNode.updateTheme(fillColor: self.presentationData.theme.badgeBackgroundColor, strokeColor: self.presentationData.theme.badgeStrokeColor, textColor: self.presentationData.theme.badgeTextColor) + self.badgeNode.updateTheme(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) self.requestLayout() } @@ -1089,7 +1091,7 @@ open class NavigationBar: ASDisplayNode { let badgeSize = self.badgeNode.measure(CGSize(width: 200.0, height: 100.0)) let backButtonArrowFrame = self.backButtonArrow.frame - transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: backButtonArrowFrame.origin.offsetBy(dx: 7.0, dy: -9.0), size: badgeSize)) + transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: backButtonArrowFrame.origin.offsetBy(dx: 16.0, dy: 2.0), size: badgeSize)) if self.rightButtonNode.supernode != nil { let rightButtonSize = self.rightButtonNode.updateLayout(constrainedSize: (CGSize(width: size.width, height: nominalHeight)), isLandscape: isLandscape) @@ -1122,7 +1124,7 @@ open class NavigationBar: ASDisplayNode { transitionBackArrowNode.alpha = max(0.0, 1.0 - progress * 1.3) if let transitionBadgeNode = self.transitionBadgeNode { - transitionBadgeNode.frame = CGRect(origin: transitionBackArrowNode.frame.origin.offsetBy(dx: 7.0, dy: -9.0), size: transitionBadgeNode.bounds.size) + transitionBadgeNode.frame = CGRect(origin: transitionBackArrowNode.frame.origin.offsetBy(dx: 16.0, dy: 2.0), size: transitionBadgeNode.bounds.size) transitionBadgeNode.alpha = transitionBackArrowNode.alpha } } @@ -1284,7 +1286,7 @@ open class NavigationBar: ASDisplayNode { public func makeTransitionBadgeNode() -> ASDisplayNode? { if self.badgeNode.supernode != nil && !self.badgeNode.isHidden { - let node = NavigationBarBadgeNode(fillColor: self.presentationData.theme.badgeBackgroundColor, strokeColor: self.presentationData.theme.badgeStrokeColor, textColor: self.presentationData.theme.badgeTextColor) + let node = NavigationBarBadgeNode(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) node.text = self.badgeNode.text let nodeSize = node.measure(CGSize(width: 200.0, height: 100.0)) node.frame = CGRect(origin: CGPoint(), size: nodeSize) diff --git a/submodules/Display/Source/NavigationBarBadge.swift b/submodules/Display/Source/NavigationBarBadge.swift index e12b716ef5..2c998d3a57 100644 --- a/submodules/Display/Source/NavigationBarBadge.swift +++ b/submodules/Display/Source/NavigationBarBadge.swift @@ -31,7 +31,7 @@ public final class NavigationBarBadgeNode: ASDisplayNode { self.backgroundNode = ASImageNode() self.backgroundNode.isLayerBacked = true self.backgroundNode.displaysAsynchronously = false - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: fillColor, strokeColor: strokeColor, strokeWidth: 1.0) + self.backgroundNode.image = generateStretchableFilledCircleImage(radius: 18.0, color: fillColor, backgroundColor: nil) super.init() @@ -43,14 +43,19 @@ public final class NavigationBarBadgeNode: ASDisplayNode { self.fillColor = fillColor self.strokeColor = strokeColor self.textColor = textColor - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: fillColor, strokeColor: strokeColor, strokeWidth: 1.0) + self.backgroundNode.image = generateStretchableFilledCircleImage(radius: 18.0, color: fillColor, backgroundColor: nil) self.textNode.attributedText = NSAttributedString(string: self.text, font: self.font, textColor: self.textColor) self.textNode.redrawIfPossible() } override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { let badgeSize = self.textNode.updateLayout(constrainedSize) - let backgroundSize = CGSize(width: max(18.0, badgeSize.width + 10.0 + 1.0), height: 18.0) + let backgroundSize: CGSize + if self.text.count < 2 { + backgroundSize = CGSize(width: 18.0, height: 18.0) + } else { + backgroundSize = CGSize(width: max(18.0, badgeSize.width + 10.0 + 1.0), height: 18.0) + } let backgroundFrame = CGRect(origin: CGPoint(), size: backgroundSize) self.backgroundNode.frame = backgroundFrame self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(backgroundFrame.midX - badgeSize.width / 2.0), y: floorToScreenPixels((backgroundFrame.size.height - badgeSize.height) / 2.0)), size: badgeSize) diff --git a/submodules/Display/Source/NavigationButtonNode.swift b/submodules/Display/Source/NavigationButtonNode.swift index 169b57ca03..0c57874efe 100644 --- a/submodules/Display/Source/NavigationButtonNode.swift +++ b/submodules/Display/Source/NavigationButtonNode.swift @@ -278,7 +278,11 @@ private final class NavigationButtonItemNode: ImmediateTextNode { let result = node.view.hitTest(self.view.convert(point, to: node.view), with: event) return result } else { - return super.hitTest(point, with: event) + let previousAlpha = self.alpha + self.alpha = 1.0 + let result = super.hitTest(point, with: event) + self.alpha = previousAlpha + return result } } @@ -300,7 +304,9 @@ private final class NavigationButtonItemNode: ImmediateTextNode { } if shouldChangeHighlight { - self.alpha = !self.isEnabled ? 1.0 : (highlighted ? 0.4 : 1.0) + if self.alpha > 0.0 { + self.alpha = !self.isEnabled ? 1.0 : (highlighted ? 0.4 : 1.0) + } self.highlightChanged(highlighted) } } @@ -379,6 +385,14 @@ public final class NavigationButtonNode: ASDisplayNode { return self.nodes.first?.text ?? "" } + var manualAlpha: CGFloat = 1.0 { + didSet { + for node in self.nodes { + node.alpha = self.manualAlpha + } + } + } + func updateManualText(_ text: String, isBack: Bool = true) { let node: NavigationButtonItemNode if self.nodes.count > 0 { @@ -404,6 +418,7 @@ public final class NavigationButtonNode: ASDisplayNode { self.nodes.append(node) self.addSubnode(node) } + node.alpha = self.manualAlpha node.item = nil node.image = nil node.text = text @@ -445,6 +460,7 @@ public final class NavigationButtonNode: ASDisplayNode { self.nodes.append(node) self.addSubnode(node) } + node.alpha = self.manualAlpha node.item = items[i] node.image = items[i].image node.text = items[i].title ?? "" diff --git a/submodules/GalleryUI/Sources/GalleryControllerNode.swift b/submodules/GalleryUI/Sources/GalleryControllerNode.swift index 0c1152e64c..ea6054ff1a 100644 --- a/submodules/GalleryUI/Sources/GalleryControllerNode.swift +++ b/submodules/GalleryUI/Sources/GalleryControllerNode.swift @@ -7,7 +7,11 @@ import SwipeToDismissGesture open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDelegate { public var statusBar: StatusBar? - public var navigationBar: NavigationBar? + public var navigationBar: NavigationBar? { + didSet { + + } + } public let footerNode: GalleryFooterNode public var currentThumbnailContainerNode: GalleryThumbnailContainerNode? public var overlayNode: ASDisplayNode? @@ -137,7 +141,6 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture self.view.addSubview(self.scrollView) self.scrollView.addSubview(self.pager.view) - self.addSubnode(self.footerNode) var previousIndex: Int? self.pager.centralItemIndexOffsetUpdated = { [weak self] itemsIndexAndProgress in @@ -256,6 +259,9 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture if let navigationBar = self.navigationBar { transition.updateFrame(node: navigationBar, frame: CGRect(origin: CGPoint(x: 0.0, y: self.areControlsHidden ? -navigationBarHeight : 0.0), size: CGSize(width: layout.size.width, height: navigationBarHeight))) + if self.footerNode.supernode == nil { + self.addSubnode(self.footerNode) + } } var thumbnailPanelHeight: CGFloat = 0.0 diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 625d8ac3ec..2ec9e63f32 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -215,8 +215,6 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { private let recognitionDisposable = MetaDisposable() private var status: MediaResourceStatus? - private var textCopiedTooltipController: UndoOverlayController? - private let pagingEnabledPromise = ValuePromise(true) init(context: AccountContext, presentationData: PresentationData, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void, present: @escaping (ViewController, Any?) -> Void) { @@ -260,8 +258,11 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { if let recognizedContentNode = strongSelf.recognizedContentNode { strongSelf.imageNode.isUserInteractionEnabled = active transition.updateAlpha(node: recognizedContentNode, alpha: active ? 1.0 : 0.0) - if !active { + if active { + strongSelf.updateControlsVisibility(false) + } else { recognizedContentNode.dismissSelection() + strongSelf.updateControlsVisibility(true) } strongSelf.pagingEnabledPromise.set(!active) } @@ -333,7 +334,6 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { if let controller = strongSelf.baseNavigationController()?.topViewController as? ViewController { let presentationData = strongSelf.context.sharedContext.currentPresentationData.with({ $0 }) let tooltipController = UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }) - strongSelf.textCopiedTooltipController = tooltipController controller.present(tooltipController, in: .window(.root)) } case .share: @@ -626,7 +626,19 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { } override func animateOut(to node: (ASDisplayNode, CGRect, () -> (UIView?, UIView?)), addToTransitionSurface: (UIView) -> Void, completion: @escaping () -> Void) { - self.textCopiedTooltipController?.dismiss() + if let controller = self.baseNavigationController()?.topViewController as? ViewController { + controller.window?.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + }) + controller.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + return true + }) + } self.fetchDisposable.set(nil) @@ -1098,6 +1110,7 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode { private let backgroundNode: ASImageNode private let selectedBackgroundNode: ASImageNode private let iconNode: ASImageNode + private let selectedIconNode: ASImageNode private let buttonNode: HighlightTrackingButtonNode var action: ((Bool) -> Void)? @@ -1106,12 +1119,11 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode { init(theme: PresentationTheme) { self.backgroundNode = ASImageNode() self.backgroundNode.displaysAsynchronously = false - self.backgroundNode.image = generateFilledCircleImage(diameter: 32.0, color: UIColor(white: 0.0, alpha: 0.6)) - + self.selectedBackgroundNode = ASImageNode() self.selectedBackgroundNode.displaysAsynchronously = false self.selectedBackgroundNode.isHidden = true - self.selectedBackgroundNode.image = generateFilledCircleImage(diameter: 32.0, color: theme.list.itemAccentColor) + self.selectedBackgroundNode.image = generateFilledCircleImage(diameter: 32.0, color: .white) self.buttonNode = HighlightTrackingButtonNode() self.buttonNode.alpha = 0.0 @@ -1121,6 +1133,12 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode { self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/LiveTextIcon"), color: .white) self.iconNode.contentMode = .center + self.selectedIconNode = ASImageNode() + self.selectedIconNode.displaysAsynchronously = false + self.selectedIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/LiveTextIcon"), color: .black) + self.selectedIconNode.contentMode = .center + self.selectedIconNode.isHidden = true + super.init() self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) @@ -1128,13 +1146,16 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode { self.buttonNode.addSubnode(self.backgroundNode) self.buttonNode.addSubnode(self.selectedBackgroundNode) self.buttonNode.addSubnode(self.iconNode) + self.buttonNode.addSubnode(self.selectedIconNode) } @objc private func buttonPressed() { let newValue = !self.buttonNode.isSelected - self.action?(newValue) self.buttonNode.isSelected = newValue self.selectedBackgroundNode.isHidden = !newValue + self.selectedIconNode.isHidden = !newValue + + self.action?(newValue) if self.interfaceIsHidden && !newValue { let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut) @@ -1159,6 +1180,7 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode { self.backgroundNode.frame = CGRect(origin: CGPoint(x: 12.0, y: 12.0), size: buttonSize) self.selectedBackgroundNode.frame = CGRect(origin: CGPoint(x: 12.0, y: 12.0), size: buttonSize) self.iconNode.frame = CGRect(origin: CGPoint(x: 12.0, y: 12.0), size: buttonSize) + self.selectedIconNode.frame = CGRect(origin: CGPoint(x: 12.0, y: 12.0), size: buttonSize) if self.appeared { if !self.buttonNode.isSelected && isHidden { @@ -1168,7 +1190,7 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode { } } - transition.updateFrame(node: self.buttonNode, frame: CGRect(x: size.width - rightInset - buttonSize.width - 24.0, y: size.height - bottomInset - buttonSize.height - 24.0, width: buttonSize.width + 24.0, height: buttonSize.height + 24.0)) + transition.updateFrame(node: self.buttonNode, frame: CGRect(x: size.width - rightInset - buttonSize.width - 24.0, y: 41.0, width: buttonSize.width + 24.0, height: buttonSize.height + 24.0)) } override func animateIn(previousContentNode: GalleryOverlayContentNode?, transition: ContainedViewLayoutTransition) { diff --git a/submodules/TelegramCore/Sources/Settings/PeerContactSettings.swift b/submodules/TelegramCore/Sources/Settings/PeerContactSettings.swift index cc832eb3cb..3182f41a13 100644 --- a/submodules/TelegramCore/Sources/Settings/PeerContactSettings.swift +++ b/submodules/TelegramCore/Sources/Settings/PeerContactSettings.swift @@ -32,7 +32,7 @@ extension PeerStatusSettings { if (flags & (1 << 8)) != 0 { result.insert(.suggestAddMembers) } - self = PeerStatusSettings(flags: result, geoDistance: geoDistance, requestChatTitle: requestChatTitle, requestChatDate: requestChatDate, requestChatIsChannel: (flags & (1 << 9)) != 0) + self = PeerStatusSettings(flags: result, geoDistance: geoDistance, requestChatTitle: requestChatTitle, requestChatDate: requestChatDate, requestChatIsChannel: (flags & (1 << 10)) != 0) } } } diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/Contents.json b/submodules/TelegramUI/Images.xcassets/Contact List/Contents.json index 38f0c81fc2..6e965652df 100644 --- a/submodules/TelegramUI/Images.xcassets/Contact List/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Contact List/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/Contact List/Context Menu/Contents.json b/submodules/TelegramUI/Images.xcassets/Contact List/Context Menu/Contents.json new file mode 100644 index 0000000000..6e965652df --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Contact List/Context Menu/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/Context Menu/PeopleNearby.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Contact List/Context Menu/PeopleNearby.imageset/Contents.json new file mode 100644 index 0000000000..6b7b65b4e5 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Contact List/Context Menu/PeopleNearby.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "peoplenearbyon_24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Contact List/Context Menu/PeopleNearby.imageset/peoplenearbyon_24.pdf b/submodules/TelegramUI/Images.xcassets/Contact List/Context Menu/PeopleNearby.imageset/peoplenearbyon_24.pdf new file mode 100644 index 0000000000..1eada00e63 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Contact List/Context Menu/PeopleNearby.imageset/peoplenearbyon_24.pdf @@ -0,0 +1,145 @@ +%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 2.934998 1.735001 cm +0.000000 0.000000 0.000000 scn +7.330000 17.464998 m +7.330000 18.423212 8.106786 19.199999 9.065001 19.199999 c +10.023214 19.199999 10.800000 18.423212 10.800000 17.464998 c +10.800000 16.506784 10.023214 15.729999 9.065001 15.729999 c +8.106786 15.729999 7.330000 16.506784 7.330000 17.464998 c +h +9.065001 20.529999 m +7.372247 20.529999 6.000000 19.157751 6.000000 17.464998 c +6.000000 15.772245 7.372247 14.399999 9.065001 14.399999 c +10.757753 14.399999 12.130000 15.772245 12.130000 17.464998 c +12.130000 19.157751 10.757753 20.529999 9.065001 20.529999 c +h +1.330000 3.865000 m +1.330000 4.026144 1.400150 4.232668 1.634496 4.483692 c +1.872207 4.738321 2.249572 5.004240 2.774642 5.255713 c +3.677844 5.688284 4.928888 6.035789 6.400000 6.230032 c +6.400001 4.564999 l +6.400001 3.755901 7.055903 3.099998 7.865001 3.099998 c +10.265000 3.099998 l +11.074098 3.099998 11.730000 3.755901 11.730000 4.564999 c +11.730000 6.230032 l +13.201113 6.035789 14.452158 5.688284 15.355359 5.255713 c +15.880429 5.004240 16.257793 4.738321 16.495506 4.483692 c +16.729851 4.232668 16.800003 4.026144 16.800003 3.865000 c +16.800003 3.677490 16.702744 3.422382 16.359842 3.113541 c +16.017097 2.804838 15.483717 2.496361 14.767962 2.223692 c +13.341063 1.680111 11.324945 1.330000 9.065001 1.330000 c +6.805056 1.330000 4.788938 1.680111 3.362040 2.223692 c +2.646284 2.496361 2.112905 2.804838 1.770159 3.113541 c +1.427257 3.422382 1.330000 3.677490 1.330000 3.865000 c +h +2.200152 6.455237 m +3.300211 6.982090 4.758119 7.368791 6.400000 7.570784 c +6.400000 8.099998 l +5.859936 8.099998 l +5.076447 8.099998 4.338713 8.757976 4.479389 9.657219 c +4.588190 10.352705 4.856467 11.405879 5.540887 12.298394 c +6.249626 13.222622 7.373408 13.929998 9.065001 13.929998 c +10.756596 13.929998 11.880377 13.222621 12.589115 12.298393 c +13.273535 11.405878 13.541812 10.352703 13.650612 9.657217 c +13.791287 8.757974 13.053553 8.099998 12.270063 8.099998 c +11.730000 8.099998 l +11.730000 7.570784 l +13.371881 7.368791 14.829790 6.982090 15.929850 6.455237 c +16.545578 6.160346 17.079411 5.807216 17.467701 5.391293 c +17.859352 4.971766 18.130001 4.456234 18.130001 3.865000 c +18.130001 3.168854 17.757156 2.582132 17.249931 2.125290 c +16.742554 1.668306 16.045780 1.287239 15.241435 0.980824 c +13.628130 0.366230 11.444248 0.000000 9.065001 0.000000 c +6.685753 0.000000 4.501871 0.366230 2.888566 0.980824 c +2.084221 1.287239 1.387449 1.668306 0.880069 2.125290 c +0.372844 2.582132 0.000000 3.168854 0.000000 3.865000 c +0.000000 4.456234 0.270649 4.971766 0.662301 5.391293 c +1.050590 5.807216 1.584423 6.160346 2.200152 6.455237 c +h +6.596293 11.489061 m +6.105442 10.848969 5.886520 10.046863 5.793407 9.451655 c +5.793330 9.451145 l +5.800257 9.444448 5.821226 9.429998 5.859936 9.429998 c +7.065000 9.429998 l +7.432269 9.429998 7.730000 9.132269 7.730000 8.764999 c +7.730000 4.564999 l +7.730000 4.490440 7.790442 4.429998 7.865001 4.429998 c +8.533002 4.429998 l +8.533002 6.764999 l +8.533002 7.058815 8.771186 7.296999 9.065001 7.296999 c +9.358817 7.296999 9.597001 7.058815 9.597001 6.764999 c +9.597001 4.429998 l +10.265000 4.429998 l +10.339560 4.429998 10.400000 4.490440 10.400000 4.564999 c +10.400000 8.764999 l +10.400000 8.941368 10.470061 9.110514 10.594772 9.235225 c +10.719484 9.359937 10.888630 9.429998 11.064999 9.429998 c +12.270063 9.429998 l +12.308775 9.429998 12.329742 9.444448 12.336671 9.451145 c +12.336594 9.451655 l +12.243481 10.046862 12.024561 10.848969 11.533709 11.489061 c +11.067177 12.097442 10.327614 12.599998 9.065001 12.599998 c +7.802389 12.599998 7.062826 12.097442 6.596293 11.489061 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 3674 +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 +0000003764 00000 n +0000003787 00000 n +0000003960 00000 n +0000004034 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +4093 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatBotInfoItem.swift b/submodules/TelegramUI/Sources/ChatBotInfoItem.swift index 6ce332d648..489e38bce5 100644 --- a/submodules/TelegramUI/Sources/ChatBotInfoItem.swift +++ b/submodules/TelegramUI/Sources/ChatBotInfoItem.swift @@ -118,7 +118,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { break case .ignore: return .fail - case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy: + case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji: return .waitForSingleTap } } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 3b413b7fdc..1f516390b6 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3060,6 +3060,31 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G ) } }) + }, openLargeEmojiInfo: { [weak self] _, fitz, file in + guard let strongSelf = self else { + return + } + let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) + actionSheet.setItemGroups([ActionSheetItemGroup(items: [ + LargeEmojiActionSheetItem(context: strongSelf.context, text: strongSelf.presentationData.strings.Conversation_LargeEmojiDisabledInfo, fitz: fitz, file: file), + ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LargeEmojiEnable, color: .accent, action: { [weak actionSheet, weak self] in + actionSheet?.dismissAnimated() + guard let strongSelf = self else { + return + } + let _ = updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in + return current.withUpdatedLargeEmoji(true) + }).start() + + strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .emoji(name: "TwoFactorSetupRememberSuccess", text: strongSelf.presentationData.strings.Conversation_LargeEmojiEnabled), elevatedLayout: false, action: { _ in return false }), in: .current) + }) + ]), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + strongSelf.chatDisplayNode.dismissInput() + strongSelf.present(actionSheet, in: .window(.root)) }, requestMessageUpdate: { [weak self] id in if let strongSelf = self { strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id) diff --git a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift index f7fe5294b8..267e5d0ce2 100644 --- a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift @@ -122,6 +122,7 @@ public final class ChatControllerInteraction { let getMessageTransitionNode: () -> ChatMessageTransitionNode? let updateChoosingSticker: (Bool) -> Void let commitEmojiInteraction: (MessageId, String, EmojiInteraction, TelegramMediaFile) -> Void + let openLargeEmojiInfo: (String, String?, TelegramMediaFile) -> Void let requestMessageUpdate: (MessageId) -> Void let cancelInteractiveKeyboardGestures: () -> Void @@ -218,6 +219,7 @@ public final class ChatControllerInteraction { getMessageTransitionNode: @escaping () -> ChatMessageTransitionNode?, updateChoosingSticker: @escaping (Bool) -> Void, commitEmojiInteraction: @escaping (MessageId, String, EmojiInteraction, TelegramMediaFile) -> Void, + openLargeEmojiInfo: @escaping (String, String?, TelegramMediaFile) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, @@ -300,6 +302,7 @@ public final class ChatControllerInteraction { self.getMessageTransitionNode = getMessageTransitionNode self.updateChoosingSticker = updateChoosingSticker self.commitEmojiInteraction = commitEmojiInteraction + self.openLargeEmojiInfo = openLargeEmojiInfo self.requestMessageUpdate = requestMessageUpdate self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures @@ -356,6 +359,7 @@ public final class ChatControllerInteraction { return nil }, updateChoosingSticker: { _ in }, commitEmojiInteraction: { _, _, _, _ in + }, openLargeEmojiInfo: { _, _, _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift index 01a062a262..5a680d2d6a 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleContentNode.swift @@ -97,6 +97,7 @@ enum ChatMessageBubbleContentTapAction { case ignore case openPollResults(Data) case copy(String) + case largeEmoji(String, String?, TelegramMediaFile) } final class ChatMessageBubbleContentItem { diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 3c91d78eb3..f72a9a7929 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -782,7 +782,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode break case .ignore: return .fail - case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy: + case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji: return .waitForSingleTap } } @@ -3098,6 +3098,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode item.controllerInteraction.copyText(text) }) } + case let .largeEmoji(emoji, fitz, file): + if let item = self.item { + return .optionalAction({ + item.controllerInteraction.openLargeEmojiInfo(emoji, fitz, file) + }) + } } } return nil @@ -3176,6 +3182,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode break case .copy: break + case .largeEmoji: + break } } if let tapMessage = tapMessage { diff --git a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift index c87866aab2..6d16c61cc7 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift @@ -460,7 +460,23 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } else if let pre = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Pre)] as? String { return .copy(pre) } else { - return .none + if let item = self.item, item.message.text.count == 1, !item.presentationData.largeEmoji { + let (emoji, fitz) = item.message.text.basicEmoji + var emojiFile: TelegramMediaFile? + + emojiFile = item.associatedData.animatedEmojiStickers[emoji]?.first?.file + if emojiFile == nil { + emojiFile = item.associatedData.animatedEmojiStickers[emoji.strippedEmoji]?.first?.file + } + + if let emojiFile = emojiFile { + return .largeEmoji(emoji, fitz, emojiFile) + } else { + return .none + } + } else { + return .none + } } } else { if let _ = self.statusNode.hitTest(self.view.convert(point, to: self.statusNode.view), with: nil) { diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index 1b841cee2e..ee7aa1e43c 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -529,6 +529,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { return nil }, updateChoosingSticker: { _ in }, commitEmojiInteraction: { _, _, _, _ in + }, openLargeEmojiInfo: { _, _, _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, diff --git a/submodules/TelegramUI/Sources/ChatTextInputMenu.swift b/submodules/TelegramUI/Sources/ChatTextInputMenu.swift index 0db7a756ca..75efd13155 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputMenu.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputMenu.swift @@ -89,4 +89,14 @@ final class ChatTextInputMenu { self.state = .general } } + + func hide() { + self.back() + if #available(iOS 13.0, *) { + UIMenuController.shared.hideMenu() + } else { + UIMenuController.shared.isMenuVisible = false + } + UIMenuController.shared.update() + } } diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 57e3745aad..f1dae67d4e 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -2048,8 +2048,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState { if case .format = self.inputMenu.state { - self.inputMenu.deactivate() - UIMenuController.shared.update() + self.inputMenu.hide() } let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) diff --git a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift index 3a5d0d7850..be65cf340a 100644 --- a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift +++ b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift @@ -155,6 +155,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode { return nil }, updateChoosingSticker: { _ in }, commitEmojiInteraction: { _, _, _, _ in + }, openLargeEmojiInfo: { _, _, _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, diff --git a/submodules/TelegramUI/Sources/LargeEmojiActionSheetItem.swift b/submodules/TelegramUI/Sources/LargeEmojiActionSheetItem.swift new file mode 100644 index 0000000000..47485ac4be --- /dev/null +++ b/submodules/TelegramUI/Sources/LargeEmojiActionSheetItem.swift @@ -0,0 +1,185 @@ +import Foundation +import UIKit +import Display +import Postbox +import SwiftSignalKit +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import AccountContext +import AnimatedStickerNode +import TelegramAnimatedStickerNode +import MediaResources +import StickerResources +import ShimmerEffect + +public final class LargeEmojiActionSheetItem: ActionSheetItem { + let context: AccountContext + let text: String + let fitz: String? + let file: TelegramMediaFile + + public init(context: AccountContext, text: String, fitz: String?, file: TelegramMediaFile) { + self.context = context + self.text = text + self.fitz = fitz + self.file = file + } + + public func node(theme: ActionSheetControllerTheme) -> ActionSheetItemNode { + return LargeEmojiActionSheetItemNode(theme: theme, context: self.context, text: self.text, fitz: self.fitz, file: self.file) + } + + public func updateNode(_ node: ActionSheetItemNode) { + } +} + +private final class LargeEmojiActionSheetItemNode: ActionSheetItemNode { + private let theme: ActionSheetControllerTheme + + private var placeholderNode: StickerShimmerEffectNode + private let imageNode: TransformImageNode + private let animationNode: AnimatedStickerNode + private let textNode: ImmediateTextNode + + private let accessibilityArea: AccessibilityAreaNode + + private let disposable = MetaDisposable() + + private var setupTimestamp: Double? + + init(theme: ActionSheetControllerTheme, context: AccountContext, text: String, fitz: String?, file: TelegramMediaFile) { + self.theme = theme + + let textFont = Font.regular(floor(theme.baseFontSize * 13.0 / 17.0)) + + self.placeholderNode = StickerShimmerEffectNode() + self.placeholderNode.isUserInteractionEnabled = false + + self.imageNode = TransformImageNode() + self.imageNode.displaysAsynchronously = false + + var fitzModifier: EmojiFitzModifier? + if let fitz = fitz { + fitzModifier = EmojiFitzModifier(emoji: fitz) + } + self.animationNode = AnimatedStickerNode() + self.animationNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: file.resource, fitzModifier: fitzModifier), width: 192, height: 192, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) + self.animationNode.visibility = true + + self.textNode = ImmediateTextNode() + self.textNode.displaysAsynchronously = false + self.textNode.maximumNumberOfLines = 0 + self.textNode.textAlignment = .center + self.textNode.isAccessibilityElement = false + + self.accessibilityArea = AccessibilityAreaNode() + + super.init(theme: theme) + + self.hasSeparator = true + + self.addSubnode(self.imageNode) + self.addSubnode(self.placeholderNode) + self.addSubnode(self.animationNode) + self.addSubnode(self.textNode) + self.addSubnode(self.accessibilityArea) + + let attributedText = NSAttributedString(string: text, font: textFont, textColor: theme.secondaryTextColor) + self.textNode.attributedText = attributedText + + self.accessibilityArea.accessibilityLabel = attributedText.string + self.accessibilityArea.accessibilityTraits = .staticText + + let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512) + self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: context.account.postbox, file: file, small: false, size: dimensions.cgSize.aspectFilled(CGSize(width: 384.0, height: 384.0)), fitzModifier: fitzModifier, thumbnail: false, synchronousLoad: true), attemptSynchronously: true) + self.disposable.set(freeMediaFileInteractiveFetched(account: context.account, fileReference: .standalone(media: file)).start()) + + self.setupTimestamp = CACurrentMediaTime() + + self.animationNode.started = { [weak self] in + if let strongSelf = self { + strongSelf.imageNode.alpha = 0.0 + + let current = CACurrentMediaTime() + if let setupTimestamp = strongSelf.setupTimestamp, current - setupTimestamp > 0.3 { + if !strongSelf.placeholderNode.alpha.isZero { + strongSelf.animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + strongSelf.removePlaceholder(animated: true) + } + } else { + strongSelf.removePlaceholder(animated: false) + } + } + } + + var firstTime = true + self.imageNode.imageUpdated = { [weak self] image in + guard let strongSelf = self else { + return + } + if image != nil { + if firstTime && !strongSelf.placeholderNode.isEmpty { + strongSelf.imageNode.alpha = 0.0 + } else { + if strongSelf.setupTimestamp == nil { + strongSelf.removePlaceholder(animated: true) + } + } + firstTime = false + } + } + + if let immediateThumbnailData = file.immediateThumbnailData { + self.placeholderNode.update(backgroundColor: nil, foregroundColor: theme.secondaryTextColor.blitOver(theme.itemBackgroundColor, alpha: 0.55), shimmeringColor: theme.itemBackgroundColor.withAlphaComponent(0.4), data: immediateThumbnailData, size: CGSize(width: 96.0, height: 96.0), imageSize: dimensions.cgSize) + } + } + + deinit { + self.disposable.dispose() + } + + override func didLoad() { + super.didLoad() + + self.animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tap))) + } + + @objc private func tap() { + let _ = self.animationNode.playIfNeeded() + } + + private func removePlaceholder(animated: Bool) { + self.placeholderNode.alpha = 0.0 + if !animated { + self.placeholderNode.removeFromSupernode() + } else { + self.placeholderNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak self] _ in + self?.placeholderNode.removeFromSupernode() + }) + } + } + + public override func updateLayout(constrainedSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + let textSize = self.textNode.updateLayout(CGSize(width: constrainedSize.width - 120.0, height: .greatestFiniteMagnitude)) + + let topInset: CGFloat = 26.0 + let textSpacing: CGFloat = 17.0 + let bottomInset: CGFloat = 15.0 + + let iconSize = CGSize(width: 96.0, height: 96.0) + self.animationNode.frame = CGRect(origin: CGPoint(x: floor((constrainedSize.width - iconSize.width) / 2.0), y: topInset), size: iconSize) + self.animationNode.updateLayout(size: iconSize) + self.placeholderNode.frame = self.animationNode.frame + + self.textNode.frame = CGRect(origin: CGPoint(x: floor((constrainedSize.width - textSize.width) / 2.0), y: topInset + iconSize.height + textSpacing), size: textSize) + + let size = CGSize(width: constrainedSize.width, height: topInset + iconSize.height + textSpacing + textSize.height + bottomInset) + self.accessibilityArea.frame = CGRect(origin: CGPoint(), size: size) + + self.placeholderNode.updateAbsoluteRect(CGRect(origin: CGPoint(x: self.placeholderNode.frame.minX, y: self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: size) + + self.updateInternalLayout(size, constrainedSize: constrainedSize) + return size + } +} diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index 9c22d09ff8..7917809ed9 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -147,6 +147,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu return nil }, updateChoosingSticker: { _ in }, commitEmojiInteraction: { _, _, _, _ in + }, openLargeEmojiInfo: { _, _, _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false), presentationContext: ChatPresentationContext(backgroundNode: nil)) diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift index 190cb52ea3..faeeb1c6ec 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift @@ -52,6 +52,22 @@ final class PeerInfoScreenLabeledValueItem: PeerInfoScreenItem { } } +private func generateExpandBackground(size: CGSize, color: UIColor) -> UIImage? { + return generateImage(size, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + var locations: [CGFloat] = [0.0, 1.0] + let colors: [CGColor] = [color.withAlphaComponent(0.0).cgColor, color.cgColor] + + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 40.0, y: size.height), options: CGGradientDrawingOptions()) + context.setFillColor(color.cgColor) + context.fill(CGRect(origin: CGPoint(x: 40.0, y: 0.0), size: CGSize(width: size.width - 40.0, height: size.height))) + }) +} + private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { private let selectionNode: PeerInfoScreenSelectableBackgroundNode private let maskNode: ASImageNode @@ -59,6 +75,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { private let textNode: ImmediateTextNode private let bottomSeparatorNode: ASDisplayNode + private let expandBackgroundNode: ASImageNode private let expandNode: ImmediateTextNode private let expandButonNode: HighlightTrackingButtonNode @@ -90,6 +107,9 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { self.bottomSeparatorNode = ASDisplayNode() self.bottomSeparatorNode.isLayerBacked = true + self.expandBackgroundNode = ASImageNode() + self.expandBackgroundNode.displaysAsynchronously = false + self.expandNode = ImmediateTextNode() self.expandNode.displaysAsynchronously = false self.expandNode.isUserInteractionEnabled = false @@ -110,6 +130,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { self.addSubnode(self.labelNode) self.addSubnode(self.textNode) + self.addSubnode(self.expandBackgroundNode) self.addSubnode(self.expandNode) self.addSubnode(self.expandButonNode) @@ -211,7 +232,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { textColorValue = presentationData.theme.list.itemAccentColor } - self.expandNode.attributedText = NSAttributedString(string: presentationData.strings.PeerInfo_BioExpand, font: Font.regular(17.0), textColor: presentationData.theme.list.itemAccentColor) + self.expandNode.attributedText = NSAttributedString(string: presentationData.strings.PeerInfo_BioExpand.uppercased(), font: Font.medium(16.0), textColor: presentationData.theme.list.itemAccentColor) let expandSize = self.expandNode.updateLayout(CGSize(width: width, height: 100.0)) self.labelNode.attributedText = NSAttributedString(string: item.label, font: Font.regular(14.0), textColor: presentationData.theme.list.itemPrimaryTextColor) @@ -223,7 +244,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue) case let .multiLine(maxLines, enabledEntities): self.textNode.maximumNumberOfLines = self.isExpanded ? maxLines : 3 - self.textNode.cutout = self.isExpanded ? nil : TextNodeCutout(bottomRight: CGSize(width: expandSize.width + 4.0, height: expandSize.height)) +// self.textNode.cutout = self.isExpanded ? nil : TextNodeCutout(bottomRight: CGSize(width: expandSize.width + 4.0, height: expandSize.height)) if enabledEntities.isEmpty { self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue) } else { @@ -246,9 +267,11 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { let textSize = textLayout.size if case .multiLine = item.textBehavior, textLayout.truncated, !self.isExpanded { + self.expandBackgroundNode.isHidden = false self.expandNode.isHidden = false self.expandButonNode.isHidden = false } else { + self.expandBackgroundNode.isHidden = true self.expandNode.isHidden = true self.expandButonNode.isHidden = true } @@ -260,6 +283,12 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { self.expandNode.frame = expandFrame self.expandButonNode.frame = expandFrame.insetBy(dx: -8.0, dy: -8.0) + var expandBackgroundFrame = expandFrame + expandBackgroundFrame.origin.x -= 50.0 + expandBackgroundFrame.size.width += 50.0 + self.expandBackgroundNode.frame = expandBackgroundFrame + self.expandBackgroundNode.image = generateExpandBackground(size: expandBackgroundFrame.size, color: presentationData.theme.list.itemBlocksBackgroundColor) + transition.updateFrame(node: self.labelNode, frame: labelFrame) transition.updateFrame(node: self.textNode, frame: textFrame) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index f9042e2600..a04619a5ea 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -2151,6 +2151,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate return nil }, updateChoosingSticker: { _ in }, commitEmojiInteraction: { _, _, _, _ in + }, openLargeEmojiInfo: { _, _, _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, diff --git a/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift b/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift index d9b2d34536..8272112c34 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift @@ -829,8 +829,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState { if case .format = self.inputMenu.state { - self.inputMenu.deactivate() - UIMenuController.shared.update() + self.inputMenu.hide() } let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index d118725be3..ba3334bbe1 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1292,6 +1292,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { return nil }, updateChoosingSticker: { _ in }, commitEmojiInteraction: { _, _, _, _ in + }, openLargeEmojiInfo: { _, _, _ in }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,