Various improvements

This commit is contained in:
Ilya Laktyushin 2022-06-07 15:58:56 +04:00
parent 149805e914
commit 57e7e6906b
20 changed files with 425 additions and 330 deletions

View File

@ -890,7 +890,7 @@ public protocol AccountContext: AnyObject {
public struct PremiumConfiguration { public struct PremiumConfiguration {
public static var defaultValue: PremiumConfiguration { public static var defaultValue: PremiumConfiguration {
return PremiumConfiguration(isPremiumDisabled: false) return PremiumConfiguration(isPremiumDisabled: true)
} }
public let isPremiumDisabled: Bool public let isPremiumDisabled: Bool

View File

@ -1034,14 +1034,14 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
let controller = internalChatListFilterExcludeChatsController(context: context, filter: filter, allFilters: filters, applyAutomatically: false, updated: { filter in let controller = internalChatListFilterExcludeChatsController(context: context, filter: filter, allFilters: filters, applyAutomatically: false, updated: { filter in
skipStateAnimation = true skipStateAnimation = true
updateState { state in updateState { state in
var state = state var updatedState = state
state.additionallyIncludePeers = filter.data?.includePeers.peers ?? [] updatedState.additionallyIncludePeers = filter.data?.includePeers.peers ?? []
state.additionallyExcludePeers = filter.data?.excludePeers ?? [] updatedState.additionallyExcludePeers = filter.data?.excludePeers ?? []
state.includeCategories = filter.data?.categories ?? [] updatedState.includeCategories = filter.data?.categories ?? []
state.excludeRead = filter.data?.excludeRead ?? false updatedState.excludeRead = filter.data?.excludeRead ?? false
state.excludeMuted = filter.data?.excludeMuted ?? false updatedState.excludeMuted = filter.data?.excludeMuted ?? false
state.excludeArchived = filter.data?.excludeArchived ?? false updatedState.excludeArchived = filter.data?.excludeArchived ?? false
return state return updatedState
} }
}) })
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
@ -1125,7 +1125,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
var found = false var found = false
for i in 0 ..< filters.count { for i in 0 ..< filters.count {
if filters[i].id == updatedFilter.id, case let .filter(_, _, _, data) = filters[i] { if filters[i].id == updatedFilter.id, case let .filter(_, _, _, data) = filters[i] {
var updatedData = data var updatedData = updatedFilter.data ?? data
var includePeers = updatedData.includePeers var includePeers = updatedData.includePeers
includePeers.setPeers(state.additionallyIncludePeers) includePeers.setPeers(state.additionallyIncludePeers)
updatedData.includePeers = includePeers updatedData.includePeers = includePeers

View File

@ -1484,6 +1484,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
currentSecretIconImage = PresentationResourcesChatList.secretIcon(item.presentationData.theme) currentSecretIconImage = PresentationResourcesChatList.secretIcon(item.presentationData.theme)
} }
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 })
if !isPeerGroup && item.index.messageIndex.id.peerId != item.context.account.peerId { if !isPeerGroup && item.index.messageIndex.id.peerId != item.context.account.peerId {
if displayAsMessage { if displayAsMessage {
switch item.content { switch item.content {
@ -1495,7 +1496,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular) currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
} else if peer.isVerified { } else if peer.isVerified {
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme) currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
} else if peer.isPremium { } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme) currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme)
} }
} }
@ -1509,7 +1510,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular) currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
} else if peer.isVerified { } else if peer.isVerified {
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme) currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
} else if peer.isPremium { } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme) currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme)
} }
} }

View File

@ -563,6 +563,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
} }
} }
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 })
var currentCredibilityIconImage: UIImage? var currentCredibilityIconImage: UIImage?
switch item.peer { switch item.peer {
case let .peer(peer, _): case let .peer(peer, _):
@ -573,7 +575,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular) currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
} else if peer.isVerified { } else if peer.isVerified {
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme) currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
} else if peer.isPremium { } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme) currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme)
} }
} }

View File

@ -24,6 +24,7 @@ swift_library(
"//submodules/AppBundle:AppBundle", "//submodules/AppBundle:AppBundle",
"//submodules/GZip:GZip", "//submodules/GZip:GZip",
"//third-party/ZipArchive:ZipArchive", "//third-party/ZipArchive:ZipArchive",
"//submodules/InAppPurchaseManager:InAppPurchaseManager",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -15,6 +15,7 @@ import AccountContext
import AppBundle import AppBundle
import ZipArchive import ZipArchive
import WebKit import WebKit
import InAppPurchaseManager
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate { @objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
@ -86,13 +87,13 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case experimentalBackground(Bool) case experimentalBackground(Bool)
case inlineStickers(Bool) case inlineStickers(Bool)
case localTranscription(Bool) case localTranscription(Bool)
case snow(Bool)
case playerEmbedding(Bool) case playerEmbedding(Bool)
case playlistPlayback(Bool) case playlistPlayback(Bool)
case voiceConference case voiceConference
case preferredVideoCodec(Int, String, String?, Bool) case preferredVideoCodec(Int, String, String?, Bool)
case disableVideoAspectScaling(Bool) case disableVideoAspectScaling(Bool)
case enableVoipTcp(Bool) case enableVoipTcp(Bool)
case resetInAppPurchases(PresentationTheme)
case hostInfo(PresentationTheme, String) case hostInfo(PresentationTheme, String)
case versionInfo(PresentationTheme) case versionInfo(PresentationTheme)
@ -108,7 +109,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return DebugControllerSection.logging.rawValue return DebugControllerSection.logging.rawValue
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries: case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
return DebugControllerSection.experiments.rawValue return DebugControllerSection.experiments.rawValue
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .inlineStickers, .localTranscription, .snow: case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .inlineStickers, .localTranscription, .resetInAppPurchases:
return DebugControllerSection.experiments.rawValue return DebugControllerSection.experiments.rawValue
case .preferredVideoCodec: case .preferredVideoCodec:
return DebugControllerSection.videoExperiments.rawValue return DebugControllerSection.videoExperiments.rawValue
@ -187,7 +188,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 31 return 31
case .localTranscription: case .localTranscription:
return 32 return 32
case .snow: case .resetInAppPurchases:
return 33 return 33
case .playerEmbedding: case .playerEmbedding:
return 34 return 34
@ -969,16 +970,6 @@ private enum DebugControllerEntry: ItemListNodeEntry {
}) })
}).start() }).start()
}) })
case let .snow(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Snow", value: value, sectionId: self.section, style: .blocks, updated: { value in
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
settings.snow = value
return PreferencesEntry(settings)
})
}).start()
})
case let .playerEmbedding(value): case let .playerEmbedding(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Player Embedding", value: value, sectionId: self.section, style: .blocks, updated: { value in return ItemListSwitchItem(presentationData: presentationData, title: "Player Embedding", value: value, sectionId: self.section, style: .blocks, updated: { value in
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
@ -1035,6 +1026,10 @@ private enum DebugControllerEntry: ItemListNodeEntry {
}) })
}).start() }).start()
}) })
case .resetInAppPurchases:
return ItemListActionItem(presentationData: presentationData, title: "Reset IAP Transactions", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: {
arguments.context?.inAppPurchaseManager?.finishAllTransactions()
})
case let .hostInfo(_, string): case let .hostInfo(_, string):
return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section) return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section)
case .versionInfo: case .versionInfo:
@ -1096,6 +1091,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground)) entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
entries.append(.inlineStickers(experimentalSettings.inlineStickers)) entries.append(.inlineStickers(experimentalSettings.inlineStickers))
entries.append(.localTranscription(experimentalSettings.localTranscription)) entries.append(.localTranscription(experimentalSettings.localTranscription))
entries.append(.resetInAppPurchases(presentationData.theme))
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding)) entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
} }

