From a9ce88255dd69ee514ddea48317cfcb6ec03906c Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 8 Mar 2025 00:01:18 +0400 Subject: [PATCH] Various fixes --- .../Telegram-iOS/en.lproj/Localizable.strings | 4 + .../ContactMultiselectionController.swift | 5 +- .../IncomingMessagePrivacyScreen.swift | 7 +- ...ectivePrivacySettingsPeersController.swift | 5 +- .../Sources/StarsStatisticsScreen.swift | 184 ++++++++++++++---- .../Sources/StarsTransactionsScreen.swift | 110 +++++------ .../Stars/Stats.imageset/Contents.json | 12 ++ .../Premium/Stars/Stats.imageset/stats_24.pdf | Bin 0 -> 3775 bytes .../ContactMultiselectionControllerNode.swift | 3 + 9 files changed, 224 insertions(+), 106 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/stats_24.pdf diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 3df82e28b0..272447cd7e 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -13941,6 +13941,7 @@ Sorry for the inconvenience."; "Stars.Intro.BuyShort" = "Top Up"; "Stars.Intro.Withdraw" = "Withdraw"; +"Stars.Intro.Stats" = "Stats"; "Group.Info.Settings" = "Group Settings"; @@ -13992,3 +13993,6 @@ Sorry for the inconvenience."; "Conversation.VideoTimeLinkCopied" = "Link with start time at %@ copied to clipboard."; "Share.VideoStartAt" = "Start at %@"; "SendStarReactions.SubtitleFrom" = "from %@"; + +"Stars.AccountRevenue.Proceeds.Info" = "Stars from your total balance can be withdrawn as rewards 21 days after they are earned."; +"Stars.AccountRevenue.Withdraw.Info" = "You can collect rewards for Stars using Fragment. You cannot withdraw less than 1000 stars. [Learn More >]()"; diff --git a/submodules/AccountContext/Sources/ContactMultiselectionController.swift b/submodules/AccountContext/Sources/ContactMultiselectionController.swift index f790e0ba78..fc3ea9691c 100644 --- a/submodules/AccountContext/Sources/ContactMultiselectionController.swift +++ b/submodules/AccountContext/Sources/ContactMultiselectionController.swift @@ -48,6 +48,7 @@ public enum ContactMultiselectionControllerMode { public var onlyUsers: Bool public var disableChannels: Bool public var disableBots: Bool + public var disableContacts: Bool public init( title: String, @@ -59,7 +60,8 @@ public enum ContactMultiselectionControllerMode { displayPresence: Bool = false, onlyUsers: Bool = false, disableChannels: Bool = false, - disableBots: Bool = false + disableBots: Bool = false, + disableContacts: Bool = false ) { self.title = title self.searchPlaceholder = searchPlaceholder @@ -71,6 +73,7 @@ public enum ContactMultiselectionControllerMode { self.onlyUsers = onlyUsers self.disableChannels = disableChannels self.disableBots = disableBots + self.disableContacts = disableContacts } } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift index 090e6672ed..4f5ddfc578 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/IncomingMessagePrivacyScreen.swift @@ -281,8 +281,9 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP chatListFilters: nil, onlyUsers: false, disableChannels: true, - disableBots: false - )), filters: [.excludeSelf])) + disableBots: true, + disableContacts: true + )))) addPeerDisposable.set((controller.result |> take(1) |> deliverOnMainQueue).start(next: { [weak controller] result in @@ -342,7 +343,7 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP controller.navigationPresentation = .modal pushControllerImpl?(controller) } else { - let controller = selectivePrivacyPeersController(context: context, title: presentationData.strings.Privacy_Messages_Exceptions_Title, footer: presentationData.strings.Privacy_Messages_RemoveFeeInfo, initialPeers: peerIds, initialEnableForPremium: false, displayPremiumCategory: false, initialEnableForBots: false, displayBotsCategory: false, updated: { updatedPeerIds, _, _ in + let controller = selectivePrivacyPeersController(context: context, title: presentationData.strings.Privacy_Messages_Exceptions_Title, footer: presentationData.strings.Privacy_Messages_RemoveFeeInfo, hideContacts: true, initialPeers: peerIds, initialEnableForPremium: false, displayPremiumCategory: false, initialEnableForBots: false, displayBotsCategory: false, updated: { updatedPeerIds, _, _ in updateState { state in var updatedState = state updatedState.disableFor = updatedPeerIds diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift index 9d79acf509..e18a0ac730 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsPeersController.swift @@ -322,7 +322,7 @@ private func selectivePrivacyPeersControllerEntries(presentationData: Presentati return entries } -public func selectivePrivacyPeersController(context: AccountContext, title: String, footer: String? = nil, initialPeers: [EnginePeer.Id: SelectivePrivacyPeer], initialEnableForPremium: Bool, displayPremiumCategory: Bool, initialEnableForBots: Bool, displayBotsCategory: Bool, updated: @escaping ([EnginePeer.Id: SelectivePrivacyPeer], Bool, Bool) -> Void) -> ViewController { +public func selectivePrivacyPeersController(context: AccountContext, title: String, footer: String? = nil, hideContacts: Bool = false, initialPeers: [EnginePeer.Id: SelectivePrivacyPeer], initialEnableForPremium: Bool, displayPremiumCategory: Bool, initialEnableForBots: Bool, displayBotsCategory: Bool, updated: @escaping ([EnginePeer.Id: SelectivePrivacyPeer], Bool, Bool) -> Void) -> ViewController { let initialState = SelectivePrivacyPeersControllerState(enableForPremium: initialEnableForPremium, enableForBots: initialEnableForBots, editing: false, peerIdWithRevealedOptions: nil) let statePromise = ValuePromise(initialState, ignoreRepeated: true) let stateValue = Atomic(value: initialState) @@ -428,7 +428,8 @@ public func selectivePrivacyPeersController(context: AccountContext, title: Stri chatListFilters: nil, onlyUsers: false, disableChannels: true, - disableBots: false + disableBots: hideContacts, + disableContacts: hideContacts )), alwaysEnabled: true)) addPeerDisposable.set((controller.result |> take(1) diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift index ea9e2ce6c5..626f027cb0 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift @@ -29,6 +29,7 @@ final class StarsStatisticsScreenComponent: Component { let peerId: EnginePeer.Id let revenueContext: StarsRevenueStatsContext let openTransaction: (StarsContext.State.Transaction) -> Void + let buy: () -> Void let withdraw: () -> Void let showTimeoutTooltip: (Int32) -> Void let buyAds: () -> Void @@ -38,6 +39,7 @@ final class StarsStatisticsScreenComponent: Component { peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext, openTransaction: @escaping (StarsContext.State.Transaction) -> Void, + buy: @escaping () -> Void, withdraw: @escaping () -> Void, showTimeoutTooltip: @escaping (Int32) -> Void, buyAds: @escaping () -> Void @@ -46,6 +48,7 @@ final class StarsStatisticsScreenComponent: Component { self.peerId = peerId self.revenueContext = revenueContext self.openTransaction = openTransaction + self.buy = buy self.withdraw = withdraw self.showTimeoutTooltip = showTimeoutTooltip self.buyAds = buyAds @@ -508,7 +511,7 @@ final class StarsStatisticsScreenComponent: Component { )), footer: AnyComponent(MultilineTextComponent( text: .plain(NSAttributedString( - string: strings.Stars_BotRevenue_Proceeds_Info, + string: component.peerId == component.context.account.peerId ? strings.Stars_AccountRevenue_Proceeds_Info : strings.Stars_BotRevenue_Proceeds_Info, font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor )), @@ -558,8 +561,8 @@ final class StarsStatisticsScreenComponent: Component { return (TelegramTextAttributes.URL, contents) }) - let balanceInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(strings.Stars_BotRevenue_Withdraw_Info, attributes: termsMarkdownAttributes, textAlignment: .natural - )) + let balanceRawString = component.peerId == component.context.account.peerId ? strings.Stars_AccountRevenue_Withdraw_Info : strings.Stars_BotRevenue_Withdraw_Info + let balanceInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(balanceRawString, attributes: termsMarkdownAttributes, textAlignment: .natural)) if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== environment.theme { self.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme) } @@ -567,6 +570,96 @@ final class StarsStatisticsScreenComponent: Component { balanceInfoString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: balanceInfoString.string)) } + var balanceItems: [AnyComponentWithIdentity] = [] + if component.peerId == component.context.account.peerId { + let withdrawEnabled = self.starsState?.balances.withdrawEnabled ?? false + balanceItems = [ + AnyComponentWithIdentity(id: 0, component: AnyComponent( + StarsBalanceComponent( + theme: environment.theme, + strings: strings, + dateTimeFormat: environment.dateTimeFormat, + count: self.starsState?.balances.availableBalance ?? StarsAmount.zero, + rate: self.starsState?.usdRate ?? 0, + actionTitle: strings.Stars_Intro_BuyShort, + actionAvailable: true, + actionIsEnabled: true, + actionIcon: PresentationResourcesItemList.itemListRoundTopupIcon(environment.theme), + action: { [weak self] in + guard let self, let component = self.component else { + return + } + component.buy() + }, + secondaryActionTitle: withdrawEnabled ? strings.Stars_Intro_Withdraw : nil, + secondaryActionIcon: PresentationResourcesItemList.itemListRoundWithdrawIcon(environment.theme), + secondaryActionCooldownUntilTimestamp: self.starsState?.balances.nextWithdrawalTimestamp, + secondaryAction: { [weak self] in + guard let self, let component = self.component else { + return + } + var remainingCooldownSeconds: Int32 = 0 + if let cooldownUntilTimestamp = self.starsState?.balances.nextWithdrawalTimestamp { + remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) + remainingCooldownSeconds = max(0, remainingCooldownSeconds) + + if remainingCooldownSeconds > 0 { + component.showTimeoutTooltip(cooldownUntilTimestamp) + } else { + component.withdraw() + } + } else { + component.withdraw() + } + } + ) + )) + ] + } else { + balanceItems = [ + AnyComponentWithIdentity(id: 0, component: AnyComponent( + StarsBalanceComponent( + theme: environment.theme, + strings: strings, + dateTimeFormat: environment.dateTimeFormat, + count: self.starsState?.balances.availableBalance ?? StarsAmount.zero, + rate: self.starsState?.usdRate ?? 0, + actionTitle: strings.Stars_BotRevenue_Withdraw_WithdrawShort, + actionAvailable: true, + actionIsEnabled: self.starsState?.balances.withdrawEnabled ?? true, + actionCooldownUntilTimestamp: self.starsState?.balances.nextWithdrawalTimestamp, + actionIcon: PresentationResourcesItemList.itemListRoundTopupIcon(environment.theme), + action: { [weak self] in + guard let self, let component = self.component else { + return + } + var remainingCooldownSeconds: Int32 = 0 + if let cooldownUntilTimestamp = self.starsState?.balances.nextWithdrawalTimestamp { + remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) + remainingCooldownSeconds = max(0, remainingCooldownSeconds) + + if remainingCooldownSeconds > 0 { + component.showTimeoutTooltip(cooldownUntilTimestamp) + } else { + component.withdraw() + } + } else { + component.withdraw() + } + }, + secondaryActionTitle: strings.Stars_BotRevenue_Withdraw_BuyAds, + secondaryActionIcon: PresentationResourcesItemList.itemListRoundWithdrawIcon(environment.theme), + secondaryAction: { [weak self] in + guard let self, let component = self.component else { + return + } + component.buyAds() + } + ) + )) + ] + } + let balanceSize = self.balanceView.update( transition: .immediate, component: AnyComponent(ListSectionComponent( @@ -597,44 +690,7 @@ final class StarsStatisticsScreenComponent: Component { } } )), - items: [AnyComponentWithIdentity(id: 0, component: AnyComponent( - StarsBalanceComponent( - theme: environment.theme, - strings: strings, - dateTimeFormat: environment.dateTimeFormat, - count: self.starsState?.balances.availableBalance ?? StarsAmount.zero, - rate: self.starsState?.usdRate ?? 0, - actionTitle: strings.Stars_BotRevenue_Withdraw_WithdrawShort, - actionAvailable: true, - actionIsEnabled: self.starsState?.balances.withdrawEnabled ?? true, - actionCooldownUntilTimestamp: self.starsState?.balances.nextWithdrawalTimestamp, - action: { [weak self] in - guard let self, let component = self.component else { - return - } - var remainingCooldownSeconds: Int32 = 0 - if let cooldownUntilTimestamp = self.starsState?.balances.nextWithdrawalTimestamp { - remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) - remainingCooldownSeconds = max(0, remainingCooldownSeconds) - - if remainingCooldownSeconds > 0 { - component.showTimeoutTooltip(cooldownUntilTimestamp) - } else { - component.withdraw() - } - } else { - component.withdraw() - } - }, - secondaryActionTitle: strings.Stars_BotRevenue_Withdraw_BuyAds, - secondaryAction: { [weak self] in - guard let self, let component = self.component else { - return - } - component.buyAds() - } - ) - ))] + items: balanceItems )), environment: {}, containerSize: CGSize(width: availableSize.width - sideInsets, height: availableSize.height) @@ -799,11 +855,14 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { private weak var tooltipScreen: UndoOverlayController? private var timer: Foundation.Timer? + private let options = Promise<[StarsTopUpOption]>() + public init(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) { self.context = context self.peerId = peerId self.revenueContext = revenueContext + var buyImpl: (() -> Void)? var withdrawImpl: (() -> Void)? var buyAdsImpl: (() -> Void)? var showTimeoutTooltipImpl: ((Int32) -> Void)? @@ -815,6 +874,9 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { openTransaction: { transaction in openTransactionImpl?(transaction) }, + buy: { + buyImpl?() + }, withdraw: { withdrawImpl?() }, @@ -842,6 +904,46 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { }) } + if peerId == context.account.peerId { + self.options.set(.single([]) |> then(context.engine.payments.starsTopUpOptions())) + } + + buyImpl = { [weak self] in + guard let self else { + return + } + let _ = (self.options.get() + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] options in + guard let self, let starsContext = context.starsContext else { + return + } + let controller = context.sharedContext.makeStarsPurchaseScreen(context: context, starsContext: starsContext, options: options, purpose: .generic, completion: { [weak self] stars in + guard let self else { + return + } + starsContext.add(balance: StarsAmount(value: stars, nanos: 0)) + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let resultController = UndoOverlayController( + presentationData: presentationData, + content: .universal( + animation: "StarsBuy", + scale: 0.066, + colors: [:], + title: presentationData.strings.Stars_Intro_PurchasedTitle, + text: presentationData.strings.Stars_Intro_PurchasedText(presentationData.strings.Stars_Intro_PurchasedText_Stars(Int32(stars))).string, + customUndoText: nil, + timeout: nil + ), + elevatedLayout: false, + action: { _ in return true}) + self.present(resultController, in: .window(.root)) + }) + self.push(controller) + }) + } + withdrawImpl = { [weak self] in guard let self else { return diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift index 6894f31d53..7bfd83869e 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift @@ -630,7 +630,7 @@ final class StarsTransactionsScreenComponent: Component { contentHeight += descriptionSize.height contentHeight += 29.0 - let withdrawAvailable = self.revenueState?.balances.withdrawEnabled ?? false + let withdrawAvailable = (self.revenueState?.balances.overallRevenue.value ?? 0) > 0 let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) let balanceSize = self.balanceView.update( @@ -656,26 +656,14 @@ final class StarsTransactionsScreenComponent: Component { } component.buy() }, - secondaryActionTitle: withdrawAvailable ? environment.strings.Stars_Intro_Withdraw : nil, - secondaryActionIcon: withdrawAvailable ? PresentationResourcesItemList.itemListRoundWithdrawIcon(environment.theme) : nil, + secondaryActionTitle: withdrawAvailable ? environment.strings.Stars_Intro_Stats : nil, + secondaryActionIcon: withdrawAvailable ? PresentationResourcesItemList.itemListStatsIcon(environment.theme) : nil, secondaryActionCooldownUntilTimestamp: self.revenueState?.balances.nextWithdrawalTimestamp, secondaryAction: withdrawAvailable ? { [weak self] in guard let self, let component = self.component else { return } - var remainingCooldownSeconds: Int32 = 0 - if let cooldownUntilTimestamp = self.revenueState?.balances.nextWithdrawalTimestamp { - remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) - remainingCooldownSeconds = max(0, remainingCooldownSeconds) - - if remainingCooldownSeconds > 0 { - component.showTimeoutTooltip(cooldownUntilTimestamp) - } else { - component.withdraw() - } - } else { - component.withdraw() - } + component.withdraw() } : nil, additionalAction: (premiumConfiguration.starsGiftsPurchaseAvailable && !premiumConfiguration.isPremiumDisabled) ? AnyComponent( Button( @@ -739,7 +727,7 @@ final class StarsTransactionsScreenComponent: Component { return } let _ = (component.context.sharedContext.makeAffiliateProgramSetupScreenInitialData(context: component.context, peerId: component.context.account.peerId, mode: .connectedPrograms) - |> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in + |> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in guard let self, let component = self.component else { return } @@ -1236,48 +1224,52 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer { guard let self else { return } - let _ = (context.engine.peers.checkStarsRevenueWithdrawalAvailability() - |> deliverOnMainQueue).start(error: { [weak self] error in - guard let self else { - return - } - switch error { - case .serverProvided: - return - case .requestPassword: - let _ = (self.starsRevenueStatsContext.state - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] state in - guard let self else { - return - } - let controller = self.context.sharedContext.makeStarsWithdrawalScreen(context: context, completion: { [weak self] amount in - guard let self else { - return - } - let controller = confirmStarsRevenueWithdrawalController(context: context, peerId: context.account.peerId, amount: amount, present: { [weak self] c, a in - self?.present(c, in: .window(.root)) - }, completion: { [weak self] url in - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {}) - - Queue.mainQueue().after(2.0) { - self?.starsRevenueStatsContext.reload() - } - }) - self.present(controller, in: .window(.root)) - }) - self.push(controller) - }) - default: - let controller = starsRevenueWithdrawalController(context: context, peerId: context.account.peerId, amount: 0, initialError: error, present: { [weak self] c, a in - self?.present(c, in: .window(.root)) - }, completion: { _ in - - }) - self.present(controller, in: .window(.root)) - } - }) + + let controller = self.context.sharedContext.makeStarsStatisticsScreen(context: context, peerId: context.account.peerId, revenueContext: self.starsRevenueStatsContext) + self.push(controller) + +// let _ = (context.engine.peers.checkStarsRevenueWithdrawalAvailability() +// |> deliverOnMainQueue).start(error: { [weak self] error in +// guard let self else { +// return +// } +// switch error { +// case .serverProvided: +// return +// case .requestPassword: +// let _ = (self.starsRevenueStatsContext.state +// |> take(1) +// |> deliverOnMainQueue).start(next: { [weak self] state in +// guard let self else { +// return +// } +// let controller = self.context.sharedContext.makeStarsWithdrawalScreen(context: context, completion: { [weak self] amount in +// guard let self else { +// return +// } +// let controller = confirmStarsRevenueWithdrawalController(context: context, peerId: context.account.peerId, amount: amount, present: { [weak self] c, a in +// self?.present(c, in: .window(.root)) +// }, completion: { [weak self] url in +// let presentationData = context.sharedContext.currentPresentationData.with { $0 } +// context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {}) +// +// Queue.mainQueue().after(2.0) { +// self?.starsRevenueStatsContext.reload() +// } +// }) +// self.present(controller, in: .window(.root)) +// }) +// self.push(controller) +// }) +// default: +// let controller = starsRevenueWithdrawalController(context: context, peerId: context.account.peerId, amount: 0, initialError: error, present: { [weak self] c, a in +// self?.present(c, in: .window(.root)) +// }, completion: { _ in +// +// }) +// self.present(controller, in: .window(.root)) +// } +// }) } showTimeoutTooltipImpl = { [weak self] cooldownUntilTimestamp in diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/Contents.json new file mode 100644 index 0000000000..a243b957ee --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "stats_24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/stats_24.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Stars/Stats.imageset/stats_24.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c054ff1aa737d2f9b26cca187bfdf095fa86a060 GIT binary patch literal 3775 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