Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2023-12-16 15:58:35 +04:00
commit 6e3463bfa6
73 changed files with 3560 additions and 374 deletions

View File

@ -592,9 +592,9 @@ public final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
return nil return nil
} }
self.animation = animation self.animation = animation
let frameCount = Int(animation.frameCount) let frameCount = max(1, Int(animation.frameCount))
self.frameCount = frameCount self.frameCount = frameCount
self.frameRate = Int(animation.frameRate) self.frameRate = max(1, Int(animation.frameRate))
self.cache = cachePathPrefix.flatMap { cachePathPrefix in self.cache = cachePathPrefix.flatMap { cachePathPrefix in
AnimatedStickerDirectFrameSourceCache(queue: queue, pathPrefix: cachePathPrefix, width: width, height: height, frameCount: frameCount, fitzModifier: fitzModifier, useHardware: useMetalCache) AnimatedStickerDirectFrameSourceCache(queue: queue, pathPrefix: cachePathPrefix, width: width, height: height, frameCount: frameCount, fitzModifier: fitzModifier, useHardware: useMetalCache)

View File

@ -904,9 +904,16 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
return return
} }
var customTokenizeUrl: String?
if let value = nativeParams["public_token"] as? String, let url = URL(string: value), let host = url.host {
if url.scheme == "https" && (host == "smart-glocal.com" || host.hasSuffix(".smart-glocal.com")) {
customTokenizeUrl = value
}
}
var dismissImpl: (() -> Void)? var dismissImpl: (() -> Void)?
let canSave = paymentForm.canSaveCredentials || paymentForm.passwordMissing let canSave = paymentForm.canSaveCredentials || paymentForm.passwordMissing
let controller = BotCheckoutNativeCardEntryController(context: strongSelf.context, provider: .smartglobal(isTesting: paymentForm.invoice.isTest, publicToken: publicToken), completion: { method in let controller = BotCheckoutNativeCardEntryController(context: strongSelf.context, provider: .smartglobal(isTesting: paymentForm.invoice.isTest, publicToken: publicToken, customTokenizeUrl: customTokenizeUrl), completion: { method in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }

View File

@ -30,7 +30,7 @@ struct BotCheckoutNativeCardEntryAdditionalFields: OptionSet {
final class BotCheckoutNativeCardEntryController: ViewController { final class BotCheckoutNativeCardEntryController: ViewController {
enum Provider { enum Provider {
case stripe(additionalFields: BotCheckoutNativeCardEntryAdditionalFields, publishableKey: String) case stripe(additionalFields: BotCheckoutNativeCardEntryAdditionalFields, publishableKey: String)
case smartglobal(isTesting: Bool, publicToken: String) case smartglobal(isTesting: Bool, publicToken: String, customTokenizeUrl: String?)
} }
private var controllerNode: BotCheckoutNativeCardEntryControllerNode { private var controllerNode: BotCheckoutNativeCardEntryControllerNode {

View File

@ -310,9 +310,11 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode,
})) }))
self.updateDone() self.updateDone()
case let .smartglobal(isTesting, publicToken): case let .smartglobal(isTesting, publicToken, customTokenizeUrl):
let url: String let url: String
if isTesting { if let customTokenizeUrl {
url = customTokenizeUrl
} else if isTesting {
url = "https://tgb-playground.smart-glocal.com/cds/v1/tokenize/card" url = "https://tgb-playground.smart-glocal.com/cds/v1/tokenize/card"
} else { } else {
url = "https://tgb.smart-glocal.com/cds/v1/tokenize/card" url = "https://tgb.smart-glocal.com/cds/v1/tokenize/card"

View File

@ -2477,7 +2477,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased())
} else if peer.isFake { } else if peer.isFake {
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
} else if case let .user(user) = peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { } else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled {
currentCredibilityIconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) currentCredibilityIconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else if peer.isVerified { } else if peer.isVerified {
currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
@ -2493,7 +2493,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased())
} else if peer.isFake { } else if peer.isFake {
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
} else if case let .user(user) = peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { } else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled {
currentCredibilityIconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) currentCredibilityIconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else if peer.isVerified { } else if peer.isVerified {
currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)

View File

@ -609,7 +609,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
currentCredibilityIcon = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_ScamAccount.uppercased()) currentCredibilityIcon = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_ScamAccount.uppercased())
} else if item.peer.isFake { } else if item.peer.isFake {
currentCredibilityIcon = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased()) currentCredibilityIcon = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased())
} else if case let .user(user) = item.peer, let emojiStatus = user.emojiStatus { } else if let emojiStatus = item.peer.emojiStatus {
currentCredibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: UIColor(white: 0.0, alpha: 0.1), themeColor: presentationData.theme.list.itemAccentColor, loopMode: .count(2)) currentCredibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: UIColor(white: 0.0, alpha: 0.1), themeColor: presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else if item.peer.isVerified { } else if item.peer.isVerified {
currentCredibilityIcon = .verified(fillColor: presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) currentCredibilityIcon = .verified(fillColor: presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)

View File

@ -225,12 +225,25 @@ open class ViewControllerComponentContainer: ViewController {
public var wasDismissed: (() -> Void)? public var wasDismissed: (() -> Void)?
public init<C: Component>(context: AccountContext, component: C, navigationBarAppearance: NavigationBarAppearance, statusBarStyle: StatusBarStyle = .default, presentationMode: PresentationMode = .default, theme: Theme = .default) where C.EnvironmentType == ViewControllerComponentContainer.Environment { public init<C: Component>(
context: AccountContext,
component: C,
navigationBarAppearance: NavigationBarAppearance,
statusBarStyle: StatusBarStyle = .default,
presentationMode: PresentationMode = .default,
theme: Theme = .default,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil
) where C.EnvironmentType == ViewControllerComponentContainer.Environment {
self.context = context self.context = context
self.component = AnyComponent(component) self.component = AnyComponent(component)
self.theme = theme self.theme = theme
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData: PresentationData
if let updatedPresentationData {
presentationData = updatedPresentationData.initial
} else {
presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
}
let navigationBarPresentationData: NavigationBarPresentationData? let navigationBarPresentationData: NavigationBarPresentationData?
switch navigationBarAppearance { switch navigationBarAppearance {
@ -243,7 +256,7 @@ open class ViewControllerComponentContainer: ViewController {
} }
super.init(navigationBarPresentationData: navigationBarPresentationData) super.init(navigationBarPresentationData: navigationBarPresentationData)
self.presentationDataDisposable = (self.context.sharedContext.presentationData self.presentationDataDisposable = ((updatedPresentationData?.signal ?? self.context.sharedContext.presentationData)
|> deliverOnMainQueue).start(next: { [weak self] presentationData in |> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self { if let strongSelf = self {
var theme = presentationData.theme var theme = presentationData.theme
@ -266,6 +279,19 @@ open class ViewControllerComponentContainer: ViewController {
strongSelf.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style strongSelf.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
} }
let navigationBarPresentationData: NavigationBarPresentationData?
switch navigationBarAppearance {
case .none:
navigationBarPresentationData = nil
case .transparent:
navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, hideBackground: true, hideBadge: false, hideSeparator: true)
case .default:
navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData)
}
if let navigationBarPresentationData {
strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData)
}
if let layout = strongSelf.validLayout { if let layout = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, transition: .immediate) strongSelf.containerLayoutUpdated(layout, transition: .immediate)
} }

View File

@ -699,7 +699,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased())
} else if peer.isFake { } else if peer.isFake {
credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
} else if case let .user(user) = peer, let emojiStatus = user.emojiStatus { } else if let emojiStatus = peer.emojiStatus {
credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else if peer.isVerified { } else if peer.isVerified {
credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)

View File

@ -428,6 +428,7 @@ public func generateScaledImage(image: UIImage?, size: CGSize, opaque: Bool = tr
public func generateSingleColorImage(size: CGSize, color: UIColor, scale: CGFloat = 0.0) -> UIImage? { public func generateSingleColorImage(size: CGSize, color: UIColor, scale: CGFloat = 0.0) -> UIImage? {
return generateImage(size, contextGenerator: { size, context in return generateImage(size, contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(color.cgColor) context.setFillColor(color.cgColor)
context.fill(CGRect(origin: CGPoint(), size: size)) context.fill(CGRect(origin: CGPoint(), size: size))
}, scale: scale) }, scale: scale)

View File

@ -52,17 +52,19 @@ public enum ListViewItemNodeVisibility: Equatable {
case visible(CGFloat, CGRect) case visible(CGFloat, CGRect)
} }
public struct ListViewItemLayoutParams { public struct ListViewItemLayoutParams: Equatable {
public let width: CGFloat public let width: CGFloat
public let leftInset: CGFloat public let leftInset: CGFloat
public let rightInset: CGFloat public let rightInset: CGFloat
public let availableHeight: CGFloat public let availableHeight: CGFloat
public let isStandalone: Bool
public init(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, availableHeight: CGFloat) { public init(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, availableHeight: CGFloat, isStandalone: Bool = false) {
self.width = width self.width = width
self.leftInset = leftInset self.leftInset = leftInset
self.rightInset = rightInset self.rightInset = rightInset
self.availableHeight = availableHeight self.availableHeight = availableHeight
self.isStandalone = isStandalone
} }
} }

View File

@ -913,7 +913,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased())
} else if item.peer.isFake { } else if item.peer.isFake {
credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
} else if case let .user(user) = item.peer, let emojiStatus = user.emojiStatus { } else if let emojiStatus = item.peer.emojiStatus {
credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else if item.peer.isVerified { } else if item.peer.isVerified {
credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)

View File

@ -122,6 +122,10 @@ public func itemListNeighborsPlainInsets(_ neighbors: ItemListNeighbors) -> UIEd
} }
public func itemListNeighborsGroupedInsets(_ neighbors: ItemListNeighbors, _ params: ListViewItemLayoutParams) -> UIEdgeInsets { public func itemListNeighborsGroupedInsets(_ neighbors: ItemListNeighbors, _ params: ListViewItemLayoutParams) -> UIEdgeInsets {
if params.isStandalone {
return UIEdgeInsets()
}
let topInset: CGFloat let topInset: CGFloat
switch neighbors.top { switch neighbors.top {
case .none: case .none:

View File

@ -599,7 +599,12 @@ private final class VariableBlurView: UIVisualEffectView {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
override func updateTraitsIfNeeded() { override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
if #available(iOS 13.0, *) {
if self.traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
self.resetEffect()
}
}
} }
private func resetEffect() { private func resetEffect() {

View File

@ -589,6 +589,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
strings: strongSelf.presentationData.strings, strings: strongSelf.presentationData.strings,
deviceMetrics: DeviceMetrics.iPhone13, deviceMetrics: DeviceMetrics.iPhone13,
emojiContent: emojiContent, emojiContent: emojiContent,
color: nil,
backgroundColor: .clear, backgroundColor: .clear,
separatorColor: strongSelf.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5), separatorColor: strongSelf.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5),
hideTopPanel: hideTopPanel, hideTopPanel: hideTopPanel,
@ -1215,6 +1216,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
strings: self.presentationData.strings, strings: self.presentationData.strings,
deviceMetrics: DeviceMetrics.iPhone13, deviceMetrics: DeviceMetrics.iPhone13,
emojiContent: emojiContent, emojiContent: emojiContent,
color: nil,
backgroundColor: .clear, backgroundColor: .clear,
separatorColor: self.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5), separatorColor: self.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5),
hideTopPanel: hideTopPanel, hideTopPanel: hideTopPanel,

View File

@ -117,6 +117,8 @@ swift_library(
"//submodules/TelegramUI/Components/Settings/PeerNameColorScreen", "//submodules/TelegramUI/Components/Settings/PeerNameColorScreen",
"//submodules/ManagedAnimationNode:ManagedAnimationNode", "//submodules/ManagedAnimationNode:ManagedAnimationNode",
"//submodules/TelegramUI/Components/Settings/QuickReactionSetupController", "//submodules/TelegramUI/Components/Settings/QuickReactionSetupController",
"//submodules/TelegramUI/Components/Settings/ThemeCarouselItem",
"//submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -307,10 +307,10 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil))
let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil))
let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let timestamp = self.referenceTimestamp let timestamp = self.referenceTimestamp

View File

@ -19,6 +19,7 @@ import ContextUI
import UndoUI import UndoUI
import ItemListPeerActionItem import ItemListPeerActionItem
import AnimationUI import AnimationUI
import ThemeSettingsThemeItem
private final class ThemePickerControllerArguments { private final class ThemePickerControllerArguments {
let context: AccountContext let context: AccountContext

View File

@ -18,7 +18,7 @@ import AnimatedStickerNode
import TelegramAnimatedStickerNode import TelegramAnimatedStickerNode
import ShimmerEffect import ShimmerEffect
import StickerResources import StickerResources
import ThemeCarouselItem
private var cachedBorderImages: [String: UIImage] = [:] private var cachedBorderImages: [String: UIImage] = [:]
private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selected: Bool) -> UIImage? { private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selected: Bool) -> UIImage? {
@ -500,7 +500,7 @@ class ThemeGridThemeItemNode: ListViewItemNode, ItemListItemNode {
for theme in item.themes { for theme in item.themes {
let selected = item.currentTheme.index == theme.index let selected = item.currentTheme.index == theme.index
let iconItem = ThemeCarouselThemeIconItem(context: item.context, emojiFile: theme.emoticon.flatMap { item.animatedEmojiStickers[$0]?.first?.file }, themeReference: theme, nightMode: item.nightMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil, action: { theme in let iconItem = ThemeCarouselThemeIconItem(context: item.context, emojiFile: theme.emoticon.flatMap { item.animatedEmojiStickers[$0]?.first?.file }, themeReference: theme, nightMode: item.nightMode, channelMode: false, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil, action: { theme in
item.updatedTheme(theme) item.updatedTheme(theme)
}, contextAction: nil) }, contextAction: nil)

View File

@ -948,7 +948,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil))
let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))

View File

@ -14,6 +14,7 @@ import DeviceLocationManager
import Geocoding import Geocoding
import WallpaperResources import WallpaperResources
import Sunrise import Sunrise
import ThemeSettingsThemeItem
private enum TriggerMode { private enum TriggerMode {
case system case system

View File

@ -455,10 +455,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil))
let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil))
let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let peer7: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(6)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer7: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(6)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))

View File

@ -19,32 +19,7 @@ import ContextUI
import UndoUI import UndoUI
import PremiumUI import PremiumUI
import PeerNameColorScreen import PeerNameColorScreen
import ThemeCarouselItem
func themeDisplayName(strings: PresentationStrings, reference: PresentationThemeReference) -> String {
let name: String
switch reference {
case let .builtin(theme):
switch theme {
case .dayClassic:
name = strings.Appearance_ThemeCarouselClassic
case .day:
name = strings.Appearance_ThemeCarouselDay
case .night:
name = strings.Appearance_ThemeCarouselNewNight
case .nightAccent:
name = strings.Appearance_ThemeCarouselTintedNight
}
case let .local(theme):
name = theme.title
case let .cloud(theme):
if let emoticon = theme.theme.emoticon {
name = emoticon
} else {
name = theme.theme.title
}
}
return name
}
private final class ThemeSettingsControllerArguments { private final class ThemeSettingsControllerArguments {
let context: AccountContext let context: AccountContext

View File

@ -1262,7 +1262,7 @@ public extension Api {
return parser(reader) return parser(reader)
} }
else { else {
telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found") telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found")
return nil return nil
} }
} }

View File

@ -839,7 +839,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased())
} else if item.peer.isFake { } else if item.peer.isFake {
credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
} else if case let .user(user) = item.peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { } else if let emojiStatus = item.peer.emojiStatus, !premiumConfiguration.isPremiumDisabled {
credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else if item.peer.isVerified { } else if item.peer.isVerified {
credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)

View File

@ -61,7 +61,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: "", photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0) return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: "", photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0)
case let .chatForbidden(id, title): case let .chatForbidden(id, title):
return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: title, photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0) return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: title, photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0)
case let .channel(flags, flags2, id, accessHash, title, username, photo, date, restrictionReason, adminRights, bannedRights, defaultBannedRights, _, usernames, _, color, profileColor, _, _): case let .channel(flags, flags2, id, accessHash, title, username, photo, date, restrictionReason, adminRights, bannedRights, defaultBannedRights, _, usernames, _, color, profileColor, emojiStatus, boostLevel):
let isMin = (flags & (1 << 12)) != 0 let isMin = (flags & (1 << 12)) != 0
let participationStatus: TelegramChannelParticipationStatus let participationStatus: TelegramChannelParticipationStatus
@ -173,7 +173,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
} }
} }
return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId) return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), approximateBoostLevel: boostLevel)
case let .channelForbidden(flags, id, accessHash, title, untilDate): case let .channelForbidden(flags, id, accessHash, title, untilDate):
let info: TelegramChannelInfo let info: TelegramChannelInfo
if (flags & Int32(1 << 8)) != 0 { if (flags & Int32(1 << 8)) != 0 {
@ -182,7 +182,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
info = .broadcast(TelegramChannelBroadcastInfo(flags: [])) info = .broadcast(TelegramChannelBroadcastInfo(flags: []))
} }
return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil)
} }
} }
@ -190,7 +190,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
switch rhs { switch rhs {
case .chat, .chatEmpty, .chatForbidden, .channelForbidden: case .chat, .chatEmpty, .chatForbidden, .channelForbidden:
return parseTelegramGroupOrChannel(chat: rhs) return parseTelegramGroupOrChannel(chat: rhs)
case let .channel(flags, flags2, _, accessHash, title, username, photo, _, _, _, _, defaultBannedRights, _, usernames, _, color, profileColor, _, _): case let .channel(flags, flags2, _, accessHash, title, username, photo, _, _, _, _, defaultBannedRights, _, usernames, _, color, profileColor, emojiStatus, boostLevel):
let isMin = (flags & (1 << 12)) != 0 let isMin = (flags & (1 << 12)) != 0
if accessHash != nil && !isMin { if accessHash != nil && !isMin {
return parseTelegramGroupOrChannel(chat: rhs) return parseTelegramGroupOrChannel(chat: rhs)
@ -252,7 +252,9 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
} }
} }
return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId) let parsedEmojiStatus = emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:))
return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: parsedEmojiStatus, approximateBoostLevel: boostLevel)
} else { } else {
return parseTelegramGroupOrChannel(chat: rhs) return parseTelegramGroupOrChannel(chat: rhs)
} }
@ -306,6 +308,6 @@ func mergeChannel(lhs: TelegramChannel?, rhs: TelegramChannel) -> TelegramChanne
let storiesHidden: Bool? = rhs.storiesHidden ?? lhs.storiesHidden let storiesHidden: Bool? = rhs.storiesHidden ?? lhs.storiesHidden
return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights, usernames: rhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId, profileColor: rhs.profileColor, profileBackgroundEmojiId: rhs.profileBackgroundEmojiId) return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights, usernames: rhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId, profileColor: rhs.profileColor, profileBackgroundEmojiId: rhs.profileBackgroundEmojiId, emojiStatus: rhs.emojiStatus, approximateBoostLevel: rhs.approximateBoostLevel)
} }

