Various improvements

This commit is contained in:
Ilya Laktyushin 2024-09-01 16:03:17 +04:00
parent a5aac7fcd7
commit e27bb3a220
18 changed files with 215 additions and 76 deletions

View File

@ -5633,6 +5633,8 @@ Sorry for the inconvenience.";
"Settings.RemoveConfirmation" = "Remove";
"Conversation.ContextMenuOpenInfo" = "Open Info";
"Conversation.ContextMenuOpenProfile" = "Open Profile";
"Conversation.ContextMenuSendMessage" = "Send Message";
"Conversation.ContextMenuMention" = "Mention";
@ -12883,3 +12885,22 @@ Sorry for the inconvenience.";
"WebBrowser.ShowInstantView" = "Show Instant View";
"WebBrowser.HideInstantView" = "Hide Instant View";
"Stats.Boosts.Stars_1" = "%@ Star";
"Stats.Boosts.Stars_any" = "%@ Stars";
"Stats.Boosts.StarsBoosts" = "12-month boosts";
"BoostGift.PrepaidGiveaway.StarsCount_1" = "%@ Star";
"BoostGift.PrepaidGiveaway.StarsCount_any" = "%@ Stars";
"BoostGift.PrepaidGiveaway.StarsBoosts" = "12-month boosts";
"Stars.Transaction.Giveaway.Boost.Stars_1" = "%@ Star";
"Stars.Transaction.Giveaway.Boost.Stars_any" = "%@ Stars";
"Stars.Transaction.Giveaway.Boost.Boosts_1" = "%@ Boost";
"Stars.Transaction.Giveaway.Boost.Boosts_any" = "%@ Boosts";
"Stars.Transaction.Giveaway.Boost.Members_1" = "%@ Member";
"Stars.Transaction.Giveaway.Boost.Members_any" = "%@ Members";
"Stars.Transaction.Giveaway.Boost.Subscriber_1" = "%@ Subscriber";
"Stars.Transaction.Giveaway.Boost.Subscriber_any" = "%@ Subscribers";
"Conversation.ContextMenuCopyEmail" = "Copy Email";

View File

@ -1016,6 +1016,7 @@ public protocol SharedAccountContext: AnyObject {
func makeStarsAmountScreen(context: AccountContext, initialValue: Int64?, completion: @escaping (Int64) -> Void) -> ViewController
func makeStarsWithdrawalScreen(context: AccountContext, stats: StarsRevenueStats, completion: @escaping (Int64) -> Void) -> ViewController
func makeStarsGiftScreen(context: AccountContext, message: EngineMessage) -> ViewController
func makeStarsGiveawayBoostScreen(context: AccountContext, peerId: EnginePeer.Id, boost: ChannelBoostersContext.State.Boost) -> ViewController
func makeMiniAppListScreenInitialData(context: AccountContext) -> Signal<MiniAppListScreenInitialData, NoError>
func makeMiniAppListScreen(context: AccountContext, initialData: MiniAppListScreenInitialData) -> ViewController

View File

@ -459,6 +459,8 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .prepaid(_, title, subtitle, prepaidGiveaway):
let color: GiftOptionItem.Icon.Color
let icon: String
let boosts: Int32
switch prepaidGiveaway.prize {
case let .premium(months):
switch months {
@ -471,16 +473,14 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
default:
color = .blue
}
case let .stars(amount, _):
if amount <= 1000 {
color = .green
} else if amount < 2500 {
color = .blue
} else {
color = .red
}
icon = "Premium/Giveaway"
boosts = prepaidGiveaway.quantity * 4
case let .stars(_, boostCount):
color = .stars
icon = "Premium/PremiumStar"
boosts = boostCount
}
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: color, name: "Premium/Giveaway"), title: title, titleFont: .bold, titleBadge: "\(prepaidGiveaway.quantity * 4)", subtitle: subtitle, sectionId: self.section, action: nil)
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: color, name: icon), title: title, titleFont: .bold, titleBadge: "\(boosts)", subtitle: subtitle, sectionId: self.section, action: nil)
case let .starsHeader(_, text, additionalText):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: additionalText, color: .generic), sectionId: self.section)
case let .stars(_, _, stars, title, subtitle, label, isSelected, maxWinners):
@ -778,9 +778,8 @@ private func createGiveawayControllerEntries(
title = presentationData.strings.BoostGift_PrepaidGiveawayCount(prepaidGiveaway.quantity)
text = presentationData.strings.BoostGift_PrepaidGiveawayMonths("\(months)").string
case let .stars(stars, _):
//TODO:localize
title = "\(stars) Telegram Stars"
text = "among \(prepaidGiveaway.quantity) winners"
title = presentationData.strings.BoostGift_PrepaidGiveaway_StarsCount(Int32(stars))
text = presentationData.strings.BoostGift_PrepaidGiveaway_StarsBoosts
}
entries.append(.prepaid(presentationData.theme, title, text, prepaidGiveaway))
}

