diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 98a944baf5..d4c335f4c6 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -13743,3 +13743,12 @@ Sorry for the inconvenience."; "Media.ChooseFromGallery" = "Choose From Gallery"; "Media.SelectFrame" = "Select Frame"; "Media.SaveCover" = "Save Cover"; + +"Gift.Withdraw.SecurityCheck" = "Security Check"; +"Gift.Withdraw.SecurityRequirements" = "Gift withdrawals are available if:\n\n• 2-Step verification was enabled for your account more than **7 days** ago.\n\n• You have logged in on this device more than **24 hours** ago."; +"Gift.Withdraw.ComeBackLater" = "\n\nPlease come back later."; +"Gift.Withdraw.SetupTwoStepAuth" = "Enable 2-Step Verification"; + +"Gift.Withdraw.EnterPassword.Title" = "Enter Password"; +"Gift.Withdraw.EnterPassword.Text" = "Please enter your Two-Step Verification password to complete this action."; +"Gift.Withdraw.EnterPassword.Done" = "Proceed"; diff --git a/submodules/ContactListUI/Sources/ContactListActionItem.swift b/submodules/ContactListUI/Sources/ContactListActionItem.swift index 0fb3edb885..372ff14006 100644 --- a/submodules/ContactListUI/Sources/ContactListActionItem.swift +++ b/submodules/ContactListUI/Sources/ContactListActionItem.swift @@ -20,9 +20,15 @@ public class ContactListActionItem: ListViewItem, ListViewItemWithHeader { case generic } + public enum Height { + case generic + case tall + } + let presentationData: ItemListPresentationData let title: String let subtitle: String? + let height: Height let icon: ContactListActionItemIcon let style: Style let highlight: ContactListActionItemHighlight @@ -31,12 +37,13 @@ public class ContactListActionItem: ListViewItem, ListViewItemWithHeader { let action: () -> Void public let header: ListViewItemHeader? - public init(presentationData: ItemListPresentationData, title: String, subtitle: String? = nil, icon: ContactListActionItemIcon, style: Style = .accent, highlight: ContactListActionItemHighlight = .cell, clearHighlightAutomatically: Bool = true, accessible: Bool = true, header: ListViewItemHeader?, action: @escaping () -> Void) { + public init(presentationData: ItemListPresentationData, title: String, subtitle: String? = nil, icon: ContactListActionItemIcon, style: Style = .accent, height: Height = .generic, highlight: ContactListActionItemHighlight = .cell, clearHighlightAutomatically: Bool = true, accessible: Bool = true, header: ListViewItemHeader?, action: @escaping () -> Void) { self.presentationData = presentationData self.title = title self.subtitle = subtitle self.icon = icon self.style = style + self.height = height self.highlight = highlight self.header = header self.clearHighlightAutomatically = clearHighlightAutomatically @@ -223,7 +230,9 @@ class ContactListActionItemNode: ListViewItemNode { let contentHeight: CGFloat let verticalInset: CGFloat = subtitleAttributedString != nil ? 6.0 : 12.0 - if case .alpha = item.highlight { + if case .tall = item.height { + contentHeight = 50.0 + } else if case .alpha = item.highlight { contentHeight = 50.0 } else { contentHeight = verticalInset * 2.0 + titleLayout.size.height + subtitleHeightComponent diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index d15cd4ab70..387ec278af 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -145,13 +145,16 @@ private enum ContactListNodeEntry: Comparable, Identifiable { }) case let .option(_, option, header, _, _): let style: ContactListActionItem.Style + let height: ContactListActionItem.Height switch option.style { case .accent: style = .accent + height = .generic case .generic: style = .generic + height = .tall } - return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, subtitle: option.subtitle, icon: option.icon, style: style, clearHighlightAutomatically: option.clearHighlightAutomatically, header: header, action: option.action) + return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, subtitle: option.subtitle, icon: option.icon, style: style, height: height, clearHighlightAutomatically: option.clearHighlightAutomatically, header: header, action: option.action) case let .peer(_, peer, presence, header, selection, _, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, displayCallIcons, hasMoreButton, enabled, storyData, requiresPremiumForMessaging, customSubtitle): var status: ContactsPeerItemStatus let itemPeer: ContactsPeerItemPeer diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 54ed9643f0..665078df1e 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -1381,7 +1381,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) } dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) } dict[-74456004] = { return Api.payments.SavedInfo.parse_savedInfo($0) } - dict[1154859627] = { return Api.payments.SavedStarGifts.parse_savedStarGifts($0) } + dict[-1779201615] = { return Api.payments.SavedStarGifts.parse_savedStarGifts($0) } dict[377215243] = { return Api.payments.StarGiftUpgradePreview.parse_starGiftUpgradePreview($0) } dict[-2069218660] = { return Api.payments.StarGiftWithdrawalUrl.parse_starGiftWithdrawalUrl($0) } dict[-1877571094] = { return Api.payments.StarGifts.parse_starGifts($0) } diff --git a/submodules/TelegramApi/Sources/Api34.swift b/submodules/TelegramApi/Sources/Api34.swift index 420c304cbd..f4240684e0 100644 --- a/submodules/TelegramApi/Sources/Api34.swift +++ b/submodules/TelegramApi/Sources/Api34.swift @@ -1404,16 +1404,17 @@ public extension Api.payments { } public extension Api.payments { enum SavedStarGifts: TypeConstructorDescription { - case savedStarGifts(flags: Int32, count: Int32, gifts: [Api.SavedStarGift], nextOffset: String?, chats: [Api.Chat], users: [Api.User]) + case savedStarGifts(flags: Int32, count: Int32, chatNotificationsEnabled: Api.Bool?, gifts: [Api.SavedStarGift], nextOffset: String?, chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .savedStarGifts(let flags, let count, let gifts, let nextOffset, let chats, let users): + case .savedStarGifts(let flags, let count, let chatNotificationsEnabled, let gifts, let nextOffset, let chats, let users): if boxed { - buffer.appendInt32(1154859627) + buffer.appendInt32(-1779201615) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(count, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {chatNotificationsEnabled!.serialize(buffer, true)} buffer.appendInt32(481674261) buffer.appendInt32(Int32(gifts.count)) for item in gifts { @@ -1436,8 +1437,8 @@ public extension Api.payments { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .savedStarGifts(let flags, let count, let gifts, let nextOffset, let chats, let users): - return ("savedStarGifts", [("flags", flags as Any), ("count", count as Any), ("gifts", gifts as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)]) + case .savedStarGifts(let flags, let count, let chatNotificationsEnabled, let gifts, let nextOffset, let chats, let users): + return ("savedStarGifts", [("flags", flags as Any), ("count", count as Any), ("chatNotificationsEnabled", chatNotificationsEnabled as Any), ("gifts", gifts as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)]) } } @@ -1446,28 +1447,33 @@ public extension Api.payments { _1 = reader.readInt32() var _2: Int32? _2 = reader.readInt32() - var _3: [Api.SavedStarGift]? + var _3: Api.Bool? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Bool + } } + var _4: [Api.SavedStarGift]? if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedStarGift.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedStarGift.self) } - var _4: String? - if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } - var _5: [Api.Chat]? + var _5: String? + if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(reader) } + var _6: [Api.Chat]? if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _6: [Api.User]? + var _7: [Api.User]? if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - let _c5 = _5 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.payments.SavedStarGifts.savedStarGifts(flags: _1!, count: _2!, gifts: _3!, nextOffset: _4, chats: _5!, users: _6!) + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.payments.SavedStarGifts.savedStarGifts(flags: _1!, count: _2!, chatNotificationsEnabled: _3, gifts: _4!, nextOffset: _5, chats: _6!, users: _7!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api38.swift b/submodules/TelegramApi/Sources/Api38.swift index dd23415054..580ab64ff2 100644 --- a/submodules/TelegramApi/Sources/Api38.swift +++ b/submodules/TelegramApi/Sources/Api38.swift @@ -9647,6 +9647,22 @@ public extension Api.functions.payments { }) } } +public extension Api.functions.payments { + static func toggleChatStarGiftNotifications(flags: Int32, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1626009505) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + return (FunctionDescription(name: "payments.toggleChatStarGiftNotifications", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.payments { static func transferStarGift(stargift: Api.InputSavedStarGift, toId: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index 56da98e1c5..2a44c6a012 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -866,14 +866,17 @@ private final class CachedProfileGifts: Codable { enum CodingKeys: String, CodingKey { case gifts case count + case notificationsEnabled } var gifts: [ProfileGiftsContext.State.StarGift] let count: Int32 + let notificationsEnabled: Bool? - init(gifts: [ProfileGiftsContext.State.StarGift], count: Int32) { + init(gifts: [ProfileGiftsContext.State.StarGift], count: Int32, notificationsEnabled: Bool?) { self.gifts = gifts self.count = count + self.notificationsEnabled = notificationsEnabled } init(from decoder: Decoder) throws { @@ -881,6 +884,7 @@ private final class CachedProfileGifts: Codable { self.gifts = try container.decode([ProfileGiftsContext.State.StarGift].self, forKey: .gifts) self.count = try container.decode(Int32.self, forKey: .count) + self.notificationsEnabled = try container.decodeIfPresent(Bool.self, forKey: .notificationsEnabled) } func encode(to encoder: Encoder) throws { @@ -888,6 +892,7 @@ private final class CachedProfileGifts: Codable { try container.encode(self.gifts, forKey: .gifts) try container.encode(self.count, forKey: .count) + try container.encodeIfPresent(self.notificationsEnabled, forKey: .notificationsEnabled) } func render(transaction: Transaction) { @@ -918,6 +923,7 @@ private final class ProfileGiftsContextImpl { private var gifts: [ProfileGiftsContext.State.StarGift] = [] private var count: Int32? private var dataState: ProfileGiftsContext.State.DataState = .ready(canLoadMore: true, nextOffset: nil) + private var notificationsEnabled: Bool? var _state: ProfileGiftsContext.State? private let stateValue = Promise() @@ -958,6 +964,7 @@ private final class ProfileGiftsContextImpl { if case .loading = self.dataState { self.gifts = cachedGifts.gifts self.count = cachedGifts.count + self.notificationsEnabled = cachedGifts.notificationsEnabled self.pushState() } })) @@ -966,12 +973,12 @@ private final class ProfileGiftsContextImpl { self.dataState = .loading self.pushState() - let signal: Signal<([ProfileGiftsContext.State.StarGift], Int32, String?), NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in + let signal: Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(peerId).flatMap(apiInputPeer) } - |> mapToSignal { inputPeer -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?), NoError> in + |> mapToSignal { inputPeer -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> in guard let inputPeer else { - return .single(([], 0, nil)) + return .single(([], 0, nil, nil)) } let flags: Int32 = 0 return network.request(Api.functions.payments.getSavedStarGifts(flags: flags, peer: inputPeer, offset: initialNextOffset ?? "", limit: 32)) @@ -979,25 +986,34 @@ private final class ProfileGiftsContextImpl { |> `catch` { _ -> Signal in return .single(nil) } - |> mapToSignal { result -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?), NoError> in + |> mapToSignal { result -> Signal<([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?), NoError> in guard let result else { - return .single(([], 0, nil)) + return .single(([], 0, nil, nil)) } - return postbox.transaction { transaction -> ([ProfileGiftsContext.State.StarGift], Int32, String?) in + return postbox.transaction { transaction -> ([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?) in switch result { - case let .savedStarGifts(_, count, apiGifts, nextOffset, chats, users): + case let .savedStarGifts(_, count, apiNotificationsEnabled, apiGifts, nextOffset, chats, users): let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) + var notificationsEnabled: Bool? + if let apiNotificationsEnabled { + if case .boolTrue = apiNotificationsEnabled { + notificationsEnabled = true + } else { + notificationsEnabled = false + } + } + let gifts = apiGifts.compactMap { ProfileGiftsContext.State.StarGift(apiSavedStarGift: $0, peerId: peerId, transaction: transaction) } - return (gifts, count, nextOffset) + return (gifts, count, nextOffset, notificationsEnabled) } } } } self.disposable.set((signal - |> deliverOn(self.queue)).start(next: { [weak self] (gifts, count, nextOffset) in + |> deliverOn(self.queue)).start(next: { [weak self] (gifts, count, nextOffset, notificationsEnabled) in guard let strongSelf = self else { return } @@ -1005,7 +1021,7 @@ private final class ProfileGiftsContextImpl { strongSelf.gifts = gifts strongSelf.cacheDisposable.set(strongSelf.account.postbox.transaction { transaction in - if let entry = CodableEntry(CachedProfileGifts(gifts: gifts, count: count)) { + if let entry = CodableEntry(CachedProfileGifts(gifts: gifts, count: count, notificationsEnabled: notificationsEnabled)) { transaction.putItemCacheEntry(id: entryId(peerId: peerId), entry: entry) } }.start()) @@ -1018,6 +1034,7 @@ private final class ProfileGiftsContextImpl { let updatedCount = max(Int32(strongSelf.gifts.count), count) strongSelf.count = updatedCount strongSelf.dataState = .ready(canLoadMore: count != 0 && updatedCount > strongSelf.gifts.count && nextOffset != nil, nextOffset: nextOffset) + strongSelf.notificationsEnabled = notificationsEnabled strongSelf.pushState() })) } @@ -1082,8 +1099,8 @@ private final class ProfileGiftsContextImpl { } private func pushState() { - self._state = ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState) - self.stateValue.set(.single(ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState))) + self._state = ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState, notificationsEnabled: self.notificationsEnabled) + self.stateValue.set(.single(ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState, notificationsEnabled: self.notificationsEnabled))) } } @@ -1246,6 +1263,7 @@ public final class ProfileGiftsContext { public var gifts: [ProfileGiftsContext.State.StarGift] public var count: Int32? public var dataState: ProfileGiftsContext.State.DataState + public var notificationsEnabled: Bool? } private let queue: Queue = .mainQueue() @@ -1591,3 +1609,24 @@ func _internal_requestStarGiftWithdrawalUrl(account: Account, reference: StarGif |> mapError { _ -> RequestStarGiftWithdrawalError in } |> switchToLatest } + +func _internal_toggleStarGiftsNotifications(account: Account, peerId: EnginePeer.Id, enabled: Bool) -> Signal { + return account.postbox.transaction { transaction -> Api.InputPeer? in + return transaction.getPeer(peerId).flatMap(apiInputPeer) + } + |> mapToSignal { inputPeer in + guard let inputPeer else { + return .complete() + } + var flags: Int32 = 0 + if enabled { + flags |= (1 << 0) + } + return account.network.request(Api.functions.payments.toggleChatStarGiftNotifications(flags: flags, peer: inputPeer)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> ignoreValues + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift index e7968cd456..0f6f152b40 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift @@ -140,5 +140,13 @@ public extension TelegramEngine { public func checkStarGiftWithdrawalAvailability(reference: StarGiftReference) -> Signal { return _internal_checkStarGiftWithdrawalAvailability(account: self.account, reference: reference) } + + public func requestStarGiftWithdrawalUrl(reference: StarGiftReference, password: String) -> Signal { + return _internal_requestStarGiftWithdrawalUrl(account: account, reference: reference, password: password) + } + + public func toggleStarGiftsNotifications(peerId: EnginePeer.Id, enabled: Bool) -> Signal { + return _internal_toggleStarGiftsNotifications(account: self.account, peerId: peerId, enabled: enabled) + } } } diff --git a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift index 9ef28bae36..dd32680db0 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift @@ -26,6 +26,7 @@ public final class GiftItemComponent: Component { public enum Color: Equatable { case red case blue + case purple case custom(Int32, Int32) func colors(theme: PresentationTheme) -> [UIColor] { @@ -54,6 +55,11 @@ public final class GiftItemComponent: Component { UIColor(rgb: 0x6fd3ff) ] } + case .purple: + return [ + UIColor(rgb: 0x747bf6), + UIColor(rgb: 0xe367d8) + ] case let .custom(topColor, _): return [ UIColor(rgb: UInt32(bitPattern: topColor)).withMultiplied(hue: 0.97, saturation: 1.45, brightness: 0.89), @@ -489,7 +495,7 @@ public final class GiftItemComponent: Component { ribbonTextView.bounds = CGRect(origin: .zero, size: ribbonTextSize) if self.ribbon.image == nil || themeUpdated || previousComponent?.ribbon?.color != component.ribbon?.color { - var direction: GradientImageDirection = .diagonal + var direction: GradientImageDirection = .mirroredDiagonal if case .custom = ribbon.color { direction = .mirroredDiagonal } diff --git a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift index dd50e73c3b..4202a5080f 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift @@ -781,7 +781,7 @@ final class GiftOptionsScreenComponent: Component { ribbon: product.discount.flatMap { GiftItemComponent.Ribbon( text: "-\($0)%", - color: .red + color: .purple ) }, isLoading: self.inProgressPremiumGift == product.id diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD index 4eec76a393..0155831897 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD @@ -44,6 +44,8 @@ swift_library( "//submodules/TelegramUI/Components/Gifts/GiftItemComponent", "//submodules/MoreButtonNode", "//submodules/TelegramUI/Components/EmojiStatusComponent", + "//submodules/PasswordSetupUI", + "//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift index 4ef0ad0e90..dbf9826806 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift @@ -3,6 +3,7 @@ import UIKit import AsyncDisplayKit import Display import ComponentFlow +import SwiftSignalKit import Postbox import TelegramCore import TelegramPresentationData @@ -12,6 +13,9 @@ import AppBundle import Markdown import GiftItemComponent import StarsAvatarComponent +import PasswordSetupUI +import OwnershipTransferController +import PresentationDataUtils private final class GiftWithdrawAlertContentNode: AlertContentNode { private let context: AccountContext @@ -292,3 +296,104 @@ public func giftWithdrawAlertController(context: AccountContext, gift: StarGift. } return controller } + +public func confirmGiftWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, reference: StarGiftReference, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { + let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } + + var dismissImpl: (() -> Void)? + var proceedImpl: (() -> Void)? + + let disposable = MetaDisposable() + + let contentNode = ChannelOwnershipTransferAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, title: presentationData.strings.Gift_Withdraw_EnterPassword_Title, text: presentationData.strings.Gift_Withdraw_EnterPassword_Text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + dismissImpl?() + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Gift_Withdraw_EnterPassword_Done, action: { + proceedImpl?() + })]) + + contentNode.complete = { + proceedImpl?() + } + + let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) + let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller, weak contentNode] presentationData in + controller?.theme = AlertControllerTheme(presentationData: presentationData) + contentNode?.theme = presentationData.theme + }) + controller.dismissed = { _ in + presentationDataDisposable.dispose() + disposable.dispose() + } + dismissImpl = { [weak controller, weak contentNode] in + contentNode?.dismissInput() + controller?.dismissAnimated() + } + proceedImpl = { [weak contentNode] in + guard let contentNode = contentNode else { + return + } + contentNode.updateIsChecking(true) + + let signal = context.engine.payments.requestStarGiftWithdrawalUrl(reference: reference, password: contentNode.password) + disposable.set((signal |> deliverOnMainQueue).start(next: { url in + dismissImpl?() + completion(url) + }, error: { [weak contentNode] error in + var errorTextAndActions: (String, [TextAlertAction])? + switch error { + case .invalidPassword: + contentNode?.animateError() + case .limitExceeded: + errorTextAndActions = (presentationData.strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + default: + errorTextAndActions = (presentationData.strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + } + contentNode?.updateIsChecking(false) + + if let (text, actions) = errorTextAndActions { + dismissImpl?() + present(textAlertController(context: context, title: nil, text: text, actions: actions), nil) + } + })) + } + + return controller +} + +public func giftWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, reference: StarGiftReference, initialError: RequestStarGiftWithdrawalError, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { + let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } + let theme = AlertControllerTheme(presentationData: presentationData) + + var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.Gift_Withdraw_SecurityCheck, font: Font.semibold(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center) + + var text = presentationData.strings.Gift_Withdraw_SecurityRequirements + let textFontSize = presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0 + + var actions: [TextAlertAction] = [] + switch initialError { + case .requestPassword: + return confirmGiftWithdrawalController(context: context, updatedPresentationData: updatedPresentationData, reference: reference, present: present, completion: completion) + case .twoStepAuthTooFresh, .authSessionTooFresh: + text = text + presentationData.strings.Gift_Withdraw_ComeBackLater + actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] + case .twoStepAuthMissing: + actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.Gift_Withdraw_SetupTwoStepAuth, action: { + let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in + if shouldDismiss { + controller.dismiss() + } + }) + present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})] + default: + title = nil + text = presentationData.strings.Login_UnknownError + actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] + } + + let body = MarkdownAttributeSet(font: Font.regular(textFontSize), textColor: theme.primaryColor) + let bold = MarkdownAttributeSet(font: Font.semibold(textFontSize), textColor: theme.primaryColor) + let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) + + return richTextAlertController(context: context, title: title, text: attributedText, actions: actions) +} diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsRevenueWithdrawalController.swift b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsRevenueWithdrawalController.swift index e56d23f863..4f52ba6b87 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsRevenueWithdrawalController.swift +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsRevenueWithdrawalController.swift @@ -72,7 +72,6 @@ public func confirmStarsRevenueWithdrawalController(context: AccountContext, upd return controller } - public func starsRevenueWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id, amount: Int64, initialError: RequestStarsRevenueWithdrawalError, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } let theme = AlertControllerTheme(presentationData: presentationData) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 8e968b1bb7..ab45b8f91b 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2450,13 +2450,34 @@ public final class SharedAccountContextImpl: SharedAccountContext { } presentExportAlertImpl = { [weak controller] in - guard let controller, case let .starGiftTransfer(_, _, gift, _, canExportDate) = source, let canExportDate else { + guard let controller, case let .starGiftTransfer(_, reference, gift, _, canExportDate) = source, let canExportDate else { return } let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) if currentTime > canExportDate || "".isEmpty { let alertController = giftWithdrawAlertController(context: context, gift: gift, commit: { - + let _ = (context.engine.payments.checkStarGiftWithdrawalAvailability(reference: reference) + |> deliverOnMainQueue).start(error: { [weak controller] error in + switch error { + case .serverProvided: + return + case .requestPassword: + let alertController = confirmGiftWithdrawalController(context: context, reference: reference, present: { [weak controller] c, a in + controller?.present(c, in: .window(.root)) + }, 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: {}) + }) + controller?.present(alertController, in: .window(.root)) + default: + let alertController = giftWithdrawalController(context: context, reference: reference, initialError: error, present: { [weak controller] c, a in + controller?.present(c, in: .window(.root)) + }, completion: { _ in + + }) + controller?.present(alertController, in: .window(.root)) + } + }) }) controller.present(alertController, in: .window(.root)) } else {