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_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/TelegramUI/Components/Stories/PeerListItemComponent",
"//submodules/InvisibleInkDustNode",
"//submodules/AlertUI",
],
visibility = [
"//visibility:public",

View File

@ -377,8 +377,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
case let .channelsHeader(_, text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
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: {
// arguments.openPeer(peer)
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: {
}, setPeerIdWithRevealedOptions: { lhs, rhs in
arguments.setItemIdWithRevealedOptions(lhs, rhs)
}, removePeer: { id in
@ -433,7 +432,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
} else {
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()
var focus = false
arguments.updateState { state in
@ -515,7 +514,7 @@ private func createGiveawayControllerEntries(
switch subject {
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
if !state.peers.isEmpty {
@ -533,22 +532,22 @@ private func createGiveawayControllerEntries(
recipientsText = presentationData.strings.PremiumGift_LabelRecipients(Int32(peersCount))
}
} 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):
entries.append(.prepaidHeader(presentationData.theme, "PREPAID GIVEAWAY"))
entries.append(.prepaid(presentationData.theme, "\(prepaidGiveaway.quantity) Telegram Premium", "\(prepaidGiveaway.months)-month subscriptions", prepaidGiveaway))
entries.append(.prepaidHeader(presentationData.theme, presentationData.strings.BoostGift_PrepaidGiveawayTitle))
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 .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(.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
let channels = [peerId] + state.channels
for channelId in channels {
@ -557,35 +556,44 @@ private func createGiveawayControllerEntries(
}
index += 1
}
entries.append(.channelAdd(presentationData.theme, "Add Channel"))
entries.append(.channelsInfo(presentationData.theme, "Choose the channels users need to be subscribed to take part in the giveaway"))
entries.append(.channelAdd(presentationData.theme, presentationData.strings.BoostGift_AddChannel))
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
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 {
let allCountries = state.countries.map { locale.localizedString(forRegionCode: $0) ?? $0 }.joined(separator: " and ")
countriesText = "from \(allCountries)"
if state.countries.count == 2 {
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 {
countriesText = "from all countries"
let countryCode = state.countries.first ?? ""
let countryName = locale.localizedString(forRegionCode: countryCode) ?? countryCode
countriesText = presentationData.strings.BoostGift_FromOneCountry(countryName).string
}
} else {
countriesText = presentationData.strings.BoostGift_FromAllCountries
}
entries.append(.usersAll(presentationData.theme, "All subscribers", countriesText, !state.onlyNewEligible))
entries.append(.usersNew(presentationData.theme, "Only new subscribers", 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(.usersAll(presentationData.theme, presentationData.strings.BoostGift_AllSubscribers, countriesText, !state.onlyNewEligible))
entries.append(.usersNew(presentationData.theme, presentationData.strings.BoostGift_OnlyNewSubscribers, countriesText, state.onlyNewEligible))
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))
if state.pickingTimeLimit {
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 {
entries.append(.durationHeader(presentationData.theme, "DURATION OF PREMIUM SUBSCRIPTIONS".uppercased()))
entries.append(.durationHeader(presentationData.theme, presentationData.strings.BoostGift_DurationTitle.uppercased()))
let recipientCount: Int
switch state.mode {
@ -628,7 +636,7 @@ private func createGiveawayControllerEntries(
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
@ -776,7 +784,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
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?()
})
@ -787,7 +795,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
case .gift:
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?()
})
let leftNavigationButton = ItemListNavigationButton(content: .none, style: .regular, enabled: false, action: {})
@ -857,7 +865,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
}
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
var updatedState = state
updatedState.subscriptions = 25
@ -915,11 +923,11 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
let text: String
switch state.mode {
case .giveaway:
title = "Giveaway Created"
text = "Check your channel's [Statistics]() to see how this giveaway boosted your channel."
title = presentationData.strings.BoostGift_GiveawayCreated_Title
text = presentationData.strings.BoostGift_GiveawayCreated_Text
case .gift:
title = "Premium Subscriptions Gifted"
text = "Check your channel's [Statistics]() to see how gifts boosted your channel."
title = presentationData.strings.BoostGift_PremiumGifted_Title
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
@ -992,8 +1000,8 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
controllers.removeLast(count)
navigationController.setViewControllers(controllers, animated: true)
let title = "Giveaway Created"
let text = "Check your channel's [Statistics]() to see how this giveaway boosted your channel."
let title = presentationData.strings.BoostGift_GiveawayCreated_Title
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 _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: peerId))

View File

@ -11,12 +11,14 @@ import ComponentFlow
final class CreateGiveawayHeaderItem: ItemListControllerHeaderItem {
let theme: PresentationTheme
let strings: PresentationStrings
let title: String
let text: String
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.strings = strings
self.title = title
self.text = text
self.cancel = cancel
@ -132,7 +134,7 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
self.titleNode.attributedText = attributedTitle
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) {

View File

@ -4,12 +4,161 @@ import AsyncDisplayKit
import Display
import SwiftSignalKit
import TelegramCore
import TelegramPresentationData
import TextFormat
import AccountContext
import AlertUI
import PresentationDataUtils
import TelegramStringFormatting
import TelegramPresentationData
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 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 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(
sharedContext: context.sharedContext,
updatedPresentationData: nil,
title: "More Boosts Needed",
text: "To boost **\(peer.compactDisplayTitle)** again, gift **Telegram Premium** to a friend and get **\(premiumConfiguration.boostsPerGiftCount)** additional boosts.",
title: presentationData.strings.ChannelBoost_MoreBoosts_Title,
text: presentationData.strings.ChannelBoost_MoreBoosts_Text(peer.compactDisplayTitle, "\(premiumConfiguration.boostsPerGiftCount)").string,
actions: [
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
],

View File

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

View File

@ -19,8 +19,6 @@ import PeerListItemComponent
import TelegramStringFormatting
import AvatarNode
//TODO:localize
private final class ReplaceBoostScreenComponent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -172,7 +170,7 @@ private final class ReplaceBoostScreenComponent: CombinedComponent {
if channelName.count > 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(
component: MultilineTextComponent(
@ -204,11 +202,11 @@ private final class ReplaceBoostScreenComponent: CombinedComponent {
if let cooldownUntil = boost.cooldownUntil, cooldownUntil > state.currentTime {
let duration = cooldownUntil - state.currentTime
let durationValue = stringForDuration(duration, position: nil)
subtitle = "Available in \(durationValue)"
subtitle = strings.ReassignBoost_AvailableIn(durationValue).string
isEnabled = false
} else {
let expiresValue = stringForDate(timestamp: boost.expires, strings: strings)
subtitle = "Boost expires on \(expiresValue)"
subtitle = strings.ReassignBoost_ExpiresOn(expiresValue).string
}
let accountContext = context.component.context
@ -245,7 +243,8 @@ private final class ReplaceBoostScreenComponent: CombinedComponent {
selectedSlotsUpdated(state.selectedSlots)
} else {
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)
}
})
@ -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)
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 {
self.updateFooterAlpha()
@ -838,9 +837,9 @@ public class ReplaceBoostScreen: ViewController {
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)
@ -984,10 +983,10 @@ private final class FooterView: UIView {
fileprivate var inProgress = false
private var currentLayout: (CGSize, UIEdgeInsets, PresentationTheme, Int32)?
func update(size: CGSize, insets: UIEdgeInsets, theme: PresentationTheme, count: Int32) -> CGFloat {
private var currentLayout: (CGSize, UIEdgeInsets, PresentationTheme, PresentationStrings, Int32)?
func update(size: CGSize, insets: UIEdgeInsets, theme: PresentationTheme, strings: PresentationStrings, count: Int32) -> CGFloat {
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.separatorView.backgroundColor = theme.rootController.tabBar.separatorColor
@ -1019,7 +1018,7 @@ private final class FooterView: UIView {
content: AnyComponentWithIdentity(
id: AnyHashable(0),
component: AnyComponent(ButtonTextContentComponent(
text: "Reassign Boosts",
text: strings.ReassignBoost_ReassignBoosts,
badge: Int(count),
textColor: theme.list.itemCheckColors.foregroundColor,
badgeBackground: theme.list.itemCheckColors.foregroundColor,
@ -1036,8 +1035,8 @@ private final class FooterView: UIView {
return
}
self.inProgress = true
if let (size, insets, theme, count) = self.currentLayout {
let _ = self.update(size: size, insets: insets, theme: theme, count: count)
if let (size, insets, theme, strings, count) = self.currentLayout {
let _ = self.update(size: size, insets: insets, theme: theme, strings: strings, count: count)
}
self.action()
}

View File

@ -554,15 +554,15 @@ private enum StatsEntry: ItemListNodeEntry {
let expiresString: String
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 icon: GiftOptionItem.Icon
var label: String?
if boost.flags.contains(.isGiveaway) {
label = "🏆 Giveaway"
label = "🏆 \(presentationData.strings.Stats_Boosts_Giveaway)"
} else if boost.flags.contains(.isGift) {
label = "🎁 Gift"
label = "🎁 \(presentationData.strings.Stats_Boosts_Gift)"
}
let color: GiftOptionItem.Icon.Color
@ -575,7 +575,7 @@ private enum StatsEntry: ItemListNodeEntry {
}
if boost.flags.contains(.isUnclaimed) {
title = "Unclaimed"
title = presentationData.strings.Stats_Boosts_Unclaimed
icon = .image(color: color, name: "Premium/Unclaimed")
expiresString = "\(durationString)\(expiresValue)"
} else if let peer = boost.peer {
@ -588,10 +588,10 @@ private enum StatsEntry: ItemListNodeEntry {
}
} else {
if boost.flags.contains(.isUnclaimed) {
title = "Unclaimed"
title = presentationData.strings.Stats_Boosts_Unclaimed
icon = .image(color: color, name: "Premium/Unclaimed")
} else if boost.flags.contains(.isGiveaway) {
title = "To be distributed"
title = presentationData.strings.Stats_Boosts_ToBeDistributed
icon = .image(color: color, name: "Premium/ToBeDistributed")
} else {
title = "Unknown"
@ -774,15 +774,14 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p
entries.append(.boostOverviewTitle(presentationData.theme, presentationData.strings.Stats_Boosts_OverviewHeader))
entries.append(.boostOverview(presentationData.theme, boostData))
//TODO:localize
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
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
}
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
@ -813,7 +812,7 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p
}
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?
@ -853,8 +852,8 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p
entries.append(.boostLinkInfo(presentationData.theme, presentationData.strings.Stats_Boosts_LinkInfo))
if giveawayAvailable {
entries.append(.gifts(presentationData.theme, "Get Boosts via Gifts"))
entries.append(.giftsInfo(presentationData.theme, "Get more boosts for your channel by gifting Premium to your subscribers."))
entries.append(.gifts(presentationData.theme, presentationData.strings.Stats_Boosts_GetBoosts))
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))) {
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
@ -226,24 +225,24 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
textSpacing += 13.0
if unclaimed {
title = "Unclaimed Prize"
title = item.presentationData.strings.Notification_PremiumPrize_Unclaimed
} else {
title = "Congratulations!"
title = item.presentationData.strings.Notification_PremiumPrize_Title
}
var peerName = ""
if let channelId, let channel = item.message.peers[channelId] {
peerName = EnginePeer(channel).compactDisplayTitle
}
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 {
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 {
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
buttonTitle = "Open Gift Link"
buttonTitle = item.presentationData.strings.Notification_PremiumPrize_View
hasServiceMessage = false
default:
break

View File

@ -25,7 +25,6 @@ import TelegramUIPreferences
import UndoUI
import TelegramStringFormatting
//TODO:localize
final class ShareWithPeersScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -941,9 +940,9 @@ final class ShareWithPeersScreenComponent: Component {
sectionTitle = environment.strings.Story_Privacy_WhoCanViewHeader
} else if section.id == 1 {
if case .members = component.stateContext.subject {
sectionTitle = "SUBSCRIBERS"
sectionTitle = environment.strings.BoostGift_Subscribers_SectionTitle
} else if case .channels = component.stateContext.subject {
sectionTitle = "CHANNELS"
sectionTitle = environment.strings.BoostGift_Channels_SectionTitle
} else {
sectionTitle = environment.strings.Story_Privacy_ContactsHeader
}
@ -1390,7 +1389,7 @@ final class ShareWithPeersScreenComponent: Component {
} else {
if case .members = component.stateContext.subject {
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 {
subtitle = nil
}
@ -1445,7 +1444,7 @@ final class ShareWithPeersScreenComponent: Component {
self.hapticFeedback.error()
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
}
if case .channels = component.stateContext.subject {
@ -1453,11 +1452,11 @@ final class ShareWithPeersScreenComponent: Component {
let alertController = textAlertController(
context: component.context,
forceTheme: environment.theme,
title: "Channel is Private",
text: "Are you sure you want to add a private channel? Users won't be able to join it without an invite link.",
title: environment.strings.BoostGift_Channels_PrivateChannel_Title,
text: environment.strings.BoostGift_Channels_PrivateChannel_Text,
actions: [
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()
})
]
@ -1475,7 +1474,7 @@ final class ShareWithPeersScreenComponent: Component {
self.hapticFeedback.error()
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
}
togglePeer()
@ -2018,9 +2017,9 @@ final class ShareWithPeersScreenComponent: Component {
let placeholder: String
switch component.stateContext.subject {
case .members:
placeholder = "Search Subscribers"
placeholder = environment.strings.BoostGift_Subscribers_Search
case .channels:
placeholder = "Search Channels"
placeholder = environment.strings.BoostGift_Channels_Search
case .chats:
placeholder = environment.strings.Story_Privacy_SearchChats
default:
@ -2367,13 +2366,13 @@ final class ShareWithPeersScreenComponent: Component {
case .contactsSearch:
title = ""
case .members:
title = "Gift Premium"
actionButtonTitle = "Save Recipients"
subtitle = "select up to 10 subscribers"
title = environment.strings.BoostGift_Subscribers_Title
subtitle = environment.strings.BoostGift_Subscribers_Subtitle("\(10)").string
actionButtonTitle = environment.strings.BoostGift_Subscribers_Save
case .channels:
title = "Add Channels"
actionButtonTitle = "Save Channels"
subtitle = "select up to \(component.context.userLimits.maxGiveawayChannelsCount) channels"
title = environment.strings.BoostGift_Channels_Title
subtitle = environment.strings.BoostGift_Channels_Subtitle("\(component.context.userLimits.maxGiveawayChannelsCount)").string
actionButtonTitle = environment.strings.BoostGift_Channels_Save
}
let titleComponent: AnyComponent<Empty>

View File

@ -180,8 +180,6 @@ final class MediaNavigationStripComponent: Component {
let itemHeight: CGFloat = 2.0
let minItemWidth: CGFloat = 2.0
var size = CGSize(width: availableSize.width, height: itemHeight)
var didSetCompletion = false
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))
if component.isSeeking {
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 {
continue

View File

@ -546,6 +546,14 @@ private final class StoryContainerScreenComponent: Component {
}
self.initialSeekTimestamp = 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
guard let self else {

View File

@ -95,12 +95,13 @@ final class StoryInteractionGuideComponent: Component {
self.component = component
self.state = state
let strings = component.strings
let sideInset: CGFloat = 48.0
//TODO:localize
let titleSize = self.titleLabel.update(
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: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height)
)
@ -114,7 +115,7 @@ final class StoryInteractionGuideComponent: Component {
let textSize = self.descriptionLabel.update(
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: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height)
)
@ -132,8 +133,8 @@ final class StoryInteractionGuideComponent: Component {
component: AnyComponent(
GuideItemComponent(
context: component.context,
title: "Go forward",
text: "Tap the screen",
title: strings.Story_Guide_ForwardTitle,
text: strings.Story_Guide_ForwardDescription,
animationName: "story_forward",
isPlaying: self.currentIndex == 0,
playbackCompleted: { [weak self] in
@ -151,8 +152,8 @@ final class StoryInteractionGuideComponent: Component {
component: AnyComponent(
GuideItemComponent(
context: component.context,
title: "Pause and Seek",
text: "Hold and move sideways",
title: strings.Story_Guide_PauseTitle,
text: strings.Story_Guide_PauseDescription,
animationName: "story_pause",
isPlaying: self.currentIndex == 1,
playbackCompleted: { [weak self] in
@ -170,8 +171,8 @@ final class StoryInteractionGuideComponent: Component {
component: AnyComponent(
GuideItemComponent(
context: component.context,
title: "Go back",
text: "Tap the left edge",
title: strings.Story_Guide_BackTitle,
text: strings.Story_Guide_BackDescription,
animationName: "story_back",
isPlaying: self.currentIndex == 2,
playbackCompleted: { [weak self] in
@ -189,8 +190,8 @@ final class StoryInteractionGuideComponent: Component {
component: AnyComponent(
GuideItemComponent(
context: component.context,
title: "Move between stories",
text: "Swipe left or right",
title: strings.Story_Guide_MoveTitle,
text: strings.Story_Guide_MoveDescription,
animationName: "story_move",
isPlaying: self.currentIndex == 3,
playbackCompleted: { [weak self] in
@ -222,7 +223,7 @@ final class StoryInteractionGuideComponent: Component {
let buttonSize = self.proceedButton.update(
transition: .immediate,
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
self?.handleTap()
}

View File

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

View File

@ -4710,7 +4710,7 @@ public final class StoryItemSetContainerComponent: Component {
let seekLabelSize = self.seekLabel.update(
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: {},
containerSize: availableSize
)

View File

@ -4787,17 +4787,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
case let .ongoing(_, status):
switch status {
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:
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:
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:
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, _, _, _, _):
let _ = status
content = .info(title: nil, text: "The giveaway is ended.", timeout: nil, customUndoText: "Learn More")
case .finished:
content = .info(title: nil, text: self.presentationData.strings.Chat_Giveaway_Toast_Ended, timeout: nil, customUndoText: self.presentationData.strings.Chat_Giveaway_Toast_LearnMore)
}
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 {
@ -19563,150 +19562,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
func displayGiveawayStatusInfo(messageId: EngineMessage.Id, giveawayInfo: PremiumGiveawayInfo) {
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId))
|> 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
}
var peerName = ""
if let peerId = giveaway.channelPeerIds.first, let peer = message.peers[peerId] {
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()
if let controller = giveawayInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, message: message, giveawayInfo: giveawayInfo) {
self.present(controller, in: .window(.root))
}
})
}