diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index eb4ecd6751..30e5a4c8a5 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -12323,6 +12323,7 @@ Sorry for the inconvenience."; "Story.ViewLink" = "Open Link"; +"PeerInfo.Bot.Username" = "Username"; "PeerInfo.Bot.Balance" = "Balance"; "PeerInfo.Bot.Balance.Stars_1" = "%@ Star"; "PeerInfo.Bot.Balance.Stars_any" = "%@ Stars"; @@ -12349,10 +12350,12 @@ Sorry for the inconvenience."; "Stars.Withdraw.AmountPlaceholder" = "Stars Amount"; "Stars.Withdraw.Withdraw" = "Withdraw"; -"Stars.Withdraw.Withdraw.ErrorMinimum" = "You cannot withdraw less than %@"; +"Stars.Withdraw.Withdraw.ErrorMinimum" = "You cannot withdraw less than [%@]()."; "Stars.Withdraw.Withdraw.ErrorMinimum.Stars_1" = "%@ Star"; "Stars.Withdraw.Withdraw.ErrorMinimum.Stars_any" = "%@ Stars"; +"Stars.Withdraw.Withdraw.ErrorTimeout" = "Next withdrawal will be available in **%@**."; + "Stars.PaidContent.Title" = "Paid Content"; "Stars.PaidContent.AmountTitle" = "ENTER UNLOCK COST"; "Stars.PaidContent.AmountPlaceholder" = "Stars to Unlock"; @@ -12369,3 +12372,7 @@ Sorry for the inconvenience."; "MediaEditor.Link.LinkName.Placeholder" = "Enter a Name"; "Story.Editor.TooltipLinkPremium" = "Subscribe to [Telegram Premium]() to add links."; + +"Story.Editor.TooltipLinkLimitValue_1" = "**%@** link"; +"Story.Editor.TooltipLinkLimitValue_any" = "**%@** links"; +"Story.Editor.TooltipReachedLinkLimitText" = "You can't add more than %@ to a story."; diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index 996d598852..43c4f710c8 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -3088,6 +3088,7 @@ public final class DrawingToolsInteraction { var isVideo = false var isAdditional = false var isMessage = false + var isLink = false if let entity = entityView.entity as? DrawingStickerEntity { if case let .dualVideoReference(isAdditionalValue) = entity.content { isVideo = true @@ -3095,6 +3096,8 @@ public final class DrawingToolsInteraction { } else if case .message = entity.content { isMessage = true } + } else if entityView.entity is DrawingLinkEntity { + isLink = true } guard (!isVideo || isAdditional) && (!isMessage || !isTopmost) else { @@ -3140,7 +3143,7 @@ public final class DrawingToolsInteraction { } })) } - if !isVideo && !isMessage { + if !isVideo && !isMessage && !isLink { if let stickerEntity = entityView.entity as? DrawingStickerEntity, case let .file(_, type) = stickerEntity.content, case .reaction = type { } else { diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 69315b7d98..6b31883599 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -518,7 +518,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[340088945] = { return Api.MediaArea.parse_mediaAreaSuggestedReaction($0) } dict[926421125] = { return Api.MediaArea.parse_mediaAreaUrl($0) } dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) } - dict[64088654] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) } + dict[-808853502] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) } dict[-1808510398] = { return Api.Message.parse_message($0) } dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) } dict[721967202] = { return Api.Message.parse_messageService($0) } @@ -872,7 +872,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-425595208] = { return Api.SmsJob.parse_smsJob($0) } dict[-1108478618] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) } dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) } - dict[-407138204] = { return Api.StarsRevenueStatus.parse_starsRevenueStatus($0) } + dict[2033461574] = { return Api.StarsRevenueStatus.parse_starsRevenueStatus($0) } dict[198776256] = { return Api.StarsTopupOption.parse_starsTopupOption($0) } dict[-1442789224] = { return Api.StarsTransaction.parse_starsTransaction($0) } dict[-670195363] = { return Api.StarsTransactionPeer.parse_starsTransactionPeer($0) } diff --git a/submodules/TelegramApi/Sources/Api14.swift b/submodules/TelegramApi/Sources/Api14.swift index 98bc7837e6..da6d3ef627 100644 --- a/submodules/TelegramApi/Sources/Api14.swift +++ b/submodules/TelegramApi/Sources/Api14.swift @@ -300,33 +300,35 @@ public extension Api { } public extension Api { enum MediaAreaCoordinates: TypeConstructorDescription { - case mediaAreaCoordinates(x: Double, y: Double, w: Double, h: Double, rotation: Double) + case mediaAreaCoordinates(flags: Int32, x: Double, y: Double, w: Double, h: Double, rotation: Double, radius: Double?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .mediaAreaCoordinates(let x, let y, let w, let h, let rotation): + case .mediaAreaCoordinates(let flags, let x, let y, let w, let h, let rotation, let radius): if boxed { - buffer.appendInt32(64088654) + buffer.appendInt32(-808853502) } + serializeInt32(flags, buffer: buffer, boxed: false) serializeDouble(x, buffer: buffer, boxed: false) serializeDouble(y, buffer: buffer, boxed: false) serializeDouble(w, buffer: buffer, boxed: false) serializeDouble(h, buffer: buffer, boxed: false) serializeDouble(rotation, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeDouble(radius!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .mediaAreaCoordinates(let x, let y, let w, let h, let rotation): - return ("mediaAreaCoordinates", [("x", x as Any), ("y", y as Any), ("w", w as Any), ("h", h as Any), ("rotation", rotation as Any)]) + case .mediaAreaCoordinates(let flags, let x, let y, let w, let h, let rotation, let radius): + return ("mediaAreaCoordinates", [("flags", flags as Any), ("x", x as Any), ("y", y as Any), ("w", w as Any), ("h", h as Any), ("rotation", rotation as Any), ("radius", radius as Any)]) } } public static func parse_mediaAreaCoordinates(_ reader: BufferReader) -> MediaAreaCoordinates? { - var _1: Double? - _1 = reader.readDouble() + var _1: Int32? + _1 = reader.readInt32() var _2: Double? _2 = reader.readDouble() var _3: Double? @@ -335,13 +337,19 @@ public extension Api { _4 = reader.readDouble() var _5: Double? _5 = reader.readDouble() + var _6: Double? + _6 = reader.readDouble() + var _7: Double? + if Int(_1!) & Int(1 << 0) != 0 {_7 = reader.readDouble() } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.MediaAreaCoordinates.mediaAreaCoordinates(x: _1!, y: _2!, w: _3!, h: _4!, rotation: _5!) + let _c6 = _6 != nil + let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.MediaAreaCoordinates.mediaAreaCoordinates(flags: _1!, x: _2!, y: _3!, w: _4!, h: _5!, rotation: _6!, radius: _7) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api23.swift b/submodules/TelegramApi/Sources/Api23.swift index 0194599f5b..c8be4ca650 100644 --- a/submodules/TelegramApi/Sources/Api23.swift +++ b/submodules/TelegramApi/Sources/Api23.swift @@ -604,26 +604,27 @@ public extension Api { } public extension Api { enum StarsRevenueStatus: TypeConstructorDescription { - case starsRevenueStatus(flags: Int32, currentBalance: Int64, availableBalance: Int64, overallRevenue: Int64) + case starsRevenueStatus(flags: Int32, currentBalance: Int64, availableBalance: Int64, overallRevenue: Int64, nextWithdrawalAt: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .starsRevenueStatus(let flags, let currentBalance, let availableBalance, let overallRevenue): + case .starsRevenueStatus(let flags, let currentBalance, let availableBalance, let overallRevenue, let nextWithdrawalAt): if boxed { - buffer.appendInt32(-407138204) + buffer.appendInt32(2033461574) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(currentBalance, buffer: buffer, boxed: false) serializeInt64(availableBalance, buffer: buffer, boxed: false) serializeInt64(overallRevenue, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(nextWithdrawalAt!, buffer: buffer, boxed: false)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .starsRevenueStatus(let flags, let currentBalance, let availableBalance, let overallRevenue): - return ("starsRevenueStatus", [("flags", flags as Any), ("currentBalance", currentBalance as Any), ("availableBalance", availableBalance as Any), ("overallRevenue", overallRevenue as Any)]) + case .starsRevenueStatus(let flags, let currentBalance, let availableBalance, let overallRevenue, let nextWithdrawalAt): + return ("starsRevenueStatus", [("flags", flags as Any), ("currentBalance", currentBalance as Any), ("availableBalance", availableBalance as Any), ("overallRevenue", overallRevenue as Any), ("nextWithdrawalAt", nextWithdrawalAt as Any)]) } } @@ -636,12 +637,15 @@ public extension Api { _3 = reader.readInt64() var _4: Int64? _4 = reader.readInt64() + var _5: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.StarsRevenueStatus.starsRevenueStatus(flags: _1!, currentBalance: _2!, availableBalance: _3!, overallRevenue: _4!) + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.StarsRevenueStatus.starsRevenueStatus(flags: _1!, currentBalance: _2!, availableBalance: _3!, overallRevenue: _4!, nextWithdrawalAt: _5) } else { return nil diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index c6c1321f44..7e000a64d8 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -473,8 +473,8 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI func mediaAreaFromApiMediaArea(_ mediaArea: Api.MediaArea) -> MediaArea? { func coodinatesFromApiMediaAreaCoordinates(_ coordinates: Api.MediaAreaCoordinates) -> MediaArea.Coordinates { switch coordinates { - case let .mediaAreaCoordinates(x, y, width, height, rotation): - return MediaArea.Coordinates(x: x, y: y, width: width, height: height, rotation: rotation) + case let .mediaAreaCoordinates(_, x, y, width, height, rotation, radius): + return MediaArea.Coordinates(x: x, y: y, width: width, height: height, rotation: rotation, cornerRadius: radius) } } switch mediaArea { @@ -551,7 +551,11 @@ func apiMediaAreasFromMediaAreas(_ mediaAreas: [MediaArea], transaction: Transac var apiMediaAreas: [Api.MediaArea] = [] for area in mediaAreas { let coordinates = area.coordinates - let inputCoordinates = Api.MediaAreaCoordinates.mediaAreaCoordinates(x: coordinates.x, y: coordinates.y, w: coordinates.width, h: coordinates.height, rotation: coordinates.rotation) + var flags: Int32 = 0 + if let _ = coordinates.cornerRadius { + flags |= (1 << 0) + } + let inputCoordinates = Api.MediaAreaCoordinates.mediaAreaCoordinates(flags: flags, x: coordinates.x, y: coordinates.y, w: coordinates.width, h: coordinates.height, rotation: coordinates.rotation, radius: coordinates.cornerRadius) switch area { case let .venue(_, venue): if let queryId = venue.queryId, let resultId = venue.resultId { diff --git a/submodules/TelegramCore/Sources/Statistics/StarsRevenueStatistics.swift b/submodules/TelegramCore/Sources/Statistics/StarsRevenueStatistics.swift index b8af085132..97f7156321 100644 --- a/submodules/TelegramCore/Sources/Statistics/StarsRevenueStatistics.swift +++ b/submodules/TelegramCore/Sources/Statistics/StarsRevenueStatistics.swift @@ -10,6 +10,7 @@ public struct StarsRevenueStats: Equatable { public let availableBalance: Int64 public let overallRevenue: Int64 public let withdrawEnabled: Bool + public let nextWithdrawalTimestamp: Int32? } public let revenueGraph: StatsGraph @@ -58,8 +59,8 @@ extension StarsRevenueStats { extension StarsRevenueStats.Balances { init(apiStarsRevenueStatus: Api.StarsRevenueStatus) { switch apiStarsRevenueStatus { - case let .starsRevenueStatus(flags, currentBalance, availableBalance, overallRevenue): - self.init(currentBalance: currentBalance, availableBalance: availableBalance, overallRevenue: overallRevenue, withdrawEnabled: ((flags & (1 << 0)) != 0)) + case let .starsRevenueStatus(flags, currentBalance, availableBalance, overallRevenue, nextWithdrawalAt): + self.init(currentBalance: currentBalance, availableBalance: availableBalance, overallRevenue: overallRevenue, withdrawEnabled: ((flags & (1 << 0)) != 0), nextWithdrawalTimestamp: nextWithdrawalAt) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MediaArea.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MediaArea.swift index 854b1f65e6..74d9ae7a78 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MediaArea.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MediaArea.swift @@ -16,6 +16,7 @@ public enum MediaArea: Codable, Equatable { case width case height case rotation + case cornerRadius } public var x: Double @@ -23,19 +24,22 @@ public enum MediaArea: Codable, Equatable { public var width: Double public var height: Double public var rotation: Double + public var cornerRadius: Double? public init( x: Double, y: Double, width: Double, height: Double, - rotation: Double + rotation: Double, + cornerRadius: Double? ) { self.x = x self.y = y self.width = width self.height = height self.rotation = rotation + self.cornerRadius = cornerRadius } public init(from decoder: Decoder) throws { @@ -46,6 +50,7 @@ public enum MediaArea: Codable, Equatable { self.width = try container.decode(Double.self, forKey: .width) self.height = try container.decode(Double.self, forKey: .height) self.rotation = try container.decode(Double.self, forKey: .rotation) + self.cornerRadius = try container.decodeIfPresent(Double.self, forKey: .cornerRadius) } public func encode(to encoder: Encoder) throws { @@ -56,6 +61,7 @@ public enum MediaArea: Codable, Equatable { try container.encode(self.width, forKey: .width) try container.encode(self.height, forKey: .height) try container.encode(self.rotation, forKey: .rotation) + try container.encodeIfPresent(self.cornerRadius, forKey: .cornerRadius) } } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift index 81a31995aa..8951b64618 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift @@ -110,7 +110,8 @@ public enum CodableDrawingEntity: Equatable { y: position.y / 1920.0 * 100.0, width: size.width * scale / 1080.0 * 100.0, height: size.height * scale / 1920.0 * 100.0, - rotation: rotation / .pi * 180.0 + rotation: rotation / .pi * 180.0, + cornerRadius: nil ) } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift index 506b9d1612..7f692ff37f 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift @@ -260,7 +260,7 @@ private final class SheetContent: CombinedComponent { placeholderColor: theme.list.itemPlaceholderTextColor, text: state.name, link: false, - placeholderText: strings.MediaEditor_Link_LinkTo_Placeholder, + placeholderText: strings.MediaEditor_Link_LinkName_Placeholder, textUpdated: { [weak state] text in state?.name = text } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 4adad76122..336dedebaa 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -4485,6 +4485,20 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate return } + if existingEntity == nil { + let maxLinkCount = 3 + var currentLinkCount = 0 + self.entitiesView.eachView { entityView in + if entityView.entity is DrawingLinkEntity { + currentLinkCount += 1 + } + } + if currentLinkCount >= maxLinkCount { + controller.presentLinkLimitTooltip() + return + } + } + var link: CreateLinkScreen.Link? if let existingEntity { link = CreateLinkScreen.Link( @@ -5977,6 +5991,29 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.present(controller, in: .current) } + fileprivate func presentLinkLimitTooltip() { + self.hapticFeedback.impact(.light) + + self.dismissAllTooltips() + + let context = self.context + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let limit: Int32 = 3 + + let value = presentationData.strings.Story_Editor_TooltipLinkLimitValue(limit) + let content: UndoOverlayContent = .info( + title: nil, + text: presentationData.strings.Story_Editor_TooltipReachedLinkLimitText(value).string, + timeout: nil, + customUndoText: nil + ) + + let controller = UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: true, position: .top, animateInAsReplacement: false, action: { _ in + return true + }) + self.present(controller, in: .window(.root)) + } + func maybePresentDiscardAlert() { self.hapticFeedback.impact(.light) if !self.isEligibleForDraft() { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 50828dd054..3e7a8a4dbe 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -1729,8 +1729,7 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL let ItemBotInfo = 10 if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { - //TODO:localize - items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text("@\(user.addressName ?? "")"), text: "Username", icon: PresentationResourcesSettings.bot, action: { + items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text("@\(user.addressName ?? "")"), text: presentationData.strings.PeerInfo_Bot_Username, icon: PresentationResourcesSettings.bot, action: { interaction.editingOpenPublicLinkSetup() })) diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsBalanceComponent.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsBalanceComponent.swift index 0fa42c61c0..5c60e5e82b 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsBalanceComponent.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsBalanceComponent.swift @@ -7,7 +7,9 @@ import AccountContext import MultilineTextComponent import TelegramPresentationData import PresentationDataUtils -import SolidRoundedButtonComponent +import ButtonComponent +import BundleIconComponent +import TelegramStringFormatting final class StarsBalanceComponent: Component { let theme: PresentationTheme @@ -17,6 +19,8 @@ final class StarsBalanceComponent: Component { let rate: Double? let actionTitle: String let actionAvailable: Bool + let actionIsEnabled: Bool + let actionCooldownUntilTimestamp: Int32? let buy: () -> Void init( @@ -27,6 +31,8 @@ final class StarsBalanceComponent: Component { rate: Double?, actionTitle: String, actionAvailable: Bool, + actionIsEnabled: Bool, + actionCooldownUntilTimestamp: Int32? = nil, buy: @escaping () -> Void ) { self.theme = theme @@ -36,6 +42,8 @@ final class StarsBalanceComponent: Component { self.rate = rate self.actionTitle = actionTitle self.actionAvailable = actionAvailable + self.actionIsEnabled = actionIsEnabled + self.actionCooldownUntilTimestamp = actionCooldownUntilTimestamp self.buy = buy } @@ -55,6 +63,12 @@ final class StarsBalanceComponent: Component { if lhs.actionAvailable != rhs.actionAvailable { return false } + if lhs.actionIsEnabled != rhs.actionIsEnabled { + return false + } + if lhs.actionCooldownUntilTimestamp != rhs.actionCooldownUntilTimestamp { + return false + } if lhs.count != rhs.count { return false } @@ -71,6 +85,9 @@ final class StarsBalanceComponent: Component { private var button = ComponentView() private var component: StarsBalanceComponent? + private weak var state: EmptyComponentState? + + private var timer: Timer? override init(frame: CGRect) { super.init(frame: frame) @@ -86,6 +103,29 @@ final class StarsBalanceComponent: Component { func update(component: StarsBalanceComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { self.component = component + self.state = state + + var remainingCooldownSeconds: Int32 = 0 + if let cooldownUntilTimestamp = component.actionCooldownUntilTimestamp { + remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) + remainingCooldownSeconds = max(0, remainingCooldownSeconds) + } + + if remainingCooldownSeconds > 0 { + if self.timer == nil { + self.timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self] _ in + guard let self else { + return + } + self.state?.updated(transition: .immediate) + }) + } + } else { + if let timer = self.timer { + self.timer = nil + timer.invalidate() + } + } let sideInset: CGFloat = 16.0 var contentHeight: CGFloat = sideInset @@ -147,19 +187,40 @@ final class StarsBalanceComponent: Component { if component.actionAvailable { contentHeight += 12.0 + let content: AnyComponentWithIdentity + if remainingCooldownSeconds > 0 { + content = AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent( + VStack([ + AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(Text(text: component.actionTitle, font: Font.semibold(17.0), color: component.theme.list.itemCheckColors.foregroundColor))), + AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(HStack([ + AnyComponentWithIdentity(id: 1, component: AnyComponent(BundleIconComponent(name: "Chat List/StatusLockIcon", tintColor: component.theme.list.itemCheckColors.fillColor.mixedWith(component.theme.list.itemCheckColors.foregroundColor, alpha: 0.7)))), + AnyComponentWithIdentity(id: 0, component: AnyComponent(Text(text: stringForRemainingTime(remainingCooldownSeconds), font: Font.with(size: 11.0, weight: .medium, traits: [.monospacedNumbers]), color: component.theme.list.itemCheckColors.fillColor.mixedWith(component.theme.list.itemCheckColors.foregroundColor, alpha: 0.7)))) + ], spacing: 3.0))) + ], spacing: 1.0) + )) + } else { + content = AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(Text(text: component.actionTitle, font: Font.semibold(17.0), color: component.theme.list.itemCheckColors.foregroundColor))) + } + let buttonSize = self.button.update( - transition: .immediate, - component: AnyComponent( - SolidRoundedButtonComponent( - title: component.actionTitle, - theme: SolidRoundedButtonComponent.Theme(theme: component.theme), - height: 50.0, - cornerRadius: 11.0, - action: { [weak self] in - self?.component?.buy() + transition: transition, + component: AnyComponent(ButtonComponent( + background: ButtonComponent.Background( + color: component.theme.list.itemCheckColors.fillColor, + foreground: component.theme.list.itemCheckColors.foregroundColor, + pressedColor: component.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) + ), + content: content, + isEnabled: component.actionIsEnabled, + allowActionWhenDisabled: false, + displaysProgress: false, + action: { [weak self] in + guard let self, let component = self.component else { + return } - ) - ), + component.buy() + } + )), environment: {}, containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0) ) @@ -186,3 +247,16 @@ final class StarsBalanceComponent: Component { return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) } } + +func stringForRemainingTime(_ duration: Int32) -> String { + let hours = duration / 3600 + let minutes = duration / 60 % 60 + let seconds = duration % 60 + let durationString: String + if hours > 0 { + durationString = String(format: "%d:%02d", hours, minutes) + } else { + durationString = String(format: "%02d:%02d", minutes, seconds) + } + return durationString +} diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsOverviewItemComponent.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsOverviewItemComponent.swift index 667fdf49d2..dfe6aeafab 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsOverviewItemComponent.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsOverviewItemComponent.swift @@ -7,7 +7,6 @@ import AccountContext import MultilineTextComponent import TelegramPresentationData import PresentationDataUtils -import SolidRoundedButtonComponent final class StarsOverviewItemComponent: Component { let theme: PresentationTheme diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift index 2e209caedf..64b825a1cb 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift @@ -29,19 +29,22 @@ final class StarsStatisticsScreenComponent: Component { let revenueContext: StarsRevenueStatsContext let openTransaction: (StarsContext.State.Transaction) -> Void let buy: () -> Void + let showTimeoutTooltip: (Int32) -> Void init( context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext, openTransaction: @escaping (StarsContext.State.Transaction) -> Void, - buy: @escaping () -> Void + buy: @escaping () -> Void, + showTimeoutTooltip: @escaping (Int32) -> Void ) { self.context = context self.peerId = peerId self.revenueContext = revenueContext self.openTransaction = openTransaction self.buy = buy + self.showTimeoutTooltip = showTimeoutTooltip } static func ==(lhs: StarsStatisticsScreenComponent, rhs: StarsStatisticsScreenComponent) -> Bool { @@ -459,11 +462,25 @@ final class StarsStatisticsScreenComponent: Component { rate: 0.2, actionTitle: strings.Stars_BotRevenue_Withdraw_Withdraw, actionAvailable: true, + actionIsEnabled: self.starsState?.balances.withdrawEnabled ?? true, + actionCooldownUntilTimestamp: self.starsState?.balances.nextWithdrawalTimestamp, buy: { [weak self] in guard let self, let component = self.component else { return } - component.buy() + var remainingCooldownSeconds: Int32 = 0 + if let cooldownUntilTimestamp = self.starsState?.balances.nextWithdrawalTimestamp { + remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) + remainingCooldownSeconds = max(0, remainingCooldownSeconds) + + if remainingCooldownSeconds > 0 { + component.showTimeoutTooltip(cooldownUntilTimestamp) + } else { + component.buy() + } + } else { + component.buy() + } } ) ))] @@ -597,13 +614,17 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { private let context: AccountContext private let peerId: EnginePeer.Id private let revenueContext: StarsRevenueStatsContext - + + private weak var tooltipScreen: UndoOverlayController? + private var timer: Foundation.Timer? + public init(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) { self.context = context self.peerId = peerId self.revenueContext = revenueContext var withdrawImpl: (() -> Void)? + var showTimeoutTooltipImpl: ((Int32) -> Void)? var openTransactionImpl: ((StarsContext.State.Transaction) -> Void)? super.init(context: context, component: StarsStatisticsScreenComponent( context: context, @@ -614,6 +635,9 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { }, buy: { withdrawImpl?() + }, + showTimeoutTooltip: { timestamp in + showTimeoutTooltipImpl?(timestamp) } ), navigationBarAppearance: .transparent) @@ -654,6 +678,10 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { }, completion: { url in let presentationData = context.sharedContext.currentPresentationData.with { $0 } context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {}) + + Queue.mainQueue().after(2.0) { + revenueContext.reload() + } }) self.present(controller, in: .window(.root)) }) @@ -669,6 +697,59 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { } }) } + + showTimeoutTooltipImpl = { [weak self] cooldownUntilTimestamp in + guard let self, self.tooltipScreen == nil else { + return + } + + let remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) + + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + let content: UndoOverlayContent = .universal( + animation: "anim_clock", + scale: 0.058, + colors: [:], + title: nil, + text: presentationData.strings.Stars_Withdraw_Withdraw_ErrorTimeout(stringForRemainingTime(remainingCooldownSeconds)).string, + customUndoText: nil, + timeout: nil + ) + let controller = UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in + return true + }) + self.tooltipScreen = controller + self.present(controller, in: .window(.root)) + + if remainingCooldownSeconds < 3600 { + if self.timer == nil { + self.timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self] _ in + guard let self else { + return + } + + if let tooltipScreen = self.tooltipScreen { + let remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) + let content: UndoOverlayContent = .universal( + animation: "anim_clock", + scale: 0.058, + colors: [:], + title: nil, + text: presentationData.strings.Stars_Withdraw_Withdraw_ErrorTimeout(stringForRemainingTime(remainingCooldownSeconds)).string, + customUndoText: nil, + timeout: nil + ) + tooltipScreen.content = content + } else { + if let timer = self.timer { + self.timer = nil + timer.invalidate() + } + } + }) + } + } + } } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift index 3b237865db..0b8589f090 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift @@ -524,6 +524,7 @@ final class StarsTransactionsScreenComponent: Component { rate: nil, actionTitle: environment.strings.Stars_Intro_Buy, actionAvailable: !premiumConfiguration.areStarsDisabled, + actionIsEnabled: true, buy: { [weak self] in guard let self, let component = self.component else { return diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsWithdrawScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsWithdrawScreen.swift index 9d3f986914..a333673559 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsWithdrawScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsWithdrawScreen.swift @@ -20,6 +20,7 @@ import AccountContext import PresentationDataUtils import ListSectionComponent import TelegramStringFormatting +import UndoUI private let amountTag = GenericComponentViewTag() @@ -286,8 +287,12 @@ private final class SheetContent: CombinedComponent { displaysProgress: false, action: { [weak state] in if let controller = controller() as? StarsWithdrawScreen, let amount = state?.amount { - controller.completion(amount) - controller.dismissAnimated() + if let minAmount, amount < minAmount { + controller.presentMinAmountTooltip(minAmount) + } else { + controller.completion(amount) + controller.dismissAnimated() + } } } ), @@ -300,8 +305,8 @@ private final class SheetContent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + button.size.height / 2.0)) ) contentSize.height += button.size.height - contentSize.height += 30.0 - + contentSize.height += 15.0 + contentSize.height += max(environment.inputHeight, environment.safeInsets.bottom) return contentSize @@ -469,6 +474,26 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer { } } } + + func presentMinAmountTooltip(_ minAmount: Int64) { + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + let resultController = UndoOverlayController( + presentationData: presentationData, + content: .image( + image: UIImage(bundleImageName: "Premium/Stars/StarLarge")!, + title: nil, + text: presentationData.strings.Stars_Withdraw_Withdraw_ErrorMinimum(presentationData.strings.Stars_Withdraw_Withdraw_ErrorMinimum_Stars(Int32(minAmount))).string, + round: false, + undoText: nil + ), + elevatedLayout: false, + action: { _ in return true}) + self.present(resultController, in: .window(.root)) + + if let view = self.node.hostView.findTaggedView(tag: amountTag) as? AmountFieldComponent.View { + view.animateError() + } + } public func dismissAnimated() { if let view = self.node.hostView.findTaggedView(tag: SheetComponent.View.Tag()) as? SheetComponent.View { @@ -593,14 +618,7 @@ private final class AmountFieldComponent: Component { if let amount, let maxAmount = component.maxValue, amount > maxAmount { textField.text = "\(maxAmount)" - - textField.layer.addShakeAnimation() - let hapticFeedback = HapticFeedback() - hapticFeedback.error() - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0, execute: { - let _ = hapticFeedback - }) - + self.animateError() return false } } @@ -615,6 +633,15 @@ private final class AmountFieldComponent: Component { self.textField.selectAll(nil) } + func animateError() { + self.textField.layer.addShakeAnimation() + let hapticFeedback = HapticFeedback() + hapticFeedback.error() + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0, execute: { + let _ = hapticFeedback + }) + } + func update(component: AmountFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { self.textField.textColor = component.textColor if let value = component.value { diff --git a/submodules/TelegramUI/Resources/Animations/anim_clock.json b/submodules/TelegramUI/Resources/Animations/anim_clock.json new file mode 100644 index 0000000000..66d5b1e933 --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/anim_clock.json @@ -0,0 +1 @@ +{"v":"5.10.1","fr":60,"ip":0,"op":120,"w":512,"h":512,"nm":"e_timer","ddd":0,"assets":[{"id":"comp_0","nm":"Timer 3","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL SCALE","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256,256,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[0.966,0.966,1]},"o":{"x":[0.28,0.28,0.28],"y":[0,0,0]},"t":0,"s":[30,30,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0.394,0.394,0]},"t":9,"s":[95,95,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":41,"s":[105,105,100]},{"t":44,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"CLOCK","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":34,"s":[80,80,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":44,"s":[110,110,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":52,"s":[82,82,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":59,"s":[92,92,100]},{"t":65,"s":[90,90,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":37,"s":[{"i":[[11.211,0],[0,0],[0,0],[0,0],[0,0],[-9.796,7.035],[0,0],[0,0],[0,0]],"o":[[-21,0],[0,0],[-2.305,12.015],[0,0],[7.291,8.293],[15.745,-11.307],[0,0],[0,0],[-0.598,-11.062]],"v":[[-6,-122.848],[-27,-101.848],[-28.463,6.836],[-24.073,27.731],[41.295,111.116],[69.984,113.783],[73.601,84.305],[14.264,9.224],[14.969,-103]],"c":true}]},{"t":46,"s":[{"i":[[11.211,0],[0,0],[0,0],[0,0],[0,0],[-8.163,8.879],[0,0],[0,0],[0,0]],"o":[[-21,0],[0,0],[0.793,12.208],[0,0],[8.824,6.639],[13.119,-14.27],[0,0],[0,0],[-0.598,-11.062]],"v":[[-6,-122.848],[-27,-101.848],[-26.959,15.958],[-17.453,35.076],[61.581,101.988],[90.214,98.77],[87.766,69.173],[14.994,7.518],[14.969,-103]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[-109.352,0],[0,-109.352],[109.352,0],[0,109.352]],"o":[[109.352,0],[0,109.352],[-109.352,0],[0,-109.352]],"v":[[0,-198],[198,0],[0,198],[-198,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Combined-Shape","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":122,"st":3,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Circle","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":130,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-100.751,0],[0,-100.751],[100.751,0],[0,100.751]],"o":[[100.751,0],[0,100.751],[-100.751,0],[0,-100.751]],"v":[[0,-182.427],[182.426,0],[0,182.426],[-182.427,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":3,"s":[64]},{"t":46,"s":[0]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":1,"op":37,"st":3,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Circle Dashes","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":130,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-100.751,0],[0,-100.751],[100.751,0],[0,100.751]],"o":[[100.751,0],[0,100.751],[-100.751,0],[0,-100.751]],"v":[[0,-182.427],[182.426,0],[0,182.426],[-182.427,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":1,"op":37,"st":3,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Arrow 1","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":3,"s":[204]},{"t":46,"s":[0]}],"ix":10},"p":{"a":0,"k":[-5.75,12.531,0],"ix":2,"l":2},"a":{"a":0,"k":[-6.389,13.924,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.126,-15.958],[0,0],[0,0],[-8.163,8.879],[0,0],[2.494,3.074]],"o":[[-0.833,11.803],[0,0],[8.824,6.639],[13.119,-14.27],[0,0],[-13.883,-15.574]],"v":[[-26.959,15.958],[-17.029,36.066],[61.581,101.988],[90.214,98.77],[87.766,69.173],[14.994,7.518]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.681,15.34],"ix":2},"a":{"a":0,"k":[-5.681,15.34],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Combined-Shape","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":1,"op":37,"st":1,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Arrow 2","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":3,"s":[28]},{"t":46,"s":[0]}],"ix":10},"p":{"a":0,"k":[-5.75,12.5,0],"ix":2,"l":2},"a":{"a":0,"k":[-6.389,13.889,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[11.211,0],[0,0],[0,0],[-0.398,-7.127],[-0.792,23.558],[0,0]],"o":[[-21,0],[0,0],[0.086,2.004],[0.924,16.553],[0.302,-8.972],[-0.598,-11.062]],"v":[[-6,-122.848],[-27,-101.848],[-26.959,15.958],[-27.036,15.947],[14.959,7.553],[14.969,-103]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.681,15.34],"ix":2},"a":{"a":0,"k":[-5.681,15.34],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Combined-Shape","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":1,"op":37,"st":1,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Timer 3","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256,256,0],"ix":2,"l":2},"a":{"a":0,"k":[256,256,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":20,"s":[100,100,100]},{"t":30,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"w":512,"h":512,"ip":-90,"op":30,"st":-90,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Timer 3","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256,256,0],"ix":2,"l":2},"a":{"a":0,"k":[256,256,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":512,"h":512,"ip":30,"op":150,"st":30,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 78386b6264..10ceb37411 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -1108,7 +1108,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white) let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white) - let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor) + let link = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: undoTextColor) let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { contents in return ("URL", contents) }), textAlignment: .natural) @@ -1417,32 +1417,41 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { func updateContent(_ content: UndoOverlayContent) { self.content = content + var undoTextColor = self.presentationData.theme.list.itemAccentColor.withMultiplied(hue: 0.933, saturation: 0.61, brightness: 1.0) + switch content { - case let .image(image, title, text, _, _): - self.iconNode?.image = image - if let title = title { - self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white) - } else { - self.titleNode.attributedText = nil - } - self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white) - self.renewWithCurrentContent() - case let .actionSucceeded(title, text, _, destructive): - var undoTextColor = self.presentationData.theme.list.itemAccentColor.withMultiplied(hue: 0.933, saturation: 0.61, brightness: 1.0) - if destructive { - undoTextColor = UIColor(rgb: 0xff7b74) - } - - let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white) - let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white) - let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor) - let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural) - if let title { - self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white) - } - self.textNode.attributedText = attributedText - default: - break + case let .info(title, text, _, _), let .universal(_, _, _, title, text, _, _): + let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white) + let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white) + let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor) + let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural) + if let title { + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white) + } + self.textNode.attributedText = attributedText + case let .image(image, title, text, _, _): + self.iconNode?.image = image + if let title = title { + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white) + } else { + self.titleNode.attributedText = nil + } + self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white) + self.renewWithCurrentContent() + case let .actionSucceeded(title, text, _, destructive): + if destructive { + undoTextColor = UIColor(rgb: 0xff7b74) + } + let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white) + let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white) + let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor) + let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural) + if let title { + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white) + } + self.textNode.attributedText = attributedText + default: + break } if let validLayout = self.validLayout {