View File

@ -93,6 +93,14 @@ public final class InAppPurchaseManager: NSObject {
return self.productsPromise.get() return self.productsPromise.get()
} }
public func finishAllTransactions() {
let paymentQueue = SKPaymentQueue.default()
let transactions = paymentQueue.transactions
for transaction in transactions {
paymentQueue.finishTransaction(transaction)
}
}
public func buyProduct(_ product: Product, account: Account) -> Signal<PurchaseState, PurchaseError> { public func buyProduct(_ product: Product, account: Account) -> Signal<PurchaseState, PurchaseError> {
let payment = SKPayment(product: product.skProduct) let payment = SKPayment(product: product.skProduct)
SKPaymentQueue.default().add(payment) SKPaymentQueue.default().add(payment)
@ -159,6 +167,18 @@ extension InAppPurchaseManager: SKProductsRequestDelegate {
} }
} }
private func getReceiptData() -> Data? {
var receiptData: Data?
if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL, FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {
do {
receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
} catch {
Logger.shared.log("InAppPurchaseManager", "Couldn't read receipt data with error: \(error.localizedDescription)")
}
}
return receiptData
}
extension InAppPurchaseManager: SKPaymentTransactionObserver { extension InAppPurchaseManager: SKPaymentTransactionObserver {
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions { for transaction in transactions {
@ -167,23 +187,28 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
let transactionState: TransactionState? let transactionState: TransactionState?
switch transaction.transactionState { switch transaction.transactionState {
case .purchased: case .purchased:
transactionState = .purchased(transactionId: transaction.transactionIdentifier) if transaction.original == nil {
if let transactionIdentifier = transaction.transactionIdentifier { transactionState = .purchased(transactionId: transaction.transactionIdentifier)
self.disposableSet.set( if let transactionIdentifier = transaction.transactionIdentifier {
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier).start(error: { error in self.disposableSet.set(
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: false).start(error: { _ in
}, completed: { queue.finishTransaction(transaction)
queue.finishTransaction(transaction) }, completed: {
}), queue.finishTransaction(transaction)
forKey: transaction.transactionIdentifier ?? "" }),
) forKey: transaction.transactionIdentifier ?? ""
)
}
} else {
transactionState = nil
queue.finishTransaction(transaction)
} }
case .restored: case .restored:
transactionState = .restored(transactionId: transaction.transactionIdentifier) transactionState = .restored(transactionId: transaction.original?.transactionIdentifier)
if let transactionIdentifier = transaction.transactionIdentifier { if let transactionIdentifier = transaction.original?.transactionIdentifier {
self.disposableSet.set( self.disposableSet.set(
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier).start(error: { error in self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: true).start(error: { _ in
queue.finishTransaction(transaction)
}, completed: { }, completed: {
queue.finishTransaction(transaction) queue.finishTransaction(transaction)
}), }),

View File

@ -358,6 +358,8 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
updatedTheme = item.presentationData.theme updatedTheme = item.presentationData.theme
} }
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.accountContext.currentAppConfiguration.with { $0 })
var credibilityIconImage: UIImage? var credibilityIconImage: UIImage?
var credibilityIconOffset: CGFloat = 4.0 var credibilityIconOffset: CGFloat = 4.0
if let peer = item.peer { if let peer = item.peer {
@ -369,6 +371,8 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
credibilityIconOffset = 2.0 credibilityIconOffset = 2.0
} else if peer.isVerified { } else if peer.isVerified {
credibilityIconImage = PresentationResourcesItemList.verifiedPeerIcon(item.presentationData.theme) credibilityIconImage = PresentationResourcesItemList.verifiedPeerIcon(item.presentationData.theme)
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
credibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme)
} }
} }

View File

@ -603,6 +603,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
var updatedLabelBadgeImage: UIImage? var updatedLabelBadgeImage: UIImage?
var currentCredibilityIconImage: UIImage? var currentCredibilityIconImage: UIImage?
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 })
if case .threatSelfAsSaved = item.aliasHandling, item.peer.id == item.context.account.peerId { if case .threatSelfAsSaved = item.aliasHandling, item.peer.id == item.context.account.peerId {
} else { } else {
@ -612,7 +614,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular) currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
} else if item.peer.isVerified { } else if item.peer.isVerified {
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme) currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
} else if item.peer.isPremium { } else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled {
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme) currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme)
} }
} }

View File