View File

@ -1876,6 +1876,12 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po
if let value = contents.media { if let value = contents.media {
media.append(value) media.append(value)
} }
var attributes = contents.attributes
if !attributes.contains(where: { $0 is AutoremoveTimeoutMessageAttribute }), let messageAutoremoveTimeout = state.messageAutoremoveTimeout {
attributes.append(AutoclearTimeoutMessageAttribute(timeout: messageAutoremoveTimeout, countdownBeginTime: nil))
}
let message = Message( let message = Message(
stableId: 1, stableId: 1,
stableVersion: 0, stableVersion: 0,
@ -1892,7 +1898,7 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po
forwardInfo: nil, forwardInfo: nil,
author: nil, author: nil,
text: contents.text, text: contents.text,
attributes: contents.attributes, attributes: attributes,
media: media, media: media,
peers: SimpleDictionary(), peers: SimpleDictionary(),
associatedMessages: SimpleDictionary(), associatedMessages: SimpleDictionary(),
@ -1967,7 +1973,7 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po
} }
let entitiesAttribute = message.textEntitiesAttribute let entitiesAttribute = message.textEntitiesAttribute
let (tags, globalTags) = tagsForStoreMessage(incoming: false, attributes: contents.attributes, media: updatedMedia, textEntities: entitiesAttribute?.entities, isPinned: false) let (tags, globalTags) = tagsForStoreMessage(incoming: false, attributes: attributes, media: updatedMedia, textEntities: entitiesAttribute?.entities, isPinned: false)
let storedMessage = StoreMessage( let storedMessage = StoreMessage(
peerId: peerId, peerId: peerId,

View File

@ -30,6 +30,9 @@ public struct StickerPackCollectionInfoFlags: OptionSet {
if flags.contains(StickerPackCollectionInfoFlags.isEmoji) { if flags.contains(StickerPackCollectionInfoFlags.isEmoji) {
rawValue |= StickerPackCollectionInfoFlags.isEmoji.rawValue rawValue |= StickerPackCollectionInfoFlags.isEmoji.rawValue
} }
if flags.contains(StickerPackCollectionInfoFlags.isAvailableAsChannelStatus) {
rawValue |= StickerPackCollectionInfoFlags.isAvailableAsChannelStatus.rawValue
}
self.rawValue = rawValue self.rawValue = rawValue
} }
@ -39,6 +42,7 @@ public struct StickerPackCollectionInfoFlags: OptionSet {
public static let isAnimated = StickerPackCollectionInfoFlags(rawValue: 1 << 2) public static let isAnimated = StickerPackCollectionInfoFlags(rawValue: 1 << 2)
public static let isVideo = StickerPackCollectionInfoFlags(rawValue: 1 << 3) public static let isVideo = StickerPackCollectionInfoFlags(rawValue: 1 << 3)
public static let isEmoji = StickerPackCollectionInfoFlags(rawValue: 1 << 4) public static let isEmoji = StickerPackCollectionInfoFlags(rawValue: 1 << 4)
public static let isAvailableAsChannelStatus = StickerPackCollectionInfoFlags(rawValue: 1 << 5)
} }

View File

@ -171,6 +171,8 @@ public final class TelegramChannel: Peer, Equatable {
public let backgroundEmojiId: Int64? public let backgroundEmojiId: Int64?
public let profileColor: PeerNameColor? public let profileColor: PeerNameColor?
public let profileBackgroundEmojiId: Int64? public let profileBackgroundEmojiId: Int64?
public let emojiStatus: PeerEmojiStatus?
public let approximateBoostLevel: Int32?
public var indexName: PeerIndexNameRepresentation { public var indexName: PeerIndexNameRepresentation {
var addressNames = self.usernames.map { $0.username } var addressNames = self.usernames.map { $0.username }
@ -181,8 +183,19 @@ public final class TelegramChannel: Peer, Equatable {
} }
public var associatedMediaIds: [MediaId]? { public var associatedMediaIds: [MediaId]? {
if let backgroundEmojiId = self.backgroundEmojiId { if let emojiStatus = self.emojiStatus, let backgroundEmojiId = self.backgroundEmojiId {
return [MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId)] return [
MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId),
MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId)
]
} else if let emojiStatus = self.emojiStatus {
return [
MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId)
]
} else if let backgroundEmojiId = self.backgroundEmojiId {
return [
MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId)
]
} else { } else {
return nil return nil
} }
@ -191,7 +204,17 @@ public final class TelegramChannel: Peer, Equatable {
public let associatedPeerId: PeerId? = nil public let associatedPeerId: PeerId? = nil
public let notificationSettingsPeerId: PeerId? = nil public let notificationSettingsPeerId: PeerId? = nil
public var timeoutAttribute: UInt32? { return nil } public var timeoutAttribute: UInt32? {
if let emojiStatus = self.emojiStatus {
if let expirationDate = emojiStatus.expirationDate {
return UInt32(max(0, expirationDate))
} else {
return nil
}
} else {
return nil
}
}
public init( public init(
id: PeerId, id: PeerId,
@ -213,7 +236,9 @@ public final class TelegramChannel: Peer, Equatable {
nameColor: PeerNameColor?, nameColor: PeerNameColor?,
backgroundEmojiId: Int64?, backgroundEmojiId: Int64?,
profileColor: PeerNameColor?, profileColor: PeerNameColor?,
profileBackgroundEmojiId: Int64? profileBackgroundEmojiId: Int64?,
emojiStatus: PeerEmojiStatus?,
approximateBoostLevel: Int32?
) { ) {
self.id = id self.id = id
self.accessHash = accessHash self.accessHash = accessHash
@ -235,6 +260,8 @@ public final class TelegramChannel: Peer, Equatable {
self.backgroundEmojiId = backgroundEmojiId self.backgroundEmojiId = backgroundEmojiId
self.profileColor = profileColor self.profileColor = profileColor
self.profileBackgroundEmojiId = profileBackgroundEmojiId self.profileBackgroundEmojiId = profileBackgroundEmojiId
self.emojiStatus = emojiStatus
self.approximateBoostLevel = approximateBoostLevel
} }
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
@ -268,6 +295,8 @@ public final class TelegramChannel: Peer, Equatable {
self.backgroundEmojiId = decoder.decodeOptionalInt64ForKey("bgem") self.backgroundEmojiId = decoder.decodeOptionalInt64ForKey("bgem")
self.profileColor = decoder.decodeOptionalInt32ForKey("pclr").flatMap { PeerNameColor(rawValue: $0) } self.profileColor = decoder.decodeOptionalInt32ForKey("pclr").flatMap { PeerNameColor(rawValue: $0) }
self.profileBackgroundEmojiId = decoder.decodeOptionalInt64ForKey("pgem") self.profileBackgroundEmojiId = decoder.decodeOptionalInt64ForKey("pgem")
self.emojiStatus = decoder.decode(PeerEmojiStatus.self, forKey: "emjs")
self.approximateBoostLevel = decoder.decodeOptionalInt32ForKey("abl")
} }
public func encode(_ encoder: PostboxEncoder) { public func encode(_ encoder: PostboxEncoder) {
@ -347,6 +376,18 @@ public final class TelegramChannel: Peer, Equatable {
} else { } else {
encoder.encodeNil(forKey: "pgem") encoder.encodeNil(forKey: "pgem")
} }
if let emojiStatus = self.emojiStatus {
encoder.encode(emojiStatus, forKey: "emjs")
} else {
encoder.encodeNil(forKey: "emjs")
}
if let approximateBoostLevel = self.approximateBoostLevel {
encoder.encodeInt32(approximateBoostLevel, forKey: "abl")
} else {
encoder.encodeNil(forKey: "abl")
}
} }
public func isEqual(_ other: Peer) -> Bool { public func isEqual(_ other: Peer) -> Bool {
@ -399,43 +440,57 @@ public final class TelegramChannel: Peer, Equatable {
if lhs.profileBackgroundEmojiId != rhs.profileBackgroundEmojiId { if lhs.profileBackgroundEmojiId != rhs.profileBackgroundEmojiId {
return false return false
} }
if lhs.emojiStatus != rhs.emojiStatus {
return false
}
if lhs.approximateBoostLevel != rhs.approximateBoostLevel {
return false
}
return true return true
} }
public func withUpdatedAddressName(_ addressName: String?) -> TelegramChannel { public func withUpdatedAddressName(_ addressName: String?) -> TelegramChannel {
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: addressName, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: addressName, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel)
} }
public func withUpdatedAddressNames(_ addressNames: [TelegramPeerUsername]) -> TelegramChannel { public func withUpdatedAddressNames(_ addressNames: [TelegramPeerUsername]) -> TelegramChannel {
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: addressNames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: addressNames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel)
} }
public func withUpdatedDefaultBannedRights(_ defaultBannedRights: TelegramChatBannedRights?) -> TelegramChannel { public func withUpdatedDefaultBannedRights(_ defaultBannedRights: TelegramChatBannedRights?) -> TelegramChannel {
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel)
} }
public func withUpdatedFlags(_ flags: TelegramChannelFlags) -> TelegramChannel { public func withUpdatedFlags(_ flags: TelegramChannelFlags) -> TelegramChannel {
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel)
} }
public func withUpdatedStoriesHidden(_ storiesHidden: Bool?) -> TelegramChannel { public func withUpdatedStoriesHidden(_ storiesHidden: Bool?) -> TelegramChannel {
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel)
} }
public func withUpdatedNameColor(_ nameColor: PeerNameColor?) -> TelegramChannel { public func withUpdatedNameColor(_ nameColor: PeerNameColor?) -> TelegramChannel {
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel)
} }
public func withUpdatedBackgroundEmojiId(_ backgroundEmojiId: Int64?) -> TelegramChannel { public func withUpdatedBackgroundEmojiId(_ backgroundEmojiId: Int64?) -> TelegramChannel {
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel)
} }
public func withUpdatedProfileColor(_ profileColor: PeerNameColor?) -> TelegramChannel { public func withUpdatedProfileColor(_ profileColor: PeerNameColor?) -> TelegramChannel {
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel)
} }
public func withUpdatedProfileBackgroundEmojiId(_ profileBackgroundEmojiId: Int64?) -> TelegramChannel { public func withUpdatedProfileBackgroundEmojiId(_ profileBackgroundEmojiId: Int64?) -> TelegramChannel {
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId) return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel)
}
public func withUpdatedEmojiStatus(_ emojiStatus: PeerEmojiStatus?) -> TelegramChannel {
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: emojiStatus, approximateBoostLevel: self.approximateBoostLevel)
}
public func withUpdatedApproximateBoostLevel(_ approximateBoostLevel: Int32?) -> TelegramChannel {
return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: approximateBoostLevel)
} }
} }

View File

@ -315,7 +315,9 @@ private class AdMessagesHistoryContextImpl {
nameColor: invite.nameColor, nameColor: invite.nameColor,
backgroundEmojiId: nil, backgroundEmojiId: nil,
profileColor: nil, profileColor: nil,
profileBackgroundEmojiId: nil profileBackgroundEmojiId: nil,
emojiStatus: nil,
approximateBoostLevel: nil
) )
case let .webPage(webPage): case let .webPage(webPage):
author = TelegramChannel( author = TelegramChannel(
@ -338,7 +340,9 @@ private class AdMessagesHistoryContextImpl {
nameColor: .blue, nameColor: .blue,
backgroundEmojiId: nil, backgroundEmojiId: nil,
profileColor: nil, profileColor: nil,
profileBackgroundEmojiId: nil profileBackgroundEmojiId: nil,
emojiStatus: nil,
approximateBoostLevel: nil
) )
} }

View File

