mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
6e3463bfa6
@ -592,9 +592,9 @@ public final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
||||
return nil
|
||||
}
|
||||
self.animation = animation
|
||||
let frameCount = Int(animation.frameCount)
|
||||
let frameCount = max(1, Int(animation.frameCount))
|
||||
self.frameCount = frameCount
|
||||
self.frameRate = Int(animation.frameRate)
|
||||
self.frameRate = max(1, Int(animation.frameRate))
|
||||
|
||||
self.cache = cachePathPrefix.flatMap { cachePathPrefix in
|
||||
AnimatedStickerDirectFrameSourceCache(queue: queue, pathPrefix: cachePathPrefix, width: width, height: height, frameCount: frameCount, fitzModifier: fitzModifier, useHardware: useMetalCache)
|
||||
|
@ -903,10 +903,17 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
guard let publicToken = nativeParams["public_token"] as? String else {
|
||||
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)?
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ struct BotCheckoutNativeCardEntryAdditionalFields: OptionSet {
|
||||
final class BotCheckoutNativeCardEntryController: ViewController {
|
||||
enum Provider {
|
||||
case stripe(additionalFields: BotCheckoutNativeCardEntryAdditionalFields, publishableKey: String)
|
||||
case smartglobal(isTesting: Bool, publicToken: String)
|
||||
case smartglobal(isTesting: Bool, publicToken: String, customTokenizeUrl: String?)
|
||||
}
|
||||
|
||||
private var controllerNode: BotCheckoutNativeCardEntryControllerNode {
|
||||
|
@ -310,9 +310,11 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode,
|
||||
}))
|
||||
|
||||
self.updateDone()
|
||||
case let .smartglobal(isTesting, publicToken):
|
||||
case let .smartglobal(isTesting, publicToken, customTokenizeUrl):
|
||||
let url: String
|
||||
if isTesting {
|
||||
if let customTokenizeUrl {
|
||||
url = customTokenizeUrl
|
||||
} else if isTesting {
|
||||
url = "https://tgb-playground.smart-glocal.com/cds/v1/tokenize/card"
|
||||
} else {
|
||||
url = "https://tgb.smart-glocal.com/cds/v1/tokenize/card"
|
||||
|
@ -2477,7 +2477,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased())
|
||||
} else if peer.isFake {
|
||||
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))
|
||||
} else if peer.isVerified {
|
||||
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())
|
||||
} else if peer.isFake {
|
||||
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))
|
||||
} else if peer.isVerified {
|
||||
currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
|
||||
|
@ -609,7 +609,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
currentCredibilityIcon = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_ScamAccount.uppercased())
|
||||
} else if item.peer.isFake {
|
||||
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))
|
||||
} else if item.peer.isVerified {
|
||||
currentCredibilityIcon = .verified(fillColor: presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
|
||||
|
@ -225,12 +225,25 @@ open class ViewControllerComponentContainer: ViewController {
|
||||
|
||||
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.component = AnyComponent(component)
|
||||
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?
|
||||
switch navigationBarAppearance {
|
||||
@ -243,7 +256,7 @@ open class ViewControllerComponentContainer: ViewController {
|
||||
}
|
||||
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
|
||||
if let strongSelf = self {
|
||||
var theme = presentationData.theme
|
||||
@ -266,6 +279,19 @@ open class ViewControllerComponentContainer: ViewController {
|
||||
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 {
|
||||
strongSelf.containerLayoutUpdated(layout, transition: .immediate)
|
||||
}
|
||||
|
@ -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())
|
||||
} else if peer.isFake {
|
||||
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))
|
||||
} else if peer.isVerified {
|
||||
credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
|
||||
|
@ -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? {
|
||||
return generateImage(size, contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(color.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
}, scale: scale)
|
||||
|
@ -52,17 +52,19 @@ public enum ListViewItemNodeVisibility: Equatable {
|
||||
case visible(CGFloat, CGRect)
|
||||
}
|
||||
|
||||
public struct ListViewItemLayoutParams {
|
||||
public struct ListViewItemLayoutParams: Equatable {
|
||||
public let width: CGFloat
|
||||
public let leftInset: CGFloat
|
||||
public let rightInset: 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.leftInset = leftInset
|
||||
self.rightInset = rightInset
|
||||
self.availableHeight = availableHeight
|
||||
self.isStandalone = isStandalone
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
} else if item.peer.isFake {
|
||||
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))
|
||||
} else if item.peer.isVerified {
|
||||
credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
|
||||
|
@ -122,6 +122,10 @@ public func itemListNeighborsPlainInsets(_ neighbors: ItemListNeighbors) -> UIEd
|
||||
}
|
||||
|
||||
public func itemListNeighborsGroupedInsets(_ neighbors: ItemListNeighbors, _ params: ListViewItemLayoutParams) -> UIEdgeInsets {
|
||||
if params.isStandalone {
|
||||
return UIEdgeInsets()
|
||||
}
|
||||
|
||||
let topInset: CGFloat
|
||||
switch neighbors.top {
|
||||
case .none:
|
||||
|
@ -599,7 +599,12 @@ private final class VariableBlurView: UIVisualEffectView {
|
||||
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() {
|
||||
|
@ -589,6 +589,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
strings: strongSelf.presentationData.strings,
|
||||
deviceMetrics: DeviceMetrics.iPhone13,
|
||||
emojiContent: emojiContent,
|
||||
color: nil,
|
||||
backgroundColor: .clear,
|
||||
separatorColor: strongSelf.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5),
|
||||
hideTopPanel: hideTopPanel,
|
||||
@ -1215,6 +1216,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
strings: self.presentationData.strings,
|
||||
deviceMetrics: DeviceMetrics.iPhone13,
|
||||
emojiContent: emojiContent,
|
||||
color: nil,
|
||||
backgroundColor: .clear,
|
||||
separatorColor: self.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5),
|
||||
hideTopPanel: hideTopPanel,
|
||||
|
@ -117,6 +117,8 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/Settings/PeerNameColorScreen",
|
||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||
"//submodules/TelegramUI/Components/Settings/QuickReactionSetupController",
|
||||
"//submodules/TelegramUI/Components/Settings/ThemeCarouselItem",
|
||||
"//submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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 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 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 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 timestamp = self.referenceTimestamp
|
||||
|
@ -19,6 +19,7 @@ import ContextUI
|
||||
import UndoUI
|
||||
import ItemListPeerActionItem
|
||||
import AnimationUI
|
||||
import ThemeSettingsThemeItem
|
||||
|
||||
private final class ThemePickerControllerArguments {
|
||||
let context: AccountContext
|
||||
|
@ -18,7 +18,7 @@ import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import ShimmerEffect
|
||||
import StickerResources
|
||||
|
||||
import ThemeCarouselItem
|
||||
|
||||
private var cachedBorderImages: [String: UIImage] = [:]
|
||||
private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selected: Bool) -> UIImage? {
|
||||
@ -500,7 +500,7 @@ class ThemeGridThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
for theme in item.themes {
|
||||
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)
|
||||
}, contextAction: nil)
|
||||
|
||||
|
@ -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 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 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 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))
|
||||
|
||||
|
@ -14,6 +14,7 @@ import DeviceLocationManager
|
||||
import Geocoding
|
||||
import WallpaperResources
|
||||
import Sunrise
|
||||
import ThemeSettingsThemeItem
|
||||
|
||||
private enum TriggerMode {
|
||||
case system
|
||||
|
@ -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 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 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 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 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))
|
||||
|
||||
|
@ -19,32 +19,7 @@ import ContextUI
|
||||
import UndoUI
|
||||
import PremiumUI
|
||||
import PeerNameColorScreen
|
||||
|
||||
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
|
||||
}
|
||||
import ThemeCarouselItem
|
||||
|
||||
private final class ThemeSettingsControllerArguments {
|
||||
let context: AccountContext
|
||||
|
@ -1262,7 +1262,7 @@ public extension Api {
|
||||
return parser(reader)
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -839,7 +839,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
||||
credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased())
|
||||
} else if item.peer.isFake {
|
||||
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))
|
||||
} else if item.peer.isVerified {
|
||||
credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
|
||||
|
@ -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)
|
||||
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)
|
||||
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 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):
|
||||
let info: TelegramChannelInfo
|
||||
if (flags & Int32(1 << 8)) != 0 {
|
||||
@ -182,7 +182,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
|
||||
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 {
|
||||
case .chat, .chatEmpty, .chatForbidden, .channelForbidden:
|
||||
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
|
||||
if accessHash != nil && !isMin {
|
||||
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 {
|
||||
return parseTelegramGroupOrChannel(chat: rhs)
|
||||
}
|
||||
@ -306,6 +308,6 @@ func mergeChannel(lhs: TelegramChannel?, rhs: TelegramChannel) -> TelegramChanne
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -1876,6 +1876,12 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po
|
||||
if let value = contents.media {
|
||||
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(
|
||||
stableId: 1,
|
||||
stableVersion: 0,
|
||||
@ -1892,7 +1898,7 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po
|
||||
forwardInfo: nil,
|
||||
author: nil,
|
||||
text: contents.text,
|
||||
attributes: contents.attributes,
|
||||
attributes: attributes,
|
||||
media: media,
|
||||
peers: SimpleDictionary(),
|
||||
associatedMessages: SimpleDictionary(),
|
||||
@ -1967,7 +1973,7 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po
|
||||
}
|
||||
|
||||
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(
|
||||
peerId: peerId,
|
||||
|
@ -30,6 +30,9 @@ public struct StickerPackCollectionInfoFlags: OptionSet {
|
||||
if flags.contains(StickerPackCollectionInfoFlags.isEmoji) {
|
||||
rawValue |= StickerPackCollectionInfoFlags.isEmoji.rawValue
|
||||
}
|
||||
if flags.contains(StickerPackCollectionInfoFlags.isAvailableAsChannelStatus) {
|
||||
rawValue |= StickerPackCollectionInfoFlags.isAvailableAsChannelStatus.rawValue
|
||||
}
|
||||
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
@ -39,6 +42,7 @@ public struct StickerPackCollectionInfoFlags: OptionSet {
|
||||
public static let isAnimated = StickerPackCollectionInfoFlags(rawValue: 1 << 2)
|
||||
public static let isVideo = StickerPackCollectionInfoFlags(rawValue: 1 << 3)
|
||||
public static let isEmoji = StickerPackCollectionInfoFlags(rawValue: 1 << 4)
|
||||
public static let isAvailableAsChannelStatus = StickerPackCollectionInfoFlags(rawValue: 1 << 5)
|
||||
}
|
||||
|
||||
|
||||
|
@ -171,6 +171,8 @@ public final class TelegramChannel: Peer, Equatable {
|
||||
public let backgroundEmojiId: Int64?
|
||||
public let profileColor: PeerNameColor?
|
||||
public let profileBackgroundEmojiId: Int64?
|
||||
public let emojiStatus: PeerEmojiStatus?
|
||||
public let approximateBoostLevel: Int32?
|
||||
|
||||
public var indexName: PeerIndexNameRepresentation {
|
||||
var addressNames = self.usernames.map { $0.username }
|
||||
@ -181,8 +183,19 @@ public final class TelegramChannel: Peer, Equatable {
|
||||
}
|
||||
|
||||
public var associatedMediaIds: [MediaId]? {
|
||||
if let backgroundEmojiId = self.backgroundEmojiId {
|
||||
return [MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId)]
|
||||
if let emojiStatus = self.emojiStatus, let backgroundEmojiId = self.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 {
|
||||
return nil
|
||||
}
|
||||
@ -191,7 +204,17 @@ public final class TelegramChannel: Peer, Equatable {
|
||||
public let associatedPeerId: 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(
|
||||
id: PeerId,
|
||||
@ -213,7 +236,9 @@ public final class TelegramChannel: Peer, Equatable {
|
||||
nameColor: PeerNameColor?,
|
||||
backgroundEmojiId: Int64?,
|
||||
profileColor: PeerNameColor?,
|
||||
profileBackgroundEmojiId: Int64?
|
||||
profileBackgroundEmojiId: Int64?,
|
||||
emojiStatus: PeerEmojiStatus?,
|
||||
approximateBoostLevel: Int32?
|
||||
) {
|
||||
self.id = id
|
||||
self.accessHash = accessHash
|
||||
@ -235,6 +260,8 @@ public final class TelegramChannel: Peer, Equatable {
|
||||
self.backgroundEmojiId = backgroundEmojiId
|
||||
self.profileColor = profileColor
|
||||
self.profileBackgroundEmojiId = profileBackgroundEmojiId
|
||||
self.emojiStatus = emojiStatus
|
||||
self.approximateBoostLevel = approximateBoostLevel
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
@ -268,6 +295,8 @@ public final class TelegramChannel: Peer, Equatable {
|
||||
self.backgroundEmojiId = decoder.decodeOptionalInt64ForKey("bgem")
|
||||
self.profileColor = decoder.decodeOptionalInt32ForKey("pclr").flatMap { PeerNameColor(rawValue: $0) }
|
||||
self.profileBackgroundEmojiId = decoder.decodeOptionalInt64ForKey("pgem")
|
||||
self.emojiStatus = decoder.decode(PeerEmojiStatus.self, forKey: "emjs")
|
||||
self.approximateBoostLevel = decoder.decodeOptionalInt32ForKey("abl")
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
@ -347,6 +376,18 @@ public final class TelegramChannel: Peer, Equatable {
|
||||
} else {
|
||||
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 {
|
||||
@ -399,43 +440,57 @@ public final class TelegramChannel: Peer, Equatable {
|
||||
if lhs.profileBackgroundEmojiId != rhs.profileBackgroundEmojiId {
|
||||
return false
|
||||
}
|
||||
if lhs.emojiStatus != rhs.emojiStatus {
|
||||
return false
|
||||
}
|
||||
if lhs.approximateBoostLevel != rhs.approximateBoostLevel {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -315,7 +315,9 @@ private class AdMessagesHistoryContextImpl {
|
||||
nameColor: invite.nameColor,
|
||||
backgroundEmojiId: nil,
|
||||
profileColor: nil,
|
||||
profileBackgroundEmojiId: nil
|
||||
profileBackgroundEmojiId: nil,
|
||||
emojiStatus: nil,
|
||||
approximateBoostLevel: nil
|
||||
)
|
||||
case let .webPage(webPage):
|
||||
author = TelegramChannel(
|
||||
@ -338,7 +340,9 @@ private class AdMessagesHistoryContextImpl {
|
||||
nameColor: .blue,
|
||||
backgroundEmojiId: nil,
|
||||
profileColor: nil,
|
||||
profileBackgroundEmojiId: nil
|
||||
profileBackgroundEmojiId: nil,
|
||||
emojiStatus: nil,
|
||||
approximateBoostLevel: nil
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1423,6 +1423,13 @@ public func _internal_pollPeerStories(postbox: Postbox, network: Network, accoun
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if "".isEmpty {
|
||||
return .complete()
|
||||
}
|
||||
#endif
|
||||
|
||||
return network.request(Api.functions.stories.getPeerStories(peer: inputPeer))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.stories.PeerStories?, NoError> in
|
||||
|
@ -514,6 +514,10 @@ public extension EnginePeer {
|
||||
return self._asPeer().profileColor
|
||||
}
|
||||
|
||||
var emojiStatus: PeerEmojiStatus? {
|
||||
return self._asPeer().emojiStatus
|
||||
}
|
||||
|
||||
var backgroundEmojiId: Int64? {
|
||||
return self._asPeer().backgroundEmojiId
|
||||
}
|
||||
|
@ -718,6 +718,10 @@ public extension TelegramEngine {
|
||||
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> {
|
||||
return self.account.postbox.transaction { transaction -> [EnginePeer] in
|
||||
return transaction.getChatListPeers(groupId: .root, filterPredicate: filterPredicate, additionalFilter: nil).map(EnginePeer.init)
|
||||
|
@ -100,9 +100,9 @@ func _internal_updatePeerNameColorAndEmoji(account: Account, peerId: EnginePeer.
|
||||
if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) {
|
||||
let flagsReplies: Int32 = (1 << 0) | (1 << 2)
|
||||
|
||||
var flagsProfile: Int32 = (1 << 0) | (1 << 2)
|
||||
var flagsProfile: Int32 = (1 << 0) | (1 << 1)
|
||||
if profileColor != nil {
|
||||
flagsProfile |= (1 << 1)
|
||||
flagsProfile |= (1 << 2)
|
||||
}
|
||||
|
||||
return combineLatest(
|
||||
@ -161,3 +161,45 @@ func _internal_updatePeerNameColorAndEmoji(account: Account, peerId: EnginePeer.
|
||||
|> castError(UpdatePeerNameColorAndEmojiError.self)
|
||||
|> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,9 @@ extension StickerPackCollectionInfo {
|
||||
if (flags & (1 << 7)) != 0 {
|
||||
setFlags.insert(.isEmoji)
|
||||
}
|
||||
if (flags & (1 << 10)) != 0 {
|
||||
setFlags.insert(.isAvailableAsChannelStatus)
|
||||
}
|
||||
|
||||
var thumbnailRepresentation: TelegramMediaImageRepresentation?
|
||||
var immediateThumbnailData: Data?
|
||||
|
@ -255,6 +255,17 @@ public extension Peer {
|
||||
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? {
|
||||
switch self {
|
||||
case let user as TelegramUser:
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -1953,11 +1953,14 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
authorNameColor = color
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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))
|
||||
} else if effectiveAuthor.isVerified {
|
||||
currentCredibilityIcon = .verified(fillColor: item.presentationData.theme.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
|
||||
|
@ -69,7 +69,7 @@ private func filterOriginalMessageFlags(_ message: Message) -> Message {
|
||||
|
||||
private func filterMessageChannelPeer(_ peer: Peer) -> Peer {
|
||||
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
|
||||
}
|
||||
|
@ -334,7 +334,6 @@ private final class LineView: UIView {
|
||||
|
||||
let _ = previousParams
|
||||
|
||||
|
||||
self.backgroundView.tintColor = primaryColor
|
||||
|
||||
if let secondaryColor {
|
||||
@ -406,6 +405,10 @@ private final class LineView: UIView {
|
||||
self.dashBackgroundView = nil
|
||||
dashBackgroundView.removeFromSuperview()
|
||||
}
|
||||
if let dashThirdBackgroundView = self.dashThirdBackgroundView {
|
||||
self.dashThirdBackgroundView = nil
|
||||
dashThirdBackgroundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
self.backgroundView.alpha = 1.0
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
titleCredibilityIcon = .fake
|
||||
} else if peer.isScam {
|
||||
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)
|
||||
} else if peer.isVerified {
|
||||
titleCredibilityIcon = .verified
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
@ -64,6 +64,7 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
public let emojiContent: EmojiPagerContentComponent
|
||||
public let backgroundColor: UIColor
|
||||
public let separatorColor: UIColor
|
||||
public let color: UIColor?
|
||||
public let hideTopPanel: Bool
|
||||
public let disableTopPanel: Bool
|
||||
public let hideTopPanelUpdated: (Bool, Transition) -> Void
|
||||
@ -73,6 +74,7 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
strings: PresentationStrings,
|
||||
deviceMetrics: DeviceMetrics,
|
||||
emojiContent: EmojiPagerContentComponent,
|
||||
color: UIColor?,
|
||||
backgroundColor: UIColor,
|
||||
separatorColor: UIColor,
|
||||
hideTopPanel: Bool,
|
||||
@ -83,6 +85,7 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
self.strings = strings
|
||||
self.deviceMetrics = deviceMetrics
|
||||
self.emojiContent = emojiContent
|
||||
self.color = color
|
||||
self.backgroundColor = backgroundColor
|
||||
self.separatorColor = separatorColor
|
||||
self.hideTopPanel = hideTopPanel
|
||||
@ -103,6 +106,9 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
if lhs.emojiContent != rhs.emojiContent {
|
||||
return false
|
||||
}
|
||||
if lhs.color != rhs.color {
|
||||
return false
|
||||
}
|
||||
if lhs.backgroundColor != rhs.backgroundColor {
|
||||
return false
|
||||
}
|
||||
@ -169,7 +175,7 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
isContentInFocus: true,
|
||||
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),
|
||||
emojiContent: component.emojiContent,
|
||||
emojiContent: component.emojiContent.withCustomTintColor(component.color),
|
||||
stickerContent: nil,
|
||||
maskContent: nil,
|
||||
gifContent: nil,
|
||||
@ -200,7 +206,8 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
isExpanded: false,
|
||||
clipContentToTopPanel: false,
|
||||
useExternalSearchContainer: false,
|
||||
hidePanels: component.disableTopPanel
|
||||
hidePanels: component.disableTopPanel,
|
||||
customTintColor: component.color
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
@ -277,6 +284,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private let currentSelection: Int64?
|
||||
private let color: UIColor?
|
||||
|
||||
private var emojiContentDisposable: Disposable?
|
||||
private var emojiContent: EmojiPagerContentComponent?
|
||||
@ -312,10 +320,11 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
|
||||
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.context = context
|
||||
self.currentSelection = currentSelection
|
||||
self.color = color
|
||||
|
||||
if let sourceView = sourceView {
|
||||
self.globalSourceRect = sourceView.convert(sourceView.bounds, to: nil)
|
||||
@ -826,17 +835,26 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
renderer: animationRenderer,
|
||||
placeholderColor: UIColor(white: 0.0, alpha: 0.0),
|
||||
pointSize: CGSize(width: 32.0, height: 32.0),
|
||||
dynamicColor: self.presentationData.theme.list.itemAccentColor
|
||||
dynamicColor: self.color ?? self.presentationData.theme.list.itemAccentColor
|
||||
)
|
||||
switch item.tintMode {
|
||||
case let .custom(color):
|
||||
baseItemLayer.contentTintColor = color
|
||||
case .accent:
|
||||
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemAccentColor
|
||||
case .primary:
|
||||
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemPrimaryTextColor
|
||||
case .none:
|
||||
break
|
||||
if let color = self.color {
|
||||
switch item.tintMode {
|
||||
case .none:
|
||||
break
|
||||
default:
|
||||
baseItemLayer.contentTintColor = color
|
||||
}
|
||||
} else {
|
||||
switch item.tintMode {
|
||||
case let .custom(color):
|
||||
baseItemLayer.contentTintColor = color
|
||||
case .accent:
|
||||
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemAccentColor
|
||||
case .primary:
|
||||
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemPrimaryTextColor
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let sublayers = animationLayer.sublayers {
|
||||
@ -988,6 +1006,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
strings: self.presentationData.strings,
|
||||
deviceMetrics: layout.deviceMetrics,
|
||||
emojiContent: emojiContent,
|
||||
color: self.color,
|
||||
backgroundColor: listBackgroundColor,
|
||||
separatorColor: separatorColor,
|
||||
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))
|
||||
}
|
||||
|
||||
var componentFrame: CGRect
|
||||
let pointsToTop: Bool
|
||||
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 {
|
||||
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 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
|
||||
if cloudFrame0.maxX >= layout.size.width - layout.safeInsets.right - 32.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 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 {
|
||||
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 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 {
|
||||
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.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)
|
||||
self.componentShadowLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.04, delay: contentDelay)
|
||||
|
||||
@ -1264,7 +1300,17 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
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) {
|
||||
snapshotView.frame = destinationView.frame
|
||||
destinationView.superview?.insertSubview(snapshotView, belowSubview: destinationView)
|
||||
@ -1279,6 +1325,10 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
case .statusSelection:
|
||||
let _ = (self.context.engine.accountData.setEmojiStatus(file: item?.itemFile, expirationDate: nil)
|
||||
|> deliverOnMainQueue).start()
|
||||
case let .backgroundSelection(completion):
|
||||
completion(item?.itemFile)
|
||||
case let .customStatusSelection(completion):
|
||||
completion(item?.itemFile, nil)
|
||||
case let .quickReactionSelection(completion):
|
||||
if let item = item, let itemFile = item.itemFile {
|
||||
var selectedReaction: MessageReaction.Reaction?
|
||||
@ -1304,7 +1354,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
completion()
|
||||
}
|
||||
|
||||
if let item = item, let destinationView = controller.destinationItemView() {
|
||||
if animateOutToView, let item = item, let destinationView = controller.destinationItemView() {
|
||||
var emojiString: String?
|
||||
if let itemFile = item.itemFile {
|
||||
attributeLoop: for attribute in itemFile.attributes {
|
||||
@ -1364,6 +1414,8 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
|
||||
public enum Mode {
|
||||
case statusSelection
|
||||
case backgroundSelection(completion: (TelegramMediaFile?) -> Void)
|
||||
case customStatusSelection(completion: (TelegramMediaFile?, Int32?) -> Void)
|
||||
case quickReactionSelection(completion: () -> Void)
|
||||
}
|
||||
|
||||
@ -1371,6 +1423,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
private weak var sourceView: UIView?
|
||||
private let emojiContent: Signal<EmojiPagerContentComponent, NoError>
|
||||
private let currentSelection: Int64?
|
||||
private let color: UIColor?
|
||||
private let mode: Mode
|
||||
private let destinationItemView: () -> UIView?
|
||||
|
||||
@ -1383,12 +1436,13 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
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.mode = mode
|
||||
self.sourceView = sourceView
|
||||
self.emojiContent = emojiContent
|
||||
self.currentSelection = currentSelection
|
||||
self.color = color
|
||||
self.destinationItemView = destinationItemView
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
@ -1418,7 +1472,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
@ -7188,6 +7188,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
public enum Subject: Equatable {
|
||||
case generic
|
||||
case status
|
||||
case channelStatus
|
||||
case reaction(onlyTop: Bool)
|
||||
case emoji
|
||||
case topicIcon
|
||||
@ -7239,6 +7240,19 @@ public final class EmojiPagerContentComponent: Component {
|
||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji)
|
||||
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)
|
||||
|> map { result -> [TelegramMediaFile] in
|
||||
switch result {
|
||||
@ -7283,6 +7297,8 @@ public final class EmojiPagerContentComponent: Component {
|
||||
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji)
|
||||
} else if case .status = subject {
|
||||
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) {
|
||||
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar)
|
||||
} 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 {
|
||||
for item in recentStatusEmoji.items {
|
||||
guard let item = item.contents.get(RecentMediaItem.self) else {
|
||||
@ -8143,7 +8319,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
var isTemplate = false
|
||||
var tintMode: Item.TintMode = .none
|
||||
if item.file.isCustomTemplateEmoji {
|
||||
if [.status, .backgroundIcon].contains(subject) {
|
||||
if [.status, .channelStatus, .backgroundIcon].contains(subject) {
|
||||
if let backgroundIconColor {
|
||||
tintMode = .custom(backgroundIconColor)
|
||||
} else {
|
||||
@ -8229,7 +8405,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
for item in featuredEmojiPack.topItems {
|
||||
var tintMode: Item.TintMode = .none
|
||||
if item.file.isCustomTemplateEmoji {
|
||||
if [.status, .backgroundIcon].contains(subject) {
|
||||
if [.status, .channelStatus, .backgroundIcon].contains(subject) {
|
||||
if let backgroundIconColor {
|
||||
tintMode = .custom(backgroundIconColor)
|
||||
} 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 enableLongPress = [.reaction(onlyTop: true), .reaction(onlyTop: false), .status].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, .channelStatus].contains(subject)
|
||||
|
||||
return EmojiPagerContentComponent(
|
||||
id: "emoji",
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
@ -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",
|
||||
],
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
21
submodules/TelegramUI/Components/ListSectionComponent/BUILD
Normal file
21
submodules/TelegramUI/Components/ListSectionComponent/BUILD
Normal 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",
|
||||
],
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
@ -332,7 +332,7 @@ private func notificationsPeerCategoryEntries(peerId: EnginePeer.Id, notificatio
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ swift_library(
|
||||
"//submodules/Markdown",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||
"//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/Components/PagerComponent",
|
||||
"//submodules/PremiumUI",
|
||||
|
@ -20,82 +20,7 @@ import BundleIconComponent
|
||||
import AnimatedTextComponent
|
||||
import TextFormat
|
||||
import AudioToolbox
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
import PremiumLockButtonSubtitleComponent
|
||||
|
||||
final class PeerAllowedReactionsScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
@ -904,7 +829,7 @@ final class PeerAllowedReactionsScreenComponent: Component {
|
||||
}).count : 0
|
||||
|
||||
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,
|
||||
theme: environment.theme,
|
||||
strings: environment.strings
|
||||
|
@ -34,15 +34,17 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem {
|
||||
let id: AnyHashable
|
||||
let label: Label
|
||||
let additionalBadgeLabel: String?
|
||||
let additionalBadgeIcon: UIImage?
|
||||
let text: String
|
||||
let icon: UIImage?
|
||||
let iconSignal: Signal<UIImage?, NoError>?
|
||||
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.label = label
|
||||
self.additionalBadgeLabel = additionalBadgeLabel
|
||||
self.additionalBadgeIcon = additionalBadgeIcon
|
||||
self.text = text
|
||||
self.icon = icon
|
||||
self.iconSignal = iconSignal
|
||||
@ -61,6 +63,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
private let labelBadgeNode: ASImageNode
|
||||
private let labelNode: ImmediateTextNode
|
||||
private var additionalLabelNode: ImmediateTextNode?
|
||||
private var additionalLabelBadgeNode: ASImageNode?
|
||||
private let textNode: ImmediateTextNode
|
||||
private let arrowNode: ASImageNode
|
||||
private let bottomSeparatorNode: ASDisplayNode
|
||||
@ -249,6 +252,24 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
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)
|
||||
if case .semitransparentBadge = item.label {
|
||||
badgeWidth += 2.0
|
||||
@ -283,6 +304,11 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
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
|
||||
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)
|
||||
|
@ -476,7 +476,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
credibilityIcon = .fake
|
||||
} else if peer.isScam {
|
||||
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)
|
||||
} else if peer.isVerified {
|
||||
credibilityIcon = .verified
|
||||
|
@ -1675,7 +1675,12 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
|
||||
}
|
||||
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()
|
||||
}))
|
||||
|
||||
@ -3835,7 +3840,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
var currentSelectedFileId: Int64?
|
||||
var topStatusTitle = strongSelf.presentationData.strings.PeerStatusSetup_NoTimerTitle
|
||||
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))
|
||||
currentSelectedFileId = emojiStatus.fileId
|
||||
|
||||
@ -7200,9 +7205,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
if self.peerId == self.context.account.peerId {
|
||||
let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .account)
|
||||
self.controller?.push(controller)
|
||||
} else {
|
||||
let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .channel(self.peerId))
|
||||
self.controller?.push(controller)
|
||||
} else if let peer = self.data?.peer, peer is TelegramChannel {
|
||||
self.controller?.push(ChannelAppearanceScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: self.peerId))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
if case let .user(user) = self.item.peer, let _ = user.emojiStatus {
|
||||
if self.item.peer.emojiStatus != nil {
|
||||
rightTextInset += 32.0
|
||||
}
|
||||
|
||||
@ -12236,6 +12240,10 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom
|
||||
} else if user.isPremium {
|
||||
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 {
|
||||
let emojiStatusSize = self.emojiStatusView.update(
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ swift_library(
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/UndoUI",
|
||||
"//submodules/WallpaperBackgroundNode",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
"//submodules/TelegramUI/Components/EntityKeyboard",
|
||||
"//submodules/SolidRoundedButtonNode",
|
||||
@ -28,6 +29,20 @@ swift_library(
|
||||
"//submodules/PremiumUI",
|
||||
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent",
|
||||
"//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:public",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -11,10 +11,11 @@ import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import AccountContext
|
||||
import WallpaperBackgroundNode
|
||||
import ListItemComponentAdaptor
|
||||
|
||||
final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem {
|
||||
final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
|
||||
struct MessageItem: Equatable {
|
||||
static func == (lhs: MessageItem, rhs: MessageItem) -> Bool {
|
||||
static func ==(lhs: MessageItem, rhs: MessageItem) -> Bool {
|
||||
if lhs.outgoing != rhs.outgoing {
|
||||
return false
|
||||
}
|
||||
@ -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 {
|
||||
@ -260,7 +299,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
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) {
|
||||
snapshot.frame = CGRect(origin: CGPoint(x: 0.0, y: -insets.top), size: snapshot.frame.size)
|
||||
strongSelf.view.addSubview(snapshot)
|
||||
@ -329,31 +368,42 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
if params.isStandalone {
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
strongSelf.bottomStripeNode.isHidden = true
|
||||
strongSelf.maskNode.isHidden = true
|
||||
} else {
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
strongSelf.bottomStripeNode.isHidden = false
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
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 bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
strongSelf.bottomStripeNode.isHidden = false
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
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)))
|
||||
|
||||
@ -377,8 +427,6 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
||||
backgroundNode.updateLayout(size: backgroundNode.bounds.size, displayMode: displayMode, transition: .immediate)
|
||||
}
|
||||
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))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import MergeLists
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import AccountContext
|
||||
import ListItemComponentAdaptor
|
||||
|
||||
private enum PeerNameColorEntryId: Hashable {
|
||||
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
|
||||
|
||||
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 {
|
||||
@ -528,24 +550,30 @@ final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
if item.currentColor != nil {
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
} else {
|
||||
if params.isStandalone {
|
||||
strongSelf.backgroundNode.isHidden = true
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
hasTopCorners = true
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
strongSelf.bottomStripeNode.isHidden = true
|
||||
strongSelf.maskNode.isHidden = true
|
||||
} else {
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
if item.currentColor != nil {
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
} else {
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
hasTopCorners = true
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = params.leftInset + 16.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
@ -555,15 +583,17 @@ final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode {
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
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.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.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()
|
||||
listInsets.top += params.leftInset + 8.0
|
||||
|
@ -14,24 +14,28 @@ import ComponentFlow
|
||||
import PeerInfoCoverComponent
|
||||
import AvatarNode
|
||||
import EmojiStatusComponent
|
||||
import ListItemComponentAdaptor
|
||||
import ComponentDisplayAdapters
|
||||
|
||||
final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem {
|
||||
final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let componentTheme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let sectionId: ItemListSectionId
|
||||
let peer: EnginePeer?
|
||||
let subtitleString: String?
|
||||
let files: [Int64: TelegramMediaFile]
|
||||
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.theme = theme
|
||||
self.componentTheme = componentTheme
|
||||
self.strings = strings
|
||||
self.sectionId = sectionId
|
||||
self.peer = peer
|
||||
self.subtitleString = subtitleString
|
||||
self.files = files
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
}
|
||||
@ -46,7 +50,7 @@ final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem {
|
||||
|
||||
Queue.mainQueue().async {
|
||||
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))
|
||||
Queue.mainQueue().async {
|
||||
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 {
|
||||
@ -104,7 +138,7 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
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
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
@ -117,7 +151,7 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
let layoutSize = layout.size
|
||||
|
||||
return (layout, { [weak self] in
|
||||
return (layout, { [weak self] animation in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -139,32 +173,41 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
if self.maskNode.supernode == nil {
|
||||
self.addSubnode(self.maskNode)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
||||
if params.isStandalone {
|
||||
self.topStripeNode.isHidden = true
|
||||
self.bottomStripeNode.isHidden = true
|
||||
self.maskNode.isHidden = true
|
||||
} else {
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
self.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
self.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
self.bottomStripeNode.isHidden = item.peer?.profileColor == nil
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
self.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
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 bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
self.bottomStripeNode.isHidden = item.peer?.profileColor == nil
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
self.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
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)))
|
||||
|
||||
@ -217,6 +260,61 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
}
|
||||
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 titleColor: UIColor
|
||||
let subtitleColor: UIColor
|
||||
@ -239,15 +337,64 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
environment: {},
|
||||
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 titleView.superview == nil {
|
||||
titleView.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
|
||||
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(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(Text(
|
||||
@ -265,8 +412,6 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
@ -18,12 +18,15 @@ import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import ShimmerEffect
|
||||
import StickerResources
|
||||
import ListItemComponentAdaptor
|
||||
import HexColor
|
||||
|
||||
private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
|
||||
let index: Int
|
||||
let emojiFile: TelegramMediaFile?
|
||||
let themeReference: PresentationThemeReference
|
||||
let nightMode: Bool
|
||||
let channelMode: Bool
|
||||
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||
let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||
var selected: Bool
|
||||
@ -48,6 +51,9 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
|
||||
if lhs.nightMode != rhs.nightMode {
|
||||
return false
|
||||
}
|
||||
if lhs.channelMode != rhs.channelMode {
|
||||
return false
|
||||
}
|
||||
if lhs.themeSpecificAccentColors != rhs.themeSpecificAccentColors {
|
||||
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 {
|
||||
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 {
|
||||
let context: AccountContext
|
||||
let emojiFile: TelegramMediaFile?
|
||||
let themeReference: PresentationThemeReference
|
||||
let nightMode: Bool
|
||||
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||
let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||
let selected: Bool
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let wallpaper: TelegramWallpaper?
|
||||
let action: (PresentationThemeReference) -> Void
|
||||
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
public class ThemeCarouselThemeIconItem: ListViewItem {
|
||||
public let context: AccountContext
|
||||
public let emojiFile: TelegramMediaFile?
|
||||
public let themeReference: PresentationThemeReference
|
||||
public let nightMode: Bool
|
||||
public let channelMode: Bool
|
||||
public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||
public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||
public let selected: Bool
|
||||
public let theme: PresentationTheme
|
||||
public let strings: PresentationStrings
|
||||
public let wallpaper: TelegramWallpaper?
|
||||
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.emojiFile = emojiFile
|
||||
self.themeReference = themeReference
|
||||
self.nightMode = nightMode
|
||||
self.channelMode = channelMode
|
||||
self.themeSpecificAccentColors = themeSpecificAccentColors
|
||||
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
|
||||
self.selected = selected
|
||||
@ -148,7 +156,6 @@ class ThemeCarouselThemeIconItem: ListViewItem {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private let textFont = Font.regular(12.0)
|
||||
private let selectedTextFont = Font.bold(12.0)
|
||||
|
||||
@ -338,6 +345,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
||||
var updatedThemeReference = false
|
||||
var updatedTheme = false
|
||||
var updatedNightMode = false
|
||||
var updatedChannelMode = false
|
||||
var updatedWallpaper = false
|
||||
var updatedSelected = false
|
||||
|
||||
@ -347,6 +355,9 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
||||
if currentItem?.nightMode != item.nightMode {
|
||||
updatedNightMode = true
|
||||
}
|
||||
if currentItem?.channelMode != item.channelMode {
|
||||
updatedChannelMode = true
|
||||
}
|
||||
if currentItem?.wallpaper != item.wallpaper {
|
||||
updatedWallpaper = true
|
||||
}
|
||||
@ -372,7 +383,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
|
||||
if updatedThemeReference || updatedWallpaper || updatedNightMode {
|
||||
if updatedThemeReference || updatedWallpaper || updatedNightMode || updatedChannelMode {
|
||||
var themeReference = item.themeReference
|
||||
if case .builtin = themeReference, item.nightMode {
|
||||
themeReference = .builtin(.night)
|
||||
@ -381,7 +392,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
||||
let color = item.themeSpecificAccentColors[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
|
||||
}
|
||||
|
||||
@ -507,23 +518,24 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeCarouselThemeItem: ListViewItem, ItemListItem {
|
||||
var sectionId: ItemListSectionId
|
||||
public class ThemeCarouselThemeItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
|
||||
public var sectionId: ItemListSectionId
|
||||
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let themes: [PresentationThemeReference]
|
||||
let animatedEmojiStickers: [String: [StickerPackItem]]
|
||||
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||
let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||
let nightMode: Bool
|
||||
let currentTheme: PresentationThemeReference
|
||||
let updatedTheme: (PresentationThemeReference) -> Void
|
||||
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
let tag: ItemListItemTag?
|
||||
public let context: AccountContext
|
||||
public let theme: PresentationTheme
|
||||
public let strings: PresentationStrings
|
||||
public let themes: [PresentationThemeReference]
|
||||
public let animatedEmojiStickers: [String: [StickerPackItem]]
|
||||
public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||
public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||
public let nightMode: Bool
|
||||
public let channelMode: Bool
|
||||
public let currentTheme: PresentationThemeReference
|
||||
public let updatedTheme: (PresentationThemeReference) -> Void
|
||||
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.theme = theme
|
||||
self.strings = strings
|
||||
@ -532,6 +544,7 @@ class ThemeCarouselThemeItem: ListViewItem, ItemListItem {
|
||||
self.themeSpecificAccentColors = themeSpecificAccentColors
|
||||
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
|
||||
self.nightMode = nightMode
|
||||
self.channelMode = channelMode
|
||||
self.currentTheme = currentTheme
|
||||
self.updatedTheme = updatedTheme
|
||||
self.contextAction = contextAction
|
||||
@ -539,7 +552,7 @@ class ThemeCarouselThemeItem: ListViewItem, ItemListItem {
|
||||
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 {
|
||||
let node = ThemeCarouselThemeItemNode()
|
||||
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 {
|
||||
if let nodeValue = node() as? ThemeCarouselThemeItemNode {
|
||||
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 {
|
||||
@ -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 backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
@ -627,11 +679,11 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
|
||||
private var tapping = false
|
||||
|
||||
var tag: ItemListItemTag? {
|
||||
public var tag: ItemListItemTag? {
|
||||
return self.item?.tag
|
||||
}
|
||||
|
||||
init() {
|
||||
public init() {
|
||||
self.containerNode = ASDisplayNode()
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
@ -654,7 +706,7 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
self.addSubnode(self.listNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
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
|
||||
let contentSize: CGSize
|
||||
let insets: UIEdgeInsets
|
||||
@ -728,20 +780,26 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
if strongSelf.maskNode.supernode == nil {
|
||||
strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
||||
if params.isStandalone {
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
strongSelf.bottomStripeNode.isHidden = true
|
||||
strongSelf.maskNode.isHidden = true
|
||||
strongSelf.backgroundNode.isHidden = true
|
||||
} else {
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = params.leftInset + 16.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
@ -751,17 +809,20 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
strongSelf.bottomStripeNode.isHidden = true
|
||||
|
||||
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.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.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.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()
|
||||
listInsets.top += params.leftInset + 12.0
|
||||
@ -781,12 +842,12 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
hasCurrentTheme = true
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
@ -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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func prepareCrossfadeTransition() {
|
||||
public func prepareCrossfadeTransition() {
|
||||
guard self.snapshotView == nil else {
|
||||
return
|
||||
}
|
||||
@ -836,7 +897,7 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
func animateCrossfadeTransition() {
|
||||
public func animateCrossfadeTransition() {
|
||||
guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else {
|
||||
return
|
||||
}
|
@ -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",
|
||||
],
|
||||
)
|
@ -14,6 +14,8 @@ import WallpaperResources
|
||||
import AccountContext
|
||||
import AppBundle
|
||||
import ContextUI
|
||||
import ListItemComponentAdaptor
|
||||
import HexColor
|
||||
|
||||
private struct ThemeSettingsThemeEntry: Comparable, Identifiable {
|
||||
let index: Int
|
||||
@ -369,24 +371,24 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
|
||||
var sectionId: ItemListSectionId
|
||||
public class ThemeSettingsThemeItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
|
||||
public var sectionId: ItemListSectionId
|
||||
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let themes: [PresentationThemeReference]
|
||||
let allThemes: [PresentationThemeReference]
|
||||
let displayUnsupported: Bool
|
||||
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||
let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||
let themePreferredBaseTheme: [Int64: TelegramBaseTheme]
|
||||
let currentTheme: PresentationThemeReference
|
||||
let updatedTheme: (PresentationThemeReference) -> Void
|
||||
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
let tag: ItemListItemTag?
|
||||
public let context: AccountContext
|
||||
public let theme: PresentationTheme
|
||||
public let strings: PresentationStrings
|
||||
public let themes: [PresentationThemeReference]
|
||||
public let allThemes: [PresentationThemeReference]
|
||||
public let displayUnsupported: Bool
|
||||
public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||
public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||
public let themePreferredBaseTheme: [Int64: TelegramBaseTheme]
|
||||
public let currentTheme: PresentationThemeReference
|
||||
public let updatedTheme: (PresentationThemeReference) -> Void
|
||||
public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
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.theme = theme
|
||||
self.strings = strings
|
||||
@ -403,7 +405,7 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
|
||||
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 {
|
||||
let node = ThemeSettingsThemeItemNode()
|
||||
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 {
|
||||
if let nodeValue = node() as? ThemeSettingsThemeItemNode {
|
||||
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 {
|
||||
@ -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 backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
@ -488,13 +526,13 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
private var item: ThemeSettingsThemeItem?
|
||||
private var layoutParams: ListViewItemLayoutParams?
|
||||
|
||||
var tag: ItemListItemTag? {
|
||||
public var tag: ItemListItemTag? {
|
||||
return self.item?.tag
|
||||
}
|
||||
|
||||
private var tapping = false
|
||||
|
||||
init() {
|
||||
public init() {
|
||||
self.containerNode = ASDisplayNode()
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
@ -517,7 +555,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
self.addSubnode(self.listNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
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
|
||||
let contentSize: CGSize
|
||||
let insets: UIEdgeInsets
|
||||
@ -591,20 +629,26 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
if strongSelf.maskNode.supernode == nil {
|
||||
strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
||||
if params.isStandalone {
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
strongSelf.bottomStripeNode.isHidden = true
|
||||
strongSelf.maskNode.isHidden = true
|
||||
strongSelf.backgroundNode.isHidden = true
|
||||
} else {
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = params.leftInset + 16.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
@ -614,15 +658,18 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
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.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.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()
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func prepareCrossfadeTransition() {
|
||||
public func prepareCrossfadeTransition() {
|
||||
guard self.snapshotView == nil else {
|
||||
return
|
||||
}
|
||||
@ -719,7 +766,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
func animateCrossfadeTransition() {
|
||||
public func animateCrossfadeTransition() {
|
||||
guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else {
|
||||
return
|
||||
}
|
@ -604,7 +604,7 @@ public final class PeerListItemComponent: Component {
|
||||
statusIcon = .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_ScamAccount.uppercased())
|
||||
} else if peer.isFake {
|
||||
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))
|
||||
} else if peer.isVerified {
|
||||
statusIcon = .verified(fillColor: component.theme.list.itemCheckColors.fillColor, foregroundColor: component.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
|
||||
|
@ -368,6 +368,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer()
|
||||
self.titleAccessoryPanelContainer.clipsToBounds = true
|
||||
|
||||
setLayerDisableScreenshots(self.titleAccessoryPanelContainer.layer, chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat)
|
||||
|
||||
self.inputContextPanelContainer = ChatControllerTitlePanelNodeContainer()
|
||||
self.inputContextOverTextPanelContainer = ChatControllerTitlePanelNodeContainer()
|
||||
|
||||
@ -1012,7 +1014,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
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?
|
||||
|
@ -540,6 +540,11 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
} else {
|
||||
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
|
||||
|
@ -363,6 +363,18 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}
|
||||
if themeUpdated {
|
||||
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 {
|
||||
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 {
|
||||
|
@ -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)
|
||||
}()
|
||||
|
||||
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>
|
||||
|
||||
var reference: MediaResourceReference?
|
||||
@ -1693,6 +1693,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
|
||||
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 ")
|
||||
|
||||
if Set(incomingColors.map(\.rgb)).count > 1 {
|
||||
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)
|
||||
}
|
||||
|
||||
let outgoingColors = colors.2
|
||||
let outgoingColors = channelMode == true ? colors.1 : colors.2
|
||||
if emoticon {
|
||||
if large {
|
||||
c.saveGState()
|
||||
@ -1780,7 +1781,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
|
||||
|
||||
c.restoreGState()
|
||||
} 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.clip()
|
||||
|
||||
|
@ -49,7 +49,9 @@
|
||||
}
|
||||
|
||||
_frameCount = (int32_t)_animation->totalFrame();
|
||||
_frameCount = MAX(1, _frameCount);
|
||||
_frameRate = (int32_t)_animation->frameRate();
|
||||
_frameRate = MAX(1, _frameRate);
|
||||
|
||||
size_t width = 0;
|
||||
size_t height = 0;
|
||||
@ -59,6 +61,9 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
width = MAX(1, width);
|
||||
height = MAX(1, height);
|
||||
|
||||
_dimensions = CGSizeMake(width, height);
|
||||
|
||||
if ((_frameRate > 360) || _animation->duration() > 9.0) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user