Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2024-11-30 00:59:10 +04:00
commit b8b0836d75
9 changed files with 264 additions and 138 deletions

View File

@ -13312,3 +13312,131 @@ Sorry for the inconvenience.";
"MediaPicker.InvertCaption.Updated.Up.Text" = "Text will be shown above the media."; "MediaPicker.InvertCaption.Updated.Up.Text" = "Text will be shown above the media.";
"MediaPicker.InvertCaption.Updated.Down.Title" = "Caption moved down"; "MediaPicker.InvertCaption.Updated.Down.Title" = "Caption moved down";
"MediaPicker.InvertCaption.Updated.Down.Text" = "Text will be shown below the media."; "MediaPicker.InvertCaption.Updated.Down.Text" = "Text will be shown below the media.";
"AffiliateSetup.AlertTerminate.Title" = "Warning";
"AffiliateSetup.AlertTerminate.Text" = "If you end your affiliate program:\n\n• Any referral links already shared will be disabled in 24 hours.\n\n• All participating affiliates will be notified.\n\n• You will be able to start a new affiliate program only in 24 hours.";
"AffiliateSetup.AlertTerminate.Action" = "End Anyway";
"AffiliateSetup.AlertApply.Title" = "Warning";
"AffiliateSetup.AlertApply.Text" = "Once you start the affiliate program, you won't be able to decrease its commission or duration. You can only increase these parameters or end the program, whuch will disable all previously distributed referral links.";
"AffiliateSetup.AlertApply.SectionCommission" = "Commission";
"AffiliateSetup.AlertApply.SectionDuration" = "Duration";
"AffiliateSetup.AlertApply.Action" = "Start";
"AffiliateSetup.ToastTerminated.Title" = "Affiliate Program Ended";
"AffiliateSetup.ToastTerminated.Text" = "Participating affiliates have been notified. All referral links will be disabled in 24 hours.";
"AffiliateSetup.SectionDuration" = "DURATION";
"AffiliateSetup.SectionDurationFooter" = "Set the duration for which affiliates will earn commissions from referred users.";
"AffiliateSetup.SectionCommission" = "COMMISSION";
"AffiliateSetup.SectionCommissionFooter" = "Define the percentage of star revenue your affiliates earn for referring users to your bot.";
"AffiliateSetup.ExistingPrograms.Action" = "View Existing Programs";
"AffiliateSetup.ExistingPrograms.Footer" = "Explore what other mini apps offer.";
"AffiliateSetup.EndAction" = "End Affiliate Program";
"AffiliateSetup.StartTitle" = "Start Affiliate Program";
"AffiliateSetup.UpdateTitle" = "Update Affiliate Program";
"AffiliateSetup.TermsFooter" = "By creating an affiliate program, you afree to the [terms and conditions](https://telegram.org/terms) of Affiliate Programs.";
"AffiliateSetup.ProgramMenu.OpenBot" = "Open Bot";
"AffiliateSetup.ProgramMenu.OpenApp" = "Open App";
"AffiliateSetup.ProgramMenu.CopyLink" = "Copy Link";
"AffiliateSetup.ProgramMenu.Leave" = "Leave";
"AffiliateSetup.ProgramLeave" = "Leave";
"AffiliateSetup.ConnectedSectionTitle" = "MY PROGRAMS";
"AffiliateSetup.SuggestedSectionTitle" = "PROGRAMS";
"AffiliateSetup.SuggestedSectionEmpty" = "No available programs yet.\nPlease check the page later.";
"AffiliateSetup.TitleNew" = "Affiliate Program";
"AffiliateSetup.TextNew" = "Affiliate Program";
"AffiliateSetup.TitleJoin" = "Affiliate Programs";
"AffiliateSetup.TextJoin" = "Earn a commission each time a user who first accessed a mini app through your referral link spends **Stars** within it.";
"AffiliateSetup.IntroNew.Title1" = "Share revenue with affiliates";
"AffiliateSetup.IntroNew.Text1" = "Set the commission for revenue generated by users referred to you.";
"AffiliateSetup.IntroNew.Title2" = "Launch your affiliate program";
"AffiliateSetup.IntroNew.Text2" = "Telegram will feature your program for millions of potential affiliates.";
"AffiliateSetup.IntroNew.Title3" = "Let affiliates promote you";
"AffiliateSetup.IntroNew.Text3" = "Affiliates will share your referral link with their audience.";
"AffiliateSetup.IntroJoin.Title1" = "Reliable";
"AffiliateSetup.IntroJoin.Text1" = "Receive guaranteed commissions for spending by users you refer.";
"AffiliateSetup.IntroJoin.Title2" = "Transparent";
"AffiliateSetup.IntroJoin.Text2" = "Track your commissions from referred users in real time.";
"AffiliateSetup.IntroJoin.Title3" = "Simple";
"AffiliateSetup.IntroJoin.Text3" = "Choose a mini app below, get your referral link, and start earning Stars.";
"AffiliateProgram.ToastLinkCopied.Title" = "Link copied to clipboard";
"AffiliateProgram.ToastLinkCopied.Text" = "Share this link and earn **%1$@** of what people who use it spend in **%2$@!";
"AffiliateProgram.DurationLifetime" = "Lifetime";
"AffiliateProgram.SortSelectorProfitability" = "Profitability";
"AffiliateProgram.SortSelectorRevenue" = "Revenue";
"AffiliateProgram.SortSelectorDate" = "Date";
"AffiliateProgram.ValueShortMonths_1" = "%@m";
"AffiliateProgram.ValueShortMonths_any" = "%@m";
"AffiliateProgram.ValueShortYears_1" = "%@y";
"AffiliateProgram.ValueShortYears_any" = "%@y";
"AffiliateProgram.ValueLongMonths_1" = "%@ MONTH";
"AffiliateProgram.ValueLongMonths_any" = "%@ MONTHS";
"AffiliateProgram.ValueLongYears_1" = "%@ YEAR";
"AffiliateProgram.ValueLongYears_any" = "%@ YEARS";
"AffiliateProgram.PeerTypeSelf" = "personal account";
"AffiliateProgram.JoinTitle" = "Affiliate Program";
"AffiliateProgram.JoinTerms" = "By joining this program, you afree to the [terms and conditions](https://telegram.org/terms) of Affiliate Programs.";
"AffiliateProgram.DailyRevenueText" = "The average daily revenue per user: #**%@**";
"AffiliateProgram.LinkTitle" = "Referral Link";
"AffiliateProgram.LinkSubtitleLifetime" = "Share this link with your users to earn a **{commission}** commission on their spending in **{bot}** **forever** after they follow your link.";
"AffiliateProgram.LinkSubtitleMonths_1" = "Share this link with your users to earn a **{commission}** commission on their spending in **{bot}** for **1 month** after they follow your link.";
"AffiliateProgram.LinkSubtitleMonths_any" = "Share this link with your users to earn a **{commission}** commission on their spending in **{bot}** for **%d months** after they follow your link.";
"AffiliateProgram.LinkSubtitleYears_1" = "Share this link with your users to earn a **{commission}** commission on their spending in **{bot}** for **1 year** after they follow your link.";
"AffiliateProgram.LinkSubtitleYears_any" = "Share this link with your users to earn a **{commission}** commission on their spending in **{bot}** for **%d years** after they follow your link.";
"AffiliateProgram.JoinSubtitleLifetime" = "**{bot}** will share **{commission}** of the revenue from each user you refer to it **forever**.";
"AffiliateProgram.JoinSubtitleMonths_1" = "**{bot}** will share **{commission}** of the revenue from each user you refer to it for **1 month.**";
"AffiliateProgram.JoinSubtitleMonths_any" = "**{bot}** will share **{commission}** of the revenue from each user you refer to it for **%d months.**";
"AffiliateProgram.JoinSubtitleYears_1" = "**{bot}** will share **{commission}** of the revenue from each user you refer to it for **1 month.**";
"AffiliateProgram.JoinSubtitleYears_any" = "**{bot}** will share **{commission}** of the revenue from each user you refer to it for **%d months.**";
"AffiliateProgram.CommistionDestinationText" = "Commission will be sent to:";
"AffiliateProgram.ActionJoin" = "Join Program";
"AffiliateProgram.ActionCopyLink" = "Copy Link";
"AffiliateProgram.ToastJoined.Title" = "Program joined";
"AffiliateProgram.ToastJoined.Text" = "You can now copy the referral link.";
"AffiliateSetup.SortSectionHeader.Date" = "SORT BY [DATE]()";
"AffiliateSetup.SortSectionHeader.Profitability" = "SORT BY [PROFITABILITY]()";
"AffiliateSetup.SortSectionHeader.Revenue" = "SORT BY [REVENUE]()";
"AffiliateProgram.UserCountFooter_0" = "No one opened {bot} through this link yet.";
"AffiliateProgram.UserCountFooter_1" = "1 user opened {bot} through this link.";
"AffiliateProgram.UserCountFooter_any" = "%d users opened {bot} through this link.";
"ChatList.InlineButtonOpenApp" = "OPEN";
"Monetization.EarnStarsInfo.Title" = "Earn Stars";
"Monetization.EarnStarsInfo.Text" = "Distribute links to mini apps and earn a share of their revenue in Stars.";
"PeerInfo.ItemAffiliateProgram.Title" = "Affiliate Program";
"PeerInfo.ItemAffiliatePrograms.Title" = "Affiliate Programs";
"PeerInfo.ItemAffiliateProgram.Footer" = "Share a link to %1$@ with your friends and and earn %2$@% of their spending there.";
"PeerInfo.ItemAffiliateProgram.ValueOff" = "Off";
"StarsTransaction.TitleCommission" = "%@% Commission";
"StarsTransaction.StarRefReason.Title" = "Reason";
"StarsTransaction.StarRefReason.Program" = "Affiliate Program";
"StarsTransaction.StarRefReason.Miniapp" = "Mini App";
"StarsTransaction.StarRefReason.Affiliate" = "Affiliate";
"StarsTransaction.StarRefReason.Referred" = "Referred User";

