Stars ref

This commit is contained in:
Isaac 2024-11-29 18:04:15 +04:00
parent c49b242cd5
commit c229c3c94f
11 changed files with 187 additions and 90 deletions

View File

@ -1575,7 +1575,8 @@ private func monetizationEntries(
premiumConfiguration: PremiumConfiguration, premiumConfiguration: PremiumConfiguration,
monetizationConfiguration: MonetizationConfiguration, monetizationConfiguration: MonetizationConfiguration,
canViewRevenue: Bool, canViewRevenue: Bool,
canViewStarsRevenue: Bool canViewStarsRevenue: Bool,
canJoinRefPrograms: Bool
) -> [StatsEntry] { ) -> [StatsEntry] {
var entries: [StatsEntry] = [] var entries: [StatsEntry] = []
@ -1700,8 +1701,10 @@ private func monetizationEntries(
if displayStarsTransactions { if displayStarsTransactions {
if !addedTransactionsTabs { if !addedTransactionsTabs {
//TODO:localize if canJoinRefPrograms {
entries.append(.earnStarsInfo) //TODO:localize
entries.append(.earnStarsInfo)
}
entries.append(.adsTransactionsTitle(presentationData.theme, presentationData.strings.Monetization_StarsTransactions.uppercased())) entries.append(.adsTransactionsTitle(presentationData.theme, presentationData.strings.Monetization_StarsTransactions.uppercased()))
} }
@ -1767,7 +1770,8 @@ private func channelStatsControllerEntries(
premiumConfiguration: PremiumConfiguration, premiumConfiguration: PremiumConfiguration,
monetizationConfiguration: MonetizationConfiguration, monetizationConfiguration: MonetizationConfiguration,
canViewRevenue: Bool, canViewRevenue: Bool,
canViewStarsRevenue: Bool canViewStarsRevenue: Bool,
canJoinRefPrograms: Bool
) -> [StatsEntry] { ) -> [StatsEntry] {
switch state.section { switch state.section {
case .stats: case .stats:
@ -1809,7 +1813,8 @@ private func channelStatsControllerEntries(
premiumConfiguration: premiumConfiguration, premiumConfiguration: premiumConfiguration,
monetizationConfiguration: monetizationConfiguration, monetizationConfiguration: monetizationConfiguration,
canViewRevenue: canViewRevenue, canViewRevenue: canViewRevenue,
canViewStarsRevenue: canViewStarsRevenue canViewStarsRevenue: canViewStarsRevenue,
canJoinRefPrograms: canJoinRefPrograms
) )
} }
} }
@ -2157,6 +2162,15 @@ public func channelStatsController(
let (canViewStats, adsRestricted, _, canViewStarsRevenue) = peerData let (canViewStats, adsRestricted, _, canViewStarsRevenue) = peerData
var canViewRevenue = peerData.2 var canViewRevenue = peerData.2
var canJoinRefPrograms = false
if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_connect_allowed"] {
if let value = value as? Double {
canJoinRefPrograms = value != 0.0
} else if let value = value as? Bool {
canJoinRefPrograms = value
}
}
let _ = canViewStatsValue.swap(canViewStats) let _ = canViewStatsValue.swap(canViewStats)
var isGroup = false var isGroup = false
@ -2262,7 +2276,7 @@ public func channelStatsController(
} }
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: title, leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: title, leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelStatsControllerEntries(presentationData: presentationData, state: state, peer: peer, data: data, messages: messages, stories: stories, interactions: interactions, boostData: boostData, boostersState: boostersState, giftsState: giftsState, giveawayAvailable: premiumConfiguration.giveawayGiftsPurchaseAvailable, isGroup: isGroup, boostsOnly: boostsOnly, revenueState: revenueState?.stats, revenueTransactions: revenueTransactions, starsState: starsState?.stats, starsTransactions: starsTransactions, adsRestricted: adsRestricted, premiumConfiguration: premiumConfiguration, monetizationConfiguration: monetizationConfiguration, canViewRevenue: canViewRevenue, canViewStarsRevenue: canViewStarsRevenue), style: .blocks, emptyStateItem: emptyStateItem, headerItem: headerItem, crossfadeState: previous == nil, animateChanges: false) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelStatsControllerEntries(presentationData: presentationData, state: state, peer: peer, data: data, messages: messages, stories: stories, interactions: interactions, boostData: boostData, boostersState: boostersState, giftsState: giftsState, giveawayAvailable: premiumConfiguration.giveawayGiftsPurchaseAvailable, isGroup: isGroup, boostsOnly: boostsOnly, revenueState: revenueState?.stats, revenueTransactions: revenueTransactions, starsState: starsState?.stats, starsTransactions: starsTransactions, adsRestricted: adsRestricted, premiumConfiguration: premiumConfiguration, monetizationConfiguration: monetizationConfiguration, canViewRevenue: canViewRevenue, canViewStarsRevenue: canViewStarsRevenue, canJoinRefPrograms: canJoinRefPrograms), style: .blocks, emptyStateItem: emptyStateItem, headerItem: headerItem, crossfadeState: previous == nil, animateChanges: false)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} }

