[WIP] Channel appearance

This commit is contained in:
Isaac 2023-12-15 15:13:40 +04:00
parent 200a2864a9
commit b66f748969
50 changed files with 3063 additions and 327 deletions

View File

@ -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
}

View File

@ -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 {

View File

@ -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"

View File

@ -225,12 +225,26 @@ 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 +257,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

View File

@ -428,6 +428,7 @@ public func generateScaledImage(image: UIImage?, size: CGSize, opaque: Bool = tr
public func generateSingleColorImage(size: CGSize, color: UIColor) -> 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))
})

View File

@ -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
}
}

View File

@ -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:

View File

@ -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() {

View File

@ -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,

View File

@ -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",

View File

@ -307,10 +307,10 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let 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))
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))
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

View File

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

View File

@ -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? {

View File

@ -948,7 +948,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let 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))
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))

View File

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

View File

@ -455,10 +455,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
let 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))
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))
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))

View File

@ -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

View File

@ -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: nil)
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)
}
}
@ -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 emojiStatus = lhs.emojiStatus
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: emojiStatus)
} 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)
}

View File

@ -171,6 +171,7 @@ public final class TelegramChannel: Peer, Equatable {
public let backgroundEmojiId: Int64?
public let profileColor: PeerNameColor?
public let profileBackgroundEmojiId: Int64?
public let emojiStatus: PeerEmojiStatus?
public var indexName: PeerIndexNameRepresentation {
var addressNames = self.usernames.map { $0.username }
@ -181,8 +182,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 +203,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 +235,8 @@ public final class TelegramChannel: Peer, Equatable {
nameColor: PeerNameColor?,
backgroundEmojiId: Int64?,
profileColor: PeerNameColor?,
profileBackgroundEmojiId: Int64?
profileBackgroundEmojiId: Int64?,
emojiStatus: PeerEmojiStatus?
) {
self.id = id
self.accessHash = accessHash
@ -235,6 +258,7 @@ public final class TelegramChannel: Peer, Equatable {
self.backgroundEmojiId = backgroundEmojiId
self.profileColor = profileColor
self.profileBackgroundEmojiId = profileBackgroundEmojiId
self.emojiStatus = emojiStatus
}
public init(decoder: PostboxDecoder) {
@ -268,6 +292,7 @@ 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")
}
public func encode(_ encoder: PostboxEncoder) {
@ -347,6 +372,12 @@ 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")
}
}
public func isEqual(_ other: Peer) -> Bool {
@ -399,43 +430,50 @@ public final class TelegramChannel: Peer, Equatable {
if lhs.profileBackgroundEmojiId != rhs.profileBackgroundEmojiId {
return false
}
if lhs.emojiStatus != rhs.emojiStatus {
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
}

View File

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

View File

@ -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
}

View File

@ -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:

View File

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

View File

@ -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)
}
return peer
}

View File

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

View File

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

View File

@ -64,6 +64,7 @@ public final class EmojiStatusSelectionComponent: Component {
public let emojiContent: EmojiPagerContentComponent
public let 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,
@ -1279,6 +1298,8 @@ 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?.fileId.id)
case let .quickReactionSelection(completion):
if let item = item, let itemFile = item.itemFile {
var selectedReaction: MessageReaction.Reaction?
@ -1364,6 +1385,7 @@ public final class EmojiStatusSelectionController: ViewController {
public enum Mode {
case statusSelection
case backgroundSelection(completion: (Int64?) -> Void)
case quickReactionSelection(completion: () -> Void)
}
@ -1371,6 +1393,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 +1406,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 +1442,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()
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -332,7 +332,7 @@ private func notificationsPeerCategoryEntries(peerId: EnginePeer.Id, notificatio
}
}
existingThreadIds.insert(value.threadId)
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)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId))
index += 1
}

View File

@ -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",

View File

@ -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

View File

@ -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)

View File

@ -1675,7 +1675,8 @@ 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
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), additionalBadgeIcon: generateDisclosureActionBoostLevelBadgeImage(text: "Level 1+"), text: "Appearance", icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: {
interaction.editingOpenNameColorSetup()
}))
@ -7200,9 +7201,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))
}
}

View File

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

View File

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

View File

@ -21,6 +21,7 @@ swift_library(
"//submodules/PresentationDataUtils",
"//submodules/UndoUI",
"//submodules/WallpaperBackgroundNode",
"//submodules/ComponentFlow",
"//submodules/TelegramUI/Components/EmojiStatusComponent",
"//submodules/TelegramUI/Components/EntityKeyboard",
"//submodules/SolidRoundedButtonNode",
@ -28,6 +29,19 @@ 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",
],
visibility = [
"//visibility:public",

View File

@ -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 {
@ -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))
}
})
}

View File

@ -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

View File

@ -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))
})
}
}

View File

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

View File

@ -18,6 +18,8 @@ import AnimatedStickerNode
import TelegramAnimatedStickerNode
import ShimmerEffect
import StickerResources
import ListItemComponentAdaptor
import HexColor
private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
let index: Int
@ -79,19 +81,19 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
}
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 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)?) {
self.context = context
@ -148,7 +150,6 @@ class ThemeCarouselThemeIconItem: ListViewItem {
}
}
private let textFont = Font.regular(12.0)
private let selectedTextFont = Font.bold(12.0)
@ -507,23 +508,23 @@ 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 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, currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
self.context = context
self.theme = theme
self.strings = strings
@ -539,7 +540,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 +556,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 +572,42 @@ 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.currentTheme != rhs.currentTheme {
return false
}
return true
}
}
private struct ThemeCarouselThemeItemNodeTransition {
@ -609,7 +646,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 +664,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 +691,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 +732,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 +765,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 +794,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
@ -810,15 +856,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 +882,7 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
}
}
func animateCrossfadeTransition() {
public func animateCrossfadeTransition() {
guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else {
return
}

View File

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

View File

@ -14,6 +14,8 @@ import WallpaperResources
import AccountContext
import 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
}

View File

@ -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 {