@ -1423,6 +1423,13 @@ public func _internal_pollPeerStories(postbox: Postbox, network: Network, accoun
guard let inputPeer = inputPeer else { guard let inputPeer = inputPeer else {
return .complete() return .complete()
} }
#if DEBUG
if "".isEmpty {
return .complete()
}
#endif
return network.request(Api.functions.stories.getPeerStories(peer: inputPeer)) return network.request(Api.functions.stories.getPeerStories(peer: inputPeer))
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<Api.stories.PeerStories?, NoError> in |> `catch` { _ -> Signal<Api.stories.PeerStories?, NoError> in

View File

@ -514,6 +514,10 @@ public extension EnginePeer {
return self._asPeer().profileColor return self._asPeer().profileColor
} }
var emojiStatus: PeerEmojiStatus? {
return self._asPeer().emojiStatus
}
var backgroundEmojiId: Int64? { var backgroundEmojiId: Int64? {
return self._asPeer().backgroundEmojiId return self._asPeer().backgroundEmojiId
} }

View File

@ -718,6 +718,10 @@ public extension TelegramEngine {
return _internal_updatePeerNameColorAndEmoji(account: self.account, peerId: peerId, nameColor: nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId) return _internal_updatePeerNameColorAndEmoji(account: self.account, peerId: peerId, nameColor: nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId)
} }
public func updatePeerEmojiStatus(peerId: EnginePeer.Id, fileId: Int64?, expirationDate: Int32?) -> Signal<Never, UpdatePeerEmojiStatusError> {
return _internal_updatePeerEmojiStatus(account: self.account, peerId: peerId, fileId: fileId, expirationDate: expirationDate)
}
public func getChatListPeers(filterPredicate: ChatListFilterPredicate) -> Signal<[EnginePeer], NoError> { public func getChatListPeers(filterPredicate: ChatListFilterPredicate) -> Signal<[EnginePeer], NoError> {
return self.account.postbox.transaction { transaction -> [EnginePeer] in return self.account.postbox.transaction { transaction -> [EnginePeer] in
return transaction.getChatListPeers(groupId: .root, filterPredicate: filterPredicate, additionalFilter: nil).map(EnginePeer.init) return transaction.getChatListPeers(groupId: .root, filterPredicate: filterPredicate, additionalFilter: nil).map(EnginePeer.init)

View File

@ -100,9 +100,9 @@ func _internal_updatePeerNameColorAndEmoji(account: Account, peerId: EnginePeer.
if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) { if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) {
let flagsReplies: Int32 = (1 << 0) | (1 << 2) let flagsReplies: Int32 = (1 << 0) | (1 << 2)
var flagsProfile: Int32 = (1 << 0) | (1 << 2) var flagsProfile: Int32 = (1 << 0) | (1 << 1)
if profileColor != nil { if profileColor != nil {
flagsProfile |= (1 << 1) flagsProfile |= (1 << 2)
} }
return combineLatest( return combineLatest(
@ -161,3 +161,45 @@ func _internal_updatePeerNameColorAndEmoji(account: Account, peerId: EnginePeer.
|> castError(UpdatePeerNameColorAndEmojiError.self) |> castError(UpdatePeerNameColorAndEmojiError.self)
|> switchToLatest |> switchToLatest
} }
public enum UpdatePeerEmojiStatusError {
case generic
}
func _internal_updatePeerEmojiStatus(account: Account, peerId: PeerId, fileId: Int64?, expirationDate: Int32?) -> Signal<Never, UpdatePeerEmojiStatusError> {
return account.postbox.transaction { transaction -> Api.InputChannel? in
let updatedStatus = fileId.flatMap {
PeerEmojiStatus(fileId: $0, expirationDate: expirationDate)
}
if let peer = transaction.getPeer(peerId) as? TelegramChannel {
updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedEmojiStatus(updatedStatus)], update: { _, updated in updated })
}
return transaction.getPeer(peerId).flatMap(apiInputChannel)
}
|> castError(UpdatePeerEmojiStatusError.self)
|> mapToSignal { inputChannel -> Signal<Never, UpdatePeerEmojiStatusError> in
guard let inputChannel = inputChannel else {
return .fail(.generic)
}
let mappedStatus: Api.EmojiStatus
if let fileId = fileId {
if let expirationDate = expirationDate {
mappedStatus = .emojiStatusUntil(documentId: fileId, until: expirationDate)
} else {
mappedStatus = .emojiStatus(documentId: fileId)
}
} else {
mappedStatus = .emojiStatusEmpty
}
return account.network.request(Api.functions.channels.updateEmojiStatus(channel: inputChannel, emojiStatus: mappedStatus))
|> ignoreValues
|> `catch` { error -> Signal<Never, UpdatePeerEmojiStatusError> in
if error.errorDescription == "CHAT_NOT_MODIFIED" {
return .complete()
} else {
return .fail(.generic)
}
}
}
}

View File

@ -49,6 +49,9 @@ extension StickerPackCollectionInfo {
if (flags & (1 << 7)) != 0 { if (flags & (1 << 7)) != 0 {
setFlags.insert(.isEmoji) setFlags.insert(.isEmoji)
} }
if (flags & (1 << 10)) != 0 {
setFlags.insert(.isAvailableAsChannelStatus)
}
var thumbnailRepresentation: TelegramMediaImageRepresentation? var thumbnailRepresentation: TelegramMediaImageRepresentation?
var immediateThumbnailData: Data? var immediateThumbnailData: Data?

View File

@ -255,6 +255,17 @@ public extension Peer {
return false return false
} }
var emojiStatus: PeerEmojiStatus? {
switch self {
case let user as TelegramUser:
return user.emojiStatus
case let channel as TelegramChannel:
return channel.emojiStatus
default:
return nil
}
}
var backgroundEmojiId: Int64? { var backgroundEmojiId: Int64? {
switch self { switch self {
case let user as TelegramUser: case let user as TelegramUser:

View File

@ -875,3 +875,29 @@ public extension PresentationData {
return PresentationData(strings: strings, theme: self.theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: self.chatWallpaper, chatFontSize: self.chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: self.listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, reduceMotion: self.reduceMotion, largeEmoji: self.largeEmoji) return PresentationData(strings: strings, theme: self.theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: self.chatWallpaper, chatFontSize: self.chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: self.listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, reduceMotion: self.reduceMotion, largeEmoji: self.largeEmoji)
} }
} }
public func themeDisplayName(strings: PresentationStrings, reference: PresentationThemeReference) -> String {
let name: String
switch reference {
case let .builtin(theme):
switch theme {
case .dayClassic:
name = strings.Appearance_ThemeCarouselClassic
case .day:
name = strings.Appearance_ThemeCarouselDay
case .night:
name = strings.Appearance_ThemeCarouselNewNight
case .nightAccent:
name = strings.Appearance_ThemeCarouselTintedNight
}
case let .local(theme):
name = theme.title
case let .cloud(theme):
if let emoticon = theme.theme.emoticon {
name = emoticon
} else {
name = theme.theme.title
}
}
return name
}

View File

@ -1953,11 +1953,14 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
authorNameColor = color authorNameColor = color
if case let .peer(peerId) = item.chatLocation, let authorPeerId = item.message.author?.id, authorPeerId == peerId { if case let .peer(peerId) = item.chatLocation, let authorPeerId = item.message.author?.id, authorPeerId == peerId {
if effectiveAuthor is TelegramChannel, let emojiStatus = effectiveAuthor.emojiStatus {
currentCredibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : item.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor, themeColor: color.withMultipliedAlpha(0.4), loopMode: .count(2))
}
} else if effectiveAuthor.isScam { } else if effectiveAuthor.isScam {
currentCredibilityIcon = .text(color: incoming ? item.presentationData.theme.theme.chat.message.incoming.scamColor : item.presentationData.theme.theme.chat.message.outgoing.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) currentCredibilityIcon = .text(color: incoming ? item.presentationData.theme.theme.chat.message.incoming.scamColor : item.presentationData.theme.theme.chat.message.outgoing.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased())
} else if effectiveAuthor.isFake { } else if effectiveAuthor.isFake {
currentCredibilityIcon = .text(color: incoming ? item.presentationData.theme.theme.chat.message.incoming.scamColor : item.presentationData.theme.theme.chat.message.outgoing.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) currentCredibilityIcon = .text(color: incoming ? item.presentationData.theme.theme.chat.message.incoming.scamColor : item.presentationData.theme.theme.chat.message.outgoing.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
} else if let user = effectiveAuthor as? TelegramUser, let emojiStatus = user.emojiStatus { } else if let emojiStatus = effectiveAuthor.emojiStatus {
currentCredibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : item.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor, themeColor: color.withMultipliedAlpha(0.4), loopMode: .count(2)) currentCredibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : item.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor, themeColor: color.withMultipliedAlpha(0.4), loopMode: .count(2))
} else if effectiveAuthor.isVerified { } else if effectiveAuthor.isVerified {
currentCredibilityIcon = .verified(fillColor: item.presentationData.theme.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) currentCredibilityIcon = .verified(fillColor: item.presentationData.theme.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)

View File

@ -69,7 +69,7 @@ private func filterOriginalMessageFlags(_ message: Message) -> Message {
private func filterMessageChannelPeer(_ peer: Peer) -> Peer { private func filterMessageChannelPeer(_ peer: Peer) -> Peer {
if let peer = peer as? TelegramChannel { if let peer = peer as? TelegramChannel {
return TelegramChannel(id: peer.id, accessHash: peer.accessHash, title: peer.title, username: peer.username, photo: peer.photo, creationDate: peer.creationDate, version: peer.version, participationStatus: peer.participationStatus, info: .group(TelegramChannelGroupInfo(flags: [])), flags: peer.flags, restrictionInfo: peer.restrictionInfo, adminRights: peer.adminRights, bannedRights: peer.bannedRights, defaultBannedRights: peer.defaultBannedRights, usernames: peer.usernames, storiesHidden: peer.storiesHidden, nameColor: peer.nameColor, backgroundEmojiId: peer.backgroundEmojiId, profileColor: peer.profileColor, profileBackgroundEmojiId: peer.profileBackgroundEmojiId) return TelegramChannel(id: peer.id, accessHash: peer.accessHash, title: peer.title, username: peer.username, photo: peer.photo, creationDate: peer.creationDate, version: peer.version, participationStatus: peer.participationStatus, info: .group(TelegramChannelGroupInfo(flags: [])), flags: peer.flags, restrictionInfo: peer.restrictionInfo, adminRights: peer.adminRights, bannedRights: peer.bannedRights, defaultBannedRights: peer.defaultBannedRights, usernames: peer.usernames, storiesHidden: peer.storiesHidden, nameColor: peer.nameColor, backgroundEmojiId: peer.backgroundEmojiId, profileColor: peer.profileColor, profileBackgroundEmojiId: peer.profileBackgroundEmojiId, emojiStatus: peer.emojiStatus, approximateBoostLevel: peer.approximateBoostLevel)
} }
return peer return peer
} }

View File

@ -334,7 +334,6 @@ private final class LineView: UIView {
let _ = previousParams let _ = previousParams
self.backgroundView.tintColor = primaryColor self.backgroundView.tintColor = primaryColor
if let secondaryColor { if let secondaryColor {
@ -406,6 +405,10 @@ private final class LineView: UIView {
self.dashBackgroundView = nil self.dashBackgroundView = nil
dashBackgroundView.removeFromSuperview() dashBackgroundView.removeFromSuperview()
} }
if let dashThirdBackgroundView = self.dashThirdBackgroundView {
self.dashThirdBackgroundView = nil
dashThirdBackgroundView.removeFromSuperview()
}
self.backgroundView.alpha = 1.0 self.backgroundView.alpha = 1.0
} }

View File

@ -257,7 +257,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
titleCredibilityIcon = .fake titleCredibilityIcon = .fake
} else if peer.isScam { } else if peer.isScam {
titleCredibilityIcon = .scam titleCredibilityIcon = .scam
} else if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { } else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled {
titleCredibilityIcon = .emojiStatus(emojiStatus) titleCredibilityIcon = .emojiStatus(emojiStatus)
} else if peer.isVerified { } else if peer.isVerified {
titleCredibilityIcon = .verified titleCredibilityIcon = .verified

View File

@ -0,0 +1,18 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "DynamicCornerRadiusView",
module_name = "DynamicCornerRadiusView",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/ComponentFlow"
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,90 @@
import Foundation
import UIKit
import ComponentFlow
private func generatePath(size: CGSize, corners: DynamicCornerRadiusView.Corners) -> CGPath {
let path = CGMutablePath()
var corners = corners
corners.minXMinY = max(0.01, corners.minXMinY)
corners.maxXMinY = max(0.01, corners.maxXMinY)
corners.minXMaxY = max(0.01, corners.minXMaxY)
corners.maxXMaxY = max(0.01, corners.maxXMaxY)
path.move(to: CGPoint(x: 0.0, y: corners.minXMinY))
path.addArc(tangent1End: CGPoint(x: 0.0, y: 0.0), tangent2End: CGPoint(x: corners.minXMinY, y: 0.0), radius: corners.minXMinY)
path.addLine(to: CGPoint(x: size.width - corners.maxXMinY, y: 0.0))
path.addArc(tangent1End: CGPoint(x: size.width, y: 0.0), tangent2End: CGPoint(x: size.width, y: corners.maxXMinY), radius: corners.maxXMinY)
path.addLine(to: CGPoint(x: size.width, y: size.height - corners.maxXMaxY))
path.addArc(tangent1End: CGPoint(x: size.width, y: size.height), tangent2End: CGPoint(x: size.width - corners.maxXMaxY, y: size.height), radius: corners.maxXMaxY)
path.addLine(to: CGPoint(x: corners.minXMaxY, y: size.height))
path.addArc(tangent1End: CGPoint(x: 0.0, y: size.height), tangent2End: CGPoint(x: 0.0, y: size.height - corners.minXMaxY), radius: corners.minXMaxY)
path.closeSubpath()
return path
}
open class DynamicCornerRadiusView: UIView {
override public static var layerClass: AnyClass {
return CAShapeLayer.self
}
public struct Corners: Equatable {
public var minXMinY: CGFloat
public var maxXMinY: CGFloat
public var minXMaxY: CGFloat
public var maxXMaxY: CGFloat
public init(minXMinY: CGFloat, maxXMinY: CGFloat, minXMaxY: CGFloat, maxXMaxY: CGFloat) {
self.minXMinY = minXMinY
self.maxXMinY = maxXMinY
self.minXMaxY = minXMaxY
self.maxXMaxY = maxXMaxY
}
}
private struct Params: Equatable {
var size: CGSize
var corners: Corners
init(size: CGSize, corners: Corners) {
self.size = size
self.corners = corners
}
}
private var params: Params?
override public init(frame: CGRect) {
super.init(frame: frame)
if let shapeLayer = self.layer as? CAShapeLayer {
shapeLayer.strokeColor = nil
}
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func update(size: CGSize, corners: Corners, transition: Transition) {
let params = Params(size: size, corners: corners)
if self.params == params {
return
}
self.params = params
self.update(params: params, transition: transition)
}
public func updateColor(color: UIColor, transition: Transition) {
if let shapeLayer = self.layer as? CAShapeLayer {
transition.setShapeLayerFillColor(layer: shapeLayer, color: color)
}
}
private func update(params: Params, transition: Transition) {
if let shapeLayer = self.layer as? CAShapeLayer {
transition.setShapeLayerPath(layer: shapeLayer, path: generatePath(size: params.size, corners: params.corners))
}
}
}

View File

@ -64,6 +64,7 @@ public final class EmojiStatusSelectionComponent: Component {
public let emojiContent: EmojiPagerContentComponent public let emojiContent: EmojiPagerContentComponent
public let backgroundColor: UIColor public let backgroundColor: UIColor
public let separatorColor: UIColor public let separatorColor: UIColor
public let color: UIColor?
public let hideTopPanel: Bool public let hideTopPanel: Bool
public let disableTopPanel: Bool public let disableTopPanel: Bool
public let hideTopPanelUpdated: (Bool, Transition) -> Void public let hideTopPanelUpdated: (Bool, Transition) -> Void
@ -73,6 +74,7 @@ public final class EmojiStatusSelectionComponent: Component {
strings: PresentationStrings, strings: PresentationStrings,
deviceMetrics: DeviceMetrics, deviceMetrics: DeviceMetrics,
emojiContent: EmojiPagerContentComponent, emojiContent: EmojiPagerContentComponent,
color: UIColor?,
backgroundColor: UIColor, backgroundColor: UIColor,
separatorColor: UIColor, separatorColor: UIColor,
hideTopPanel: Bool, hideTopPanel: Bool,
@ -83,6 +85,7 @@ public final class EmojiStatusSelectionComponent: Component {
self.strings = strings self.strings = strings
self.deviceMetrics = deviceMetrics self.deviceMetrics = deviceMetrics
self.emojiContent = emojiContent self.emojiContent = emojiContent
self.color = color
self.backgroundColor = backgroundColor self.backgroundColor = backgroundColor
self.separatorColor = separatorColor self.separatorColor = separatorColor
self.hideTopPanel = hideTopPanel self.hideTopPanel = hideTopPanel
@ -103,6 +106,9 @@ public final class EmojiStatusSelectionComponent: Component {
if lhs.emojiContent != rhs.emojiContent { if lhs.emojiContent != rhs.emojiContent {
return false return false
} }
if lhs.color != rhs.color {
return false
}
if lhs.backgroundColor != rhs.backgroundColor { if lhs.backgroundColor != rhs.backgroundColor {
return false return false
} }
@ -169,7 +175,7 @@ public final class EmojiStatusSelectionComponent: Component {
isContentInFocus: true, isContentInFocus: true,
containerInsets: UIEdgeInsets(top: topPanelHeight - 34.0, left: 0.0, bottom: 0.0, right: 0.0), containerInsets: UIEdgeInsets(top: topPanelHeight - 34.0, left: 0.0, bottom: 0.0, right: 0.0),
topPanelInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0), topPanelInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0),
emojiContent: component.emojiContent, emojiContent: component.emojiContent.withCustomTintColor(component.color),
stickerContent: nil, stickerContent: nil,
maskContent: nil, maskContent: nil,
gifContent: nil, gifContent: nil,
@ -200,7 +206,8 @@ public final class EmojiStatusSelectionComponent: Component {
isExpanded: false, isExpanded: false,
clipContentToTopPanel: false, clipContentToTopPanel: false,
useExternalSearchContainer: false, useExternalSearchContainer: false,
hidePanels: component.disableTopPanel hidePanels: component.disableTopPanel,
customTintColor: component.color
)), )),
environment: {}, environment: {},
containerSize: availableSize containerSize: availableSize
@ -277,6 +284,7 @@ public final class EmojiStatusSelectionController: ViewController {
private var validLayout: ContainerViewLayout? private var validLayout: ContainerViewLayout?
private let currentSelection: Int64? private let currentSelection: Int64?
private let color: UIColor?
private var emojiContentDisposable: Disposable? private var emojiContentDisposable: Disposable?
private var emojiContent: EmojiPagerContentComponent? private var emojiContent: EmojiPagerContentComponent?
@ -312,10 +320,11 @@ public final class EmojiStatusSelectionController: ViewController {
private var isReactionSearchActive: Bool = false private var isReactionSearchActive: Bool = false
init(controller: EmojiStatusSelectionController, context: AccountContext, sourceView: UIView?, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?) { init(controller: EmojiStatusSelectionController, context: AccountContext, sourceView: UIView?, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?, color: UIColor?) {
self.controller = controller self.controller = controller
self.context = context self.context = context
self.currentSelection = currentSelection self.currentSelection = currentSelection
self.color = color
if let sourceView = sourceView { if let sourceView = sourceView {
self.globalSourceRect = sourceView.convert(sourceView.bounds, to: nil) self.globalSourceRect = sourceView.convert(sourceView.bounds, to: nil)
@ -826,8 +835,16 @@ public final class EmojiStatusSelectionController: ViewController {
renderer: animationRenderer, renderer: animationRenderer,
placeholderColor: UIColor(white: 0.0, alpha: 0.0), placeholderColor: UIColor(white: 0.0, alpha: 0.0),
pointSize: CGSize(width: 32.0, height: 32.0), pointSize: CGSize(width: 32.0, height: 32.0),
dynamicColor: self.presentationData.theme.list.itemAccentColor dynamicColor: self.color ?? self.presentationData.theme.list.itemAccentColor
) )
if let color = self.color {
switch item.tintMode {
case .none:
break
default:
baseItemLayer.contentTintColor = color
}
} else {
switch item.tintMode { switch item.tintMode {
case let .custom(color): case let .custom(color):
baseItemLayer.contentTintColor = color baseItemLayer.contentTintColor = color
@ -838,6 +855,7 @@ public final class EmojiStatusSelectionController: ViewController {
case .none: case .none:
break break
} }
}
if let sublayers = animationLayer.sublayers { if let sublayers = animationLayer.sublayers {
for sublayer in sublayers { for sublayer in sublayers {
@ -988,6 +1006,7 @@ public final class EmojiStatusSelectionController: ViewController {
strings: self.presentationData.strings, strings: self.presentationData.strings,
deviceMetrics: layout.deviceMetrics, deviceMetrics: layout.deviceMetrics,
emojiContent: emojiContent, emojiContent: emojiContent,
color: self.color,
backgroundColor: listBackgroundColor, backgroundColor: listBackgroundColor,
separatorColor: separatorColor, separatorColor: separatorColor,
hideTopPanel: self.isReactionSearchActive, hideTopPanel: self.isReactionSearchActive,
@ -1024,11 +1043,18 @@ public final class EmojiStatusSelectionController: ViewController {
sourceOrigin = CGPoint(x: layout.size.width / 2.0, y: floor(layout.size.height / 2.0 - componentSize.height)) sourceOrigin = CGPoint(x: layout.size.width / 2.0, y: floor(layout.size.height / 2.0 - componentSize.height))
} }
var componentFrame: CGRect
let pointsToTop: Bool
if sourceOrigin.y + 5.0 + componentSize.height > layout.size.height - layout.insets(options: []).bottom { if sourceOrigin.y + 5.0 + componentSize.height > layout.size.height - layout.insets(options: []).bottom {
sourceOrigin.y = layout.size.height - layout.insets(options: []).bottom - componentSize.height - 5.0 componentFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - componentSize.width) / 2.0), y: sourceOrigin.y - 25.0 - componentSize.height), size: componentSize)
pointsToTop = false
} else {
componentFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - componentSize.width) / 2.0), y: sourceOrigin.y + 5.0), size: componentSize)
pointsToTop = true
}
if componentFrame.minY < layout.insets(options: [.statusBar]).top {
componentFrame.origin.y = layout.insets(options: [.statusBar]).top
} }
let componentFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - componentSize.width) / 2.0), y: sourceOrigin.y + 5.0), size: componentSize)
if self.componentShadowLayer.bounds.size != componentFrame.size { if self.componentShadowLayer.bounds.size != componentFrame.size {
let componentShadowPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: componentFrame.size), cornerRadius: 24.0).cgPath let componentShadowPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: componentFrame.size), cornerRadius: 24.0).cgPath
@ -1038,7 +1064,12 @@ public final class EmojiStatusSelectionController: ViewController {
let cloudOffset0: CGFloat = 30.0 let cloudOffset0: CGFloat = 30.0
let cloudSize0: CGFloat = 16.0 let cloudSize0: CGFloat = 16.0
var cloudFrame0 = CGRect(origin: CGPoint(x: floor(sourceOrigin.x + cloudOffset0 - cloudSize0 / 2.0), y: componentFrame.minY - cloudSize0 / 2.0), size: CGSize(width: cloudSize0, height: cloudSize0)) var cloudFrame0: CGRect
if pointsToTop {
cloudFrame0 = CGRect(origin: CGPoint(x: floor(sourceOrigin.x + cloudOffset0 - cloudSize0 / 2.0), y: componentFrame.minY - cloudSize0 / 2.0), size: CGSize(width: cloudSize0, height: cloudSize0))
} else {
cloudFrame0 = CGRect(origin: CGPoint(x: floor(sourceOrigin.x + cloudOffset0 - cloudSize0 / 2.0), y: componentFrame.maxY - cloudSize0 / 2.0), size: CGSize(width: cloudSize0, height: cloudSize0))
}
var invertX = false var invertX = false
if cloudFrame0.maxX >= layout.size.width - layout.safeInsets.right - 32.0 { if cloudFrame0.maxX >= layout.size.width - layout.safeInsets.right - 32.0 {
cloudFrame0.origin.x = floor(sourceOrigin.x - cloudSize0 - cloudOffset0 + cloudSize0 / 2.0) cloudFrame0.origin.x = floor(sourceOrigin.x - cloudSize0 - cloudOffset0 + cloudSize0 / 2.0)
@ -1055,7 +1086,12 @@ public final class EmojiStatusSelectionController: ViewController {
let cloudOffset1 = CGPoint(x: -9.0, y: -14.0) let cloudOffset1 = CGPoint(x: -9.0, y: -14.0)
let cloudSize1: CGFloat = 8.0 let cloudSize1: CGFloat = 8.0
var cloudFrame1 = CGRect(origin: CGPoint(x: floor(cloudFrame0.midX + cloudOffset1.x - cloudSize1 / 2.0), y: floor(cloudFrame0.midY + cloudOffset1.y - cloudSize1 / 2.0)), size: CGSize(width: cloudSize1, height: cloudSize1)) var cloudFrame1: CGRect
if pointsToTop {
cloudFrame1 = CGRect(origin: CGPoint(x: floor(cloudFrame0.midX + cloudOffset1.x - cloudSize1 / 2.0), y: floor(cloudFrame0.midY + cloudOffset1.y - cloudSize1 / 2.0)), size: CGSize(width: cloudSize1, height: cloudSize1))
} else {
cloudFrame1 = CGRect(origin: CGPoint(x: floor(cloudFrame0.midX + cloudOffset1.x - cloudSize1 / 2.0), y: floor(cloudFrame0.midY - cloudOffset1.y - cloudSize1 / 2.0)), size: CGSize(width: cloudSize1, height: cloudSize1))
}
if invertX { if invertX {
cloudFrame1.origin.x = floor(cloudFrame0.midX - cloudSize1 - cloudOffset1.x + cloudSize1 / 2.0) cloudFrame1.origin.x = floor(cloudFrame0.midX - cloudSize1 - cloudOffset1.x + cloudSize1 / 2.0)
} }
@ -1076,7 +1112,7 @@ public final class EmojiStatusSelectionController: ViewController {
let contentDuration: Double = 0.3 let contentDuration: Double = 0.3
let contentDelay: Double = 0.14 let contentDelay: Double = 0.14
let initialContentFrame = CGRect(origin: CGPoint(x: cloudFrame0.midX - 24.0, y: componentFrame.minY), size: CGSize(width: 24.0 * 2.0, height: 24.0 * 2.0)) let initialContentFrame = CGRect(origin: CGPoint(x: cloudFrame0.midX - 24.0, y: pointsToTop ? componentFrame.minY : (componentFrame.maxY - 24.0 * 2.0)), size: CGSize(width: 24.0 * 2.0, height: 24.0 * 2.0))
if let emojiView = self.componentHost.findTaggedView(tag: EmojiPagerContentComponent.Tag(id: AnyHashable("emoji"))) as? EmojiPagerContentComponent.View { if let emojiView = self.componentHost.findTaggedView(tag: EmojiPagerContentComponent.Tag(id: AnyHashable("emoji"))) as? EmojiPagerContentComponent.View {
emojiView.animateIn(fromLocation: self.view.convert(initialContentFrame.center, to: emojiView)) emojiView.animateIn(fromLocation: self.view.convert(initialContentFrame.center, to: emojiView))
@ -1084,7 +1120,7 @@ public final class EmojiStatusSelectionController: ViewController {
componentView.layer.animatePosition(from: initialContentFrame.center, to: componentFrame.center, duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring) componentView.layer.animatePosition(from: initialContentFrame.center, to: componentFrame.center, duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring)
componentView.layer.animateBounds(from: CGRect(origin: CGPoint(x: -(componentFrame.minX - initialContentFrame.minX), y: -(componentFrame.minY - initialContentFrame.minY)), size: initialContentFrame.size), to: CGRect(origin: CGPoint(), size: componentFrame.size), duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring) componentView.layer.animateBounds(from: CGRect(origin: CGPoint(x: -(componentFrame.minX - initialContentFrame.minX), y: -(componentFrame.minY - initialContentFrame.minY)), size: initialContentFrame.size), to: CGRect(origin: CGPoint(), size: componentFrame.size), duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring)
self.componentShadowLayer.animateFrame(from: CGRect(origin: CGPoint(x: cloudFrame0.midX - 24.0, y: componentFrame.minY), size: CGSize(width: 24.0 * 2.0, height: 24.0 * 2.0)), to: componentView.frame, duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring) self.componentShadowLayer.animateFrame(from: CGRect(origin: CGPoint(x: cloudFrame0.midX - 24.0, y: pointsToTop ? componentFrame.minY : (componentFrame.maxY - 24.0 * 2.0)), size: CGSize(width: 24.0 * 2.0, height: 24.0 * 2.0)), to: componentView.frame, duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring)
componentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.04, delay: contentDelay) componentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.04, delay: contentDelay)
self.componentShadowLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.04, delay: contentDelay) self.componentShadowLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.04, delay: contentDelay)
@ -1264,7 +1300,17 @@ public final class EmojiStatusSelectionController: ViewController {
return return
} }
if let _ = item, let destinationView = controller.destinationItemView() { var animateOutToView = false
switch controller.mode {
case .backgroundSelection, .customStatusSelection, .quickReactionSelection:
if let itemFile = item?.itemFile, itemFile.fileId.id != 0 {
animateOutToView = true
}
case .statusSelection:
animateOutToView = true
}
if animateOutToView, item != nil, let destinationView = controller.destinationItemView() {
if let snapshotView = destinationView.snapshotView(afterScreenUpdates: false) { if let snapshotView = destinationView.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = destinationView.frame snapshotView.frame = destinationView.frame
destinationView.superview?.insertSubview(snapshotView, belowSubview: destinationView) destinationView.superview?.insertSubview(snapshotView, belowSubview: destinationView)
@ -1279,6 +1325,10 @@ public final class EmojiStatusSelectionController: ViewController {
case .statusSelection: case .statusSelection:
let _ = (self.context.engine.accountData.setEmojiStatus(file: item?.itemFile, expirationDate: nil) let _ = (self.context.engine.accountData.setEmojiStatus(file: item?.itemFile, expirationDate: nil)
|> deliverOnMainQueue).start() |> deliverOnMainQueue).start()
case let .backgroundSelection(completion):
completion(item?.itemFile)
case let .customStatusSelection(completion):
completion(item?.itemFile, nil)
case let .quickReactionSelection(completion): case let .quickReactionSelection(completion):
if let item = item, let itemFile = item.itemFile { if let item = item, let itemFile = item.itemFile {
var selectedReaction: MessageReaction.Reaction? var selectedReaction: MessageReaction.Reaction?
@ -1304,7 +1354,7 @@ public final class EmojiStatusSelectionController: ViewController {
completion() completion()
} }
if let item = item, let destinationView = controller.destinationItemView() { if animateOutToView, let item = item, let destinationView = controller.destinationItemView() {
var emojiString: String? var emojiString: String?
if let itemFile = item.itemFile { if let itemFile = item.itemFile {
attributeLoop: for attribute in itemFile.attributes { attributeLoop: for attribute in itemFile.attributes {
@ -1364,6 +1414,8 @@ public final class EmojiStatusSelectionController: ViewController {
public enum Mode { public enum Mode {
case statusSelection case statusSelection
case backgroundSelection(completion: (TelegramMediaFile?) -> Void)
case customStatusSelection(completion: (TelegramMediaFile?, Int32?) -> Void)
case quickReactionSelection(completion: () -> Void) case quickReactionSelection(completion: () -> Void)
} }
@ -1371,6 +1423,7 @@ public final class EmojiStatusSelectionController: ViewController {
private weak var sourceView: UIView? private weak var sourceView: UIView?
private let emojiContent: Signal<EmojiPagerContentComponent, NoError> private let emojiContent: Signal<EmojiPagerContentComponent, NoError>
private let currentSelection: Int64? private let currentSelection: Int64?
private let color: UIColor?
private let mode: Mode private let mode: Mode
private let destinationItemView: () -> UIView? private let destinationItemView: () -> UIView?
@ -1383,12 +1436,13 @@ public final class EmojiStatusSelectionController: ViewController {
return true return true
} }
public init(context: AccountContext, mode: Mode, sourceView: UIView, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?, destinationItemView: @escaping () -> UIView?) { public init(context: AccountContext, mode: Mode, sourceView: UIView, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?, color: UIColor? = nil, destinationItemView: @escaping () -> UIView?) {
self.context = context self.context = context
self.mode = mode self.mode = mode
self.sourceView = sourceView self.sourceView = sourceView
self.emojiContent = emojiContent self.emojiContent = emojiContent
self.currentSelection = currentSelection self.currentSelection = currentSelection
self.color = color
self.destinationItemView = destinationItemView self.destinationItemView = destinationItemView
super.init(navigationBarPresentationData: nil) super.init(navigationBarPresentationData: nil)
@ -1418,7 +1472,7 @@ public final class EmojiStatusSelectionController: ViewController {
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {
self.displayNode = Node(controller: self, context: self.context, sourceView: self.sourceView, emojiContent: self.emojiContent, currentSelection: self.currentSelection) self.displayNode = Node(controller: self, context: self.context, sourceView: self.sourceView, emojiContent: self.emojiContent, currentSelection: self.currentSelection, color: self.color)
super.displayNodeDidLoad() super.displayNodeDidLoad()
} }

View File

@ -7188,6 +7188,7 @@ public final class EmojiPagerContentComponent: Component {
public enum Subject: Equatable { public enum Subject: Equatable {
case generic case generic
case status case status
case channelStatus
case reaction(onlyTop: Bool) case reaction(onlyTop: Bool)
case emoji case emoji
case topicIcon case topicIcon
@ -7239,6 +7240,19 @@ public final class EmojiPagerContentComponent: Component {
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji) orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji)
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentStatusEmoji) orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentStatusEmoji)
iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconStatusEmoji, forceActualized: false)
|> map { result -> [TelegramMediaFile] in
switch result {
case let .result(_, items, _):
return items.map(\.file)
default:
return []
}
}
|> take(1)
} else if case .channelStatus = subject {
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji)
iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconStatusEmoji, forceActualized: false) iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconStatusEmoji, forceActualized: false)
|> map { result -> [TelegramMediaFile] in |> map { result -> [TelegramMediaFile] in
switch result { switch result {
@ -7283,6 +7297,8 @@ public final class EmojiPagerContentComponent: Component {
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji) searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji)
} else if case .status = subject { } else if case .status = subject {
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .status) searchCategories = context.engine.stickers.emojiSearchCategories(kind: .status)
} else if case .channelStatus = subject {
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .status)
} else if [.profilePhoto, .groupPhoto].contains(subject) { } else if [.profilePhoto, .groupPhoto].contains(subject) {
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar) searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar)
} else { } else {
@ -7564,6 +7580,166 @@ public final class EmojiPagerContentComponent: Component {
} }
} }
if let recentStatusEmoji = recentStatusEmoji {
for item in recentStatusEmoji.items {
guard let item = item.contents.get(RecentMediaItem.self) else {
continue
}
let file = item.media
if existingIds.contains(file.fileId) {
continue
}
existingIds.insert(file.fileId)
var tintMode: Item.TintMode = .none
if file.isCustomTemplateEmoji {
tintMode = .accent
}
for attribute in file.attributes {
if case let .CustomEmoji(_, _, _, packReference) = attribute {
switch packReference {
case let .id(id, _):
if id == 773947703670341676 || id == 2964141614563343 {
tintMode = .accent
}
default:
break
}
}
}
let resultItem: EmojiPagerContentComponent.Item
let animationData = EntityKeyboardAnimationData(file: file)
resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
content: .animation(animationData),
itemFile: file,
subgroupId: nil,
icon: .none,
tintMode: tintMode
)
if let groupIndex = itemGroupIndexById[groupId] {
if itemGroups[groupIndex].items.count >= (5 + 8) * 8 {
break
}
itemGroups[groupIndex].items.append(resultItem)
}
}
}
if let featuredStatusEmoji = featuredStatusEmoji {
for item in featuredStatusEmoji.items {
guard let item = item.contents.get(RecentMediaItem.self) else {
continue
}
let file = item.media
if existingIds.contains(file.fileId) {
continue
}
existingIds.insert(file.fileId)
let resultItem: EmojiPagerContentComponent.Item
var tintMode: Item.TintMode = .none
if file.isCustomTemplateEmoji {
tintMode = .accent
}
for attribute in file.attributes {
if case let .CustomEmoji(_, _, _, packReference) = attribute {
switch packReference {
case let .id(id, _):
if id == 773947703670341676 || id == 2964141614563343 {
tintMode = .accent
}
default:
break
}
}
}
let animationData = EntityKeyboardAnimationData(file: file)
resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
content: .animation(animationData),
itemFile: file,
subgroupId: nil,
icon: .none,
tintMode: tintMode
)
if let groupIndex = itemGroupIndexById[groupId] {
if itemGroups[groupIndex].items.count >= (5 + 8) * 8 {
break
}
itemGroups[groupIndex].items.append(resultItem)
}
}
}
} else if case .channelStatus = subject {
let resultItem = EmojiPagerContentComponent.Item(
animationData: nil,
content: .icon(.stop),
itemFile: nil,
subgroupId: nil,
icon: .none,
tintMode: .accent
)
let groupId = "recent"
if let groupIndex = itemGroupIndexById[groupId] {
itemGroups[groupIndex].items.append(resultItem)
} else {
itemGroupIndexById[groupId] = itemGroups.count
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: topStatusTitle?.uppercased(), subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem]))
}
var existingIds = Set<MediaId>()
for file in iconStatusEmoji.prefix(7) {
if existingIds.contains(file.fileId) {
continue
}
existingIds.insert(file.fileId)
var tintMode: Item.TintMode = .none
if file.isCustomTemplateEmoji {
tintMode = .accent
}
for attribute in file.attributes {
if case let .CustomEmoji(_, _, _, packReference) = attribute {
switch packReference {
case let .id(id, _):
if id == 773947703670341676 || id == 2964141614563343 {
tintMode = .accent
}
default:
break
}
}
}
let resultItem: EmojiPagerContentComponent.Item
let animationData = EntityKeyboardAnimationData(file: file)
resultItem = EmojiPagerContentComponent.Item(
animationData: animationData,
content: .animation(animationData),
itemFile: file,
subgroupId: nil,
icon: .none,
tintMode: tintMode
)
if let groupIndex = itemGroupIndexById[groupId] {
itemGroups[groupIndex].items.append(resultItem)
}
}
if let recentStatusEmoji = recentStatusEmoji { if let recentStatusEmoji = recentStatusEmoji {
for item in recentStatusEmoji.items { for item in recentStatusEmoji.items {
guard let item = item.contents.get(RecentMediaItem.self) else { guard let item = item.contents.get(RecentMediaItem.self) else {
@ -8143,7 +8319,7 @@ public final class EmojiPagerContentComponent: Component {
var isTemplate = false var isTemplate = false
var tintMode: Item.TintMode = .none var tintMode: Item.TintMode = .none
if item.file.isCustomTemplateEmoji { if item.file.isCustomTemplateEmoji {
if [.status, .backgroundIcon].contains(subject) { if [.status, .channelStatus, .backgroundIcon].contains(subject) {
if let backgroundIconColor { if let backgroundIconColor {
tintMode = .custom(backgroundIconColor) tintMode = .custom(backgroundIconColor)
} else { } else {
@ -8229,7 +8405,7 @@ public final class EmojiPagerContentComponent: Component {
for item in featuredEmojiPack.topItems { for item in featuredEmojiPack.topItems {
var tintMode: Item.TintMode = .none var tintMode: Item.TintMode = .none
if item.file.isCustomTemplateEmoji { if item.file.isCustomTemplateEmoji {
if [.status, .backgroundIcon].contains(subject) { if [.status, .channelStatus, .backgroundIcon].contains(subject) {
if let backgroundIconColor { if let backgroundIconColor {
tintMode = .custom(backgroundIconColor) tintMode = .custom(backgroundIconColor)
} else { } else {
@ -8361,8 +8537,8 @@ public final class EmojiPagerContentComponent: Component {
) )
} }
let warpContentsOnEdges = [.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction, .status, .profilePhoto, .groupPhoto, .backgroundIcon].contains(subject) let warpContentsOnEdges = [.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction, .status, .channelStatus, .profilePhoto, .groupPhoto, .backgroundIcon].contains(subject)
let enableLongPress = [.reaction(onlyTop: true), .reaction(onlyTop: false), .status].contains(subject) let enableLongPress = [.reaction(onlyTop: true), .reaction(onlyTop: false), .status, .channelStatus].contains(subject)
return EmojiPagerContentComponent( return EmojiPagerContentComponent(
id: "emoji", id: "emoji",

View File

@ -0,0 +1,21 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ListActionItemComponent",
module_name = "ListActionItemComponent",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display",
"//submodules/ComponentFlow",
"//submodules/TelegramPresentationData",
"//submodules/TelegramUI/Components/ListSectionComponent",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,187 @@
import Foundation
import UIKit
import Display
import ComponentFlow
import TelegramPresentationData
import ListSectionComponent
public final class ListActionItemComponent: Component {
public let theme: PresentationTheme
public let title: AnyComponent<Empty>
public let icon: AnyComponentWithIdentity<Empty>?
public let hasArrow: Bool
public let action: ((UIView) -> Void)?
public init(
theme: PresentationTheme,
title: AnyComponent<Empty>,
icon: AnyComponentWithIdentity<Empty>?,
hasArrow: Bool = true,
action: ((UIView) -> Void)?
) {
self.theme = theme
self.title = title
self.icon = icon
self.hasArrow = hasArrow
self.action = action
}
public static func ==(lhs: ListActionItemComponent, rhs: ListActionItemComponent) -> Bool {
if lhs.theme !== rhs.theme {
return false
}
if lhs.title != rhs.title {
return false
}
if lhs.icon != rhs.icon {
return false
}
if lhs.hasArrow != rhs.hasArrow {
return false
}
if (lhs.action == nil) != (rhs.action == nil) {
return false
}
return true
}
public final class View: HighlightTrackingButton, ListSectionComponent.ChildView {
private let title = ComponentView<Empty>()
private var icon: ComponentView<Empty>?
private let arrowView: UIImageView
private var component: ListActionItemComponent?
public var iconView: UIView? {
return self.icon?.view
}
public var customUpdateIsHighlighted: ((Bool) -> Void)?
public override init(frame: CGRect) {
self.arrowView = UIImageView()
super.init(frame: CGRect())
self.addSubview(self.arrowView)
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
self.internalHighligthedChanged = { [weak self] isHighlighted in
guard let self else {
return
}
if let customUpdateIsHighlighted = self.customUpdateIsHighlighted {
customUpdateIsHighlighted(isHighlighted)
}
}
}
required public init?(coder: NSCoder) {
preconditionFailure()
}
@objc private func pressed() {
self.component?.action?(self)
}
func update(component: ListActionItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
let previousComponent = self.component
self.component = component
self.isEnabled = component.action != nil
let verticalInset: CGFloat = 11.0
let contentLeftInset: CGFloat = 16.0
let contentRightInset: CGFloat = component.hasArrow ? 30.0 : 16.0
var contentHeight: CGFloat = 0.0
contentHeight += verticalInset
let titleSize = self.title.update(
transition: transition,
component: component.title,
environment: {},
containerSize: CGSize(width: availableSize.width - contentLeftInset, height: availableSize.height)
)
let titleFrame = CGRect(origin: CGPoint(x: contentLeftInset, y: verticalInset), size: titleSize)
if let titleView = self.title.view {
if titleView.superview == nil {
titleView.isUserInteractionEnabled = false
self.addSubview(titleView)
}
transition.setFrame(view: titleView, frame: titleFrame)
}
contentHeight += titleSize.height
contentHeight += verticalInset
if let iconValue = component.icon {
if previousComponent?.icon?.id != iconValue.id, let icon = self.icon {
self.icon = nil
if let iconView = icon.view {
transition.setAlpha(view: iconView, alpha: 0.0, completion: { [weak iconView] _ in
iconView?.removeFromSuperview()
})
}
}
var iconTransition = transition
let icon: ComponentView<Empty>
if let current = self.icon {
icon = current
} else {
iconTransition = iconTransition.withAnimation(.none)
icon = ComponentView()
self.icon = icon
}
let iconSize = icon.update(
transition: iconTransition,
component: iconValue.component,
environment: {},
containerSize: CGSize(width: availableSize.width, height: availableSize.height)
)
let iconFrame = CGRect(origin: CGPoint(x: availableSize.width - contentRightInset - iconSize.width, y: floor((contentHeight - iconSize.height) * 0.5)), size: iconSize)
if let iconView = icon.view {
if iconView.superview == nil {
iconView.isUserInteractionEnabled = false
self.addSubview(iconView)
transition.animateAlpha(view: iconView, from: 0.0, to: 1.0)
}
iconTransition.setFrame(view: iconView, frame: iconFrame)
}
} else {
if let icon = self.icon {
self.icon = nil
if let iconView = icon.view {
transition.setAlpha(view: iconView, alpha: 0.0, completion: { [weak iconView] _ in
iconView?.removeFromSuperview()
})
}
}
}
if self.arrowView.image == nil {
self.arrowView.image = PresentationResourcesItemList.disclosureArrowImage(component.theme)?.withRenderingMode(.alwaysTemplate)
}
self.arrowView.tintColor = component.theme.list.disclosureArrowColor
if let image = self.arrowView.image {
let arrowFrame = CGRect(origin: CGPoint(x: availableSize.width - 7.0 - image.size.width, y: floor((contentHeight - image.size.height) * 0.5)), size: image.size)
transition.setFrame(view: self.arrowView, frame: arrowFrame)
}
transition.setAlpha(view: self.arrowView, alpha: component.hasArrow ? 1.0 : 0.0)
return CGSize(width: availableSize.width, height: contentHeight)
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View File

@ -0,0 +1,21 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ListItemComponentAdaptor",
module_name = "ListItemComponentAdaptor",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/AsyncDisplayKit",
"//submodules/Display",
"//submodules/ComponentFlow",
"//submodules/Components/ComponentDisplayAdapters",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,131 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Display
import ComponentFlow
import ComponentDisplayAdapters
public protocol _ListItemComponentAdaptorItemGenerator: AnyObject, Equatable {
func item() -> ListViewItem
}
public final class ListItemComponentAdaptor: Component {
public typealias ItemGenerator = _ListItemComponentAdaptorItemGenerator
private let itemGenerator: AnyObject
private let isEqualImpl: (AnyObject) -> Bool
private let itemImpl: () -> ListViewItem
private let params: ListViewItemLayoutParams
public init<ItemGeneratorType: ItemGenerator>(
itemGenerator: ItemGeneratorType,
params: ListViewItemLayoutParams
) {
self.itemGenerator = itemGenerator
self.isEqualImpl = { other in
if let other = other as? ItemGeneratorType, itemGenerator == other {
return true
} else {
return false
}
}
self.itemImpl = {
return itemGenerator.item()
}
self.params = params
}
public static func ==(lhs: ListItemComponentAdaptor, rhs: ListItemComponentAdaptor) -> Bool {
if !lhs.isEqualImpl(rhs.itemGenerator) {
return false
}
if lhs.params != rhs.params {
return false
}
return true
}
public final class View: UIView {
private var itemNode: ListViewItemNode?
func update(component: ListItemComponentAdaptor, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
let item = component.itemImpl()
if let itemNode = self.itemNode {
let mappedAnimation: ListViewItemUpdateAnimation
switch transition.animation {
case .none:
mappedAnimation = .None
case let .curve(duration, curve):
mappedAnimation = .System(duration: duration, transition: ControlledTransition(duration: duration, curve: curve.containedViewLayoutTransitionCurve, interactive: false))
}
var resultSize: CGSize?
item.updateNode(
async: { f in f() },
node: { return itemNode },
params: component.params,
previousItem: nil,
nextItem: nil,
animation: mappedAnimation,
completion: { [weak itemNode] layout, apply in
resultSize = layout.size
guard let itemNode else {
return
}
let nodeFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: layout.size.height))
itemNode.contentSize = layout.contentSize
itemNode.insets = layout.insets
itemNode.frame = nodeFrame
apply(ListViewItemApply(isOnScreen: true))
}
)
if let resultSize {
transition.setFrame(view: itemNode.view, frame: CGRect(origin: CGPoint(), size: resultSize))
return resultSize
} else {
#if DEBUG
assertionFailure()
#endif
return self.bounds.size
}
} else {
var itemNode: ListViewItemNode?
item.nodeConfiguredForParams(
async: { f in f() },
params: component.params,
synchronousLoads: true,
previousItem: nil,
nextItem: nil,
completion: { result, apply in
itemNode = result
apply().1(ListViewItemApply(isOnScreen: true))
}
)
if let itemNode {
self.itemNode = itemNode
self.addSubnode(itemNode)
return itemNode.bounds.size
} else {
#if DEBUG
assertionFailure()
#endif
return self.bounds.size
}
}
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View File

@ -0,0 +1,21 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ListSectionComponent",
module_name = "ListSectionComponent",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display",
"//submodules/ComponentFlow",
"//submodules/TelegramPresentationData",
"//submodules/TelegramUI/Components/DynamicCornerRadiusView",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,288 @@
import Foundation
import UIKit
import Display
import ComponentFlow
import TelegramPresentationData
import DynamicCornerRadiusView
public protocol ListSectionComponentChildView: AnyObject {
var customUpdateIsHighlighted: ((Bool) -> Void)? { get set }
}
public final class ListSectionComponent: Component {
public typealias ChildView = ListSectionComponentChildView
public enum Background: Equatable {
case none
case all
case range(from: AnyHashable, corners: DynamicCornerRadiusView.Corners)
}
public let theme: PresentationTheme
public let background: Background
public let header: AnyComponent<Empty>?
public let footer: AnyComponent<Empty>?
public let items: [AnyComponentWithIdentity<Empty>]
public init(
theme: PresentationTheme,
background: Background = .all,
header: AnyComponent<Empty>?,
footer: AnyComponent<Empty>?,
items: [AnyComponentWithIdentity<Empty>]
) {
self.theme = theme
self.background = background
self.header = header
self.footer = footer
self.items = items
}
public static func ==(lhs: ListSectionComponent, rhs: ListSectionComponent) -> Bool {
if lhs.theme !== rhs.theme {
return false
}
if lhs.background != rhs.background {
return false
}
if lhs.header != rhs.header {
return false
}
if lhs.footer != rhs.footer {
return false
}
if lhs.items != rhs.items {
return false
}
return true
}
public final class View: UIView {
private let contentView: UIView
private let contentBackgroundView: DynamicCornerRadiusView
private var header: ComponentView<Empty>?
private var footer: ComponentView<Empty>?
private var itemViews: [AnyHashable: ComponentView<Empty>] = [:]
private var isHighlighted: Bool = false
private var component: ListSectionComponent?
public override init(frame: CGRect) {
self.contentView = UIView()
self.contentView.layer.cornerRadius = 11.0
self.contentView.clipsToBounds = true
self.contentBackgroundView = DynamicCornerRadiusView()
super.init(frame: CGRect())
self.addSubview(self.contentBackgroundView)
self.addSubview(self.contentView)
}
required public init?(coder: NSCoder) {
preconditionFailure()
}
private func updateIsHighlighted(isHighlighted: Bool) {
if self.isHighlighted == isHighlighted {
return
}
self.isHighlighted = isHighlighted
guard let component = self.component else {
return
}
let transition: Transition
let backgroundColor: UIColor
if isHighlighted {
transition = .immediate
backgroundColor = component.theme.list.itemHighlightedBackgroundColor
} else {
transition = .easeInOut(duration: 0.2)
backgroundColor = component.theme.list.itemBlocksBackgroundColor
}
self.contentBackgroundView.updateColor(color: backgroundColor, transition: transition)
}
func update(component: ListSectionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
self.component = component
let backgroundColor: UIColor
if self.isHighlighted {
backgroundColor = component.theme.list.itemHighlightedBackgroundColor
} else {
backgroundColor = component.theme.list.itemBlocksBackgroundColor
}
self.contentBackgroundView.updateColor(color: backgroundColor, transition: transition)
let headerSideInset: CGFloat = 16.0
var contentHeight: CGFloat = 0.0
if let headerValue = component.header {
let header: ComponentView<Empty>
var headerTransition = transition
if let current = self.header {
header = current
} else {
headerTransition = headerTransition.withAnimation(.none)
header = ComponentView()
self.header = header
}
let headerSize = header.update(
transition: headerTransition,
component: headerValue,
environment: {},
containerSize: CGSize(width: availableSize.width - headerSideInset * 2.0, height: availableSize.height)
)
if let headerView = header.view {
if headerView.superview == nil {
self.addSubview(headerView)
}
headerTransition.setFrame(view: headerView, frame: CGRect(origin: CGPoint(x: headerSideInset, y: contentHeight), size: headerSize))
}
contentHeight += headerSize.height
} else {
if let header = self.header {
self.header = nil
header.view?.removeFromSuperview()
}
}
var innerContentHeight: CGFloat = 0.0
var validItemIds: [AnyHashable] = []
for item in component.items {
validItemIds.append(item.id)
let itemView: ComponentView<Empty>
var itemTransition = transition
if let current = self.itemViews[item.id] {
itemView = current
} else {
itemTransition = itemTransition.withAnimation(.none)
itemView = ComponentView()
self.itemViews[item.id] = itemView
}
let itemSize = itemView.update(
transition: itemTransition,
component: item.component,
environment: {},
containerSize: CGSize(width: availableSize.width, height: availableSize.height)
)
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: innerContentHeight), size: itemSize)
if let itemComponentView = itemView.view {
if itemComponentView.superview == nil {
self.contentView.addSubview(itemComponentView)
transition.animateAlpha(view: itemComponentView, from: 0.0, to: 1.0)
if let itemComponentView = itemComponentView as? ChildView {
itemComponentView.customUpdateIsHighlighted = { [weak self] isHighlighted in
guard let self else {
return
}
self.updateIsHighlighted(isHighlighted: isHighlighted)
}
}
}
itemTransition.setFrame(view: itemComponentView, frame: itemFrame)
}
innerContentHeight += itemSize.height
}
var removedItemIds: [AnyHashable] = []
for (id, itemView) in self.itemViews {
if !validItemIds.contains(id) {
removedItemIds.append(id)
if let itemComponentView = itemView.view {
transition.setAlpha(view: itemComponentView, alpha: 0.0, completion: { [weak itemComponentView] _ in
itemComponentView?.removeFromSuperview()
})
}
}
}
for id in removedItemIds {
self.itemViews.removeValue(forKey: id)
}
if innerContentHeight != 0.0 && contentHeight != 0.0 {
contentHeight += 7.0
}
let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: availableSize.width, height: innerContentHeight))
transition.setFrame(view: self.contentView, frame: contentFrame)
let backgroundFrame: CGRect
var backgroundAlpha: CGFloat = 1.0
switch component.background {
case .none:
backgroundFrame = contentFrame
backgroundAlpha = 0.0
self.contentBackgroundView.update(size: backgroundFrame.size, corners: DynamicCornerRadiusView.Corners(minXMinY: 11.0, maxXMinY: 11.0, minXMaxY: 11.0, maxXMaxY: 11.0), transition: transition)
case .all:
backgroundFrame = contentFrame
self.contentBackgroundView.update(size: backgroundFrame.size, corners: DynamicCornerRadiusView.Corners(minXMinY: 11.0, maxXMinY: 11.0, minXMaxY: 11.0, maxXMaxY: 11.0), transition: transition)
case let .range(from, corners):
if let itemComponentView = self.itemViews[from]?.view, itemComponentView.frame.minY < contentFrame.height {
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: contentFrame.minY + itemComponentView.frame.minY), size: CGSize(width: contentFrame.width, height: contentFrame.height - itemComponentView.frame.minY))
} else {
backgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minY, y: contentFrame.height), size: CGSize(width: contentFrame.width, height: 0.0))
}
self.contentBackgroundView.update(size: backgroundFrame.size, corners: corners, transition: transition)
}
transition.setFrame(view: self.contentBackgroundView, frame: backgroundFrame)
transition.setAlpha(view: self.contentBackgroundView, alpha: backgroundAlpha)
contentHeight += innerContentHeight
if let footerValue = component.footer {
let footer: ComponentView<Empty>
var footerTransition = transition
if let current = self.footer {
footer = current
} else {
footerTransition = footerTransition.withAnimation(.none)
footer = ComponentView()
self.footer = footer
}
let footerSize = footer.update(
transition: footerTransition,
component: footerValue,
environment: {},
containerSize: CGSize(width: availableSize.width - headerSideInset * 2.0, height: availableSize.height)
)
if contentHeight != 0.0 {
contentHeight += 7.0
}
if let footerView = footer.view {
if footerView.superview == nil {
self.addSubview(footerView)
}
footerTransition.setFrame(view: footerView, frame: CGRect(origin: CGPoint(x: headerSideInset, y: contentHeight), size: footerSize))
}
contentHeight += footerSize.height
} else {
if let footer = self.footer {
self.footer = nil
footer.view?.removeFromSuperview()
}
}
return CGSize(width: availableSize.width, height: contentHeight)
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View File

@ -332,7 +332,7 @@ private func notificationsPeerCategoryEntries(peerId: EnginePeer.Id, notificatio
} }
} }
existingThreadIds.insert(value.threadId) existingThreadIds.insert(value.threadId)
entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, .channel(TelegramChannel(id: peerId, accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(TelegramChannelGroupInfo(flags: [])), flags: [.isForum], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId)) entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, .channel(TelegramChannel(id: peerId, accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(TelegramChannelGroupInfo(flags: [])), flags: [.isForum], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId))
index += 1 index += 1
} }

View File

@ -26,6 +26,7 @@ swift_library(
"//submodules/Markdown", "//submodules/Markdown",
"//submodules/TelegramUI/Components/ButtonComponent", "//submodules/TelegramUI/Components/ButtonComponent",
"//submodules/TelegramUI/Components/AnimatedTextComponent", "//submodules/TelegramUI/Components/AnimatedTextComponent",
"//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent",
"//submodules/Components/BundleIconComponent", "//submodules/Components/BundleIconComponent",
"//submodules/Components/PagerComponent", "//submodules/Components/PagerComponent",
"//submodules/PremiumUI", "//submodules/PremiumUI",

View File

@ -20,82 +20,7 @@ import BundleIconComponent
import AnimatedTextComponent import AnimatedTextComponent
import TextFormat import TextFormat
import AudioToolbox import AudioToolbox
import PremiumLockButtonSubtitleComponent
private final class ButtonSubtitleComponent: CombinedComponent {
let count: Int
let theme: PresentationTheme
let strings: PresentationStrings
init(count: Int, theme: PresentationTheme, strings: PresentationStrings) {
self.count = count
self.theme = theme
self.strings = strings
}
static func ==(lhs: ButtonSubtitleComponent, rhs: ButtonSubtitleComponent) -> Bool {
if lhs.count != rhs.count {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.strings !== rhs.strings {
return false
}
return true
}
static var body: Body {
let icon = Child(BundleIconComponent.self)
let text = Child(AnimatedTextComponent.self)
return { context in
let icon = icon.update(
component: BundleIconComponent(
name: "Chat/Input/Accessory Panels/TextLockIcon",
tintColor: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7),
maxSize: CGSize(width: 10.0, height: 10.0)
),
availableSize: CGSize(width: 100.0, height: 100.0),
transition: context.transition
)
var textItems: [AnimatedTextComponent.Item] = []
let levelString = context.component.strings.ChannelReactions_LevelRequiredLabel("")
var previousIndex = 0
let nsLevelString = levelString.string as NSString
for range in levelString.ranges.sorted(by: { $0.range.lowerBound < $1.range.lowerBound }) {
if range.range.lowerBound > previousIndex {
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: range.range.lowerBound - previousIndex)))))
}
if range.index == 0 {
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .number(context.component.count, minDigits: 1)))
}
previousIndex = range.range.upperBound
}
if nsLevelString.length > previousIndex {
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(100), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: nsLevelString.length - previousIndex)))))
}
let text = text.update(
component: AnimatedTextComponent(font: Font.medium(11.0), color: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7), items: textItems),
availableSize: CGSize(width: context.availableSize.width - 20.0, height: 100.0),
transition: context.transition
)
let spacing: CGFloat = 3.0
let size = CGSize(width: icon.size.width + spacing + text.size.width, height: text.size.height)
context.add(icon
.position(icon.size.centered(in: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: icon.size.width, height: size.height))).center)
)
context.add(text
.position(text.size.centered(in: CGRect(origin: CGPoint(x: icon.size.width + spacing, y: 0.0), size: text.size)).center)
)
return size
}
}
}
final class PeerAllowedReactionsScreenComponent: Component { final class PeerAllowedReactionsScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -904,7 +829,7 @@ final class PeerAllowedReactionsScreenComponent: Component {
}).count : 0 }).count : 0
if let boostStatus = self.boostStatus, customReactionCount > boostStatus.level { if let boostStatus = self.boostStatus, customReactionCount > boostStatus.level {
buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(ButtonSubtitleComponent( buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(PremiumLockButtonSubtitleComponent(
count: customReactionCount, count: customReactionCount,
theme: environment.theme, theme: environment.theme,
strings: environment.strings strings: environment.strings

View File

@ -34,15 +34,17 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem {
let id: AnyHashable let id: AnyHashable
let label: Label let label: Label
let additionalBadgeLabel: String? let additionalBadgeLabel: String?
let additionalBadgeIcon: UIImage?
let text: String let text: String
let icon: UIImage? let icon: UIImage?
let iconSignal: Signal<UIImage?, NoError>? let iconSignal: Signal<UIImage?, NoError>?
let action: (() -> Void)? let action: (() -> Void)?
init(id: AnyHashable, label: Label = .none, additionalBadgeLabel: String? = nil, text: String, icon: UIImage? = nil, iconSignal: Signal<UIImage?, NoError>? = nil, action: (() -> Void)?) { init(id: AnyHashable, label: Label = .none, additionalBadgeLabel: String? = nil, additionalBadgeIcon: UIImage? = nil, text: String, icon: UIImage? = nil, iconSignal: Signal<UIImage?, NoError>? = nil, action: (() -> Void)?) {
self.id = id self.id = id
self.label = label self.label = label
self.additionalBadgeLabel = additionalBadgeLabel self.additionalBadgeLabel = additionalBadgeLabel
self.additionalBadgeIcon = additionalBadgeIcon
self.text = text self.text = text
self.icon = icon self.icon = icon
self.iconSignal = iconSignal self.iconSignal = iconSignal
@ -61,6 +63,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
private let labelBadgeNode: ASImageNode private let labelBadgeNode: ASImageNode
private let labelNode: ImmediateTextNode private let labelNode: ImmediateTextNode
private var additionalLabelNode: ImmediateTextNode? private var additionalLabelNode: ImmediateTextNode?
private var additionalLabelBadgeNode: ASImageNode?
private let textNode: ImmediateTextNode private let textNode: ImmediateTextNode
private let arrowNode: ASImageNode private let arrowNode: ASImageNode
private let bottomSeparatorNode: ASDisplayNode private let bottomSeparatorNode: ASDisplayNode
@ -249,6 +252,24 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
self.labelBadgeNode.removeFromSupernode() self.labelBadgeNode.removeFromSupernode()
} }
if let additionalBadgeIcon = item.additionalBadgeIcon {
let additionalLabelBadgeNode: ASImageNode
if let current = self.additionalLabelBadgeNode {
additionalLabelBadgeNode = current
} else {
additionalLabelBadgeNode = ASImageNode()
additionalLabelBadgeNode.isUserInteractionEnabled = false
self.additionalLabelBadgeNode = additionalLabelBadgeNode
self.insertSubnode(additionalLabelBadgeNode, belowSubnode: self.labelNode)
}
additionalLabelBadgeNode.image = additionalBadgeIcon
} else {
if let additionalLabelBadgeNode = self.additionalLabelBadgeNode {
self.additionalLabelBadgeNode = nil
additionalLabelBadgeNode.removeFromSupernode()
}
}
var badgeWidth = max(badgeDiameter, labelSize.width + 10.0) var badgeWidth = max(badgeDiameter, labelSize.width + 10.0)
if case .semitransparentBadge = item.label { if case .semitransparentBadge = item.label {
badgeWidth += 2.0 badgeWidth += 2.0
@ -283,6 +304,11 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
additionalLabelNode.removeFromSupernode() additionalLabelNode.removeFromSupernode()
} }
if let additionalLabelBadgeNode = self.additionalLabelBadgeNode, let image = additionalLabelBadgeNode.image {
let additionalLabelSize = image.size
additionalLabelBadgeNode.frame = CGRect(origin: CGPoint(x: textFrame.maxX + 6.0, y: floor((height - additionalLabelSize.height) / 2.0) + 1.0), size: additionalLabelSize)
}
let labelBadgeNodeFrame: CGRect let labelBadgeNodeFrame: CGRect
if case let .image(_, imageSize) = item.label { if case let .image(_, imageSize) = item.label {
labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - imageSize.width, y: floorToScreenPixels(textFrame.midY - imageSize.height / 2.0)), size:imageSize) labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - imageSize.width, y: floorToScreenPixels(textFrame.midY - imageSize.height / 2.0)), size:imageSize)

View File

@ -476,7 +476,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
credibilityIcon = .fake credibilityIcon = .fake
} else if peer.isScam { } else if peer.isScam {
credibilityIcon = .scam credibilityIcon = .scam
} else if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { } else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled {
credibilityIcon = .emojiStatus(emojiStatus) credibilityIcon = .emojiStatus(emojiStatus)
} else if peer.isVerified { } else if peer.isVerified {
credibilityIcon = .verified credibilityIcon = .verified

View File

@ -1675,7 +1675,12 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
} }
let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors) let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors)
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), text: presentationData.strings.Channel_ChannelColor, icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: { //TODO:localize
var boostIcon: UIImage?
if let approximateBoostLevel = channel.approximateBoostLevel, approximateBoostLevel < 1 {
boostIcon = generateDisclosureActionBoostLevelBadgeImage(text: "Level 1+")
}
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), additionalBadgeIcon: boostIcon, text: "Appearance", icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: {
interaction.editingOpenNameColorSetup() interaction.editingOpenNameColorSetup()
})) }))
@ -3835,7 +3840,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
var currentSelectedFileId: Int64? var currentSelectedFileId: Int64?
var topStatusTitle = strongSelf.presentationData.strings.PeerStatusSetup_NoTimerTitle var topStatusTitle = strongSelf.presentationData.strings.PeerStatusSetup_NoTimerTitle
if let peer = strongSelf.data?.peer { if let peer = strongSelf.data?.peer {
if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus { if let emojiStatus = peer.emojiStatus {
selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId)) selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId))
currentSelectedFileId = emojiStatus.fileId currentSelectedFileId = emojiStatus.fileId
@ -7200,9 +7205,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
if self.peerId == self.context.account.peerId { if self.peerId == self.context.account.peerId {
let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .account) let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .account)
self.controller?.push(controller) self.controller?.push(controller)
} else { } else if let peer = self.data?.peer, peer is TelegramChannel {
let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .channel(self.peerId)) self.controller?.push(ChannelAppearanceScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: self.peerId))
self.controller?.push(controller)
} }
} }
@ -12218,7 +12222,7 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom
self.avatarNode.setPeer(context: self.item.context, account: self.item.account, theme: self.presentationData.theme, peer: self.item.peer) self.avatarNode.setPeer(context: self.item.context, account: self.item.account, theme: self.presentationData.theme, peer: self.item.peer)
if case let .user(user) = self.item.peer, let _ = user.emojiStatus { if self.item.peer.emojiStatus != nil {
rightTextInset += 32.0 rightTextInset += 32.0
} }
@ -12236,6 +12240,10 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom
} else if user.isPremium { } else if user.isPremium {
iconContent = .premium(color: self.presentationData.theme.list.itemAccentColor) iconContent = .premium(color: self.presentationData.theme.list.itemAccentColor)
} }
} else if case let .channel(channel) = self.item.peer {
if let emojiStatus = channel.emojiStatus {
iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever)
}
} }
if let iconContent { if let iconContent {
let emojiStatusSize = self.emojiStatusView.update( let emojiStatusSize = self.emojiStatusView.update(

View File

@ -0,0 +1,22 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "PremiumLockButtonSubtitleComponent",
module_name = "PremiumLockButtonSubtitleComponent",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display",
"//submodules/ComponentFlow",
"//submodules/TelegramPresentationData",
"//submodules/Components/BundleIconComponent",
"//submodules/TelegramUI/Components/AnimatedTextComponent",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,83 @@
import Foundation
import UIKit
import Display
import ComponentFlow
import TelegramPresentationData
import BundleIconComponent
import AnimatedTextComponent
public final class PremiumLockButtonSubtitleComponent: CombinedComponent {
public let count: Int
public let theme: PresentationTheme
public let strings: PresentationStrings
public init(count: Int, theme: PresentationTheme, strings: PresentationStrings) {
self.count = count
self.theme = theme
self.strings = strings
}
public static func ==(lhs: PremiumLockButtonSubtitleComponent, rhs: PremiumLockButtonSubtitleComponent) -> Bool {
if lhs.count != rhs.count {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.strings !== rhs.strings {
return false
}
return true
}
public static var body: Body {
let icon = Child(BundleIconComponent.self)
let text = Child(AnimatedTextComponent.self)
return { context in
let icon = icon.update(
component: BundleIconComponent(
name: "Chat/Input/Accessory Panels/TextLockIcon",
tintColor: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7),
maxSize: CGSize(width: 10.0, height: 10.0)
),
availableSize: CGSize(width: 100.0, height: 100.0),
transition: context.transition
)
var textItems: [AnimatedTextComponent.Item] = []
let levelString = context.component.strings.ChannelReactions_LevelRequiredLabel("")
var previousIndex = 0
let nsLevelString = levelString.string as NSString
for range in levelString.ranges.sorted(by: { $0.range.lowerBound < $1.range.lowerBound }) {
if range.range.lowerBound > previousIndex {
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: range.range.lowerBound - previousIndex)))))
}
if range.index == 0 {
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .number(context.component.count, minDigits: 1)))
}
previousIndex = range.range.upperBound
}
if nsLevelString.length > previousIndex {
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(100), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: nsLevelString.length - previousIndex)))))
}
let text = text.update(
component: AnimatedTextComponent(font: Font.medium(11.0), color: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7), items: textItems),
availableSize: CGSize(width: context.availableSize.width - 20.0, height: 100.0),
transition: context.transition
)
let spacing: CGFloat = 3.0
let size = CGSize(width: icon.size.width + spacing + text.size.width, height: text.size.height)
context.add(icon
.position(icon.size.centered(in: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: icon.size.width, height: size.height))).center)
)
context.add(text
.position(text.size.centered(in: CGRect(origin: CGPoint(x: icon.size.width + spacing, y: 0.0), size: text.size)).center)
)
return size
}
}
}

