diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 64b5d4beaa..127d6d2eda 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -12230,10 +12230,22 @@ Sorry for the inconvenience."; "Stars.Intro.Title" = "Telegram Stars"; "Stars.Intro.Description" = "Buy Stars to unlock content and services in miniapps on Telegram."; "Stars.Intro.Balance" = "Balance"; +"Stars.Intro.YourBalance" = "your balance"; +"Stars.Intro.Buy" = "Buy More Stars"; "Stars.Intro.AllTransactions" = "All Transactions"; "Stars.Intro.Incoming" = "Incoming"; "Stars.Intro.Outgoing" = "Outgoing"; +"Stars.Intro.Transaction.AppleTopUp.Title" = "Stars Top-Up"; +"Stars.Intro.Transaction.AppleTopUp.Subtitle" = "via App Store"; +"Stars.Intro.Transaction.GoogleTopUp.Title" = "Stars Top-Up"; +"Stars.Intro.Transaction.GoogleTopUp.Subtitle" = "via Play Market"; +"Stars.Intro.Transaction.PremiumBotTopUp.Title" = "Stars Top-Up"; +"Stars.Intro.Transaction.PremiumBotTopUp.Subtitle" = "via Premium Bot"; +"Stars.Intro.Transaction.FragmentTopUp.Title" = "Stars Top-Up"; +"Stars.Intro.Transaction.FragmentTopUp.Subtitle" = "via Fragment"; +"Stars.Intro.Transaction.Unsupported.Title" = "Unsupported"; + "Stars.Purchase.GetStars" = "Get Stars"; "Stars.Purchase.GetStarsInfo" = "Choose how many Stars you would like to buy."; @@ -12249,6 +12261,7 @@ Sorry for the inconvenience."; "Stars.Purchase.Info" = "By proceeding and purchasing Stars, you agree with [Terms and Conditions]()."; "Stars.Purchase.Terms_URL" = "https://telegram.org/tos"; +"Stars.Transaction.Via" = "Via"; "Stars.Transaction.To" = "To"; "Stars.Transaction.Id" = "Transaction ID"; "Stars.Transaction.Date" = "Date"; @@ -12256,6 +12269,16 @@ Sorry for the inconvenience."; "Stars.Transaction.Terms_URL" = "https://telegram.org/tos"; "Stars.Transaction.CopiedId" = "Transaction ID copied to clipboard."; +"Stars.Transaction.AppleTopUp.Title" = "Stars Top-Up"; +"Stars.Transaction.AppleTopUp.Subtitle" = "App Store"; +"Stars.Transaction.GoogleTopUp.Title" = "Stars Top-Up"; +"Stars.Transaction.GoogleTopUp.Subtitle" = "Play Market"; +"Stars.Transaction.PremiumBotTopUp.Title" = "Stars Top-Up"; +"Stars.Transaction.PremiumBotTopUp.Subtitle" = "Premium Bot"; +"Stars.Transaction.FragmentTopUp.Title" = "Stars Top-Up"; +"Stars.Transaction.FragmentTopUp.Subtitle" = "Fragment"; +"Stars.Transaction.Unsupported.Title" = "Unsupported"; + "Stars.Transfer.Title" = "Confirm Your Purchase"; "Stars.Transfer.Info" = "Do you want to buy **%1$@** in **%2$@** for **%3$@**?"; "Stars.Transfer.Info.Stars_1" = "%@ Star"; diff --git a/submodules/ItemListAvatarAndNameInfoItem/BUILD b/submodules/ItemListAvatarAndNameInfoItem/BUILD index e24785c855..dbf1c6e68d 100644 --- a/submodules/ItemListAvatarAndNameInfoItem/BUILD +++ b/submodules/ItemListAvatarAndNameInfoItem/BUILD @@ -13,6 +13,7 @@ swift_library( "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", + "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/PeerPresenceStatusManager:PeerPresenceStatusManager", diff --git a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift index efc5bc8c84..7c467645b2 100644 --- a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift +++ b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift @@ -2,6 +2,7 @@ import Foundation import UIKit import Display import AsyncDisplayKit +import Postbox import TelegramCore import SwiftSignalKit import TelegramPresentationData @@ -60,7 +61,7 @@ public enum ItemListAvatarAndNameInfoItemName: Equatable { } } - public func composedDisplayTitle(context: AccountContext, strings: PresentationStrings) -> String { + public func composedDisplayTitle(context: AccountContext?, strings: PresentationStrings) -> String { switch self { case let .personName(firstName, lastName, phone): if !firstName.isEmpty { @@ -72,7 +73,11 @@ public enum ItemListAvatarAndNameInfoItemName: Equatable { } else if !lastName.isEmpty { return lastName } else if !phone.isEmpty { - return formatPhoneNumber(context: context, number: "+\(phone)") + if let context { + return formatPhoneNumber(context: context, number: "+\(phone)") + } else { + return "+\(phone)" + } } else { return strings.User_DeletedAccount } @@ -127,7 +132,18 @@ public enum ItemListAvatarAndNameInfoItemMode { } public class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem { - let accountContext: AccountContext + public enum ItemContext { + case accountContext(AccountContext) + case other(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network) + + var accountContext: AccountContext? { + if case let .accountContext(accountContext) = self { + return accountContext + } + return nil + } + } + let itemContext: ItemContext let presentationData: ItemListPresentationData let dateTimeFormat: PresentationDateTimeFormat let mode: ItemListAvatarAndNameInfoItemMode @@ -150,8 +166,8 @@ public class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem { public let selectable: Bool - public init(accountContext: AccountContext, presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, mode: ItemListAvatarAndNameInfoItemMode, peer: EnginePeer?, presence: EnginePeer.Presence?, label: String? = nil, memberCount: Int?, state: ItemListAvatarAndNameInfoItemState, sectionId: ItemListSectionId, style: ItemListAvatarAndNameInfoItemStyle, editingNameUpdated: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, editingNameCompleted: @escaping () -> Void = {}, avatarTapped: @escaping () -> Void, context: ItemListAvatarAndNameInfoItemContext? = nil, updatingImage: ItemListAvatarAndNameInfoItemUpdatingAvatar? = nil, call: (() -> Void)? = nil, action: (() -> Void)? = nil, longTapAction: (() -> Void)? = nil, tag: ItemListItemTag? = nil) { - self.accountContext = accountContext + public init(itemContext: ItemContext, presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, mode: ItemListAvatarAndNameInfoItemMode, peer: EnginePeer?, presence: EnginePeer.Presence?, label: String? = nil, memberCount: Int?, state: ItemListAvatarAndNameInfoItemState, sectionId: ItemListSectionId, style: ItemListAvatarAndNameInfoItemStyle, editingNameUpdated: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, editingNameCompleted: @escaping () -> Void = {}, avatarTapped: @escaping () -> Void, context: ItemListAvatarAndNameInfoItemContext? = nil, updatingImage: ItemListAvatarAndNameInfoItemUpdatingAvatar? = nil, call: (() -> Void)? = nil, action: (() -> Void)? = nil, longTapAction: (() -> Void)? = nil, tag: ItemListItemTag? = nil) { + self.itemContext = itemContext self.presentationData = presentationData self.dateTimeFormat = dateTimeFormat self.mode = mode @@ -358,7 +374,12 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo updatedTheme = item.presentationData.theme } - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.accountContext.currentAppConfiguration.with { $0 }) + let premiumConfiguration: PremiumConfiguration + if case let .accountContext(context) = item.itemContext { + premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + } else { + premiumConfiguration = .defaultValue + } var credibilityIconImage: UIImage? var credibilityIconOffset: CGFloat = 4.0 @@ -395,7 +416,7 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo nameMaximumNumberOfLines = 2 } - let (nameNodeLayout, nameNodeApply) = layoutNameNode(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayTitle.composedDisplayTitle(context: item.accountContext, strings: item.presentationData.strings), font: nameFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: nameMaximumNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: baseWidth - 20 - 94.0 - (item.call != nil ? 36.0 : 0.0) - additionalTitleInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (nameNodeLayout, nameNodeApply) = layoutNameNode(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayTitle.composedDisplayTitle(context: item.itemContext.accountContext, strings: item.presentationData.strings), font: nameFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: nameMaximumNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: baseWidth - 20 - 94.0 - (item.call != nil ? 36.0 : 0.0) - additionalTitleInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) var statusText: String = "" let statusColor: UIColor @@ -404,7 +425,11 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo switch item.mode { case .settings: if let phone = peer.phone, !phone.isEmpty { - statusText += formatPhoneNumber(context: item.accountContext, number: phone) + if let accountContext = item.itemContext.accountContext { + statusText += formatPhoneNumber(context: accountContext, number: phone) + } else { + statusText += formatPhoneNumber(phone) + } } if let username = peer.addressName, !username.isEmpty { if !statusText.isEmpty { @@ -669,7 +694,13 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo overrideImage = .deletedIcon } - strongSelf.avatarNode.setPeer(context: item.accountContext, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: ignoreEmpty ? nil : item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) + switch item.itemContext { + case let .accountContext(context): + strongSelf.avatarNode.setPeer(context: context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: ignoreEmpty ? nil : item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) + case let .other(accountPeerId, postbox, network): + strongSelf.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: postbox, network: network, contentSettings: .default, theme: item.presentationData.theme, peer: peer, authorOfMessage: nil, overrideImage: overrideImage, emptyColor: ignoreEmpty ? nil : item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) + } + } let avatarFrame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: floor((layout.contentSize.height - 66.0) / 2.0)), size: CGSize(width: 66.0, height: 66.0)) diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift index b45a71658b..87ba5d676e 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift @@ -346,7 +346,7 @@ private enum ChannelAdminEntry: ItemListNodeEntry { let arguments = arguments as! ChannelAdminControllerArguments switch self { case let .info(_, _, dateTimeFormat, peer, presence): - return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in + return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in }, avatarTapped: { }) case let .rankTitle(_, text, count, limit): diff --git a/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift b/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift index 2e0ecd6a3a..f7b2f1b24a 100644 --- a/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift @@ -215,7 +215,7 @@ private enum ChannelBannedMemberEntry: ItemListNodeEntry { let arguments = arguments as! ChannelBannedMemberControllerArguments switch self { case let .info(_, _, dateTimeFormat, peer, presence): - return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in + return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in }, avatarTapped: { }) case let .rightsHeader(_, text): diff --git a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift index 9eeebabcdf..be85ed72cc 100644 --- a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift +++ b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift @@ -35,7 +35,7 @@ private enum DeviceContactInfoAction { } private final class DeviceContactInfoControllerArguments { - let accountContext: AccountContext? + let context: ShareControllerAccountContext let isPlain: Bool let updateEditingName: (ItemListAvatarAndNameInfoItemName) -> Void let updatePhone: (Int64, String) -> Void @@ -52,8 +52,8 @@ private final class DeviceContactInfoControllerArguments { let updateShareViaException: (Bool) -> Void let openAvatar: (EnginePeer) -> Void - init(accountContext: AccountContext?, isPlain: Bool, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, updatePhone: @escaping (Int64, String) -> Void, updatePhoneLabel: @escaping (Int64, String) -> Void, deletePhone: @escaping (Int64) -> Void, setPhoneIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, addPhoneNumber: @escaping () -> Void, performAction: @escaping (DeviceContactInfoAction) -> Void, toggleSelection: @escaping (DeviceContactInfoDataId) -> Void, callPhone: @escaping (String) -> Void, openUrl: @escaping (String) -> Void, openAddress: @escaping (DeviceContactAddressData) -> Void, displayCopyContextMenu: @escaping (DeviceContactInfoEntryTag, String) -> Void, updateShareViaException: @escaping (Bool) -> Void, openAvatar: @escaping (EnginePeer) -> Void) { - self.accountContext = accountContext + init(context: ShareControllerAccountContext, isPlain: Bool, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, updatePhone: @escaping (Int64, String) -> Void, updatePhoneLabel: @escaping (Int64, String) -> Void, deletePhone: @escaping (Int64) -> Void, setPhoneIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, addPhoneNumber: @escaping () -> Void, performAction: @escaping (DeviceContactInfoAction) -> Void, toggleSelection: @escaping (DeviceContactInfoDataId) -> Void, callPhone: @escaping (String) -> Void, openUrl: @escaping (String) -> Void, openAddress: @escaping (DeviceContactAddressData) -> Void, displayCopyContextMenu: @escaping (DeviceContactInfoEntryTag, String) -> Void, updateShareViaException: @escaping (Bool) -> Void, openAvatar: @escaping (EnginePeer) -> Void) { + self.context = context self.isPlain = isPlain self.updateEditingName = updateEditingName self.updatePhone = updatePhone @@ -406,10 +406,13 @@ private enum DeviceContactInfoEntry: ItemListNodeEntry { let arguments = arguments as! DeviceContactInfoControllerArguments switch self { case let .info(_, _, _, dateTimeFormat, peer, state, jobSummary, _, hiddenAvatar): - guard let accountContext = arguments.accountContext else { - fatalError() + let itemContext: ItemListAvatarAndNameInfoItem.ItemContext + if let context = arguments.context as? ShareControllerAppAccountContext { + itemContext = .accountContext(context.context) + } else { + itemContext = .other(accountPeerId: arguments.context.accountPeerId, postbox: arguments.context.stateManager.postbox, network: arguments.context.stateManager.network) } - return ItemListAvatarAndNameInfoItem(accountContext: accountContext, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .contact, peer: peer, presence: nil, label: jobSummary, memberCount: nil, state: state, sectionId: self.section, style: arguments.isPlain ? .plain : .blocks(withTopInset: false, withExtendedBottomInset: true), editingNameUpdated: { editingName in + return ItemListAvatarAndNameInfoItem(itemContext: itemContext, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .contact, peer: peer, presence: nil, label: jobSummary, memberCount: nil, state: state, sectionId: self.section, style: arguments.isPlain ? .plain : .blocks(withTopInset: false, withExtendedBottomInset: true), editingNameUpdated: { editingName in arguments.updateEditingName(editingName) }, avatarTapped: { if peer.smallProfileImage != nil { @@ -945,13 +948,7 @@ public func deviceContactInfoController(context: ShareControllerAccountContext, shareViaException = shareViaExceptionValue } - let accountContext: AccountContext? - if let context = context as? ShareControllerAppAccountContext { - accountContext = context.context - } else { - accountContext = nil - } - let arguments = DeviceContactInfoControllerArguments(accountContext: accountContext, isPlain: !isShare, updateEditingName: { editingName in + let arguments = DeviceContactInfoControllerArguments(context: context, isPlain: !isShare, updateEditingName: { editingName in updateState { state in var state = state if let _ = state.editingState { diff --git a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift index e5f16c1201..177571d2b1 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift @@ -35,8 +35,9 @@ private final class DataAndStorageControllerArguments { let openBrowserSelection: () -> Void let openIntents: () -> Void let toggleEnableSensitiveContent: (Bool) -> Void + let presentSensitiveContentConfirmation: () -> Void - init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, togglePauseMusicOnRecording: @escaping (Bool) -> Void, toggleRaiseToListen: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) { + init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, togglePauseMusicOnRecording: @escaping (Bool) -> Void, toggleRaiseToListen: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void, presentSensitiveContentConfirmation: @escaping () -> Void) { self.openStorageUsage = openStorageUsage self.openNetworkUsage = openNetworkUsage self.openProxy = openProxy @@ -51,6 +52,7 @@ private final class DataAndStorageControllerArguments { self.openBrowserSelection = openBrowserSelection self.openIntents = openIntents self.toggleEnableSensitiveContent = toggleEnableSensitiveContent + self.presentSensitiveContentConfirmation = presentSensitiveContentConfirmation } } @@ -110,6 +112,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry { case connectionHeader(PresentationTheme, String) case connectionProxy(PresentationTheme, String, String) case enableSensitiveContent(String, Bool) + case enableSensitiveContentInfo(String) var section: ItemListSectionId { switch self { @@ -127,7 +130,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry { return DataAndStorageSection.other.rawValue case .connectionHeader, .connectionProxy: return DataAndStorageSection.connection.rawValue - case .enableSensitiveContent: + case .enableSensitiveContent, .enableSensitiveContentInfo: return DataAndStorageSection.enableSensitiveContent.rawValue } } @@ -174,12 +177,14 @@ private enum DataAndStorageEntry: ItemListNodeEntry { return 34 case .raiseToListenInfo: return 35 - case .connectionHeader: - return 36 - case .connectionProxy: - return 37 case .enableSensitiveContent: + return 36 + case .enableSensitiveContentInfo: + return 37 + case .connectionHeader: return 38 + case .connectionProxy: + return 39 } } @@ -323,6 +328,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry { } else { return false } + case let .enableSensitiveContentInfo(text): + if case .enableSensitiveContentInfo(text) = rhs { + return true + } else { + return false + } } } @@ -421,9 +432,15 @@ private enum DataAndStorageEntry: ItemListNodeEntry { arguments.openProxy() }) case let .enableSensitiveContent(text, value): - return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in - arguments.toggleEnableSensitiveContent(value) + return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enableInteractiveChanges: value, sectionId: self.section, style: .blocks, updated: { updatedValue in + if value { + arguments.toggleEnableSensitiveContent(updatedValue) + } else { + arguments.presentSensitiveContentConfirmation() + } }, tag: nil) + case let .enableSensitiveContentInfo(text): + return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) } } } @@ -627,6 +644,14 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat entries.append(.raiseToListen(presentationData.theme, presentationData.strings.Settings_RaiseToListen, data.mediaInputSettings.enableRaiseToSpeak)) entries.append(.raiseToListenInfo(presentationData.theme, presentationData.strings.Settings_RaiseToListenInfo)) + +#if DEBUG + if let contentSettingsConfiguration = contentSettingsConfiguration { //, contentSettingsConfiguration.canAdjustSensitiveContent { + entries.append(.enableSensitiveContent("Show 18+ Content", contentSettingsConfiguration.sensitiveContentEnabled)) + entries.append(.enableSensitiveContentInfo("Do not hide media that contain content suitable only for adults.")) + } +#endif + let proxyValue: String if let proxySettings = data.proxySettings, let activeServer = proxySettings.activeServer, proxySettings.enabled { switch activeServer.connection { @@ -640,13 +665,7 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat } entries.append(.connectionHeader(presentationData.theme, presentationData.strings.ChatSettings_ConnectionType_Title.uppercased())) entries.append(.connectionProxy(presentationData.theme, presentationData.strings.SocksProxySetup_Title, proxyValue)) - - #if DEBUG - if let contentSettingsConfiguration = contentSettingsConfiguration, contentSettingsConfiguration.canAdjustSensitiveContent { - entries.append(.enableSensitiveContent("Display Sensitive Content", contentSettingsConfiguration.sensitiveContentEnabled)) - } - #endif - + return entries } @@ -779,6 +798,8 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da return DataAndStorageData(automaticMediaDownloadSettings: automaticMediaDownloadSettings, autodownloadSettings: autodownloadSettings, generatedMediaStoreSettings: generatedMediaStoreSettings, mediaInputSettings: mediaInputSettings, voiceCallSettings: voiceCallSettings, proxySettings: proxySettings) }) + var enableSensitiveContentImpl: (() -> Void)? + let arguments = DataAndStorageControllerArguments(openStorageUsage: { pushControllerImpl?(StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in return storageUsageExceptionsScreen(context: context, category: category) @@ -882,8 +903,22 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da } }) updateSensitiveContentDisposable.set(updateRemoteContentSettingsConfiguration(postbox: context.account.postbox, network: context.account.network, sensitiveContentEnabled: value).start()) + }, presentSensitiveContentConfirmation: { + let controller = textAlertController(context: context, title: "18+ Content", text: "Confirm that you are over 18 years old and update your settings to see potentially explicit and sensitive content.", actions: [ + TextAlertAction(type: .genericAction, title: "Cancel", action: { + + }), + TextAlertAction(type: .defaultAction, title: "Confirm", action: { + enableSensitiveContentImpl?() + }) + ]) + presentControllerImpl?(controller, nil) }) + enableSensitiveContentImpl = { + arguments.toggleEnableSensitiveContent(true) + } + let preferencesKey: PostboxViewKey = .preferences(keys: Set([ApplicationSpecificPreferencesKeys.mediaAutoSaveSettings])) let preferences = context.account.postbox.combinedView(keys: [preferencesKey]) |> map { views -> MediaAutoSaveSettings in diff --git a/submodules/SettingsUI/Sources/Data and Storage/SaveIncomingMediaController.swift b/submodules/SettingsUI/Sources/Data and Storage/SaveIncomingMediaController.swift index 9f4dca4e0e..b361db75b4 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/SaveIncomingMediaController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/SaveIncomingMediaController.swift @@ -167,7 +167,7 @@ private enum SaveIncomingMediaEntry: ItemListNodeEntry { switch self { case let .peer(peer, presence): return ItemListAvatarAndNameInfoItem( - accountContext: arguments.context, + itemContext: .accountContext(arguments.context), presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), mode: .generic, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift index 9e87fb6957..dbb04fca3d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift @@ -72,10 +72,10 @@ struct InternalStarsStatus { let nextOffset: String? } -func _internal_requestStarsState(account: Account, peerId: EnginePeer.Id, offset: String?) -> Signal { +func _internal_requestStarsState(account: Account, peerId: EnginePeer.Id, offset: String?) -> Signal { return account.postbox.transaction { transaction -> Peer? in return transaction.getPeer(peerId) - } |> mapToSignal { peer -> Signal in + } |> mapToSignal { peer -> Signal in guard let peer, let inputPeer = apiInputPeer(peer) else { return .never() } @@ -88,15 +88,9 @@ func _internal_requestStarsState(account: Account, peerId: EnginePeer.Id, offset } return signal - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) - } - |> mapToSignal { result -> Signal in - guard let result else { - return .single(nil) - } - return account.postbox.transaction { transaction -> InternalStarsStatus? in + |> retryRequest + |> mapToSignal { result -> Signal in + return account.postbox.transaction { transaction -> InternalStarsStatus in switch result { case let .starsStatus(_, balance, history, nextOffset, chats, users): let peers = AccumulatedPeers(chats: chats, users: users) @@ -168,14 +162,10 @@ private final class StarsContextImpl { self.disposable.set((_internal_requestStarsState(account: self.account, peerId: self.peerId, offset: nil) |> deliverOnMainQueue).start(next: { [weak self] status in if let self { - if let status { - self._state = StarsContext.State(balance: status.balance, transactions: status.transactions, canLoadMore: status.nextOffset != nil, isLoading: false) - self.nextOffset = status.nextOffset - - self.loadMore() - } else { - self._state = nil - } + self._state = StarsContext.State(balance: status.balance, transactions: status.transactions, canLoadMore: status.nextOffset != nil, isLoading: false) + self.nextOffset = status.nextOffset + + self.loadMore() } })) } @@ -202,12 +192,8 @@ private final class StarsContextImpl { self.disposable.set((_internal_requestStarsState(account: self.account, peerId: self.peerId, offset: nextOffset) |> deliverOnMainQueue).start(next: { [weak self] status in if let self { - if let status { - self._state = StarsContext.State(balance: status.balance, transactions: currentState.transactions + status.transactions, canLoadMore: status.nextOffset != nil, isLoading: false) - self.nextOffset = status.nextOffset - } else { - self.nextOffset = nil - } + self._state = StarsContext.State(balance: status.balance, transactions: currentState.transactions + status.transactions, canLoadMore: status.nextOffset != nil, isLoading: false) + self.nextOffset = status.nextOffset } })) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift index 8cba75d6bf..23b0b369d4 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/TelegramEnginePayments.swift @@ -73,17 +73,7 @@ public extension TelegramEngine { public func peerStarsContext(peerId: EnginePeer.Id) -> StarsContext { return StarsContext(account: self.account, peerId: peerId) } - - public func peerStarsState(peerId: EnginePeer.Id) -> Signal { - return _internal_requestStarsState(account: self.account, peerId: peerId, offset: nil) - |> map { state -> StarsContext.State? in - guard let state else { - return nil - } - return StarsContext.State(balance: state.balance, transactions: state.transactions, canLoadMore: false, isLoading: false) - } - } - + public func sendStarsPaymentForm(formId: Int64, source: BotPaymentInvoiceSource) -> Signal { return _internal_sendStarsPaymentForm(account: self.account, formId: formId, source: source) } diff --git a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift index c4c232a975..6613b4752b 100644 --- a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift +++ b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift @@ -847,8 +847,8 @@ private let starImage: UIImage? = { generateImage(CGSize(width: 32.0, height: 32.0), contextGenerator: { size, context in context.clear(CGRect(origin: .zero, size: size)) - if let image = generateTintedImage(image: UIImage(bundleImageName: "Premium/Stars/Star"), color: .white), let cgImage = image.cgImage { - context.draw(cgImage, in: CGRect(origin: .zero, size: size).insetBy(dx: 2.0, dy: 2.0), byTiling: false) + if let image = generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: .white), let cgImage = image.cgImage { + context.draw(cgImage, in: CGRect(origin: .zero, size: size).insetBy(dx: 4.0, dy: 4.0), byTiling: false) } })?.withRenderingMode(.alwaysTemplate) }() diff --git a/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift index d963c523a2..8616872705 100644 --- a/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift +++ b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift @@ -317,7 +317,7 @@ public class ShareRootControllerImpl { presentationDataPromise.set(.single(presentationData)) var immediatePeerId: PeerId? - if #available(iOS 13.2, *), let sendMessageIntent = self.getExtensionContext()?.intent as? INSendMessageIntent { + if !"".isEmpty, #available(iOS 13.2, *), let sendMessageIntent = self.getExtensionContext()?.intent as? INSendMessageIntent { if let contact = sendMessageIntent.recipients?.first, let handle = contact.customIdentifier, handle.hasPrefix("tg") { let string = handle.suffix(from: handle.index(handle.startIndex, offsetBy: 2)) if let peerId = Int64(string) { diff --git a/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift b/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift index e4bfca1905..5eb583a75b 100644 --- a/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift @@ -1059,13 +1059,13 @@ func generateStarsIcon(count: Int) -> UIImage { var originX = floorToScreenPixels((size.width - totalWidth) / 2.0) - let mainImage = UIImage(bundleImageName: "Premium/Stars/Star") + let mainImage = UIImage(bundleImageName: "Premium/Stars/StarLarge") if let cgImage = mainImage?.cgImage, let partCGImage = partImage.cgImage { context.draw(cgImage, in: CGRect(origin: CGPoint(x: originX, y: 0.0), size: imageSize), byTiling: false) - originX += spacing + originX += spacing + UIScreenPixel for _ in 0 ..< count - 1 { - context.draw(partCGImage, in: CGRect(origin: CGPoint(x: originX, y: UIScreenPixel), size: imageSize), byTiling: false) + context.draw(partCGImage, in: CGRect(origin: CGPoint(x: originX, y: -UIScreenPixel), size: imageSize).insetBy(dx: -1.0 + UIScreenPixel, dy: -1.0 + UIScreenPixel), byTiling: false) originX += spacing } } diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsBalanceComponent.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsBalanceComponent.swift index e1371b6504..a2a67317f9 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsBalanceComponent.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsBalanceComponent.swift @@ -52,7 +52,7 @@ final class StarsBalanceComponent: Component { override init(frame: CGRect) { super.init(frame: frame) - self.icon.image = UIImage(bundleImageName: "Premium/Stars/StarLarge") + self.icon.image = UIImage(bundleImageName: "Premium/Stars/BalanceStar") self.addSubview(self.icon) } @@ -107,7 +107,7 @@ final class StarsBalanceComponent: Component { transition: .immediate, component: AnyComponent( MultilineTextComponent( - text: .plain(NSAttributedString(string: "your balance", font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)), + text: .plain(NSAttributedString(string: component.strings.Stars_Intro_YourBalance, font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)), horizontalAlignment: .center ) ), @@ -126,7 +126,7 @@ final class StarsBalanceComponent: Component { transition: .immediate, component: AnyComponent( SolidRoundedButtonComponent( - title: "Buy More Stars", + title: component.strings.Stars_Intro_Buy, theme: SolidRoundedButtonComponent.Theme(theme: component.theme), height: 50.0, cornerRadius: 11.0, diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionScreen.swift index 11fe0415ac..a9e8e3b5c7 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionScreen.swift @@ -118,7 +118,9 @@ private final class StarsTransactionSheetContent: CombinedComponent { let closeButton = Child(Button.self) let title = Child(MultilineTextComponent.self) let star = Child(GiftAvatarComponent.self) - let description = Child(BalancedTextComponent.self) + let amount = Child(BalancedTextComponent.self) + let amountStar = Child(BundleIconComponent.self) + let description = Child(MultilineTextComponent.self) let table = Child(TableComponent.self) let additional = Child(BalancedTextComponent.self) let button = Child(SolidRoundedButtonComponent.self) @@ -157,6 +159,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { ) let titleText: String + let amountText: String let descriptionText: String let additionalText: String let buttonText: String @@ -164,6 +167,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { let count: Int64 let transactionId: String? let date: Int32 + let via: String? let toPeer: EnginePeer? let photo: TelegramMediaWebFile? @@ -173,17 +177,24 @@ private final class StarsTransactionSheetContent: CombinedComponent { switch transaction.peer { case let .peer(peer): titleText = transaction.title ?? peer.compactDisplayTitle + via = nil case .appStore: - titleText = "In-App Purchase" + titleText = strings.Stars_Transaction_AppleTopUp_Title + via = strings.Stars_Transaction_AppleTopUp_Subtitle case .playMarket: - titleText = "Play Market" + titleText = strings.Stars_Transaction_GoogleTopUp_Title + via = strings.Stars_Transaction_GoogleTopUp_Subtitle case .premiumBot: - titleText = "Premium Bot" + titleText = strings.Stars_Transaction_PremiumBotTopUp_Title + via = strings.Stars_Transaction_PremiumBotTopUp_Subtitle case .fragment: - titleText = "Fragment" + titleText = strings.Stars_Transaction_FragmentTopUp_Title + via = strings.Stars_Transaction_FragmentTopUp_Subtitle case .unsupported: - titleText = "Unsupported" + titleText = strings.Stars_Transaction_Unsupported_Title + via = nil } + descriptionText = transaction.description ?? "" count = transaction.count transactionId = transaction.id @@ -196,7 +207,9 @@ private final class StarsTransactionSheetContent: CombinedComponent { photo = transaction.photo case let .receipt(receipt): titleText = receipt.invoiceMedia.title + descriptionText = receipt.invoiceMedia.description count = (receipt.invoice.prices.first?.amount ?? receipt.invoiceMedia.totalAmount) * -1 + via = nil transactionId = receipt.transactionId date = receipt.date if let peer = state.peerMap[receipt.botPaymentId] { @@ -207,22 +220,15 @@ private final class StarsTransactionSheetContent: CombinedComponent { photo = receipt.invoiceMedia.photo } + let formattedAmount = presentationStringsFormattedNumber(abs(Int32(count)), dateTimeFormat.decimalSeparator) + if count < 0 { + amountText = "- \(formattedAmount)" + } else { + amountText = "+ \(formattedAmount)" + } additionalText = strings.Stars_Transaction_Terms buttonText = strings.Common_OK - if count < 0 { - descriptionText = " - \(count * -1) # " - } else { - descriptionText = " + \(count) # " - } - - let descriptionAttributedText = NSMutableAttributedString(string: descriptionText, font: Font.semibold(18.0), textColor: descriptionText.hasPrefix("-") ? theme.list.itemDestructiveColor : theme.list.itemDisclosureActions.constructive.fillColor) - if let range = descriptionAttributedText.string.range(of: "#"), let chevronImage = generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: UIColor(rgb: 0xf09903)) { - descriptionAttributedText.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: descriptionAttributedText.string)) - descriptionAttributedText.addAttribute(.foregroundColor, value: UIColor(rgb: 0xf09903), range: NSRange(range, in: descriptionAttributedText.string)) - descriptionAttributedText.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: descriptionAttributedText.string)) - } - let title = title.update( component: MultilineTextComponent( text: .plain(NSAttributedString( @@ -254,9 +260,10 @@ private final class StarsTransactionSheetContent: CombinedComponent { transition: .immediate ) - let description = description.update( + let amountAttributedText = NSMutableAttributedString(string: amountText, font: Font.semibold(17.0), textColor: amountText.hasPrefix("-") ? theme.list.itemDestructiveColor : theme.list.itemDisclosureActions.constructive.fillColor) + let amount = amount.update( component: BalancedTextComponent( - text: .plain(descriptionAttributedText), + text: .plain(amountAttributedText), horizontalAlignment: .center, maximumNumberOfLines: 0, lineSpacing: 0.2 @@ -264,7 +271,16 @@ private final class StarsTransactionSheetContent: CombinedComponent { availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), transition: .immediate ) - + + let amountStar = amountStar.update( + component: BundleIconComponent( + name: "Premium/Stars/StarMedium", + tintColor: nil + ), + availableSize: context.availableSize, + transition: .immediate + ) + let tableFont = Font.regular(15.0) let tableTextColor = theme.list.itemPrimaryTextColor let tableLinkColor = theme.list.itemAccentColor @@ -294,6 +310,14 @@ private final class StarsTransactionSheetContent: CombinedComponent { ) ) )) + } else if let via { + tableItems.append(.init( + id: "via", + title: strings.Stars_Transaction_Via, + component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: via, font: tableFont, textColor: tableTextColor))) + ) + )) } if let transactionId { @@ -393,10 +417,35 @@ private final class StarsTransactionSheetContent: CombinedComponent { var originY: CGFloat = 0.0 originY += star.size.height - 23.0 - context.add(description - .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + description.size.height / 2.0)) + if !descriptionText.isEmpty { + let description = description.update( + component: MultilineTextComponent( + text: .plain(NSAttributedString( + string: descriptionText, + font: Font.regular(15.0), + textColor: theme.actionSheet.primaryTextColor, + paragraphAlignment: .center + )), + horizontalAlignment: .center, + maximumNumberOfLines: 3 + ), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude), + transition: .immediate + ) + context.add(description + .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + description.size.height / 2.0)) + ) + originY += description.size.height + 10.0 + } + + context.add(amount + .position(CGPoint(x: context.availableSize.width / 2.0 - 10.0, y: originY + amount.size.height / 2.0)) ) - originY += description.size.height + 20.0 + context.add(amountStar + .position(CGPoint(x: context.availableSize.width / 2.0 + amount.size.width / 2.0 + amountStar.size.width / 2.0 - 7.0, y: originY + amountStar.size.height / 2.0)) + ) + + originY += amount.size.height + 20.0 context.add(table .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0)) @@ -1054,7 +1103,6 @@ private final class TransactionCellComponent: Component { let size = CGSize(width: textSize.width + spacing + buttonSize.width, height: textSize.height) - let buttonFrame = CGRect(origin: CGPoint(x: textSize.width + spacing, y: floorToScreenPixels((size.height - buttonSize.height) / 2.0)), size: buttonSize) if let buttonView = self.button.view { if buttonView.superview == nil { @@ -1063,7 +1111,7 @@ private final class TransactionCellComponent: Component { transition.setFrame(view: buttonView, frame: buttonFrame) } - let textFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) + let textFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0) + 1.0), size: textSize) if let textView = self.text.view { if textView.superview == nil { self.addSubview(textView) diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift index 93c0ebe317..9e15b901bf 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift @@ -203,7 +203,7 @@ final class StarsTransactionsListPanelComponent: Component { separatorView = UIView() self.separatorViews[id] = separatorView - self.addSubview(separatorView) + self.scrollView.addSubview(separatorView) } separatorView.backgroundColor = environment.theme.list.itemBlocksSeparatorColor @@ -213,33 +213,38 @@ final class StarsTransactionsListPanelComponent: Component { let itemTitle: String let itemSubtitle: String? let itemDate: String - let itemLabel: NSAttributedString switch item.transaction.peer { case let .peer(peer): itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast) itemSubtitle = item.transaction.title - itemLabel = NSAttributedString(string: "- \(item.transaction.count * -1)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDestructiveColor) case .appStore: - itemTitle = "Stars Top-Up" - itemSubtitle = "via App Store" - itemLabel = NSAttributedString(string: "+ \(item.transaction.count)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDisclosureActions.constructive.fillColor) + itemTitle = environment.strings.Stars_Intro_Transaction_AppleTopUp_Title + itemSubtitle = environment.strings.Stars_Intro_Transaction_AppleTopUp_Subtitle case .playMarket: - itemTitle = "Stars Top-Up" - itemSubtitle = "via Play Market" - itemLabel = NSAttributedString(string: "+ \(item.transaction.count)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDisclosureActions.constructive.fillColor) + itemTitle = environment.strings.Stars_Intro_Transaction_GoogleTopUp_Title + itemSubtitle = environment.strings.Stars_Intro_Transaction_GoogleTopUp_Subtitle case .fragment: - itemTitle = "Stars Top-Up" - itemSubtitle = "via Fragment" - itemLabel = NSAttributedString(string: "+ \(item.transaction.count)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDisclosureActions.constructive.fillColor) + itemTitle = environment.strings.Stars_Intro_Transaction_FragmentTopUp_Title + itemSubtitle = environment.strings.Stars_Intro_Transaction_FragmentTopUp_Subtitle case .premiumBot: - itemTitle = "Stars Top-Up" - itemSubtitle = "via Premium Bot" - itemLabel = NSAttributedString(string: "+ \(item.transaction.count)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDisclosureActions.constructive.fillColor) + itemTitle = environment.strings.Stars_Intro_Transaction_PremiumBotTopUp_Title + itemSubtitle = environment.strings.Stars_Intro_Transaction_PremiumBotTopUp_Subtitle case .unsupported: - itemTitle = "Unsupported" + itemTitle = environment.strings.Stars_Intro_Transaction_Unsupported_Title itemSubtitle = nil - itemLabel = NSAttributedString(string: "+ \(item.transaction.count)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDisclosureActions.constructive.fillColor) } + + let itemLabel: NSAttributedString + let labelString: String + + let formattedLabel = presentationStringsFormattedNumber(abs(Int32(item.transaction.count)), environment.dateTimeFormat.decimalSeparator) + if item.transaction.count < 0 { + labelString = "- \(formattedLabel)" + } else { + labelString = "+ \(formattedLabel)" + } + itemLabel = NSAttributedString(string: labelString, font: Font.medium(fontBaseDisplaySize), textColor: labelString.hasPrefix("-") ? environment.theme.list.itemDestructiveColor : environment.theme.list.itemDisclosureActions.constructive.fillColor) + itemDate = stringForMediumCompactDate(timestamp: item.transaction.date, strings: environment.strings, dateTimeFormat: environment.dateTimeFormat) var titleComponents: [AnyComponentWithIdentity] = [] @@ -577,7 +582,7 @@ private final class LabelComponent: CombinedComponent { let iconSize = CGSize(width: 20.0, height: 20.0) let icon = icon.update( component: BundleIconComponent( - name: "Premium/Stars/Star", + name: "Premium/Stars/StarLarge", tintColor: nil ), availableSize: iconSize, @@ -592,7 +597,7 @@ private final class LabelComponent: CombinedComponent { .position(CGPoint(x: text.size.width / 2.0, y: size.height / 2.0)) ) context.add(icon - .position(CGPoint(x: totalWidth - iconSize.width / 2.0, y: size.height / 2.0)) + .position(CGPoint(x: totalWidth - iconSize.width / 2.0, y: size.height / 2.0 - UIScreenPixel)) ) return size } diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsPanelContainerComponent.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsPanelContainerComponent.swift index e295255123..0176aca27d 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsPanelContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsPanelContainerComponent.swift @@ -623,7 +623,9 @@ final class StarsTransactionsPanelContainerComponent: Component { } } - let _ = self.header.update( + let sideInset: CGFloat = 16.0 + let condensedPanelWidth: CGFloat = availableSize.width - sideInset * 2.0 + let headerSize = self.header.update( transition: transition, component: AnyComponent(StarsTransactionsHeaderComponent( theme: component.theme, @@ -648,13 +650,13 @@ final class StarsTransactionsPanelContainerComponent: Component { } )), environment: {}, - containerSize: topPanelFrame.size + containerSize: CGSize(width: condensedPanelWidth, height: topPanelFrame.size.height) ) if let headerView = self.header.view { if headerView.superview == nil { self.addSubview(headerView) } - transition.setFrame(view: headerView, frame: topPanelFrame) + transition.setFrame(view: headerView, frame: CGRect(origin: topPanelFrame.origin.offsetBy(dx: sideInset, dy: 0.0), size: headerSize)) } let childEnvironment = StarsTransactionsPanelEnvironment( diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift index 2398bbfd58..c65b493a29 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift @@ -15,6 +15,7 @@ import BalancedTextComponent import Markdown import PremiumStarComponent import ListSectionComponent +import BundleIconComponent import TextFormat final class StarsTransactionsScreenComponent: Component { @@ -90,7 +91,9 @@ final class StarsTransactionsScreenComponent: Component { private let balanceView = ComponentView() - private let topBalanceView = ComponentView() + private let topBalanceTitleView = ComponentView() + private let topBalanceValueView = ComponentView() + private let topBalanceIconView = ComponentView() private let panelContainer = ComponentView() @@ -241,8 +244,15 @@ final class StarsTransactionsScreenComponent: Component { panelContainerView.updateNavigationMergeFactor(value: 1.0 - expansionDistanceFactor, transition: transition) } - if let topBalanceView = self.topBalanceView.view { - topBalanceView.alpha = 1.0 - expansionDistanceFactor + let topBalanceAlpha = 1.0 - expansionDistanceFactor + if let view = self.topBalanceTitleView.view { + view.alpha = topBalanceAlpha + } + if let view = self.topBalanceValueView.view { + view.alpha = topBalanceAlpha + } + if let view = self.topBalanceIconView.view { + view.alpha = topBalanceAlpha } } @@ -408,36 +418,69 @@ final class StarsTransactionsScreenComponent: Component { starTransition.setBounds(view: titleView, bounds: CGRect(origin: .zero, size: titleSize)) } - let textFont = Font.regular(14.0) - let boldTextFont = Font.semibold(14.0) - let textColor = environment.theme.actionSheet.primaryTextColor - let linkColor = environment.theme.actionSheet.controlAccentColor - let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in - return (TelegramTextAttributes.URL, contents) - }) - let balanceAttributedString = parseMarkdownIntoAttributedString(" \(environment.strings.Stars_Intro_Balance)\n # **\(self.starsState?.balance ?? 0)**", attributes: markdownAttributes, textAlignment: .right).mutableCopy() as! NSMutableAttributedString - if let range = balanceAttributedString.string.range(of: "#"), let chevronImage = generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: UIColor(rgb: 0xf09903)) { - balanceAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: balanceAttributedString.string)) - balanceAttributedString.addAttribute(.foregroundColor, value: UIColor(rgb: 0xf09903), range: NSRange(range, in: balanceAttributedString.string)) - balanceAttributedString.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: balanceAttributedString.string)) - } - let topBalanceSize = self.topBalanceView.update( + let topBalanceTitleSize = self.topBalanceTitleView.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(balanceAttributedString), + text: .plain(NSAttributedString( + string: environment.strings.Stars_Intro_Balance, + font: Font.regular(14.0), + textColor: environment.theme.actionSheet.primaryTextColor + )), horizontalAlignment: .right, - maximumNumberOfLines: 0, - lineSpacing: 0.1 + maximumNumberOfLines: 1 )), environment: {}, containerSize: CGSize(width: 120.0, height: 100.0) ) - if let topBalanceView = self.topBalanceView.view { - if topBalanceView.superview == nil { - topBalanceView.alpha = 0.0 - self.addSubview(topBalanceView) + + let topBalanceValueSize = self.topBalanceValueView.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: presentationStringsFormattedNumber(Int32(self.starsState?.balance ?? 0), environment.dateTimeFormat.decimalSeparator), + font: Font.semibold(14.0), + textColor: environment.theme.actionSheet.primaryTextColor + )), + horizontalAlignment: .right, + maximumNumberOfLines: 1 + )), + environment: {}, + containerSize: CGSize(width: 120.0, height: 100.0) + ) + let topBalanceIconSize = self.topBalanceIconView.update( + transition: .immediate, + component: AnyComponent(BundleIconComponent(name: "Premium/Stars/StarSmall", tintColor: nil)), + environment: {}, + containerSize: availableSize + ) + + let navigationHeight = environment.navigationHeight - environment.statusBarHeight + let topBalanceOriginY = environment.statusBarHeight + (navigationHeight - topBalanceTitleSize.height - topBalanceValueSize.height) / 2.0 + let topBalanceTitleFrame = CGRect(origin: CGPoint(x: availableSize.width - topBalanceTitleSize.width - 16.0, y: topBalanceOriginY), size: topBalanceTitleSize) + if let topBalanceTitleView = self.topBalanceTitleView.view { + if topBalanceTitleView.superview == nil { + topBalanceTitleView.alpha = 0.0 + self.addSubview(topBalanceTitleView) } - starTransition.setFrame(view: topBalanceView, frame: CGRect(origin: CGPoint(x: availableSize.width - topBalanceSize.width - 16.0, y: 56.0), size: topBalanceSize)) + starTransition.setFrame(view: topBalanceTitleView, frame: topBalanceTitleFrame) + } + + let topBalanceValueFrame = CGRect(origin: CGPoint(x: availableSize.width - topBalanceValueSize.width - 16.0, y: topBalanceTitleFrame.maxY), size: topBalanceValueSize) + if let topBalanceValueView = self.topBalanceValueView.view { + if topBalanceValueView.superview == nil { + topBalanceValueView.alpha = 0.0 + self.addSubview(topBalanceValueView) + } + starTransition.setFrame(view: topBalanceValueView, frame: topBalanceValueFrame) + } + + let topBalanceIconFrame = CGRect(origin: CGPoint(x: topBalanceValueFrame.minX - topBalanceIconSize.width - 2.0, y: floorToScreenPixels(topBalanceValueFrame.midY - topBalanceIconSize.height / 2.0) - UIScreenPixel), size: topBalanceIconSize) + if let topBalanceIconView = self.topBalanceIconView.view { + if topBalanceIconView.superview == nil { + topBalanceIconView.alpha = 0.0 + self.addSubview(topBalanceIconView) + } + starTransition.setFrame(view: topBalanceIconView, frame: topBalanceIconFrame) } contentHeight += 181.0 diff --git a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift index abf0a3408d..186639df2d 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift @@ -262,11 +262,12 @@ private final class SheetContent: CombinedComponent { contentSize.height += 28.0 if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== theme { - state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Premium/Stars/Star"), color: UIColor(rgb: 0xf09903))!, theme) + state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Premium/Stars/StarLarge"), color: UIColor(rgb: 0xf09903))!, theme) } + let balanceValue = presentationStringsFormattedNumber(Int32(state.balance ?? 0), environment.dateTimeFormat.decimalSeparator) let balanceAttributedString = NSMutableAttributedString(string: strings.Stars_Transfer_Balance, font: Font.regular(14.0), textColor: textColor) - balanceAttributedString.append(NSMutableAttributedString(string: "\n # \(state.balance ?? 0)", font: Font.semibold(16.0), textColor: textColor)) + balanceAttributedString.append(NSMutableAttributedString(string: "\n # \(balanceValue)", font: Font.semibold(16.0), textColor: textColor)) if let range = balanceAttributedString.string.range(of: "#"), let chevronImage = state.cachedChevronImage?.0 { balanceAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: balanceAttributedString.string)) balanceAttributedString.addAttribute(.foregroundColor, value: UIColor(rgb: 0xf09903), range: NSRange(range, in: balanceAttributedString.string)) @@ -336,7 +337,7 @@ private final class SheetContent: CombinedComponent { let resultController = UndoOverlayController( presentationData: presentationData, content: .image( - image: UIImage(bundleImageName: "Premium/Stars/Star")!, + image: UIImage(bundleImageName: "Premium/Stars/StarLarge")!, title: presentationData.strings.Stars_Transfer_PurchasedTitle, text: presentationData.strings.Stars_Transfer_PurchasedText(invoice.title, botTitle, presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))).string, round: false, diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/Star.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Stars/BalanceStar.imageset/Contents.json similarity index 71% rename from submodules/TelegramUI/Images.xcassets/Premium/Stars/Star.imageset/Contents.json rename to submodules/TelegramUI/Images.xcassets/Premium/Stars/BalanceStar.imageset/Contents.json index d4ec011d84..ac82493b96 100644 --- a/submodules/TelegramUI/Images.xcassets/Premium/Stars/Star.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Premium/Stars/BalanceStar.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "transactionstar_20 (2).pdf", + "filename" : "balancestar_48 (2).pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarLarge.imageset/balancestar_48 (2).pdf b/submodules/TelegramUI/Images.xcassets/Premium/Stars/BalanceStar.imageset/balancestar_48 (2).pdf similarity index 100% rename from submodules/TelegramUI/Images.xcassets/Premium/Stars/StarLarge.imageset/balancestar_48 (2).pdf rename to submodules/TelegramUI/Images.xcassets/Premium/Stars/BalanceStar.imageset/balancestar_48 (2).pdf diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/Star.imageset/transactionstar_20 (2).pdf b/submodules/TelegramUI/Images.xcassets/Premium/Stars/Star.imageset/transactionstar_20 (2).pdf deleted file mode 100644 index aefce12699..0000000000 Binary files a/submodules/TelegramUI/Images.xcassets/Premium/Stars/Star.imageset/transactionstar_20 (2).pdf and /dev/null differ diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarLarge.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarLarge.imageset/Contents.json index ac82493b96..653a52e723 100644 --- a/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarLarge.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarLarge.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "balancestar_48 (2).pdf", + "filename" : "Star20.pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarLarge.imageset/Star20.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarLarge.imageset/Star20.pdf new file mode 100644 index 0000000000..ad28ef79f1 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarLarge.imageset/Star20.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarMedium.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarMedium.imageset/Contents.json new file mode 100644 index 0000000000..bea582e947 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarMedium.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "star_18.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarMedium.imageset/star_18.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarMedium.imageset/star_18.pdf new file mode 100644 index 0000000000..3f23ba8d2b Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarMedium.imageset/star_18.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarSmall.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarSmall.imageset/Contents.json new file mode 100644 index 0000000000..0c43076025 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarSmall.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "star_16 (2).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarSmall.imageset/star_16 (2).pdf b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarSmall.imageset/star_16 (2).pdf new file mode 100644 index 0000000000..ea68a6d380 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Premium/Stars/StarSmall.imageset/star_16 (2).pdf differ diff --git a/submodules/TelegramUI/Sources/CreateChannelController.swift b/submodules/TelegramUI/Sources/CreateChannelController.swift index 88091d0eb9..c5f11b6048 100644 --- a/submodules/TelegramUI/Sources/CreateChannelController.swift +++ b/submodules/TelegramUI/Sources/CreateChannelController.swift @@ -191,7 +191,7 @@ private enum CreateChannelEntry: ItemListNodeEntry { let arguments = arguments as! CreateChannelArguments switch self { case let .channelInfo(_, _, dateTimeFormat, peer, state, avatar): - return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in + return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in arguments.updateEditingName(editingName) }, editingNameCompleted: { arguments.focusOnDescription() diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift index a47f348bc9..d1108c444d 100644 --- a/submodules/TelegramUI/Sources/CreateGroupController.swift +++ b/submodules/TelegramUI/Sources/CreateGroupController.swift @@ -318,7 +318,7 @@ private enum CreateGroupEntry: ItemListNodeEntry { let arguments = arguments as! CreateGroupArguments switch self { case let .groupInfo(_, _, dateTimeFormat, peer, state, avatar): - return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in + return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in arguments.updateEditingName(editingName) }, editingNameCompleted: { arguments.done()