mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Channel appearance
This commit is contained in:
parent
200a2864a9
commit
b66f748969
@ -903,10 +903,17 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
guard let publicToken = nativeParams["public_token"] as? String else {
|
||||
return
|
||||
}
|
||||
|
||||
var customTokenizeUrl: String?
|
||||
if let value = nativeParams["public_token"] as? String, let url = URL(string: value), let host = url.host {
|
||||
if url.scheme == "https" && (host == "smart-glocal.com" || host.hasSuffix(".smart-glocal.com")) {
|
||||
customTokenizeUrl = value
|
||||
}
|
||||
}
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
let canSave = paymentForm.canSaveCredentials || paymentForm.passwordMissing
|
||||
let controller = BotCheckoutNativeCardEntryController(context: strongSelf.context, provider: .smartglobal(isTesting: paymentForm.invoice.isTest, publicToken: publicToken), completion: { method in
|
||||
let controller = BotCheckoutNativeCardEntryController(context: strongSelf.context, provider: .smartglobal(isTesting: paymentForm.invoice.isTest, publicToken: publicToken, customTokenizeUrl: customTokenizeUrl), completion: { method in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ struct BotCheckoutNativeCardEntryAdditionalFields: OptionSet {
|
||||
final class BotCheckoutNativeCardEntryController: ViewController {
|
||||
enum Provider {
|
||||
case stripe(additionalFields: BotCheckoutNativeCardEntryAdditionalFields, publishableKey: String)
|
||||
case smartglobal(isTesting: Bool, publicToken: String)
|
||||
case smartglobal(isTesting: Bool, publicToken: String, customTokenizeUrl: String?)
|
||||
}
|
||||
|
||||
private var controllerNode: BotCheckoutNativeCardEntryControllerNode {
|
||||
|
@ -310,9 +310,11 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode,
|
||||
}))
|
||||
|
||||
self.updateDone()
|
||||
case let .smartglobal(isTesting, publicToken):
|
||||
case let .smartglobal(isTesting, publicToken, customTokenizeUrl):
|
||||
let url: String
|
||||
if isTesting {
|
||||
if let customTokenizeUrl {
|
||||
url = customTokenizeUrl
|
||||
} else if isTesting {
|
||||
url = "https://tgb-playground.smart-glocal.com/cds/v1/tokenize/card"
|
||||
} else {
|
||||
url = "https://tgb.smart-glocal.com/cds/v1/tokenize/card"
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
})
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,6 +122,10 @@ public func itemListNeighborsPlainInsets(_ neighbors: ItemListNeighbors) -> UIEd
|
||||
}
|
||||
|
||||
public func itemListNeighborsGroupedInsets(_ neighbors: ItemListNeighbors, _ params: ListViewItemLayoutParams) -> UIEdgeInsets {
|
||||
if params.isStandalone {
|
||||
return UIEdgeInsets()
|
||||
}
|
||||
|
||||
let topInset: CGFloat
|
||||
switch neighbors.top {
|
||||
case .none:
|
||||
|
@ -599,7 +599,12 @@ private final class VariableBlurView: UIVisualEffectView {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func updateTraitsIfNeeded() {
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
if #available(iOS 13.0, *) {
|
||||
if self.traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
|
||||
self.resetEffect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func resetEffect() {
|
||||
|
@ -589,6 +589,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
strings: strongSelf.presentationData.strings,
|
||||
deviceMetrics: DeviceMetrics.iPhone13,
|
||||
emojiContent: emojiContent,
|
||||
color: nil,
|
||||
backgroundColor: .clear,
|
||||
separatorColor: strongSelf.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5),
|
||||
hideTopPanel: hideTopPanel,
|
||||
@ -1215,6 +1216,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
strings: self.presentationData.strings,
|
||||
deviceMetrics: DeviceMetrics.iPhone13,
|
||||
emojiContent: emojiContent,
|
||||
color: nil,
|
||||
backgroundColor: .clear,
|
||||
separatorColor: self.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5),
|
||||
hideTopPanel: hideTopPanel,
|
||||
|
@ -117,6 +117,8 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/Settings/PeerNameColorScreen",
|
||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||
"//submodules/TelegramUI/Components/Settings/QuickReactionSetupController",
|
||||
"//submodules/TelegramUI/Components/Settings/ThemeCarouselItem",
|
||||
"//submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -307,10 +307,10 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
|
||||
let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
|
||||
let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
|
||||
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
|
||||
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil))
|
||||
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
|
||||
|
@ -19,6 +19,7 @@ import ContextUI
|
||||
import UndoUI
|
||||
import ItemListPeerActionItem
|
||||
import AnimationUI
|
||||
import ThemeSettingsThemeItem
|
||||
|
||||
private final class ThemePickerControllerArguments {
|
||||
let context: AccountContext
|
||||
|
@ -18,7 +18,7 @@ import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import ShimmerEffect
|
||||
import StickerResources
|
||||
|
||||
import ThemeCarouselItem
|
||||
|
||||
private var cachedBorderImages: [String: UIImage] = [:]
|
||||
private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selected: Bool) -> UIImage? {
|
||||
|
@ -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))
|
||||
|
||||
|
@ -14,6 +14,7 @@ import DeviceLocationManager
|
||||
import Geocoding
|
||||
import WallpaperResources
|
||||
import Sunrise
|
||||
import ThemeSettingsThemeItem
|
||||
|
||||
private enum TriggerMode {
|
||||
case system
|
||||
|
@ -455,10 +455,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
|
||||
let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
|
||||
let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
|
||||
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil))
|
||||
let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil))
|
||||
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))
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -255,6 +255,17 @@ public extension Peer {
|
||||
return false
|
||||
}
|
||||
|
||||
var emojiStatus: PeerEmojiStatus? {
|
||||
switch self {
|
||||
case let user as TelegramUser:
|
||||
return user.emojiStatus
|
||||
case let channel as TelegramChannel:
|
||||
return channel.emojiStatus
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var backgroundEmojiId: Int64? {
|
||||
switch self {
|
||||
case let user as TelegramUser:
|
||||
|
@ -875,3 +875,29 @@ public extension PresentationData {
|
||||
return PresentationData(strings: strings, theme: self.theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: self.chatWallpaper, chatFontSize: self.chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: self.listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, reduceMotion: self.reduceMotion, largeEmoji: self.largeEmoji)
|
||||
}
|
||||
}
|
||||
|
||||
public func themeDisplayName(strings: PresentationStrings, reference: PresentationThemeReference) -> String {
|
||||
let name: String
|
||||
switch reference {
|
||||
case let .builtin(theme):
|
||||
switch theme {
|
||||
case .dayClassic:
|
||||
name = strings.Appearance_ThemeCarouselClassic
|
||||
case .day:
|
||||
name = strings.Appearance_ThemeCarouselDay
|
||||
case .night:
|
||||
name = strings.Appearance_ThemeCarouselNewNight
|
||||
case .nightAccent:
|
||||
name = strings.Appearance_ThemeCarouselTintedNight
|
||||
}
|
||||
case let .local(theme):
|
||||
name = theme.title
|
||||
case let .cloud(theme):
|
||||
if let emoticon = theme.theme.emoticon {
|
||||
name = emoticon
|
||||
} else {
|
||||
name = theme.theme.title
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "DynamicCornerRadiusView",
|
||||
module_name = "DynamicCornerRadiusView",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/ComponentFlow"
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,90 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import ComponentFlow
|
||||
|
||||
private func generatePath(size: CGSize, corners: DynamicCornerRadiusView.Corners) -> CGPath {
|
||||
let path = CGMutablePath()
|
||||
|
||||
var corners = corners
|
||||
corners.minXMinY = max(0.01, corners.minXMinY)
|
||||
corners.maxXMinY = max(0.01, corners.maxXMinY)
|
||||
corners.minXMaxY = max(0.01, corners.minXMaxY)
|
||||
corners.maxXMaxY = max(0.01, corners.maxXMaxY)
|
||||
|
||||
path.move(to: CGPoint(x: 0.0, y: corners.minXMinY))
|
||||
path.addArc(tangent1End: CGPoint(x: 0.0, y: 0.0), tangent2End: CGPoint(x: corners.minXMinY, y: 0.0), radius: corners.minXMinY)
|
||||
path.addLine(to: CGPoint(x: size.width - corners.maxXMinY, y: 0.0))
|
||||
path.addArc(tangent1End: CGPoint(x: size.width, y: 0.0), tangent2End: CGPoint(x: size.width, y: corners.maxXMinY), radius: corners.maxXMinY)
|
||||
path.addLine(to: CGPoint(x: size.width, y: size.height - corners.maxXMaxY))
|
||||
path.addArc(tangent1End: CGPoint(x: size.width, y: size.height), tangent2End: CGPoint(x: size.width - corners.maxXMaxY, y: size.height), radius: corners.maxXMaxY)
|
||||
path.addLine(to: CGPoint(x: corners.minXMaxY, y: size.height))
|
||||
path.addArc(tangent1End: CGPoint(x: 0.0, y: size.height), tangent2End: CGPoint(x: 0.0, y: size.height - corners.minXMaxY), radius: corners.minXMaxY)
|
||||
path.closeSubpath()
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
open class DynamicCornerRadiusView: UIView {
|
||||
override public static var layerClass: AnyClass {
|
||||
return CAShapeLayer.self
|
||||
}
|
||||
|
||||
public struct Corners: Equatable {
|
||||
public var minXMinY: CGFloat
|
||||
public var maxXMinY: CGFloat
|
||||
public var minXMaxY: CGFloat
|
||||
public var maxXMaxY: CGFloat
|
||||
|
||||
public init(minXMinY: CGFloat, maxXMinY: CGFloat, minXMaxY: CGFloat, maxXMaxY: CGFloat) {
|
||||
self.minXMinY = minXMinY
|
||||
self.maxXMinY = maxXMinY
|
||||
self.minXMaxY = minXMaxY
|
||||
self.maxXMaxY = maxXMaxY
|
||||
}
|
||||
}
|
||||
|
||||
private struct Params: Equatable {
|
||||
var size: CGSize
|
||||
var corners: Corners
|
||||
|
||||
init(size: CGSize, corners: Corners) {
|
||||
self.size = size
|
||||
self.corners = corners
|
||||
}
|
||||
}
|
||||
|
||||
private var params: Params?
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
if let shapeLayer = self.layer as? CAShapeLayer {
|
||||
shapeLayer.strokeColor = nil
|
||||
}
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func update(size: CGSize, corners: Corners, transition: Transition) {
|
||||
let params = Params(size: size, corners: corners)
|
||||
if self.params == params {
|
||||
return
|
||||
}
|
||||
self.params = params
|
||||
self.update(params: params, transition: transition)
|
||||
}
|
||||
|
||||
public func updateColor(color: UIColor, transition: Transition) {
|
||||
if let shapeLayer = self.layer as? CAShapeLayer {
|
||||
transition.setShapeLayerFillColor(layer: shapeLayer, color: color)
|
||||
}
|
||||
}
|
||||
|
||||
private func update(params: Params, transition: Transition) {
|
||||
if let shapeLayer = self.layer as? CAShapeLayer {
|
||||
transition.setShapeLayerPath(layer: shapeLayer, path: generatePath(size: params.size, corners: params.corners))
|
||||
}
|
||||
}
|
||||
}
|
@ -64,6 +64,7 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
public let emojiContent: EmojiPagerContentComponent
|
||||
public let backgroundColor: UIColor
|
||||
public let separatorColor: UIColor
|
||||
public let color: UIColor?
|
||||
public let hideTopPanel: Bool
|
||||
public let disableTopPanel: Bool
|
||||
public let hideTopPanelUpdated: (Bool, Transition) -> Void
|
||||
@ -73,6 +74,7 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
strings: PresentationStrings,
|
||||
deviceMetrics: DeviceMetrics,
|
||||
emojiContent: EmojiPagerContentComponent,
|
||||
color: UIColor?,
|
||||
backgroundColor: UIColor,
|
||||
separatorColor: UIColor,
|
||||
hideTopPanel: Bool,
|
||||
@ -83,6 +85,7 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
self.strings = strings
|
||||
self.deviceMetrics = deviceMetrics
|
||||
self.emojiContent = emojiContent
|
||||
self.color = color
|
||||
self.backgroundColor = backgroundColor
|
||||
self.separatorColor = separatorColor
|
||||
self.hideTopPanel = hideTopPanel
|
||||
@ -103,6 +106,9 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
if lhs.emojiContent != rhs.emojiContent {
|
||||
return false
|
||||
}
|
||||
if lhs.color != rhs.color {
|
||||
return false
|
||||
}
|
||||
if lhs.backgroundColor != rhs.backgroundColor {
|
||||
return false
|
||||
}
|
||||
@ -169,7 +175,7 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
isContentInFocus: true,
|
||||
containerInsets: UIEdgeInsets(top: topPanelHeight - 34.0, left: 0.0, bottom: 0.0, right: 0.0),
|
||||
topPanelInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0),
|
||||
emojiContent: component.emojiContent,
|
||||
emojiContent: component.emojiContent.withCustomTintColor(component.color),
|
||||
stickerContent: nil,
|
||||
maskContent: nil,
|
||||
gifContent: nil,
|
||||
@ -200,7 +206,8 @@ public final class EmojiStatusSelectionComponent: Component {
|
||||
isExpanded: false,
|
||||
clipContentToTopPanel: false,
|
||||
useExternalSearchContainer: false,
|
||||
hidePanels: component.disableTopPanel
|
||||
hidePanels: component.disableTopPanel,
|
||||
customTintColor: component.color
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
@ -277,6 +284,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private let currentSelection: Int64?
|
||||
private let color: UIColor?
|
||||
|
||||
private var emojiContentDisposable: Disposable?
|
||||
private var emojiContent: EmojiPagerContentComponent?
|
||||
@ -312,10 +320,11 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
|
||||
private var isReactionSearchActive: Bool = false
|
||||
|
||||
init(controller: EmojiStatusSelectionController, context: AccountContext, sourceView: UIView?, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?) {
|
||||
init(controller: EmojiStatusSelectionController, context: AccountContext, sourceView: UIView?, emojiContent: Signal<EmojiPagerContentComponent, NoError>, currentSelection: Int64?, color: UIColor?) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
self.currentSelection = currentSelection
|
||||
self.color = color
|
||||
|
||||
if let sourceView = sourceView {
|
||||
self.globalSourceRect = sourceView.convert(sourceView.bounds, to: nil)
|
||||
@ -826,17 +835,26 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
renderer: animationRenderer,
|
||||
placeholderColor: UIColor(white: 0.0, alpha: 0.0),
|
||||
pointSize: CGSize(width: 32.0, height: 32.0),
|
||||
dynamicColor: self.presentationData.theme.list.itemAccentColor
|
||||
dynamicColor: self.color ?? self.presentationData.theme.list.itemAccentColor
|
||||
)
|
||||
switch item.tintMode {
|
||||
case let .custom(color):
|
||||
baseItemLayer.contentTintColor = color
|
||||
case .accent:
|
||||
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemAccentColor
|
||||
case .primary:
|
||||
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemPrimaryTextColor
|
||||
case .none:
|
||||
break
|
||||
if let color = self.color {
|
||||
switch item.tintMode {
|
||||
case .none:
|
||||
break
|
||||
default:
|
||||
baseItemLayer.contentTintColor = color
|
||||
}
|
||||
} else {
|
||||
switch item.tintMode {
|
||||
case let .custom(color):
|
||||
baseItemLayer.contentTintColor = color
|
||||
case .accent:
|
||||
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemAccentColor
|
||||
case .primary:
|
||||
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemPrimaryTextColor
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let sublayers = animationLayer.sublayers {
|
||||
@ -988,6 +1006,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
strings: self.presentationData.strings,
|
||||
deviceMetrics: layout.deviceMetrics,
|
||||
emojiContent: emojiContent,
|
||||
color: self.color,
|
||||
backgroundColor: listBackgroundColor,
|
||||
separatorColor: separatorColor,
|
||||
hideTopPanel: self.isReactionSearchActive,
|
||||
@ -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()
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ListActionItemComponent",
|
||||
module_name = "ListActionItemComponent",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/Display",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/TelegramUI/Components/ListSectionComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,187 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramPresentationData
|
||||
import ListSectionComponent
|
||||
|
||||
public final class ListActionItemComponent: Component {
|
||||
public let theme: PresentationTheme
|
||||
public let title: AnyComponent<Empty>
|
||||
public let icon: AnyComponentWithIdentity<Empty>?
|
||||
public let hasArrow: Bool
|
||||
public let action: ((UIView) -> Void)?
|
||||
|
||||
public init(
|
||||
theme: PresentationTheme,
|
||||
title: AnyComponent<Empty>,
|
||||
icon: AnyComponentWithIdentity<Empty>?,
|
||||
hasArrow: Bool = true,
|
||||
action: ((UIView) -> Void)?
|
||||
) {
|
||||
self.theme = theme
|
||||
self.title = title
|
||||
self.icon = icon
|
||||
self.hasArrow = hasArrow
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public static func ==(lhs: ListActionItemComponent, rhs: ListActionItemComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.icon != rhs.icon {
|
||||
return false
|
||||
}
|
||||
if lhs.hasArrow != rhs.hasArrow {
|
||||
return false
|
||||
}
|
||||
if (lhs.action == nil) != (rhs.action == nil) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: HighlightTrackingButton, ListSectionComponent.ChildView {
|
||||
private let title = ComponentView<Empty>()
|
||||
private var icon: ComponentView<Empty>?
|
||||
|
||||
private let arrowView: UIImageView
|
||||
|
||||
private var component: ListActionItemComponent?
|
||||
|
||||
public var iconView: UIView? {
|
||||
return self.icon?.view
|
||||
}
|
||||
|
||||
public var customUpdateIsHighlighted: ((Bool) -> Void)?
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
self.arrowView = UIImageView()
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.addSubview(self.arrowView)
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
self.internalHighligthedChanged = { [weak self] isHighlighted in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let customUpdateIsHighlighted = self.customUpdateIsHighlighted {
|
||||
customUpdateIsHighlighted(isHighlighted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
self.component?.action?(self)
|
||||
}
|
||||
|
||||
func update(component: ListActionItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
let previousComponent = self.component
|
||||
self.component = component
|
||||
|
||||
self.isEnabled = component.action != nil
|
||||
|
||||
let verticalInset: CGFloat = 11.0
|
||||
|
||||
let contentLeftInset: CGFloat = 16.0
|
||||
let contentRightInset: CGFloat = component.hasArrow ? 30.0 : 16.0
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
contentHeight += verticalInset
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: transition,
|
||||
component: component.title,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - contentLeftInset, height: availableSize.height)
|
||||
)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: contentLeftInset, y: verticalInset), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
titleView.isUserInteractionEnabled = false
|
||||
self.addSubview(titleView)
|
||||
}
|
||||
transition.setFrame(view: titleView, frame: titleFrame)
|
||||
}
|
||||
contentHeight += titleSize.height
|
||||
|
||||
contentHeight += verticalInset
|
||||
|
||||
if let iconValue = component.icon {
|
||||
if previousComponent?.icon?.id != iconValue.id, let icon = self.icon {
|
||||
self.icon = nil
|
||||
if let iconView = icon.view {
|
||||
transition.setAlpha(view: iconView, alpha: 0.0, completion: { [weak iconView] _ in
|
||||
iconView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var iconTransition = transition
|
||||
let icon: ComponentView<Empty>
|
||||
if let current = self.icon {
|
||||
icon = current
|
||||
} else {
|
||||
iconTransition = iconTransition.withAnimation(.none)
|
||||
icon = ComponentView()
|
||||
self.icon = icon
|
||||
}
|
||||
|
||||
let iconSize = icon.update(
|
||||
transition: iconTransition,
|
||||
component: iconValue.component,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: availableSize.height)
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: availableSize.width - contentRightInset - iconSize.width, y: floor((contentHeight - iconSize.height) * 0.5)), size: iconSize)
|
||||
if let iconView = icon.view {
|
||||
if iconView.superview == nil {
|
||||
iconView.isUserInteractionEnabled = false
|
||||
self.addSubview(iconView)
|
||||
transition.animateAlpha(view: iconView, from: 0.0, to: 1.0)
|
||||
}
|
||||
iconTransition.setFrame(view: iconView, frame: iconFrame)
|
||||
}
|
||||
} else {
|
||||
if let icon = self.icon {
|
||||
self.icon = nil
|
||||
if let iconView = icon.view {
|
||||
transition.setAlpha(view: iconView, alpha: 0.0, completion: { [weak iconView] _ in
|
||||
iconView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.arrowView.image == nil {
|
||||
self.arrowView.image = PresentationResourcesItemList.disclosureArrowImage(component.theme)?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
self.arrowView.tintColor = component.theme.list.disclosureArrowColor
|
||||
if let image = self.arrowView.image {
|
||||
let arrowFrame = CGRect(origin: CGPoint(x: availableSize.width - 7.0 - image.size.width, y: floor((contentHeight - image.size.height) * 0.5)), size: image.size)
|
||||
transition.setFrame(view: self.arrowView, frame: arrowFrame)
|
||||
}
|
||||
transition.setAlpha(view: self.arrowView, alpha: component.hasArrow ? 1.0 : 0.0)
|
||||
|
||||
return CGSize(width: availableSize.width, height: contentHeight)
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ListItemComponentAdaptor",
|
||||
module_name = "ListItemComponentAdaptor",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,131 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import ComponentDisplayAdapters
|
||||
|
||||
public protocol _ListItemComponentAdaptorItemGenerator: AnyObject, Equatable {
|
||||
func item() -> ListViewItem
|
||||
}
|
||||
|
||||
public final class ListItemComponentAdaptor: Component {
|
||||
public typealias ItemGenerator = _ListItemComponentAdaptorItemGenerator
|
||||
|
||||
private let itemGenerator: AnyObject
|
||||
private let isEqualImpl: (AnyObject) -> Bool
|
||||
private let itemImpl: () -> ListViewItem
|
||||
private let params: ListViewItemLayoutParams
|
||||
|
||||
public init<ItemGeneratorType: ItemGenerator>(
|
||||
itemGenerator: ItemGeneratorType,
|
||||
params: ListViewItemLayoutParams
|
||||
) {
|
||||
self.itemGenerator = itemGenerator
|
||||
self.isEqualImpl = { other in
|
||||
if let other = other as? ItemGeneratorType, itemGenerator == other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
self.itemImpl = {
|
||||
return itemGenerator.item()
|
||||
}
|
||||
self.params = params
|
||||
}
|
||||
|
||||
public static func ==(lhs: ListItemComponentAdaptor, rhs: ListItemComponentAdaptor) -> Bool {
|
||||
if !lhs.isEqualImpl(rhs.itemGenerator) {
|
||||
return false
|
||||
}
|
||||
if lhs.params != rhs.params {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private var itemNode: ListViewItemNode?
|
||||
|
||||
func update(component: ListItemComponentAdaptor, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
let item = component.itemImpl()
|
||||
|
||||
if let itemNode = self.itemNode {
|
||||
let mappedAnimation: ListViewItemUpdateAnimation
|
||||
switch transition.animation {
|
||||
case .none:
|
||||
mappedAnimation = .None
|
||||
case let .curve(duration, curve):
|
||||
mappedAnimation = .System(duration: duration, transition: ControlledTransition(duration: duration, curve: curve.containedViewLayoutTransitionCurve, interactive: false))
|
||||
}
|
||||
|
||||
var resultSize: CGSize?
|
||||
item.updateNode(
|
||||
async: { f in f() },
|
||||
node: { return itemNode },
|
||||
params: component.params,
|
||||
previousItem: nil,
|
||||
nextItem: nil,
|
||||
animation: mappedAnimation,
|
||||
completion: { [weak itemNode] layout, apply in
|
||||
resultSize = layout.size
|
||||
|
||||
guard let itemNode else {
|
||||
return
|
||||
}
|
||||
|
||||
let nodeFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: layout.size.height))
|
||||
|
||||
itemNode.contentSize = layout.contentSize
|
||||
itemNode.insets = layout.insets
|
||||
itemNode.frame = nodeFrame
|
||||
|
||||
apply(ListViewItemApply(isOnScreen: true))
|
||||
}
|
||||
)
|
||||
if let resultSize {
|
||||
transition.setFrame(view: itemNode.view, frame: CGRect(origin: CGPoint(), size: resultSize))
|
||||
return resultSize
|
||||
} else {
|
||||
#if DEBUG
|
||||
assertionFailure()
|
||||
#endif
|
||||
return self.bounds.size
|
||||
}
|
||||
} else {
|
||||
var itemNode: ListViewItemNode?
|
||||
item.nodeConfiguredForParams(
|
||||
async: { f in f() },
|
||||
params: component.params,
|
||||
synchronousLoads: true,
|
||||
previousItem: nil,
|
||||
nextItem: nil,
|
||||
completion: { result, apply in
|
||||
itemNode = result
|
||||
apply().1(ListViewItemApply(isOnScreen: true))
|
||||
}
|
||||
)
|
||||
if let itemNode {
|
||||
self.itemNode = itemNode
|
||||
self.addSubnode(itemNode)
|
||||
|
||||
return itemNode.bounds.size
|
||||
} else {
|
||||
#if DEBUG
|
||||
assertionFailure()
|
||||
#endif
|
||||
return self.bounds.size
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
21
submodules/TelegramUI/Components/ListSectionComponent/BUILD
Normal file
21
submodules/TelegramUI/Components/ListSectionComponent/BUILD
Normal file
@ -0,0 +1,21 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ListSectionComponent",
|
||||
module_name = "ListSectionComponent",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/Display",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/TelegramUI/Components/DynamicCornerRadiusView",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,288 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramPresentationData
|
||||
import DynamicCornerRadiusView
|
||||
|
||||
public protocol ListSectionComponentChildView: AnyObject {
|
||||
var customUpdateIsHighlighted: ((Bool) -> Void)? { get set }
|
||||
}
|
||||
|
||||
public final class ListSectionComponent: Component {
|
||||
public typealias ChildView = ListSectionComponentChildView
|
||||
|
||||
public enum Background: Equatable {
|
||||
case none
|
||||
case all
|
||||
case range(from: AnyHashable, corners: DynamicCornerRadiusView.Corners)
|
||||
}
|
||||
|
||||
public let theme: PresentationTheme
|
||||
public let background: Background
|
||||
public let header: AnyComponent<Empty>?
|
||||
public let footer: AnyComponent<Empty>?
|
||||
public let items: [AnyComponentWithIdentity<Empty>]
|
||||
|
||||
public init(
|
||||
theme: PresentationTheme,
|
||||
background: Background = .all,
|
||||
header: AnyComponent<Empty>?,
|
||||
footer: AnyComponent<Empty>?,
|
||||
items: [AnyComponentWithIdentity<Empty>]
|
||||
) {
|
||||
self.theme = theme
|
||||
self.background = background
|
||||
self.header = header
|
||||
self.footer = footer
|
||||
self.items = items
|
||||
}
|
||||
|
||||
public static func ==(lhs: ListSectionComponent, rhs: ListSectionComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.background != rhs.background {
|
||||
return false
|
||||
}
|
||||
if lhs.header != rhs.header {
|
||||
return false
|
||||
}
|
||||
if lhs.footer != rhs.footer {
|
||||
return false
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let contentView: UIView
|
||||
private let contentBackgroundView: DynamicCornerRadiusView
|
||||
|
||||
private var header: ComponentView<Empty>?
|
||||
private var footer: ComponentView<Empty>?
|
||||
private var itemViews: [AnyHashable: ComponentView<Empty>] = [:]
|
||||
|
||||
private var isHighlighted: Bool = false
|
||||
|
||||
private var component: ListSectionComponent?
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
self.contentView = UIView()
|
||||
self.contentView.layer.cornerRadius = 11.0
|
||||
self.contentView.clipsToBounds = true
|
||||
|
||||
self.contentBackgroundView = DynamicCornerRadiusView()
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.addSubview(self.contentBackgroundView)
|
||||
self.addSubview(self.contentView)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
private func updateIsHighlighted(isHighlighted: Bool) {
|
||||
if self.isHighlighted == isHighlighted {
|
||||
return
|
||||
}
|
||||
self.isHighlighted = isHighlighted
|
||||
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let transition: Transition
|
||||
let backgroundColor: UIColor
|
||||
if isHighlighted {
|
||||
transition = .immediate
|
||||
backgroundColor = component.theme.list.itemHighlightedBackgroundColor
|
||||
} else {
|
||||
transition = .easeInOut(duration: 0.2)
|
||||
backgroundColor = component.theme.list.itemBlocksBackgroundColor
|
||||
}
|
||||
self.contentBackgroundView.updateColor(color: backgroundColor, transition: transition)
|
||||
}
|
||||
|
||||
func update(component: ListSectionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
let backgroundColor: UIColor
|
||||
if self.isHighlighted {
|
||||
backgroundColor = component.theme.list.itemHighlightedBackgroundColor
|
||||
} else {
|
||||
backgroundColor = component.theme.list.itemBlocksBackgroundColor
|
||||
}
|
||||
self.contentBackgroundView.updateColor(color: backgroundColor, transition: transition)
|
||||
|
||||
let headerSideInset: CGFloat = 16.0
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
||||
if let headerValue = component.header {
|
||||
let header: ComponentView<Empty>
|
||||
var headerTransition = transition
|
||||
if let current = self.header {
|
||||
header = current
|
||||
} else {
|
||||
headerTransition = headerTransition.withAnimation(.none)
|
||||
header = ComponentView()
|
||||
self.header = header
|
||||
}
|
||||
|
||||
let headerSize = header.update(
|
||||
transition: headerTransition,
|
||||
component: headerValue,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - headerSideInset * 2.0, height: availableSize.height)
|
||||
)
|
||||
if let headerView = header.view {
|
||||
if headerView.superview == nil {
|
||||
self.addSubview(headerView)
|
||||
}
|
||||
headerTransition.setFrame(view: headerView, frame: CGRect(origin: CGPoint(x: headerSideInset, y: contentHeight), size: headerSize))
|
||||
}
|
||||
contentHeight += headerSize.height
|
||||
} else {
|
||||
if let header = self.header {
|
||||
self.header = nil
|
||||
header.view?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
var innerContentHeight: CGFloat = 0.0
|
||||
var validItemIds: [AnyHashable] = []
|
||||
for item in component.items {
|
||||
validItemIds.append(item.id)
|
||||
|
||||
let itemView: ComponentView<Empty>
|
||||
var itemTransition = transition
|
||||
if let current = self.itemViews[item.id] {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = itemTransition.withAnimation(.none)
|
||||
itemView = ComponentView()
|
||||
self.itemViews[item.id] = itemView
|
||||
}
|
||||
|
||||
let itemSize = itemView.update(
|
||||
transition: itemTransition,
|
||||
component: item.component,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: availableSize.height)
|
||||
)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: innerContentHeight), size: itemSize)
|
||||
if let itemComponentView = itemView.view {
|
||||
if itemComponentView.superview == nil {
|
||||
self.contentView.addSubview(itemComponentView)
|
||||
transition.animateAlpha(view: itemComponentView, from: 0.0, to: 1.0)
|
||||
|
||||
if let itemComponentView = itemComponentView as? ChildView {
|
||||
itemComponentView.customUpdateIsHighlighted = { [weak self] isHighlighted in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.updateIsHighlighted(isHighlighted: isHighlighted)
|
||||
}
|
||||
}
|
||||
}
|
||||
itemTransition.setFrame(view: itemComponentView, frame: itemFrame)
|
||||
}
|
||||
innerContentHeight += itemSize.height
|
||||
}
|
||||
var removedItemIds: [AnyHashable] = []
|
||||
for (id, itemView) in self.itemViews {
|
||||
if !validItemIds.contains(id) {
|
||||
removedItemIds.append(id)
|
||||
|
||||
if let itemComponentView = itemView.view {
|
||||
transition.setAlpha(view: itemComponentView, alpha: 0.0, completion: { [weak itemComponentView] _ in
|
||||
itemComponentView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in removedItemIds {
|
||||
self.itemViews.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
if innerContentHeight != 0.0 && contentHeight != 0.0 {
|
||||
contentHeight += 7.0
|
||||
}
|
||||
|
||||
let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: availableSize.width, height: innerContentHeight))
|
||||
transition.setFrame(view: self.contentView, frame: contentFrame)
|
||||
|
||||
let backgroundFrame: CGRect
|
||||
var backgroundAlpha: CGFloat = 1.0
|
||||
switch component.background {
|
||||
case .none:
|
||||
backgroundFrame = contentFrame
|
||||
backgroundAlpha = 0.0
|
||||
self.contentBackgroundView.update(size: backgroundFrame.size, corners: DynamicCornerRadiusView.Corners(minXMinY: 11.0, maxXMinY: 11.0, minXMaxY: 11.0, maxXMaxY: 11.0), transition: transition)
|
||||
case .all:
|
||||
backgroundFrame = contentFrame
|
||||
self.contentBackgroundView.update(size: backgroundFrame.size, corners: DynamicCornerRadiusView.Corners(minXMinY: 11.0, maxXMinY: 11.0, minXMaxY: 11.0, maxXMaxY: 11.0), transition: transition)
|
||||
case let .range(from, corners):
|
||||
if let itemComponentView = self.itemViews[from]?.view, itemComponentView.frame.minY < contentFrame.height {
|
||||
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: contentFrame.minY + itemComponentView.frame.minY), size: CGSize(width: contentFrame.width, height: contentFrame.height - itemComponentView.frame.minY))
|
||||
} else {
|
||||
backgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minY, y: contentFrame.height), size: CGSize(width: contentFrame.width, height: 0.0))
|
||||
}
|
||||
self.contentBackgroundView.update(size: backgroundFrame.size, corners: corners, transition: transition)
|
||||
}
|
||||
transition.setFrame(view: self.contentBackgroundView, frame: backgroundFrame)
|
||||
transition.setAlpha(view: self.contentBackgroundView, alpha: backgroundAlpha)
|
||||
|
||||
contentHeight += innerContentHeight
|
||||
|
||||
if let footerValue = component.footer {
|
||||
let footer: ComponentView<Empty>
|
||||
var footerTransition = transition
|
||||
if let current = self.footer {
|
||||
footer = current
|
||||
} else {
|
||||
footerTransition = footerTransition.withAnimation(.none)
|
||||
footer = ComponentView()
|
||||
self.footer = footer
|
||||
}
|
||||
|
||||
let footerSize = footer.update(
|
||||
transition: footerTransition,
|
||||
component: footerValue,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - headerSideInset * 2.0, height: availableSize.height)
|
||||
)
|
||||
if contentHeight != 0.0 {
|
||||
contentHeight += 7.0
|
||||
}
|
||||
if let footerView = footer.view {
|
||||
if footerView.superview == nil {
|
||||
self.addSubview(footerView)
|
||||
}
|
||||
footerTransition.setFrame(view: footerView, frame: CGRect(origin: CGPoint(x: headerSideInset, y: contentHeight), size: footerSize))
|
||||
}
|
||||
contentHeight += footerSize.height
|
||||
} else {
|
||||
if let footer = self.footer {
|
||||
self.footer = nil
|
||||
footer.view?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
return CGSize(width: availableSize.width, height: contentHeight)
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
@ -332,7 +332,7 @@ private func notificationsPeerCategoryEntries(peerId: EnginePeer.Id, notificatio
|
||||
}
|
||||
}
|
||||
existingThreadIds.insert(value.threadId)
|
||||
entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, .channel(TelegramChannel(id: peerId, accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(TelegramChannelGroupInfo(flags: [])), flags: [.isForum], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId))
|
||||
entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, .channel(TelegramChannel(id: peerId, accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(TelegramChannelGroupInfo(flags: [])), flags: [.isForum], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId))
|
||||
index += 1
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ swift_library(
|
||||
"//submodules/Markdown",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||
"//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/Components/PagerComponent",
|
||||
"//submodules/PremiumUI",
|
||||
|
@ -20,82 +20,7 @@ import BundleIconComponent
|
||||
import AnimatedTextComponent
|
||||
import TextFormat
|
||||
import AudioToolbox
|
||||
|
||||
private final class ButtonSubtitleComponent: CombinedComponent {
|
||||
let count: Int
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
|
||||
init(count: Int, theme: PresentationTheme, strings: PresentationStrings) {
|
||||
self.count = count
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
}
|
||||
|
||||
static func ==(lhs: ButtonSubtitleComponent, rhs: ButtonSubtitleComponent) -> Bool {
|
||||
if lhs.count != rhs.count {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let icon = Child(BundleIconComponent.self)
|
||||
let text = Child(AnimatedTextComponent.self)
|
||||
|
||||
return { context in
|
||||
let icon = icon.update(
|
||||
component: BundleIconComponent(
|
||||
name: "Chat/Input/Accessory Panels/TextLockIcon",
|
||||
tintColor: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7),
|
||||
maxSize: CGSize(width: 10.0, height: 10.0)
|
||||
),
|
||||
availableSize: CGSize(width: 100.0, height: 100.0),
|
||||
transition: context.transition
|
||||
)
|
||||
var textItems: [AnimatedTextComponent.Item] = []
|
||||
|
||||
let levelString = context.component.strings.ChannelReactions_LevelRequiredLabel("")
|
||||
var previousIndex = 0
|
||||
let nsLevelString = levelString.string as NSString
|
||||
for range in levelString.ranges.sorted(by: { $0.range.lowerBound < $1.range.lowerBound }) {
|
||||
if range.range.lowerBound > previousIndex {
|
||||
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: range.range.lowerBound - previousIndex)))))
|
||||
}
|
||||
if range.index == 0 {
|
||||
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .number(context.component.count, minDigits: 1)))
|
||||
}
|
||||
previousIndex = range.range.upperBound
|
||||
}
|
||||
if nsLevelString.length > previousIndex {
|
||||
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(100), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: nsLevelString.length - previousIndex)))))
|
||||
}
|
||||
|
||||
let text = text.update(
|
||||
component: AnimatedTextComponent(font: Font.medium(11.0), color: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7), items: textItems),
|
||||
availableSize: CGSize(width: context.availableSize.width - 20.0, height: 100.0),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let spacing: CGFloat = 3.0
|
||||
let size = CGSize(width: icon.size.width + spacing + text.size.width, height: text.size.height)
|
||||
context.add(icon
|
||||
.position(icon.size.centered(in: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: icon.size.width, height: size.height))).center)
|
||||
)
|
||||
context.add(text
|
||||
.position(text.size.centered(in: CGRect(origin: CGPoint(x: icon.size.width + spacing, y: 0.0), size: text.size)).center)
|
||||
)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
}
|
||||
import PremiumLockButtonSubtitleComponent
|
||||
|
||||
final class PeerAllowedReactionsScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
@ -904,7 +829,7 @@ final class PeerAllowedReactionsScreenComponent: Component {
|
||||
}).count : 0
|
||||
|
||||
if let boostStatus = self.boostStatus, customReactionCount > boostStatus.level {
|
||||
buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(ButtonSubtitleComponent(
|
||||
buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(PremiumLockButtonSubtitleComponent(
|
||||
count: customReactionCount,
|
||||
theme: environment.theme,
|
||||
strings: environment.strings
|
||||
|
@ -34,15 +34,17 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem {
|
||||
let id: AnyHashable
|
||||
let label: Label
|
||||
let additionalBadgeLabel: String?
|
||||
let additionalBadgeIcon: UIImage?
|
||||
let text: String
|
||||
let icon: UIImage?
|
||||
let iconSignal: Signal<UIImage?, NoError>?
|
||||
let action: (() -> Void)?
|
||||
|
||||
init(id: AnyHashable, label: Label = .none, additionalBadgeLabel: String? = nil, text: String, icon: UIImage? = nil, iconSignal: Signal<UIImage?, NoError>? = nil, action: (() -> Void)?) {
|
||||
init(id: AnyHashable, label: Label = .none, additionalBadgeLabel: String? = nil, additionalBadgeIcon: UIImage? = nil, text: String, icon: UIImage? = nil, iconSignal: Signal<UIImage?, NoError>? = nil, action: (() -> Void)?) {
|
||||
self.id = id
|
||||
self.label = label
|
||||
self.additionalBadgeLabel = additionalBadgeLabel
|
||||
self.additionalBadgeIcon = additionalBadgeIcon
|
||||
self.text = text
|
||||
self.icon = icon
|
||||
self.iconSignal = iconSignal
|
||||
@ -61,6 +63,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
private let labelBadgeNode: ASImageNode
|
||||
private let labelNode: ImmediateTextNode
|
||||
private var additionalLabelNode: ImmediateTextNode?
|
||||
private var additionalLabelBadgeNode: ASImageNode?
|
||||
private let textNode: ImmediateTextNode
|
||||
private let arrowNode: ASImageNode
|
||||
private let bottomSeparatorNode: ASDisplayNode
|
||||
@ -249,6 +252,24 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
self.labelBadgeNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let additionalBadgeIcon = item.additionalBadgeIcon {
|
||||
let additionalLabelBadgeNode: ASImageNode
|
||||
if let current = self.additionalLabelBadgeNode {
|
||||
additionalLabelBadgeNode = current
|
||||
} else {
|
||||
additionalLabelBadgeNode = ASImageNode()
|
||||
additionalLabelBadgeNode.isUserInteractionEnabled = false
|
||||
self.additionalLabelBadgeNode = additionalLabelBadgeNode
|
||||
self.insertSubnode(additionalLabelBadgeNode, belowSubnode: self.labelNode)
|
||||
}
|
||||
additionalLabelBadgeNode.image = additionalBadgeIcon
|
||||
} else {
|
||||
if let additionalLabelBadgeNode = self.additionalLabelBadgeNode {
|
||||
self.additionalLabelBadgeNode = nil
|
||||
additionalLabelBadgeNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
var badgeWidth = max(badgeDiameter, labelSize.width + 10.0)
|
||||
if case .semitransparentBadge = item.label {
|
||||
badgeWidth += 2.0
|
||||
@ -283,6 +304,11 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
additionalLabelNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let additionalLabelBadgeNode = self.additionalLabelBadgeNode, let image = additionalLabelBadgeNode.image {
|
||||
let additionalLabelSize = image.size
|
||||
additionalLabelBadgeNode.frame = CGRect(origin: CGPoint(x: textFrame.maxX + 6.0, y: floor((height - additionalLabelSize.height) / 2.0) + 1.0), size: additionalLabelSize)
|
||||
}
|
||||
|
||||
let labelBadgeNodeFrame: CGRect
|
||||
if case let .image(_, imageSize) = item.label {
|
||||
labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - imageSize.width, y: floorToScreenPixels(textFrame.midY - imageSize.height / 2.0)), size:imageSize)
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "PremiumLockButtonSubtitleComponent",
|
||||
module_name = "PremiumLockButtonSubtitleComponent",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/Display",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,83 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramPresentationData
|
||||
import BundleIconComponent
|
||||
import AnimatedTextComponent
|
||||
|
||||
public final class PremiumLockButtonSubtitleComponent: CombinedComponent {
|
||||
public let count: Int
|
||||
public let theme: PresentationTheme
|
||||
public let strings: PresentationStrings
|
||||
|
||||
public init(count: Int, theme: PresentationTheme, strings: PresentationStrings) {
|
||||
self.count = count
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
}
|
||||
|
||||
public static func ==(lhs: PremiumLockButtonSubtitleComponent, rhs: PremiumLockButtonSubtitleComponent) -> Bool {
|
||||
if lhs.count != rhs.count {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public static var body: Body {
|
||||
let icon = Child(BundleIconComponent.self)
|
||||
let text = Child(AnimatedTextComponent.self)
|
||||
|
||||
return { context in
|
||||
let icon = icon.update(
|
||||
component: BundleIconComponent(
|
||||
name: "Chat/Input/Accessory Panels/TextLockIcon",
|
||||
tintColor: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7),
|
||||
maxSize: CGSize(width: 10.0, height: 10.0)
|
||||
),
|
||||
availableSize: CGSize(width: 100.0, height: 100.0),
|
||||
transition: context.transition
|
||||
)
|
||||
var textItems: [AnimatedTextComponent.Item] = []
|
||||
|
||||
let levelString = context.component.strings.ChannelReactions_LevelRequiredLabel("")
|
||||
var previousIndex = 0
|
||||
let nsLevelString = levelString.string as NSString
|
||||
for range in levelString.ranges.sorted(by: { $0.range.lowerBound < $1.range.lowerBound }) {
|
||||
if range.range.lowerBound > previousIndex {
|
||||
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: range.range.lowerBound - previousIndex)))))
|
||||
}
|
||||
if range.index == 0 {
|
||||
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .number(context.component.count, minDigits: 1)))
|
||||
}
|
||||
previousIndex = range.range.upperBound
|
||||
}
|
||||
if nsLevelString.length > previousIndex {
|
||||
textItems.append(AnimatedTextComponent.Item(id: AnyHashable(100), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: nsLevelString.length - previousIndex)))))
|
||||
}
|
||||
|
||||
let text = text.update(
|
||||
component: AnimatedTextComponent(font: Font.medium(11.0), color: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7), items: textItems),
|
||||
availableSize: CGSize(width: context.availableSize.width - 20.0, height: 100.0),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let spacing: CGFloat = 3.0
|
||||
let size = CGSize(width: icon.size.width + spacing + text.size.width, height: text.size.height)
|
||||
context.add(icon
|
||||
.position(icon.size.centered(in: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: icon.size.width, height: size.height))).center)
|
||||
)
|
||||
context.add(text
|
||||
.position(text.size.centered(in: CGRect(origin: CGPoint(x: icon.size.width + spacing, y: 0.0), size: text.size)).center)
|
||||
)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ swift_library(
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/UndoUI",
|
||||
"//submodules/WallpaperBackgroundNode",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
"//submodules/TelegramUI/Components/EntityKeyboard",
|
||||
"//submodules/SolidRoundedButtonNode",
|
||||
@ -28,6 +29,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",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -11,10 +11,11 @@ import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import AccountContext
|
||||
import WallpaperBackgroundNode
|
||||
import ListItemComponentAdaptor
|
||||
|
||||
final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem {
|
||||
final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
|
||||
struct MessageItem: Equatable {
|
||||
static func == (lhs: MessageItem, rhs: MessageItem) -> Bool {
|
||||
static func ==(lhs: MessageItem, rhs: MessageItem) -> Bool {
|
||||
if lhs.outgoing != rhs.outgoing {
|
||||
return false
|
||||
}
|
||||
@ -118,6 +119,44 @@ final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func item() -> ListViewItem {
|
||||
return self
|
||||
}
|
||||
|
||||
public static func ==(lhs: PeerNameColorChatPreviewItem, rhs: PeerNameColorChatPreviewItem) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.componentTheme !== rhs.componentTheme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.fontSize != rhs.fontSize {
|
||||
return false
|
||||
}
|
||||
if lhs.chatBubbleCorners != rhs.chatBubbleCorners {
|
||||
return false
|
||||
}
|
||||
if lhs.wallpaper != rhs.wallpaper {
|
||||
return false
|
||||
}
|
||||
if lhs.dateTimeFormat != rhs.dateTimeFormat {
|
||||
return false
|
||||
}
|
||||
if lhs.nameDisplayOrder != rhs.nameDisplayOrder {
|
||||
return false
|
||||
}
|
||||
if lhs.messageItems != rhs.messageItems {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
||||
@ -329,31 +368,42 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
if params.isStandalone {
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
strongSelf.bottomStripeNode.isHidden = true
|
||||
strongSelf.maskNode.isHidden = true
|
||||
} else {
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
strongSelf.bottomStripeNode.isHidden = false
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
strongSelf.bottomStripeNode.isHidden = false
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
|
||||
@ -377,8 +427,6 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
||||
backgroundNode.updateLayout(size: backgroundNode.bounds.size, displayMode: displayMode, transition: .immediate)
|
||||
}
|
||||
strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import MergeLists
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import AccountContext
|
||||
import ListItemComponentAdaptor
|
||||
|
||||
private enum PeerNameColorEntryId: Hashable {
|
||||
case color(Int32)
|
||||
@ -313,7 +314,7 @@ private final class PeerNameColorIconItemNode : ListViewItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
final class PeerNameColorItem: ListViewItem, ItemListItem {
|
||||
final class PeerNameColorItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
|
||||
var sectionId: ItemListSectionId
|
||||
|
||||
let theme: PresentationTheme
|
||||
@ -365,6 +366,27 @@ final class PeerNameColorItem: ListViewItem, ItemListItem {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func item() -> ListViewItem {
|
||||
return self
|
||||
}
|
||||
|
||||
public static func ==(lhs: PeerNameColorItem, rhs: PeerNameColorItem) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.colors != rhs.colors {
|
||||
return false
|
||||
}
|
||||
if lhs.isProfile != rhs.isProfile {
|
||||
return false
|
||||
}
|
||||
if lhs.currentColor != rhs.currentColor {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private struct PeerNameColorItemNodeTransition {
|
||||
@ -528,24 +550,30 @@ final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode {
|
||||
strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
if item.currentColor != nil {
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
} else {
|
||||
if params.isStandalone {
|
||||
strongSelf.backgroundNode.isHidden = true
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
hasTopCorners = true
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
strongSelf.bottomStripeNode.isHidden = true
|
||||
strongSelf.maskNode.isHidden = true
|
||||
} else {
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
if item.currentColor != nil {
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
} else {
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
hasTopCorners = true
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = params.leftInset + 16.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
@ -555,15 +583,17 @@ final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode {
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
|
||||
strongSelf.containerNode.frame = CGRect(x: 0.0, y: 0.0, width: contentSize.width, height: contentSize.height)
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
|
||||
var listInsets = UIEdgeInsets()
|
||||
listInsets.top += params.leftInset + 8.0
|
||||
|
@ -14,24 +14,28 @@ import ComponentFlow
|
||||
import PeerInfoCoverComponent
|
||||
import AvatarNode
|
||||
import EmojiStatusComponent
|
||||
import ListItemComponentAdaptor
|
||||
import ComponentDisplayAdapters
|
||||
|
||||
final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem {
|
||||
final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let componentTheme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let sectionId: ItemListSectionId
|
||||
let peer: EnginePeer?
|
||||
let subtitleString: String?
|
||||
let files: [Int64: TelegramMediaFile]
|
||||
let nameDisplayOrder: PresentationPersonNameOrder
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, peer: EnginePeer?, files: [Int64: TelegramMediaFile], nameDisplayOrder: PresentationPersonNameOrder) {
|
||||
init(context: AccountContext, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, peer: EnginePeer?, subtitleString: String? = nil, files: [Int64: TelegramMediaFile], nameDisplayOrder: PresentationPersonNameOrder) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.componentTheme = componentTheme
|
||||
self.strings = strings
|
||||
self.sectionId = sectionId
|
||||
self.peer = peer
|
||||
self.subtitleString = subtitleString
|
||||
self.files = files
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
}
|
||||
@ -46,7 +50,7 @@ final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem {
|
||||
|
||||
Queue.mainQueue().async {
|
||||
completion(node, {
|
||||
return (nil, { _ in apply() })
|
||||
return (nil, { _ in apply(.None) })
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -61,13 +65,43 @@ final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem {
|
||||
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||
Queue.mainQueue().async {
|
||||
completion(layout, { _ in
|
||||
apply()
|
||||
apply(animation)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func item() -> ListViewItem {
|
||||
return self
|
||||
}
|
||||
|
||||
static func ==(lhs: PeerNameColorProfilePreviewItem, rhs: PeerNameColorProfilePreviewItem) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.componentTheme !== rhs.componentTheme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
if lhs.files != rhs.files {
|
||||
return false
|
||||
}
|
||||
if lhs.nameDisplayOrder != rhs.nameDisplayOrder {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
@ -104,7 +138,7 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
deinit {
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: PeerNameColorProfilePreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
func asyncLayout() -> (_ item: PeerNameColorProfilePreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) {
|
||||
return { [weak self] item, params, neighbors in
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
@ -117,7 +151,7 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
let layoutSize = layout.size
|
||||
|
||||
return (layout, { [weak self] in
|
||||
return (layout, { [weak self] animation in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -139,32 +173,41 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
if self.maskNode.supernode == nil {
|
||||
self.addSubnode(self.maskNode)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
||||
if params.isStandalone {
|
||||
self.topStripeNode.isHidden = true
|
||||
self.bottomStripeNode.isHidden = true
|
||||
self.maskNode.isHidden = true
|
||||
} else {
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
self.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
self.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
self.bottomStripeNode.isHidden = item.peer?.profileColor == nil
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
self.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
self.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
self.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
self.bottomStripeNode.isHidden = item.peer?.profileColor == nil
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
self.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
|
||||
@ -217,6 +260,61 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
}
|
||||
self.avatarNode.frame = avatarFrame.offsetBy(dx: coverFrame.minX, dy: coverFrame.minY)
|
||||
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 })
|
||||
|
||||
enum CredibilityIcon {
|
||||
case none
|
||||
case premium
|
||||
case verified
|
||||
case fake
|
||||
case scam
|
||||
case emojiStatus(PeerEmojiStatus)
|
||||
}
|
||||
|
||||
let credibilityIcon: CredibilityIcon
|
||||
if let peer = item.peer {
|
||||
if peer.isFake {
|
||||
credibilityIcon = .fake
|
||||
} else if peer.isScam {
|
||||
credibilityIcon = .scam
|
||||
} else if case let .user(user) = peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled {
|
||||
credibilityIcon = .emojiStatus(emojiStatus)
|
||||
} else if case let .channel(channel) = peer, let emojiStatus = channel.emojiStatus, !premiumConfiguration.isPremiumDisabled {
|
||||
credibilityIcon = .emojiStatus(emojiStatus)
|
||||
} else if peer.isVerified {
|
||||
credibilityIcon = .verified
|
||||
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled && (peer.id != item.context.account.peerId) {
|
||||
credibilityIcon = .premium
|
||||
} else {
|
||||
credibilityIcon = .none
|
||||
}
|
||||
} else {
|
||||
credibilityIcon = .none
|
||||
}
|
||||
|
||||
let statusColor: UIColor
|
||||
if let peer = item.peer, peer.profileColor != nil {
|
||||
statusColor = .white
|
||||
} else {
|
||||
statusColor = item.theme.list.itemCheckColors.fillColor
|
||||
}
|
||||
|
||||
let emojiStatusContent: EmojiStatusComponent.Content
|
||||
switch credibilityIcon {
|
||||
case .none:
|
||||
emojiStatusContent = .none
|
||||
case .premium:
|
||||
emojiStatusContent = .premium(color: statusColor)
|
||||
case .verified:
|
||||
emojiStatusContent = .verified(fillColor: statusColor, foregroundColor: .clear, sizeType: .large)
|
||||
case .fake:
|
||||
emojiStatusContent = .text(color: item.theme.chat.message.incoming.scamColor, string: item.strings.Message_FakeAccount.uppercased())
|
||||
case .scam:
|
||||
emojiStatusContent = .text(color: item.theme.chat.message.incoming.scamColor, string: item.strings.Message_ScamAccount.uppercased())
|
||||
case let .emojiStatus(emojiStatus):
|
||||
emojiStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: item.theme.list.mediaPlaceholderColor, themeColor: statusColor, loopMode: .forever)
|
||||
}
|
||||
|
||||
let backgroundColor: UIColor
|
||||
let titleColor: UIColor
|
||||
let subtitleColor: UIColor
|
||||
@ -239,15 +337,64 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: coverFrame.width - 16.0, height: 100.0)
|
||||
)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: coverFrame.minX + floor((coverFrame.width - titleSize.width) * 0.5), y: avatarFrame.maxY + 10.0), size: titleSize)
|
||||
|
||||
var titleContentWidth = titleSize.width
|
||||
var hasStatusIcon = false
|
||||
if case .none = emojiStatusContent {
|
||||
} else {
|
||||
hasStatusIcon = true
|
||||
titleContentWidth += 4.0 + 34.0
|
||||
}
|
||||
let _ = hasStatusIcon
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: coverFrame.minX + floor((coverFrame.width - titleContentWidth) * 0.5), y: avatarFrame.maxY + 10.0), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
titleView.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
|
||||
self.view.addSubview(titleView)
|
||||
}
|
||||
titleView.frame = titleFrame
|
||||
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
|
||||
animation.animator.updatePosition(layer: titleView.layer, position: titleFrame.origin, completion: nil)
|
||||
}
|
||||
|
||||
let subtitleString: String = item.strings.LastSeen_JustNow
|
||||
let icon: ComponentView<Empty>
|
||||
if let current = self.icon {
|
||||
icon = current
|
||||
} else {
|
||||
icon = ComponentView()
|
||||
self.icon = icon
|
||||
}
|
||||
let iconSize = CGSize(width: 34.0, height: 34.0)
|
||||
let _ = icon.update(
|
||||
transition: Transition(animation.transition),
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: item.context,
|
||||
animationCache: item.context.animationCache,
|
||||
animationRenderer: item.context.animationRenderer,
|
||||
content: emojiStatusContent,
|
||||
isVisibleForAnimations: true,
|
||||
action: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: iconSize
|
||||
)
|
||||
if let iconView = icon.view {
|
||||
if iconView.superview == nil {
|
||||
self.view.addSubview(iconView)
|
||||
}
|
||||
let iconFrame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: titleFrame.minY + floorToScreenPixels((titleFrame.height - iconSize.height) * 0.5)), size: iconSize)
|
||||
iconView.bounds = CGRect(origin: CGPoint(), size: iconFrame.size)
|
||||
animation.animator.updatePosition(layer: iconView.layer, position: iconFrame.center, completion: nil)
|
||||
}
|
||||
|
||||
let subtitleString: String
|
||||
if let value = item.subtitleString {
|
||||
subtitleString = value
|
||||
} else if case .channel = item.peer {
|
||||
subtitleString = item.strings.Channel_Status
|
||||
} else {
|
||||
subtitleString = item.strings.LastSeen_JustNow
|
||||
}
|
||||
let subtitleSize = self.subtitle.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(Text(
|
||||
@ -265,8 +412,6 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
self.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
self.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
self.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ThemeCarouselItem",
|
||||
module_name = "ThemeCarouselItem",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/Display",
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/MergeLists",
|
||||
"//submodules/TelegramUIPreferences",
|
||||
"//submodules/ItemListUI",
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/WallpaperResources",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/AppBundle",
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode",
|
||||
"//submodules/ShimmerEffect",
|
||||
"//submodules/StickerResources",
|
||||
"//submodules/TelegramUI/Components/ListItemComponentAdaptor",
|
||||
"//submodules/HexColor",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -18,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
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ThemeSettingsThemeItem",
|
||||
module_name = "ThemeSettingsThemeItem",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/Display",
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/MergeLists",
|
||||
"//submodules/TelegramUIPreferences",
|
||||
"//submodules/ItemListUI",
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/WallpaperResources",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/AppBundle",
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/HexColor",
|
||||
"//submodules/TelegramUI/Components/ListItemComponentAdaptor",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -14,6 +14,8 @@ import WallpaperResources
|
||||
import AccountContext
|
||||
import AppBundle
|
||||
import ContextUI
|
||||
import ListItemComponentAdaptor
|
||||
import HexColor
|
||||
|
||||
private struct ThemeSettingsThemeEntry: Comparable, Identifiable {
|
||||
let index: Int
|
||||
@ -369,24 +371,24 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
|
||||
var sectionId: ItemListSectionId
|
||||
public class ThemeSettingsThemeItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator {
|
||||
public var sectionId: ItemListSectionId
|
||||
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let themes: [PresentationThemeReference]
|
||||
let allThemes: [PresentationThemeReference]
|
||||
let displayUnsupported: Bool
|
||||
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||
let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||
let themePreferredBaseTheme: [Int64: TelegramBaseTheme]
|
||||
let currentTheme: PresentationThemeReference
|
||||
let updatedTheme: (PresentationThemeReference) -> Void
|
||||
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
let tag: ItemListItemTag?
|
||||
public let context: AccountContext
|
||||
public let theme: PresentationTheme
|
||||
public let strings: PresentationStrings
|
||||
public let themes: [PresentationThemeReference]
|
||||
public let allThemes: [PresentationThemeReference]
|
||||
public let displayUnsupported: Bool
|
||||
public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||
public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||
public let themePreferredBaseTheme: [Int64: TelegramBaseTheme]
|
||||
public let currentTheme: PresentationThemeReference
|
||||
public let updatedTheme: (PresentationThemeReference) -> Void
|
||||
public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
public let tag: ItemListItemTag?
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], allThemes: [PresentationThemeReference], displayUnsupported: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], themePreferredBaseTheme: [Int64: TelegramBaseTheme], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
|
||||
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], allThemes: [PresentationThemeReference], displayUnsupported: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], themePreferredBaseTheme: [Int64: TelegramBaseTheme], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
@ -403,7 +405,7 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
|
||||
self.sectionId = sectionId
|
||||
}
|
||||
|
||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
async {
|
||||
let node = ThemeSettingsThemeItemNode()
|
||||
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||
@ -419,7 +421,7 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
|
||||
}
|
||||
}
|
||||
|
||||
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
Queue.mainQueue().async {
|
||||
if let nodeValue = node() as? ThemeSettingsThemeItemNode {
|
||||
let makeLayout = nodeValue.asyncLayout()
|
||||
@ -435,6 +437,42 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func item() -> ListViewItem {
|
||||
return self
|
||||
}
|
||||
|
||||
public static func ==(lhs: ThemeSettingsThemeItem, rhs: ThemeSettingsThemeItem) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.themes != rhs.themes {
|
||||
return false
|
||||
}
|
||||
if lhs.displayUnsupported != rhs.displayUnsupported {
|
||||
return false
|
||||
}
|
||||
if lhs.themeSpecificAccentColors != rhs.themeSpecificAccentColors {
|
||||
return false
|
||||
}
|
||||
if lhs.themeSpecificChatWallpapers != rhs.themeSpecificChatWallpapers {
|
||||
return false
|
||||
}
|
||||
if lhs.themePreferredBaseTheme != rhs.themePreferredBaseTheme {
|
||||
return false
|
||||
}
|
||||
if lhs.currentTheme != rhs.currentTheme {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private struct ThemeSettingsThemeItemNodeTransition {
|
||||
@ -472,7 +510,7 @@ private func ensureThemeVisible(listNode: ListView, themeReference: Presentation
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
public class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
private let containerNode: ASDisplayNode
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
@ -488,13 +526,13 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
private var item: ThemeSettingsThemeItem?
|
||||
private var layoutParams: ListViewItemLayoutParams?
|
||||
|
||||
var tag: ItemListItemTag? {
|
||||
public var tag: ItemListItemTag? {
|
||||
return self.item?.tag
|
||||
}
|
||||
|
||||
private var tapping = false
|
||||
|
||||
init() {
|
||||
public init() {
|
||||
self.containerNode = ASDisplayNode()
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
@ -517,7 +555,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
self.addSubnode(self.listNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
}
|
||||
@ -558,7 +596,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
})
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: ThemeSettingsThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
public func asyncLayout() -> (_ item: ThemeSettingsThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
return { item, params, neighbors in
|
||||
let contentSize: CGSize
|
||||
let insets: UIEdgeInsets
|
||||
@ -591,20 +629,26 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
if strongSelf.maskNode.supernode == nil {
|
||||
strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
|
||||
if params.isStandalone {
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
strongSelf.bottomStripeNode.isHidden = true
|
||||
strongSelf.maskNode.isHidden = true
|
||||
strongSelf.backgroundNode.isHidden = true
|
||||
} else {
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
let bottomStripeOffset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = params.leftInset + 16.0
|
||||
bottomStripeOffset = -separatorHeight
|
||||
@ -614,15 +658,18 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
bottomStripeOffset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
|
||||
strongSelf.containerNode.frame = CGRect(x: 0.0, y: 0.0, width: contentSize.width, height: contentSize.height)
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
|
||||
var listInsets = UIEdgeInsets()
|
||||
listInsets.top += params.leftInset + 4.0
|
||||
@ -694,15 +741,15 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||
override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||
}
|
||||
|
||||
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
func prepareCrossfadeTransition() {
|
||||
public func prepareCrossfadeTransition() {
|
||||
guard self.snapshotView == nil else {
|
||||
return
|
||||
}
|
||||
@ -719,7 +766,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
func animateCrossfadeTransition() {
|
||||
public func animateCrossfadeTransition() {
|
||||
guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else {
|
||||
return
|
||||
}
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user