From 084bb5bcd580a9024d355012c3b571753d5534f2 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 28 Apr 2025 23:34:03 +0200 Subject: [PATCH] [WIP] Post suggestions --- .../Sources/AccountContext.swift | 7 +- .../IncomingMessagePrivacyScreen.swift | 2 +- .../ChatChannelSubscriberInputPanelNode.swift | 111 ++++++++++++------ .../Components/Chat/ChatEmptyNode/BUILD | 1 + .../ChatEmptyNode/Sources/ChatEmptyNode.swift | 20 +++- .../ChatScheduleTimeControllerNode.swift | 34 +++++- .../PeerInfo/MessagePriceItem/BUILD | 1 + .../Sources/MessagePriceItem.swift | 70 ++++++++--- .../Sources/PeerInfoScreen.swift | 2 +- .../PostSuggestionsSettingsScreen/BUILD | 3 +- .../PostSuggestionsSettingsScreen.swift | 73 ++++++++---- .../Sources/StarsWithdrawalScreen.swift | 19 +-- .../PostSuggestions.imageset/Contents.json | 12 ++ .../postsuggestions.pdf | Bin 0 -> 4256 bytes .../Contents.json | 12 ++ .../ic_discussion.pdf | Bin 0 -> 5373 bytes .../SuggestPost.imageset/Contents.json | 12 ++ .../postsuggestionsbutton.pdf | Bin 0 -> 5732 bytes .../ChatInterfaceStateContextMenus.swift | 30 +++++ submodules/TooltipUI/BUILD | 1 + .../TooltipUI/Sources/TooltipScreen.swift | 95 +++++++++++++-- 21 files changed, 412 insertions(+), 93 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/PostSuggestions.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/PostSuggestions.imageset/postsuggestions.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Info/PostSuggestionsIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Info/PostSuggestionsIcon.imageset/ic_discussion.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/SuggestPost.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/SuggestPost.imageset/postsuggestionsbutton.pdf diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index c217c4ee6f..1fbf3888c4 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -996,8 +996,13 @@ public enum SendInviteLinkScreenSubject { } public enum StarsWithdrawalScreenSubject { + public enum PaidMessageKind { + case privacy + case postSuggestion + } + case withdraw - case enterAmount(current: StarsAmount) + case enterAmount(current: StarsAmount, minValue: StarsAmount, fractionAfterCommission: Int, kind: PaidMessageKind) } public protocol SharedAccountContext: AnyObject { diff --git a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift index 6db96282fd..006d640616 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift @@ -376,7 +376,7 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP if case let .paidMessages(value) = stateValue.with({ $0 }).updatedValue { currentAmount = value } - let starsScreen = context.sharedContext.makeStarsWithdrawalScreen(context: context, subject: .enterAmount(current: currentAmount), completion: { amount in + let starsScreen = context.sharedContext.makeStarsWithdrawalScreen(context: context, subject: .enterAmount(current: currentAmount, minValue: StarsAmount(value: 1, nanos: 0), fractionAfterCommission: 80, kind: .privacy), completion: { amount in updateState { state in var state = state state.updatedValue = .paidMessages(StarsAmount(value: amount, nanos: 0)) diff --git a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift index a1add0ec6b..993fab330b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift @@ -321,48 +321,88 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { return self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: transition, interfaceState: interfaceState, metrics: metrics, force: false) } - private var displayedGiftTooltip = false - private func presentGiftTooltip() { - guard let context = self.context, !self.displayedGiftTooltip, let parentController = self.interfaceInteraction?.chatController() else { + private var displayedGiftOrSuggestTooltip = false + private func presentGiftOrSuggestTooltip() { + guard let context = self.context, !self.displayedGiftOrSuggestTooltip, let parentController = self.interfaceInteraction?.chatController() else { return } - self.displayedGiftTooltip = true + self.displayedGiftOrSuggestTooltip = true - let _ = (ApplicationSpecificNotice.getChannelSendGiftTooltip(accountManager: context.sharedContext.accountManager) - |> deliverOnMainQueue).start(next: { [weak self] count in + let _ = (combineLatest(queue: .mainQueue(), + ApplicationSpecificNotice.getChannelSendGiftTooltip(accountManager: context.sharedContext.accountManager), + ApplicationSpecificNotice.getChannelSuggestTooltip(accountManager: context.sharedContext.accountManager) + |> deliverOnMainQueue)).start(next: { [weak self] giftCount, suggestCount in guard let self else { return } - guard count < 2 else { - return + + /*#if DEBUG + var giftCount = giftCount + var suggestCount = suggestCount + if "".isEmpty { + giftCount = 2 + suggestCount = 0 } + #endif*/ - let _ = ApplicationSpecificNotice.incrementChannelSendGiftTooltip(accountManager: context.sharedContext.accountManager).start() - - Queue.mainQueue().after(0.4, { - let absoluteFrame = self.giftButton.view.convert(self.giftButton.bounds, to: parentController.view) - let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY + 11.0), size: CGSize()) + if giftCount < 2 && !self.giftButton.isHidden { + let _ = ApplicationSpecificNotice.incrementChannelSendGiftTooltip(accountManager: context.sharedContext.accountManager).start() - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let text: String = presentationData.strings.Chat_SendGiftTooltip - - let tooltipController = TooltipScreen( - account: context.account, - sharedContext: context.sharedContext, - text: .plain(text: text), - balancedTextLayout: false, - style: .wide, - arrowStyle: .small, - icon: nil, - location: .point(location, .bottom), - displayDuration: .default, - inset: 8.0, - shouldDismissOnTouch: { _, _ in - return .ignore - } - ) - self.interfaceInteraction?.presentControllerInCurrent(tooltipController, nil) - }) + Queue.mainQueue().after(0.4, { + let absoluteFrame = self.giftButton.view.convert(self.giftButton.bounds, to: parentController.view) + let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY + 11.0), size: CGSize()) + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let text: String = presentationData.strings.Chat_SendGiftTooltip + + let tooltipController = TooltipScreen( + account: context.account, + sharedContext: context.sharedContext, + text: .plain(text: text), + balancedTextLayout: false, + style: .wide, + arrowStyle: .small, + icon: nil, + location: .point(location, .bottom), + displayDuration: .default, + inset: 8.0, + shouldDismissOnTouch: { _, _ in + return .ignore + } + ) + self.interfaceInteraction?.presentControllerInCurrent(tooltipController, nil) + }) + } else if suggestCount < 2 && !self.suggestedPostButton.isHidden { + let _ = ApplicationSpecificNotice.incrementChannelSuggestTooltip(accountManager: context.sharedContext.accountManager).start() + + Queue.mainQueue().after(0.4, { + let absoluteFrame = self.suggestedPostButton.view.convert(self.suggestedPostButton.bounds, to: parentController.view) + let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY + 11.0), size: CGSize()) + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let _ = presentationData + //TODO:localize + let text: String = "Tap here to suggest a message" + + let tooltipController = TooltipScreen( + account: context.account, + sharedContext: context.sharedContext, + text: .plain(text: text), + textBadge: "NEW", + balancedTextLayout: false, + style: .wide, + arrowStyle: .small, + icon: nil, + location: .point(location, .bottom), + displayDuration: .default, + inset: 8.0, + shouldDismissOnTouch: { _, _ in + return .ignore + } + ) + self.interfaceInteraction?.presentControllerInCurrent(tooltipController, nil) + }) + } }) } @@ -377,7 +417,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { if previousState?.theme !== interfaceState.theme { self.badgeBackground.image = PresentationResourcesChatList.badgeBackgroundActive(interfaceState.theme, diameter: 20.0) self.helpButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/Help"), color: interfaceState.theme.chat.inputPanel.panelControlAccentColor), for: .normal) - self.suggestedPostButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/Gift"), color: interfaceState.theme.chat.inputPanel.panelControlAccentColor), for: .normal) + self.suggestedPostButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/SuggestPost"), color: interfaceState.theme.chat.inputPanel.panelControlAccentColor), for: .normal) self.giftButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/Gift"), color: interfaceState.theme.chat.inputPanel.panelControlAccentColor), for: .normal) } @@ -431,11 +471,12 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { self.helpButton.isHidden = true //TODO:release self.suggestedPostButton.isHidden = false - self.presentGiftTooltip() + self.presentGiftOrSuggestTooltip() } else if case .broadcast = peer.info { self.giftButton.isHidden = true self.helpButton.isHidden = true self.suggestedPostButton.isHidden = false + self.presentGiftOrSuggestTooltip() } else if peer.flags.contains(.isGigagroup), self.action == .muteNotifications || self.action == .unmuteNotifications { self.giftButton.isHidden = true self.helpButton.isHidden = false diff --git a/submodules/TelegramUI/Components/Chat/ChatEmptyNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatEmptyNode/BUILD index b374c14152..0d443277ba 100644 --- a/submodules/TelegramUI/Components/Chat/ChatEmptyNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatEmptyNode/BUILD @@ -32,6 +32,7 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ChatMediaInputStickerGridItem", "//submodules/PremiumUI", "//submodules/TelegramUI/Components/LottieComponent", + "//submodules/Components/BundleIconComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift b/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift index 9bdc20deaf..f577d0ffc1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift @@ -23,6 +23,7 @@ import ChatMediaInputStickerGridItem import UndoUI import PremiumUI import LottieComponent +import BundleIconComponent private protocol ChatEmptyNodeContent { func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize @@ -1367,16 +1368,27 @@ public final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatE contentsHeight += iconBackgroundSize contentsHeight += iconTextSpacing - let iconSize = self.icon.update( - transition: .immediate, - component: AnyComponent( + let iconComponent: AnyComponent + if case let .customChatContents(customChatContents) = interfaceState.subject, case .postSuggestions = customChatContents.kind { + iconComponent = AnyComponent( + BundleIconComponent( + name: "Chat/Empty Chat/PostSuggestions", + tintColor: serviceColor.primaryText + ) + ) + } else { + iconComponent = AnyComponent( LottieComponent( content: LottieComponent.AppBundleContent(name: "PremiumRequired"), color: serviceColor.primaryText, size: CGSize(width: 120.0, height: 120.0), loop: true ) - ), + ) + } + let iconSize = self.icon.update( + transition: .immediate, + component: iconComponent, environment: {}, containerSize: CGSize(width: maxWidth - sideInset * 2.0, height: 500.0) ) diff --git a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeControllerNode.swift b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeControllerNode.swift index 88c6b9a864..5c117b39ca 100644 --- a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeControllerNode.swift +++ b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeControllerNode.swift @@ -26,6 +26,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, ASScrollViewDel private let backgroundNode: ASDisplayNode private let contentBackgroundNode: ASDisplayNode private let titleNode: ASTextNode + private let textNode: ASTextNode? private let cancelButton: HighlightableButtonNode private let doneButton: SolidRoundedButtonNode private let onlineButton: SolidRoundedButtonNode @@ -93,6 +94,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, ASScrollViewDel self.contentBackgroundNode.backgroundColor = backgroundColor let title: String + var text: String? switch mode { case .scheduledMessages: title = self.presentationData.strings.Conversation_ScheduleMessage_Title @@ -101,6 +103,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, ASScrollViewDel case .suggestPost: //TODO:localize title = "Time" + text = "Set the date and time you want\nyour message to be published." } self.titleNode = ASTextNode() @@ -108,6 +111,19 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, ASScrollViewDel self.titleNode.accessibilityLabel = title self.titleNode.accessibilityTraits = [.staticText] + if let text { + let textNode = ASTextNode() + textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: textColor) + textNode.maximumNumberOfLines = 0 + textNode.textAlignment = .center + textNode.lineSpacing = 0.2 + textNode.accessibilityLabel = text + textNode.accessibilityTraits = [.staticText] + self.textNode = textNode + } else { + self.textNode = nil + } + self.cancelButton = HighlightableButtonNode() self.cancelButton.setTitle(self.presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: accentColor, for: .normal) self.cancelButton.accessibilityLabel = self.presentationData.strings.Common_Cancel @@ -146,6 +162,9 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, ASScrollViewDel self.backgroundNode.addSubnode(self.effectNode) self.backgroundNode.addSubnode(self.contentBackgroundNode) self.contentContainerNode.addSubnode(self.titleNode) + if let textNode = self.textNode { + self.contentContainerNode.addSubnode(textNode) + } self.contentContainerNode.addSubnode(self.cancelButton) self.contentContainerNode.addSubnode(self.doneButton) if case .scheduledMessages(true) = self.mode { @@ -422,6 +441,13 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, ASScrollViewDel } let width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 0.0) + let textControlSpacing: CGFloat = -8.0 + let textDoneSpacing: CGFloat = 21.0 + let textSize = self.textNode?.measure(CGSize(width: width, height: 1000.0)) + if let textSize { + contentHeight += textSize.height + textControlSpacing + textDoneSpacing + } + let sideInset = floor((layout.size.width - width) / 2.0) let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: layout.size.height - contentHeight), size: CGSize(width: width, height: contentHeight)) let contentFrame = contentContainerFrame @@ -446,7 +472,13 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, ASScrollViewDel let buttonInset: CGFloat = 16.0 let doneButtonHeight = self.doneButton.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition) - transition.updateFrame(node: self.doneButton, frame: CGRect(x: buttonInset, y: contentHeight - doneButtonHeight - insets.bottom - 16.0 - buttonOffset, width: contentFrame.width, height: doneButtonHeight)) + let doneButtonFrame = CGRect(x: buttonInset, y: contentHeight - doneButtonHeight - insets.bottom - 16.0 - buttonOffset, width: contentFrame.width, height: doneButtonHeight) + transition.updateFrame(node: self.doneButton, frame: doneButtonFrame) + + if let textNode = self.textNode, let textSize { + let textFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - textSize.width) / 2.0), y: doneButtonFrame.minY - textDoneSpacing - textSize.height), size: textSize) + transition.updateFrame(node: textNode, frame: textFrame) + } let onlineButtonHeight = self.onlineButton.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition) transition.updateFrame(node: self.onlineButton, frame: CGRect(x: buttonInset, y: contentHeight - onlineButtonHeight - cleanInsets.bottom - 16.0, width: contentFrame.width, height: onlineButtonHeight)) diff --git a/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/BUILD b/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/BUILD index adbe07190b..78ff393298 100644 --- a/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/BUILD @@ -23,6 +23,7 @@ swift_library( "//submodules/TelegramUI/Components/ButtonComponent", "//submodules/Components/BundleIconComponent", "//submodules/Components/MultilineTextComponent", + "//submodules/TelegramUI/Components/ListItemComponentAdaptor", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift b/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift index 09043395e5..d907298c7e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift @@ -12,11 +12,12 @@ import ComponentFlow import ButtonComponent import BundleIconComponent import MultilineTextComponent +import ListItemComponentAdaptor private let textFont = Font.with(size: 17.0, traits: .monospacedNumbers) private let smallTextFont = Font.with(size: 13.0, traits: .monospacedNumbers) -public final class MessagePriceItem: ListViewItem, ItemListItem { +public final class MessagePriceItem: Equatable, ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator { let theme: PresentationTheme let strings: PresentationStrings let isEnabled: Bool @@ -75,11 +76,46 @@ public final class MessagePriceItem: ListViewItem, ItemListItem { } } } + + public func item() -> ListViewItem { + return self + } + + public static func ==(lhs: MessagePriceItem, rhs: MessagePriceItem) -> Bool { + + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.isEnabled != rhs.isEnabled { + return false + } + if lhs.minValue != rhs.minValue { + return false + } + if lhs.value != rhs.value { + return false + } + if lhs.price != rhs.price { + return false + } + if (lhs.openSetCustom == nil) != (rhs.openSetCustom == nil) { + return false + } + if (lhs.openPremiumInfo == nil) != (rhs.openPremiumInfo == nil) { + return false + } + + return true + } } private class MessagePriceItemNode: ListViewItemNode { private struct Amount: Equatable { private let sliderSteps: [Int] + private let minRealValue: Int private let maxRealValue: Int let maxSliderValue: Int private let isLogarithmic: Bool @@ -87,9 +123,9 @@ private class MessagePriceItemNode: ListViewItemNode { private(set) var realValue: Int private(set) var sliderValue: Int - private static func makeSliderSteps(maxRealValue: Int, isLogarithmic: Bool) -> [Int] { + private static func makeSliderSteps(minRealValue: Int, maxRealValue: Int, isLogarithmic: Bool) -> [Int] { if isLogarithmic { - var sliderSteps: [Int] = [ 1, 10, 50, 100, 500, 1_000, 2_000, 5_000, 7_500, 10_000 ] + var sliderSteps: [Int] = [ minRealValue, 10, 50, 100, 500, 1_000, 2_000, 5_000, 7_500, 10_000 ] sliderSteps.removeAll(where: { $0 >= maxRealValue }) sliderSteps.append(maxRealValue) return sliderSteps @@ -126,8 +162,9 @@ private class MessagePriceItemNode: ListViewItemNode { } } - init(realValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) { - self.sliderSteps = Amount.makeSliderSteps(maxRealValue: maxRealValue, isLogarithmic: isLogarithmic) + init(realValue: Int, minRealValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) { + self.sliderSteps = Amount.makeSliderSteps(minRealValue: minRealValue, maxRealValue: maxRealValue, isLogarithmic: isLogarithmic) + self.minRealValue = minRealValue self.maxRealValue = maxRealValue self.maxSliderValue = maxSliderValue self.isLogarithmic = isLogarithmic @@ -136,8 +173,9 @@ private class MessagePriceItemNode: ListViewItemNode { self.sliderValue = Amount.remapValueToSlider(realValue: self.realValue, maxSliderValue: self.maxSliderValue, steps: self.sliderSteps) } - init(sliderValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) { - self.sliderSteps = Amount.makeSliderSteps(maxRealValue: maxRealValue, isLogarithmic: isLogarithmic) + init(sliderValue: Int, minRealValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) { + self.sliderSteps = Amount.makeSliderSteps(minRealValue: minRealValue, maxRealValue: maxRealValue, isLogarithmic: isLogarithmic) + self.minRealValue = minRealValue self.maxRealValue = maxRealValue self.maxSliderValue = maxSliderValue self.isLogarithmic = isLogarithmic @@ -147,11 +185,11 @@ private class MessagePriceItemNode: ListViewItemNode { } func withRealValue(_ realValue: Int) -> Amount { - return Amount(realValue: realValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic) + return Amount(realValue: realValue, minRealValue: self.minRealValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic) } func withSliderValue(_ sliderValue: Int) -> Amount { - return Amount(sliderValue: sliderValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic) + return Amount(sliderValue: sliderValue, minRealValue: self.minRealValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic) } } @@ -171,7 +209,7 @@ private class MessagePriceItemNode: ListViewItemNode { private let button: ComponentView - private var amount: Amount = Amount(realValue: 1, maxRealValue: 1000, maxSliderValue: 1000, isLogarithmic: true) + private var amount: Amount = Amount(realValue: 1, minRealValue: 1, maxRealValue: 1000, maxSliderValue: 1000, isLogarithmic: true) private var item: MessagePriceItem? private var layoutParams: ListViewItemLayoutParams? @@ -195,7 +233,9 @@ private class MessagePriceItemNode: ListViewItemNode { self.centerTextButtonBackground = UIImageView() self.centerLeftTextNode = ImmediateTextNode() self.centerLeftTextNode.isUserInteractionEnabled = false + self.centerLeftTextNode.displaysAsynchronously = false self.centerRightTextNode = ImmediateTextNode() + self.centerRightTextNode.displaysAsynchronously = false self.centerRightTextNode.isUserInteractionEnabled = false self.lockIconNode = ASImageNode() @@ -226,7 +266,7 @@ private class MessagePriceItemNode: ListViewItemNode { sliderView.lineSize = 4.0 sliderView.disablesInteractiveTransitionGestureRecognizer = true if let item = self.item, let params = self.layoutParams { - self.amount = Amount(realValue: Int(item.value), maxRealValue: Int(item.maxValue), maxSliderValue: 999, isLogarithmic: true) + self.amount = Amount(realValue: Int(item.value), minRealValue: Int(item.minValue), maxRealValue: Int(item.maxValue), maxSliderValue: 999, isLogarithmic: true) sliderView.minimumValue = 0 sliderView.startValue = 0 @@ -328,7 +368,9 @@ private class MessagePriceItemNode: ListViewItemNode { strongSelf.leftTextNode.attributedText = NSAttributedString(string: "\(item.minValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor) strongSelf.rightTextNode.attributedText = NSAttributedString(string: "\(item.maxValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor) - let centralLeftText = item.strings.Privacy_Messages_Stars(Int32(item.value)) + //TODO:localize + let centralLeftText = item.value == 0 ? "Free" : item.strings.Privacy_Messages_Stars(Int32(item.value)) + strongSelf.centerLeftTextNode.attributedText = NSAttributedString(string: centralLeftText, font: textFont, textColor: item.openSetCustom != nil ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor) strongSelf.centerRightTextNode.attributedText = NSAttributedString(string: item.price, font: smallTextFont, textColor: item.openSetCustom != nil ? item.theme.list.itemAccentColor.withMultipliedAlpha(0.5) : item.theme.list.itemSecondaryTextColor) @@ -336,7 +378,7 @@ private class MessagePriceItemNode: ListViewItemNode { let rightTextSize = strongSelf.rightTextNode.updateLayout(CGSize(width: 100.0, height: 100.0)) let centerLeftTextSize = strongSelf.centerLeftTextNode.updateLayout(CGSize(width: 200.0, height: 100.0)) let centerRightTextSize = strongSelf.centerRightTextNode.updateLayout(CGSize(width: 200.0, height: 100.0)) - let centerSpacing: CGFloat = 6.0 + let centerSpacing: CGFloat = item.price.isEmpty ? 0.0 : 6.0 let sideInset: CGFloat = 18.0 @@ -386,7 +428,7 @@ private class MessagePriceItemNode: ListViewItemNode { } if !sliderView.isTracking { - strongSelf.amount = Amount(realValue: Int(item.value), maxRealValue: Int(item.maxValue), maxSliderValue: 999, isLogarithmic: true) + strongSelf.amount = Amount(realValue: Int(item.value), minRealValue: Int(item.minValue), maxRealValue: Int(item.maxValue), maxSliderValue: 999, isLogarithmic: true) sliderView.value = CGFloat(strongSelf.amount.sliderValue) } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 2a7a6e720b..c8c38b1516 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -2207,7 +2207,7 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL })) //TODO:localize - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .text("Off"), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Post Suggestions", icon: UIImage(bundleImageName: "Chat/Info/GroupDiscussionIcon"), action: { + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .text("Off"), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Post Suggestions", icon: UIImage(bundleImageName: "Chat/Info/PostSuggestionsIcon"), action: { interaction.editingOpenPostSuggestionsSetup() })) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/BUILD b/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/BUILD index eb9501ea4d..4166ff8413 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/BUILD @@ -27,10 +27,11 @@ swift_library( "//submodules/Components/BundleIconComponent", "//submodules/TextFormat", "//submodules/TelegramUI/Components/ListSectionComponent", - "//submodules/TelegramUI/Components/ListItemSliderSelectorComponent", "//submodules/TelegramUI/Components/ListSwitchItemComponent", "//submodules/TelegramUI/Components/ListActionItemComponent", "//submodules/TelegramStringFormatting", + "//submodules/TelegramUI/Components/ListItemComponentAdaptor", + "//submodules/TelegramUI/Components/PeerInfo/MessagePriceItem", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/Sources/PostSuggestionsSettingsScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/Sources/PostSuggestionsSettingsScreen.swift index 7f29cb8160..7da2229daa 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/Sources/PostSuggestionsSettingsScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/Sources/PostSuggestionsSettingsScreen.swift @@ -17,23 +17,27 @@ import ListSectionComponent import BundleIconComponent import LottieComponent import ListSwitchItemComponent -import ListItemSliderSelectorComponent import ListSwitchItemComponent import ListActionItemComponent import Markdown import TelegramStringFormatting +import MessagePriceItem +import ListItemComponentAdaptor final class PostSuggestionsSettingsScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let usdWithdrawRate: Int64 let completion: () -> Void init( context: AccountContext, + usdWithdrawRate: Int64, completion: @escaping () -> Void ) { self.context = context + self.usdWithdrawRate = usdWithdrawRate self.completion = completion } @@ -315,26 +319,50 @@ final class PostSuggestionsSettingsScreenComponent: Component { var contentSectionItems: [AnyComponentWithIdentity] = [] - let sliderValueList = (0 ... 10000).map { i -> String in - return "\(i)" - } - //TODO:localize - let sliderTitle: String - let sliderSecondaryTitle: String? - let usdAmount = Double(self.starCount) * 0.013 - let usdAmountString = formatCurrencyAmount(Int64(usdAmount * 100.0), currency: "USD") - if self.starCount == 0 { - sliderTitle = "Free" - sliderSecondaryTitle = nil - } else if self.starCount == 1 { - sliderTitle = "\(self.starCount) Star" - sliderSecondaryTitle = "~\(usdAmountString)" - } else { - sliderTitle = "\(self.starCount) Stars" - sliderSecondaryTitle = "~\(usdAmountString)" - } + let usdRate = Double(component.usdWithdrawRate) / 1000.0 / 100.0 + let price = self.starCount == 0 ? "" : "≈\(formatTonUsdValue(Int64(self.starCount), divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))" - contentSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemSliderSelectorComponent( + contentSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor( + itemGenerator: MessagePriceItem( + theme: environment.theme, + strings: environment.strings, + isEnabled: true, minValue: 0, maxValue: 10000, + value: Int64(self.starCount), + price: price, + sectionId: 0, + updated: { [weak self] value, _ in + guard let self else { + return + } + + self.starCount = Int(value) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + }, + openSetCustom: { [weak self] in + guard let self, let component = self.component, let environment = self.environment else { + return + } + + let currentAmount: StarsAmount = StarsAmount(value: Int64(self.starCount), nanos: 0) + let starsScreen = component.context.sharedContext.makeStarsWithdrawalScreen(context: component.context, subject: .enterAmount(current: currentAmount, minValue: StarsAmount(value: 0, nanos: 0), fractionAfterCommission: 85, kind: .postSuggestion), completion: { [weak self] amount in + guard let self else { + return + } + + self.starCount = Int(amount) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + }) + environment.controller()?.push(starsScreen) + }, + openPremiumInfo: nil + ), + params: ListViewItemLayoutParams(width: availableSize.width - sideInset * 2.0, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true) + )))) + /*contentSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemSliderSelectorComponent( theme: environment.theme, content: .discrete(ListItemSliderSelectorComponent.Discrete( values: sliderValueList.map { item in @@ -353,7 +381,7 @@ final class PostSuggestionsSettingsScreenComponent: Component { self.state?.updated(transition: .immediate) } )) - )))) + ))))*/ let contentSectionSize = self.contentSection.update( transition: transition, @@ -445,8 +473,11 @@ public final class PostSuggestionsSettingsScreen: ViewControllerComponentContain ) { self.context = context + let configuration = StarsSubscriptionConfiguration.with(appConfiguration: context.currentAppConfiguration.with({ $0 })) + super.init(context: context, component: PostSuggestionsSettingsScreenComponent( context: context, + usdWithdrawRate: configuration.usdWithdrawRate, completion: completion ), navigationBarAppearance: .default, theme: .default, updatedPresentationData: nil) diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift index 3b385b4213..5912a035b5 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift @@ -156,13 +156,18 @@ private final class SheetContent: CombinedComponent { minAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMinAmount, nanos: 0) maxAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMaxAmount, nanos: 0) amountLabel = nil - case .paidMessages: + case let .paidMessages(_, minAmountValue, _, kind): //TODO:localize - titleString = "Price per Message" + switch kind { + case .privacy: + titleString = "Price per Message" + case .postSuggestion: + titleString = "Price for each Suggestion" + } amountTitle = "PRICE IN STARS" amountPlaceholder = "Enter Price" - minAmount = StarsAmount(value: 1, nanos: 0) + minAmount = StarsAmount(value: minAmountValue, nanos: 0) maxAmount = StarsAmount(value: resaleConfiguration.paidMessageMaxAmount, nanos: 0) amountLabel = nil } @@ -289,10 +294,10 @@ private final class SheetContent: CombinedComponent { text: .plain(amountInfoString), maximumNumberOfLines: 0 )) - case .paidMessages: + case let .paidMessages(_, _, fractionAfterCommission, _): let amountInfoString: NSAttributedString if let value = state.amount?.value, value > 0 { - let fullValue: Int64 = Int64(value) * 1_000_000_000 * 80 / 100 + let fullValue: Int64 = Int64(value) * 1_000_000_000 * Int64(fractionAfterCommission) / 100 let amountValue = StarsAmount(value: fullValue / 1_000_000_000, nanos: Int32(fullValue % 1_000_000_000)) amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString("You will receive **\(amountValue) Stars**.", attributes: amountMarkdownAttributes, textAlignment: .natural)) } else { @@ -457,7 +462,7 @@ private final class SheetContent: CombinedComponent { amount = nil case .starGiftResell: amount = nil - case let .paidMessages(initialValue): + case let .paidMessages(initialValue, _, _, _): amount = StarsAmount(value: initialValue, nanos: 0) } @@ -580,7 +585,7 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer { case paidMedia(Int64?) case reaction(Int64?) case starGiftResell(Bool) - case paidMessages(Int64) + case paidMessages(current: Int64, minValue: Int64, fractionAfterCommission: Int, kind: StarsWithdrawalScreenSubject.PaidMessageKind) } private let context: AccountContext diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/PostSuggestions.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/PostSuggestions.imageset/Contents.json new file mode 100644 index 0000000000..e4f0fa4d43 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/PostSuggestions.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "postsuggestions.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/PostSuggestions.imageset/postsuggestions.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Empty Chat/PostSuggestions.imageset/postsuggestions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..84791cdcb91279478bd045d894a9f3ff2e8929fa GIT binary patch literal 4256 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/PostSuggestionsIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Info/PostSuggestionsIcon.imageset/Contents.json new file mode 100644 index 0000000000..a88c7d45f3 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Info/PostSuggestionsIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_discussion.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Info/PostSuggestionsIcon.imageset/ic_discussion.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Info/PostSuggestionsIcon.imageset/ic_discussion.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ab7bf2f7544fce2f9639ea25ec7957096218f4d8 GIT binary patch literal 5373 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 U;s*V(dSW0^D1e(=Sw{u%FBVd%p#T5? literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/SuggestPost.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/SuggestPost.imageset/Contents.json new file mode 100644 index 0000000000..b4dafac96c --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/SuggestPost.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "postsuggestionsbutton.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/SuggestPost.imageset/postsuggestionsbutton.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/SuggestPost.imageset/postsuggestionsbutton.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6adbed6a54f92bcc7954ceb06f622d42a2de84a7 GIT binary patch literal 5732 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 S Void) -> Void)? = nil + actions.append(.action(ContextMenuActionItem(text: "Deleting suggested post will auto-refund your order.", textColor: .primary, textLayout: .multiline, textFont: .custom(font: Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 0.8)), height: nil, verticalOffset: nil), badge: nil, icon: { theme in + return nil + }, iconSource: nil, action: action))) } } diff --git a/submodules/TooltipUI/BUILD b/submodules/TooltipUI/BUILD index 3233d2069b..a747e4fd7c 100644 --- a/submodules/TooltipUI/BUILD +++ b/submodules/TooltipUI/BUILD @@ -26,6 +26,7 @@ swift_library( "//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent", "//submodules/Components/BalancedTextComponent", "//submodules/Components/MultilineTextWithEntitiesComponent", + "//submodules/Components/MultilineTextComponent", "//submodules/ShimmerEffect", ], visibility = [ diff --git a/submodules/TooltipUI/Sources/TooltipScreen.swift b/submodules/TooltipUI/Sources/TooltipScreen.swift index 85d4e81d37..e94c4ddce0 100644 --- a/submodules/TooltipUI/Sources/TooltipScreen.swift +++ b/submodules/TooltipUI/Sources/TooltipScreen.swift @@ -17,6 +17,7 @@ import AvatarStoryIndicatorComponent import AccountContext import Markdown import BalancedTextComponent +import MultilineTextComponent import MultilineTextWithEntitiesComponent import ShimmerEffect @@ -113,6 +114,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode { private let context: AccountContext? private let text: TooltipScreen.Text + private let textBadge: String? private let textAlignment: TooltipScreen.Alignment private let balancedTextLayout: Bool private let constrainWidth: CGFloat? @@ -148,6 +150,8 @@ private final class TooltipScreenNode: ViewControllerTracingNode { private var avatarNode: AvatarNode? private var avatarStoryIndicator: ComponentView? private let textView = ComponentView() + private var textBadgeView: ComponentView? + private var textBadgeBackgroundView: ComponentView? private var closeButtonNode: HighlightableButtonNode? private var actionButtonNode: HighlightableButtonNode? @@ -166,6 +170,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode { account: Account, sharedContext: SharedAccountContext, text: TooltipScreen.Text, + textBadge: String?, textAlignment: TooltipScreen.Alignment, balancedTextLayout: Bool, constrainWidth: CGFloat?, @@ -390,6 +395,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode { self.fontSize = fontSize self.text = text + self.textBadge = textBadge self.textAlignment = textAlignment self.balancedTextLayout = balancedTextLayout self.constrainWidth = constrainWidth @@ -658,18 +664,48 @@ private final class TooltipScreenNode: ViewControllerTracingNode { ) } + let textBadgeSpacing: CGFloat = 9.0 + let textBadgeRightInset: CGFloat = 5.0 + + var textContentSize = textSize + var textBadgeSize: CGSize? + if let textBadge = self.textBadge { + let textBadgeView: ComponentView + if let current = self.textBadgeView { + textBadgeView = current + } else { + textBadgeView = ComponentView() + self.textBadgeView = textBadgeView + } + let textBadgeSizeValue = textBadgeView.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: textBadge, font: Font.semibold(floor(self.fontSize * 0.8)), textColor: textColor)) + )), + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + textBadgeSize = textBadgeSizeValue + textContentSize.width += textBadgeSpacing + textBadgeSizeValue.width + textBadgeRightInset + } else { + if let textBadgeView = self.textBadgeView { + self.textBadgeView = nil + textBadgeView.view?.removeFromSuperview() + } + } + var backgroundFrame: CGRect var backgroundHeight: CGFloat switch self.tooltipStyle { case .default, .gradient: - backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0 + backgroundHeight = max(animationSize.height, textContentSize.height) + contentVerticalInset * 2.0 case .wide: - backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0 + 4.0 + backgroundHeight = max(animationSize.height, textContentSize.height) + contentVerticalInset * 2.0 + 4.0 case let .customBlur(_, inset): - backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0 + inset * 2.0 + backgroundHeight = max(animationSize.height, textContentSize.height) + contentVerticalInset * 2.0 + inset * 2.0 case .light: - backgroundHeight = max(28.0, max(animationSize.height, textSize.height) + 4.0 * 2.0) + backgroundHeight = max(28.0, max(animationSize.height, textContentSize.height) + 4.0 * 2.0) } if self.actionButtonNode != nil { backgroundHeight += 4.0 @@ -678,7 +714,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode { var invertArrow = false switch self.location { case let .point(rect, arrowPosition): - var backgroundWidth = textSize.width + contentInset * 2.0 + animationSize.width + animationSpacing + var backgroundWidth = textContentSize.width + contentInset * 2.0 + animationSize.width + animationSpacing if self.closeButtonNode != nil || self.actionButtonNode != nil { backgroundWidth += buttonInset } @@ -769,7 +805,6 @@ private final class TooltipScreenNode: ViewControllerTracingNode { } let textFrame = CGRect(origin: CGPoint(x: contentInset + animationSize.width + animationSpacing, y: floor((backgroundHeight - textSize.height) / 2.0)), size: textSize) - if let textComponentView = self.textView.view { if textComponentView.superview == nil { textComponentView.layer.anchorPoint = CGPoint() @@ -779,6 +814,49 @@ private final class TooltipScreenNode: ViewControllerTracingNode { transition.updateBounds(layer: textComponentView.layer, bounds: CGRect(origin: CGPoint(), size: textFrame.size)) } + if let textBadgeView = self.textBadgeView, let textBadgeSize { + let textBadgeFrame = CGRect(origin: CGPoint(x: textFrame.maxX + textBadgeSpacing, y: textFrame.minY + 2.0), size: textBadgeSize) + if let textBadgeComponentView = textBadgeView.view { + if textBadgeComponentView.superview == nil { + textBadgeComponentView.layer.anchorPoint = CGPoint() + self.containerNode.view.addSubview(textBadgeComponentView) + } + transition.updatePosition(layer: textBadgeComponentView.layer, position: textBadgeFrame.origin) + transition.updateBounds(layer: textBadgeComponentView.layer, bounds: CGRect(origin: CGPoint(), size: textBadgeFrame.size)) + } + + var textBadgeBackgroundFrame = textBadgeFrame.insetBy(dx: -4.0, dy: -3.0) + textBadgeBackgroundFrame.size.height -= UIScreenPixel + textBadgeBackgroundFrame.size.width -= UIScreenPixel + + let textBadgeBackgroundView: ComponentView + if let current = self.textBadgeBackgroundView { + textBadgeBackgroundView = current + } else { + textBadgeBackgroundView = ComponentView() + self.textBadgeBackgroundView = textBadgeBackgroundView + } + let _ = textBadgeBackgroundView.update( + transition: .immediate, + component: AnyComponent(FilledRoundedRectangleComponent( + color: UIColor(white: 1.0, alpha: 0.1), + cornerRadius: .value(5.0), + smoothCorners: true + )), + environment: {}, + containerSize: textBadgeBackgroundFrame.size + ) + if let textBadgeBackgroundComponentView = textBadgeBackgroundView.view { + if textBadgeBackgroundComponentView.superview == nil, let textBadgeComponentView = textBadgeView.view { + self.containerNode.view.insertSubview(textBadgeBackgroundComponentView, belowSubview: textBadgeComponentView) + } + textBadgeBackgroundComponentView.frame = textBadgeBackgroundFrame + } + } else if let textBadgeBackgroundView = self.textBadgeBackgroundView { + self.textBadgeBackgroundView = nil + textBadgeBackgroundView.view?.removeFromSuperview() + } + if let closeButtonNode = self.closeButtonNode { let closeSize = CGSize(width: 44.0, height: 44.0) transition.updateFrame(node: closeButtonNode, frame: CGRect(origin: CGPoint(x: textFrame.maxX - 6.0, y: floor((backgroundHeight - closeSize.height) / 2.0)), size: closeSize)) @@ -1120,6 +1198,7 @@ public final class TooltipScreen: ViewController { private let account: Account private let sharedContext: SharedAccountContext public let text: TooltipScreen.Text + private let textBadge: String? public let textAlignment: TooltipScreen.Alignment private let balancedTextLayout: Bool private let constrainWidth: CGFloat? @@ -1160,6 +1239,7 @@ public final class TooltipScreen: ViewController { account: Account, sharedContext: SharedAccountContext, text: TooltipScreen.Text, + textBadge: String? = nil, textAlignment: TooltipScreen.Alignment = .natural, balancedTextLayout: Bool = false, constrainWidth: CGFloat? = nil, @@ -1179,6 +1259,7 @@ public final class TooltipScreen: ViewController { self.account = account self.sharedContext = sharedContext self.text = text + self.textBadge = textBadge self.textAlignment = textAlignment self.balancedTextLayout = balancedTextLayout self.constrainWidth = constrainWidth @@ -1250,7 +1331,7 @@ public final class TooltipScreen: ViewController { } override public func loadDisplayNode() { - self.displayNode = TooltipScreenNode(context: self.context, account: self.account, sharedContext: self.sharedContext, text: self.text, textAlignment: self.textAlignment, balancedTextLayout: self.balancedTextLayout, constrainWidth: self.constrainWidth, style: self.style, arrowStyle: self.arrowStyle, icon: self.icon, action: self.action, location: self.location, displayDuration: self.displayDuration, inset: self.inset, cornerRadius: self.cornerRadius, isShimmering: self.isShimmering, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in + self.displayNode = TooltipScreenNode(context: self.context, account: self.account, sharedContext: self.sharedContext, text: self.text, textBadge: self.textBadge, textAlignment: self.textAlignment, balancedTextLayout: self.balancedTextLayout, constrainWidth: self.constrainWidth, style: self.style, arrowStyle: self.arrowStyle, icon: self.icon, action: self.action, location: self.location, displayDuration: self.displayDuration, inset: self.inset, cornerRadius: self.cornerRadius, isShimmering: self.isShimmering, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in guard let strongSelf = self else { return }