@ -622,9 +622,16 @@ private struct ChannelVisibilityControllerState: Equatable {
} }
} }
private func channelVisibilityControllerEntries(presentationData: PresentationData, mode: ChannelVisibilityControllerMode, view: PeerView, publicChannelsToRevoke: [Peer]?, importers: PeerInvitationImportersState?, state: ChannelVisibilityControllerState, limits: EngineConfiguration.UserLimits, premiumLimits: EngineConfiguration.UserLimits, isPremium: Bool) -> [ChannelVisibilityEntry] { private func channelVisibilityControllerEntries(presentationData: PresentationData, mode: ChannelVisibilityControllerMode, view: PeerView, publicChannelsToRevoke: [Peer]?, importers: PeerInvitationImportersState?, state: ChannelVisibilityControllerState, limits: EngineConfiguration.UserLimits, premiumLimits: EngineConfiguration.UserLimits, isPremium: Bool, isPremiumDisabled: Bool) -> [ChannelVisibilityEntry] {
var entries: [ChannelVisibilityEntry] = [] var entries: [ChannelVisibilityEntry] = []
let isInitialSetup: Bool
if case .initialSetup = mode {
isInitialSetup = true
} else {
isInitialSetup = false
}
if let peer = view.peers[view.peerId] as? TelegramChannel { if let peer = view.peers[view.peerId] as? TelegramChannel {
var isGroup = false var isGroup = false
if case .group = peer.info { if case .group = peer.info {
@ -721,36 +728,30 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
} }
} }
switch selectedType { if case .revokeNames = mode {
case .publicChannel: let count = Int32(publicChannelsToRevoke?.count ?? 0)
var displayAvailability = false entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(premiumLimits.maxPublicLinksCount)").string, count, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount, isPremiumDisabled))
if peer.addressName == nil {
displayAvailability = publicChannelsToRevoke != nil && !(publicChannelsToRevoke!.isEmpty) if let publicChannelsToRevoke = publicChannelsToRevoke {
} var index: Int32 = 0
for peer in publicChannelsToRevoke.sorted(by: { lhs, rhs in
if !"".isEmpty && displayAvailability { var lhsDate: Int32 = 0
if let publicChannelsToRevoke = publicChannelsToRevoke { var rhsDate: Int32 = 0
// entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(20)").string, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount)) if let lhs = lhs as? TelegramChannel {
lhsDate = lhs.creationDate
var index: Int32 = 0
for peer in publicChannelsToRevoke.sorted(by: { lhs, rhs in
var lhsDate: Int32 = 0
var rhsDate: Int32 = 0
if let lhs = lhs as? TelegramChannel {
lhsDate = lhs.creationDate
}
if let rhs = rhs as? TelegramChannel {
rhsDate = rhs.creationDate
}
return lhsDate > rhsDate
}) {
entries.append(.existingLinkPeerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: true, revealed: state.revealedRevokePeerId == peer.id), state.revokingPeerId == nil))
index += 1
}
} else {
entries.append(.publicLinkAvailability(presentationData.theme, presentationData.strings.Group_Username_CreatePublicLinkHelp, true))
} }
} else { if let rhs = rhs as? TelegramChannel {
rhsDate = rhs.creationDate
}
return lhsDate > rhsDate
}) {
entries.append(.existingLinkPeerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: true, revealed: state.revealedRevokePeerId == peer.id), state.revokingPeerId == nil))
index += 1
}
}
} else {
switch selectedType {
case .publicChannel:
entries.append(.editablePublicLink(presentationData.theme, presentationData.strings, presentationData.strings.Group_PublicLink_Placeholder, currentAddressName)) entries.append(.editablePublicLink(presentationData.theme, presentationData.strings, presentationData.strings.Group_PublicLink_Placeholder, currentAddressName))
if let status = state.addressNameValidationStatus { if let status = state.addressNameValidationStatus {
let text: String let text: String
@ -804,7 +805,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Group_PublicLink_Info)) entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Group_PublicLink_Info))
} else { } else {
entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePublicLinkHelp)) entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePublicLinkHelp))
} }
} else { } else {
entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePublicLinkHelp)) entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePublicLinkHelp))
} }
@ -815,199 +816,203 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage)) entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo)) entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
} }
}
case .privateChannel:
let invite = (view.cachedData as? CachedChannelData)?.exportedInvitation
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_InviteLink.uppercased()))
entries.append(.privateLink(presentationData.theme, invite, importers?.importers.prefix(3).compactMap { $0.peer.peer.flatMap(EnginePeer.init) } ?? [], importers?.count ?? 0, mode != .initialSetup))
if isGroup {
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp))
} else {
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePrivateLinkHelp))
}
switch mode {
case .initialSetup, .revokeNames:
break
case .generic, .privateLink:
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
}
}
var isDiscussion = false
if let cachedData = view.cachedData as? CachedChannelData, case .known = cachedData.linkedDiscussionPeerId {
isDiscussion = true
}
if isGroup && (selectedType == .publicChannel || isDiscussion) {
if isDiscussion {
entries.append(.joinToSendHeader(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_Title.uppercased()))
entries.append(.joinToSendEveryone(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_Everyone, joinToSend == .everyone))
entries.append(.joinToSendMembers(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_OnlyMembers, joinToSend == .members))
}
if !isDiscussion || joinToSend == .members {
entries.append(.approveMembers(presentationData.theme, presentationData.strings.Group_Setup_ApproveNewMembers, approveMembers))
entries.append(.approveMembersInfo(presentationData.theme, presentationData.strings.Group_Setup_ApproveNewMembersInfo))
}
}
entries.append(.forwardingHeader(presentationData.theme, isGroup ? presentationData.strings.Group_Setup_ForwardingGroupTitle.uppercased() : presentationData.strings.Group_Setup_ForwardingChannelTitle.uppercased()))
entries.append(.forwardingDisabled(presentationData.theme, presentationData.strings.Group_Setup_ForwardingDisabled, !forwardingEnabled))
entries.append(.forwardingInfo(presentationData.theme, forwardingEnabled ? (isGroup ? presentationData.strings.Group_Setup_ForwardingGroupInfo : presentationData.strings.Group_Setup_ForwardingChannelInfo) : (isGroup ? presentationData.strings.Group_Setup_ForwardingGroupInfoDisabled : presentationData.strings.Group_Setup_ForwardingChannelInfoDisabled)))
} else if let peer = view.peers[view.peerId] as? TelegramGroup {
switch mode {
case .revokeNames:
if let publicChannelsToRevoke = publicChannelsToRevoke {
// entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(20)").string, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount))
entries.append(.publicLinkAvailability(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesInfo, false))
var index: Int32 = 0
for peer in publicChannelsToRevoke.sorted(by: { lhs, rhs in
var lhsDate: Int32 = 0
var rhsDate: Int32 = 0
if let lhs = lhs as? TelegramChannel {
lhsDate = lhs.creationDate
}
if let rhs = rhs as? TelegramChannel {
rhsDate = rhs.creationDate
}
return lhsDate > rhsDate
}) {
entries.append(.existingLinkPeerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: true, revealed: state.revealedRevokePeerId == peer.id), state.revokingPeerId == nil))
index += 1
}
}
case .privateLink:
let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_InviteLink.uppercased()))
entries.append(.privateLink(presentationData.theme, invite, importers?.importers.prefix(3).compactMap { $0.peer.peer.flatMap(EnginePeer.init) } ?? [], importers?.count ?? 0, mode != .initialSetup))
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.GroupInfo_InviteLink_Help))
switch mode {
case .initialSetup, .revokeNames:
break
case .generic, .privateLink:
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
}
case .generic, .initialSetup:
let selectedType: CurrentChannelType
if let current = state.selectedType {
selectedType = current
} else {
selectedType = .privateChannel
}
let currentAddressName: String
if let current = state.editingPublicLinkText {
currentAddressName = current
} else {
currentAddressName = ""
}
entries.append(.typeHeader(presentationData.theme, presentationData.strings.Group_Setup_TypeHeader.uppercased()))
entries.append(.typePublic(presentationData.theme, presentationData.strings.Channel_Setup_TypePublic, selectedType == .publicChannel))
entries.append(.typePrivate(presentationData.theme, presentationData.strings.Channel_Setup_TypePrivate, selectedType == .privateChannel))
switch selectedType {
case .publicChannel:
entries.append(.typeInfo(presentationData.theme, presentationData.strings.Group_Setup_TypePublicHelp))
case .privateChannel: case .privateChannel:
entries.append(.typeInfo(presentationData.theme, presentationData.strings.Group_Setup_TypePrivateHelp)) let invite = (view.cachedData as? CachedChannelData)?.exportedInvitation
} entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_InviteLink.uppercased()))
entries.append(.privateLink(presentationData.theme, invite, importers?.importers.prefix(3).compactMap { $0.peer.peer.flatMap(EnginePeer.init) } ?? [], importers?.count ?? 0, !isInitialSetup))
switch selectedType { if isGroup {
case .publicChannel: entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp))
let displayAvailability = publicChannelsToRevoke == nil || !(publicChannelsToRevoke!.isEmpty) } else {
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Channel_Username_CreatePrivateLinkHelp))
}
switch mode {
case .initialSetup, .revokeNames:
break
case .generic, .privateLink:
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
}
}
if displayAvailability { var isDiscussion = false
if let publicChannelsToRevoke = publicChannelsToRevoke { if let cachedData = view.cachedData as? CachedChannelData, case .known = cachedData.linkedDiscussionPeerId {
// entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(20)").string, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount)) isDiscussion = true
}
entries.append(.publicLinkAvailability(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesInfo, false))
var index: Int32 = 0 if isGroup && (selectedType == .publicChannel || isDiscussion) {
for peer in publicChannelsToRevoke.sorted(by: { lhs, rhs in if isDiscussion {
var lhsDate: Int32 = 0 entries.append(.joinToSendHeader(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_Title.uppercased()))
var rhsDate: Int32 = 0 entries.append(.joinToSendEveryone(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_Everyone, joinToSend == .everyone))
if let lhs = lhs as? TelegramChannel { entries.append(.joinToSendMembers(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_OnlyMembers, joinToSend == .members))
lhsDate = lhs.creationDate }
if !isDiscussion || joinToSend == .members {
entries.append(.approveMembers(presentationData.theme, presentationData.strings.Group_Setup_ApproveNewMembers, approveMembers))
entries.append(.approveMembersInfo(presentationData.theme, presentationData.strings.Group_Setup_ApproveNewMembersInfo))
}
}
entries.append(.forwardingHeader(presentationData.theme, isGroup ? presentationData.strings.Group_Setup_ForwardingGroupTitle.uppercased() : presentationData.strings.Group_Setup_ForwardingChannelTitle.uppercased()))
entries.append(.forwardingDisabled(presentationData.theme, presentationData.strings.Group_Setup_ForwardingDisabled, !forwardingEnabled))
entries.append(.forwardingInfo(presentationData.theme, forwardingEnabled ? (isGroup ? presentationData.strings.Group_Setup_ForwardingGroupInfo : presentationData.strings.Group_Setup_ForwardingChannelInfo) : (isGroup ? presentationData.strings.Group_Setup_ForwardingGroupInfoDisabled : presentationData.strings.Group_Setup_ForwardingChannelInfoDisabled)))
}
} else if let peer = view.peers[view.peerId] as? TelegramGroup {
if case .revokeNames = mode {
let count = Int32(publicChannelsToRevoke?.count ?? 0)
entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(premiumLimits.maxPublicLinksCount)").string, count, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount, isPremiumDisabled))
if let publicChannelsToRevoke = publicChannelsToRevoke {
var index: Int32 = 0
for peer in publicChannelsToRevoke.sorted(by: { lhs, rhs in
var lhsDate: Int32 = 0
var rhsDate: Int32 = 0
if let lhs = lhs as? TelegramChannel {
lhsDate = lhs.creationDate
}
if let rhs = rhs as? TelegramChannel {
rhsDate = rhs.creationDate
}
return lhsDate > rhsDate
}) {
entries.append(.existingLinkPeerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: true, revealed: state.revealedRevokePeerId == peer.id), state.revokingPeerId == nil))
index += 1
}
}
} else {
switch mode {
case .revokeNames:
break
case .privateLink:
let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_InviteLink.uppercased()))
entries.append(.privateLink(presentationData.theme, invite, importers?.importers.prefix(3).compactMap { $0.peer.peer.flatMap(EnginePeer.init) } ?? [], importers?.count ?? 0, !isInitialSetup))
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.GroupInfo_InviteLink_Help))
switch mode {
case .initialSetup, .revokeNames:
break
case .generic, .privateLink:
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
}
case .generic, .initialSetup:
let selectedType: CurrentChannelType
if let current = state.selectedType {
selectedType = current
} else {
selectedType = .privateChannel
}
let currentAddressName: String
if let current = state.editingPublicLinkText {
currentAddressName = current
} else {
currentAddressName = ""
}
entries.append(.typeHeader(presentationData.theme, presentationData.strings.Group_Setup_TypeHeader.uppercased()))
entries.append(.typePublic(presentationData.theme, presentationData.strings.Channel_Setup_TypePublic, selectedType == .publicChannel))
entries.append(.typePrivate(presentationData.theme, presentationData.strings.Channel_Setup_TypePrivate, selectedType == .privateChannel))
switch selectedType {
case .publicChannel:
entries.append(.typeInfo(presentationData.theme, presentationData.strings.Group_Setup_TypePublicHelp))
case .privateChannel:
entries.append(.typeInfo(presentationData.theme, presentationData.strings.Group_Setup_TypePrivateHelp))
}
switch selectedType {
case .publicChannel:
let displayAvailability = publicChannelsToRevoke == nil || !(publicChannelsToRevoke!.isEmpty)
if displayAvailability {
if let publicChannelsToRevoke = publicChannelsToRevoke {
// entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(20)").string, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount))
entries.append(.publicLinkAvailability(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesInfo, false))
var index: Int32 = 0
for peer in publicChannelsToRevoke.sorted(by: { lhs, rhs in
var lhsDate: Int32 = 0
var rhsDate: Int32 = 0
if let lhs = lhs as? TelegramChannel {
lhsDate = lhs.creationDate
}
if let rhs = rhs as? TelegramChannel {
rhsDate = rhs.creationDate
}
return lhsDate > rhsDate
}) {
entries.append(.existingLinkPeerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: true, revealed: state.revealedRevokePeerId == peer.id), state.revokingPeerId == nil))
index += 1
} }
if let rhs = rhs as? TelegramChannel { } else {
rhsDate = rhs.creationDate entries.append(.publicLinkAvailability(presentationData.theme, presentationData.strings.Group_Username_CreatePublicLinkHelp, true))
}
return lhsDate > rhsDate
}) {
entries.append(.existingLinkPeerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: true, revealed: state.revealedRevokePeerId == peer.id), state.revokingPeerId == nil))
index += 1
} }
} else { } else {
entries.append(.publicLinkAvailability(presentationData.theme, presentationData.strings.Group_Username_CreatePublicLinkHelp, true)) entries.append(.editablePublicLink(presentationData.theme, presentationData.strings, "", currentAddressName))
} if let status = state.addressNameValidationStatus {
} else { let text: String
entries.append(.editablePublicLink(presentationData.theme, presentationData.strings, "", currentAddressName)) switch status {
if let status = state.addressNameValidationStatus { case let .invalidFormat(error):
let text: String switch error {
switch status { case .startsWithDigit:
case let .invalidFormat(error): text = presentationData.strings.Group_Username_InvalidStartsWithNumber
switch error { case .startsWithUnderscore:
case .startsWithDigit: text = presentationData.strings.Channel_Username_InvalidStartsWithUnderscore
text = presentationData.strings.Group_Username_InvalidStartsWithNumber case .endsWithUnderscore:
case .startsWithUnderscore: text = presentationData.strings.Channel_Username_InvalidEndsWithUnderscore
text = presentationData.strings.Channel_Username_InvalidStartsWithUnderscore case .tooShort:
case .endsWithUnderscore: text = presentationData.strings.Group_Username_InvalidTooShort
text = presentationData.strings.Channel_Username_InvalidEndsWithUnderscore case .invalidCharacters:
case .tooShort: text = presentationData.strings.Channel_Username_InvalidCharacters
text = presentationData.strings.Group_Username_InvalidTooShort }
case .invalidCharacters: case let .availability(availability):
switch availability {
case .available:
text = presentationData.strings.Channel_Username_UsernameIsAvailable(currentAddressName).string
case .invalid:
text = presentationData.strings.Channel_Username_InvalidCharacters text = presentationData.strings.Channel_Username_InvalidCharacters
case .taken:
text = presentationData.strings.Channel_Username_InvalidTaken
} }
case let .availability(availability): case .checking:
switch availability { text = presentationData.strings.Channel_Username_CheckingUsername
case .available:
text = presentationData.strings.Channel_Username_UsernameIsAvailable(currentAddressName).string
case .invalid:
text = presentationData.strings.Channel_Username_InvalidCharacters
case .taken:
text = presentationData.strings.Channel_Username_InvalidTaken
} }
case .checking:
text = presentationData.strings.Channel_Username_CheckingUsername entries.append(.publicLinkStatus(presentationData.theme, text, status))
} }
entries.append(.publicLinkStatus(presentationData.theme, text, status)) entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePublicLinkHelp))
} }
case .privateChannel:
entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePublicLinkHelp)) let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation
} entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_InviteLink.uppercased()))
case .privateChannel: entries.append(.privateLink(presentationData.theme, invite, importers?.importers.prefix(3).compactMap { $0.peer.peer.flatMap(EnginePeer.init) } ?? [], importers?.count ?? 0, !isInitialSetup))
let invite = (view.cachedData as? CachedGroupData)?.exportedInvitation entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp))
entries.append(.privateLinkHeader(presentationData.theme, presentationData.strings.InviteLink_InviteLink.uppercased())) switch mode {
entries.append(.privateLink(presentationData.theme, invite, importers?.importers.prefix(3).compactMap { $0.peer.peer.flatMap(EnginePeer.init) } ?? [], importers?.count ?? 0, mode != .initialSetup)) case .initialSetup, .revokeNames:
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp)) break
switch mode { case .generic, .privateLink:
case .initialSetup, .revokeNames: entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage))
break entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
case .generic, .privateLink: }
entries.append(.privateLinkManage(presentationData.theme, presentationData.strings.InviteLink_Manage)) }
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
}
} }
}
let forwardingEnabled: Bool
let forwardingEnabled: Bool if let enabled = state.forwardingEnabled {
if let enabled = state.forwardingEnabled { forwardingEnabled = enabled
forwardingEnabled = enabled
} else {
if peer.flags.contains(.copyProtectionEnabled) {
forwardingEnabled = false
} else { } else {
forwardingEnabled = true if peer.flags.contains(.copyProtectionEnabled) {
forwardingEnabled = false
} else {
forwardingEnabled = true
}
} }
entries.append(.forwardingHeader(presentationData.theme, presentationData.strings.Group_Setup_ForwardingGroupTitle.uppercased()))
entries.append(.forwardingDisabled(presentationData.theme, presentationData.strings.Group_Setup_ForwardingDisabled, !forwardingEnabled))
entries.append(.forwardingInfo(presentationData.theme, forwardingEnabled ? presentationData.strings.Group_Setup_ForwardingGroupInfo : presentationData.strings.Group_Setup_ForwardingGroupInfoDisabled))
} }
entries.append(.forwardingHeader(presentationData.theme, presentationData.strings.Group_Setup_ForwardingGroupTitle.uppercased()))
entries.append(.forwardingDisabled(presentationData.theme, presentationData.strings.Group_Setup_ForwardingDisabled, !forwardingEnabled))
entries.append(.forwardingInfo(presentationData.theme, forwardingEnabled ? presentationData.strings.Group_Setup_ForwardingGroupInfo : presentationData.strings.Group_Setup_ForwardingGroupInfoDisabled))
} }
return entries return entries
@ -1079,16 +1084,24 @@ public enum ChannelVisibilityControllerMode {
case initialSetup case initialSetup
case generic case generic
case privateLink case privateLink
case revokeNames case revokeNames([Peer])
} }
public func channelVisibilityController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, mode: ChannelVisibilityControllerMode, upgradedToSupergroup: @escaping (PeerId, @escaping () -> Void) -> Void, onDismissRemoveController: ViewController? = nil) -> ViewController { public func channelVisibilityController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, mode: ChannelVisibilityControllerMode, upgradedToSupergroup: @escaping (PeerId, @escaping () -> Void) -> Void, onDismissRemoveController: ViewController? = nil, revokedPeerAddressName: ((PeerId) -> Void)? = nil) -> ViewController {
let statePromise = ValuePromise(ChannelVisibilityControllerState(), ignoreRepeated: true) let statePromise = ValuePromise(ChannelVisibilityControllerState(), ignoreRepeated: true)
let stateValue = Atomic(value: ChannelVisibilityControllerState()) let stateValue = Atomic(value: ChannelVisibilityControllerState())
let updateState: ((ChannelVisibilityControllerState) -> ChannelVisibilityControllerState) -> Void = { f in let updateState: ((ChannelVisibilityControllerState) -> ChannelVisibilityControllerState) -> Void = { f in
statePromise.set(stateValue.modify { f($0) }) statePromise.set(stateValue.modify { f($0) })
} }
let adminedPublicChannels = Promise<[Peer]?>()
if case let .revokeNames(peers) = mode {
adminedPublicChannels.set(.single(peers))
} else {
adminedPublicChannels.set(context.engine.peers.adminedPublicChannels(scope: .all)
|> map(Optional.init))
}
let peersDisablingAddressNameAssignment = Promise<[Peer]?>() let peersDisablingAddressNameAssignment = Promise<[Peer]?>()
peersDisablingAddressNameAssignment.set(.single(nil) |> then(context.engine.peers.channelAddressNameAssignmentAvailability(peerId: peerId.namespace == Namespaces.Peer.CloudChannel ? peerId : nil) |> mapToSignal { result -> Signal<[Peer]?, NoError> in peersDisablingAddressNameAssignment.set(.single(nil) |> then(context.engine.peers.channelAddressNameAssignmentAvailability(peerId: peerId.namespace == Namespaces.Peer.CloudChannel ? peerId : nil) |> mapToSignal { result -> Signal<[Peer]?, NoError> in
if case .addressNameLimitReached = result { if case .addressNameLimitReached = result {
@ -1133,8 +1146,42 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
actionsDisposable.add(toggleRequestToJoinDisposable) actionsDisposable.add(toggleRequestToJoinDisposable)
let arguments = ChannelVisibilityControllerArguments(context: context, updateCurrentType: { type in let arguments = ChannelVisibilityControllerArguments(context: context, updateCurrentType: { type in
updateState { state in if type == .publicChannel {
return state.withUpdatedSelectedType(type) let _ = combineLatest(
queue: Queue.mainQueue(),
adminedPublicChannels.get() |> filter { $0 != nil } |> take(1),
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)),
context.engine.data.get(
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
)
).start(next: { peers, accountPeer, data in
let (limits, premiumLimits) = data
let isPremium = accountPeer?.isPremium ?? false
if let peers = peers {
let count = Int32(peers.count)
if count < limits.maxPublicLinksCount || (count < premiumLimits.maxPublicLinksCount && isPremium) {
updateState { state in
return state.withUpdatedSelectedType(type)
}
} else {
let controller = channelVisibilityController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, mode: .revokeNames(peers), upgradedToSupergroup: { _, _ in }, revokedPeerAddressName: { revokedPeerId in
let updatedPublicChannels = peers.filter { $0.id != revokedPeerId }
adminedPublicChannels.set(.single(updatedPublicChannels) |> then(
context.engine.peers.adminedPublicChannels(scope: .all) |> map(Optional.init))
)
})
controller.navigationPresentation = .modal
pushControllerImpl?(controller)
}
} else {
}
})
} else {
updateState { state in
return state.withUpdatedSelectedType(type)
}
} }
}, updatePublicLinkText: { currentText, text in }, updatePublicLinkText: { currentText, text in
if text.isEmpty { if text.isEmpty {
@ -1179,11 +1226,8 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
return state.withUpdatedRevokingPeerId(nil) return state.withUpdatedRevokingPeerId(nil)
} }
}, completed: { }, completed: {
peersDisablingAddressNameAssignment.set(.single([]) |> delay(0.2, queue: Queue.mainQueue()) |> afterNext { _ in revokedPeerAddressName?(peerId)
updateState { state in dismissImpl?()
return state.withUpdatedRevokingPeerId(nil)
}
})
})) }))
}, copyLink: { invite in }, copyLink: { invite in
UIPasteboard.general.string = invite.link UIPasteboard.general.string = invite.link
@ -1353,7 +1397,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
presentationData, presentationData,
statePromise.get() |> deliverOnMainQueue, statePromise.get() |> deliverOnMainQueue,
peerView, peerView,
peersDisablingAddressNameAssignment.get() |> deliverOnMainQueue, adminedPublicChannels.get(),
importersContext, importersContext,
importersState.get(), importersState.get(),
context.engine.data.get( context.engine.data.get(
@ -1373,7 +1417,10 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
var rightNavigationButton: ItemListNavigationButton? var rightNavigationButton: ItemListNavigationButton?
if case .revokeNames = mode { if case .revokeNames = mode {
footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: presentationData.strings.Premium_IncreaseLimit, colorful: true, action: {
let controller = PremiumIntroScreen(context: context, source: .publicLinks)
pushControllerImpl?(controller)
})
} else { } else {
if let peer = peer as? TelegramChannel { if let peer = peer as? TelegramChannel {
var doneEnabled = true var doneEnabled = true
@ -1400,7 +1447,14 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
} }
} }
rightNavigationButton = ItemListNavigationButton(content: .text(mode == .initialSetup ? presentationData.strings.Common_Next : presentationData.strings.Common_Done), style: state.updatingAddressName ? .activity : .bold, enabled: doneEnabled, action: { let isInitialSetup: Bool
if case .initialSetup = mode {
isInitialSetup = true
} else {
isInitialSetup = false
}
rightNavigationButton = ItemListNavigationButton(content: .text(isInitialSetup ? presentationData.strings.Common_Next : presentationData.strings.Common_Done), style: state.updatingAddressName ? .activity : .bold, enabled: doneEnabled, action: {
var updatedAddressNameValue: String? var updatedAddressNameValue: String?
updateState { state in updateState { state in
updatedAddressNameValue = updatedAddressName(mode: mode, state: state, peer: peer, cachedData: view.cachedData) updatedAddressNameValue = updatedAddressName(mode: mode, state: state, peer: peer, cachedData: view.cachedData)
@ -1618,14 +1672,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
if selectedType == .publicChannel, let hadNamesToRevoke = hadNamesToRevoke, !crossfade { if selectedType == .publicChannel, let hadNamesToRevoke = hadNamesToRevoke, !crossfade {
crossfade = hadNamesToRevoke != hasNamesToRevoke crossfade = hadNamesToRevoke != hasNamesToRevoke
} }
if hasNamesToRevoke && selectedType == .publicChannel {
footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: presentationData.strings.Premium_IncreaseLimit, colorful: true, action: {
let controller = PremiumIntroScreen(context: context, source: .publicLinks)
pushControllerImpl?(controller)
})
}
if let hadNamesToRevoke = hadNamesToRevoke { if let hadNamesToRevoke = hadNamesToRevoke {
animateChanges = hadNamesToRevoke != hasNamesToRevoke animateChanges = hadNamesToRevoke != hasNamesToRevoke
} }
@ -1645,7 +1692,8 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
title = presentationData.strings.Premium_LimitReached title = presentationData.strings.Premium_LimitReached
} }
let entries = channelVisibilityControllerEntries(presentationData: presentationData, mode: mode, view: view, publicChannelsToRevoke: publicChannelsToRevoke, importers: importers, state: state, limits: limits, premiumLimits: premiumLimits, isPremium: isPremium) let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
let entries = channelVisibilityControllerEntries(presentationData: presentationData, mode: mode, view: view, publicChannelsToRevoke: publicChannelsToRevoke, importers: importers, state: state, limits: limits, premiumLimits: premiumLimits, isPremium: isPremium, isPremiumDisabled: premiumConfiguration.isPremiumDisabled)
var focusItemTag: ItemListItemTag? var focusItemTag: ItemListItemTag?
if entries.count > 1, let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil { if entries.count > 1, let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil {

View File

@ -81,6 +81,9 @@ private let boldTextFont = Font.semibold(15.0)
class IncreaseLimitHeaderItemNode: ListViewItemNode { class IncreaseLimitHeaderItemNode: ListViewItemNode {
private var hostView: ComponentHostView<Empty>? private var hostView: ComponentHostView<Empty>?
private var params: (AnyComponent<Empty>, CGSize, ListViewItemNodeLayout, CGSize)?
private let titleNode: TextNode private let titleNode: TextNode
private let textNode: TextNode private let textNode: TextNode
@ -109,6 +112,19 @@ class IncreaseLimitHeaderItemNode: ListViewItemNode {
let hostView = ComponentHostView<Empty>() let hostView = ComponentHostView<Empty>()
self.hostView = hostView self.hostView = hostView
self.view.addSubview(hostView) self.view.addSubview(hostView)
if let (component, containerSize, layout, textSize) = self.params {
let size = hostView.update(
transition: .immediate,
component: component,
environment: {},
containerSize: containerSize
)
hostView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - size.width) / 2.0), y: -30.0), size: size)
let textSpacing: CGFloat = -6.0
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - textSize.width) / 2.0), y: size.height + textSpacing), size: textSize)
}
} }
func asyncLayout() -> (_ item: IncreaseLimitHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { func asyncLayout() -> (_ item: IncreaseLimitHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
@ -161,31 +177,36 @@ class IncreaseLimitHeaderItemNode: ListViewItemNode {
] ]
} }
let component = AnyComponent(PremiumLimitDisplayComponent(
inactiveColor: item.theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5),
activeColors: gradientColors,
inactiveTitle: item.strings.Premium_Free,
inactiveValue: item.count > item.limit ? "\(item.limit)" : "",
inactiveTitleColor: item.theme.list.itemPrimaryTextColor,
activeTitle: item.strings.Premium_Premium,
activeValue: item.count >= item.premiumCount ? "" : "\(item.premiumCount)",
activeTitleColor: .white,
badgeIconName: badgeIconName,
badgeText: "\(item.count)",
badgePosition: CGFloat(item.count) / CGFloat(item.premiumCount),
isPremiumDisabled: item.isPremiumDisabled
))
let containerSize = CGSize(width: layout.size.width - params.leftInset - params.rightInset, height: 200.0)
let _ = textApply()
if let hostView = strongSelf.hostView { if let hostView = strongSelf.hostView {
let size = hostView.update( let size = hostView.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(PremiumLimitDisplayComponent( component: component,
inactiveColor: item.theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5),
activeColors: gradientColors,
inactiveTitle: item.strings.Premium_Free,
inactiveValue: item.count > item.limit ? "\(item.limit)" : "",
inactiveTitleColor: item.theme.list.itemPrimaryTextColor,
activeTitle: item.strings.Premium_Premium,
activeValue: item.count >= item.premiumCount ? "" : "\(item.premiumCount)",
activeTitleColor: .white,
badgeIconName: badgeIconName,
badgeText: "\(item.count)",
badgePosition: CGFloat(item.count) / CGFloat(item.premiumCount),
isPremiumDisabled: item.isPremiumDisabled
)),
environment: {}, environment: {},
containerSize: CGSize(width: layout.size.width - params.leftInset - params.rightInset, height: 200.0) containerSize: containerSize
) )
hostView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - size.width) / 2.0), y: -30.0), size: size) hostView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - size.width) / 2.0), y: -30.0), size: size)
let _ = textApply()
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - textLayout.size.width) / 2.0), y: size.height + textSpacing), size: textLayout.size) strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - textLayout.size.width) / 2.0), y: size.height + textSpacing), size: textLayout.size)
} }
strongSelf.params = (component, containerSize, layout, textLayout.size)
} }
}) })
} }

