Update localization

This commit is contained in:
Ilya Laktyushin 2023-10-25 06:21:02 +04:00
parent 983e720453
commit d23e67a62d
17 changed files with 443 additions and 263 deletions

View File

@ -10151,3 +10151,150 @@ Sorry for the inconvenience.";
"Stats.Boosts.ShowMoreBoosts_1" = "Show %@ More Boost"; "Stats.Boosts.ShowMoreBoosts_1" = "Show %@ More Boost";
"Stats.Boosts.ShowMoreBoosts_any" = "Show %@ More Boosts"; "Stats.Boosts.ShowMoreBoosts_any" = "Show %@ More Boosts";
"ReassignBoost.Title" = "Reassign Boosts";
"ReassignBoost.Description" = "To boost **%1$@**, reassign a previous boost or gift **Telegram Premium** to a friend to get **%2$@** additional boosts.";
"ReassignBoost.ReassignBoosts" = "Reassign Boosts";
"ReassignBoost.AvailableIn" = "Available in %@";
"ReassignBoost.ExpiresOn" = "Boost expires on %@";
"ReassignBoost.WaitForCooldown" = "Wait until the boost is available or get **%1$@** more boosts by gifting a **Telegram Premium** subscription.";
"ReassignBoost.Success" = "Wait until the boost is available or get **%1$@** more boosts by gifting a **Telegram Premium** subscription.";
//"\(replacedBoosts) boosts are reassigned from \(inChannels) other channel."
"ChannelBoost.MoreBoosts.Title" = "More Boosts Needed";
"ChannelBoost.MoreBoosts.Text" = "To boost **%1$@** again, gift **Telegram Premium** to a friend and get **%2$@** additional boosts.";
"BoostGift.Title" = "Boosts via Gifts";
"BoostGift.Description" = "Get more boosts for your channel by gifting\nPremium to your subscribers.";
"BoostGift.PrepaidGiveawayTitle" = "PREPAID GIVEAWAY";
"BoostGift.PrepaidGiveawayCount_1" = "%@ Telegram Premium";
"BoostGift.PrepaidGiveawayCount_any" = "%@ Telegram Premium";
"BoostGift.PrepaidGiveawayMonths" = "%@-month subscriptions";
"BoostGift.CreateGiveaway" = "Create Giveaway";
"BoostGift.CreateGiveawayInfo" = "winners are chosen randomly";
"BoostGift.AwardSpecificUsers" = "Award Specific Users";
"BoostGift.SelectRecipients" = "select recipients";
"BoostGift.QuantityTitle" = "QUANTITY OF PRIZES";
"BoostGift.QuantityBoosts_1" = "%@ BOOST";
"BoostGift.QuantityBoosts_any" = "%@ BOOSTS";
"BoostGift.QuantityInfo" = "Choose how many Premium subscriptions to give away and boosts to receive.";
"BoostGift.ChannelsTitle" = "CHANNELS INCLUDED IN THE GIVEAWAY";
"BoostGift.AddChannel" = "Add Channel";
"BoostGift.ChannelsBoosts_1" = "this channel will receive %@ boost";
"BoostGift.ChannelsBoosts_any" = "this channel will receive %@ boosts";
"BoostGift.ChannelsInfo" = "Choose the channels users need to be subscribed to take part in the giveaway";
"BoostGift.UsersTitle" = "USERS ELIGIBLE FOR THE GIVEAWAY";
"BoostGift.FromCountries_1" = "from %@ country";
"BoostGift.FromCountries_any" = "from %@ countries";
"BoostGift.FromTwoCountries" = "from %1$@ and %2$@";
"BoostGift.FromOneCountry" = "from %1$@";
"BoostGift.FromAllCountries" = "from all countries";
"BoostGift.AllSubscribers" = "All subscribers";
"BoostGift.OnlyNewSubscribers" = "Only new subscribers";
"BoostGift.LimitSubscribersInfo" = "Choose if you want to limit the giveaway only to those who joined the channel after the giveaway started.";
"BoostGift.DateTitle" = "DATE WHEN GIVEAWAY ENDS";
"BoostGift.DateEnds" = "Ends";
"BoostGift.DateInfo" = "Choose when %1$@ of your channel will be randomly selected to receive Telegram Premium.";
"BoostGift.DateInfoSubscribers_1" = "%@ subscriber";
"BoostGift.DateInfoSubscribers_any" = "%@ subscribers";
"BoostGift.DurationTitle" = "DURATION OF PREMIUM SUBSCRIPTIONS";
"BoostGift.PremiumInfo" = "You can review the list of features and terms of use for Telegram Premium [here]().";
"BoostGift.GiftPremium" = "Gift Premium";
"BoostGift.StartGiveaway" = "Start Giveaway";
"BoostGift.ReduceQuantity.Title" = "Reduce Quantity";
"BoostGift.ReduceQuantity.Text" = "You can't acquire %1$@ %2$@-month subscriptions in the app. Do you want to reduce quantity to %3$@?";
"BoostGift.ReduceQuantity.Reduce" = "Reduce";
"BoostGift.GiveawayCreated.Title" = "Giveaway Created";
"BoostGift.GiveawayCreated.Text" = "Check your channel's [Statistics]() to see how this giveaway boosted your channel.";
"BoostGift.PremiumGifted.Title" = "Premium Subscriptions Gifted";
"BoostGift.PremiumGifted.Text" = "Check your channel's [Statistics]() to see how gifts boosted your channel.";
"BoostGift.Subscribers.Title" = "Gift Premium";
"BoostGift.Subscribers.Subtitle" = "select up to %@ subscribers";
"BoostGift.Subscribers.SectionTitle" = "SUBSCRIBERS";
"BoostGift.Subscribers.Joined" = "joined %@";
"BoostGift.Subscribers.Search" = "Search Subscribers";
"BoostGift.Subscribers.MaximumReached" = "You can select maximum %@ subscribers.";
"BoostGift.Subscribers.Save" = "Save Recipients";
"BoostGift.Channels.Title" = "Add Channels";
"BoostGift.Channels.Subtitle" = "select up to %@ channels";
"BoostGift.Channels.SectionTitle" = "CHANNELS";
"BoostGift.Channels.Search" = "Search Channels";
"BoostGift.Channels.MaximumReached" = "You can select maximum %@ channels.";
"BoostGift.Channels.PrivateChannel.Title" = "Channel is Private";
"BoostGift.Channels.PrivateChannel.Text" = "Are you sure you want to add a private channel? Users won't be able to join it without an invite link.";
"BoostGift.Channels.PrivateChannel.Add" = "Add";
"BoostGift.Channels.Save" = "Save Channels";
"Stats.Boosts.PrepaidGiveawaysTitle" = "PREPAID GIVEAWAYS";
"Stats.Boosts.PrepaidGiveawayCount_any" = "%@ Telegram Premium";
"Stats.Boosts.PrepaidGiveawayMonths" = "%@-month subscriptions";
"Stats.Boosts.PrepaidGiveawaysInfo" = "Select a giveaway you already paid for to set it up.";
"Stats.Boosts.ShortMonth" = "%@m";
"Stats.Boosts.Giveaway" = "Giveaway";
"Stats.Boosts.Gift" = "Gift";
"Stats.Boosts.TabBoosts_1" = "%@ Boost";
"Stats.Boosts.TabBoosts_any" = "%@ Boosts";
"Stats.Boosts.TabGifts_1" = "%@ Boost";
"Stats.Boosts.TabGifts_any" = "%@ Boosts";
"Stats.Boosts.ToBeDistributed" = "To Be Distributed";
"Stats.Boosts.Unclaimed" = "Unclaimed";
"Stats.Boosts.GetBoosts" = "Get Boosts via Gifts";
"Stats.Boosts.GetBoostsInfo" = "Get more boosts for your channel by gifting Premium to your subscribers.";
"Notification.PremiumPrize.Title" = "Congratulations!";
"Notification.PremiumPrize.UnclaimedText" = "You have an unclaimed prize from a giveaway by **%1$@**.\n\nThis prize is a **Telegram Premium** subscription for %2$@.";
"Notification.PremiumPrize.GiveawayText" = "You won a prize in a giveaway organized by **%1$@**.\n\nYour prize is a **Telegram Premium** subscription for %2$@.";
"Notification.PremiumPrize.GiftText" = "You've received a gift from **%1$@**.\n\nYour gift is a **Telegram Premium** subscription for %2$@.";
"Notification.PremiumPrize.Months_1" = "**%@** month";
"Notification.PremiumPrize.Months_any" = "**%@** months";
"Notification.PremiumPrize.View" = "Open Gift Link";
"Notification.PremiumPrize.Unclaimed" = "Unclaimed Prize";
"Story.SlideToSeek" = "Slide left or right to seek";
"Story.Guide.Title" = "Watching Stories";
"Story.Guide.Description" = "You can use these gestures to control playback.";
"Story.Guide.ForwardTitle" = "Go forward";
"Story.Guide.ForwardDescription" = "Tap the screen";
"Story.Guide.PauseTitle" = "Pause and Seek";
"Story.Guide.PauseDescription" = "Hold and move sideways";
"Story.Guide.BackTitle" = "Go back";
"Story.Guide.BackDescription" = "Tap the left edge";
"Story.Guide.MoveTitle" = "Move between stories";
"Story.Guide.MoveDescription" = "Swipe left or right";
"Story.Guide.Proceed" = "Tap to keep watching";
"Chat.Giveaway.Info.Title" = "About This Giveaway";
"Chat.Giveaway.Info.EndedTitle" = "Giveaway Ended";
"Chat.Giveaway.Info.AlmostOver" = "The giveaway is almost over.";
"Chat.Giveaway.Info.Subscriptions_1" = "**%@ Telegram Premium** subscription";
"Chat.Giveaway.Info.Subscriptions_any" = "**%@ Telegram Premium** subscriptions";
"Chat.Giveaway.Info.RandomUsers_1" = "**%@** random user";
"Chat.Giveaway.Info.RandomUsers_any" = "**%@** random user";
"Chat.Giveaway.Info.RandomSubscribers_1" = "**%@** random subscriber";
"Chat.Giveaway.Info.RandomSubscribers_any" = "**%@** random subscribers";
"Chat.Giveaway.Info.Months_1" = "**%@** month";
"Chat.Giveaway.Info.Months_any" = "**%@** months";
"Chat.Giveaway.Info.ActivatedLinks_1" = "%@ winner already used their gift link.";
"Chat.Giveaway.Info.ActivatedLinks_any" = "%@ of the winners already used their gift links.";
"Chat.Giveaway.Info.Refunded" = "The channel cancelled the prizes by reversing the payment for them.";
"Chat.Giveaway.Info.Won" = "You won a prize in this giveaway. %@";
"Chat.Giveaway.Info.DidntWin" = "You didn't win a prize in this giveaway.";
"Chat.Giveaway.Info.ViewPrize" = "View My Prize";
"Chat.Giveaway.Toast.NotAllowed" = "You can't participate in this giveaway.";
"Chat.Giveaway.Toast.Participating" = "You are participating in this giveaway.";
"Chat.Giveaway.Toast.NotQualified" = "You are not qualified for this giveaway yet.";
"Chat.Giveaway.Toast.AlmostOver" = "The giveaway is almost over.";
"Chat.Giveaway.Toast.Ended" = "The giveaway is ended.";
"Chat.Giveaway.Toast.LearnMore" = "Learn More";