View File

@ -21,6 +21,7 @@ swift_library(
"//submodules/PresentationDataUtils", "//submodules/PresentationDataUtils",
"//submodules/UndoUI", "//submodules/UndoUI",
"//submodules/WallpaperBackgroundNode", "//submodules/WallpaperBackgroundNode",
"//submodules/ComponentFlow",
"//submodules/TelegramUI/Components/EmojiStatusComponent", "//submodules/TelegramUI/Components/EmojiStatusComponent",
"//submodules/TelegramUI/Components/EntityKeyboard", "//submodules/TelegramUI/Components/EntityKeyboard",
"//submodules/SolidRoundedButtonNode", "//submodules/SolidRoundedButtonNode",
@ -28,6 +29,20 @@ swift_library(
"//submodules/PremiumUI", "//submodules/PremiumUI",
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent", "//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent",
"//submodules/AvatarNode", "//submodules/AvatarNode",
"//submodules/Components/ViewControllerComponent",
"//submodules/Components/BundleIconComponent",
"//submodules/TelegramUI/Components/AnimatedTextComponent",
"//submodules/TelegramUI/Components/ButtonComponent",
"//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent",
"//submodules/TelegramUI/Components/ListItemComponentAdaptor",
"//submodules/TelegramUI/Components/ListSectionComponent",
"//submodules/TelegramUI/Components/ListActionItemComponent",
"//submodules/TelegramUI/Components/Settings/ThemeCarouselItem",
"//submodules/Components/MultilineTextComponent",
"//submodules/TelegramUI/Components/EmojiStatusSelectionComponent",
"//submodules/TelegramUI/Components/DynamicCornerRadiusView",
"//submodules/Components/ComponentDisplayAdapters",
"//submodules/WallpaperResources",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -11,8 +11,9 @@ import ItemListUI
import PresentationDataUtils import PresentationDataUtils
import AccountContext import AccountContext
import WallpaperBackgroundNode import WallpaperBackgroundNode
import ListItemComponentAdaptor
final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem { final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
struct MessageItem: Equatable { struct MessageItem: Equatable {
static func ==(lhs: MessageItem, rhs: MessageItem) -> Bool { static func ==(lhs: MessageItem, rhs: MessageItem) -> Bool {
if lhs.outgoing != rhs.outgoing { if lhs.outgoing != rhs.outgoing {
@ -118,6 +119,44 @@ final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem {
} }
} }
} }
public func item() -> ListViewItem {
return self
}
public static func ==(lhs: PeerNameColorChatPreviewItem, rhs: PeerNameColorChatPreviewItem) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.componentTheme !== rhs.componentTheme {
return false
}
if lhs.strings !== rhs.strings {
return false
}
if lhs.fontSize != rhs.fontSize {
return false
}
if lhs.chatBubbleCorners != rhs.chatBubbleCorners {
return false
}
if lhs.wallpaper != rhs.wallpaper {
return false
}
if lhs.dateTimeFormat != rhs.dateTimeFormat {
return false
}
if lhs.nameDisplayOrder != rhs.nameDisplayOrder {
return false
}
if lhs.messageItems != rhs.messageItems {
return false
}
return true
}
} }
final class PeerNameColorChatPreviewItemNode: ListViewItemNode { final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
@ -260,7 +299,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize) strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
if let currentItem, currentItem.messageItems.first?.nameColor != item.messageItems.first?.nameColor || currentItem.messageItems.first?.backgroundEmojiId != item.messageItems.first?.backgroundEmojiId { if let currentItem, currentItem.messageItems.first?.nameColor != item.messageItems.first?.nameColor || currentItem.messageItems.first?.backgroundEmojiId != item.messageItems.first?.backgroundEmojiId || currentItem.theme !== item.theme || currentItem.wallpaper != item.wallpaper {
if let snapshot = strongSelf.view.snapshotView(afterScreenUpdates: false) { if let snapshot = strongSelf.view.snapshotView(afterScreenUpdates: false) {
snapshot.frame = CGRect(origin: CGPoint(x: 0.0, y: -insets.top), size: snapshot.frame.size) snapshot.frame = CGRect(origin: CGPoint(x: 0.0, y: -insets.top), size: snapshot.frame.size)
strongSelf.view.addSubview(snapshot) strongSelf.view.addSubview(snapshot)
@ -329,9 +368,16 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
strongSelf.insertSubnode(strongSelf.maskNode, at: 3) strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
} }
if params.isStandalone {
strongSelf.topStripeNode.isHidden = true
strongSelf.bottomStripeNode.isHidden = true
strongSelf.maskNode.isHidden = true
} else {
let hasCorners = itemListHasRoundedBlockLayout(params) let hasCorners = itemListHasRoundedBlockLayout(params)
var hasTopCorners = false var hasTopCorners = false
var hasBottomCorners = false var hasBottomCorners = false
switch neighbors.top { switch neighbors.top {
case .sameSection(false): case .sameSection(false):
strongSelf.topStripeNode.isHidden = true strongSelf.topStripeNode.isHidden = true
@ -355,6 +401,10 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
}
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
let displayMode: WallpaperDisplayMode let displayMode: WallpaperDisplayMode
@ -377,8 +427,6 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
backgroundNode.updateLayout(size: backgroundNode.bounds.size, displayMode: displayMode, transition: .immediate) backgroundNode.updateLayout(size: backgroundNode.bounds.size, displayMode: displayMode, transition: .immediate)
} }
strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
} }
}) })
} }