View File

@ -180,7 +180,7 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingMessageIconImage : graphics.chatBubbleActionButtonOutgoingMessageIconImage iconImage = incoming ? graphics.chatBubbleActionButtonIncomingMessageIconImage : graphics.chatBubbleActionButtonOutgoingMessageIconImage
case let .url(value): case let .url(value):
var isApp = false var isApp = false
if isTelegramMeLink(value), let internalUrl = parseFullInternalUrl(sharedContext: context.sharedContext, url: value) { if isTelegramMeLink(value), let internalUrl = parseFullInternalUrl(sharedContext: context.sharedContext, context: context, url: value) {
if case .peer(_, .appStart) = internalUrl { if case .peer(_, .appStart) = internalUrl {
isApp = true isApp = true
} else if case .peer(_, .attachBotStart) = internalUrl { } else if case .peer(_, .attachBotStart) = internalUrl {

View File

@ -65,6 +65,12 @@ final class AffiliateProgramSetupScreenComponent: Component {
return true return true
} }
private class ScrollView: UIScrollView {
override func touchesShouldCancel(in view: UIView) -> Bool {
return true
}
}
final class View: UIView, UIScrollViewDelegate { final class View: UIView, UIScrollViewDelegate {
private let scrollView: UIScrollView private let scrollView: UIScrollView
@ -119,7 +125,7 @@ final class AffiliateProgramSetupScreenComponent: Component {
private var isSuggestedSortModeUpdating: Bool = false private var isSuggestedSortModeUpdating: Bool = false
override init(frame: CGRect) { override init(frame: CGRect) {
self.scrollView = UIScrollView() self.scrollView = ScrollView()
self.scrollView.showsVerticalScrollIndicator = true self.scrollView.showsVerticalScrollIndicator = true
self.scrollView.showsHorizontalScrollIndicator = false self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.scrollsToTop = false self.scrollView.scrollsToTop = false
@ -456,6 +462,20 @@ If you end your affiliate program:
controller.presentInGlobalOverlay(contextController) controller.presentInGlobalOverlay(contextController)
} }
private func openExistingAffiliatePrograms() {
guard let component = self.component else {
return
}
let _ = (component.context.sharedContext.makeAffiliateProgramSetupScreenInitialData(context: component.context, peerId: component.initialContent.peerId, mode: .connectedPrograms)
|> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in
guard let self, let component = self.component else {
return
}
let setupScreen = component.context.sharedContext.makeAffiliateProgramSetupScreen(context: component.context, initialData: initialData)
self.environment?.controller()?.push(setupScreen)
})
}
func update(component: AffiliateProgramSetupScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize { func update(component: AffiliateProgramSetupScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
self.isUpdating = true self.isUpdating = true
defer { defer {
@ -991,8 +1011,7 @@ If you end your affiliate program:
guard let self else { guard let self else {
return return
} }
self.openExistingAffiliatePrograms()
let _ = self
} }
))) )))
], ],

View File

