diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 8ad1ae6e6e..69735af767 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -896,14 +896,12 @@ public enum JoinAffiliateProgramScreenMode { public final class Active { public let targetPeer: EnginePeer - public let link: String - public let userCount: Int - public let copyLink: () -> Void + public let bot: TelegramConnectedStarRefBotList.Item + public let copyLink: (TelegramConnectedStarRefBotList.Item) -> Void - public init(targetPeer: EnginePeer, link: String, userCount: Int, copyLink: @escaping () -> Void) { + public init(targetPeer: EnginePeer, bot: TelegramConnectedStarRefBotList.Item, copyLink: @escaping (TelegramConnectedStarRefBotList.Item) -> Void) { self.targetPeer = targetPeer - self.link = link - self.userCount = userCount + self.bot = bot self.copyLink = copyLink } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift index ed675dcccd..e6b91b533b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift @@ -632,7 +632,6 @@ func _internal_updateStarRefProgram(account: Account, id: EnginePeer.Id, program } public final class TelegramConnectedStarRefBotList : Equatable { - public final class Item: Equatable { public let peer: EnginePeer public let url: String diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift index 7736f9d3d3..5e23254693 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift @@ -302,6 +302,10 @@ public struct StarsAmount: Equatable, Comparable, Hashable, Codable, CustomStrin } public static func +(lhs: StarsAmount, rhs: StarsAmount) -> StarsAmount { + if rhs.value < 0 || rhs.nanos < 0 { + return lhs - StarsAmount(value: abs(rhs.value), nanos: abs(rhs.nanos)) + } + let totalNanos = Int64(lhs.nanos) + Int64(rhs.nanos) let overflow = totalNanos / 1_000_000_000 let remainingNanos = totalNanos % 1_000_000_000 @@ -529,7 +533,7 @@ private final class StarsContextImpl { } var transactions = state.transactions if addTransaction { - transactions.insert(.init(flags: [.isLocal], id: "\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore, title: nil, description: nil, photo: nil, transactionDate: nil, transactionUrl: nil, paidMessageId: nil, giveawayMessageId: nil, media: [], subscriptionPeriod: nil, starGift: nil, floodskipNumber: nil), at: 0) + transactions.insert(.init(flags: [.isLocal], id: "\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore, title: nil, description: nil, photo: nil, transactionDate: nil, transactionUrl: nil, paidMessageId: nil, giveawayMessageId: nil, media: [], subscriptionPeriod: nil, starGift: nil, floodskipNumber: nil, starrefCommissionPermille: nil, starrefPeerId: nil, starrefAmount: nil), at: 0) } self.updateState(StarsContext.State(flags: [.isPendingBalance], balance: max(StarsAmount(value: 0, nanos: 0), state.balance + balance), subscriptions: state.subscriptions, canLoadMoreSubscriptions: state.canLoadMoreSubscriptions, transactions: transactions, canLoadMoreTransactions: state.canLoadMoreTransactions, isLoading: state.isLoading)) @@ -552,10 +556,6 @@ private extension StarsContext.State.Transaction { init?(apiTransaction: Api.StarsTransaction, peerId: EnginePeer.Id?, transaction: Transaction) { switch apiTransaction { case let .starsTransaction(apiFlags, id, stars, date, transactionPeer, title, description, photo, transactionDate, transactionUrl, _, messageId, extendedMedia, subscriptionPeriod, giveawayPostId, starGift, floodskipNumber, starrefCommissionPermille, starrefPeer, starrefAmount): - let _ = starrefCommissionPermille - let _ = starrefPeer - let _ = starrefAmount - let parsedPeer: StarsContext.State.Transaction.Peer var paidMessageId: MessageId? var giveawayMessageId: MessageId? @@ -611,7 +611,7 @@ private extension StarsContext.State.Transaction { let media = extendedMedia.flatMap({ $0.compactMap { textMediaAndExpirationTimerFromApiMedia($0, PeerId(0)).media } }) ?? [] let _ = subscriptionPeriod - self.init(flags: flags, id: id, count: StarsAmount(apiAmount: stars), date: date, peer: parsedPeer, title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), transactionDate: transactionDate, transactionUrl: transactionUrl, paidMessageId: paidMessageId, giveawayMessageId: giveawayMessageId, media: media, subscriptionPeriod: subscriptionPeriod, starGift: starGift.flatMap { StarGift(apiStarGift: $0) }, floodskipNumber: floodskipNumber) + self.init(flags: flags, id: id, count: StarsAmount(apiAmount: stars), date: date, peer: parsedPeer, title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), transactionDate: transactionDate, transactionUrl: transactionUrl, paidMessageId: paidMessageId, giveawayMessageId: giveawayMessageId, media: media, subscriptionPeriod: subscriptionPeriod, starGift: starGift.flatMap { StarGift(apiStarGift: $0) }, floodskipNumber: floodskipNumber, starrefCommissionPermille: starrefCommissionPermille, starrefPeerId: starrefPeer.flatMap(\.peerId), starrefAmount: starrefAmount.flatMap(StarsAmount.init(apiAmount:))) } } } @@ -686,6 +686,9 @@ public final class StarsContext { public let subscriptionPeriod: Int32? public let starGift: StarGift? public let floodskipNumber: Int32? + public let starrefCommissionPermille: Int32? + public let starrefPeerId: PeerId? + public let starrefAmount: StarsAmount? public init( flags: Flags, @@ -703,7 +706,10 @@ public final class StarsContext { media: [Media], subscriptionPeriod: Int32?, starGift: StarGift?, - floodskipNumber: Int32? + floodskipNumber: Int32?, + starrefCommissionPermille: Int32?, + starrefPeerId: PeerId?, + starrefAmount: StarsAmount? ) { self.flags = flags self.id = id @@ -721,6 +727,9 @@ public final class StarsContext { self.subscriptionPeriod = subscriptionPeriod self.starGift = starGift self.floodskipNumber = floodskipNumber + self.starrefCommissionPermille = starrefCommissionPermille + self.starrefPeerId = starrefPeerId + self.starrefAmount = starrefAmount } public static func == (lhs: Transaction, rhs: Transaction) -> Bool { @@ -772,6 +781,15 @@ public final class StarsContext { if lhs.floodskipNumber != rhs.floodskipNumber { return false } + if lhs.starrefCommissionPermille != rhs.starrefCommissionPermille { + return false + } + if lhs.starrefPeerId != rhs.starrefPeerId { + return false + } + if lhs.starrefAmount != rhs.starrefAmount { + return false + } return true } } diff --git a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift index 3580b86fde..81b047cd54 100644 --- a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift @@ -364,9 +364,8 @@ If you end your affiliate program: programDuration: bot.durationMonths, mode: .active(JoinAffiliateProgramScreenMode.Active( targetPeer: targetPeer, - link: bot.url, - userCount: Int(bot.participants), - copyLink: { [weak self] in + bot: bot, + copyLink: { [weak self] bot in guard let self, let component = self.component else { return } diff --git a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/JoinAffiliateProgramScreen.swift b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/JoinAffiliateProgramScreen.swift index a16b6c5df0..60d49a143d 100644 --- a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/JoinAffiliateProgramScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/JoinAffiliateProgramScreen.swift @@ -119,10 +119,14 @@ private final class JoinAffiliateProgramScreenComponent: Component { private var topOffsetDistance: CGFloat? private var currentTargetPeer: EnginePeer? + private var currentMode: JoinAffiliateProgramScreen.Mode? private var possibleTargetPeers: [EnginePeer] = [] private var possibleTargetPeersDisposable: Disposable? + private var changeTargetPeerDisposable: Disposable? + private var isChangingTargetPeer: Bool = false + private var cachedCloseImage: UIImage? override init(frame: CGRect) { @@ -194,6 +198,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { deinit { self.possibleTargetPeersDisposable?.dispose() + self.changeTargetPeerDisposable?.dispose() } func scrollViewDidScroll(_ scrollView: UIScrollView) { @@ -337,7 +342,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { guard let component = self.component, let environment = self.environment, let controller = environment.controller() else { return } - guard case let .join(join) = component.mode else { + guard let currentTargetPeer = self.currentTargetPeer else { return } @@ -346,7 +351,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }) let peers: [EnginePeer] = self.possibleTargetPeers.isEmpty ? [ - join.initialTargetPeer + currentTargetPeer ] : self.possibleTargetPeers let avatarSize = CGSize(width: 30.0, height: 30.0) @@ -360,14 +365,80 @@ private final class JoinAffiliateProgramScreenComponent: Component { } else { peerLabel = "bot" } - items.append(.action(ContextMenuActionItem(text: peer.displayTitle(strings: environment.strings, displayOrder: presentationData.nameDisplayOrder), textLayout: .secondLineWithValue(peerLabel), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: component.context.account, peer: peer, size: avatarSize)), action: { [weak self] c, _ in + let isSelected = peer.id == self.currentTargetPeer?.id + let accentColor = environment.theme.list.itemAccentColor + let avatarSignal = peerAvatarCompleteImage(account: component.context.account, peer: peer, size: avatarSize) + |> map { image in + let context = DrawingContext(size: avatarSize, scale: 0.0, clear: true) + context?.withContext { c in + UIGraphicsPushContext(c) + defer { + UIGraphicsPopContext() + } + if isSelected { + + } + c.saveGState() + let scaleFactor = (avatarSize.width - 3.0 * 2.0) / avatarSize.width + if isSelected { + c.translateBy(x: avatarSize.width * 0.5, y: avatarSize.height * 0.5) + c.scaleBy(x: scaleFactor, y: scaleFactor) + c.translateBy(x: -avatarSize.width * 0.5, y: -avatarSize.height * 0.5) + } + if let image { + image.draw(in: CGRect(origin: CGPoint(), size: avatarSize)) + } + c.restoreGState() + + if isSelected { + c.setStrokeColor(accentColor.cgColor) + let lineWidth: CGFloat = 1.0 + UIScreenPixel + c.setLineWidth(lineWidth) + c.strokeEllipse(in: CGRect(origin: CGPoint(), size: avatarSize).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5)) + } + } + return context?.generateImage() + } + items.append(.action(ContextMenuActionItem(text: peer.displayTitle(strings: environment.strings, displayOrder: presentationData.nameDisplayOrder), textLayout: .secondLineWithValue(peerLabel), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: avatarSignal), action: { [weak self] c, _ in c?.dismiss(completion: {}) - guard let self else { + guard let self, let currentMode = self.currentMode, let component = self.component else { + return + } + if self.currentTargetPeer?.id == peer.id { return } self.currentTargetPeer = peer + + switch currentMode { + case .join: + self.currentTargetPeer = peer + case let .active(active): + self.isChangingTargetPeer = true + self.changeTargetPeerDisposable?.dispose() + self.changeTargetPeerDisposable = (component.context.engine.peers.connectStarRefBot(id: peer.id, botId: component.sourcePeer.id) + |> deliverOnMainQueue).startStrict(next: { [weak self] result in + guard let self else { + return + } + self.isChangingTargetPeer = false + + self.currentMode = .active(JoinAffiliateProgramScreen.Mode.Active( + targetPeer: peer, + bot: result, + copyLink: active.copyLink + )) + self.state?.updated(transition: .immediate) + }, error: { [weak self] _ in + guard let self else { + return + } + self.isChangingTargetPeer = false + self.state?.updated(transition: .immediate) + }) + } + self.state?.updated(transition: .immediate) }))) } @@ -389,7 +460,11 @@ private final class JoinAffiliateProgramScreenComponent: Component { let sideInset: CGFloat = 16.0 + environment.safeInsets.left + let currentMode = self.currentMode ?? component.mode + if self.component == nil { + self.currentMode = component.mode + var loadPossibleTargetPeers = false switch component.mode { case let .join(join): @@ -461,7 +536,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { let clippingY: CGFloat - if let currentTargetPeer = self.currentTargetPeer, case .join = component.mode { + if let currentTargetPeer = self.currentTargetPeer, case .join = currentMode { contentHeight += 34.0 let sourceAvatarSize = self.sourceAvatar.update( @@ -592,7 +667,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { transition.setFrame(view: linkIconView, frame: linkIconFrame) } - if active.userCount != 0 { + if active.bot.participants != 0 { let linkIconBadgeSize = self.linkIconBadge.update( transition: .immediate, component: AnyComponent(BorderedBadgeComponent( @@ -605,7 +680,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { scaleFactor: 1.0 ))), AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: "\(active.userCount)", font: Font.bold(14.0), textColor: .white)) + text: .plain(NSAttributedString(string: "\(active.bot.participants)", font: Font.bold(14.0), textColor: .white)) ))) ], spacing: 4.0)), insets: UIEdgeInsets(top: 4.0, left: 9.0, bottom: 4.0, right: 8.0), @@ -637,7 +712,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { let titleString: String let subtitleString: String let termsString: String - switch component.mode { + switch currentMode { case .join: titleString = "Affiliate Program" subtitleString = "**\(component.sourcePeer.compactDisplayTitle)** will share **\(commissionTitle)** of the revenue from each user you refer to it for **\(durationTitle)**." @@ -651,12 +726,12 @@ private final class JoinAffiliateProgramScreenComponent: Component { timeString = "for **\(durationTitle)** after they follow your link." } subtitleString = "Share this link with your users to earn a **\(commissionTitle)** commission on their spending in **\(component.sourcePeer.compactDisplayTitle)** \(timeString)." - if active.userCount == 0 { + if active.bot.participants == 0 { termsString = "No one opened \(component.sourcePeer.compactDisplayTitle) through this link yet." - } else if active.userCount == 1 { + } else if active.bot.participants == 1 { termsString = "1 user opened \(component.sourcePeer.compactDisplayTitle) through this link." } else { - termsString = "\(active.userCount) users opened \(component.sourcePeer.compactDisplayTitle) through this link." + termsString = "\(active.bot.participants) users opened \(component.sourcePeer.compactDisplayTitle) through this link." } } let titleSize = self.title.update( @@ -777,8 +852,8 @@ private final class JoinAffiliateProgramScreenComponent: Component { } contentHeight += 12.0 - if case let .active(active) = component.mode { - var cleanLink = active.link + if case let .active(active) = currentMode { + var cleanLink = active.bot.url let removePrefixes: [String] = ["http://", "https://"] for prefix in removePrefixes { if cleanLink.hasPrefix(prefix) { @@ -805,7 +880,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { return } self.environment?.controller()?.dismiss() - active.copyLink() + active.copyLink(active.bot) }, animateAlpha: true, animateScale: false, @@ -826,7 +901,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { } let actionButtonTitle: String - switch component.mode { + switch currentMode { case .join: actionButtonTitle = "Join Program" case .active: @@ -864,7 +939,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { join.completion(currentTargetPeer) } case let .active(active): - active.copyLink() + active.copyLink(active.bot) } } )), @@ -928,7 +1003,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { self.itemLayout = ItemLayout(containerSize: availableSize, containerInset: containerInset, bottomInset: environment.safeInsets.bottom, topInset: topInset) - if case .active = component.mode { + if case .active = currentMode { let toast: ComponentView if let current = self.toast { toast = current diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 9196b3df55..fbdd2ae558 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -8620,16 +8620,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if let result { mode = .active(JoinAffiliateProgramScreenMode.Active( targetPeer: accountPeer, - link: result.url, - userCount: Int(result.participants), - copyLink: { [weak self] in + bot: result, + copyLink: { [weak self] result in guard let self else { return } //TODO:localize UIPasteboard.general.string = result.url let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }) - self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: "Link copied to clipboard", text: "Share this link and earn **\(result.commissionPermille / 10)%** of what people who use it spend in **\(EnginePeer.user(peer).compactDisplayTitle)**!"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: "Link copied to clipboard", text: "Share this link and earn **\(result.commissionPermille / 10)%** of what people who use it spend in **\(result.peer.compactDisplayTitle)**!"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) } )) } else { @@ -8654,15 +8653,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro programDuration: bot.durationMonths, mode: .active(JoinAffiliateProgramScreenMode.Active( targetPeer: targetPeer, - link: bot.url, - userCount: Int(bot.participants), - copyLink: { [weak self] in + bot: bot, + copyLink: { [weak self] result in guard let self else { return } - UIPasteboard.general.string = bot.url + UIPasteboard.general.string = result.url let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }) - self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: "Link copied to clipboard", text: "Share this link and earn **\(bot.commissionPermille / 10)%** of what people who use it spend in **\(bot.peer.compactDisplayTitle)**!"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: "Link copied to clipboard", text: "Share this link and earn **\(result.commissionPermille / 10)%** of what people who use it spend in **\(result.peer.compactDisplayTitle)**!"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) } )) )) diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift index f1db81a16b..521978c68f 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift @@ -33,7 +33,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { let context: AccountContext let subject: StarsTransactionScreen.Subject let cancel: (Bool) -> Void - let openPeer: (EnginePeer) -> Void + let openPeer: (EnginePeer, Bool) -> Void let openMessage: (EngineMessage.Id) -> Void let openMedia: ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void let openAppExamples: () -> Void @@ -44,7 +44,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { context: AccountContext, subject: StarsTransactionScreen.Subject, cancel: @escaping (Bool) -> Void, - openPeer: @escaping (EnginePeer) -> Void, + openPeer: @escaping (EnginePeer, Bool) -> Void, openMessage: @escaping (EngineMessage.Id) -> Void, openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void, openAppExamples: @escaping () -> Void, @@ -95,6 +95,9 @@ private final class StarsTransactionSheetContent: CombinedComponent { if case let .peer(peer) = transaction.peer { peerIds.append(peer.id) } + if let starrefPeerId = transaction.starrefPeerId { + peerIds.append(starrefPeerId) + } case let .receipt(receipt): peerIds.append(receipt.botPaymentId) case let .gift(message): @@ -232,6 +235,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { var giveawayMessageId: MessageId? var isBoost = false var giftAnimation: TelegramMediaFile? + var isRefProgram = false var delayedCloseOnOpenPeer = true switch subject { @@ -399,6 +403,23 @@ private final class StarsTransactionSheetContent: CombinedComponent { } transactionPeer = transaction.peer isGift = true + } else if let starrefCommissionPermille = transaction.starrefCommissionPermille { + //TODO:localize + isRefProgram = true + if transaction.starrefPeerId == nil { + titleText = "\(starrefCommissionPermille / 10)% Commission" + } else { + titleText = transaction.title ?? "Product" + } + descriptionText = "" + count = transaction.count + countOnTop = false + transactionId = transaction.id + date = transaction.date + transactionPeer = transaction.peer + if case let .peer(peer) = transaction.peer { + toPeer = peer + } } else if transaction.flags.contains(.isReaction) { titleText = strings.Stars_Transaction_Reaction_Title descriptionText = "" @@ -722,7 +743,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { ) ) )) - } else if let toPeer { + } else if let toPeer, !isRefProgram { let title: String if isSubscription { if isBotSubscription { @@ -751,7 +772,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { ), action: { if delayedCloseOnOpenPeer { - component.openPeer(toPeer) + component.openPeer(toPeer, false) Queue.mainQueue().after(1.0, { component.cancel(false) }) @@ -845,6 +866,133 @@ private final class StarsTransactionSheetContent: CombinedComponent { ) )) } + + if case let .transaction(transaction, _) = subject { + //TODO:localize + if transaction.starrefCommissionPermille != nil { + if transaction.starrefPeerId == nil { + tableItems.append(.init( + id: "reason", + title: "Reason", + component: AnyComponent( + Button( + content: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "Affiliate Program", font: tableFont, textColor: tableLinkColor)) + )), + action: { + if let toPeer { + component.openPeer(toPeer, true) + Queue.mainQueue().after(1.0, { + component.cancel(false) + }) + } + } + ) + ), + insets: UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, right: 5.0) + )) + } + if let toPeer, transaction.starrefPeerId == nil { + tableItems.append(.init( + id: "miniapp", + title: "Mini App", + component: AnyComponent( + Button( + content: AnyComponent( + PeerCellComponent( + context: component.context, + theme: theme, + peer: toPeer + ) + ), + action: { + if delayedCloseOnOpenPeer { + component.openPeer(toPeer, false) + Queue.mainQueue().after(1.0, { + component.cancel(false) + }) + } else { + if let controller = controller() as? StarsTransactionScreen, let navigationController = controller.navigationController, let chatController = navigationController.viewControllers.first(where: { $0 is ChatController }) as? ChatController { + chatController.playShakeAnimation() + } + component.cancel(true) + } + } + ) + ) + )) + } + } + if let starRefPeerId = transaction.starrefPeerId, let starRefPeer = state.peerMap[starRefPeerId] { + //TODO:localize + tableItems.append(.init( + id: "to", + title: "Affiliate", + component: AnyComponent( + Button( + content: AnyComponent( + PeerCellComponent( + context: component.context, + theme: theme, + peer: starRefPeer + ) + ), + action: { + if delayedCloseOnOpenPeer { + component.openPeer(starRefPeer, false) + Queue.mainQueue().after(1.0, { + component.cancel(false) + }) + } else { + if let controller = controller() as? StarsTransactionScreen, let navigationController = controller.navigationController, let chatController = navigationController.viewControllers.first(where: { $0 is ChatController }) as? ChatController { + chatController.playShakeAnimation() + } + component.cancel(true) + } + } + ) + ) + )) + if let toPeer { + tableItems.append(.init( + id: "referred", + title: "Referred User", + component: AnyComponent( + Button( + content: AnyComponent( + PeerCellComponent( + context: component.context, + theme: theme, + peer: toPeer + ) + ), + action: { + if delayedCloseOnOpenPeer { + component.openPeer(toPeer, true) + Queue.mainQueue().after(1.0, { + component.cancel(false) + }) + } else { + if let controller = controller() as? StarsTransactionScreen, let navigationController = controller.navigationController, let chatController = navigationController.viewControllers.first(where: { $0 is ChatController }) as? ChatController { + chatController.playShakeAnimation() + } + component.cancel(true) + } + } + ) + ) + )) + } + } + if let starrefCommissionPermille = transaction.starrefCommissionPermille, transaction.starrefPeerId != nil { + tableItems.append(.init( + id: "commission", + title: "Commission", + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "\(starrefCommissionPermille / 10)%", font: tableFont, textColor: tableTextColor)) + )), + insets: UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, right: 5.0) + )) + } + } if let transactionId { tableItems.append(.init( @@ -1200,7 +1348,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent { let context: AccountContext let subject: StarsTransactionScreen.Subject - let openPeer: (EnginePeer) -> Void + let openPeer: (EnginePeer, Bool) -> Void let openMessage: (EngineMessage.Id) -> Void let openMedia: ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void let openAppExamples: () -> Void @@ -1210,7 +1358,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent { init( context: AccountContext, subject: StarsTransactionScreen.Subject, - openPeer: @escaping (EnginePeer) -> Void, + openPeer: @escaping (EnginePeer, Bool) -> Void, openMessage: @escaping (EngineMessage.Id) -> Void, openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void, openAppExamples: @escaping () -> Void, @@ -1363,7 +1511,7 @@ public class StarsTransactionScreen: ViewControllerComponentContainer { ) { self.context = context - var openPeerImpl: ((EnginePeer) -> Void)? + var openPeerImpl: ((EnginePeer, Bool) -> Void)? var openMessageImpl: ((EngineMessage.Id) -> Void)? var openMediaImpl: (([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void)? var openAppExamplesImpl: (() -> Void)? @@ -1375,8 +1523,8 @@ public class StarsTransactionScreen: ViewControllerComponentContainer { component: StarsTransactionSheetComponent( context: context, subject: subject, - openPeer: { peerId in - openPeerImpl?(peerId) + openPeer: { peerId, isProfile in + openPeerImpl?(peerId, isProfile) }, openMessage: { messageId in openMessageImpl?(messageId) @@ -1402,7 +1550,7 @@ public class StarsTransactionScreen: ViewControllerComponentContainer { self.navigationPresentation = .flatModal self.automaticallyControlPresentationContextLayout = false - openPeerImpl = { [weak self] peer in + openPeerImpl = { [weak self] peer, isProfile in guard let self, let navigationController = self.navigationController as? NavigationController else { return } @@ -1415,7 +1563,13 @@ public class StarsTransactionScreen: ViewControllerComponentContainer { guard let peer else { return } - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(peer), subject: nil, botStart: nil, updateTextInputState: nil, keepStack: .always, useExisting: true, purposefulAction: nil, scrollToEndIfExists: false, activateMessageSearch: nil, animated: true)) + if isProfile { + if let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + navigationController.pushViewController(controller) + } + } else { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, chatController: nil, context: context, chatLocation: .peer(peer), subject: nil, botStart: nil, updateTextInputState: nil, keepStack: .always, useExisting: true, purposefulAction: nil, scrollToEndIfExists: false, activateMessageSearch: nil, animated: true)) + } }) }