View File

@ -10,6 +10,7 @@ import MergeLists
import ItemListUI import ItemListUI
import PresentationDataUtils import PresentationDataUtils
import AccountContext import AccountContext
import ListItemComponentAdaptor
private enum PeerNameColorEntryId: Hashable { private enum PeerNameColorEntryId: Hashable {
case color(Int32) case color(Int32)
@ -313,7 +314,7 @@ private final class PeerNameColorIconItemNode : ListViewItemNode {
} }
} }
final class PeerNameColorItem: ListViewItem, ItemListItem { final class PeerNameColorItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
var sectionId: ItemListSectionId var sectionId: ItemListSectionId
let theme: PresentationTheme let theme: PresentationTheme
@ -365,6 +366,27 @@ final class PeerNameColorItem: ListViewItem, ItemListItem {
} }
} }
} }
public func item() -> ListViewItem {
return self
}
public static func ==(lhs: PeerNameColorItem, rhs: PeerNameColorItem) -> Bool {
if lhs.theme !== rhs.theme {
return false
}
if lhs.colors != rhs.colors {
return false
}
if lhs.isProfile != rhs.isProfile {
return false
}
if lhs.currentColor != rhs.currentColor {
return false
}
return true
}
} }
private struct PeerNameColorItemNodeTransition { private struct PeerNameColorItemNodeTransition {
@ -528,6 +550,12 @@ final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3) strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3)
} }
if params.isStandalone {
strongSelf.backgroundNode.isHidden = true
strongSelf.topStripeNode.isHidden = true
strongSelf.bottomStripeNode.isHidden = true
strongSelf.maskNode.isHidden = true
} else {
let hasCorners = itemListHasRoundedBlockLayout(params) let hasCorners = itemListHasRoundedBlockLayout(params)
var hasTopCorners = false var hasTopCorners = false
var hasBottomCorners = false var hasBottomCorners = false
@ -556,14 +584,16 @@ final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode {
hasBottomCorners = true hasBottomCorners = true
strongSelf.bottomStripeNode.isHidden = hasCorners strongSelf.bottomStripeNode.isHidden = hasCorners
} }
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
}
strongSelf.containerNode.frame = CGRect(x: 0.0, y: 0.0, width: contentSize.width, height: contentSize.height) strongSelf.containerNode.frame = CGRect(x: 0.0, y: 0.0, width: contentSize.width, height: contentSize.height)
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
var listInsets = UIEdgeInsets() var listInsets = UIEdgeInsets()
listInsets.top += params.leftInset + 8.0 listInsets.top += params.leftInset + 8.0

