mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
b2351194d4
commit
4aeab37c70
@ -13876,3 +13876,11 @@ Sorry for the inconvenience.";
|
||||
"Chat.PanelCustomStatusShortInfo" = "%@ is a mark for [Premium subscribers >]()";
|
||||
|
||||
"Chat.InputTextPaidMessagePlaceholder" = "Message for %@";
|
||||
|
||||
"Privacy.Messages.Stars_1" = "%@ Star";
|
||||
"Privacy.Messages.Stars_any" = "%@ Stars";
|
||||
"Privacy.Messages.Unlock" = "Unlock with Telegram Premium";
|
||||
|
||||
"Premium.PaidMessages" = "Paid Messages";
|
||||
"Premium.PaidMessagesInfo" = "Charge a fee for messages from non-contacts or new senders.";
|
||||
"Premium.PaidMessages.Proceed" = "About Telegram Premium";
|
||||
|
@ -1357,25 +1357,29 @@ public struct StarsSubscriptionConfiguration {
|
||||
maxFee: 2500,
|
||||
usdWithdrawRate: 1200,
|
||||
paidMessageMaxAmount: 10000,
|
||||
paidMessageCommissionPermille: 850
|
||||
paidMessageCommissionPermille: 850,
|
||||
paidMessagesAvailable: false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
public let maxFee: Int64
|
||||
public let usdWithdrawRate: Int64
|
||||
public let paidMessageMaxAmount: Int64
|
||||
public let paidMessageCommissionPermille: Int32
|
||||
public let paidMessagesAvailable: Bool
|
||||
|
||||
fileprivate init(
|
||||
maxFee: Int64,
|
||||
usdWithdrawRate: Int64,
|
||||
paidMessageMaxAmount: Int64,
|
||||
paidMessageCommissionPermille: Int32
|
||||
paidMessageCommissionPermille: Int32,
|
||||
paidMessagesAvailable: Bool
|
||||
) {
|
||||
self.maxFee = maxFee
|
||||
self.usdWithdrawRate = usdWithdrawRate
|
||||
self.paidMessageMaxAmount = paidMessageMaxAmount
|
||||
self.paidMessageCommissionPermille = paidMessageCommissionPermille
|
||||
self.paidMessagesAvailable = paidMessagesAvailable
|
||||
}
|
||||
|
||||
public static func with(appConfiguration: AppConfiguration) -> StarsSubscriptionConfiguration {
|
||||
@ -1384,11 +1388,14 @@ public struct StarsSubscriptionConfiguration {
|
||||
let usdWithdrawRate = (data["stars_usd_withdraw_rate_x1000"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.usdWithdrawRate
|
||||
let paidMessageMaxAmount = (data["stars_paid_message_amount_max"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.paidMessageMaxAmount
|
||||
let paidMessageCommissionPermille = (data["stars_paid_message_commission_permille"] as? Double).flatMap(Int32.init) ?? StarsSubscriptionConfiguration.defaultValue.paidMessageCommissionPermille
|
||||
let paidMessagesAvailable = (data["stars_paid_messages_available"] as? Bool) ?? StarsSubscriptionConfiguration.defaultValue.paidMessagesAvailable
|
||||
|
||||
return StarsSubscriptionConfiguration(
|
||||
maxFee: maxFee,
|
||||
usdWithdrawRate: usdWithdrawRate,
|
||||
paidMessageMaxAmount: paidMessageMaxAmount,
|
||||
paidMessageCommissionPermille: paidMessageCommissionPermille
|
||||
paidMessageCommissionPermille: paidMessageCommissionPermille,
|
||||
paidMessagesAvailable: paidMessagesAvailable
|
||||
)
|
||||
} else {
|
||||
return .defaultValue
|
||||
|
@ -42,6 +42,7 @@ public enum PremiumIntroSource {
|
||||
case folderTags
|
||||
case animatedEmoji
|
||||
case messageEffects
|
||||
case paidMessages
|
||||
}
|
||||
|
||||
public enum PremiumGiftSource: Equatable {
|
||||
@ -79,6 +80,7 @@ public enum PremiumDemoSubject {
|
||||
case folderTags
|
||||
case business
|
||||
case messageEffects
|
||||
case paidMessages
|
||||
|
||||
case businessLocation
|
||||
case businessHours
|
||||
|
@ -347,6 +347,8 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode {
|
||||
iconFrame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - iconSize.width) / 2.0), y: floor((contentSize.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
}
|
||||
strongSelf.imageNode.frame = iconFrame
|
||||
} else {
|
||||
strongSelf.imageNode.image = nil
|
||||
}
|
||||
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: strongSelf.backgroundNode.frame.height + UIScreenPixel + UIScreenPixel))
|
||||
|
@ -141,7 +141,7 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
|
||||
if (vertical)
|
||||
startPosition = 2 * visualMargin + visualTotalLength - startPosition;
|
||||
|
||||
CGFloat endPosition = visualMargin + visualTotalLength / (_maximumValue - _minimumValue) * (ABS(_minimumValue) + 1.0);
|
||||
CGFloat endPosition = visualMargin + visualTotalLength / (_maximumValue - _minimumValue) * (ABS(_minimumValue) + _maximumValue);
|
||||
if (vertical)
|
||||
endPosition = 2 * visualMargin + visualTotalLength - endPosition;
|
||||
|
||||
|
@ -93,7 +93,7 @@ private enum ChannelPermissionsEntry: ItemListNodeEntry {
|
||||
case chargeForMessagesInfo(PresentationTheme, String)
|
||||
|
||||
case messagePriceHeader(PresentationTheme, String)
|
||||
case messagePrice(PresentationTheme, Int64, String)
|
||||
case messagePrice(PresentationTheme, Int64, Int64, String)
|
||||
case messagePriceInfo(PresentationTheme, String)
|
||||
|
||||
case unrestrictBoostersSwitch(PresentationTheme, String, Bool)
|
||||
@ -241,8 +241,8 @@ private enum ChannelPermissionsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .messagePrice(lhsTheme, lhsValue, lhsPrice):
|
||||
if case let .messagePrice(rhsTheme, rhsValue, rhsPrice) = rhs, lhsTheme === rhsTheme, lhsValue == rhsValue, lhsPrice == rhsPrice {
|
||||
case let .messagePrice(lhsTheme, lhsValue, lhsMaxValue, lhsPrice):
|
||||
if case let .messagePrice(rhsTheme, rhsValue, rhsMaxValue, rhsPrice) = rhs, lhsTheme === rhsTheme, lhsValue == rhsValue, lhsMaxValue == rhsMaxValue, lhsPrice == rhsPrice {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -424,8 +424,8 @@ private enum ChannelPermissionsEntry: ItemListNodeEntry {
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(value), sectionId: self.section)
|
||||
case let .messagePriceHeader(_, value):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: value, sectionId: self.section)
|
||||
case let .messagePrice(_, value, price):
|
||||
return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, minValue: 1, maxValue: 10000, value: value, price: price, sectionId: self.section, updated: { value in
|
||||
case let .messagePrice(_, value, maxValue, price):
|
||||
return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, isEnabled: true, minValue: 1, maxValue: maxValue, value: value, price: price, sectionId: self.section, updated: { value in
|
||||
arguments.updateStarsAmount(StarsAmount(value: value, nanos: 0))
|
||||
})
|
||||
case let .messagePriceInfo(_, value):
|
||||
@ -733,7 +733,7 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen
|
||||
price = "≈\(formatTonUsdValue(sendPaidMessageStars, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
|
||||
entries.append(.messagePriceHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_MessagePrice))
|
||||
entries.append(.messagePrice(presentationData.theme, sendPaidMessageStars, price))
|
||||
entries.append(.messagePrice(presentationData.theme, sendPaidMessageStars, configuration.paidMessageMaxAmount, price))
|
||||
entries.append(.messagePriceInfo(presentationData.theme, presentationData.strings.GroupInfo_Permissions_MessagePriceInfo("\(configuration.paidMessageCommissionPermille / 10)", price).string))
|
||||
}
|
||||
}
|
||||
|
@ -1099,6 +1099,25 @@ private final class DemoSheetContent: CombinedComponent {
|
||||
)
|
||||
)
|
||||
|
||||
availableItems[.paidMessages] = DemoPagerComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: PremiumDemoScreen.Subject.paidMessages,
|
||||
component: AnyComponent(
|
||||
PageComponent(
|
||||
content: AnyComponent(PhoneDemoComponent(
|
||||
context: component.context,
|
||||
position: .top,
|
||||
videoFile: configuration.videos["paid_messages"],
|
||||
decoration: .badgeStars
|
||||
)),
|
||||
title: strings.Premium_PaidMessages,
|
||||
text: strings.Premium_PaidMessagesInfo,
|
||||
textColor: textColor
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
let index: Int = 0
|
||||
var items: [DemoPagerComponent.Item] = []
|
||||
if let item = availableItems.first(where: { $0.value.content.id == component.subject as AnyHashable }) {
|
||||
@ -1195,6 +1214,8 @@ private final class DemoSheetContent: CombinedComponent {
|
||||
text = strings.Premium_FolderTagsStandaloneInfo
|
||||
case .messageEffects:
|
||||
text = strings.Premium_MessageEffectsInfo
|
||||
case .paidMessages:
|
||||
text = strings.Premium_PaidMessagesInfo
|
||||
default:
|
||||
text = ""
|
||||
}
|
||||
@ -1279,6 +1300,9 @@ private final class DemoSheetContent: CombinedComponent {
|
||||
case .emojiStatus:
|
||||
buttonText = strings.Premium_EmojiStatus_Proceed
|
||||
buttonAnimationName = "premium_unlock"
|
||||
case .paidMessages:
|
||||
buttonText = strings.Premium_PaidMessages_Proceed
|
||||
buttonAnimationName = "premium_unlock"
|
||||
default:
|
||||
buttonText = strings.Common_OK
|
||||
}
|
||||
@ -1468,6 +1492,7 @@ public class PremiumDemoScreen: ViewControllerComponentContainer {
|
||||
case business
|
||||
case folderTags
|
||||
case messageEffects
|
||||
case paidMessages
|
||||
|
||||
case businessLocation
|
||||
case businessHours
|
||||
@ -1526,6 +1551,8 @@ public class PremiumDemoScreen: ViewControllerComponentContainer {
|
||||
return .folderTags
|
||||
case .messageEffects:
|
||||
return .messageEffects
|
||||
case .paidMessages:
|
||||
return .paidMessages
|
||||
case .businessLocation:
|
||||
return .businessLocation
|
||||
case .businessHours:
|
||||
|
@ -433,6 +433,7 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
|
||||
UIColor(rgb: 0xdb374b),
|
||||
UIColor(rgb: 0xcb3e6d),
|
||||
UIColor(rgb: 0xbc4395),
|
||||
UIColor(rgb: 0xbc4395),
|
||||
UIColor(rgb: 0xab4ac4),
|
||||
UIColor(rgb: 0xab4ac4),
|
||||
UIColor(rgb: 0xa34cd7),
|
||||
@ -538,6 +539,8 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
|
||||
demoSubject = .messagePrivacy
|
||||
case .messageEffects:
|
||||
demoSubject = .messageEffects
|
||||
case .paidMessages:
|
||||
demoSubject = .paidMessages
|
||||
case .business:
|
||||
demoSubject = .business
|
||||
default:
|
||||
|
@ -302,6 +302,12 @@ public enum PremiumSource: Equatable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .paidMessages:
|
||||
if case .messageEffects = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,6 +355,7 @@ public enum PremiumSource: Equatable {
|
||||
case messageTags
|
||||
case folderTags
|
||||
case messageEffects
|
||||
case paidMessages
|
||||
|
||||
var identifier: String? {
|
||||
switch self {
|
||||
@ -442,6 +449,8 @@ public enum PremiumSource: Equatable {
|
||||
return "folder_tags"
|
||||
case .messageEffects:
|
||||
return "effects"
|
||||
case .paidMessages:
|
||||
return "paid_messages"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -470,6 +479,7 @@ public enum PremiumPerk: CaseIterable {
|
||||
case business
|
||||
case folderTags
|
||||
case messageEffects
|
||||
case paidMessages
|
||||
|
||||
case businessLocation
|
||||
case businessHours
|
||||
@ -504,7 +514,8 @@ public enum PremiumPerk: CaseIterable {
|
||||
.messagePrivacy,
|
||||
.folderTags,
|
||||
.business,
|
||||
.messageEffects
|
||||
.messageEffects,
|
||||
.paidMessages
|
||||
]
|
||||
}
|
||||
|
||||
@ -578,6 +589,8 @@ public enum PremiumPerk: CaseIterable {
|
||||
return "folder_tags"
|
||||
case .messageEffects:
|
||||
return "effects"
|
||||
case .paidMessages:
|
||||
return "paid_messages"
|
||||
case .business:
|
||||
return "business"
|
||||
case .businessLocation:
|
||||
@ -647,6 +660,8 @@ public enum PremiumPerk: CaseIterable {
|
||||
return strings.Premium_Business
|
||||
case .messageEffects:
|
||||
return strings.Premium_MessageEffects
|
||||
case .paidMessages:
|
||||
return strings.Premium_PaidMessages
|
||||
case .businessLocation:
|
||||
return strings.Business_Location
|
||||
case .businessHours:
|
||||
@ -714,6 +729,8 @@ public enum PremiumPerk: CaseIterable {
|
||||
return strings.Premium_BusinessInfo
|
||||
case .messageEffects:
|
||||
return strings.Premium_MessageEffectsInfo
|
||||
case .paidMessages:
|
||||
return strings.Premium_PaidMessagesInfo
|
||||
case .businessLocation:
|
||||
return strings.Business_LocationInfo
|
||||
case .businessHours:
|
||||
@ -781,7 +798,8 @@ public enum PremiumPerk: CaseIterable {
|
||||
return "Premium/Perk/Business"
|
||||
case .messageEffects:
|
||||
return "Premium/Perk/MessageEffects"
|
||||
|
||||
case .paidMessages:
|
||||
return "Premium/Perk/PaidMessages"
|
||||
case .businessLocation:
|
||||
return "Premium/BusinessPerk/Location"
|
||||
case .businessHours:
|
||||
@ -819,6 +837,7 @@ struct PremiumIntroConfiguration {
|
||||
.colors,
|
||||
.wallpapers,
|
||||
.profileBadge,
|
||||
.paidMessages,
|
||||
.messagePrivacy,
|
||||
.advancedChatManagement,
|
||||
.noAds,
|
||||
@ -867,6 +886,12 @@ struct PremiumIntroConfiguration {
|
||||
perks = PremiumIntroConfiguration.defaultValue.perks
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if !perks.contains(.paidMessages) {
|
||||
perks.append(.paidMessages)
|
||||
}
|
||||
#endif
|
||||
|
||||
var businessPerks: [PremiumPerk] = []
|
||||
if let values = data["business_promo_order"] as? [String] {
|
||||
for value in values {
|
||||
@ -1859,6 +1884,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
UIColor(rgb: 0xdb374b),
|
||||
UIColor(rgb: 0xcb3e6d),
|
||||
UIColor(rgb: 0xbc4395),
|
||||
UIColor(rgb: 0xbc4395),
|
||||
UIColor(rgb: 0xab4ac4),
|
||||
UIColor(rgb: 0xab4ac4),
|
||||
UIColor(rgb: 0xa34cd7),
|
||||
@ -2092,6 +2118,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
demoSubject = .messagePrivacy
|
||||
case .messageEffects:
|
||||
demoSubject = .messageEffects
|
||||
case .paidMessages:
|
||||
demoSubject = .paidMessages
|
||||
case .business:
|
||||
demoSubject = .business
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
|
@ -842,6 +842,24 @@ public class PremiumLimitsListScreen: ViewController {
|
||||
)
|
||||
)
|
||||
)
|
||||
availableItems[.paidMessages] = DemoPagerComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: PremiumDemoScreen.Subject.paidMessages,
|
||||
component: AnyComponent(
|
||||
PageComponent(
|
||||
content: AnyComponent(PhoneDemoComponent(
|
||||
context: context,
|
||||
position: .top,
|
||||
videoFile: videos["paid_messages"],
|
||||
decoration: .badgeStars
|
||||
)),
|
||||
title: strings.Premium_PaidMessages,
|
||||
text: strings.Premium_PaidMessagesInfo,
|
||||
textColor: textColor
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
availableItems[.business] = DemoPagerComponent.Item(
|
||||
AnyComponentWithIdentity(
|
||||
id: PremiumDemoScreen.Subject.business,
|
||||
|
@ -19,19 +19,22 @@ private final class IncomingMessagePrivacyScreenArguments {
|
||||
let disabledValuePressed: () -> Void
|
||||
let infoLinkAction: () -> Void
|
||||
let openExceptions: () -> Void
|
||||
let openPremiumInfo: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
updateValue: @escaping (GlobalPrivacySettings.NonContactChatsPrivacy) -> Void,
|
||||
disabledValuePressed: @escaping () -> Void,
|
||||
infoLinkAction: @escaping () -> Void,
|
||||
openExceptions: @escaping () -> Void
|
||||
openExceptions: @escaping () -> Void,
|
||||
openPremiumInfo: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.updateValue = updateValue
|
||||
self.disabledValuePressed = disabledValuePressed
|
||||
self.infoLinkAction = infoLinkAction
|
||||
self.openExceptions = openExceptions
|
||||
self.openPremiumInfo = openPremiumInfo
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +52,7 @@ private enum GlobalAutoremoveEntry: ItemListNodeEntry {
|
||||
case optionChargeForMessages(value: GlobalPrivacySettings.NonContactChatsPrivacy, isEnabled: Bool)
|
||||
case footer(value: GlobalPrivacySettings.NonContactChatsPrivacy)
|
||||
case priceHeader
|
||||
case price(value: Int64, price: String)
|
||||
case price(value: Int64, maxValue: Int64, price: String, isEnabled: Bool)
|
||||
case priceInfo(commission: Int32, value: String)
|
||||
case exceptionsHeader
|
||||
case exceptions(count: Int)
|
||||
@ -128,12 +131,8 @@ private enum GlobalAutoremoveEntry: ItemListNodeEntry {
|
||||
if case .paidMessages = value {
|
||||
isChecked = true
|
||||
}
|
||||
return ItemListCheckboxItem(presentationData: presentationData, icon: isEnabled ? nil : generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/Lock"), color: presentationData.theme.list.itemSecondaryTextColor), iconPlacement: .check, title: presentationData.strings.Privacy_Messages_ChargeForMessages, style: .left, checked: isChecked, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||
if isEnabled {
|
||||
arguments.updateValue(.paidMessages(StarsAmount(value: 400, nanos: 0)))
|
||||
} else {
|
||||
arguments.disabledValuePressed()
|
||||
}
|
||||
return ItemListCheckboxItem(presentationData: presentationData, icon: isEnabled || isChecked ? nil : generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/Lock"), color: presentationData.theme.list.itemSecondaryTextColor), iconPlacement: .check, title: presentationData.strings.Privacy_Messages_ChargeForMessages, style: .left, checked: isChecked, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||
arguments.updateValue(.paidMessages(StarsAmount(value: 400, nanos: 0)))
|
||||
})
|
||||
case let .footer(value):
|
||||
let text: String
|
||||
@ -149,9 +148,11 @@ private enum GlobalAutoremoveEntry: ItemListNodeEntry {
|
||||
})
|
||||
case .priceHeader:
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: presentationData.strings.Privacy_Messages_MessagePrice, sectionId: self.section)
|
||||
case let .price(value, price):
|
||||
return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, minValue: 1, maxValue: 10000, value: value, price: price, sectionId: self.section, updated: { value in
|
||||
case let .price(value, maxValue, price, isEnabled):
|
||||
return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, isEnabled: isEnabled, minValue: 1, maxValue: maxValue, value: value, price: price, sectionId: self.section, updated: { value in
|
||||
arguments.updateValue(.paidMessages(StarsAmount(value: value, nanos: 0)))
|
||||
}, openPremiumInfo: {
|
||||
arguments.openPremiumInfo()
|
||||
})
|
||||
case let .priceInfo(commission, value):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .markdown(presentationData.strings.Privacy_Messages_MessagePriceInfo("\(commission)", value).string), sectionId: self.section)
|
||||
@ -178,7 +179,9 @@ private func incomingMessagePrivacyScreenEntries(presentationData: PresentationD
|
||||
entries.append(.header)
|
||||
entries.append(.optionEverybody(value: state.updatedValue))
|
||||
entries.append(.optionPremium(value: state.updatedValue, isEnabled: enableSetting))
|
||||
entries.append(.optionChargeForMessages(value: state.updatedValue, isEnabled: isPremium))
|
||||
if configuration.paidMessagesAvailable {
|
||||
entries.append(.optionChargeForMessages(value: state.updatedValue, isEnabled: isPremium))
|
||||
}
|
||||
|
||||
if case let .paidMessages(amount) = state.updatedValue {
|
||||
entries.append(.footer(value: state.updatedValue))
|
||||
@ -188,11 +191,14 @@ private func incomingMessagePrivacyScreenEntries(presentationData: PresentationD
|
||||
|
||||
let price = "≈\(formatTonUsdValue(amount.value, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
|
||||
entries.append(.price(value: amount.value, price: price))
|
||||
entries.append(.price(value: amount.value, maxValue: configuration.paidMessageMaxAmount, price: price, isEnabled: isPremium))
|
||||
entries.append(.priceInfo(commission: configuration.paidMessageCommissionPermille / 10, value: price))
|
||||
entries.append(.exceptionsHeader)
|
||||
entries.append(.exceptions(count: state.disableFor.count))
|
||||
entries.append(.exceptionsInfo)
|
||||
|
||||
if isPremium {
|
||||
entries.append(.exceptionsHeader)
|
||||
entries.append(.exceptions(count: state.disableFor.count))
|
||||
entries.append(.exceptionsInfo)
|
||||
}
|
||||
} else {
|
||||
entries.append(.footer(value: state.updatedValue))
|
||||
entries.append(.info)
|
||||
@ -347,6 +353,17 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP
|
||||
})
|
||||
pushControllerImpl?(controller)
|
||||
}
|
||||
},
|
||||
openPremiumInfo: {
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .paidMessages, forceDark: false, action: {
|
||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .paidMessages, forceDark: false, dismissed: nil)
|
||||
replaceImpl?(controller)
|
||||
}, dismissed: nil)
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
pushControllerImpl?(controller)
|
||||
}
|
||||
)
|
||||
|
||||
@ -414,7 +431,12 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP
|
||||
controller?.push(c)
|
||||
}
|
||||
controller.attemptNavigation = { _ in
|
||||
update(stateValue.with({ $0 }).updatedValue)
|
||||
let updatedValue = stateValue.with({ $0 }).updatedValue
|
||||
if !context.isPremium, case .paidMessages = updatedValue {
|
||||
|
||||
} else {
|
||||
update(updatedValue)
|
||||
}
|
||||
return true
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
|
@ -19,6 +19,10 @@ swift_library(
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/ItemListUI",
|
||||
"//submodules/LegacyComponents",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -8,6 +8,10 @@ import TelegramPresentationData
|
||||
import LegacyComponents
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import ComponentFlow
|
||||
import ButtonComponent
|
||||
import BundleIconComponent
|
||||
import MultilineTextComponent
|
||||
|
||||
private let textFont = Font.with(size: 17.0, traits: .monospacedNumbers)
|
||||
private let smallTextFont = Font.with(size: 13.0, traits: .monospacedNumbers)
|
||||
@ -15,22 +19,26 @@ private let smallTextFont = Font.with(size: 13.0, traits: .monospacedNumbers)
|
||||
public final class MessagePriceItem: ListViewItem, ItemListItem {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let isEnabled: Bool
|
||||
let minValue: Int64
|
||||
let maxValue: Int64
|
||||
let value: Int64
|
||||
let price: String
|
||||
public let sectionId: ItemListSectionId
|
||||
let updated: (Int64) -> Void
|
||||
let openPremiumInfo: (() -> Void)?
|
||||
|
||||
public init(theme: PresentationTheme, strings: PresentationStrings, minValue: Int64, maxValue: Int64, value: Int64, price: String, sectionId: ItemListSectionId, updated: @escaping (Int64) -> Void) {
|
||||
public init(theme: PresentationTheme, strings: PresentationStrings, isEnabled: Bool, minValue: Int64, maxValue: Int64, value: Int64, price: String, sectionId: ItemListSectionId, updated: @escaping (Int64) -> Void, openPremiumInfo: (() -> Void)? = nil) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.isEnabled = isEnabled
|
||||
self.minValue = minValue
|
||||
self.maxValue = maxValue
|
||||
self.value = value
|
||||
self.price = price
|
||||
self.sectionId = sectionId
|
||||
self.updated = updated
|
||||
self.openPremiumInfo = openPremiumInfo
|
||||
}
|
||||
|
||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
@ -155,6 +163,9 @@ private class MessagePriceItemNode: ListViewItemNode {
|
||||
private let rightTextNode: ImmediateTextNode
|
||||
private let centerLeftTextNode: ImmediateTextNode
|
||||
private let centerRightTextNode: ImmediateTextNode
|
||||
private let lockIconNode: ASImageNode
|
||||
|
||||
private let button: ComponentView<Empty>
|
||||
|
||||
private var amount: Amount = Amount(realValue: 1, maxRealValue: 1000, maxSliderValue: 1000, isLogarithmic: true)
|
||||
|
||||
@ -178,12 +189,18 @@ private class MessagePriceItemNode: ListViewItemNode {
|
||||
self.centerLeftTextNode = ImmediateTextNode()
|
||||
self.centerRightTextNode = ImmediateTextNode()
|
||||
|
||||
self.lockIconNode = ASImageNode()
|
||||
self.lockIconNode.displaysAsynchronously = false
|
||||
|
||||
self.button = ComponentView<Empty>()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.addSubnode(self.leftTextNode)
|
||||
self.addSubnode(self.rightTextNode)
|
||||
self.addSubnode(self.centerLeftTextNode)
|
||||
self.addSubnode(self.centerRightTextNode)
|
||||
self.addSubnode(self.lockIconNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@ -224,11 +241,15 @@ private class MessagePriceItemNode: ListViewItemNode {
|
||||
themeUpdated = true
|
||||
}
|
||||
|
||||
let contentSize: CGSize
|
||||
var contentSize: CGSize
|
||||
let insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
contentSize = CGSize(width: params.width, height: 88.0)
|
||||
if !item.isEnabled {
|
||||
contentSize.height = 166.0
|
||||
}
|
||||
|
||||
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
@ -290,8 +311,7 @@ private class MessagePriceItemNode: ListViewItemNode {
|
||||
strongSelf.leftTextNode.attributedText = NSAttributedString(string: "\(item.minValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor)
|
||||
strongSelf.rightTextNode.attributedText = NSAttributedString(string: "\(item.maxValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor)
|
||||
|
||||
//TODO:localize
|
||||
let centralLeftText = "\(item.value) Stars"
|
||||
let centralLeftText = item.strings.Privacy_Messages_Stars(Int32(item.value))
|
||||
strongSelf.centerLeftTextNode.attributedText = NSAttributedString(string: centralLeftText, font: textFont, textColor: item.theme.list.itemPrimaryTextColor)
|
||||
strongSelf.centerRightTextNode.attributedText = NSAttributedString(string: item.price, font: smallTextFont, textColor: item.theme.list.itemSecondaryTextColor)
|
||||
|
||||
@ -323,6 +343,66 @@ private class MessagePriceItemNode: ListViewItemNode {
|
||||
|
||||
sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + 18.0, y: 36.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 18.0 * 2.0, height: 44.0))
|
||||
}
|
||||
|
||||
strongSelf.lockIconNode.isHidden = item.isEnabled
|
||||
if !item.isEnabled {
|
||||
if themeUpdated {
|
||||
strongSelf.lockIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/SmallLock"), color: item.theme.list.itemSecondaryTextColor.withMultipliedAlpha(0.5))
|
||||
}
|
||||
if let image = strongSelf.lockIconNode.image {
|
||||
strongSelf.lockIconNode.frame = CGRect(origin: CGPoint(x: centerLeftFrame.minX - image.size.width - 1.0, y: 12.0 + UIScreenPixel), size: image.size)
|
||||
}
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let buttonSize = CGSize(width: params.width - params.leftInset - params.rightInset - sideInset * 2.0, height: 50.0)
|
||||
let _ = strongSelf.button.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: item.theme.list.itemCheckColors.fillColor,
|
||||
foreground: item.theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: item.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("unlock"),
|
||||
component: AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable("icon"),
|
||||
component: AnyComponent(BundleIconComponent(name: "Chat/Stickers/Lock", tintColor: item.theme.list.itemCheckColors.foregroundColor))
|
||||
),
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable("label"),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: item.strings.Privacy_Messages_Unlock, font: Font.semibold(17.0), textColor: item.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
)
|
||||
], spacing: 3.0)
|
||||
)
|
||||
),
|
||||
isEnabled: true,
|
||||
tintWhenDisabled: false,
|
||||
allowActionWhenDisabled: false,
|
||||
displaysProgress: false,
|
||||
action: { [weak self] in
|
||||
guard let self, let item = self.item else {
|
||||
return
|
||||
}
|
||||
item.openPremiumInfo?()
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: buttonSize
|
||||
)
|
||||
if let buttonView = strongSelf.button.view {
|
||||
if buttonView.superview == nil {
|
||||
strongSelf.view.addSubview(buttonView)
|
||||
}
|
||||
buttonView.frame = CGRect(origin: CGPoint(x: params.leftInset + sideInset, y: contentSize.height - buttonSize.height - sideInset), size: buttonSize)
|
||||
}
|
||||
} else if let buttonView = strongSelf.button.view, buttonView.superview != nil {
|
||||
buttonView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/PaidMessages.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Perk/PaidMessages.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "paidmessages.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Premium/Perk/PaidMessages.imageset/paidmessages.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Perk/PaidMessages.imageset/paidmessages.pdf
vendored
Normal file
Binary file not shown.
@ -2486,6 +2486,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
mappedSource = .messageEffects
|
||||
case .animatedEmoji:
|
||||
mappedSource = .animatedEmoji
|
||||
case .paidMessages:
|
||||
mappedSource = .paidMessages
|
||||
}
|
||||
let controller = PremiumIntroScreen(context: context, source: mappedSource, modal: modal, forceDark: forceDark)
|
||||
controller.wasDismissed = dismissed
|
||||
@ -2542,6 +2544,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
mappedSubject = .folderTags
|
||||
case .messageEffects:
|
||||
mappedSubject = .messageEffects
|
||||
case .paidMessages:
|
||||
mappedSubject = .paidMessages
|
||||
case .business:
|
||||
mappedSubject = .business
|
||||
buttonText = presentationData.strings.Chat_EmptyStateIntroFooterPremiumActionButton
|
||||
|
Loading…
x
Reference in New Issue
Block a user