@ -92,7 +92,7 @@ private final class JoinAffiliateProgramScreenComponent: Component {
private let linkIconBackground = ComponentView<Empty>() private let linkIconBackground = ComponentView<Empty>()
private let linkIcon = ComponentView<Empty>() private let linkIcon = ComponentView<Empty>()
private let linkIconBadge = ComponentView<Empty>() private var linkIconBadge: ComponentView<Empty>?
private let title = ComponentView<Empty>() private let title = ComponentView<Empty>()
private let subtitle = ComponentView<Empty>() private let subtitle = ComponentView<Empty>()
@ -668,7 +668,17 @@ private final class JoinAffiliateProgramScreenComponent: Component {
} }
if active.bot.participants != 0 { if active.bot.participants != 0 {
let linkIconBadgeSize = self.linkIconBadge.update( let linkIconBadge: ComponentView<Empty>
var linkIconBadgeTransition = transition
if let current = self.linkIconBadge {
linkIconBadge = current
} else {
linkIconBadgeTransition = linkIconBadgeTransition.withAnimation(.none)
linkIconBadge = ComponentView()
self.linkIconBadge = linkIconBadge
}
let linkIconBadgeSize = linkIconBadge.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(BorderedBadgeComponent( component: AnyComponent(BorderedBadgeComponent(
backgroundColor: UIColor(rgb: 0x34C759), backgroundColor: UIColor(rgb: 0x34C759),
@ -690,12 +700,15 @@ private final class JoinAffiliateProgramScreenComponent: Component {
containerSize: CGSize(width: 100.0, height: 100.0) containerSize: CGSize(width: 100.0, height: 100.0)
) )
let linkIconBadgeFrame = CGRect(origin: CGPoint(x: linkIconBackgroundFrame.minX + floor((linkIconBackgroundFrame.width - linkIconBadgeSize.width) * 0.5), y: linkIconBackgroundFrame.maxY - floor(linkIconBadgeSize.height * 0.5)), size: linkIconBadgeSize) let linkIconBadgeFrame = CGRect(origin: CGPoint(x: linkIconBackgroundFrame.minX + floor((linkIconBackgroundFrame.width - linkIconBadgeSize.width) * 0.5), y: linkIconBackgroundFrame.maxY - floor(linkIconBadgeSize.height * 0.5)), size: linkIconBadgeSize)
if let linkIconBadgeView = self.linkIconBadge.view { if let linkIconBadgeView = linkIconBadge.view {
if linkIconBadgeView.superview == nil { if linkIconBadgeView.superview == nil {
self.scrollContentView.addSubview(linkIconBadgeView) self.scrollContentView.addSubview(linkIconBadgeView)
} }
transition.setFrame(view: linkIconBadgeView, frame: linkIconBadgeFrame) linkIconBadgeTransition.setFrame(view: linkIconBadgeView, frame: linkIconBadgeFrame)
} }
} else if let linkIconBadge = self.linkIconBadge {
self.linkIconBadge = nil
linkIconBadge.view?.removeFromSuperview()
} }
contentHeight += linkIconBackgroundSize.height + 21.0 contentHeight += linkIconBackgroundSize.height + 21.0
@ -895,6 +908,7 @@ private final class JoinAffiliateProgramScreenComponent: Component {
self.scrollContentView.addSubview(linkTextView) self.scrollContentView.addSubview(linkTextView)
} }
transition.setFrame(view: linkTextView, frame: linkTextFrame) transition.setFrame(view: linkTextView, frame: linkTextFrame)
transition.setAlpha(view: linkTextView, alpha: self.isChangingTargetPeer ? 0.6 : 1.0)
} }
contentHeight += linkTextSize.height contentHeight += linkTextSize.height
contentHeight += 24.0 contentHeight += 24.0

View File

@ -1430,17 +1430,28 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) {
} else { } else {
if let starRefProgram = cachedData.starRefProgram, starRefProgram.endDate == nil { if let starRefProgram = cachedData.starRefProgram, starRefProgram.endDate == nil {
if items[.botAffiliateProgram] == nil { var canJoinRefProgram = false
items[.botAffiliateProgram] = [] if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_connect_allowed"] {
if let value = value as? Double {
canJoinRefProgram = value != 0.0
} else if let value = value as? Bool {
canJoinRefProgram = value
}
}
if canJoinRefProgram {
if items[.botAffiliateProgram] == nil {
items[.botAffiliateProgram] = []
}
//TODO:localize
let programTitleValue: String
programTitleValue = "\(starRefProgram.commissionPermille / 10)%"
//TODO:localize
items[.botAffiliateProgram]!.append(PeerInfoScreenDisclosureItem(id: 0, label: .labelBadge(programTitleValue), additionalBadgeLabel: nil, text: "Affiliate Program", icon: PresentationResourcesSettings.affiliateProgram, action: {
interaction.editingOpenAffiliateProgram()
}))
items[.botAffiliateProgram]!.append(PeerInfoScreenCommentItem(id: 1, text: "Share a link to \(EnginePeer.user(user).compactDisplayTitle) with your friends and and earn \(starRefProgram.commissionPermille / 10)% of their spending there."))
} }
//TODO:localize
let programTitleValue: String
programTitleValue = "\(starRefProgram.commissionPermille / 10)%"
//TODO:localize
items[.botAffiliateProgram]!.append(PeerInfoScreenDisclosureItem(id: 0, label: .labelBadge(programTitleValue), additionalBadgeLabel: nil, text: "Affiliate Program", icon: PresentationResourcesSettings.affiliateProgram, action: {
interaction.editingOpenAffiliateProgram()
}))
items[.botAffiliateProgram]!.append(PeerInfoScreenCommentItem(id: 1, text: "Share a link to \(EnginePeer.user(user).compactDisplayTitle) with your friends and and earn \(starRefProgram.commissionPermille / 10)% of their spending there."))
} }
} }
} }
@ -1938,16 +1949,28 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text("@\(user.addressName ?? "")"), text: presentationData.strings.PeerInfo_Bot_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() interaction.editingOpenPublicLinkSetup()
})) }))
//TODO:localize
let programTitleValue: PeerInfoScreenDisclosureItem.Label var canSetupRefProgram = false
if let cachedData = data.cachedData as? CachedUserData, let starRefProgram = cachedData.starRefProgram, starRefProgram.endDate == nil { if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_program_allowed"] {
programTitleValue = .labelBadge("\(starRefProgram.commissionPermille / 10)%") if let value = value as? Double {
} else { canSetupRefProgram = value != 0.0
programTitleValue = .text("Off") } else if let value = value as? Bool {
canSetupRefProgram = value
}
}
if canSetupRefProgram {
//TODO:localize
let programTitleValue: PeerInfoScreenDisclosureItem.Label
if let cachedData = data.cachedData as? CachedUserData, let starRefProgram = cachedData.starRefProgram, starRefProgram.endDate == nil {
programTitleValue = .labelBadge("\(starRefProgram.commissionPermille / 10)%")
} else {
programTitleValue = .text("Off")
}
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliateProgram, label: programTitleValue, additionalBadgeLabel: presentationData.strings.Settings_New, text: "Affiliate Program", icon: PresentationResourcesSettings.affiliateProgram, action: {
interaction.editingOpenAffiliateProgram()
}))
} }
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliateProgram, label: programTitleValue, additionalBadgeLabel: presentationData.strings.Settings_New, text: "Affiliate Program", icon: PresentationResourcesSettings.affiliateProgram, action: {
interaction.editingOpenAffiliateProgram()
}))
items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemIntro, text: presentationData.strings.PeerInfo_Bot_EditIntro, icon: UIImage(bundleImageName: "Peer Info/BotIntro"), action: { items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemIntro, text: presentationData.strings.PeerInfo_Bot_EditIntro, icon: UIImage(bundleImageName: "Peer Info/BotIntro"), action: {
interaction.openPeerMention("botfather", .withBotStartPayload(ChatControllerInitialBotStart(payload: "\(user.addressName ?? "")-intro", behavior: .interactive))) interaction.openPeerMention("botfather", .withBotStartPayload(ChatControllerInitialBotStart(payload: "\(user.addressName ?? "")-intro", behavior: .interactive)))
@ -2199,9 +2222,20 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
} }
if channel.hasPermission(.changeInfo) { if channel.hasPermission(.changeInfo) {
items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliatePrograms, label: .text(""), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Affiliate Programs", icon: PresentationResourcesSettings.affiliateProgram, action: { var canJoinRefProgram = false
interaction.editingOpenAffiliateProgram() if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_connect_allowed"] {
})) if let value = value as? Double {
canJoinRefProgram = value != 0.0
} else if let value = value as? Bool {
canJoinRefProgram = value
}
}
if canJoinRefProgram {
items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliatePrograms, label: .text(""), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Affiliate Programs", icon: PresentationResourcesSettings.affiliateProgram, action: {
interaction.editingOpenAffiliateProgram()
}))
}
} }
if isCreator { //if let cachedData = data.cachedData as? CachedChannelData, cachedData.flags.contains(.canDeleteHistory) { if isCreator { //if let cachedData = data.cachedData as? CachedChannelData, cachedData.flags.contains(.canDeleteHistory) {
@ -8596,7 +8630,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
if let peer = self.data?.peer as? TelegramUser, let botInfo = peer.botInfo { if let peer = self.data?.peer as? TelegramUser, let botInfo = peer.botInfo {
if botInfo.flags.contains(.canEdit) { if botInfo.flags.contains(.canEdit) {
let _ = (self.context.sharedContext.makeAffiliateProgramSetupScreenInitialData(context: self.context, peerId: peer.id, mode: .editProgram) let _ = (self.context.sharedContext.makeAffiliateProgramSetupScreenInitialData(context: self.context, peerId: peer.id, mode: .editProgram)
|> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in |> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in
guard let self else { guard let self else {
return return
} }

View File

@ -662,46 +662,57 @@ final class StarsTransactionsScreenComponent: Component {
contentHeight += balanceSize.height contentHeight += balanceSize.height
contentHeight += 34.0 contentHeight += 34.0
let earnStarsSectionSize = self.earnStarsSection.update( var canJoinRefProgram = false
transition: .immediate, if let data = component.context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_connect_allowed"] {
component: AnyComponent(ListSectionComponent( if let value = value as? Double {
theme: environment.theme, canJoinRefProgram = value != 0.0
header: nil, } else if let value = value as? Bool {
footer: nil, canJoinRefProgram = value
items: [ }
//TODO:localize }
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor(
itemGenerator: ItemListDisclosureItem(presentationData: ItemListPresentationData(presentationData), icon: PresentationResourcesSettings.earnStars, title: "Earn Stars", titleBadge: presentationData.strings.Settings_New, label: "Distribute links to mini apps and earn a share of their revenue in Stars.", labelStyle: .multilineDetailText, sectionId: 0, style: .blocks, action: { if canJoinRefProgram {
}), let earnStarsSectionSize = self.earnStarsSection.update(
params: ListViewItemLayoutParams(width: availableSize.width, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true), transition: .immediate,
action: { [weak self] in component: AnyComponent(ListSectionComponent(
guard let self, let component = self.component else { theme: environment.theme,
return header: nil,
} footer: nil,
let _ = (component.context.sharedContext.makeAffiliateProgramSetupScreenInitialData(context: component.context, peerId: component.context.account.peerId, mode: .connectedPrograms) items: [
|> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in //TODO:localize
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor(
itemGenerator: ItemListDisclosureItem(presentationData: ItemListPresentationData(presentationData), icon: PresentationResourcesSettings.earnStars, title: "Earn Stars", titleBadge: presentationData.strings.Settings_New, label: "Distribute links to mini apps and earn a share of their revenue in Stars.", labelStyle: .multilineDetailText, sectionId: 0, style: .blocks, action: {
}),
params: ListViewItemLayoutParams(width: availableSize.width, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true),
action: { [weak self] in
guard let self, let component = self.component else { guard let self, let component = self.component else {
return return
} }
let setupScreen = component.context.sharedContext.makeAffiliateProgramSetupScreen(context: component.context, initialData: initialData) let _ = (component.context.sharedContext.makeAffiliateProgramSetupScreenInitialData(context: component.context, peerId: component.context.account.peerId, mode: .connectedPrograms)
self.controller?()?.push(setupScreen) |> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in
}) guard let self, let component = self.component else {
} return
))) }
] let setupScreen = component.context.sharedContext.makeAffiliateProgramSetupScreen(context: component.context, initialData: initialData)
)), self.controller?()?.push(setupScreen)
environment: {}, })
containerSize: CGSize(width: availableSize.width - sideInsets, height: availableSize.height) }
) )))
let earnStarsSectionFrame = CGRect(origin: CGPoint(x: sideInsets * 0.5, y: contentHeight), size: earnStarsSectionSize) ]
if let earnStarsSectionView = self.earnStarsSection.view { )),
if earnStarsSectionView.superview == nil { environment: {},
self.scrollView.addSubview(earnStarsSectionView) containerSize: CGSize(width: availableSize.width - sideInsets, height: availableSize.height)
)
let earnStarsSectionFrame = CGRect(origin: CGPoint(x: sideInsets * 0.5, y: contentHeight), size: earnStarsSectionSize)
if let earnStarsSectionView = self.earnStarsSection.view {
if earnStarsSectionView.superview == nil {
self.scrollView.addSubview(earnStarsSectionView)
}
starTransition.setFrame(view: earnStarsSectionView, frame: earnStarsSectionFrame)
} }
starTransition.setFrame(view: earnStarsSectionView, frame: earnStarsSectionFrame) contentHeight += earnStarsSectionSize.height
contentHeight += 44.0
} }
contentHeight += earnStarsSectionSize.height
contentHeight += 44.0
let fontBaseDisplaySize = 17.0 let fontBaseDisplaySize = 17.0
var subscriptionsItems: [AnyComponentWithIdentity<Empty>] = [] var subscriptionsItems: [AnyComponentWithIdentity<Empty>] = []

View File

@ -118,7 +118,7 @@ func openWebAppImpl(
var fullSize = false var fullSize = false
var isFullscreen = false var isFullscreen = false
if isTelegramMeLink(url), let internalUrl = parseFullInternalUrl(sharedContext: context.sharedContext, url: url), case .peer(_, .appStart) = internalUrl { if isTelegramMeLink(url), let internalUrl = parseFullInternalUrl(sharedContext: context.sharedContext, context: context, url: url), case .peer(_, .appStart) = internalUrl {
if url.contains("mode=fullscreen") { if url.contains("mode=fullscreen") {
isFullscreen = true isFullscreen = true
fullSize = true fullSize = true

View File

@ -1147,7 +1147,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
self.allAdMessages = (messages.first, [], 0) self.allAdMessages = (messages.first, [], 0)
} else { } else {
var adPeerName: String? var adPeerName: String?
if let adAttribute = messages.first?.adAttribute, let parsedUrl = parseAdUrl(sharedContext: self.context.sharedContext, url: adAttribute.url), case let .peer(reference, _) = parsedUrl, case let .name(peerName) = reference { if let adAttribute = messages.first?.adAttribute, let parsedUrl = parseAdUrl(sharedContext: self.context.sharedContext, context: self.context, url: adAttribute.url), case let .peer(reference, _) = parsedUrl, case let .name(peerName) = reference {
adPeerName = peerName adPeerName = peerName
} }

View File

@ -114,7 +114,7 @@ public func parseConfirmationCodeUrl(sharedContext: SharedAccountContext, url: U
} }
} }
if url.scheme == "tg" { if url.scheme == "tg" {
if let host = url.host, let query = url.query, let parsedUrl = parseInternalUrl(sharedContext: sharedContext, query: host + "?" + query) { if let host = url.host, let query = url.query, let parsedUrl = parseInternalUrl(sharedContext: sharedContext, context: nil, query: host + "?" + query) {
switch parsedUrl { switch parsedUrl {
case let .confirmationCode(code): case let .confirmationCode(code):
return code return code

View File

@ -113,7 +113,7 @@ private enum ParsedUrl {
case internalUrl(ParsedInternalUrl) case internalUrl(ParsedInternalUrl)
} }
public func parseInternalUrl(sharedContext: SharedAccountContext, query: String) -> ParsedInternalUrl? { public func parseInternalUrl(sharedContext: SharedAccountContext, context: AccountContext?, query: String) -> ParsedInternalUrl? {
var query = query var query = query
if query.hasPrefix("s/") { if query.hasPrefix("s/") {
query = String(query[query.index(query.startIndex, offsetBy: 2)...]) query = String(query[query.index(query.startIndex, offsetBy: 2)...])
@ -270,7 +270,12 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, query: String)
} }
return .peer(.name(peerName), .attachBotStart(value, startAttach)) return .peer(.name(peerName), .attachBotStart(value, startAttach))
} else if queryItem.name == "start" { } else if queryItem.name == "start" {
let linkRefPrefix = "_tgref_" var linkRefPrefix = "_tgr_"
if let context {
if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_start_param_prefixes"] as? String {
linkRefPrefix = value
}
}
if value.hasPrefix(linkRefPrefix) { if value.hasPrefix(linkRefPrefix) {
let referrer = String(value[value.index(value.startIndex, offsetBy: linkRefPrefix.count)...]) let referrer = String(value[value.index(value.startIndex, offsetBy: linkRefPrefix.count)...])
return .peer(.name(peerName), .referrer(referrer)) return .peer(.name(peerName), .referrer(referrer))
@ -1139,14 +1144,14 @@ public func parseProxyUrl(sharedContext: SharedAccountContext, url: String) -> (
for scheme in schemes { for scheme in schemes {
let basePrefix = scheme + basePath + "/" let basePrefix = scheme + basePath + "/"
if url.lowercased().hasPrefix(basePrefix) { if url.lowercased().hasPrefix(basePrefix) {
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: String(url[basePrefix.endIndex...])), case let .proxy(host, port, username, password, secret) = internalUrl { if let internalUrl = parseInternalUrl(sharedContext: sharedContext, context: nil, query: String(url[basePrefix.endIndex...])), case let .proxy(host, port, username, password, secret) = internalUrl {
return (host, port, username, password, secret) return (host, port, username, password, secret)
} }
} }
} }
} }
if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query { if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query {
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: host + "?" + query), case let .proxy(host, port, username, password, secret) = internalUrl { if let internalUrl = parseInternalUrl(sharedContext: sharedContext, context: nil, query: host + "?" + query), case let .proxy(host, port, username, password, secret) = internalUrl {
return (host, port, username, password, secret) return (host, port, username, password, secret)
} }
} }
@ -1160,14 +1165,14 @@ public func parseStickerPackUrl(sharedContext: SharedAccountContext, url: String
for scheme in schemes { for scheme in schemes {
let basePrefix = scheme + basePath + "/" let basePrefix = scheme + basePath + "/"
if url.lowercased().hasPrefix(basePrefix) { if url.lowercased().hasPrefix(basePrefix) {
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: String(url[basePrefix.endIndex...])), case let .stickerPack(name, _) = internalUrl { if let internalUrl = parseInternalUrl(sharedContext: sharedContext, context: nil, query: String(url[basePrefix.endIndex...])), case let .stickerPack(name, _) = internalUrl {
return name return name
} }
} }
} }
} }
if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query { if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query {
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: host + "?" + query), case let .stickerPack(name, _) = internalUrl { if let internalUrl = parseInternalUrl(sharedContext: sharedContext, context: nil, query: host + "?" + query), case let .stickerPack(name, _) = internalUrl {
return name return name
} }
} }
@ -1181,14 +1186,14 @@ public func parseWallpaperUrl(sharedContext: SharedAccountContext, url: String)
for scheme in schemes { for scheme in schemes {
let basePrefix = scheme + basePath + "/" let basePrefix = scheme + basePath + "/"
if url.lowercased().hasPrefix(basePrefix) { if url.lowercased().hasPrefix(basePrefix) {
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: String(url[basePrefix.endIndex...])), case let .wallpaper(wallpaper) = internalUrl { if let internalUrl = parseInternalUrl(sharedContext: sharedContext, context: nil, query: String(url[basePrefix.endIndex...])), case let .wallpaper(wallpaper) = internalUrl {
return wallpaper return wallpaper
} }
} }
} }
} }
if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query { if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query {
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: host + "?" + query), case let .wallpaper(wallpaper) = internalUrl { if let internalUrl = parseInternalUrl(sharedContext: sharedContext, context: nil, query: host + "?" + query), case let .wallpaper(wallpaper) = internalUrl {
return wallpaper return wallpaper
} }
} }
@ -1196,20 +1201,20 @@ public func parseWallpaperUrl(sharedContext: SharedAccountContext, url: String)
return nil return nil
} }
public func parseAdUrl(sharedContext: SharedAccountContext, url: String) -> ParsedInternalUrl? { public func parseAdUrl(sharedContext: SharedAccountContext, context: AccountContext, url: String) -> ParsedInternalUrl? {
let schemes = ["http://", "https://", ""] let schemes = ["http://", "https://", ""]
for basePath in baseTelegramMePaths { for basePath in baseTelegramMePaths {
for scheme in schemes { for scheme in schemes {
let basePrefix = scheme + basePath + "/" let basePrefix = scheme + basePath + "/"
if url.lowercased().hasPrefix(basePrefix) { if url.lowercased().hasPrefix(basePrefix) {
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: String(url[basePrefix.endIndex...])), case .peer = internalUrl { if let internalUrl = parseInternalUrl(sharedContext: sharedContext, context: context, query: String(url[basePrefix.endIndex...])), case .peer = internalUrl {
return internalUrl return internalUrl
} }
} }
} }
} }
if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query { if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query {
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: host + "?" + query), case .peer = internalUrl { if let internalUrl = parseInternalUrl(sharedContext: sharedContext, context: context, query: host + "?" + query), case .peer = internalUrl {
return internalUrl return internalUrl
} }
} }
@ -1217,13 +1222,13 @@ public func parseAdUrl(sharedContext: SharedAccountContext, url: String) -> Pars
return nil return nil
} }
public func parseFullInternalUrl(sharedContext: SharedAccountContext, url: String) -> ParsedInternalUrl? { public func parseFullInternalUrl(sharedContext: SharedAccountContext, context: AccountContext, url: String) -> ParsedInternalUrl? {
let schemes = ["http://", "https://", ""] let schemes = ["http://", "https://", ""]
for basePath in baseTelegramMePaths { for basePath in baseTelegramMePaths {
for scheme in schemes { for scheme in schemes {
let basePrefix = scheme + basePath + "/" let basePrefix = scheme + basePath + "/"
if url.lowercased().hasPrefix(basePrefix) { if url.lowercased().hasPrefix(basePrefix) {
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: String(url[basePrefix.endIndex...])) { if let internalUrl = parseInternalUrl(sharedContext: sharedContext, context: context, query: String(url[basePrefix.endIndex...])) {
return internalUrl return internalUrl
} }
} }
@ -1309,7 +1314,7 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String
} }
} }
if url.lowercased().hasPrefix(basePrefix) { if url.lowercased().hasPrefix(basePrefix) {
if let internalUrl = parseInternalUrl(sharedContext: context.sharedContext, query: String(url[basePrefix.endIndex...])) { if let internalUrl = parseInternalUrl(sharedContext: context.sharedContext, context: context, query: String(url[basePrefix.endIndex...])) {
return resolveInternalUrl(context: context, url: internalUrl) return resolveInternalUrl(context: context, url: internalUrl)
|> map { result -> ResolveUrlResult in |> map { result -> ResolveUrlResult in
switch result { switch result {

View File

@ -456,7 +456,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
} }
}) })
} else { } else {
if let url = controller.url, isTelegramMeLink(url), let internalUrl = parseFullInternalUrl(sharedContext: self.context.sharedContext, url: url), case .peer(_, .appStart) = internalUrl { if let url = controller.url, isTelegramMeLink(url), let internalUrl = parseFullInternalUrl(sharedContext: self.context.sharedContext, context: self.context, url: url), case .peer(_, .appStart) = internalUrl {
let _ = (self.context.sharedContext.resolveUrl(context: self.context, peerId: controller.peerId, url: url, skipUrlAuth: false) let _ = (self.context.sharedContext.resolveUrl(context: self.context, peerId: controller.peerId, url: url, skipUrlAuth: false)
|> deliverOnMainQueue).startStandalone(next: { [weak self] result in |> deliverOnMainQueue).startStandalone(next: { [weak self] result in
guard let self, let controller = self.controller else { guard let self, let controller = self.controller else {