View File

@ -14,24 +14,28 @@ import ComponentFlow
import PeerInfoCoverComponent import PeerInfoCoverComponent
import AvatarNode import AvatarNode
import EmojiStatusComponent import EmojiStatusComponent
import ListItemComponentAdaptor
import ComponentDisplayAdapters
final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem { final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
let context: AccountContext let context: AccountContext
let theme: PresentationTheme let theme: PresentationTheme
let componentTheme: PresentationTheme let componentTheme: PresentationTheme
let strings: PresentationStrings let strings: PresentationStrings
let sectionId: ItemListSectionId let sectionId: ItemListSectionId
let peer: EnginePeer? let peer: EnginePeer?
let subtitleString: String?
let files: [Int64: TelegramMediaFile] let files: [Int64: TelegramMediaFile]
let nameDisplayOrder: PresentationPersonNameOrder let nameDisplayOrder: PresentationPersonNameOrder
init(context: AccountContext, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, peer: EnginePeer?, files: [Int64: TelegramMediaFile], nameDisplayOrder: PresentationPersonNameOrder) { init(context: AccountContext, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, peer: EnginePeer?, subtitleString: String? = nil, files: [Int64: TelegramMediaFile], nameDisplayOrder: PresentationPersonNameOrder) {
self.context = context self.context = context
self.theme = theme self.theme = theme
self.componentTheme = componentTheme self.componentTheme = componentTheme
self.strings = strings self.strings = strings
self.sectionId = sectionId self.sectionId = sectionId
self.peer = peer self.peer = peer
self.subtitleString = subtitleString
self.files = files self.files = files
self.nameDisplayOrder = nameDisplayOrder self.nameDisplayOrder = nameDisplayOrder
} }
@ -46,7 +50,7 @@ final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem {
Queue.mainQueue().async { Queue.mainQueue().async {
completion(node, { completion(node, {
return (nil, { _ in apply() }) return (nil, { _ in apply(.None) })
}) })
} }
} }
@ -61,13 +65,43 @@ final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem {
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
Queue.mainQueue().async { Queue.mainQueue().async {
completion(layout, { _ in completion(layout, { _ in
apply() apply(animation)
}) })
} }
} }
} }
} }
} }
func item() -> ListViewItem {
return self
}
static func ==(lhs: PeerNameColorProfilePreviewItem, rhs: PeerNameColorProfilePreviewItem) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.componentTheme !== rhs.componentTheme {
return false
}
if lhs.strings !== rhs.strings {
return false
}
if lhs.peer != rhs.peer {
return false
}
if lhs.files != rhs.files {
return false
}
if lhs.nameDisplayOrder != rhs.nameDisplayOrder {
return false
}
return true
}
} }
final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
@ -104,7 +138,7 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
deinit { deinit {
} }
func asyncLayout() -> (_ item: PeerNameColorProfilePreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { func asyncLayout() -> (_ item: PeerNameColorProfilePreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) {
return { [weak self] item, params, neighbors in return { [weak self] item, params, neighbors in
let separatorHeight = UIScreenPixel let separatorHeight = UIScreenPixel
@ -117,7 +151,7 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
let layoutSize = layout.size let layoutSize = layout.size
return (layout, { [weak self] in return (layout, { [weak self] animation in
guard let self else { guard let self else {
return return
} }
@ -140,6 +174,11 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
self.addSubnode(self.maskNode) self.addSubnode(self.maskNode)
} }
if params.isStandalone {
self.topStripeNode.isHidden = true
self.bottomStripeNode.isHidden = true
self.maskNode.isHidden = true
} else {
let hasCorners = itemListHasRoundedBlockLayout(params) let hasCorners = itemListHasRoundedBlockLayout(params)
var hasTopCorners = false var hasTopCorners = false
var hasBottomCorners = false var hasBottomCorners = false
@ -166,6 +205,10 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil
self.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
self.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
}
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
let coverFrame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) let coverFrame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)
@ -217,6 +260,61 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
} }
self.avatarNode.frame = avatarFrame.offsetBy(dx: coverFrame.minX, dy: coverFrame.minY) self.avatarNode.frame = avatarFrame.offsetBy(dx: coverFrame.minX, dy: coverFrame.minY)
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 })
enum CredibilityIcon {
case none
case premium
case verified
case fake
case scam
case emojiStatus(PeerEmojiStatus)
}
let credibilityIcon: CredibilityIcon
if let peer = item.peer {
if peer.isFake {
credibilityIcon = .fake
} else if peer.isScam {
credibilityIcon = .scam
} else if case let .user(user) = peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled {
credibilityIcon = .emojiStatus(emojiStatus)
} else if case let .channel(channel) = peer, let emojiStatus = channel.emojiStatus, !premiumConfiguration.isPremiumDisabled {
credibilityIcon = .emojiStatus(emojiStatus)
} else if peer.isVerified {
credibilityIcon = .verified
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled && (peer.id != item.context.account.peerId) {
credibilityIcon = .premium
} else {
credibilityIcon = .none
}
} else {
credibilityIcon = .none
}
let statusColor: UIColor
if let peer = item.peer, peer.profileColor != nil {
statusColor = .white
} else {
statusColor = item.theme.list.itemCheckColors.fillColor
}
let emojiStatusContent: EmojiStatusComponent.Content
switch credibilityIcon {
case .none:
emojiStatusContent = .none
case .premium:
emojiStatusContent = .premium(color: statusColor)
case .verified:
emojiStatusContent = .verified(fillColor: statusColor, foregroundColor: .clear, sizeType: .large)
case .fake:
emojiStatusContent = .text(color: item.theme.chat.message.incoming.scamColor, string: item.strings.Message_FakeAccount.uppercased())
case .scam:
emojiStatusContent = .text(color: item.theme.chat.message.incoming.scamColor, string: item.strings.Message_ScamAccount.uppercased())
case let .emojiStatus(emojiStatus):
emojiStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: item.theme.list.mediaPlaceholderColor, themeColor: statusColor, loopMode: .forever)
}
let backgroundColor: UIColor let backgroundColor: UIColor
let titleColor: UIColor let titleColor: UIColor
let subtitleColor: UIColor let subtitleColor: UIColor
@ -239,15 +337,64 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
environment: {}, environment: {},
containerSize: CGSize(width: coverFrame.width - 16.0, height: 100.0) containerSize: CGSize(width: coverFrame.width - 16.0, height: 100.0)
) )
let titleFrame = CGRect(origin: CGPoint(x: coverFrame.minX + floor((coverFrame.width - titleSize.width) * 0.5), y: avatarFrame.maxY + 10.0), size: titleSize)
var titleContentWidth = titleSize.width
var hasStatusIcon = false
if case .none = emojiStatusContent {
} else {
hasStatusIcon = true
titleContentWidth += 4.0 + 34.0
}
let _ = hasStatusIcon
let titleFrame = CGRect(origin: CGPoint(x: coverFrame.minX + floor((coverFrame.width - titleContentWidth) * 0.5), y: avatarFrame.maxY + 10.0), size: titleSize)
if let titleView = self.title.view { if let titleView = self.title.view {
if titleView.superview == nil { if titleView.superview == nil {
titleView.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
self.view.addSubview(titleView) self.view.addSubview(titleView)
} }
titleView.frame = titleFrame titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
animation.animator.updatePosition(layer: titleView.layer, position: titleFrame.origin, completion: nil)
} }
let subtitleString: String = item.strings.LastSeen_JustNow let icon: ComponentView<Empty>
if let current = self.icon {
icon = current
} else {
icon = ComponentView()
self.icon = icon
}
let iconSize = CGSize(width: 34.0, height: 34.0)
let _ = icon.update(
transition: Transition(animation.transition),
component: AnyComponent(EmojiStatusComponent(
context: item.context,
animationCache: item.context.animationCache,
animationRenderer: item.context.animationRenderer,
content: emojiStatusContent,
isVisibleForAnimations: true,
action: nil
)),
environment: {},
containerSize: iconSize
)
if let iconView = icon.view {
if iconView.superview == nil {
self.view.addSubview(iconView)
}
let iconFrame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: titleFrame.minY + floorToScreenPixels((titleFrame.height - iconSize.height) * 0.5)), size: iconSize)
iconView.bounds = CGRect(origin: CGPoint(), size: iconFrame.size)
animation.animator.updatePosition(layer: iconView.layer, position: iconFrame.center, completion: nil)
}
let subtitleString: String
if let value = item.subtitleString {
subtitleString = value
} else if case .channel = item.peer {
subtitleString = item.strings.Channel_Status
} else {
subtitleString = item.strings.LastSeen_JustNow
}
let subtitleSize = self.subtitle.update( let subtitleSize = self.subtitle.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(Text( component: AnyComponent(Text(
@ -265,8 +412,6 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
} }
self.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) self.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)
self.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
self.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
}) })
} }
} }