View File

@ -807,7 +807,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
super.init() super.init()
self.disposable = (context.engine.data.get( self.disposable = (context.engine.data.subscribe(
TelegramEngine.EngineData.Item.Configuration.App(), TelegramEngine.EngineData.Item.Configuration.App(),
TelegramEngine.EngineData.Item.Configuration.PremiumPromo() TelegramEngine.EngineData.Item.Configuration.PremiumPromo()
) )
@ -841,6 +841,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
} }
}) })
let _ = updatePremiumPromoConfigurationOnce(account: context.account).start()
let stickersKey: PostboxViewKey = .orderedItemList(id: Namespaces.OrderedItemList.CloudPremiumStickers) let stickersKey: PostboxViewKey = .orderedItemList(id: Namespaces.OrderedItemList.CloudPremiumStickers)
self.stickersDisposable = (self.context.account.postbox.combinedView(keys: [stickersKey]) self.stickersDisposable = (self.context.account.postbox.combinedView(keys: [stickersKey])
|> deliverOnMainQueue).start(next: { [weak self] views in |> deliverOnMainQueue).start(next: { [weak self] views in
@ -1362,9 +1364,9 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
self.disposable = combineLatest( self.disposable = combineLatest(
queue: Queue.mainQueue(), queue: Queue.mainQueue(),
availableProducts, availableProducts,
context.account.postbox.peerView(id: context.account.peerId) context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|> map { view -> Bool in |> map { peer -> Bool in
return view.peers[view.peerId]?.isPremium ?? false return peer?.isPremium ?? false
}, },
otherPeerName otherPeerName
).start(next: { [weak self] products, isPremium, otherPeerName in ).start(next: { [weak self] products, isPremium, otherPeerName in
@ -1418,6 +1420,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
}, completed: { [weak self] in }, completed: { [weak self] in
if let strongSelf = self { if let strongSelf = self {
let _ = updatePremiumPromoConfigurationOnce(account: strongSelf.context.account).start()
strongSelf.isPremium = true strongSelf.isPremium = true
strongSelf.updated(transition: .easeInOut(duration: 0.25)) strongSelf.updated(transition: .easeInOut(duration: 0.25))
strongSelf.completion() strongSelf.completion()

View File

@ -49,7 +49,7 @@ final class ReactionsCarouselComponent: Component {
public func update(component: ReactionsCarouselComponent, availableSize: CGSize, environment: Environment<DemoPageEnvironment>, transition: Transition) -> CGSize { public func update(component: ReactionsCarouselComponent, availableSize: CGSize, environment: Environment<DemoPageEnvironment>, transition: Transition) -> CGSize {
let isDisplaying = environment[DemoPageEnvironment.self].isDisplaying let isDisplaying = environment[DemoPageEnvironment.self].isDisplaying
if self.node == nil { if self.node == nil && !component.reactions.isEmpty {
let node = ReactionCarouselNode( let node = ReactionCarouselNode(
context: component.context, context: component.context,
theme: component.theme, theme: component.theme,

View File

@ -45,7 +45,7 @@ final class StickersCarouselComponent: Component {
public func update(component: StickersCarouselComponent, availableSize: CGSize, environment: Environment<DemoPageEnvironment>, transition: Transition) -> CGSize { public func update(component: StickersCarouselComponent, availableSize: CGSize, environment: Environment<DemoPageEnvironment>, transition: Transition) -> CGSize {
let isDisplaying = environment[DemoPageEnvironment.self].isDisplaying let isDisplaying = environment[DemoPageEnvironment.self].isDisplaying
if self.node == nil { if self.node == nil && !component.stickers.isEmpty {
let node = StickersCarouselNode( let node = StickersCarouselNode(
context: component.context, context: component.context,
stickers: component.stickers stickers: component.stickers

View File

@ -6227,11 +6227,13 @@ public extension Api.functions.messages {
} }
} }
public extension Api.functions.payments { public extension Api.functions.payments {
static func assignAppStoreTransaction(transactionId: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) { static func assignAppStoreTransaction(flags: Int32, transactionId: String, receipt: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(1654235439) buffer.appendInt32(267129798)
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(transactionId, buffer: buffer, boxed: false) serializeString(transactionId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "payments.assignAppStoreTransaction", parameters: [("transactionId", String(describing: transactionId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in serializeBytes(receipt, buffer: buffer, boxed: false)
return (FunctionDescription(name: "payments.assignAppStoreTransaction", parameters: [("flags", String(describing: flags)), ("transactionId", String(describing: transactionId)), ("receipt", String(describing: receipt))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.Updates? var result: Api.Updates?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
@ -6381,21 +6383,6 @@ public extension Api.functions.payments {
}) })
} }
} }
public extension Api.functions.payments {
static func restoreAppStoreReceipt(receipt: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(-2132923705)
serializeBytes(receipt, buffer: buffer, boxed: false)
return (FunctionDescription(name: "payments.restoreAppStoreReceipt", parameters: [("receipt", String(describing: receipt))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
}
public extension Api.functions.payments { public extension Api.functions.payments {
static func restorePlayMarketReceipt(receipt: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) { static func restorePlayMarketReceipt(receipt: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer() let buffer = Buffer()

View File

@ -495,6 +495,8 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
let leftInset: CGFloat = 58.0 + params.leftInset let leftInset: CGFloat = 58.0 + params.leftInset
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 })
var titleIconsWidth: CGFloat = 0.0 var titleIconsWidth: CGFloat = 0.0
var currentCredibilityIconImage: UIImage? var currentCredibilityIconImage: UIImage?
var credibilityIconOffset: CGFloat = 0.0 var credibilityIconOffset: CGFloat = 0.0
@ -507,6 +509,9 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
} else if item.peer.isVerified { } else if item.peer.isVerified {
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme) currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
credibilityIconOffset = 3.0 credibilityIconOffset = 3.0
} else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled {
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme)
credibilityIconOffset = 3.0
} }
if let currentCredibilityIconImage = currentCredibilityIconImage { if let currentCredibilityIconImage = currentCredibilityIconImage {

View File

@ -829,6 +829,8 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
let verticalInset: CGFloat = 8.0 let verticalInset: CGFloat = 8.0
let verticalOffset: CGFloat = 0.0 let verticalOffset: CGFloat = 0.0
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 })
var titleIconsWidth: CGFloat = 0.0 var titleIconsWidth: CGFloat = 0.0
var currentCredibilityIconImage: UIImage? var currentCredibilityIconImage: UIImage?
var credibilityIconOffset: CGFloat = 0.0 var credibilityIconOffset: CGFloat = 0.0
@ -841,6 +843,9 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
} else if item.peer.isVerified { } else if item.peer.isVerified {
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme) currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
credibilityIconOffset = 3.0 credibilityIconOffset = 3.0
} else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled {
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme)
credibilityIconOffset = 3.0
} }
if let currentCredibilityIconImage = currentCredibilityIconImage { if let currentCredibilityIconImage = currentCredibilityIconImage {

View File

@ -8,26 +8,18 @@ public enum AssignAppStoreTransactionError {
case generic case generic
} }
func _internal_assignAppStoreTransaction(account: Account, transactionId: String) -> Signal<Never, AssignAppStoreTransactionError> { func _internal_assignAppStoreTransaction(account: Account, transactionId: String, receipt: Data, restore: Bool) -> Signal<Never, AssignAppStoreTransactionError> {
return account.network.request(Api.functions.payments.assignAppStoreTransaction(transactionId: transactionId)) var flags: Int32 = 0
if restore {
flags |= (1 << 0)
}
return account.network.request(Api.functions.payments.assignAppStoreTransaction(flags: flags, transactionId: transactionId, receipt: Buffer(data: receipt)))
|> mapError { _ -> AssignAppStoreTransactionError in |> mapError { _ -> AssignAppStoreTransactionError in
return .generic return .generic
} }
|> mapToSignal { updates -> Signal<Never, AssignAppStoreTransactionError> in |> mapToSignal { updates -> Signal<Never, AssignAppStoreTransactionError> in
account.stateManager.addUpdates(updates) account.stateManager.addUpdates(updates)
return .complete()
return account.postbox.peerView(id: account.peerId)
|> castError(AssignAppStoreTransactionError.self)
|> take(until: { view in
if let peer = view.peers[view.peerId], peer.isPremium {
return SignalTakeAction(passthrough: false, complete: true)
} else {
return SignalTakeAction(passthrough: false, complete: false)
}
})
|> mapToSignal { _ -> Signal<Never, AssignAppStoreTransactionError> in
return .never()
}
} }
} }

View File

@ -1,3 +1,4 @@
import Foundation
import SwiftSignalKit import SwiftSignalKit
import Postbox import Postbox
@ -37,8 +38,8 @@ public extension TelegramEngine {
return _internal_clearBotPaymentInfo(network: self.account.network, info: info) return _internal_clearBotPaymentInfo(network: self.account.network, info: info)
} }
public func assignAppStoreTransaction(transactionId: String) -> Signal<Never, AssignAppStoreTransactionError> { public func assignAppStoreTransaction(transactionId: String, receipt: Data, restore: Bool) -> Signal<Never, AssignAppStoreTransactionError> {
return _internal_assignAppStoreTransaction(account: self.account, transactionId: transactionId) return _internal_assignAppStoreTransaction(account: self.account, transactionId: transactionId, receipt: receipt, restore: restore)
} }
public func canPurchasePremium() -> Signal<Bool, NoError> { public func canPurchasePremium() -> Signal<Bool, NoError> {

View File

@ -2300,6 +2300,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let themeUpdated = self.presentationData?.theme !== presentationData.theme let themeUpdated = self.presentationData?.theme !== presentationData.theme
self.presentationData = presentationData self.presentationData = presentationData
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
let credibilityIcon: CredibilityIcon let credibilityIcon: CredibilityIcon
if let peer = peer { if let peer = peer {
if peer.isFake { if peer.isFake {
@ -2308,7 +2310,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
credibilityIcon = .scam credibilityIcon = .scam
} else if peer.isVerified { } else if peer.isVerified {
credibilityIcon = .verified credibilityIcon = .verified
} else if peer.isPremium { } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
credibilityIcon = .premium credibilityIcon = .premium
} else { } else {
credibilityIcon = .none credibilityIcon = .none