View File

@ -223,7 +223,9 @@ class CreateGiveawayHeaderItemNode: ItemListControllerHeaderItemNode {
isVisible: true,
hasIdleAnimations: true,
colors: colors,
particleColor: particleColor
particleColor: particleColor,
backgroundColor: self.item.theme.list.blocksBackgroundColor
))
let containerSize = CGSize(width: min(414.0, layout.size.width), height: 220.0)

View File

@ -614,6 +614,7 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
)),
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
followContentSizeChanges: true,
clipsContent: true,
animateOut: animateOut
),
environment: {

View File

@ -682,7 +682,8 @@ private func privacyAndSecurityControllerEntries(
} else {
value = privacySettings.accountRemovalTimeout
}
entries.append(.accountTimeout(presentationData.theme, presentationData.strings.PrivacySettings_DeleteAccountIfAwayFor, timeIntervalString(strings: presentationData.strings, value: value)))
entries.append(.accountTimeout(presentationData.theme, presentationData.strings.PrivacySettings_DeleteAccountIfAwayFor, presentationData.strings.MessageTimer_Months(max(1, value / (60 * 60 * 24 * 30)))))
} else {
entries.append(.accountTimeout(presentationData.theme, presentationData.strings.PrivacySettings_DeleteAccountIfAwayFor, presentationData.strings.Channel_NotificationLoading))
}
@ -1174,10 +1175,11 @@ public func privacyAndSecurityController(
1 * 30 * 24 * 60 * 60,
3 * 30 * 24 * 60 * 60,
6 * 30 * 24 * 60 * 60,
365 * 24 * 60 * 60
365 * 24 * 60 * 60,
548 * 24 * 60 * 60
]
var timeoutItems: [ActionSheetItem] = timeoutValues.map { value in
return ActionSheetButtonItem(title: timeIntervalString(strings: presentationData.strings, value: value), action: {
return ActionSheetButtonItem(title: presentationData.strings.MessageTimer_Months(max(1, value / (60 * 60 * 24 * 30))), action: {
dismissAction()
timeoutAction(value)
})

View File

@ -962,7 +962,7 @@ private enum StatsEntry: ItemListNodeEntry {
case let .booster(_, _, _, boost):
let count = boost.multiplier
let expiresValue = stringForDate(timestamp: boost.expires, strings: presentationData.strings)
let expiresString: String
var expiresString: String
let durationMonths = Int32(round(Float(boost.expires - boost.date) / (86400.0 * 30.0)))
let durationString = presentationData.strings.Stats_Boosts_ShortMonth("\(durationMonths)").string
@ -998,17 +998,23 @@ private enum StatsEntry: ItemListNodeEntry {
expiresString = presentationData.strings.Stats_Boosts_ExpiresOn(expiresValue).string
}
} else {
expiresString = "\(durationString)\(expiresValue)"
if boost.flags.contains(.isUnclaimed) {
title = presentationData.strings.Stats_Boosts_Unclaimed
icon = .image(color: color, name: "Premium/Unclaimed")
} else if boost.flags.contains(.isGiveaway) {
title = presentationData.strings.Stats_Boosts_ToBeDistributed
icon = .image(color: color, name: "Premium/ToBeDistributed")
if let stars = boost.stars {
title = presentationData.strings.Stats_Boosts_Stars(Int32(stars))
icon = .image(color: .stars, name: "Premium/PremiumStar")
expiresString = expiresValue
} else {
title = presentationData.strings.Stats_Boosts_ToBeDistributed
icon = .image(color: color, name: "Premium/ToBeDistributed")
}
} else {
title = "Unknown"
icon = .image(color: color, name: "Premium/ToBeDistributed")
}
expiresString = "\(durationString)\(expiresValue)"
}
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: icon, title: title, titleFont: .bold, titleBadge: count > 1 ? "\(count)" : nil, subtitle: expiresString, label: label.flatMap { .semitransparent($0) }, sectionId: self.section, action: {
arguments.openBoost(boost)
@ -1038,6 +1044,8 @@ private enum StatsEntry: ItemListNodeEntry {
})
case let .boostPrepaid(_, _, title, subtitle, prepaidGiveaway):
let color: GiftOptionItem.Icon.Color
let icon: String
var boosts: Int32
switch prepaidGiveaway.prize {
case let .premium(months):
switch months {
@ -1050,16 +1058,14 @@ private enum StatsEntry: ItemListNodeEntry {
default:
color = .blue
}
case let .stars(amount, _):
if amount <= 1000 {
color = .green
} else if amount < 2500 {
color = .blue
} else {
color = .red
}
icon = "Premium/Giveaway"
boosts = prepaidGiveaway.quantity * 4
case let .stars(_, boostCount):
color = .stars
icon = "Premium/PremiumStar"
boosts = boostCount
}
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: color, name: "Premium/Giveaway"), title: title, titleFont: .bold, titleBadge: "\(prepaidGiveaway.quantity * 4)", subtitle: subtitle, label: nil, sectionId: self.section, action: {
return GiftOptionItem(presentationData: presentationData, context: arguments.context, icon: .image(color: color, name: icon), title: title, titleFont: .bold, titleBadge: "\(boosts)", subtitle: subtitle, label: nil, sectionId: self.section, action: {
arguments.createPrepaidGiveaway(prepaidGiveaway)
})
case let .adsHeader(_, text):
@ -1441,8 +1447,8 @@ private func boostsEntries(
title = presentationData.strings.Stats_Boosts_PrepaidGiveawayCount(giveaway.quantity)
text = presentationData.strings.Stats_Boosts_PrepaidGiveawayMonths("\(months)").string
case let .stars(stars, _):
title = "\(stars) Telegram Stars"
text = "among \(giveaway.quantity) winners"
title = presentationData.strings.Stats_Boosts_Stars(Int32(stars))
text = presentationData.strings.Stats_Boosts_StarsBoosts
}
entries.append(.boostPrepaid(i, presentationData.theme, title, text, giveaway))
i += 1
@ -1942,8 +1948,13 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
}
if boost.peer == nil, boost.flags.contains(.isGiveaway) && !boost.flags.contains(.isUnclaimed) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentImpl?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.Stats_Boosts_TooltipToBeDistributed, timeout: nil, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }))
if let _ = boost.stars {
let controller = context.sharedContext.makeStarsGiveawayBoostScreen(context: context, peerId: peerId, boost: boost)
pushImpl?(controller)
} else {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentImpl?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.Stats_Boosts_TooltipToBeDistributed, timeout: nil, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }))
}
return
}