View File

@ -0,0 +1,37 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ThemeCarouselItem",
module_name = "ThemeCarouselItem",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display",
"//submodules/AsyncDisplayKit",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/Postbox",
"//submodules/TelegramCore",
"//submodules/TelegramPresentationData",
"//submodules/MergeLists",
"//submodules/TelegramUIPreferences",
"//submodules/ItemListUI",
"//submodules/PresentationDataUtils",
"//submodules/WallpaperResources",
"//submodules/AccountContext",
"//submodules/AppBundle",
"//submodules/ContextUI",
"//submodules/AnimatedStickerNode",
"//submodules/TelegramAnimatedStickerNode",
"//submodules/ShimmerEffect",
"//submodules/StickerResources",
"//submodules/TelegramUI/Components/ListItemComponentAdaptor",
"//submodules/HexColor",
],
visibility = [
"//visibility:public",
],
)

View File

@ -18,12 +18,15 @@ import AnimatedStickerNode
import TelegramAnimatedStickerNode import TelegramAnimatedStickerNode
import ShimmerEffect import ShimmerEffect
import StickerResources import StickerResources
import ListItemComponentAdaptor
import HexColor
private struct ThemeCarouselThemeEntry: Comparable, Identifiable { private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
let index: Int let index: Int
let emojiFile: TelegramMediaFile? let emojiFile: TelegramMediaFile?
let themeReference: PresentationThemeReference let themeReference: PresentationThemeReference
let nightMode: Bool let nightMode: Bool
let channelMode: Bool
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
var selected: Bool var selected: Bool
@ -48,6 +51,9 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
if lhs.nightMode != rhs.nightMode { if lhs.nightMode != rhs.nightMode {
return false return false
} }
if lhs.channelMode != rhs.channelMode {
return false
}
if lhs.themeSpecificAccentColors != rhs.themeSpecificAccentColors { if lhs.themeSpecificAccentColors != rhs.themeSpecificAccentColors {
return false return false
} }
@ -74,30 +80,32 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
} }
func item(context: AccountContext, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem { func item(context: AccountContext, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem {
return ThemeCarouselThemeIconItem(context: context, emojiFile: self.emojiFile, themeReference: self.themeReference, nightMode: self.nightMode, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action, contextAction: contextAction) return ThemeCarouselThemeIconItem(context: context, emojiFile: self.emojiFile, themeReference: self.themeReference, nightMode: self.nightMode, channelMode: self.channelMode, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action, contextAction: contextAction)
} }
} }
class ThemeCarouselThemeIconItem: ListViewItem { public class ThemeCarouselThemeIconItem: ListViewItem {
let context: AccountContext public let context: AccountContext
let emojiFile: TelegramMediaFile? public let emojiFile: TelegramMediaFile?
let themeReference: PresentationThemeReference public let themeReference: PresentationThemeReference
let nightMode: Bool public let nightMode: Bool
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] public let channelMode: Bool
let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
let selected: Bool public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
let theme: PresentationTheme public let selected: Bool
let strings: PresentationStrings public let theme: PresentationTheme
let wallpaper: TelegramWallpaper? public let strings: PresentationStrings
let action: (PresentationThemeReference) -> Void public let wallpaper: TelegramWallpaper?
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)? public let action: (PresentationThemeReference) -> Void
public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
public init(context: AccountContext, emojiFile: TelegramMediaFile?, themeReference: PresentationThemeReference, nightMode: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], selected: Bool, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper?, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) { public init(context: AccountContext, emojiFile: TelegramMediaFile?, themeReference: PresentationThemeReference, nightMode: Bool, channelMode: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], selected: Bool, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper?, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) {
self.context = context self.context = context
self.emojiFile = emojiFile self.emojiFile = emojiFile
self.themeReference = themeReference self.themeReference = themeReference
self.nightMode = nightMode self.nightMode = nightMode
self.channelMode = channelMode
self.themeSpecificAccentColors = themeSpecificAccentColors self.themeSpecificAccentColors = themeSpecificAccentColors
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
self.selected = selected self.selected = selected
@ -148,7 +156,6 @@ class ThemeCarouselThemeIconItem: ListViewItem {
} }
} }
private let textFont = Font.regular(12.0) private let textFont = Font.regular(12.0)
private let selectedTextFont = Font.bold(12.0) private let selectedTextFont = Font.bold(12.0)
@ -338,6 +345,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
var updatedThemeReference = false var updatedThemeReference = false
var updatedTheme = false var updatedTheme = false
var updatedNightMode = false var updatedNightMode = false
var updatedChannelMode = false
var updatedWallpaper = false var updatedWallpaper = false
var updatedSelected = false var updatedSelected = false
@ -347,6 +355,9 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
if currentItem?.nightMode != item.nightMode { if currentItem?.nightMode != item.nightMode {
updatedNightMode = true updatedNightMode = true
} }
if currentItem?.channelMode != item.channelMode {
updatedChannelMode = true
}
if currentItem?.wallpaper != item.wallpaper { if currentItem?.wallpaper != item.wallpaper {
updatedWallpaper = true updatedWallpaper = true
} }
@ -372,7 +383,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
if let strongSelf = self { if let strongSelf = self {
strongSelf.item = item strongSelf.item = item
if updatedThemeReference || updatedWallpaper || updatedNightMode { if updatedThemeReference || updatedWallpaper || updatedNightMode || updatedChannelMode {
var themeReference = item.themeReference var themeReference = item.themeReference
if case .builtin = themeReference, item.nightMode { if case .builtin = themeReference, item.nightMode {
themeReference = .builtin(.night) themeReference = .builtin(.night)
@ -381,7 +392,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
let color = item.themeSpecificAccentColors[themeReference.index] let color = item.themeSpecificAccentColors[themeReference.index]
let wallpaper = item.themeSpecificChatWallpapers[themeReference.index] let wallpaper = item.themeSpecificChatWallpapers[themeReference.index]
strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: color, wallpaper: wallpaper ?? item.wallpaper, nightMode: item.nightMode, emoticon: true)) strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: color, wallpaper: wallpaper ?? item.wallpaper, nightMode: item.nightMode, channelMode: item.channelMode, emoticon: true))
strongSelf.imageNode.backgroundColor = nil strongSelf.imageNode.backgroundColor = nil
} }
@ -507,23 +518,24 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
} }
} }
class ThemeCarouselThemeItem: ListViewItem, ItemListItem { public class ThemeCarouselThemeItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
var sectionId: ItemListSectionId public var sectionId: ItemListSectionId
let context: AccountContext public let context: AccountContext
let theme: PresentationTheme public let theme: PresentationTheme
let strings: PresentationStrings public let strings: PresentationStrings
let themes: [PresentationThemeReference] public let themes: [PresentationThemeReference]
let animatedEmojiStickers: [String: [StickerPackItem]] public let animatedEmojiStickers: [String: [StickerPackItem]]
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
let nightMode: Bool public let nightMode: Bool
let currentTheme: PresentationThemeReference public let channelMode: Bool
let updatedTheme: (PresentationThemeReference) -> Void public let currentTheme: PresentationThemeReference
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)? public let updatedTheme: (PresentationThemeReference) -> Void
let tag: ItemListItemTag? public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
public let tag: ItemListItemTag?
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], animatedEmojiStickers: [String: [StickerPackItem]], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], nightMode: Bool, currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) { public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], animatedEmojiStickers: [String: [StickerPackItem]], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], nightMode: Bool, channelMode: Bool = false, currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
self.context = context self.context = context
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
@ -532,6 +544,7 @@ class ThemeCarouselThemeItem: ListViewItem, ItemListItem {
self.themeSpecificAccentColors = themeSpecificAccentColors self.themeSpecificAccentColors = themeSpecificAccentColors
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
self.nightMode = nightMode self.nightMode = nightMode
self.channelMode = channelMode
self.currentTheme = currentTheme self.currentTheme = currentTheme
self.updatedTheme = updatedTheme self.updatedTheme = updatedTheme
self.contextAction = contextAction self.contextAction = contextAction
@ -539,7 +552,7 @@ class ThemeCarouselThemeItem: ListViewItem, ItemListItem {
self.sectionId = sectionId self.sectionId = sectionId
} }
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) { public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
async { async {
let node = ThemeCarouselThemeItemNode() let node = ThemeCarouselThemeItemNode()
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
@ -555,7 +568,7 @@ class ThemeCarouselThemeItem: ListViewItem, ItemListItem {
} }
} }
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
Queue.mainQueue().async { Queue.mainQueue().async {
if let nodeValue = node() as? ThemeCarouselThemeItemNode { if let nodeValue = node() as? ThemeCarouselThemeItemNode {
let makeLayout = nodeValue.asyncLayout() let makeLayout = nodeValue.asyncLayout()
@ -571,6 +584,45 @@ class ThemeCarouselThemeItem: ListViewItem, ItemListItem {
} }
} }
} }
public func item() -> ListViewItem {
return self
}
public static func ==(lhs: ThemeCarouselThemeItem, rhs: ThemeCarouselThemeItem) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.strings !== rhs.strings {
return false
}
if lhs.themes != rhs.themes {
return false
}
if lhs.animatedEmojiStickers != rhs.animatedEmojiStickers {
return false
}
if lhs.themeSpecificAccentColors != rhs.themeSpecificAccentColors {
return false
}
if lhs.themeSpecificChatWallpapers != rhs.themeSpecificChatWallpapers {
return false
}
if lhs.nightMode != rhs.nightMode {
return false
}
if lhs.channelMode != rhs.channelMode {
return false
}
if lhs.currentTheme != rhs.currentTheme {
return false
}
return true
}
} }
private struct ThemeCarouselThemeItemNodeTransition { private struct ThemeCarouselThemeItemNodeTransition {
@ -609,7 +661,7 @@ private func ensureThemeVisible(listNode: ListView, themeReference: Presentation
} }
} }
class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { public class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
private let containerNode: ASDisplayNode private let containerNode: ASDisplayNode
private let backgroundNode: ASDisplayNode private let backgroundNode: ASDisplayNode
private let topStripeNode: ASDisplayNode private let topStripeNode: ASDisplayNode
@ -627,11 +679,11 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
private var tapping = false private var tapping = false
var tag: ItemListItemTag? { public var tag: ItemListItemTag? {
return self.item?.tag return self.item?.tag
} }
init() { public init() {
self.containerNode = ASDisplayNode() self.containerNode = ASDisplayNode()
self.backgroundNode = ASDisplayNode() self.backgroundNode = ASDisplayNode()
@ -654,7 +706,7 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
self.addSubnode(self.listNode) self.addSubnode(self.listNode)
} }
override func didLoad() { override public func didLoad() {
super.didLoad() super.didLoad()
self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true
} }
@ -695,7 +747,7 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
}) })
} }
func asyncLayout() -> (_ item: ThemeCarouselThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { public func asyncLayout() -> (_ item: ThemeCarouselThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
return { item, params, neighbors in return { item, params, neighbors in
let contentSize: CGSize let contentSize: CGSize
let insets: UIEdgeInsets let insets: UIEdgeInsets
@ -729,6 +781,12 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3) strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3)
} }
if params.isStandalone {
strongSelf.topStripeNode.isHidden = true
strongSelf.bottomStripeNode.isHidden = true
strongSelf.maskNode.isHidden = true
strongSelf.backgroundNode.isHidden = true
} else {
let hasCorners = itemListHasRoundedBlockLayout(params) let hasCorners = itemListHasRoundedBlockLayout(params)
var hasTopCorners = false var hasTopCorners = false
var hasBottomCorners = false var hasBottomCorners = false
@ -755,13 +813,16 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.bottomStripeNode.isHidden = true strongSelf.bottomStripeNode.isHidden = true
strongSelf.containerNode.frame = CGRect(x: 0.0, y: 0.0, width: contentSize.width, height: contentSize.height)
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
}
strongSelf.containerNode.frame = CGRect(x: 0.0, y: 0.0, width: contentSize.width, height: contentSize.height)
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
var listInsets = UIEdgeInsets() var listInsets = UIEdgeInsets()
listInsets.top += params.leftInset + 12.0 listInsets.top += params.leftInset + 12.0
@ -781,12 +842,12 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
hasCurrentTheme = true hasCurrentTheme = true
} }
let emojiFile = theme.emoticon.flatMap { item.animatedEmojiStickers[$0]?.first?.file } let emojiFile = theme.emoticon.flatMap { item.animatedEmojiStickers[$0]?.first?.file }
entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: emojiFile, themeReference: theme, nightMode: item.nightMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil)) entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: emojiFile, themeReference: theme, nightMode: item.nightMode, channelMode: item.channelMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil))
index += 1 index += 1
} }
if !hasCurrentTheme { if !hasCurrentTheme {
entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: nil, themeReference: item.currentTheme, nightMode: false, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: true, theme: item.theme, strings: item.strings, wallpaper: nil)) entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: nil, themeReference: item.currentTheme, nightMode: false, channelMode: item.channelMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: true, theme: item.theme, strings: item.strings, wallpaper: nil))
} }
let action: (PresentationThemeReference) -> Void = { [weak self] themeReference in let action: (PresentationThemeReference) -> Void = { [weak self] themeReference in
@ -810,15 +871,15 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
} }
} }
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
} }
override func animateRemoved(_ currentTimestamp: Double, duration: Double) { override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
} }
func prepareCrossfadeTransition() { public func prepareCrossfadeTransition() {
guard self.snapshotView == nil else { guard self.snapshotView == nil else {
return return
} }
@ -836,7 +897,7 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
} }
} }
func animateCrossfadeTransition() { public func animateCrossfadeTransition() {
guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else { guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else {
return return
} }

View File

@ -0,0 +1,33 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ThemeSettingsThemeItem",
module_name = "ThemeSettingsThemeItem",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display",
"//submodules/AsyncDisplayKit",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/Postbox",
"//submodules/TelegramCore",
"//submodules/TelegramPresentationData",
"//submodules/MergeLists",
"//submodules/TelegramUIPreferences",
"//submodules/ItemListUI",
"//submodules/PresentationDataUtils",
"//submodules/WallpaperResources",
"//submodules/AccountContext",
"//submodules/AppBundle",
"//submodules/ContextUI",
"//submodules/HexColor",
"//submodules/TelegramUI/Components/ListItemComponentAdaptor",
],
visibility = [
"//visibility:public",
],
)