View File

@ -109,6 +109,7 @@ swift_library(
"//submodules/CountrySelectionUI", "//submodules/CountrySelectionUI",
"//submodules/TelegramUI/Components/Stories/PeerListItemComponent", "//submodules/TelegramUI/Components/Stories/PeerListItemComponent",
"//submodules/InvisibleInkDustNode", "//submodules/InvisibleInkDustNode",
"//submodules/AlertUI",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -377,8 +377,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
case let .channelsHeader(_, text): case let .channelsHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .channel(_, _, peer, boosts, isRevealed): case let .channel(_, _, peer, boosts, isRevealed):
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: presentationData.nameDisplayOrder, context: arguments.context, peer: peer, presence: nil, text: boosts.flatMap { .text("this channel will receive \($0) boosts", .secondary) } ?? .none, label: .none, editing: ItemListPeerItemEditing(editable: boosts == nil, editing: false, revealed: isRevealed), switchValue: nil, enabled: true, selectable: peer.id != arguments.context.account.peerId, sectionId: self.section, action: { return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: presentationData.nameDisplayOrder, context: arguments.context, peer: peer, presence: nil, text: boosts.flatMap { .text(presentationData.strings.BoostGift_ChannelsBoosts($0), .secondary) } ?? .none, label: .none, editing: ItemListPeerItemEditing(editable: boosts == nil, editing: false, revealed: isRevealed), switchValue: nil, enabled: true, selectable: peer.id != arguments.context.account.peerId, sectionId: self.section, action: {
// arguments.openPeer(peer)
}, setPeerIdWithRevealedOptions: { lhs, rhs in }, setPeerIdWithRevealedOptions: { lhs, rhs in
arguments.setItemIdWithRevealedOptions(lhs, rhs) arguments.setItemIdWithRevealedOptions(lhs, rhs)
}, removePeer: { id in }, removePeer: { id in
@ -433,7 +432,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
} else { } else {
text = presentationData.strings.InviteLink_Create_TimeLimitExpiryDateNever text = presentationData.strings.InviteLink_Create_TimeLimitExpiryDateNever
} }
return ItemListDisclosureItem(presentationData: presentationData, title: "Ends", label: text, labelStyle: active ? .coloredText(theme.list.itemAccentColor) : .text, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: { return ItemListDisclosureItem(presentationData: presentationData, title: presentationData.strings.BoostGift_DateEnds, label: text, labelStyle: active ? .coloredText(theme.list.itemAccentColor) : .text, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: {
arguments.dismissInput() arguments.dismissInput()
var focus = false var focus = false
arguments.updateState { state in arguments.updateState { state in
@ -515,7 +514,7 @@ private func createGiveawayControllerEntries(
switch subject { switch subject {
case .generic: case .generic:
entries.append(.createGiveaway(presentationData.theme, "Create Giveaway", "winners are chosen randomly", state.mode == .giveaway)) entries.append(.createGiveaway(presentationData.theme, presentationData.strings.BoostGift_CreateGiveaway, presentationData.strings.BoostGift_CreateGiveawayInfo, state.mode == .giveaway))
let recipientsText: String let recipientsText: String
if !state.peers.isEmpty { if !state.peers.isEmpty {
@ -533,22 +532,22 @@ private func createGiveawayControllerEntries(
recipientsText = presentationData.strings.PremiumGift_LabelRecipients(Int32(peersCount)) recipientsText = presentationData.strings.PremiumGift_LabelRecipients(Int32(peersCount))
} }
} else { } else {
recipientsText = "select recipients" recipientsText = presentationData.strings.BoostGift_SelectRecipients
} }
entries.append(.awardUsers(presentationData.theme, "Award Specific Users", recipientsText, state.mode == .gift)) entries.append(.awardUsers(presentationData.theme, presentationData.strings.BoostGift_AwardSpecificUsers, recipientsText, state.mode == .gift))
case let .prepaid(prepaidGiveaway): case let .prepaid(prepaidGiveaway):
entries.append(.prepaidHeader(presentationData.theme, "PREPAID GIVEAWAY")) entries.append(.prepaidHeader(presentationData.theme, presentationData.strings.BoostGift_PrepaidGiveawayTitle))
entries.append(.prepaid(presentationData.theme, "\(prepaidGiveaway.quantity) Telegram Premium", "\(prepaidGiveaway.months)-month subscriptions", prepaidGiveaway)) entries.append(.prepaid(presentationData.theme, presentationData.strings.BoostGift_PrepaidGiveawayCount(prepaidGiveaway.quantity), presentationData.strings.BoostGift_PrepaidGiveawayMonths("\(prepaidGiveaway.months)").string, prepaidGiveaway))
} }
if case .giveaway = state.mode { if case .giveaway = state.mode {
if case .generic = subject { if case .generic = subject {
entries.append(.subscriptionsHeader(presentationData.theme, "QUANTITY OF PRIZES".uppercased(), "\(state.subscriptions * 4) BOOSTS")) entries.append(.subscriptionsHeader(presentationData.theme, presentationData.strings.BoostGift_QuantityTitle.uppercased(), presentationData.strings.BoostGift_QuantityBoosts(state.subscriptions * 4)))
entries.append(.subscriptions(presentationData.theme, state.subscriptions)) entries.append(.subscriptions(presentationData.theme, state.subscriptions))
entries.append(.subscriptionsInfo(presentationData.theme, "Choose how many Premium subscriptions to give away and boosts to receive.")) entries.append(.subscriptionsInfo(presentationData.theme, presentationData.strings.BoostGift_QuantityInfo))
} }
entries.append(.channelsHeader(presentationData.theme, "CHANNELS INCLUDED IN THE GIVEAWAY".uppercased())) entries.append(.channelsHeader(presentationData.theme, presentationData.strings.BoostGift_ChannelsTitle.uppercased()))
var index: Int32 = 0 var index: Int32 = 0
let channels = [peerId] + state.channels let channels = [peerId] + state.channels
for channelId in channels { for channelId in channels {
@ -557,35 +556,44 @@ private func createGiveawayControllerEntries(
} }
index += 1 index += 1
} }
entries.append(.channelAdd(presentationData.theme, "Add Channel")) entries.append(.channelAdd(presentationData.theme, presentationData.strings.BoostGift_AddChannel))
entries.append(.channelsInfo(presentationData.theme, "Choose the channels users need to be subscribed to take part in the giveaway")) entries.append(.channelsInfo(presentationData.theme, presentationData.strings.BoostGift_ChannelsInfo))
entries.append(.usersHeader(presentationData.theme, "USERS ELIGIBLE FOR THE GIVEAWAY".uppercased())) entries.append(.usersHeader(presentationData.theme, presentationData.strings.BoostGift_UsersTitle.uppercased()))
let countriesText: String let countriesText: String
if state.countries.count > 2 { if state.countries.count > 2 {
countriesText = "from \(state.countries.count) countries" countriesText = presentationData.strings.BoostGift_FromCountries(Int32(state.countries.count))
} else if !state.countries.isEmpty { } else if !state.countries.isEmpty {
let allCountries = state.countries.map { locale.localizedString(forRegionCode: $0) ?? $0 }.joined(separator: " and ") if state.countries.count == 2 {
countriesText = "from \(allCountries)" let firstCountryCode = state.countries.first ?? ""
let secondCountryCode = state.countries.last ?? ""
let firstCountryName = locale.localizedString(forRegionCode: firstCountryCode) ?? firstCountryCode
let secondCountryName = locale.localizedString(forRegionCode: secondCountryCode) ?? secondCountryCode
countriesText = presentationData.strings.BoostGift_FromTwoCountries(firstCountryName, secondCountryName).string
} else {
let countryCode = state.countries.first ?? ""
let countryName = locale.localizedString(forRegionCode: countryCode) ?? countryCode
countriesText = presentationData.strings.BoostGift_FromOneCountry(countryName).string
}
} else { } else {
countriesText = "from all countries" countriesText = presentationData.strings.BoostGift_FromAllCountries
} }
entries.append(.usersAll(presentationData.theme, "All subscribers", countriesText, !state.onlyNewEligible)) entries.append(.usersAll(presentationData.theme, presentationData.strings.BoostGift_AllSubscribers, countriesText, !state.onlyNewEligible))
entries.append(.usersNew(presentationData.theme, "Only new subscribers", countriesText, state.onlyNewEligible)) entries.append(.usersNew(presentationData.theme, presentationData.strings.BoostGift_OnlyNewSubscribers, countriesText, state.onlyNewEligible))
entries.append(.usersInfo(presentationData.theme, "Choose if you want to limit the giveaway only to those who joined the channel after the giveaway started.")) entries.append(.usersInfo(presentationData.theme, presentationData.strings.BoostGift_LimitSubscribersInfo))
entries.append(.timeHeader(presentationData.theme, "DATE WHEN GIVEAWAY ENDS".uppercased())) entries.append(.timeHeader(presentationData.theme, presentationData.strings.BoostGift_DateTitle.uppercased()))
entries.append(.timeExpiryDate(presentationData.theme, presentationData.dateTimeFormat, state.time, state.pickingTimeLimit)) entries.append(.timeExpiryDate(presentationData.theme, presentationData.dateTimeFormat, state.time, state.pickingTimeLimit))
if state.pickingTimeLimit { if state.pickingTimeLimit {
entries.append(.timeCustomPicker(presentationData.theme, presentationData.dateTimeFormat, state.time, minDate, maxDate)) entries.append(.timeCustomPicker(presentationData.theme, presentationData.dateTimeFormat, state.time, minDate, maxDate))
} }
entries.append(.timeInfo(presentationData.theme, "Choose when \(state.subscriptions) subscribers of your channel will be randomly selected to receive Telegram Premium.")) entries.append(.timeInfo(presentationData.theme, presentationData.strings.BoostGift_DateInfo(presentationData.strings.BoostGift_DateInfoSubscribers(Int32(state.subscriptions))).string))
} }
if case .generic = subject { if case .generic = subject {
entries.append(.durationHeader(presentationData.theme, "DURATION OF PREMIUM SUBSCRIPTIONS".uppercased())) entries.append(.durationHeader(presentationData.theme, presentationData.strings.BoostGift_DurationTitle.uppercased()))
let recipientCount: Int let recipientCount: Int
switch state.mode { switch state.mode {
@ -628,7 +636,7 @@ private func createGiveawayControllerEntries(
i += 1 i += 1
} }
entries.append(.durationInfo(presentationData.theme, "You can review the list of features and terms of use for Telegram Premium [here]().")) entries.append(.durationInfo(presentationData.theme, presentationData.strings.BoostGift_PremiumInfo))
} }
return entries return entries
@ -776,7 +784,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
let (state, peersMap) = stateAndPeersMap let (state, peersMap) = stateAndPeersMap
let headerItem = CreateGiveawayHeaderItem(theme: presentationData.theme, title: "Boosts via Gifts", text: "Get more boosts for your channel by gifting\nPremium to your subscribers.", cancel: { let headerItem = CreateGiveawayHeaderItem(theme: presentationData.theme, strings: presentationData.strings, title: presentationData.strings.BoostGift_Title, text: presentationData.strings.BoostGift_Description, cancel: {
dismissImpl?() dismissImpl?()
}) })
@ -787,7 +795,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
case .gift: case .gift:
badgeCount = Int32(state.peers.count) badgeCount = Int32(state.peers.count)
} }
let footerItem = CreateGiveawayFooterItem(theme: presentationData.theme, title: state.mode == .gift ? "Gift Premium" : "Start Giveaway", badgeCount: badgeCount, isLoading: state.updating, action: { let footerItem = CreateGiveawayFooterItem(theme: presentationData.theme, title: state.mode == .gift ? presentationData.strings.BoostGift_GiftPremium : presentationData.strings.BoostGift_StartGiveaway, badgeCount: badgeCount, isLoading: state.updating, action: {
buyActionImpl?() buyActionImpl?()
}) })
let leftNavigationButton = ItemListNavigationButton(content: .none, style: .regular, enabled: false, action: {}) let leftNavigationButton = ItemListNavigationButton(content: .none, style: .regular, enabled: false, action: {})
@ -857,7 +865,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
} }
guard let selectedProduct else { guard let selectedProduct else {
let alertController = textAlertController(context: context, title: "Reduce Quantity", text: "You can't acquire \(state.subscriptions) \(selectedMonths)-month subscriptions in the app. Do you want to reduce quantity to 25?", actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: "Reduce", action: { let alertController = textAlertController(context: context, title: presentationData.strings.BoostGift_ReduceQuantity_Title, text: presentationData.strings.BoostGift_ReduceQuantity_Text("\(state.subscriptions)", "\(selectedMonths)", "\(25)").string, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.BoostGift_ReduceQuantity_Reduce, action: {
updateState { state in updateState { state in
var updatedState = state var updatedState = state
updatedState.subscriptions = 25 updatedState.subscriptions = 25
@ -915,11 +923,11 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
let text: String let text: String
switch state.mode { switch state.mode {
case .giveaway: case .giveaway:
title = "Giveaway Created" title = presentationData.strings.BoostGift_GiveawayCreated_Title
text = "Check your channel's [Statistics]() to see how this giveaway boosted your channel." text = presentationData.strings.BoostGift_GiveawayCreated_Text
case .gift: case .gift:
title = "Premium Subscriptions Gifted" title = presentationData.strings.BoostGift_PremiumGifted_Title
text = "Check your channel's [Statistics]() to see how gifts boosted your channel." text = presentationData.strings.BoostGift_PremiumGifted_Text
} }
let tooltipController = UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: title, text: text, customUndoText: nil, timeout: nil, linkAction: { [weak navigationController] _ in let tooltipController = UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: title, text: text, customUndoText: nil, timeout: nil, linkAction: { [weak navigationController] _ in
@ -992,8 +1000,8 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
controllers.removeLast(count) controllers.removeLast(count)
navigationController.setViewControllers(controllers, animated: true) navigationController.setViewControllers(controllers, animated: true)
let title = "Giveaway Created" let title = presentationData.strings.BoostGift_GiveawayCreated_Title
let text = "Check your channel's [Statistics]() to see how this giveaway boosted your channel." let text = presentationData.strings.BoostGift_GiveawayCreated_Text
let tooltipController = UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: title, text: text, customUndoText: nil, timeout: nil, linkAction: { [weak navigationController] _ in let tooltipController = UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: title, text: text, customUndoText: nil, timeout: nil, linkAction: { [weak navigationController] _ in
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: peerId)) let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: peerId))

View File

@ -11,12 +11,14 @@ import ComponentFlow
final class CreateGiveawayHeaderItem: ItemListControllerHeaderItem { final class CreateGiveawayHeaderItem: ItemListControllerHeaderItem {
let theme: PresentationTheme let theme: PresentationTheme
let strings: PresentationStrings
let title: String let title: String
let text: String let text: String
let cancel: () -> Void let cancel: () -> Void
init(theme: PresentationTheme, title: String, text: String, cancel: @escaping () -> Void) { init(theme: PresentationTheme, strings: PresentationStrings, title: String, text: String, cancel: @escaping () -> Void) {
self.theme = theme self.theme = theme
self.strings = strings
self.title = title self.title = title
self.text = text self.text = text
self.cancel = cancel self.cancel = cancel
@ -132,7 +134,7 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
self.titleNode.attributedText = attributedTitle self.titleNode.attributedText = attributedTitle
self.textNode.attributedText = attributedText self.textNode.attributedText = attributedText
self.cancelNode.setAttributedTitle(NSAttributedString(string: "Cancel", font: Font.regular(17.0), textColor: self.item.theme.rootController.navigationBar.accentTextColor), for: .normal) self.cancelNode.setAttributedTitle(NSAttributedString(string: self.item.strings.Common_Cancel, font: Font.regular(17.0), textColor: self.item.theme.rootController.navigationBar.accentTextColor), for: .normal)
} }
override func updateContentOffset(_ contentOffset: CGFloat, transition: ContainedViewLayoutTransition) { override func updateContentOffset(_ contentOffset: CGFloat, transition: ContainedViewLayoutTransition) {

View File

@ -4,12 +4,161 @@ import AsyncDisplayKit
import Display import Display
import SwiftSignalKit import SwiftSignalKit
import TelegramCore import TelegramCore
import TelegramPresentationData
import TextFormat
import AccountContext import AccountContext
import AlertUI import TelegramStringFormatting
import PresentationDataUtils import TelegramPresentationData
import Markdown import Markdown
import AlertUI
public func giveawayInfoController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, message: EngineMessage, giveawayInfo: PremiumGiveawayInfo) -> ViewController? {
guard let giveaway = message.media.first(where: { $0 is TelegramMediaGiveaway }) as? TelegramMediaGiveaway else {
return nil
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var peerName = ""
if let peerId = giveaway.channelPeerIds.first, let peer = message.peers[peerId] {
peerName = EnginePeer(peer).compactDisplayTitle
}
let untilDate = stringForDate(timestamp: giveaway.untilDate, strings: presentationData.strings)
let title: String
let text: String
var warning: String?
var dismissImpl: (() -> Void)?
var actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
dismissImpl?()
})]
switch giveawayInfo {
case let .ongoing(start, status):
let startDate = stringForDate(timestamp: start, strings: presentationData.strings)
title = presentationData.strings.Chat_Giveaway_Info_Title
let intro: String
if case .almostOver = status {
intro = "The giveaway was sponsored by the admins of **\(peerName)**, who acquired **\(giveaway.quantity) Telegram Premium** subscriptions for **\(giveaway.months)** months for its followers."
} else {
intro = "The giveaway is sponsored by the admins of **\(peerName)**, who acquired **\(giveaway.quantity) Telegram Premium** subscriptions for **\(giveaway.months)** months for its followers."
}
let ending: String
if giveaway.flags.contains(.onlyNewSubscribers) {
if giveaway.channelPeerIds.count > 1 {
ending = "On **\(untilDate)**, Telegram will automatically select **\(giveaway.quantity)** random users that joined **\(peerName)** and **\(giveaway.channelPeerIds.count - 1)** other listed channels after **\(startDate)**."
} else {
ending = "On **\(untilDate)**, Telegram will automatically select **\(giveaway.quantity)** random users that joined **\(peerName)** after **\(startDate)**."
}
} else {
if giveaway.channelPeerIds.count > 1 {
ending = "On **\(untilDate)**, Telegram will automatically select **\(giveaway.quantity)** random subscribers of **\(peerName)** and **\(giveaway.channelPeerIds.count - 1)** other listed channels."
} else {
ending = "On **\(untilDate)**, Telegram will automatically select **\(giveaway.quantity)** random subscribers of **\(peerName)**."
}
}
var participation: String
switch status {
case .notQualified:
if giveaway.channelPeerIds.count > 1 {
participation = "To take part in this giveaway please join the channel **\(peerName)** (**\(giveaway.channelPeerIds.count - 1)** other listed channels) before **\(untilDate)**."
} else {
participation = "To take part in this giveaway please join the channel **\(peerName)** before **\(untilDate)**."
}
case let .notAllowed(reason):
switch reason {
case let .joinedTooEarly(joinedOn):
let joinDate = stringForDate(timestamp: joinedOn, strings: presentationData.strings)
participation = "You are not eligible to participate in this giveaway, because you joined this channel on **\(joinDate)**, which is before the contest started."
case let .channelAdmin(adminId):
let _ = adminId
participation = "You are not eligible to participate in this giveaway, because you are an admin of participating channel (**\(peerName)**)."
case let .disallowedCountry(countryCode):
let _ = countryCode
participation = "You are not eligible to participate in this giveaway, because your country is not included in the terms of the giveaway."
}
case .participating:
if giveaway.channelPeerIds.count > 1 {
participation = "You are participating in this giveaway, because you have joined the channel **\(peerName)** (**\(giveaway.channelPeerIds.count - 1)** other listed channels)."
} else {
participation = "You are participating in this giveaway, because you have joined the channel **\(peerName)**."
}
case .almostOver:
participation = presentationData.strings.Chat_Giveaway_Info_AlmostOver
}
if !participation.isEmpty {
participation = "\n\n\(participation)"
}
text = "\(intro)\n\n\(ending)\(participation)"
case let .finished(status, start, finish, _, activatedCount):
let startDate = stringForDate(timestamp: start, strings: presentationData.strings)
let finishDate = stringForDate(timestamp: finish, strings: presentationData.strings)
title = presentationData.strings.Chat_Giveaway_Info_EndedTitle
let intro = "The giveaway was sponsored by the admins of **\(peerName)**, who acquired **\(giveaway.quantity) Telegram Premium** subscriptions for **\(giveaway.months)** months for its followers."
var ending: String
if giveaway.flags.contains(.onlyNewSubscribers) {
if giveaway.channelPeerIds.count > 1 {
ending = "On **\(finishDate)**, Telegram automatically selected **\(giveaway.quantity)** random users that joined **\(peerName)** and other listed channels after **\(startDate)**."
} else {
ending = "On **\(finishDate)**, Telegram automatically selected **\(giveaway.quantity)** random users that joined **\(peerName)** after **\(startDate)**."
}
} else {
if giveaway.channelPeerIds.count > 1 {
ending = "On **\(finishDate)**, Telegram automatically selected **\(giveaway.quantity)** random subscribers of **\(peerName)** and other listed channels."
} else {
ending = "On **\(finishDate)**, Telegram automatically selected **\(giveaway.quantity)** random subscribers of **\(peerName)**."
}
}
if activatedCount > 0 {
ending += " " + presentationData.strings.Chat_Giveaway_Info_ActivatedLinks(activatedCount)
}
var result: String
switch status {
case .refunded:
result = ""
warning = presentationData.strings.Chat_Giveaway_Info_Refunded
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Close, action: {
dismissImpl?()
})]
case .notWon:
result = "\n\n" + presentationData.strings.Chat_Giveaway_Info_DidntWin
case let .won(slug):
result = "\n\n" + presentationData.strings.Chat_Giveaway_Info_Won("🏆").string
let _ = slug
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Chat_Giveaway_Info_ViewPrize, action: {
dismissImpl?()
}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
dismissImpl?()
})]
}
text = "\(intro)\n\n\(ending)\(result)"
}
let alertController = giveawayInfoAlertController(
context: context,
updatedPresentationData: updatedPresentationData,
title: title,
text: text,
warning: warning,
actions: actions
)
dismissImpl = { [weak alertController] in
alertController?.dismissAnimated()
}
return alertController
}
private final class GiveawayInfoAlertContentNode: AlertContentNode { private final class GiveawayInfoAlertContentNode: AlertContentNode {
private let title: String private let title: String
@ -229,7 +378,7 @@ private final class GiveawayInfoAlertContentNode: AlertContentNode {
} }
} }
func giveawayInfoAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, title: String, text: String, warning: String?, actions: [TextAlertAction]) -> AlertController { private func giveawayInfoAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, title: String, text: String, warning: String?, actions: [TextAlertAction]) -> AlertController {
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
let contentNode = GiveawayInfoAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, title: title, text: text, warning: warning, actions: actions) let contentNode = GiveawayInfoAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, title: title, text: text, warning: warning, actions: actions)

View File

@ -186,8 +186,8 @@ public func PremiumBoostScreen(
let controller = textAlertController( let controller = textAlertController(
sharedContext: context.sharedContext, sharedContext: context.sharedContext,
updatedPresentationData: nil, updatedPresentationData: nil,
title: "More Boosts Needed", title: presentationData.strings.ChannelBoost_MoreBoosts_Title,
text: "To boost **\(peer.compactDisplayTitle)** again, gift **Telegram Premium** to a friend and get **\(premiumConfiguration.boostsPerGiftCount)** additional boosts.", text: presentationData.strings.ChannelBoost_MoreBoosts_Text(peer.compactDisplayTitle, "\(premiumConfiguration.boostsPerGiftCount)").string,
actions: [ actions: [
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {}) TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
], ],

View File

@ -22,6 +22,7 @@ import TelegramStringFormatting
import UndoUI import UndoUI
import InvisibleInkDustNode import InvisibleInkDustNode
//TODO:localize
private final class PremiumGiftCodeSheetContent: CombinedComponent { private final class PremiumGiftCodeSheetContent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment typealias EnvironmentType = ViewControllerComponentContainer.Environment

View File

@ -19,8 +19,6 @@ import PeerListItemComponent
import TelegramStringFormatting import TelegramStringFormatting
import AvatarNode import AvatarNode
//TODO:localize
private final class ReplaceBoostScreenComponent: CombinedComponent { private final class ReplaceBoostScreenComponent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -172,7 +170,7 @@ private final class ReplaceBoostScreenComponent: CombinedComponent {
if channelName.count > 48 { if channelName.count > 48 {
channelName = "\(channelName.prefix(48))..." channelName = "\(channelName.prefix(48))..."
} }
let descriptionString = "To boost **\(channelName)**, reassign a previous boost or gift **Telegram Premium** to a friend to get **\(premiumConfiguration.boostsPerGiftCount)** additional boosts." let descriptionString = strings.ReassignBoost_Description(channelName, "\(premiumConfiguration.boostsPerGiftCount)").string
let description = description.update( let description = description.update(
component: MultilineTextComponent( component: MultilineTextComponent(
@ -204,11 +202,11 @@ private final class ReplaceBoostScreenComponent: CombinedComponent {
if let cooldownUntil = boost.cooldownUntil, cooldownUntil > state.currentTime { if let cooldownUntil = boost.cooldownUntil, cooldownUntil > state.currentTime {
let duration = cooldownUntil - state.currentTime let duration = cooldownUntil - state.currentTime
let durationValue = stringForDuration(duration, position: nil) let durationValue = stringForDuration(duration, position: nil)
subtitle = "Available in \(durationValue)" subtitle = strings.ReassignBoost_AvailableIn(durationValue).string
isEnabled = false isEnabled = false
} else { } else {
let expiresValue = stringForDate(timestamp: boost.expires, strings: strings) let expiresValue = stringForDate(timestamp: boost.expires, strings: strings)
subtitle = "Boost expires on \(expiresValue)" subtitle = strings.ReassignBoost_ExpiresOn(expiresValue).string
} }
let accountContext = context.component.context let accountContext = context.component.context
@ -245,7 +243,8 @@ private final class ReplaceBoostScreenComponent: CombinedComponent {
selectedSlotsUpdated(state.selectedSlots) selectedSlotsUpdated(state.selectedSlots)
} else { } else {
let presentationData = accountContext.sharedContext.currentPresentationData.with { $0 } let presentationData = accountContext.sharedContext.currentPresentationData.with { $0 }
let undoController = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "Wait until the boost is available or get **3** more boosts by gifting a **Telegram Premium** subscription.", timeout: nil, customUndoText: nil), elevatedLayout: false, position: .top, action: { _ in return true })
let undoController = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: strings.ReassignBoost_WaitForCooldown("\(premiumConfiguration.boostsPerGiftCount)").string, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .top, action: { _ in return true })
presentController(undoController) presentController(undoController)
} }
}) })
@ -559,7 +558,7 @@ public class ReplaceBoostScreen: ViewController {
let footerInsets = UIEdgeInsets(top: 0.0, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.safeInsets.right) let footerInsets = UIEdgeInsets(top: 0.0, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.safeInsets.right)
transition.setFrame(view: self.footerView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topInset), size: layout.size)) transition.setFrame(view: self.footerView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topInset), size: layout.size))
self.footerHeight = self.footerView.update(size: layout.size, insets: footerInsets, theme: self.presentationData.theme, count: Int32(self.selectedSlots.count)) self.footerHeight = self.footerView.update(size: layout.size, insets: footerInsets, theme: self.presentationData.theme, strings: self.presentationData.strings, count: Int32(self.selectedSlots.count))
if !hadLayout { if !hadLayout {
self.updateFooterAlpha() self.updateFooterAlpha()
@ -838,9 +837,9 @@ public class ReplaceBoostScreen: ViewController {
presentControllerImpl?(c) presentControllerImpl?(c)
})) }))
self.title = "Reassign Boosts" self.title = presentationData.strings.ReassignBoost_Title
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Close, style: .plain, target: self, action: #selector(self.cancelPressed)) self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
@ -984,10 +983,10 @@ private final class FooterView: UIView {
fileprivate var inProgress = false fileprivate var inProgress = false
private var currentLayout: (CGSize, UIEdgeInsets, PresentationTheme, Int32)? private var currentLayout: (CGSize, UIEdgeInsets, PresentationTheme, PresentationStrings, Int32)?
func update(size: CGSize, insets: UIEdgeInsets, theme: PresentationTheme, count: Int32) -> CGFloat { func update(size: CGSize, insets: UIEdgeInsets, theme: PresentationTheme, strings: PresentationStrings, count: Int32) -> CGFloat {
let hadLayout = self.currentLayout != nil let hadLayout = self.currentLayout != nil
self.currentLayout = (size, insets, theme, count) self.currentLayout = (size, insets, theme, strings, count)
self.backgroundNode.updateColor(color: theme.rootController.tabBar.backgroundColor, transition: .immediate) self.backgroundNode.updateColor(color: theme.rootController.tabBar.backgroundColor, transition: .immediate)
self.separatorView.backgroundColor = theme.rootController.tabBar.separatorColor self.separatorView.backgroundColor = theme.rootController.tabBar.separatorColor
@ -1019,7 +1018,7 @@ private final class FooterView: UIView {
content: AnyComponentWithIdentity( content: AnyComponentWithIdentity(
id: AnyHashable(0), id: AnyHashable(0),
component: AnyComponent(ButtonTextContentComponent( component: AnyComponent(ButtonTextContentComponent(
text: "Reassign Boosts", text: strings.ReassignBoost_ReassignBoosts,
badge: Int(count), badge: Int(count),
textColor: theme.list.itemCheckColors.foregroundColor, textColor: theme.list.itemCheckColors.foregroundColor,
badgeBackground: theme.list.itemCheckColors.foregroundColor, badgeBackground: theme.list.itemCheckColors.foregroundColor,
@ -1036,8 +1035,8 @@ private final class FooterView: UIView {
return return
} }
self.inProgress = true self.inProgress = true
if let (size, insets, theme, count) = self.currentLayout { if let (size, insets, theme, strings, count) = self.currentLayout {
let _ = self.update(size: size, insets: insets, theme: theme, count: count) let _ = self.update(size: size, insets: insets, theme: theme, strings: strings, count: count)
} }
self.action() self.action()
} }

View File

@ -554,15 +554,15 @@ private enum StatsEntry: ItemListNodeEntry {
let expiresString: String let expiresString: String
let durationMonths = Int32(round(Float(boost.expires - boost.date) / (86400.0 * 30.0))) let durationMonths = Int32(round(Float(boost.expires - boost.date) / (86400.0 * 30.0)))
let durationString = "\(durationMonths)m" let durationString = presentationData.strings.Stats_Boosts_ShortMonth("\(durationMonths)").string
let title: String let title: String
let icon: GiftOptionItem.Icon let icon: GiftOptionItem.Icon
var label: String? var label: String?
if boost.flags.contains(.isGiveaway) { if boost.flags.contains(.isGiveaway) {
label = "🏆 Giveaway" label = "🏆 \(presentationData.strings.Stats_Boosts_Giveaway)"
} else if boost.flags.contains(.isGift) { } else if boost.flags.contains(.isGift) {
label = "🎁 Gift" label = "🎁 \(presentationData.strings.Stats_Boosts_Gift)"
} }
let color: GiftOptionItem.Icon.Color let color: GiftOptionItem.Icon.Color
@ -575,7 +575,7 @@ private enum StatsEntry: ItemListNodeEntry {
} }
if boost.flags.contains(.isUnclaimed) { if boost.flags.contains(.isUnclaimed) {
title = "Unclaimed" title = presentationData.strings.Stats_Boosts_Unclaimed
icon = .image(color: color, name: "Premium/Unclaimed") icon = .image(color: color, name: "Premium/Unclaimed")
expiresString = "\(durationString)\(expiresValue)" expiresString = "\(durationString)\(expiresValue)"
} else if let peer = boost.peer { } else if let peer = boost.peer {
@ -588,10 +588,10 @@ private enum StatsEntry: ItemListNodeEntry {
} }
} else { } else {
if boost.flags.contains(.isUnclaimed) { if boost.flags.contains(.isUnclaimed) {
title = "Unclaimed" title = presentationData.strings.Stats_Boosts_Unclaimed
icon = .image(color: color, name: "Premium/Unclaimed") icon = .image(color: color, name: "Premium/Unclaimed")
} else if boost.flags.contains(.isGiveaway) { } else if boost.flags.contains(.isGiveaway) {
title = "To be distributed" title = presentationData.strings.Stats_Boosts_ToBeDistributed
icon = .image(color: color, name: "Premium/ToBeDistributed") icon = .image(color: color, name: "Premium/ToBeDistributed")
} else { } else {
title = "Unknown" title = "Unknown"
@ -774,15 +774,14 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p
entries.append(.boostOverviewTitle(presentationData.theme, presentationData.strings.Stats_Boosts_OverviewHeader)) entries.append(.boostOverviewTitle(presentationData.theme, presentationData.strings.Stats_Boosts_OverviewHeader))
entries.append(.boostOverview(presentationData.theme, boostData)) entries.append(.boostOverview(presentationData.theme, boostData))
//TODO:localize
if !boostData.prepaidGiveaways.isEmpty { if !boostData.prepaidGiveaways.isEmpty {
entries.append(.boostPrepaidTitle(presentationData.theme, "PREPAID GIVEAWAYS")) entries.append(.boostPrepaidTitle(presentationData.theme, presentationData.strings.Stats_Boosts_PrepaidGiveawaysTitle))
var i: Int32 = 0 var i: Int32 = 0
for giveaway in boostData.prepaidGiveaways { for giveaway in boostData.prepaidGiveaways {
entries.append(.boostPrepaid(i, presentationData.theme, "\(giveaway.quantity) Telegram Premium", "\(giveaway.months)-month subscriptions", giveaway)) entries.append(.boostPrepaid(i, presentationData.theme, presentationData.strings.Stats_Boosts_PrepaidGiveawayCount(giveaway.quantity), presentationData.strings.Stats_Boosts_PrepaidGiveawayMonths("\(giveaway.months)").string, giveaway))
i += 1 i += 1
} }
entries.append(.boostPrepaidInfo(presentationData.theme, "Select a giveaway you already paid for to set it up.")) entries.append(.boostPrepaidInfo(presentationData.theme, presentationData.strings.Stats_Boosts_PrepaidGiveawaysInfo))
} }
let boostersTitle: String let boostersTitle: String
@ -813,7 +812,7 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p
} }
if boostsCount > 0 && giftsCount > 0 && boostsCount != giftsCount { if boostsCount > 0 && giftsCount > 0 && boostsCount != giftsCount {
entries.append(.boosterTabs(presentationData.theme, "\(boostsCount) Boosts", "\(giftsCount) Gifts", state.giftsSelected)) entries.append(.boosterTabs(presentationData.theme, presentationData.strings.Stats_Boosts_TabBoosts(boostsCount), presentationData.strings.Stats_Boosts_TabGifts(giftsCount), state.giftsSelected))
} }
let selectedState: ChannelBoostersContext.State? let selectedState: ChannelBoostersContext.State?
@ -853,8 +852,8 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p
entries.append(.boostLinkInfo(presentationData.theme, presentationData.strings.Stats_Boosts_LinkInfo)) entries.append(.boostLinkInfo(presentationData.theme, presentationData.strings.Stats_Boosts_LinkInfo))
if giveawayAvailable { if giveawayAvailable {
entries.append(.gifts(presentationData.theme, "Get Boosts via Gifts")) entries.append(.gifts(presentationData.theme, presentationData.strings.Stats_Boosts_GetBoosts))
entries.append(.giftsInfo(presentationData.theme, "Get more boosts for your channel by gifting Premium to your subscribers.")) entries.append(.giftsInfo(presentationData.theme, presentationData.strings.Stats_Boosts_GetBoostsInfo))
} }
} }
} }

View File

@ -189,7 +189,6 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
} }
} }
//TODO:localize
override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) { override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
let makeLabelLayout = TextNode.asyncLayout(self.labelNode) let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
@ -226,24 +225,24 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
textSpacing += 13.0 textSpacing += 13.0
if unclaimed { if unclaimed {
title = "Unclaimed Prize" title = item.presentationData.strings.Notification_PremiumPrize_Unclaimed
} else { } else {
title = "Congratulations!" title = item.presentationData.strings.Notification_PremiumPrize_Title
} }
var peerName = "" var peerName = ""
if let channelId, let channel = item.message.peers[channelId] { if let channelId, let channel = item.message.peers[channelId] {
peerName = EnginePeer(channel).compactDisplayTitle peerName = EnginePeer(channel).compactDisplayTitle
} }
if unclaimed { if unclaimed {
text = "You have an unclaimed prize from a giveaway by **\(peerName)**.\n\nThis prize is a **Telegram Premium** subscription for **\(monthsValue)** months." text = item.presentationData.strings.Notification_PremiumPrize_UnclaimedText(peerName, item.presentationData.strings.Notification_PremiumPrize_Months(monthsValue)).string
} else if fromGiveaway { } else if fromGiveaway {
text = "You won a prize in a giveaway organized by **\(peerName)**.\n\nYour prize is a **Telegram Premium** subscription for **\(monthsValue)** months." text = item.presentationData.strings.Notification_PremiumPrize_GiveawayText(peerName, item.presentationData.strings.Notification_PremiumPrize_Months(monthsValue)).string
} else { } else {
text = "You've received a gift from **\(peerName)**.\n\nYour gift is a **Telegram Premium** subscription for **\(monthsValue)** months." text = item.presentationData.strings.Notification_PremiumPrize_GiftText(peerName, item.presentationData.strings.Notification_PremiumPrize_Months(monthsValue)).string
} }
months = monthsValue months = monthsValue
buttonTitle = "Open Gift Link" buttonTitle = item.presentationData.strings.Notification_PremiumPrize_View
hasServiceMessage = false hasServiceMessage = false
default: default:
break break

View File

@ -25,7 +25,6 @@ import TelegramUIPreferences
import UndoUI import UndoUI
import TelegramStringFormatting import TelegramStringFormatting
//TODO:localize
final class ShareWithPeersScreenComponent: Component { final class ShareWithPeersScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -941,9 +940,9 @@ final class ShareWithPeersScreenComponent: Component {
sectionTitle = environment.strings.Story_Privacy_WhoCanViewHeader sectionTitle = environment.strings.Story_Privacy_WhoCanViewHeader
} else if section.id == 1 { } else if section.id == 1 {
if case .members = component.stateContext.subject { if case .members = component.stateContext.subject {
sectionTitle = "SUBSCRIBERS" sectionTitle = environment.strings.BoostGift_Subscribers_SectionTitle
} else if case .channels = component.stateContext.subject { } else if case .channels = component.stateContext.subject {
sectionTitle = "CHANNELS" sectionTitle = environment.strings.BoostGift_Channels_SectionTitle
} else { } else {
sectionTitle = environment.strings.Story_Privacy_ContactsHeader sectionTitle = environment.strings.Story_Privacy_ContactsHeader
} }
@ -1390,7 +1389,7 @@ final class ShareWithPeersScreenComponent: Component {
} else { } else {
if case .members = component.stateContext.subject { if case .members = component.stateContext.subject {
if let invitedAt = stateValue.invitedAt[peer.id] { if let invitedAt = stateValue.invitedAt[peer.id] {
subtitle = "joined \(stringForMediumDate(timestamp: invitedAt, strings: environment.strings, dateTimeFormat: environment.dateTimeFormat))" subtitle = environment.strings.BoostGift_Subscribers_Joined(stringForMediumDate(timestamp: invitedAt, strings: environment.strings, dateTimeFormat: environment.dateTimeFormat)).string
} else { } else {
subtitle = nil subtitle = nil
} }
@ -1445,7 +1444,7 @@ final class ShareWithPeersScreenComponent: Component {
self.hapticFeedback.error() self.hapticFeedback.error()
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
controller.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "You can select maximum \(component.context.userLimits.maxGiveawayChannelsCount) channels.", timeout: nil, customUndoText: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }), in: .current) controller.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: environment.strings.BoostGift_Channels_MaximumReached("\(component.context.userLimits.maxGiveawayChannelsCount)").string, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }), in: .current)
return return
} }
if case .channels = component.stateContext.subject { if case .channels = component.stateContext.subject {
@ -1453,11 +1452,11 @@ final class ShareWithPeersScreenComponent: Component {
let alertController = textAlertController( let alertController = textAlertController(
context: component.context, context: component.context,
forceTheme: environment.theme, forceTheme: environment.theme,
title: "Channel is Private", title: environment.strings.BoostGift_Channels_PrivateChannel_Title,
text: "Are you sure you want to add a private channel? Users won't be able to join it without an invite link.", text: environment.strings.BoostGift_Channels_PrivateChannel_Text,
actions: [ actions: [
TextAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: {}),
TextAlertAction(type: .defaultAction, title: "Add", action: { TextAlertAction(type: .defaultAction, title: environment.strings.BoostGift_Channels_PrivateChannel_Add, action: {
togglePeer() togglePeer()
}) })
] ]
@ -1475,7 +1474,7 @@ final class ShareWithPeersScreenComponent: Component {
self.hapticFeedback.error() self.hapticFeedback.error()
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
controller.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "You can select maximum 10 subscribers.", timeout: nil, customUndoText: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }), in: .current) controller.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: environment.strings.BoostGift_Subscribers_MaximumReached("\(10)").string, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }), in: .current)
return return
} }
togglePeer() togglePeer()
@ -2018,9 +2017,9 @@ final class ShareWithPeersScreenComponent: Component {
let placeholder: String let placeholder: String
switch component.stateContext.subject { switch component.stateContext.subject {
case .members: case .members:
placeholder = "Search Subscribers" placeholder = environment.strings.BoostGift_Subscribers_Search
case .channels: case .channels:
placeholder = "Search Channels" placeholder = environment.strings.BoostGift_Channels_Search
case .chats: case .chats:
placeholder = environment.strings.Story_Privacy_SearchChats placeholder = environment.strings.Story_Privacy_SearchChats
default: default:
@ -2367,13 +2366,13 @@ final class ShareWithPeersScreenComponent: Component {
case .contactsSearch: case .contactsSearch:
title = "" title = ""
case .members: case .members:
title = "Gift Premium" title = environment.strings.BoostGift_Subscribers_Title
actionButtonTitle = "Save Recipients" subtitle = environment.strings.BoostGift_Subscribers_Subtitle("\(10)").string
subtitle = "select up to 10 subscribers" actionButtonTitle = environment.strings.BoostGift_Subscribers_Save
case .channels: case .channels:
title = "Add Channels" title = environment.strings.BoostGift_Channels_Title
actionButtonTitle = "Save Channels" subtitle = environment.strings.BoostGift_Channels_Subtitle("\(component.context.userLimits.maxGiveawayChannelsCount)").string
subtitle = "select up to \(component.context.userLimits.maxGiveawayChannelsCount) channels" actionButtonTitle = environment.strings.BoostGift_Channels_Save
} }
let titleComponent: AnyComponent<Empty> let titleComponent: AnyComponent<Empty>

View File

@ -180,8 +180,6 @@ final class MediaNavigationStripComponent: Component {
let itemHeight: CGFloat = 2.0 let itemHeight: CGFloat = 2.0
let minItemWidth: CGFloat = 2.0 let minItemWidth: CGFloat = 2.0
var size = CGSize(width: availableSize.width, height: itemHeight)
var didSetCompletion = false var didSetCompletion = false
var validIndices: [Int] = [] var validIndices: [Int] = []
@ -223,7 +221,6 @@ final class MediaNavigationStripComponent: Component {
var itemFrame = CGRect(origin: CGPoint(x: -globalOffset + CGFloat(i) * (itemWidth + spacing), y: 0.0), size: CGSize(width: itemWidth, height: itemHeight)) var itemFrame = CGRect(origin: CGPoint(x: -globalOffset + CGFloat(i) * (itemWidth + spacing), y: 0.0), size: CGSize(width: itemWidth, height: itemHeight))
if component.isSeeking { if component.isSeeking {
itemFrame = CGRect(origin: .zero, size: CGSize(width: availableSize.width, height: 6.0)) itemFrame = CGRect(origin: .zero, size: CGSize(width: availableSize.width, height: 6.0))
size.height = itemFrame.height
} }
if itemFrame.maxX < 0.0 || itemFrame.minX >= availableSize.width { if itemFrame.maxX < 0.0 || itemFrame.minX >= availableSize.width {
continue continue

View File

@ -546,6 +546,14 @@ private final class StoryContainerScreenComponent: Component {
} }
self.initialSeekTimestamp = nil self.initialSeekTimestamp = nil
self.previousSeekTime = nil self.previousSeekTime = nil
guard let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else {
return
}
guard let visibleItemView = itemSetComponentView.visibleItems[slice.item.storyItem.id]?.view.view as? StoryItemContentComponent.View else {
return
}
visibleItemView.seekEnded()
} }
longPressRecognizer.shouldBegin = { [weak self] touch in longPressRecognizer.shouldBegin = { [weak self] touch in
guard let self else { guard let self else {

View File

@ -95,12 +95,13 @@ final class StoryInteractionGuideComponent: Component {
self.component = component self.component = component
self.state = state self.state = state
let strings = component.strings
let sideInset: CGFloat = 48.0 let sideInset: CGFloat = 48.0
//TODO:localize
let titleSize = self.titleLabel.update( let titleSize = self.titleLabel.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "Watching Stories", font: Font.semibold(20.0), textColor: .white, paragraphAlignment: .center)))), component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Story_Guide_Title, font: Font.semibold(20.0), textColor: .white, paragraphAlignment: .center)))),
environment: {}, environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height) containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height)
) )
@ -114,7 +115,7 @@ final class StoryInteractionGuideComponent: Component {
let textSize = self.descriptionLabel.update( let textSize = self.descriptionLabel.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(BalancedTextComponent(text: .plain(NSAttributedString(string: "You can use these gestures to control playback.", font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.6), paragraphAlignment: .center)), maximumNumberOfLines: 0, lineSpacing: 0.2)), component: AnyComponent(BalancedTextComponent(text: .plain(NSAttributedString(string: strings.Story_Guide_Description, font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.6), paragraphAlignment: .center)), maximumNumberOfLines: 0, lineSpacing: 0.2)),
environment: {}, environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height) containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height)
) )
@ -132,8 +133,8 @@ final class StoryInteractionGuideComponent: Component {
component: AnyComponent( component: AnyComponent(
GuideItemComponent( GuideItemComponent(
context: component.context, context: component.context,
title: "Go forward", title: strings.Story_Guide_ForwardTitle,
text: "Tap the screen", text: strings.Story_Guide_ForwardDescription,
animationName: "story_forward", animationName: "story_forward",
isPlaying: self.currentIndex == 0, isPlaying: self.currentIndex == 0,
playbackCompleted: { [weak self] in playbackCompleted: { [weak self] in
@ -151,8 +152,8 @@ final class StoryInteractionGuideComponent: Component {
component: AnyComponent( component: AnyComponent(
GuideItemComponent( GuideItemComponent(
context: component.context, context: component.context,
title: "Pause and Seek", title: strings.Story_Guide_PauseTitle,
text: "Hold and move sideways", text: strings.Story_Guide_PauseDescription,
animationName: "story_pause", animationName: "story_pause",
isPlaying: self.currentIndex == 1, isPlaying: self.currentIndex == 1,
playbackCompleted: { [weak self] in playbackCompleted: { [weak self] in
@ -170,8 +171,8 @@ final class StoryInteractionGuideComponent: Component {
component: AnyComponent( component: AnyComponent(
GuideItemComponent( GuideItemComponent(
context: component.context, context: component.context,
title: "Go back", title: strings.Story_Guide_BackTitle,
text: "Tap the left edge", text: strings.Story_Guide_BackDescription,
animationName: "story_back", animationName: "story_back",
isPlaying: self.currentIndex == 2, isPlaying: self.currentIndex == 2,
playbackCompleted: { [weak self] in playbackCompleted: { [weak self] in
@ -189,8 +190,8 @@ final class StoryInteractionGuideComponent: Component {
component: AnyComponent( component: AnyComponent(
GuideItemComponent( GuideItemComponent(
context: component.context, context: component.context,
title: "Move between stories", title: strings.Story_Guide_MoveTitle,
text: "Swipe left or right", text: strings.Story_Guide_MoveDescription,
animationName: "story_move", animationName: "story_move",
isPlaying: self.currentIndex == 3, isPlaying: self.currentIndex == 3,
playbackCompleted: { [weak self] in playbackCompleted: { [weak self] in
@ -222,7 +223,7 @@ final class StoryInteractionGuideComponent: Component {
let buttonSize = self.proceedButton.update( let buttonSize = self.proceedButton.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(Button( component: AnyComponent(Button(
content: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "Tap to keep watching", font: Font.semibold(17.0), textColor: .white, paragraphAlignment: .center)))), content: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Story_Guide_Proceed, font: Font.semibold(17.0), textColor: .white, paragraphAlignment: .center)))),
action: { [weak self] in action: { [weak self] in
self?.handleTap() self?.handleTap()
} }

View File

@ -277,7 +277,9 @@ final class StoryItemContentComponent: Component {
} }
self.videoPlaybackStatus = status self.videoPlaybackStatus = status
self.updateVideoPlaybackProgress() if !self.isSeeking {
self.updateVideoPlaybackProgress()
}
}) })
} }
} }
@ -360,7 +362,9 @@ final class StoryItemContentComponent: Component {
} }
if case .file = self.currentMessageMedia { if case .file = self.currentMessageMedia {
self.updateVideoPlaybackProgress() if !self.isSeeking {
self.updateVideoPlaybackProgress()
}
} else { } else {
if !self.markedAsSeen { if !self.markedAsSeen {
self.markedAsSeen = true self.markedAsSeen = true
@ -536,6 +540,7 @@ final class StoryItemContentComponent: Component {
) )
} }
private var isSeeking = false
func seekTo(_ timestamp: Double, apply: Bool) { func seekTo(_ timestamp: Double, apply: Bool) {
guard let videoNode = self.videoNode else { guard let videoNode = self.videoNode else {
return return
@ -543,8 +548,13 @@ final class StoryItemContentComponent: Component {
if apply { if apply {
videoNode.seek(timestamp) videoNode.seek(timestamp)
} }
self.isSeeking = true
self.updateVideoPlaybackProgress(timestamp) self.updateVideoPlaybackProgress(timestamp)
} }
func seekEnded() {
self.isSeeking = false
}
func update(component: StoryItemContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<StoryContentItem.Environment>, transition: Transition) -> CGSize { func update(component: StoryItemContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<StoryContentItem.Environment>, transition: Transition) -> CGSize {
let previousItem = self.component?.item let previousItem = self.component?.item

View File

@ -4710,7 +4710,7 @@ public final class StoryItemSetContainerComponent: Component {
let seekLabelSize = self.seekLabel.update( let seekLabelSize = self.seekLabel.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(Text(text: "Slide left or right to seek", font: Font.semibold(14.0), color: .white)), component: AnyComponent(Text(text: component.strings.Story_SlideToSeek, font: Font.semibold(14.0), color: .white)),
environment: {}, environment: {},
containerSize: availableSize containerSize: availableSize
) )

View File

@ -4787,17 +4787,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
case let .ongoing(_, status): case let .ongoing(_, status):
switch status { switch status {
case .notAllowed: case .notAllowed:
content = .info(title: nil, text: "You can't participate in this giveaway.", timeout: nil, customUndoText: "Learn More") content = .info(title: nil, text: self.presentationData.strings.Chat_Giveaway_Toast_NotAllowed, timeout: nil, customUndoText: self.presentationData.strings.Chat_Giveaway_Toast_LearnMore)
case .participating: case .participating:
content = .succeed(text: "You are participating in this giveaway.", timeout: nil, customUndoText: "Learn More") content = .succeed(text: self.presentationData.strings.Chat_Giveaway_Toast_Participating, timeout: nil, customUndoText: self.presentationData.strings.Chat_Giveaway_Toast_LearnMore)
case .notQualified: case .notQualified:
content = .info(title: nil, text: "You are not qualified for this giveaway yet.", timeout: nil, customUndoText: "Learn More") content = .info(title: nil, text: self.presentationData.strings.Chat_Giveaway_Toast_NotQualified, timeout: nil, customUndoText: self.presentationData.strings.Chat_Giveaway_Toast_LearnMore)
case .almostOver: case .almostOver:
content = .info(title: nil, text: "The giveaway is almost over.", timeout: nil, customUndoText: "Learn More") content = .info(title: nil, text: self.presentationData.strings.Chat_Giveaway_Toast_AlmostOver, timeout: nil, customUndoText: self.presentationData.strings.Chat_Giveaway_Toast_LearnMore)
} }
case let .finished(status, _, _, _, _): case .finished:
let _ = status content = .info(title: nil, text: self.presentationData.strings.Chat_Giveaway_Toast_Ended, timeout: nil, customUndoText: self.presentationData.strings.Chat_Giveaway_Toast_LearnMore)
content = .info(title: nil, text: "The giveaway is ended.", timeout: nil, customUndoText: "Learn More")
} }
let controller = UndoOverlayController(presentationData: self.presentationData, content: content, elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { [weak self] action in let controller = UndoOverlayController(presentationData: self.presentationData, content: content, elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { [weak self] action in
if case .undo = action, let self { if case .undo = action, let self {
@ -19563,150 +19562,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
func displayGiveawayStatusInfo(messageId: EngineMessage.Id, giveawayInfo: PremiumGiveawayInfo) { func displayGiveawayStatusInfo(messageId: EngineMessage.Id, giveawayInfo: PremiumGiveawayInfo) {
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId)) let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId))
|> deliverOnMainQueue).startStandalone(next: { [weak self] message in |> deliverOnMainQueue).startStandalone(next: { [weak self] message in
guard let self, let message, let giveaway = message.media.first(where: { $0 is TelegramMediaGiveaway }) as? TelegramMediaGiveaway else { guard let self, let message else {
return return
} }
var peerName = "" if let controller = giveawayInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, message: message, giveawayInfo: giveawayInfo) {
if let peerId = giveaway.channelPeerIds.first, let peer = message.peers[peerId] { self.present(controller, in: .window(.root))
peerName = EnginePeer(peer).compactDisplayTitle
}
let untilDate = stringForDate(timestamp: giveaway.untilDate, strings: self.presentationData.strings)
let title: String
let text: String
var warning: String?
var dismissImpl: (() -> Void)?
var actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {
dismissImpl?()
})]
switch giveawayInfo {
case let .ongoing(start, status):
let startDate = stringForDate(timestamp: start, strings: self.presentationData.strings)
title = "About This Giveaway"
let intro: String
if case .almostOver = status {
intro = "The giveaway was sponsored by the admins of **\(peerName)**, who acquired **\(giveaway.quantity) Telegram Premium** subscriptions for **\(giveaway.months)** months for its followers."
} else {
intro = "The giveaway is sponsored by the admins of **\(peerName)**, who acquired **\(giveaway.quantity) Telegram Premium** subscriptions for **\(giveaway.months)** months for its followers."
}
let ending: String
if giveaway.flags.contains(.onlyNewSubscribers) {
if giveaway.channelPeerIds.count > 1 {
ending = "On **\(untilDate)**, Telegram will automatically select **\(giveaway.quantity)** random users that joined **\(peerName)** and **\(giveaway.channelPeerIds.count - 1)** other listed channels after **\(startDate)**."
} else {
ending = "On **\(untilDate)**, Telegram will automatically select **\(giveaway.quantity)** random users that joined **\(peerName)** after **\(startDate)**."
}
} else {
if giveaway.channelPeerIds.count > 1 {
ending = "On **\(untilDate)**, Telegram will automatically select **\(giveaway.quantity)** random subscribers of **\(peerName)** and **\(giveaway.channelPeerIds.count - 1)** other listed channels."
} else {
ending = "On **\(untilDate)**, Telegram will automatically select **\(giveaway.quantity)** random subscribers of **\(peerName)**."
}
}
var participation: String
switch status {
case .notQualified:
if giveaway.channelPeerIds.count > 1 {
participation = "To take part in this giveaway please join the channel **\(peerName)** (**\(giveaway.channelPeerIds.count - 1)** other listed channels) before **\(untilDate)**."
} else {
participation = "To take part in this giveaway please join the channel **\(peerName)** before **\(untilDate)**."
}
case let .notAllowed(reason):
switch reason {
case let .joinedTooEarly(joinedOn):
let joinDate = stringForDate(timestamp: joinedOn, strings: self.presentationData.strings)
participation = "You are not eligible to participate in this giveaway, because you joined this channel on **\(joinDate)**, which is before the contest started."
case let .channelAdmin(adminId):
let _ = adminId
participation = "You are not eligible to participate in this giveaway, because you are an admin of participating channel (**\(peerName)**)."
case let .disallowedCountry(countryCode):
let _ = countryCode
participation = "You are not eligible to participate in this giveaway, because your country is not included in the terms of the giveaway."
}
case .participating:
if giveaway.channelPeerIds.count > 1 {
participation = "You are participating in this giveaway, because you have joined the channel **\(peerName)** (**\(giveaway.channelPeerIds.count - 1)** other listed channels)."
} else {
participation = "You are participating in this giveaway, because you have joined the channel **\(peerName)**."
}
case .almostOver:
participation = "The giveaway is over, preparing results."
}
if !participation.isEmpty {
participation = "\n\n\(participation)"
}
text = "\(intro)\n\n\(ending)\(participation)"
case let .finished(status, start, finish, _, activatedCount):
let startDate = stringForDate(timestamp: start, strings: self.presentationData.strings)
let finishDate = stringForDate(timestamp: finish, strings: self.presentationData.strings)
title = "Giveaway Ended"
let intro = "The giveaway was sponsored by the admins of **\(peerName)**, who acquired **\(giveaway.quantity) Telegram Premium** subscriptions for **\(giveaway.months)** months for its followers."
var ending: String
if giveaway.flags.contains(.onlyNewSubscribers) {
if giveaway.channelPeerIds.count > 1 {
ending = "On **\(finishDate)**, Telegram automatically selected **\(giveaway.quantity)** random users that joined **\(peerName)** and other listed channels after **\(startDate)**."
} else {
ending = "On **\(finishDate)**, Telegram automatically selected **\(giveaway.quantity)** random users that joined **\(peerName)** after **\(startDate)**."
}
} else {
if giveaway.channelPeerIds.count > 1 {
ending = "On **\(finishDate)**, Telegram automatically selected **\(giveaway.quantity)** random subscribers of **\(peerName)** and other listed channels."
} else {
ending = "On **\(finishDate)**, Telegram automatically selected **\(giveaway.quantity)** random subscribers of **\(peerName)**."
}
}
if activatedCount > 0 {
ending += " \(activatedCount) of the winners already used their gift links."
}
var result: String
switch status {
case .refunded:
result = ""
warning = "The channel cancelled the prizes by reversing the payment for them."
actions = [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Close, action: {
dismissImpl?()
})]
case .notWon:
result = "\n\nYou didn't win a prize in this giveaway."
case let .won(slug):
result = "\n\nYou won a prize in this giveaway. 🏆"
let _ = slug
actions = [TextAlertAction(type: .defaultAction, title: "View My Prize", action: {
dismissImpl?()
}), TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {
dismissImpl?()
})]
}
text = "\(intro)\n\n\(ending)\(result)"
}
let alertController = giveawayInfoAlertController(
context: self.context,
updatedPresentationData: self.updatedPresentationData,
title: title,
text: text,
warning: warning,
actions: actions
)
self.present(alertController, in: .window(.root))
dismissImpl = { [weak alertController] in
alertController?.dismissAnimated()
} }
}) })
} }