View File

@ -1186,7 +1186,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
guard let self else { guard let self else {
return return
} }
//TODO:localize
self.context.sharedContext.openWebApp( self.context.sharedContext.openWebApp(
context: self.context, context: self.context,
parentController: self, parentController: self,

View File

@ -3098,8 +3098,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
var actionButtonTitleNodeLayoutAndApply: (TextNodeLayout, () -> TextNode)? var actionButtonTitleNodeLayoutAndApply: (TextNodeLayout, () -> TextNode)?
if case .none = badgeContent, case .none = mentionBadgeContent, case let .chat(itemPeer) = contentPeer, case let .user(user) = itemPeer.chatMainPeer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp) { if case .none = badgeContent, case .none = mentionBadgeContent, case let .chat(itemPeer) = contentPeer, case let .user(user) = itemPeer.chatMainPeer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp) {
//TODO:localize actionButtonTitleNodeLayoutAndApply = makeActionButtonTitleNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.ChatList_InlineButtonOpenApp, font: Font.semibold(15.0), textColor: theme.unreadBadgeActiveTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: rawContentWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
actionButtonTitleNodeLayoutAndApply = makeActionButtonTitleNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "OPEN", font: Font.semibold(15.0), textColor: theme.unreadBadgeActiveTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: rawContentWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
} }
var badgeSize: CGFloat = 0.0 var badgeSize: CGFloat = 0.0

View File