View File

@ -14,6 +14,8 @@ import WallpaperResources
import AccountContext import AccountContext
import AppBundle import AppBundle
import ContextUI import ContextUI
import ListItemComponentAdaptor
import HexColor
private struct ThemeSettingsThemeEntry: Comparable, Identifiable { private struct ThemeSettingsThemeEntry: Comparable, Identifiable {
let index: Int let index: Int
@ -369,24 +371,24 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
} }
} }
class ThemeSettingsThemeItem: ListViewItem, ItemListItem { public class ThemeSettingsThemeItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
var sectionId: ItemListSectionId public var sectionId: ItemListSectionId
let context: AccountContext public let context: AccountContext
let theme: PresentationTheme public let theme: PresentationTheme
let strings: PresentationStrings public let strings: PresentationStrings
let themes: [PresentationThemeReference] public let themes: [PresentationThemeReference]
let allThemes: [PresentationThemeReference] public let allThemes: [PresentationThemeReference]
let displayUnsupported: Bool public let displayUnsupported: Bool
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
let themePreferredBaseTheme: [Int64: TelegramBaseTheme] public let themePreferredBaseTheme: [Int64: TelegramBaseTheme]
let currentTheme: PresentationThemeReference public let currentTheme: PresentationThemeReference
let updatedTheme: (PresentationThemeReference) -> Void public let updatedTheme: (PresentationThemeReference) -> Void
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)? public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
let tag: ItemListItemTag? public let tag: ItemListItemTag?
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], allThemes: [PresentationThemeReference], displayUnsupported: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], themePreferredBaseTheme: [Int64: TelegramBaseTheme], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) { public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], allThemes: [PresentationThemeReference], displayUnsupported: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], themePreferredBaseTheme: [Int64: TelegramBaseTheme], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
self.context = context self.context = context
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
@ -403,7 +405,7 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
self.sectionId = sectionId self.sectionId = sectionId
} }
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) { public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
async { async {
let node = ThemeSettingsThemeItemNode() let node = ThemeSettingsThemeItemNode()
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
@ -419,7 +421,7 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
} }
} }
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
Queue.mainQueue().async { Queue.mainQueue().async {
if let nodeValue = node() as? ThemeSettingsThemeItemNode { if let nodeValue = node() as? ThemeSettingsThemeItemNode {
let makeLayout = nodeValue.asyncLayout() let makeLayout = nodeValue.asyncLayout()
@ -435,6 +437,42 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
} }
} }
} }
public func item() -> ListViewItem {
return self
}
public static func ==(lhs: ThemeSettingsThemeItem, rhs: ThemeSettingsThemeItem) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.theme !== rhs.theme {
return false
}
if lhs.strings !== rhs.strings {
return false
}
if lhs.themes != rhs.themes {
return false
}
if lhs.displayUnsupported != rhs.displayUnsupported {
return false
}
if lhs.themeSpecificAccentColors != rhs.themeSpecificAccentColors {
return false
}
if lhs.themeSpecificChatWallpapers != rhs.themeSpecificChatWallpapers {
return false
}
if lhs.themePreferredBaseTheme != rhs.themePreferredBaseTheme {
return false
}
if lhs.currentTheme != rhs.currentTheme {
return false
}
return true
}
} }
private struct ThemeSettingsThemeItemNodeTransition { private struct ThemeSettingsThemeItemNodeTransition {
@ -472,7 +510,7 @@ private func ensureThemeVisible(listNode: ListView, themeReference: Presentation
} }
} }
class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { public class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
private let containerNode: ASDisplayNode private let containerNode: ASDisplayNode
private let backgroundNode: ASDisplayNode private let backgroundNode: ASDisplayNode
private let topStripeNode: ASDisplayNode private let topStripeNode: ASDisplayNode
@ -488,13 +526,13 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
private var item: ThemeSettingsThemeItem? private var item: ThemeSettingsThemeItem?
private var layoutParams: ListViewItemLayoutParams? private var layoutParams: ListViewItemLayoutParams?
var tag: ItemListItemTag? { public var tag: ItemListItemTag? {
return self.item?.tag return self.item?.tag
} }
private var tapping = false private var tapping = false
init() { public init() {
self.containerNode = ASDisplayNode() self.containerNode = ASDisplayNode()
self.backgroundNode = ASDisplayNode() self.backgroundNode = ASDisplayNode()
@ -517,7 +555,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
self.addSubnode(self.listNode) self.addSubnode(self.listNode)
} }
override func didLoad() { override public func didLoad() {
super.didLoad() super.didLoad()
self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true
} }
@ -558,7 +596,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
}) })
} }
func asyncLayout() -> (_ item: ThemeSettingsThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { public func asyncLayout() -> (_ item: ThemeSettingsThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
return { item, params, neighbors in return { item, params, neighbors in
let contentSize: CGSize let contentSize: CGSize
let insets: UIEdgeInsets let insets: UIEdgeInsets
@ -592,6 +630,12 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3) strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3)
} }
if params.isStandalone {
strongSelf.topStripeNode.isHidden = true
strongSelf.bottomStripeNode.isHidden = true
strongSelf.maskNode.isHidden = true
strongSelf.backgroundNode.isHidden = true
} else {
let hasCorners = itemListHasRoundedBlockLayout(params) let hasCorners = itemListHasRoundedBlockLayout(params)
var hasTopCorners = false var hasTopCorners = false
var hasBottomCorners = false var hasBottomCorners = false
@ -616,13 +660,16 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
strongSelf.bottomStripeNode.isHidden = hasCorners strongSelf.bottomStripeNode.isHidden = hasCorners
} }
strongSelf.containerNode.frame = CGRect(x: 0.0, y: 0.0, width: contentSize.width, height: contentSize.height)
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
}
strongSelf.containerNode.frame = CGRect(x: 0.0, y: 0.0, width: contentSize.width, height: contentSize.height)
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
var listInsets = UIEdgeInsets() var listInsets = UIEdgeInsets()
listInsets.top += params.leftInset + 4.0 listInsets.top += params.leftInset + 4.0
@ -694,15 +741,15 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
} }
} }
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
} }
override func animateRemoved(_ currentTimestamp: Double, duration: Double) { override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
} }
func prepareCrossfadeTransition() { public func prepareCrossfadeTransition() {
guard self.snapshotView == nil else { guard self.snapshotView == nil else {
return return
} }
@ -719,7 +766,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
} }
} }
func animateCrossfadeTransition() { public func animateCrossfadeTransition() {
guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else { guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else {
return return
} }

View File

@ -604,7 +604,7 @@ public final class PeerListItemComponent: Component {
statusIcon = .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_ScamAccount.uppercased()) statusIcon = .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_ScamAccount.uppercased())
} else if peer.isFake { } else if peer.isFake {
statusIcon = .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_FakeAccount.uppercased()) statusIcon = .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_FakeAccount.uppercased())
} else if case let .user(user) = peer, let emojiStatus = user.emojiStatus { } else if let emojiStatus = peer.emojiStatus {
statusIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: component.theme.list.mediaPlaceholderColor, themeColor: component.theme.list.itemAccentColor, loopMode: .count(2)) statusIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: component.theme.list.mediaPlaceholderColor, themeColor: component.theme.list.itemAccentColor, loopMode: .count(2))
} else if peer.isVerified { } else if peer.isVerified {
statusIcon = .verified(fillColor: component.theme.list.itemCheckColors.fillColor, foregroundColor: component.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) statusIcon = .verified(fillColor: component.theme.list.itemCheckColors.fillColor, foregroundColor: component.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)

View File

@ -368,6 +368,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer() self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer()
self.titleAccessoryPanelContainer.clipsToBounds = true self.titleAccessoryPanelContainer.clipsToBounds = true
setLayerDisableScreenshots(self.titleAccessoryPanelContainer.layer, chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat)
self.inputContextPanelContainer = ChatControllerTitlePanelNodeContainer() self.inputContextPanelContainer = ChatControllerTitlePanelNodeContainer()
self.inputContextOverTextPanelContainer = ChatControllerTitlePanelNodeContainer() self.inputContextOverTextPanelContainer = ChatControllerTitlePanelNodeContainer()
@ -1012,7 +1014,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
} }
if let historyNodeContainer = self.historyNodeContainer as? HistoryNodeContainer { if let historyNodeContainer = self.historyNodeContainer as? HistoryNodeContainer {
historyNodeContainer.isSecret = self.chatPresentationInterfaceState.copyProtectionEnabled || self.chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat let isSecret = self.chatPresentationInterfaceState.copyProtectionEnabled || self.chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat
if historyNodeContainer.isSecret != isSecret {
historyNodeContainer.isSecret = isSecret
setLayerDisableScreenshots(self.titleAccessoryPanelContainer.layer, isSecret)
}
} }
var previousListBottomInset: CGFloat? var previousListBottomInset: CGFloat?

View File

@ -540,6 +540,11 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
} else { } else {
emojiStatus = emojiStatusValue emojiStatus = emojiStatusValue
} }
} else if let channel = interfaceState.renderedPeer?.peer as? TelegramChannel, let emojiStatusValue = channel.emojiStatus {
if channel.isFake || channel.isScam {
} else {
emojiStatus = emojiStatusValue
}
} }
/*#if DEBUG /*#if DEBUG

View File

@ -363,6 +363,18 @@ public final class SharedAccountContextImpl: SharedAccountContext {
} }
if themeUpdated { if themeUpdated {
updateLegacyTheme() updateLegacyTheme()
/*if #available(iOS 13.0, *) {
let userInterfaceStyle: UIUserInterfaceStyle
if strongSelf.currentPresentationData.with({ $0 }).theme.overallDarkAppearance {
userInterfaceStyle = .dark
} else {
userInterfaceStyle = .light
}
if let eventView = strongSelf.mainWindow?.hostView.eventView, eventView.overrideUserInterfaceStyle != userInterfaceStyle {
eventView.overrideUserInterfaceStyle = userInterfaceStyle
}
}*/
} }
if themeNameUpdated { if themeNameUpdated {
strongSelf.presentCrossfadeController() strongSelf.presentCrossfadeController()
@ -962,6 +974,18 @@ public final class SharedAccountContextImpl: SharedAccountContext {
} }
}) })
} }
/*if #available(iOS 13.0, *) {
let userInterfaceStyle: UIUserInterfaceStyle
if self.currentPresentationData.with({ $0 }).theme.overallDarkAppearance {
userInterfaceStyle = .dark
} else {
userInterfaceStyle = .light
}
if let eventView = self.mainWindow?.hostView.eventView, eventView.overrideUserInterfaceStyle != userInterfaceStyle {
eventView.overrideUserInterfaceStyle = userInterfaceStyle
}
}*/
} }
deinit { deinit {

View File

@ -1398,7 +1398,7 @@ private let messageImage: UIImage = {
return messageBubbleImage(maxCornerRadius: 16.0, minCornerRadius: 16.0, incoming: true, fillColor: .white, strokeColor: .clear, neighbors: .none, shadow: nil, wallpaper: .color(0x000000), knockout: false) return messageBubbleImage(maxCornerRadius: 16.0, minCornerRadius: 16.0, incoming: true, fillColor: .white, strokeColor: .clear, neighbors: .none, shadow: nil, wallpaper: .color(0x000000), knockout: false)
}() }()
public func themeIconImage(account: Account, accountManager: AccountManager<TelegramAccountManagerTypes>, theme: PresentationThemeReference, color: PresentationThemeAccentColor?, wallpaper: TelegramWallpaper? = nil, nightMode: Bool? = nil, emoticon: Bool = false, large: Bool = false, qr: Bool = false, message: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { public func themeIconImage(account: Account, accountManager: AccountManager<TelegramAccountManagerTypes>, theme: PresentationThemeReference, color: PresentationThemeAccentColor?, wallpaper: TelegramWallpaper? = nil, nightMode: Bool? = nil, channelMode: Bool? = nil, emoticon: Bool = false, large: Bool = false, qr: Bool = false, message: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
let colorsSignal: Signal<((UIColor, UIColor?, [UInt32]), [UIColor], [UIColor], UIImage?, Bool, Bool, CGFloat, Int32?), NoError> let colorsSignal: Signal<((UIColor, UIColor?, [UInt32]), [UIColor], [UIColor], UIImage?, Bool, Bool, CGFloat, Int32?), NoError>
var reference: MediaResourceReference? var reference: MediaResourceReference?
@ -1693,6 +1693,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
c.translateBy(x: -114.0, y: -32.0) c.translateBy(x: -114.0, y: -32.0)
let _ = try? drawSvgPath(c, path: "M12.8304,29.8712 C10.0551,31.8416 6.6628,33 2.99998,33 C1.98426,33 0.989361,32.9109 0.022644,32.7402 C2.97318,31.9699 5.24596,29.5785 5.84625,26.5607 C5.99996,25.7879 5.99996,24.8586 5.99996,23 V16.0 H6.00743 C6.27176,7.11861 13.5546,0 22.5,0 H61.5 C70.6127,0 78,7.3873 78,16.5 C78,25.6127 70.6127,33 61.5,33 H22.5 C18.8883,33 15.5476,31.8396 12.8304,29.8712 ") let _ = try? drawSvgPath(c, path: "M12.8304,29.8712 C10.0551,31.8416 6.6628,33 2.99998,33 C1.98426,33 0.989361,32.9109 0.022644,32.7402 C2.97318,31.9699 5.24596,29.5785 5.84625,26.5607 C5.99996,25.7879 5.99996,24.8586 5.99996,23 V16.0 H6.00743 C6.27176,7.11861 13.5546,0 22.5,0 H61.5 C70.6127,0 78,7.3873 78,16.5 C78,25.6127 70.6127,33 61.5,33 H22.5 C18.8883,33 15.5476,31.8396 12.8304,29.8712 ")
if Set(incomingColors.map(\.rgb)).count > 1 { if Set(incomingColors.map(\.rgb)).count > 1 {
c.clip() c.clip()
@ -1748,7 +1749,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0) c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
} }
let outgoingColors = colors.2 let outgoingColors = channelMode == true ? colors.1 : colors.2
if emoticon { if emoticon {
if large { if large {
c.saveGState() c.saveGState()
@ -1780,7 +1781,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
c.restoreGState() c.restoreGState()
} else { } else {
let rect = CGRect(x: 8.0, y: 72.0, width: 48.0, height: 24.0) let rect = CGRect(x: 8.0, y: arguments.drawingSize.height - 24.0 - 9.0 - 3.0, width: arguments.drawingSize.width - 8.0 * 2.0, height: 24.0)
c.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 12.0).cgPath) c.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 12.0).cgPath)
c.clip() c.clip()

View File

@ -49,7 +49,9 @@
} }
_frameCount = (int32_t)_animation->totalFrame(); _frameCount = (int32_t)_animation->totalFrame();
_frameCount = MAX(1, _frameCount);
_frameRate = (int32_t)_animation->frameRate(); _frameRate = (int32_t)_animation->frameRate();
_frameRate = MAX(1, _frameRate);
size_t width = 0; size_t width = 0;
size_t height = 0; size_t height = 0;
@ -59,6 +61,9 @@
return nil; return nil;
} }
width = MAX(1, width);
height = MAX(1, height);
_dimensions = CGSizeMake(width, height); _dimensions = CGSizeMake(width, height);
if ((_frameRate > 360) || _animation->duration() > 9.0) { if ((_frameRate > 360) || _animation->duration() > 9.0) {