View File

@ -200,7 +200,7 @@ private final class ChannelBoostersContextImpl {
var result: [ChannelBoostersContext.State.Boost] = []
for boost in cachedResult.boosts {
let peer = boost.peerId.flatMap { transaction.getPeer($0) }
result.append(ChannelBoostersContext.State.Boost(flags: ChannelBoostersContext.State.Boost.Flags(rawValue: boost.flags), id: boost.id, peer: peer.flatMap { EnginePeer($0) }, date: boost.date, expires: boost.expires, multiplier: boost.multiplier, slug: boost.slug))
result.append(ChannelBoostersContext.State.Boost(flags: ChannelBoostersContext.State.Boost.Flags(rawValue: boost.flags), id: boost.id, peer: peer.flatMap { EnginePeer($0) }, date: boost.date, expires: boost.expires, multiplier: boost.multiplier, stars: boost.stars, slug: boost.slug, giveawayMessageId: boost.giveawayMessageId))
}
return (result, cachedResult.count, true)
} else {
@ -452,6 +452,7 @@ private final class CachedChannelBoosters: Codable {
case date
case expires
case multiplier
case stars
case slug
case channelPeerId
case giveawayMessageId
@ -463,17 +464,19 @@ private final class CachedChannelBoosters: Codable {
var date: Int32
var expires: Int32
var multiplier: Int32
var stars: Int64?
var slug: String?
var channelPeerId: EnginePeer.Id
var giveawayMessageId: EngineMessage.Id?
init(flags: Int32, id: String, peerId: EnginePeer.Id?, date: Int32, expires: Int32, multiplier: Int32, slug: String?, channelPeerId: EnginePeer.Id, giveawayMessageId: EngineMessage.Id?) {
init(flags: Int32, id: String, peerId: EnginePeer.Id?, date: Int32, expires: Int32, multiplier: Int32, stars: Int64?, slug: String?, channelPeerId: EnginePeer.Id, giveawayMessageId: EngineMessage.Id?) {
self.flags = flags
self.id = id
self.peerId = peerId
self.date = date
self.expires = expires
self.multiplier = multiplier
self.stars = stars
self.slug = slug
self.channelPeerId = channelPeerId
self.giveawayMessageId = giveawayMessageId
@ -488,6 +491,7 @@ private final class CachedChannelBoosters: Codable {
self.date = try container.decode(Int32.self, forKey: .date)
self.expires = try container.decode(Int32.self, forKey: .expires)
self.multiplier = try container.decode(Int32.self, forKey: .multiplier)
self.stars = try container.decodeIfPresent(Int64.self, forKey: .stars)
self.slug = try container.decodeIfPresent(String.self, forKey: .slug)
self.channelPeerId = EnginePeer.Id(try container.decode(Int64.self, forKey: .channelPeerId))
self.giveawayMessageId = try container.decodeIfPresent(Int32.self, forKey: .giveawayMessageId).flatMap { EngineMessage.Id(peerId: self.channelPeerId, namespace: Namespaces.Message.Cloud, id: $0) }
@ -502,6 +506,7 @@ private final class CachedChannelBoosters: Codable {
try container.encode(self.date, forKey: .date)
try container.encode(self.expires, forKey: .expires)
try container.encode(self.multiplier, forKey: .multiplier)
try container.encodeIfPresent(self.stars, forKey: .stars)
try container.encodeIfPresent(self.slug, forKey: .slug)
try container.encode(self.channelPeerId.toInt64(), forKey: .channelPeerId)
try container.encodeIfPresent(self.giveawayMessageId?.id, forKey: .giveawayMessageId)
@ -518,7 +523,7 @@ private final class CachedChannelBoosters: Codable {
}
init(channelPeerId: EnginePeer.Id, boosts: [ChannelBoostersContext.State.Boost], count: Int32) {
self.boosts = boosts.map { CachedBoost(flags: $0.flags.rawValue, id: $0.id, peerId: $0.peer?.id, date: $0.date, expires: $0.expires, multiplier: $0.multiplier, slug: $0.slug, channelPeerId: channelPeerId, giveawayMessageId: $0.giveawayMessageId) }
self.boosts = boosts.map { CachedBoost(flags: $0.flags.rawValue, id: $0.id, peerId: $0.peer?.id, date: $0.date, expires: $0.expires, multiplier: $0.multiplier, stars: $0.stars, slug: $0.slug, channelPeerId: channelPeerId, giveawayMessageId: $0.giveawayMessageId) }
self.count = count
}

View File

@ -76,6 +76,7 @@ public final class PremiumStarComponent: Component {
let hasIdleAnimations: Bool
let colors: [UIColor]?
let particleColor: UIColor?
let backgroundColor: UIColor?
public init(
theme: PresentationTheme,
@ -83,7 +84,8 @@ public final class PremiumStarComponent: Component {
isVisible: Bool,
hasIdleAnimations: Bool,
colors: [UIColor]? = nil,
particleColor: UIColor? = nil
particleColor: UIColor? = nil,
backgroundColor: UIColor? = nil
) {
self.theme = theme
self.isIntro = isIntro
@ -91,10 +93,11 @@ public final class PremiumStarComponent: Component {
self.hasIdleAnimations = hasIdleAnimations
self.colors = colors
self.particleColor = particleColor
self.backgroundColor = backgroundColor
}
public static func ==(lhs: PremiumStarComponent, rhs: PremiumStarComponent) -> Bool {
return lhs.theme === rhs.theme && lhs.isIntro == rhs.isIntro && lhs.isVisible == rhs.isVisible && lhs.hasIdleAnimations == rhs.hasIdleAnimations && lhs.colors == rhs.colors && lhs.particleColor == rhs.particleColor
return lhs.theme === rhs.theme && lhs.isIntro == rhs.isIntro && lhs.isVisible == rhs.isVisible && lhs.hasIdleAnimations == rhs.hasIdleAnimations && lhs.colors == rhs.colors && lhs.particleColor == rhs.particleColor && lhs.backgroundColor == rhs.backgroundColor
}
public final class View: UIView, SCNSceneRendererDelegate, ComponentTaggedView {
@ -694,8 +697,10 @@ public final class PremiumStarComponent: Component {
self.playAppearanceAnimation(velocity: nil, mirror: component.colors?.contains(UIColor(rgb: 0xe57d02)) == true, explode: true, force: true)
}
if let _ = component.particleColor {
self.sceneView.backgroundColor = component.theme.list.blocksBackgroundColor
if let backgroundColor = component.backgroundColor {
self.sceneView.backgroundColor = backgroundColor
} else {
self.sceneView.backgroundColor = .clear
}
self.sceneView.bounds = CGRect(origin: .zero, size: CGSize(width: availableSize.width * 2.0, height: availableSize.height * 2.0))

View File

@ -707,7 +707,11 @@ public final class StarsImageComponent: Component {
} else {
avatarNode = ImageNode()
avatarNode.displaysAsynchronously = false
containerNode.view.addSubview(avatarNode.view)
if let smallIconOutlineView = self.smallIconOutlineView {
containerNode.view.insertSubview(avatarNode.view, belowSubview: smallIconOutlineView)
} else {
containerNode.view.addSubview(avatarNode.view)
}
self.avatarNode = avatarNode
avatarNode.setSignal(peerAvatarCompleteImage(account: component.context.account, peer: peer, size: imageSize, font: avatarPlaceholderFont(size: 43.0), fullSize: true))

View File

@ -786,7 +786,8 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
UIColor(rgb: 0xf9b004),
UIColor(rgb: 0xfdd219)
],
particleColor: UIColor(rgb: 0xf9b004)
particleColor: UIColor(rgb: 0xf9b004),
backgroundColor: environment.theme.list.blocksBackgroundColor
),
availableSize: CGSize(width: min(414.0, context.availableSize.width), height: 220.0),
transition: context.transition

View File

@ -35,6 +35,7 @@ swift_library(
"//submodules/TelegramUI/Components/Stars/StarsAvatarComponent",
"//submodules/GalleryUI",
"//submodules/TelegramUI/Components/MiniAppListScreen",
"//submodules/TelegramUI/Components/Premium/PremiumStarComponent",
],
visibility = [
"//visibility:public",

View File

@ -24,6 +24,7 @@ import StarsImageComponent
import GalleryUI
import StarsAvatarComponent
import MiniAppListScreen
import PremiumStarComponent
private final class StarsTransactionSheetContent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -104,6 +105,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
peerIds.append(subscription.peer.id)
case let .importer(_, _, importer, _):
peerIds.append(importer.peer.peerId)
case let .boost(peerId, _):
peerIds.append(peerId)
}
self.disposable = (context.engine.data.get(
@ -141,6 +144,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let closeButton = Child(Button.self)
let title = Child(MultilineTextComponent.self)
let star = Child(StarsImageComponent.self)
let activeStar = Child(PremiumStarComponent.self)
let amountBackground = Child(RoundedRectangle.self)
let amount = Child(BalancedTextComponent.self)
let amountStar = Child(BundleIconComponent.self)
let description = Child(MultilineTextComponent.self)
@ -191,6 +196,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let titleText: String
let amountText: String
var descriptionText: String
var boostsText: String?
let additionalText = strings.Stars_Transaction_Terms
var buttonText: String? = strings.Common_OK
var buttonIsDestructive = false
@ -206,6 +212,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
var via: String?
var messageId: EngineMessage.Id?
var toPeer: EnginePeer?
// var toString: String?
var transactionPeer: StarsContext.State.Transaction.Peer?
var media: [AnyMediaReference] = []
var photo: TelegramMediaWebFile?
@ -217,9 +224,24 @@ private final class StarsTransactionSheetContent: CombinedComponent {
var isCancelled = false
var isReaction = false
var giveawayMessageId: MessageId?
var isBoost = false
var delayedCloseOnOpenPeer = true
switch subject {
case let .boost(peerId, boost):
guard let stars = boost.stars else {
fatalError()
}
let boosts = boost.multiplier
titleText = strings.Stars_Transaction_Giveaway_Boost_Stars(Int32(stars))
descriptionText = ""
boostsText = strings.Stars_Transaction_Giveaway_Boost_Boosts(boosts)
count = stars
date = boost.date
toPeer = state.peerMap[peerId]
// toString = strings.Stars_Transaction_Giveaway_Boost_Subscribers(boost.quantity)
giveawayMessageId = boost.giveawayMessageId
isBoost = true
case let .importer(peer, pricing, importer, usdRate):
let usdValue = formatTonUsdValue(pricing.amount, divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat)
titleText = strings.Stars_Transaction_Subscription_Title
@ -493,7 +515,14 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let formattedAmount = presentationStringsFormattedNumber(abs(Int32(count)), dateTimeFormat.groupingSeparator)
let countColor: UIColor
if isSubscription || isSubscriber {
var countFont: UIFont = isSubscription || isSubscriber ? Font.regular(17.0) : Font.semibold(17.0)
var countBackgroundColor: UIColor?
if let boostsText {
amountText = boostsText
countColor = .white
countBackgroundColor = UIColor(rgb: 0x9671ff)
countFont = Font.with(size: 14.0, design: .round, weight: .semibold)
} else if isSubscription || isSubscriber {
amountText = strings.Stars_Transaction_Subscription_PerMonth(formattedAmount).string
countColor = theme.list.itemSecondaryTextColor
} else if countIsGeneric {
@ -542,23 +571,45 @@ private final class StarsTransactionSheetContent: CombinedComponent {
} else {
imageIcon = nil
}
let star = star.update(
component: StarsImageComponent(
context: component.context,
subject: imageSubject,
theme: theme,
diameter: 90.0,
backgroundColor: theme.actionSheet.opaqueItemBackgroundColor,
icon: imageIcon,
action: !media.isEmpty ? { transitionNode, addToTransitionSurface in
component.openMedia(media.map { $0.media }, transitionNode, addToTransitionSurface)
} : nil
),
availableSize: CGSize(width: context.availableSize.width, height: 200.0),
transition: .immediate
)
let amountAttributedText = NSMutableAttributedString(string: amountText, font: isSubscription || isSubscriber ? Font.regular(17.0) : Font.semibold(17.0), textColor: countColor)
var starChild: _UpdatedChildComponent
if isBoost {
starChild = activeStar.update(
component: PremiumStarComponent(
theme: theme,
isIntro: false,
isVisible: true,
hasIdleAnimations: true,
colors: [
UIColor(rgb: 0xe57d02),
UIColor(rgb: 0xf09903),
UIColor(rgb: 0xf9b004),
UIColor(rgb: 0xfdd219)
],
particleColor: UIColor(rgb: 0xf9b004),
backgroundColor: theme.actionSheet.opaqueItemBackgroundColor
),
availableSize: CGSize(width: context.availableSize.width, height: 200.0),
transition: .immediate
)
} else {
starChild = star.update(
component: StarsImageComponent(
context: component.context,
subject: imageSubject,
theme: theme,
diameter: 90.0,
backgroundColor: theme.actionSheet.opaqueItemBackgroundColor,
icon: imageIcon,
action: !media.isEmpty ? { transitionNode, addToTransitionSurface in
component.openMedia(media.map { $0.media }, transitionNode, addToTransitionSurface)
} : nil
),
availableSize: CGSize(width: context.availableSize.width, height: 200.0),
transition: .immediate
)
}
let amountAttributedText = NSMutableAttributedString(string: amountText, font: countFont, textColor: countColor)
let amount = amount.update(
component: BalancedTextComponent(
text: .plain(amountAttributedText),
@ -572,7 +623,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let amountStar = amountStar.update(
component: BundleIconComponent(
name: "Premium/Stars/StarMedium",
name: boostsText != nil ? "Premium/BoostButtonIcon" : "Premium/Stars/StarMedium",
tintColor: nil
),
availableSize: context.availableSize,
@ -824,17 +875,17 @@ private final class StarsTransactionSheetContent: CombinedComponent {
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
transition: .immediate
)
context.add(starChild
.position(CGPoint(x: context.availableSize.width / 2.0, y: starChild.size.height / 2.0 - 19.0))
)
context.add(title
.position(CGPoint(x: context.availableSize.width / 2.0, y: 31.0 + 125.0))
)
context.add(star
.position(CGPoint(x: context.availableSize.width / 2.0, y: star.size.height / 2.0 - 19.0))
)
var originY: CGFloat = 0.0
originY += star.size.height - 23.0
originY += starChild.size.height - 23.0
var descriptionSize: CGSize = .zero
if !descriptionText.isEmpty {
@ -884,7 +935,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
originY += description.size.height + 10.0
}
let amountSpacing: CGFloat = 1.0
let amountSpacing: CGFloat = countBackgroundColor != nil ? 4.0 : 1.0
var totalAmountWidth: CGFloat = amount.size.width + amountSpacing + amountStar.size.width
var amountOriginX: CGFloat = floor(context.availableSize.width - totalAmountWidth) / 2.0
if let (statusText, statusColor) = transactionStatus {
@ -928,7 +979,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let amountLabelOriginX: CGFloat
let amountStarOriginX: CGFloat
if isSubscription || isSubscriber {
if isSubscription || isSubscriber || boostsText != nil {
amountStarOriginX = amountOriginX + amountStar.size.width / 2.0
amountLabelOriginX = amountOriginX + amountStar.size.width + amountSpacing + amount.size.width / 2.0
} else {
@ -936,11 +987,26 @@ private final class StarsTransactionSheetContent: CombinedComponent {
amountStarOriginX = amountOriginX + amount.size.width + amountSpacing + amountStar.size.width / 2.0
}
var amountLabelOffsetY: CGFloat = 0.0
var amountStarOffsetY: CGFloat = 0.0
if let countBackgroundColor {
let amountBackground = amountBackground.update(
component: RoundedRectangle(color: countBackgroundColor, cornerRadius: 23 / 2.0),
availableSize: CGSize(width: totalAmountWidth + 14.0, height: 23.0),
transition: .immediate
)
context.add(amountBackground
.position(CGPoint(x: context.availableSize.width / 2.0, y: amountOrigin + amount.size.height / 2.0 + 1.0))
)
amountLabelOffsetY = 2.0
amountStarOffsetY = 5.0
}
context.add(amount
.position(CGPoint(x: amountLabelOriginX, y: amountOrigin + amount.size.height / 2.0))
.position(CGPoint(x: amountLabelOriginX, y: amountOrigin + amount.size.height / 2.0 + amountLabelOffsetY))
)
context.add(amountStar
.position(CGPoint(x: amountStarOriginX, y: amountOrigin + amountStar.size.height / 2.0 - UIScreenPixel))
.position(CGPoint(x: amountStarOriginX, y: amountOrigin + amountStar.size.height / 2.0 - UIScreenPixel + amountStarOffsetY))
)
context.add(table
@ -1166,6 +1232,7 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
case gift(EngineMessage)
case subscription(StarsContext.State.Subscription)
case importer(EnginePeer, StarsSubscriptionPricing, PeerInvitationImportersState.Importer, Double)
case boost(EnginePeer.Id, ChannelBoostersContext.State.Boost)
}
private let context: AccountContext

View File

@ -424,7 +424,8 @@ final class StarsTransactionsScreenComponent: Component {
UIColor(rgb: 0xf9b004),
UIColor(rgb: 0xfdd219)
],
particleColor: UIColor(rgb: 0xf9b004)
particleColor: UIColor(rgb: 0xf9b004),
backgroundColor: environment.theme.list.blocksBackgroundColor
)),
environment: {},
containerSize: CGSize(width: min(414.0, availableSize.width), height: 220.0)

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "premium_30.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -128,13 +128,15 @@ extension ChatControllerImpl {
var canAddToReadingList = true
let canOpenIn = availableOpenInOptions(context: self.context, item: .url(url: url)).count > 1
var isEmail = false
let mailtoString = "mailto:"
var openText = self.presentationData.strings.Conversation_LinkDialogOpen
var copyText = self.presentationData.strings.Conversation_ContextMenuCopyLink
if cleanUrl.hasPrefix(mailtoString) {
canAddToReadingList = false
cleanUrl = String(cleanUrl[cleanUrl.index(cleanUrl.startIndex, offsetBy: mailtoString.distance(from: mailtoString.startIndex, to: mailtoString.endIndex))...])
// isEmail = true
copyText = self.presentationData.strings.Conversation_ContextMenuCopyEmail
isEmail = true
} else if canOpenIn {
openText = self.presentationData.strings.Conversation_FileOpenIn
}
@ -168,16 +170,16 @@ extension ChatControllerImpl {
)
items.append(
.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuCopyLink, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
.action(ContextMenuActionItem(text: copyText, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
f(.default)
guard let self else {
return
}
UIPasteboard.general.string = url
UIPasteboard.general.string = cleanUrl
self.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
self.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: isEmail ? presentationData.strings.Conversation_EmailCopied : presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
}))
)

View File

@ -2791,6 +2791,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return StarsTransactionScreen(context: context, subject: .gift(message))
}
public func makeStarsGiveawayBoostScreen(context: AccountContext, peerId: EnginePeer.Id, boost: ChannelBoostersContext.State.Boost) -> ViewController {
return StarsTransactionScreen(context: context, subject: .boost(peerId, boost))
}
public func makeMiniAppListScreenInitialData(context: AccountContext) -> Signal<MiniAppListScreenInitialData, NoError> {
return MiniAppListScreen.initialData(context: context)
}