@ -1219,8 +1219,7 @@ private enum StatsEntry: ItemListNodeEntry {
arguments.presentCpmLocked() arguments.presentCpmLocked()
}) })
case .earnStarsInfo: case .earnStarsInfo:
//TODO:localize return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.earnStars, title: presentationData.strings.Monetization_EarnStarsInfo_Title, titleBadge: presentationData.strings.Settings_New, label: presentationData.strings.Monetization_EarnStarsInfo_Text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, action: {
return ItemListDisclosureItem(presentationData: 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: self.section, style: .blocks, action: {
arguments.openEarnStars() arguments.openEarnStars()
}) })
} }
@ -1702,7 +1701,6 @@ private func monetizationEntries(
if displayStarsTransactions { if displayStarsTransactions {
if !addedTransactionsTabs { if !addedTransactionsTabs {
if canJoinRefPrograms { if canJoinRefPrograms {
//TODO:localize
entries.append(.earnStarsInfo) entries.append(.earnStarsInfo)
} }

View File

@ -43,6 +43,23 @@ private func textForTimeout(value: Int32) -> String {
} }
} }
private func textForDurationMonths(strings: PresentationStrings, value: Int) -> (short: String, full: String) {
if value >= Int(Int32.max) {
return ("", strings.AffiliateProgram_DurationLifetime.uppercased())
}
if value > 12 {
return (
strings.AffiliateProgram_ValueShortYears(Int32(value / 12)),
strings.AffiliateProgram_ValueLongYears(Int32(value / 12))
)
} else {
return (
strings.AffiliateProgram_ValueShortMonths(Int32(value)),
strings.AffiliateProgram_ValueLongMonths(Int32(value))
)
}
}
final class AffiliateProgramSetupScreenComponent: Component { final class AffiliateProgramSetupScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -181,25 +198,25 @@ final class AffiliateProgramSetupScreenComponent: Component {
if let durationMonths = programDuration { if let durationMonths = programDuration {
durationTitle = timeIntervalString(strings: environment.strings, value: durationMonths * (30 * 24 * 60 * 60)) durationTitle = timeIntervalString(strings: environment.strings, value: durationMonths * (30 * 24 * 60 * 60))
} else { } else {
durationTitle = "Lifetime" durationTitle = environment.strings.AffiliateProgram_DurationLifetime
} }
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }) let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
self.environment?.controller()?.present(tableAlert( self.environment?.controller()?.present(tableAlert(
theme: presentationData.theme, theme: presentationData.theme,
title: "Warning", title: environment.strings.AffiliateSetup_AlertApply_Title,
text: "Once you start the affiliate program, you won't be able to decrease its commission or duration. You can only increase these parameters or end the program, whuch will disable all previously distributed referral links.", text: environment.strings.AffiliateSetup_AlertApply_Text,
table: TableComponent(theme: environment.theme, items: [ table: TableComponent(theme: environment.theme, items: [
TableComponent.Item(id: 0, title: "Commission", component: AnyComponent(MultilineTextComponent( TableComponent.Item(id: 0, title: environment.strings.AffiliateSetup_AlertApply_SectionCommission, component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: commissionTitle, font: Font.regular(17.0), textColor: environment.theme.actionSheet.primaryTextColor)) text: .plain(NSAttributedString(string: commissionTitle, font: Font.regular(17.0), textColor: environment.theme.actionSheet.primaryTextColor))
))), ))),
TableComponent.Item(id: 1, title: "Duration", component: AnyComponent(MultilineTextComponent( TableComponent.Item(id: 1, title: environment.strings.AffiliateSetup_AlertApply_SectionDuration, component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: durationTitle, font: Font.regular(17.0), textColor: environment.theme.actionSheet.primaryTextColor)) text: .plain(NSAttributedString(string: durationTitle, font: Font.regular(17.0), textColor: environment.theme.actionSheet.primaryTextColor))
))) )))
]), ]),
actions: [ actions: [
ComponentAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), ComponentAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: {}),
ComponentAlertAction(type: .defaultAction, title: "Start", action: { [weak self] in ComponentAlertAction(type: .defaultAction, title: environment.strings.AffiliateSetup_AlertApply_Action, action: { [weak self] in
guard let self else { guard let self else {
return return
} }
@ -210,26 +227,17 @@ final class AffiliateProgramSetupScreenComponent: Component {
} }
private func requestApplyEndProgram() { private func requestApplyEndProgram() {
guard let component = self.component else { guard let component = self.component, let environment = self.environment else {
return return
} }
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }) let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
self.environment?.controller()?.present(standardTextAlertController( self.environment?.controller()?.present(standardTextAlertController(
theme: AlertControllerTheme(presentationData: presentationData), theme: AlertControllerTheme(presentationData: presentationData),
title: "Warning", title: environment.strings.AffiliateSetup_AlertTerminate_Title,
text: text: environment.strings.AffiliateSetup_AlertTerminate_Text,
"""
If you end your affiliate program:
Any referral links already shared will be disabled in 24 hours.
All participating affiliates will be notified.
You will be able to start a new affiliate program only in 24 hours.
""",
actions: [ actions: [
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}),
TextAlertAction(type: .defaultDestructiveAction, title: "End Anyway", action: { [weak self] in TextAlertAction(type: .defaultDestructiveAction, title: environment.strings.AffiliateSetup_AlertTerminate_Action, action: { [weak self] in
guard let self else { guard let self else {
return return
} }
@ -291,7 +299,7 @@ If you end your affiliate program:
program: nil program: nil
) )
|> deliverOnMainQueue).startStrict(completed: { [weak self] in |> deliverOnMainQueue).startStrict(completed: { [weak self] in
guard let self, let component = self.component, let controller = self.environment?.controller() else { guard let self, let component = self.component, let environment = self.environment, let controller = self.environment?.controller() else {
return return
} }
self.isApplying = false self.isApplying = false
@ -300,7 +308,7 @@ If you end your affiliate program:
if let navigationController = controller.navigationController, let index = navigationController.viewControllers.firstIndex(where: { $0 === controller }), index != 0 { if let navigationController = controller.navigationController, let index = navigationController.viewControllers.firstIndex(where: { $0 === controller }), index != 0 {
if let previousController = navigationController.viewControllers[index - 1] as? ViewController { if let previousController = navigationController.viewControllers[index - 1] as? ViewController {
previousController.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_link_broken", scale: 0.065, colors: [:], title: "Affiliate Program Ended", text: "Participating affiliates have been notified. All referral links will be disabled in 24 hours.", customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) previousController.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_link_broken", scale: 0.065, colors: [:], title: environment.strings.AffiliateSetup_ToastTerminated_Title, text: environment.strings.AffiliateSetup_ToastTerminated_Text, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
} }
} }
controller.dismiss() controller.dismiss()
@ -378,7 +386,7 @@ If you end your affiliate program:
} }
UIPasteboard.general.string = bot.url UIPasteboard.general.string = bot.url
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }) let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: "Link copied to clipboard", text: "Share this link and earn **\(formatPermille(bot.commissionPermille))%** of what people who use it spend in **\(bot.peer.compactDisplayTitle)**!"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: presentationData.strings.AffiliateProgram_ToastLinkCopied_Title, text: presentationData.strings.AffiliateProgram_ToastLinkCopied_Text(formatPermille(bot.commissionPermille), bot.peer.compactDisplayTitle).string), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
} }
)) ))
)) ))
@ -417,9 +425,9 @@ If you end your affiliate program:
var items: [ContextMenuItem] = [] var items: [ContextMenuItem] = []
let availableModes: [(TelegramSuggestedStarRefBotList.SortMode, String)] = [ let availableModes: [(TelegramSuggestedStarRefBotList.SortMode, String)] = [
(.profitability, "Profitability"), (.profitability, environment.strings.AffiliateProgram_SortSelectorProfitability),
(.revenue, "Revenue"), (.revenue, environment.strings.AffiliateProgram_SortSelectorRevenue),
(.date, "Date") (.date, environment.strings.AffiliateProgram_SortSelectorDate)
] ]
for (mode, title) in availableModes { for (mode, title) in availableModes {
let isSelected = mode == self.suggestedSortMode let isSelected = mode == self.suggestedSortMode
@ -483,14 +491,14 @@ If you end your affiliate program:
self.isUpdating = false self.isUpdating = false
} }
let durationItems: [(months: Int32, title: String, selectedTitle: String)] = [ let durationItems: [Int32] = [
(1, "1m", "1 MONTH"), 1,
(3, "3m", "3 MONTHS"), 3,
(6, "6m", "6 MONTHS"), 6,
(12, "1y", "1 YEAR"), 12,
(2 * 12, "2y", "2 YEARS"), 2 * 12,
(3 * 12, "3y", "3 YEARS"), 3 * 12,
(Int32.max, "", "LIFETIME") Int32.max
] ]
let environment = environment[EnvironmentType.self].value let environment = environment[EnvironmentType.self].value
@ -630,11 +638,11 @@ If you end your affiliate program:
let subtitleValue: String let subtitleValue: String
switch component.initialContent.mode { switch component.initialContent.mode {
case .editProgram: case .editProgram:
titleValue = "Affiliate Program" titleValue = environment.strings.AffiliateSetup_TitleNew
subtitleValue = "Reward those who help grow your userbase." subtitleValue = environment.strings.AffiliateSetup_TextNew
case .connectedPrograms: case .connectedPrograms:
titleValue = "Affiliate Programs" titleValue = environment.strings.AffiliateSetup_TitleJoin
subtitleValue = "Earn a commission each time a user who first accessed a mini app through your referral link spends **Stars** within it." subtitleValue = environment.strings.AffiliateSetup_TextJoin
} }
let titleSize = self.title.update( let titleSize = self.title.update(
transition: .immediate, transition: .immediate,
@ -696,36 +704,36 @@ If you end your affiliate program:
introItems = [ introItems = [
( (
"Chat/Context Menu/Smile", "Chat/Context Menu/Smile",
"Share revenue with affiliates", environment.strings.AffiliateSetup_IntroNew_Title1,
"Set the commission for revenue generated by users referred to you." environment.strings.AffiliateSetup_IntroNew_Text1
), ),
( (
"Chat/Context Menu/Channels", "Chat/Context Menu/Channels",
"Launch your affiliate program", environment.strings.AffiliateSetup_IntroNew_Title2,
"Telegram will feature your program for millions of potential affiliates." environment.strings.AffiliateSetup_IntroNew_Text2
), ),
( (
"Chat/Context Menu/Link", "Chat/Context Menu/Link",
"Let affiliates promote you", environment.strings.AffiliateSetup_IntroNew_Title3,
"Affiliates will share your referral link with their audience." environment.strings.AffiliateSetup_IntroNew_Text3
) )
] ]
case .connectedPrograms: case .connectedPrograms:
introItems = [ introItems = [
( (
"Peer Info/RefProgram/IntroListSecure", "Peer Info/RefProgram/IntroListSecure",
"Reliable", environment.strings.AffiliateSetup_IntroJoin_Title1,
"Receive guaranteed commissions for spending by users you refer." environment.strings.AffiliateSetup_IntroJoin_Text1
), ),
( (
"Peer Info/RefProgram/IntroListEye", "Peer Info/RefProgram/IntroListEye",
"Transparent", environment.strings.AffiliateSetup_IntroJoin_Title2,
"Track your commissions from referred users in real time." environment.strings.AffiliateSetup_IntroJoin_Text2
), ),
( (
"Peer Info/RefProgram/IntroListLike", "Peer Info/RefProgram/IntroListLike",
"Simple", environment.strings.AffiliateSetup_IntroJoin_Title3,
"Choose a mini app below, get your referral link, and start earning Stars." environment.strings.AffiliateSetup_IntroJoin_Text3
) )
] ]
} }
@ -855,7 +863,7 @@ If you end your affiliate program:
theme: environment.theme, theme: environment.theme,
header: AnyComponent(MultilineTextComponent( header: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
string: "COMMISSION", string: environment.strings.AffiliateSetup_SectionCommission,
font: Font.regular(13.0), font: Font.regular(13.0),
textColor: environment.theme.list.freeTextColor textColor: environment.theme.list.freeTextColor
)), )),
@ -863,7 +871,7 @@ If you end your affiliate program:
)), )),
footer: AnyComponent(MultilineTextComponent( footer: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
string: "Define the percentage of star revenue your affiliates earn for referring users to your bot.", string: environment.strings.AffiliateSetup_SectionCommissionFooter,
font: Font.regular(13.0), font: Font.regular(13.0),
textColor: environment.theme.list.freeTextColor textColor: environment.theme.list.freeTextColor
)), )),
@ -911,10 +919,10 @@ If you end your affiliate program:
var selectedDurationIndex = 0 var selectedDurationIndex = 0
var durationMinValueIndex = 0 var durationMinValueIndex = 0
for i in 0 ..< durationItems.count { for i in 0 ..< durationItems.count {
if self.durationValue == Int(durationItems[i].months) { if self.durationValue == Int(durationItems[i]) {
selectedDurationIndex = i selectedDurationIndex = i
} }
if self.durationMinValue == Int(durationItems[i].months) { if self.durationMinValue == Int(durationItems[i]) {
durationMinValueIndex = i durationMinValueIndex = i
} }
} }
@ -925,7 +933,7 @@ If you end your affiliate program:
header: AnyComponent(HStack([ header: AnyComponent(HStack([
AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent( AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
string: "DURATION", string: environment.strings.AffiliateSetup_SectionDuration,
font: Font.regular(13.0), font: Font.regular(13.0),
textColor: environment.theme.list.freeTextColor textColor: environment.theme.list.freeTextColor
)), )),
@ -933,7 +941,7 @@ If you end your affiliate program:
))), ))),
AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent( AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
string: durationItems[selectedDurationIndex].selectedTitle, string: textForDurationMonths(strings: environment.strings, value: Int(durationItems[selectedDurationIndex])).full,
font: Font.regular(13.0), font: Font.regular(13.0),
textColor: environment.theme.list.freeTextColor textColor: environment.theme.list.freeTextColor
)), )),
@ -942,7 +950,7 @@ If you end your affiliate program:
], spacing: 4.0, alignment: .alternatingLeftRight)), ], spacing: 4.0, alignment: .alternatingLeftRight)),
footer: AnyComponent(MultilineTextComponent( footer: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
string: "Set the duration for which affiliates will earn commissions from referred users.", string: environment.strings.AffiliateSetup_SectionDurationFooter,
font: Font.regular(13.0), font: Font.regular(13.0),
textColor: environment.theme.list.freeTextColor textColor: environment.theme.list.freeTextColor
)), )),
@ -952,7 +960,9 @@ If you end your affiliate program:
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemSliderSelectorComponent( AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemSliderSelectorComponent(
theme: environment.theme, theme: environment.theme,
content: .discrete(ListItemSliderSelectorComponent.Discrete( content: .discrete(ListItemSliderSelectorComponent.Discrete(
values: durationItems.map(\.title), values: durationItems.map {
textForDurationMonths(strings: environment.strings, value: Int($0)).short
},
markPositions: true, markPositions: true,
selectedIndex: max(durationMinValueIndex, selectedDurationIndex), selectedIndex: max(durationMinValueIndex, selectedDurationIndex),
minSelectedIndex: durationMinValueIndex, minSelectedIndex: durationMinValueIndex,
@ -961,7 +971,7 @@ If you end your affiliate program:
guard let self else { guard let self else {
return return
} }
self.durationValue = Int(durationItems[value].months) self.durationValue = Int(durationItems[value])
self.state?.updated(transition: .immediate) self.state?.updated(transition: .immediate)
} }
)) ))
@ -990,7 +1000,7 @@ If you end your affiliate program:
header: nil, header: nil,
footer: AnyComponent(MultilineTextComponent( footer: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
string: "Explore what other mini apps offer.", string: environment.strings.AffiliateSetup_ExistingPrograms_Footer,
font: Font.regular(13.0), font: Font.regular(13.0),
textColor: environment.theme.list.freeTextColor textColor: environment.theme.list.freeTextColor
)), )),
@ -1002,7 +1012,7 @@ If you end your affiliate program:
title: AnyComponent(VStack([ title: AnyComponent(VStack([
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent( AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
string: "View Existing Programs", string: environment.strings.AffiliateSetup_ExistingPrograms_Action,
font: Font.regular(presentationData.listsFontSize.baseDisplaySize), font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
textColor: environment.theme.list.itemPrimaryTextColor textColor: environment.theme.list.itemPrimaryTextColor
)), )),
@ -1045,7 +1055,7 @@ If you end your affiliate program:
title: AnyComponent(VStack([ title: AnyComponent(VStack([
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent( AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
string: "End Affiliate Program", string: environment.strings.AffiliateSetup_EndAction,
font: Font.regular(presentationData.listsFontSize.baseDisplaySize), font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
textColor: environment.theme.list.itemDestructiveColor textColor: environment.theme.list.itemDestructiveColor
)), )),
@ -1086,7 +1096,7 @@ If you end your affiliate program:
transition: .immediate, transition: .immediate,
component: AnyComponent(MultilineTextComponent( component: AnyComponent(MultilineTextComponent(
text: .markdown( text: .markdown(
text: "By creating an affiliate program, you afree to the [terms and conditions](https://telegram.org/terms) of Affiliate Programs.", text: environment.strings.AffiliateSetup_TermsFooter,
attributes: MarkdownAttributes( attributes: MarkdownAttributes(
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor), body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor),
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.itemSecondaryTextColor), bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.itemSecondaryTextColor),
@ -1111,9 +1121,9 @@ If you end your affiliate program:
let remainingTime: Int32 = max(0, endDate - timestamp) let remainingTime: Int32 = max(0, endDate - timestamp)
buttonText = textForTimeout(value: remainingTime) buttonText = textForTimeout(value: remainingTime)
} else if self.currentProgram != nil { } else if self.currentProgram != nil {
buttonText = "Update Affiliate Program" buttonText = environment.strings.AffiliateSetup_UpdateTitle
} else { } else {
buttonText = "Start Affiliate Program" buttonText = environment.strings.AffiliateSetup_StartTitle
} }
let bottomPanelButtonSize = self.bottomPanelButton.update( let bottomPanelButtonSize = self.bottomPanelButton.update(
transition: transition, transition: transition,
@ -1187,7 +1197,7 @@ If you end your affiliate program:
if let durationMonths = item.durationMonths { if let durationMonths = item.durationMonths {
durationTitle = timeIntervalString(strings: environment.strings, value: durationMonths * (30 * 24 * 60 * 60)) durationTitle = timeIntervalString(strings: environment.strings, value: durationMonths * (30 * 24 * 60 * 60))
} else { } else {
durationTitle = "Lifetime" durationTitle = environment.strings.AffiliateProgram_DurationLifetime
} }
let commissionTitle = "\(formatPermille(item.commissionPermille))%" let commissionTitle = "\(formatPermille(item.commissionPermille))%"
@ -1201,9 +1211,9 @@ If you end your affiliate program:
let openTitle: String let openTitle: String
if case let .user(user) = item.peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp) { if case let .user(user) = item.peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp) {
openTitle = "Open App" openTitle = environment.strings.AffiliateSetup_ProgramMenu_OpenApp
} else { } else {
openTitle = "Open Bot" openTitle = environment.strings.AffiliateSetup_ProgramMenu_OpenBot
} }
itemList.append(.action(ContextMenuActionItem(text: openTitle, textColor: .primary, icon: { theme in itemList.append(.action(ContextMenuActionItem(text: openTitle, textColor: .primary, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Bots"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Bots"), color: theme.contextMenu.primaryColor)
@ -1239,7 +1249,7 @@ If you end your affiliate program:
}) })
}))) })))
itemList.append(.action(ContextMenuActionItem(text: "Copy Link", textColor: .primary, icon: { theme in itemList.append(.action(ContextMenuActionItem(text: environment.strings.AffiliateSetup_ProgramMenu_CopyLink, textColor: .primary, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, f in }, action: { [weak self] _, f in
f(.default) f(.default)
@ -1251,10 +1261,10 @@ If you end your affiliate program:
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }) let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
UIPasteboard.general.string = item.url UIPasteboard.general.string = item.url
environment.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: "Link copied to clipboard", text: "Share this link and earn **\(formatPermille(item.commissionPermille))%** of what people who use it spend in **\(item.peer.compactDisplayTitle)**!"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) environment.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: environment.strings.AffiliateProgram_ToastLinkCopied_Title, text: environment.strings.AffiliateProgram_ToastLinkCopied_Text(formatPermille(item.commissionPermille), item.peer.compactDisplayTitle ).string), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
}))) })))
itemList.append(.action(ContextMenuActionItem(text: "Leave", textColor: .destructive, icon: { theme in itemList.append(.action(ContextMenuActionItem(text: environment.strings.AffiliateSetup_ProgramMenu_Leave, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
}, action: { [weak self] c, _ in }, action: { [weak self] c, _ in
c?.dismiss(completion: { c?.dismiss(completion: {
@ -1314,7 +1324,7 @@ If you end your affiliate program:
self.openConnectedBot(bot: item) self.openConnectedBot(bot: item)
}, },
inlineActions: PeerListItemComponent.InlineActionsState(actions: [ inlineActions: PeerListItemComponent.InlineActionsState(actions: [
PeerListItemComponent.InlineAction(id: 0, title: "Leave", color: .destructive, action: { [weak self] in PeerListItemComponent.InlineAction(id: 0, title: environment.strings.AffiliateSetup_ProgramLeave, color: .destructive, action: { [weak self] in
guard let self else { guard let self else {
return return
} }
@ -1333,7 +1343,7 @@ If you end your affiliate program:
theme: environment.theme, theme: environment.theme,
header: AnyComponent(MultilineTextComponent( header: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
string: "MY PROGRAMS", string: environment.strings.AffiliateSetup_ConnectedSectionTitle,
font: Font.regular(13.0), font: Font.regular(13.0),
textColor: environment.theme.list.freeTextColor textColor: environment.theme.list.freeTextColor
)), )),
@ -1369,7 +1379,7 @@ If you end your affiliate program:
suggestedSectionItems.append(AnyComponentWithIdentity(id: "empty", component: AnyComponent(ZStack([ suggestedSectionItems.append(AnyComponentWithIdentity(id: "empty", component: AnyComponent(ZStack([
AnyComponentWithIdentity(id: 0, component: AnyComponent(Rectangle(color: .clear, width: nil, height: 100.0))), AnyComponentWithIdentity(id: 0, component: AnyComponent(Rectangle(color: .clear, width: nil, height: 100.0))),
AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent( AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: "No available programs yet.\nPlease check the page later.", font: Font.regular(15.0), textColor: environment.theme.list.itemSecondaryTextColor)), text: .plain(NSAttributedString(string: environment.strings.AffiliateSetup_SuggestedSectionEmpty, font: Font.regular(15.0), textColor: environment.theme.list.itemSecondaryTextColor)),
horizontalAlignment: .center, horizontalAlignment: .center,
maximumNumberOfLines: 0, maximumNumberOfLines: 0,
lineSpacing: 0.2 lineSpacing: 0.2
@ -1382,7 +1392,7 @@ If you end your affiliate program:
if let durationMonths = item.program.durationMonths { if let durationMonths = item.program.durationMonths {
durationTitle = timeIntervalString(strings: environment.strings, value: durationMonths * (30 * 24 * 60 * 60)) durationTitle = timeIntervalString(strings: environment.strings, value: durationMonths * (30 * 24 * 60 * 60))
} else { } else {
durationTitle = "Lifetime" durationTitle = environment.strings.AffiliateProgram_DurationLifetime
} }
suggestedSectionItems.append(AnyComponentWithIdentity(id: item.peer.id, component: AnyComponent(PeerListItemComponent( suggestedSectionItems.append(AnyComponentWithIdentity(id: item.peer.id, component: AnyComponent(PeerListItemComponent(
@ -1476,7 +1486,7 @@ If you end your affiliate program:
var suggestedHeaderItems: [AnyComponentWithIdentity<Empty>] = [] var suggestedHeaderItems: [AnyComponentWithIdentity<Empty>] = []
suggestedHeaderItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent( suggestedHeaderItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString( text: .plain(NSAttributedString(
string: "PROGRAMS", string: environment.strings.AffiliateSetup_SuggestedSectionTitle,
font: Font.regular(13.0), font: Font.regular(13.0),
textColor: environment.theme.list.freeTextColor textColor: environment.theme.list.freeTextColor
)), )),

View File

@ -365,11 +365,11 @@ private final class JoinAffiliateProgramScreenComponent: Component {
for peer in peers { for peer in peers {
let peerLabel: String let peerLabel: String
if peer.id == component.context.account.peerId { if peer.id == component.context.account.peerId {
peerLabel = "personal account" peerLabel = environment.strings.AffiliateProgram_PeerTypeSelf
} else if case .channel = peer { } else if case .channel = peer {
peerLabel = "channel" peerLabel = environment.strings.Channel_Status
} else { } else {
peerLabel = "bot" peerLabel = environment.strings.Bot_GenericBotStatus
} }
let isSelected = peer.id == self.currentTargetPeer?.id let isSelected = peer.id == self.currentTargetPeer?.id
let accentColor = environment.theme.list.itemAccentColor let accentColor = environment.theme.list.itemAccentColor
@ -767,12 +767,6 @@ private final class JoinAffiliateProgramScreenComponent: Component {
} }
let commissionTitle = "\(formatPermille(component.commissionPermille))%" let commissionTitle = "\(formatPermille(component.commissionPermille))%"
let durationTitle: String
if let durationMonths = component.programDuration {
durationTitle = timeIntervalString(strings: environment.strings, value: durationMonths * (24 * 60 * 60))
} else {
durationTitle = "lifetime"
}
let titleString: String let titleString: String
var subtitleString: String var subtitleString: String
@ -780,34 +774,40 @@ private final class JoinAffiliateProgramScreenComponent: Component {
let termsString: String let termsString: String
switch currentMode { switch currentMode {
case .join: case .join:
titleString = "Affiliate Program" titleString = environment.strings.AffiliateProgram_JoinTitle
subtitleString = "**\(component.sourcePeer.compactDisplayTitle)** will share **\(commissionTitle)** of the revenue from each user you refer to it for **\(durationTitle)**."
if let programDuration = component.programDuration {
if programDuration < 12 {
subtitleString = environment.strings.AffiliateProgram_JoinSubtitleMonths(Int32(programDuration)).replacingOccurrences(of: "{bot}", with: component.sourcePeer.compactDisplayTitle).replacingOccurrences(of: "{commission}", with: commissionTitle)
} else {
subtitleString = environment.strings.AffiliateProgram_JoinSubtitleYears(Int32(programDuration / 12)).replacingOccurrences(of: "{bot}", with: component.sourcePeer.compactDisplayTitle).replacingOccurrences(of: "{commission}", with: commissionTitle)
}
} else {
subtitleString = environment.strings.AffiliateProgram_JoinSubtitleLifetime.replacingOccurrences(of: "{bot}", with: component.sourcePeer.compactDisplayTitle).replacingOccurrences(of: "{commission}", with: commissionTitle)
}
if component.revenuePerUser != 0.0 { if component.revenuePerUser != 0.0 {
var revenueString = String(format: "%.1f", component.revenuePerUser) var revenueString = String(format: "%.1f", component.revenuePerUser)
if revenueString.hasSuffix(".0") { if revenueString.hasSuffix(".0") {
revenueString = String(revenueString[revenueString.startIndex ..< revenueString.index(revenueString.endIndex, offsetBy: -2)]) revenueString = String(revenueString[revenueString.startIndex ..< revenueString.index(revenueString.endIndex, offsetBy: -2)])
} }
dailyRevenueString = "Daily revenue per user: #**\(revenueString)**" dailyRevenueString = environment.strings.AffiliateProgram_DailyRevenueText(revenueString).string
} }
termsString = "By joining this program, you afree to the [terms and conditions](https://telegram.org/terms) of Affiliate Programs." termsString = environment.strings.AffiliateProgram_JoinTerms
case let .active(active): case let .active(active):
titleString = "Referral Link" titleString = environment.strings.AffiliateProgram_LinkTitle
let timeString: String if let programDuration = component.programDuration {
if component.programDuration == nil { if programDuration < 12 {
timeString = "**forever** after they follow your link." subtitleString = environment.strings.AffiliateProgram_LinkSubtitleMonths(Int32(programDuration)).replacingOccurrences(of: "{bot}", with: component.sourcePeer.compactDisplayTitle).replacingOccurrences(of: "{commission}", with: commissionTitle)
} else {
subtitleString = environment.strings.AffiliateProgram_LinkSubtitleYears(Int32(programDuration / 12)).replacingOccurrences(of: "{bot}", with: component.sourcePeer.compactDisplayTitle).replacingOccurrences(of: "{commission}", with: commissionTitle)
}
} else { } else {
timeString = "for **\(durationTitle)** after they follow your link." subtitleString = environment.strings.AffiliateProgram_LinkSubtitleLifetime.replacingOccurrences(of: "{bot}", with: component.sourcePeer.compactDisplayTitle).replacingOccurrences(of: "{commission}", with: commissionTitle)
}
subtitleString = "Share this link with your users to earn a **\(commissionTitle)** commission on their spending in **\(component.sourcePeer.compactDisplayTitle)** \(timeString)."
if active.bot.participants == 0 {
termsString = "No one opened \(component.sourcePeer.compactDisplayTitle) through this link yet."
} else if active.bot.participants == 1 {
termsString = "1 user opened \(component.sourcePeer.compactDisplayTitle) through this link."
} else {
termsString = "\(active.bot.participants) users opened \(component.sourcePeer.compactDisplayTitle) through this link."
} }
termsString = environment.strings.AffiliateProgram_UserCountFooter(Int32(active.bot.participants)).replacingOccurrences(of: "{bot}", with: component.sourcePeer.compactDisplayTitle)
} }
let titleSize = self.title.update( let titleSize = self.title.update(
transition: .immediate, transition: .immediate,
@ -986,7 +986,7 @@ private final class JoinAffiliateProgramScreenComponent: Component {
let targetTextSize = self.targetText.update( let targetTextSize = self.targetText.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(MultilineTextComponent( component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: "Commission will be sent to:", font: Font.regular(15.0), textColor: environment.theme.list.itemPrimaryTextColor)), text: .plain(NSAttributedString(string: environment.strings.AffiliateProgram_CommistionDestinationText, font: Font.regular(15.0), textColor: environment.theme.list.itemPrimaryTextColor)),
horizontalAlignment: .center, horizontalAlignment: .center,
maximumNumberOfLines: 0, maximumNumberOfLines: 0,
lineSpacing: 0.2 lineSpacing: 0.2
@ -1087,9 +1087,9 @@ private final class JoinAffiliateProgramScreenComponent: Component {
let actionButtonTitle: String let actionButtonTitle: String
switch currentMode { switch currentMode {
case .join: case .join:
actionButtonTitle = "Join Program" actionButtonTitle = environment.strings.AffiliateProgram_ActionJoin
case .active: case .active:
actionButtonTitle = "Copy Link" actionButtonTitle = environment.strings.AffiliateProgram_ActionCopyLink
} }
let actionButtonSize = self.actionButton.update( let actionButtonSize = self.actionButton.update(
transition: transition, transition: transition,
@ -1207,11 +1207,11 @@ private final class JoinAffiliateProgramScreenComponent: Component {
)), )),
content: AnyComponent(VStack([ content: AnyComponent(VStack([
AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent( AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
text: .markdown(text: "Program joined", attributes: MarkdownAttributes(body: bold, bold: bold, link: body, linkAttribute: { _ in nil })), text: .markdown(text: environment.strings.AffiliateProgram_ToastJoined_Title, attributes: MarkdownAttributes(body: bold, bold: bold, link: body, linkAttribute: { _ in nil })),
maximumNumberOfLines: 0 maximumNumberOfLines: 0
))), ))),
AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent( AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent(
text: .markdown(text: "You can now copy the referral link.", attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in nil })), text: .markdown(text: environment.strings.AffiliateProgram_ToastJoined_Text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in nil })),
maximumNumberOfLines: 0 maximumNumberOfLines: 0
))) )))
], alignment: .left, spacing: 6.0)), ], alignment: .left, spacing: 6.0)),
@ -1920,11 +1920,11 @@ final class BotSectionSortButtonComponent: Component {
let sortByString: String let sortByString: String
switch component.sortMode { switch component.sortMode {
case .date: case .date:
sortByString = "SORT BY [DATE]()" sortByString = component.strings.AffiliateSetup_SortSectionHeader_Date
case .profitability: case .profitability:
sortByString = "SORT BY [PROFITABILITY]()" sortByString = component.strings.AffiliateSetup_SortSectionHeader_Profitability
case .revenue: case .revenue:
sortByString = "SORT BY [REVENUE]()" sortByString = component.strings.AffiliateSetup_SortSectionHeader_Revenue
} }
let textSize = self.text.update( let textSize = self.text.update(
transition: .immediate, transition: .immediate,

View File

@ -1443,14 +1443,12 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
if items[.botAffiliateProgram] == nil { if items[.botAffiliateProgram] == nil {
items[.botAffiliateProgram] = [] items[.botAffiliateProgram] = []
} }
//TODO:localize
let programTitleValue: String let programTitleValue: String
programTitleValue = "\(formatPermille(starRefProgram.commissionPermille))%" programTitleValue = "\(formatPermille(starRefProgram.commissionPermille))%"
//TODO:localize items[.botAffiliateProgram]!.append(PeerInfoScreenDisclosureItem(id: 0, label: .labelBadge(programTitleValue), additionalBadgeLabel: nil, text: presentationData.strings.PeerInfo_ItemAffiliateProgram_Title, icon: PresentationResourcesSettings.affiliateProgram, action: {
items[.botAffiliateProgram]!.append(PeerInfoScreenDisclosureItem(id: 0, label: .labelBadge(programTitleValue), additionalBadgeLabel: nil, text: "Affiliate Program", icon: PresentationResourcesSettings.affiliateProgram, action: {
interaction.editingOpenAffiliateProgram() interaction.editingOpenAffiliateProgram()
})) }))
items[.botAffiliateProgram]!.append(PeerInfoScreenCommentItem(id: 1, text: "Share a link to \(EnginePeer.user(user).compactDisplayTitle) with your friends and and earn \(formatPermille(starRefProgram.commissionPermille))% of their spending there.")) items[.botAffiliateProgram]!.append(PeerInfoScreenCommentItem(id: 1, text: presentationData.strings.PeerInfo_ItemAffiliateProgram_Footer(EnginePeer.user(user).compactDisplayTitle, formatPermille(starRefProgram.commissionPermille)).string))
} }
} }
} }
@ -1960,14 +1958,13 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
} }
if canSetupRefProgram { if canSetupRefProgram {
//TODO:localize
let programTitleValue: PeerInfoScreenDisclosureItem.Label let programTitleValue: PeerInfoScreenDisclosureItem.Label
if let cachedData = data.cachedData as? CachedUserData, let starRefProgram = cachedData.starRefProgram, starRefProgram.endDate == nil { if let cachedData = data.cachedData as? CachedUserData, let starRefProgram = cachedData.starRefProgram, starRefProgram.endDate == nil {
programTitleValue = .labelBadge("\(formatPermille(starRefProgram.commissionPermille))%") programTitleValue = .labelBadge("\(formatPermille(starRefProgram.commissionPermille))%")
} else { } else {
programTitleValue = .text("Off") programTitleValue = .text(presentationData.strings.PeerInfo_ItemAffiliateProgram_ValueOff)
} }
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliateProgram, label: programTitleValue, additionalBadgeLabel: presentationData.strings.Settings_New, text: "Affiliate Program", icon: PresentationResourcesSettings.affiliateProgram, action: { items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliateProgram, label: programTitleValue, additionalBadgeLabel: presentationData.strings.Settings_New, text: presentationData.strings.PeerInfo_ItemAffiliateProgram_Title, icon: PresentationResourcesSettings.affiliateProgram, action: {
interaction.editingOpenAffiliateProgram() interaction.editingOpenAffiliateProgram()
})) }))
} }
@ -2232,7 +2229,7 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
} }
if canJoinRefProgram { if canJoinRefProgram {
items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliatePrograms, label: .text(""), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Affiliate Programs", icon: PresentationResourcesSettings.affiliateProgram, action: { items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliatePrograms, label: .text(""), additionalBadgeLabel: presentationData.strings.Settings_New, text: presentationData.strings.PeerInfo_ItemAffiliatePrograms_Title, icon: PresentationResourcesSettings.affiliateProgram, action: {
interaction.editingOpenAffiliateProgram() interaction.editingOpenAffiliateProgram()
})) }))
} }
@ -8659,10 +8656,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
guard let self else { guard let self else {
return return
} }
//TODO:localize
UIPasteboard.general.string = result.url UIPasteboard.general.string = result.url
let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }) 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 **\(formatPermille(result.commissionPermille))%** of what people who use it spend in **\(result.peer.compactDisplayTitle)**!"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: presentationData.strings.AffiliateProgram_ToastLinkCopied_Title, text: presentationData.strings.AffiliateProgram_ToastLinkCopied_Text(formatPermille(result.commissionPermille), result.peer.compactDisplayTitle).string), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
} }
)) ))
} else { } else {

View File

@ -404,12 +404,11 @@ private final class StarsTransactionSheetContent: CombinedComponent {
transactionPeer = transaction.peer transactionPeer = transaction.peer
isGift = true isGift = true
} else if let starrefCommissionPermille = transaction.starrefCommissionPermille { } else if let starrefCommissionPermille = transaction.starrefCommissionPermille {
//TODO:localize
isRefProgram = true isRefProgram = true
if transaction.starrefPeerId == nil { if transaction.starrefPeerId == nil {
titleText = "\(formatPermille(starrefCommissionPermille))% Commission" titleText = strings.StarsTransaction_TitleCommission(formatPermille(starrefCommissionPermille)).string
} else { } else {
titleText = transaction.title ?? "Product" titleText = transaction.title ?? " "
} }
descriptionText = "" descriptionText = ""
count = transaction.count count = transaction.count
@ -868,15 +867,14 @@ private final class StarsTransactionSheetContent: CombinedComponent {
} }
if case let .transaction(transaction, _) = subject { if case let .transaction(transaction, _) = subject {
//TODO:localize
if transaction.starrefCommissionPermille != nil { if transaction.starrefCommissionPermille != nil {
if transaction.starrefPeerId == nil { if transaction.starrefPeerId == nil {
tableItems.append(.init( tableItems.append(.init(
id: "reason", id: "reason",
title: "Reason", title: strings.StarsTransaction_StarRefReason_Title,
component: AnyComponent( component: AnyComponent(
Button( Button(
content: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "Affiliate Program", font: tableFont, textColor: tableLinkColor)) content: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.StarsTransaction_StarRefReason_Program, font: tableFont, textColor: tableLinkColor))
)), )),
action: { action: {
if let toPeer { if let toPeer {
@ -894,7 +892,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
if let toPeer, transaction.starrefPeerId == nil { if let toPeer, transaction.starrefPeerId == nil {
tableItems.append(.init( tableItems.append(.init(
id: "miniapp", id: "miniapp",
title: "Mini App", title: strings.StarsTransaction_StarRefReason_Miniapp,
component: AnyComponent( component: AnyComponent(
Button( Button(
content: AnyComponent( content: AnyComponent(
@ -923,10 +921,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
} }
} }
if let starRefPeerId = transaction.starrefPeerId, let starRefPeer = state.peerMap[starRefPeerId] { if let starRefPeerId = transaction.starrefPeerId, let starRefPeer = state.peerMap[starRefPeerId] {
//TODO:localize
tableItems.append(.init( tableItems.append(.init(
id: "to", id: "to",
title: "Affiliate", title: strings.StarsTransaction_StarRefReason_Affiliate,
component: AnyComponent( component: AnyComponent(
Button( Button(
content: AnyComponent( content: AnyComponent(
@ -955,7 +952,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
if let toPeer { if let toPeer {
tableItems.append(.init( tableItems.append(.init(
id: "referred", id: "referred",
title: "Referred User", title: strings.StarsTransaction_StarRefReason_Referred,
component: AnyComponent( component: AnyComponent(
Button( Button(
content: AnyComponent( content: AnyComponent(

View File

@ -679,9 +679,8 @@ final class StarsTransactionsScreenComponent: Component {
header: nil, header: nil,
footer: nil, footer: nil,
items: [ items: [
//TODO:localize
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor( 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: { itemGenerator: ItemListDisclosureItem(presentationData: ItemListPresentationData(presentationData), icon: PresentationResourcesSettings.earnStars, title: environment.strings.Monetization_EarnStarsInfo_Title, titleBadge: presentationData.strings.Settings_New, label: environment.strings.Monetization_EarnStarsInfo_Text, labelStyle: .multilineDetailText, sectionId: 0, style: .blocks, action: {
}), }),
params: ListViewItemLayoutParams(width: availableSize.width, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true), params: ListViewItemLayoutParams(width: availableSize.width, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true),
action: { [weak self] in action: { [weak self] in