mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 06:10:03 +00:00
Channel appearance
This commit is contained in:
parent
3fc9d37ba7
commit
2708aa0a41
@ -1388,7 +1388,8 @@ public class PeerNameColors: Equatable {
|
|||||||
profilePaletteDarkColors: [:],
|
profilePaletteDarkColors: [:],
|
||||||
profileStoryColors: [:],
|
profileStoryColors: [:],
|
||||||
profileStoryDarkColors: [:],
|
profileStoryDarkColors: [:],
|
||||||
profileDisplayOrder: []
|
profileDisplayOrder: [],
|
||||||
|
nameColorsChannelMinRequiredBoostLevel: [:]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1404,6 +1405,8 @@ public class PeerNameColors: Equatable {
|
|||||||
public let profileStoryDarkColors: [Int32: Colors]
|
public let profileStoryDarkColors: [Int32: Colors]
|
||||||
public let profileDisplayOrder: [Int32]
|
public let profileDisplayOrder: [Int32]
|
||||||
|
|
||||||
|
public let nameColorsChannelMinRequiredBoostLevel: [Int32: Int32]
|
||||||
|
|
||||||
public func get(_ color: PeerNameColor, dark: Bool = false) -> Colors {
|
public func get(_ color: PeerNameColor, dark: Bool = false) -> Colors {
|
||||||
if dark, let colors = self.darkColors[color.rawValue] {
|
if dark, let colors = self.darkColors[color.rawValue] {
|
||||||
return colors
|
return colors
|
||||||
@ -1453,7 +1456,8 @@ public class PeerNameColors: Equatable {
|
|||||||
profilePaletteDarkColors: [Int32: Colors],
|
profilePaletteDarkColors: [Int32: Colors],
|
||||||
profileStoryColors: [Int32: Colors],
|
profileStoryColors: [Int32: Colors],
|
||||||
profileStoryDarkColors: [Int32: Colors],
|
profileStoryDarkColors: [Int32: Colors],
|
||||||
profileDisplayOrder: [Int32]
|
profileDisplayOrder: [Int32],
|
||||||
|
nameColorsChannelMinRequiredBoostLevel: [Int32: Int32]
|
||||||
) {
|
) {
|
||||||
self.colors = colors
|
self.colors = colors
|
||||||
self.darkColors = darkColors
|
self.darkColors = darkColors
|
||||||
@ -1465,6 +1469,7 @@ public class PeerNameColors: Equatable {
|
|||||||
self.profileStoryColors = profileStoryColors
|
self.profileStoryColors = profileStoryColors
|
||||||
self.profileStoryDarkColors = profileStoryDarkColors
|
self.profileStoryDarkColors = profileStoryDarkColors
|
||||||
self.profileDisplayOrder = profileDisplayOrder
|
self.profileDisplayOrder = profileDisplayOrder
|
||||||
|
self.nameColorsChannelMinRequiredBoostLevel = nameColorsChannelMinRequiredBoostLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func with(availableReplyColors: EngineAvailableColorOptions, availableProfileColors: EngineAvailableColorOptions) -> PeerNameColors {
|
public static func with(availableReplyColors: EngineAvailableColorOptions, availableProfileColors: EngineAvailableColorOptions) -> PeerNameColors {
|
||||||
@ -1479,8 +1484,14 @@ public class PeerNameColors: Equatable {
|
|||||||
var profileStoryDarkColors: [Int32: Colors] = [:]
|
var profileStoryDarkColors: [Int32: Colors] = [:]
|
||||||
var profileDisplayOrder: [Int32] = []
|
var profileDisplayOrder: [Int32] = []
|
||||||
|
|
||||||
|
var nameColorsChannelMinRequiredBoostLevel: [Int32: Int32] = [:]
|
||||||
|
|
||||||
if !availableReplyColors.options.isEmpty {
|
if !availableReplyColors.options.isEmpty {
|
||||||
for option in availableReplyColors.options {
|
for option in availableReplyColors.options {
|
||||||
|
if let requiredChannelMinBoostLevel = option.value.requiredChannelMinBoostLevel {
|
||||||
|
nameColorsChannelMinRequiredBoostLevel[option.key] = requiredChannelMinBoostLevel
|
||||||
|
}
|
||||||
|
|
||||||
if let parsedLight = PeerNameColors.Colors(colors: option.value.light.background) {
|
if let parsedLight = PeerNameColors.Colors(colors: option.value.light.background) {
|
||||||
colors[option.key] = parsedLight
|
colors[option.key] = parsedLight
|
||||||
}
|
}
|
||||||
@ -1539,7 +1550,8 @@ public class PeerNameColors: Equatable {
|
|||||||
profilePaletteDarkColors: profilePaletteDarkColors,
|
profilePaletteDarkColors: profilePaletteDarkColors,
|
||||||
profileStoryColors: profileStoryColors,
|
profileStoryColors: profileStoryColors,
|
||||||
profileStoryDarkColors: profileStoryDarkColors,
|
profileStoryDarkColors: profileStoryDarkColors,
|
||||||
profileDisplayOrder: profileDisplayOrder
|
profileDisplayOrder: profileDisplayOrder,
|
||||||
|
nameColorsChannelMinRequiredBoostLevel: nameColorsChannelMinRequiredBoostLevel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4739,7 +4739,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
|||||||
if self.experimentalSnapScrollToItem {
|
if self.experimentalSnapScrollToItem {
|
||||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: ListViewScrollToItem(index: index, position: ListViewScrollPosition.visible, animated: animated, curve: ListViewAnimationCurve.Default(duration: nil), directionHint: ListViewScrollToItemDirectionHint.Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: ListViewScrollToItem(index: index, position: ListViewScrollPosition.visible, animated: animated, curve: ListViewAnimationCurve.Default(duration: nil), directionHint: ListViewScrollToItemDirectionHint.Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||||
} else {
|
} else {
|
||||||
if node.frame.minY < self.insets.top {
|
if node.frame.minY < self.insets.top + overflow {
|
||||||
if !allowIntersection || node.frame.maxY < self.insets.top {
|
if !allowIntersection || node.frame.maxY < self.insets.top {
|
||||||
let position: ListViewScrollPosition
|
let position: ListViewScrollPosition
|
||||||
if allowIntersection {
|
if allowIntersection {
|
||||||
@ -4749,7 +4749,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
|||||||
}
|
}
|
||||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: ListViewScrollToItem(index: index, position: position, animated: animated, curve: curve, directionHint: ListViewScrollToItemDirectionHint.Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: ListViewScrollToItem(index: index, position: position, animated: animated, curve: curve, directionHint: ListViewScrollToItemDirectionHint.Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||||
}
|
}
|
||||||
} else if node.frame.maxY > self.visibleSize.height - self.insets.bottom {
|
} else if node.frame.maxY > self.visibleSize.height - self.insets.bottom - overflow {
|
||||||
if !allowIntersection || node.frame.minY > self.visibleSize.height - self.insets.bottom {
|
if !allowIntersection || node.frame.minY > self.visibleSize.height - self.insets.bottom {
|
||||||
let position: ListViewScrollPosition
|
let position: ListViewScrollPosition
|
||||||
if allowIntersection {
|
if allowIntersection {
|
||||||
|
|||||||
@ -20,25 +20,18 @@ import SolidRoundedButtonComponent
|
|||||||
import BlurredBackgroundComponent
|
import BlurredBackgroundComponent
|
||||||
import UndoUI
|
import UndoUI
|
||||||
|
|
||||||
public enum BoostSubject: Equatable {
|
func requiredBoostSubjectLevel(subject: BoostSubject, context: AccountContext, configuration: PremiumConfiguration) -> Int32 {
|
||||||
case stories
|
switch subject {
|
||||||
case channelReactions(reactionCount: Int32)
|
|
||||||
case nameColors
|
|
||||||
case nameIcon
|
|
||||||
case profileColors
|
|
||||||
case profileIcon
|
|
||||||
case emojiStatus
|
|
||||||
case wallpaper
|
|
||||||
case customWallpaper
|
|
||||||
|
|
||||||
public func requiredLevel(_ configuration: PremiumConfiguration) -> Int32 {
|
|
||||||
switch self {
|
|
||||||
case .stories:
|
case .stories:
|
||||||
return 1
|
return 1
|
||||||
case let .channelReactions(reactionCount):
|
case let .channelReactions(reactionCount):
|
||||||
return reactionCount
|
return reactionCount
|
||||||
case .nameColors:
|
case let .nameColors(colors):
|
||||||
return configuration.minChannelNameColorLevel
|
if let value = context.peerNameColors.nameColorsChannelMinRequiredBoostLevel[colors.rawValue] {
|
||||||
|
return value
|
||||||
|
} else {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
case .nameIcon:
|
case .nameIcon:
|
||||||
return configuration.minChannelNameIconLevel
|
return configuration.minChannelNameIconLevel
|
||||||
case .profileColors:
|
case .profileColors:
|
||||||
@ -53,6 +46,21 @@ public enum BoostSubject: Equatable {
|
|||||||
return configuration.minChannelCustomWallpaperLevel
|
return configuration.minChannelCustomWallpaperLevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum BoostSubject: Equatable {
|
||||||
|
case stories
|
||||||
|
case channelReactions(reactionCount: Int32)
|
||||||
|
case nameColors(colors: PeerNameColor)
|
||||||
|
case nameIcon
|
||||||
|
case profileColors
|
||||||
|
case profileIcon
|
||||||
|
case emojiStatus
|
||||||
|
case wallpaper
|
||||||
|
case customWallpaper
|
||||||
|
|
||||||
|
public func requiredLevel(context: AccountContext, configuration: PremiumConfiguration) -> Int32 {
|
||||||
|
return requiredBoostSubjectLevel(subject: self, context: context, configuration: configuration)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class LevelHeaderComponent: CombinedComponent {
|
private final class LevelHeaderComponent: CombinedComponent {
|
||||||
@ -479,7 +487,9 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
textString = strings.ChannelBoost_CustomReactionsText("\(reactionCount)", "\(reactionCount)").string
|
textString = strings.ChannelBoost_CustomReactionsText("\(reactionCount)", "\(reactionCount)").string
|
||||||
needsSecondParagraph = false
|
needsSecondParagraph = false
|
||||||
case .nameColors:
|
case .nameColors:
|
||||||
textString = strings.ChannelBoost_EnableNameColorLevelText("\(premiumConfiguration.minChannelNameColorLevel)").string
|
let colorLevel = component.subject.requiredLevel(context: context.component.context, configuration: premiumConfiguration)
|
||||||
|
|
||||||
|
textString = strings.ChannelBoost_EnableNameColorLevelText("\(colorLevel)").string
|
||||||
case .nameIcon:
|
case .nameIcon:
|
||||||
textString = strings.ChannelBoost_EnableNameIconLevelText("\(premiumConfiguration.minChannelNameIconLevel)").string
|
textString = strings.ChannelBoost_EnableNameIconLevelText("\(premiumConfiguration.minChannelNameIconLevel)").string
|
||||||
case .profileColors:
|
case .profileColors:
|
||||||
|
|||||||
@ -1180,6 +1180,7 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
if let remaining {
|
if let remaining {
|
||||||
let storiesString = strings.ChannelBoost_StoriesPerDay(level + 1)
|
let storiesString = strings.ChannelBoost_StoriesPerDay(level + 1)
|
||||||
let valueString = strings.ChannelBoost_MoreBoosts(remaining)
|
let valueString = strings.ChannelBoost_MoreBoosts(remaining)
|
||||||
|
|
||||||
switch boostSubject {
|
switch boostSubject {
|
||||||
case .stories:
|
case .stories:
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
@ -1189,9 +1190,12 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
titleText = strings.ChannelBoost_IncreaseLimit
|
titleText = strings.ChannelBoost_IncreaseLimit
|
||||||
string = strings.ChannelBoost_IncreaseLimitText(valueString, storiesString).string
|
string = strings.ChannelBoost_IncreaseLimitText(valueString, storiesString).string
|
||||||
}
|
}
|
||||||
case .nameColors:
|
case let .nameColors(colors):
|
||||||
titleText = strings.ChannelBoost_EnableColors
|
titleText = strings.ChannelBoost_EnableColors
|
||||||
string = strings.ChannelBoost_EnableColorsLevelText("\(premiumConfiguration.minChannelNameColorLevel)").string
|
|
||||||
|
let colorLevel = requiredBoostSubjectLevel(subject: .nameColors(colors: colors), context: component.context, configuration: premiumConfiguration)
|
||||||
|
|
||||||
|
string = strings.ChannelBoost_EnableColorsLevelText("\(colorLevel)").string
|
||||||
case let .channelReactions(reactionCount):
|
case let .channelReactions(reactionCount):
|
||||||
titleText = strings.ChannelBoost_CustomReactions
|
titleText = strings.ChannelBoost_CustomReactions
|
||||||
string = strings.ChannelBoost_CustomReactionsText("\(reactionCount)", "\(reactionCount)").string
|
string = strings.ChannelBoost_CustomReactionsText("\(reactionCount)", "\(reactionCount)").string
|
||||||
@ -1778,7 +1782,7 @@ public class PremiumLimitScreen: ViewControllerComponentContainer {
|
|||||||
|
|
||||||
public enum BoostSubject: Equatable {
|
public enum BoostSubject: Equatable {
|
||||||
case stories
|
case stories
|
||||||
case nameColors
|
case nameColors(colors: PeerNameColor)
|
||||||
case channelReactions(reactionCount: Int)
|
case channelReactions(reactionCount: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -193,10 +193,14 @@ private final class ThemeGridThemeItemIconNode : ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let string: String?
|
let string: String?
|
||||||
if let _ = item.themeReference.emoticon {
|
if let themeReference = item.themeReference {
|
||||||
|
if let _ = themeReference.emoticon {
|
||||||
string = nil
|
string = nil
|
||||||
} else {
|
} else {
|
||||||
string = themeDisplayName(strings: item.strings, reference: item.themeReference)
|
string = themeDisplayName(strings: item.strings, reference: themeReference)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
string = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let text = NSAttributedString(string: string ?? item.strings.Conversation_Theme_NoTheme, font: Font.bold(14.0), textColor: .white)
|
let text = NSAttributedString(string: string ?? item.strings.Conversation_Theme_NoTheme, font: Font.bold(14.0), textColor: .white)
|
||||||
@ -207,7 +211,7 @@ private final class ThemeGridThemeItemIconNode : ASDisplayNode {
|
|||||||
let (_, emojiApply) = makeEmojiLayout(TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: size.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
let (_, emojiApply) = makeEmojiLayout(TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: size.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
if updatedThemeReference || updatedWallpaper || updatedNightMode || updatedSize {
|
if updatedThemeReference || updatedWallpaper || updatedNightMode || updatedSize {
|
||||||
var themeReference = item.themeReference
|
if var themeReference = item.themeReference {
|
||||||
if case .builtin = themeReference, item.nightMode {
|
if case .builtin = themeReference, item.nightMode {
|
||||||
themeReference = .builtin(.night)
|
themeReference = .builtin(.night)
|
||||||
}
|
}
|
||||||
@ -218,6 +222,7 @@ private final class ThemeGridThemeItemIconNode : ASDisplayNode {
|
|||||||
self.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: color, wallpaper: wallpaper ?? item.wallpaper, nightMode: item.nightMode, emoticon: true, large: true))
|
self.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: color, wallpaper: wallpaper ?? item.wallpaper, nightMode: item.nightMode, emoticon: true, large: true))
|
||||||
self.imageNode.backgroundColor = nil
|
self.imageNode.backgroundColor = nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if updatedTheme || updatedSelected {
|
if updatedTheme || updatedSelected {
|
||||||
self.overlayNode.image = generateBorderImage(theme: item.theme, bordered: false, selected: item.selected)
|
self.overlayNode.image = generateBorderImage(theme: item.theme, bordered: false, selected: item.selected)
|
||||||
@ -501,7 +506,9 @@ class ThemeGridThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
let selected = item.currentTheme.index == theme.index
|
let selected = item.currentTheme.index == theme.index
|
||||||
|
|
||||||
let iconItem = ThemeCarouselThemeIconItem(context: item.context, emojiFile: theme.emoticon.flatMap { item.animatedEmojiStickers[$0]?.first?.file }, themeReference: theme, nightMode: item.nightMode, channelMode: false, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil, action: { theme in
|
let iconItem = ThemeCarouselThemeIconItem(context: item.context, emojiFile: theme.emoticon.flatMap { item.animatedEmojiStickers[$0]?.first?.file }, themeReference: theme, nightMode: item.nightMode, channelMode: false, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil, action: { theme in
|
||||||
|
if let theme {
|
||||||
item.updatedTheme(theme)
|
item.updatedTheme(theme)
|
||||||
|
}
|
||||||
}, contextAction: nil)
|
}, contextAction: nil)
|
||||||
|
|
||||||
validIds.append(theme.index)
|
validIds.append(theme.index)
|
||||||
|
|||||||
@ -283,8 +283,10 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
case let .chatPreview(theme, wallpaper, fontSize, chatBubbleCorners, strings, dateTimeFormat, nameDisplayOrder, items):
|
case let .chatPreview(theme, wallpaper, fontSize, chatBubbleCorners, strings, dateTimeFormat, nameDisplayOrder, items):
|
||||||
return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: theme, strings: strings, sectionId: self.section, fontSize: fontSize, chatBubbleCorners: chatBubbleCorners, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, messageItems: items)
|
return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: theme, strings: strings, sectionId: self.section, fontSize: fontSize, chatBubbleCorners: chatBubbleCorners, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, messageItems: items)
|
||||||
case let .themes(theme, strings, chatThemes, currentTheme, nightMode, animatedEmojiStickers, themeSpecificAccentColors, themeSpecificChatWallpapers):
|
case let .themes(theme, strings, chatThemes, currentTheme, nightMode, animatedEmojiStickers, themeSpecificAccentColors, themeSpecificChatWallpapers):
|
||||||
return ThemeCarouselThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: chatThemes, animatedEmojiStickers: animatedEmojiStickers, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, nightMode: nightMode, currentTheme: currentTheme, updatedTheme: { theme in
|
return ThemeCarouselThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: chatThemes, hasNoTheme: false, animatedEmojiStickers: animatedEmojiStickers, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, nightMode: nightMode, currentTheme: currentTheme, updatedTheme: { theme in
|
||||||
|
if let theme {
|
||||||
arguments.selectTheme(theme)
|
arguments.selectTheme(theme)
|
||||||
|
}
|
||||||
}, contextAction: { theme, node, gesture in
|
}, contextAction: { theme, node, gesture in
|
||||||
arguments.themeContextAction(false, theme, node, gesture)
|
arguments.themeContextAction(false, theme, node, gesture)
|
||||||
}, tag: ThemeSettingsEntryTag.theme)
|
}, tag: ThemeSettingsEntryTag.theme)
|
||||||
|
|||||||
@ -628,6 +628,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
if abs(panGestureState.offsetFraction) > 0.6 || abs(velocity.y) >= 100.0 {
|
if abs(panGestureState.offsetFraction) > 0.6 || abs(velocity.y) >= 100.0 {
|
||||||
self.panGestureState = PanGestureState(offsetFraction: panGestureState.offsetFraction < 0.0 ? -1.0 : 1.0)
|
self.panGestureState = PanGestureState(offsetFraction: panGestureState.offsetFraction < 0.0 ? -1.0 : 1.0)
|
||||||
self.notifyDismissedInteractivelyOnPanGestureApply = true
|
self.notifyDismissedInteractivelyOnPanGestureApply = true
|
||||||
|
self.callScreen.beginPictureInPictureIfPossible()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update(transition: .animated(duration: 0.4, curve: .spring))
|
self.update(transition: .animated(duration: 0.4, curve: .spring))
|
||||||
|
|||||||
@ -94,16 +94,19 @@ public final class EngineAvailableColorOptions: Codable, Equatable {
|
|||||||
case light = "l"
|
case light = "l"
|
||||||
case dark = "d"
|
case dark = "d"
|
||||||
case isHidden = "h"
|
case isHidden = "h"
|
||||||
|
case requiredChannelMinBoostLevel = "rcmb"
|
||||||
}
|
}
|
||||||
|
|
||||||
public let light: ColorOption
|
public let light: ColorOption
|
||||||
public let dark: ColorOption?
|
public let dark: ColorOption?
|
||||||
public let isHidden: Bool
|
public let isHidden: Bool
|
||||||
|
public let requiredChannelMinBoostLevel: Int32?
|
||||||
|
|
||||||
public init(light: ColorOption, dark: ColorOption?, isHidden: Bool) {
|
public init(light: ColorOption, dark: ColorOption?, isHidden: Bool, requiredChannelMinBoostLevel: Int32?) {
|
||||||
self.light = light
|
self.light = light
|
||||||
self.dark = dark
|
self.dark = dark
|
||||||
self.isHidden = isHidden
|
self.isHidden = isHidden
|
||||||
|
self.requiredChannelMinBoostLevel = requiredChannelMinBoostLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@ -112,6 +115,7 @@ public final class EngineAvailableColorOptions: Codable, Equatable {
|
|||||||
self.light = try container.decode(ColorOption.self, forKey: .light)
|
self.light = try container.decode(ColorOption.self, forKey: .light)
|
||||||
self.dark = try container.decodeIfPresent(ColorOption.self, forKey: .dark)
|
self.dark = try container.decodeIfPresent(ColorOption.self, forKey: .dark)
|
||||||
self.isHidden = try container.decode(Bool.self, forKey: .isHidden)
|
self.isHidden = try container.decode(Bool.self, forKey: .isHidden)
|
||||||
|
self.requiredChannelMinBoostLevel = try container.decodeIfPresent(Int32.self, forKey: .requiredChannelMinBoostLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@ -120,6 +124,7 @@ public final class EngineAvailableColorOptions: Codable, Equatable {
|
|||||||
try container.encode(self.light, forKey: .light)
|
try container.encode(self.light, forKey: .light)
|
||||||
try container.encodeIfPresent(self.dark, forKey: .dark)
|
try container.encodeIfPresent(self.dark, forKey: .dark)
|
||||||
try container.encodeIfPresent(self.isHidden, forKey: .isHidden)
|
try container.encodeIfPresent(self.isHidden, forKey: .isHidden)
|
||||||
|
try container.encodeIfPresent(self.requiredChannelMinBoostLevel, forKey: .requiredChannelMinBoostLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: ColorOptionPack, rhs: ColorOptionPack) -> Bool {
|
public static func ==(lhs: ColorOptionPack, rhs: ColorOptionPack) -> Bool {
|
||||||
@ -135,6 +140,9 @@ public final class EngineAvailableColorOptions: Codable, Equatable {
|
|||||||
if lhs.isHidden != rhs.isHidden {
|
if lhs.isHidden != rhs.isHidden {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.requiredChannelMinBoostLevel != rhs.requiredChannelMinBoostLevel {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,14 +270,14 @@ private extension EngineAvailableColorOptions {
|
|||||||
var mappedOptions: [Option] = []
|
var mappedOptions: [Option] = []
|
||||||
for apiColor in apiColors {
|
for apiColor in apiColors {
|
||||||
switch apiColor {
|
switch apiColor {
|
||||||
case let .peerColorOption(flags, colorId, colors, darkColors, _):
|
case let .peerColorOption(flags, colorId, colors, darkColors, requiredChannelMinBoostLevel):
|
||||||
let isHidden = (flags & (1 << 0)) != 0
|
let isHidden = (flags & (1 << 0)) != 0
|
||||||
|
|
||||||
let mappedColors = colors.flatMap(EngineAvailableColorOptions.ColorOption.init(apiColors:))
|
let mappedColors = colors.flatMap(EngineAvailableColorOptions.ColorOption.init(apiColors:))
|
||||||
let mappedDarkColors = darkColors.flatMap(EngineAvailableColorOptions.ColorOption.init(apiColors:))
|
let mappedDarkColors = darkColors.flatMap(EngineAvailableColorOptions.ColorOption.init(apiColors:))
|
||||||
|
|
||||||
if let mappedColors = mappedColors {
|
if let mappedColors = mappedColors {
|
||||||
mappedOptions.append(Option(key: colorId, value: ColorOptionPack(light: mappedColors, dark: mappedDarkColors, isHidden: isHidden)))
|
mappedOptions.append(Option(key: colorId, value: ColorOptionPack(light: mappedColors, dark: mappedDarkColors, isHidden: isHidden, requiredChannelMinBoostLevel: requiredChannelMinBoostLevel)))
|
||||||
} else if colorId >= 0 && colorId <= 6 {
|
} else if colorId >= 0 && colorId <= 6 {
|
||||||
let staticMap: [UInt32] = [
|
let staticMap: [UInt32] = [
|
||||||
0xcc5049,
|
0xcc5049,
|
||||||
@ -282,7 +290,7 @@ private extension EngineAvailableColorOptions {
|
|||||||
]
|
]
|
||||||
let colorPack = MultiColorPack(colors: [staticMap[Int(colorId)]])
|
let colorPack = MultiColorPack(colors: [staticMap[Int(colorId)]])
|
||||||
let defaultColors = EngineAvailableColorOptions.ColorOption(palette: colorPack, background: colorPack, stories: nil)
|
let defaultColors = EngineAvailableColorOptions.ColorOption(palette: colorPack, background: colorPack, stories: nil)
|
||||||
mappedOptions.append(Option(key: colorId, value: ColorOptionPack(light: defaultColors, dark: nil, isHidden: isHidden)))
|
mappedOptions.append(Option(key: colorId, value: ColorOptionPack(light: defaultColors, dark: nil, isHidden: isHidden, requiredChannelMinBoostLevel: requiredChannelMinBoostLevel)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -499,7 +499,7 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let requiredLevel = requiredBoostSubject.requiredLevel(premiumConfiguration)
|
let requiredLevel = requiredBoostSubject.requiredLevel(context: component.context, configuration: premiumConfiguration)
|
||||||
if let boostLevel = self.boostLevel, requiredLevel > boostLevel {
|
if let boostLevel = self.boostLevel, requiredLevel > boostLevel {
|
||||||
self.displayBoostLevels(subject: requiredBoostSubject)
|
self.displayBoostLevels(subject: requiredBoostSubject)
|
||||||
return
|
return
|
||||||
@ -806,7 +806,7 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
return availableSize
|
return availableSize
|
||||||
}
|
}
|
||||||
|
|
||||||
var requiredBoostSubject: BoostSubject = .nameColors
|
var requiredBoostSubjects: [BoostSubject] = [.nameColors(colors: resolvedState.nameColor)]
|
||||||
|
|
||||||
let replyIconLevel = 5
|
let replyIconLevel = 5
|
||||||
var profileIconLevel = 7
|
var profileIconLevel = 7
|
||||||
@ -824,34 +824,27 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
|
|
||||||
let replyFileId = resolvedState.replyFileId
|
let replyFileId = resolvedState.replyFileId
|
||||||
if replyFileId != nil {
|
if replyFileId != nil {
|
||||||
requiredBoostSubject = .nameIcon
|
requiredBoostSubjects.append(.nameIcon)
|
||||||
}
|
}
|
||||||
|
|
||||||
let profileColor = resolvedState.profileColor
|
let profileColor = resolvedState.profileColor
|
||||||
if profileColor != nil {
|
if profileColor != nil {
|
||||||
requiredBoostSubject = .profileColors
|
requiredBoostSubjects.append(.profileColors)
|
||||||
}
|
}
|
||||||
|
|
||||||
let backgroundFileId = resolvedState.backgroundFileId
|
let backgroundFileId = resolvedState.backgroundFileId
|
||||||
if backgroundFileId != nil {
|
if backgroundFileId != nil {
|
||||||
requiredBoostSubject = .profileIcon
|
requiredBoostSubjects.append(.profileIcon)
|
||||||
}
|
}
|
||||||
|
|
||||||
let emojiStatus = resolvedState.emojiStatus
|
let emojiStatus = resolvedState.emojiStatus
|
||||||
if emojiStatus != nil {
|
if emojiStatus != nil {
|
||||||
requiredBoostSubject = .emojiStatus
|
requiredBoostSubjects.append(.emojiStatus)
|
||||||
}
|
}
|
||||||
let statusFileId = emojiStatus?.fileId
|
let statusFileId = emojiStatus?.fileId
|
||||||
|
|
||||||
let cloudThemes: [PresentationThemeReference] = contentsData.availableThemes.map { .cloud(PresentationCloudTheme(theme: $0, resolvedWallpaper: nil, creatorAccountId: $0.isCreator ? component.context.account.id : nil)) }
|
let cloudThemes: [PresentationThemeReference] = contentsData.availableThemes.map { .cloud(PresentationCloudTheme(theme: $0, resolvedWallpaper: nil, creatorAccountId: $0.isCreator ? component.context.account.id : nil)) }
|
||||||
var chatThemes = cloudThemes.filter { $0.emoticon != nil }
|
let chatThemes = cloudThemes.filter { $0.emoticon != nil }
|
||||||
chatThemes.insert(.builtin(.dayClassic), at: 0)
|
|
||||||
|
|
||||||
if !chatThemes.isEmpty {
|
|
||||||
if self.currentTheme == nil {
|
|
||||||
self.currentTheme = chatThemes[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let currentTheme = self.currentTheme, (self.resolvedCurrentTheme?.reference != currentTheme || self.resolvedCurrentTheme?.isDark != environment.theme.overallDarkAppearance), (self.resolvingCurrentTheme?.reference != currentTheme || self.resolvingCurrentTheme?.isDark != environment.theme.overallDarkAppearance) {
|
if let currentTheme = self.currentTheme, (self.resolvedCurrentTheme?.reference != currentTheme || self.resolvedCurrentTheme?.isDark != environment.theme.overallDarkAppearance), (self.resolvingCurrentTheme?.reference != currentTheme || self.resolvingCurrentTheme?.isDark != environment.theme.overallDarkAppearance) {
|
||||||
self.resolvingCurrentTheme?.disposable.dispose()
|
self.resolvingCurrentTheme?.disposable.dispose()
|
||||||
@ -891,8 +884,8 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.currentTheme != nil && self.currentTheme != chatThemes.first {
|
if self.currentTheme != nil {
|
||||||
requiredBoostSubject = .wallpaper
|
requiredBoostSubjects.append(.wallpaper)
|
||||||
}
|
}
|
||||||
|
|
||||||
if case let .user(user) = peer {
|
if case let .user(user) = peer {
|
||||||
@ -913,6 +906,12 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let requiredBoostSubject: BoostSubject
|
||||||
|
if let maxBoostSubject = requiredBoostSubjects.max(by: { $0.requiredLevel(context: component.context, configuration: premiumConfiguration) < $1.requiredLevel(context: component.context, configuration: premiumConfiguration) }) {
|
||||||
|
requiredBoostSubject = maxBoostSubject
|
||||||
|
} else {
|
||||||
|
requiredBoostSubject = .nameColors(colors: resolvedState.nameColor)
|
||||||
|
}
|
||||||
self.requiredBoostSubject = requiredBoostSubject
|
self.requiredBoostSubject = requiredBoostSubject
|
||||||
|
|
||||||
let topInset: CGFloat = 24.0
|
let topInset: CGFloat = 24.0
|
||||||
@ -951,7 +950,7 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
)),
|
)),
|
||||||
maximumNumberOfLines: 0
|
maximumNumberOfLines: 0
|
||||||
))))
|
))))
|
||||||
if replyFileId != nil, let boostLevel = self.boostLevel, boostLevel < premiumConfiguration.minChannelNameIconLevel {
|
if let boostLevel = self.boostLevel, boostLevel < premiumConfiguration.minChannelNameIconLevel {
|
||||||
replyLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
|
replyLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
|
||||||
strings: environment.strings,
|
strings: environment.strings,
|
||||||
level: replyIconLevel
|
level: replyIconLevel
|
||||||
@ -1047,7 +1046,7 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
|
|
||||||
contentHeight += sectionSpacing
|
contentHeight += sectionSpacing
|
||||||
|
|
||||||
if !chatThemes.isEmpty, let currentTheme {
|
if !chatThemes.isEmpty {
|
||||||
var wallpaperLogoContents: [AnyComponentWithIdentity<Empty>] = []
|
var wallpaperLogoContents: [AnyComponentWithIdentity<Empty>] = []
|
||||||
wallpaperLogoContents.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
|
wallpaperLogoContents.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
@ -1057,7 +1056,7 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
)),
|
)),
|
||||||
maximumNumberOfLines: 0
|
maximumNumberOfLines: 0
|
||||||
))))
|
))))
|
||||||
if currentTheme != chatThemes[0], let boostLevel = self.boostLevel, boostLevel < premiumConfiguration.minChannelCustomWallpaperLevel {
|
if let boostLevel = self.boostLevel, boostLevel < premiumConfiguration.minChannelCustomWallpaperLevel {
|
||||||
wallpaperLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
|
wallpaperLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
|
||||||
strings: environment.strings,
|
strings: environment.strings,
|
||||||
level: themeLevel
|
level: themeLevel
|
||||||
@ -1085,12 +1084,13 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
strings: environment.strings,
|
strings: environment.strings,
|
||||||
sectionId: 0,
|
sectionId: 0,
|
||||||
themes: chatThemes,
|
themes: chatThemes,
|
||||||
|
hasNoTheme: true,
|
||||||
animatedEmojiStickers: component.context.animatedEmojiStickers,
|
animatedEmojiStickers: component.context.animatedEmojiStickers,
|
||||||
themeSpecificAccentColors: [:],
|
themeSpecificAccentColors: [:],
|
||||||
themeSpecificChatWallpapers: [:],
|
themeSpecificChatWallpapers: [:],
|
||||||
nightMode: environment.theme.overallDarkAppearance,
|
nightMode: environment.theme.overallDarkAppearance,
|
||||||
channelMode: true,
|
channelMode: true,
|
||||||
currentTheme: currentTheme,
|
currentTheme: self.currentTheme,
|
||||||
updatedTheme: { [weak self] value in
|
updatedTheme: { [weak self] value in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -1138,7 +1138,7 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
)),
|
)),
|
||||||
maximumNumberOfLines: 0
|
maximumNumberOfLines: 0
|
||||||
))))
|
))))
|
||||||
if backgroundFileId != nil, let boostLevel = self.boostLevel, boostLevel < premiumConfiguration.minChannelProfileIconLevel {
|
if let boostLevel = self.boostLevel, boostLevel < premiumConfiguration.minChannelProfileIconLevel {
|
||||||
profileLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
|
profileLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
|
||||||
strings: environment.strings,
|
strings: environment.strings,
|
||||||
level: profileIconLevel
|
level: profileIconLevel
|
||||||
@ -1249,7 +1249,7 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
)),
|
)),
|
||||||
maximumNumberOfLines: 0
|
maximumNumberOfLines: 0
|
||||||
))))
|
))))
|
||||||
if emojiStatus != nil, let boostLevel = self.boostLevel, boostLevel < premiumConfiguration.minChannelEmojiStatusLevel {
|
if let boostLevel = self.boostLevel, boostLevel < premiumConfiguration.minChannelEmojiStatusLevel {
|
||||||
emojiStatusContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
|
emojiStatusContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
|
||||||
strings: environment.strings,
|
strings: environment.strings,
|
||||||
level: emojiStatusLevel
|
level: emojiStatusLevel
|
||||||
@ -1361,7 +1361,7 @@ final class ChannelAppearanceScreenComponent: Component {
|
|||||||
Text(text: "Apply Changes", font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.foregroundColor)
|
Text(text: "Apply Changes", font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.foregroundColor)
|
||||||
)))
|
)))
|
||||||
|
|
||||||
let requiredLevel = requiredBoostSubject.requiredLevel(premiumConfiguration)
|
let requiredLevel = requiredBoostSubject.requiredLevel(context: component.context, configuration: premiumConfiguration)
|
||||||
if let boostLevel = self.boostLevel, requiredLevel > boostLevel {
|
if let boostLevel = self.boostLevel, requiredLevel > boostLevel {
|
||||||
buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(PremiumLockButtonSubtitleComponent(
|
buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(PremiumLockButtonSubtitleComponent(
|
||||||
count: Int(requiredLevel),
|
count: Int(requiredLevel),
|
||||||
|
|||||||
@ -416,7 +416,7 @@ private func ensureColorVisible(listNode: ListView, color: PeerNameColor, animat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let resultNode = resultNode {
|
if let resultNode = resultNode {
|
||||||
listNode.ensureItemNodeVisible(resultNode, animated: animated, overflow: 24.0)
|
listNode.ensureItemNodeVisible(resultNode, animated: animated, overflow: 76.0)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@ -891,7 +891,7 @@ public func PeerNameColorScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let link = status.url
|
let link = status.url
|
||||||
let controller = PremiumLimitScreen(context: context, subject: .storiesChannelBoost(peer: peer, boostSubject: .nameColors, isCurrent: true, level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), link: link, myBoostCount: 0, canBoostAgain: false), count: Int32(status.boosts), action: {
|
let controller = PremiumLimitScreen(context: context, subject: .storiesChannelBoost(peer: peer, boostSubject: .nameColors(colors: .blue), isCurrent: true, level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), link: link, myBoostCount: 0, canBoostAgain: false), count: Int32(status.boosts), action: {
|
||||||
UIPasteboard.general.string = link
|
UIPasteboard.general.string = link
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
presentImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.ChannelBoost_BoostLinkCopied), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }))
|
presentImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.ChannelBoost_BoostLinkCopied), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }))
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import HexColor
|
|||||||
private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
|
private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
|
||||||
let index: Int
|
let index: Int
|
||||||
let emojiFile: TelegramMediaFile?
|
let emojiFile: TelegramMediaFile?
|
||||||
let themeReference: PresentationThemeReference
|
let themeReference: PresentationThemeReference?
|
||||||
let nightMode: Bool
|
let nightMode: Bool
|
||||||
let channelMode: Bool
|
let channelMode: Bool
|
||||||
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||||
@ -45,7 +45,7 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
|
|||||||
if lhs.emojiFile?.fileId != rhs.emojiFile?.fileId {
|
if lhs.emojiFile?.fileId != rhs.emojiFile?.fileId {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.themeReference.index != rhs.themeReference.index {
|
if lhs.themeReference?.index != rhs.themeReference?.index {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.nightMode != rhs.nightMode {
|
if lhs.nightMode != rhs.nightMode {
|
||||||
@ -79,7 +79,7 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
|
|||||||
return lhs.index < rhs.index
|
return lhs.index < rhs.index
|
||||||
}
|
}
|
||||||
|
|
||||||
func item(context: AccountContext, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem {
|
func item(context: AccountContext, action: @escaping (PresentationThemeReference?) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem {
|
||||||
return ThemeCarouselThemeIconItem(context: context, emojiFile: self.emojiFile, themeReference: self.themeReference, nightMode: self.nightMode, channelMode: self.channelMode, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action, contextAction: contextAction)
|
return ThemeCarouselThemeIconItem(context: context, emojiFile: self.emojiFile, themeReference: self.themeReference, nightMode: self.nightMode, channelMode: self.channelMode, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action, contextAction: contextAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
|
|||||||
public class ThemeCarouselThemeIconItem: ListViewItem {
|
public class ThemeCarouselThemeIconItem: ListViewItem {
|
||||||
public let context: AccountContext
|
public let context: AccountContext
|
||||||
public let emojiFile: TelegramMediaFile?
|
public let emojiFile: TelegramMediaFile?
|
||||||
public let themeReference: PresentationThemeReference
|
public let themeReference: PresentationThemeReference?
|
||||||
public let nightMode: Bool
|
public let nightMode: Bool
|
||||||
public let channelMode: Bool
|
public let channelMode: Bool
|
||||||
public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||||
@ -97,10 +97,10 @@ public class ThemeCarouselThemeIconItem: ListViewItem {
|
|||||||
public let theme: PresentationTheme
|
public let theme: PresentationTheme
|
||||||
public let strings: PresentationStrings
|
public let strings: PresentationStrings
|
||||||
public let wallpaper: TelegramWallpaper?
|
public let wallpaper: TelegramWallpaper?
|
||||||
public let action: (PresentationThemeReference) -> Void
|
public let action: (PresentationThemeReference?) -> Void
|
||||||
public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
|
|
||||||
public init(context: AccountContext, emojiFile: TelegramMediaFile?, themeReference: PresentationThemeReference, nightMode: Bool, channelMode: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], selected: Bool, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper?, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) {
|
public init(context: AccountContext, emojiFile: TelegramMediaFile?, themeReference: PresentationThemeReference?, nightMode: Bool, channelMode: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], selected: Bool, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper?, action: @escaping (PresentationThemeReference?) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.emojiFile = emojiFile
|
self.emojiFile = emojiFile
|
||||||
self.themeReference = themeReference
|
self.themeReference = themeReference
|
||||||
@ -336,6 +336,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func asyncLayout() -> (ThemeCarouselThemeIconItem, ListViewItemLayoutParams) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
func asyncLayout() -> (ThemeCarouselThemeIconItem, ListViewItemLayoutParams) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
||||||
|
let makeTextLayout = TextNode.asyncLayout(self.textNode)
|
||||||
let makeEmojiLayout = TextNode.asyncLayout(self.emojiNode)
|
let makeEmojiLayout = TextNode.asyncLayout(self.emojiNode)
|
||||||
let makeImageLayout = self.imageNode.asyncLayout()
|
let makeImageLayout = self.imageNode.asyncLayout()
|
||||||
|
|
||||||
@ -368,9 +369,14 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
|||||||
updatedSelected = true
|
updatedSelected = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
let text = NSAttributedString(string: "No\nWallpaper", font: Font.semibold(15.0), textColor: item.theme.actionSheet.controlAccentColor)
|
||||||
|
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: text, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
var string: String?
|
var string: String?
|
||||||
if let _ = item.themeReference.emoticon {
|
if item.themeReference == nil {
|
||||||
|
string = "❌"
|
||||||
|
} else if let _ = item.themeReference?.emoticon {
|
||||||
} else {
|
} else {
|
||||||
string = "🎨"
|
string = "🎨"
|
||||||
}
|
}
|
||||||
@ -384,7 +390,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
|||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
|
|
||||||
if updatedThemeReference || updatedWallpaper || updatedNightMode || updatedChannelMode {
|
if updatedThemeReference || updatedWallpaper || updatedNightMode || updatedChannelMode {
|
||||||
var themeReference = item.themeReference
|
if var themeReference = item.themeReference {
|
||||||
if case .builtin = themeReference, item.nightMode {
|
if case .builtin = themeReference, item.nightMode {
|
||||||
themeReference = .builtin(.night)
|
themeReference = .builtin(.night)
|
||||||
}
|
}
|
||||||
@ -394,6 +400,9 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
|||||||
|
|
||||||
strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: color, wallpaper: wallpaper ?? item.wallpaper, nightMode: item.nightMode, channelMode: item.channelMode, emoticon: true))
|
strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: color, wallpaper: wallpaper ?? item.wallpaper, nightMode: item.nightMode, channelMode: item.channelMode, emoticon: true))
|
||||||
strongSelf.imageNode.backgroundColor = nil
|
strongSelf.imageNode.backgroundColor = nil
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if updatedTheme || updatedSelected {
|
if updatedTheme || updatedSelected {
|
||||||
@ -424,6 +433,10 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
|||||||
strongSelf.emojiNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 78.0), size: CGSize(width: 90.0, height: 30.0))
|
strongSelf.emojiNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 78.0), size: CGSize(width: 90.0, height: 30.0))
|
||||||
strongSelf.emojiNode.isHidden = string == nil
|
strongSelf.emojiNode.isHidden = string == nil
|
||||||
|
|
||||||
|
let _ = textApply()
|
||||||
|
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((90.0 - textLayout.size.width) / 2.0), y: 24.0), size: textLayout.size)
|
||||||
|
strongSelf.textNode.isHidden = item.themeReference != nil
|
||||||
|
|
||||||
let emojiFrame = CGRect(origin: CGPoint(x: 33.0, y: 79.0), size: CGSize(width: 24.0, height: 24.0))
|
let emojiFrame = CGRect(origin: CGPoint(x: 33.0, y: 79.0), size: CGSize(width: 24.0, height: 24.0))
|
||||||
if let file = item.emojiFile, currentItem?.emojiFile == nil {
|
if let file = item.emojiFile, currentItem?.emojiFile == nil {
|
||||||
let imageApply = strongSelf.emojiImageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: emojiFrame.size, boundingSize: emojiFrame.size, intrinsicInsets: UIEdgeInsets()))
|
let imageApply = strongSelf.emojiImageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: emojiFrame.size, boundingSize: emojiFrame.size, intrinsicInsets: UIEdgeInsets()))
|
||||||
@ -462,7 +475,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let presentationData = item.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = item.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
strongSelf.activateAreaNode.accessibilityLabel = item.themeReference.emoticon.flatMap { presentationData.strings.Appearance_VoiceOver_Theme($0).string }
|
strongSelf.activateAreaNode.accessibilityLabel = item.themeReference?.emoticon.flatMap { presentationData.strings.Appearance_VoiceOver_Theme($0).string }
|
||||||
if item.selected {
|
if item.selected {
|
||||||
strongSelf.activateAreaNode.accessibilityTraits = [.button, .selected]
|
strongSelf.activateAreaNode.accessibilityTraits = [.button, .selected]
|
||||||
} else {
|
} else {
|
||||||
@ -525,21 +538,23 @@ public class ThemeCarouselThemeItem: ListViewItem, ItemListItem, ListItemCompone
|
|||||||
public let theme: PresentationTheme
|
public let theme: PresentationTheme
|
||||||
public let strings: PresentationStrings
|
public let strings: PresentationStrings
|
||||||
public let themes: [PresentationThemeReference]
|
public let themes: [PresentationThemeReference]
|
||||||
|
public let hasNoTheme: Bool
|
||||||
public let animatedEmojiStickers: [String: [StickerPackItem]]
|
public let animatedEmojiStickers: [String: [StickerPackItem]]
|
||||||
public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
|
||||||
public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
|
||||||
public let nightMode: Bool
|
public let nightMode: Bool
|
||||||
public let channelMode: Bool
|
public let channelMode: Bool
|
||||||
public let currentTheme: PresentationThemeReference
|
public let currentTheme: PresentationThemeReference?
|
||||||
public let updatedTheme: (PresentationThemeReference) -> Void
|
public let updatedTheme: (PresentationThemeReference?) -> Void
|
||||||
public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
public let tag: ItemListItemTag?
|
public let tag: ItemListItemTag?
|
||||||
|
|
||||||
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], animatedEmojiStickers: [String: [StickerPackItem]], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], nightMode: Bool, channelMode: Bool = false, currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
|
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], hasNoTheme: Bool, animatedEmojiStickers: [String: [StickerPackItem]], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], nightMode: Bool, channelMode: Bool = false, currentTheme: PresentationThemeReference?, updatedTheme: @escaping (PresentationThemeReference?) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.themes = themes
|
self.themes = themes
|
||||||
|
self.hasNoTheme = hasNoTheme
|
||||||
self.animatedEmojiStickers = animatedEmojiStickers
|
self.animatedEmojiStickers = animatedEmojiStickers
|
||||||
self.themeSpecificAccentColors = themeSpecificAccentColors
|
self.themeSpecificAccentColors = themeSpecificAccentColors
|
||||||
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
|
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
|
||||||
@ -634,7 +649,7 @@ private struct ThemeCarouselThemeItemNodeTransition {
|
|||||||
let updatePosition: Bool
|
let updatePosition: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preparedTransition(context: AccountContext, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, from fromEntries: [ThemeCarouselThemeEntry], to toEntries: [ThemeCarouselThemeEntry], crossfade: Bool, updatePosition: Bool) -> ThemeCarouselThemeItemNodeTransition {
|
private func preparedTransition(context: AccountContext, action: @escaping (PresentationThemeReference?) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, from fromEntries: [ThemeCarouselThemeEntry], to toEntries: [ThemeCarouselThemeEntry], crossfade: Bool, updatePosition: Bool) -> ThemeCarouselThemeItemNodeTransition {
|
||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
@ -648,7 +663,7 @@ private func ensureThemeVisible(listNode: ListView, themeReference: Presentation
|
|||||||
var resultNode: ThemeCarouselThemeItemIconNode?
|
var resultNode: ThemeCarouselThemeItemIconNode?
|
||||||
listNode.forEachItemNode { node in
|
listNode.forEachItemNode { node in
|
||||||
if resultNode == nil, let node = node as? ThemeCarouselThemeItemIconNode {
|
if resultNode == nil, let node = node as? ThemeCarouselThemeItemIconNode {
|
||||||
if node.item?.themeReference.index == themeReference.index {
|
if node.item?.themeReference?.index == themeReference.index {
|
||||||
resultNode = node
|
resultNode = node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -736,7 +751,7 @@ public class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
var scrollToItem: ListViewScrollToItem?
|
var scrollToItem: ListViewScrollToItem?
|
||||||
if !self.initialized || !self.tapping {
|
if !self.initialized || !self.tapping {
|
||||||
if let index = transition.entries.firstIndex(where: { entry in
|
if let index = transition.entries.firstIndex(where: { entry in
|
||||||
return entry.themeReference.index == item.currentTheme.index
|
return entry.themeReference?.index == item.currentTheme?.index
|
||||||
}) {
|
}) {
|
||||||
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(-57.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Down)
|
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(-57.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Down)
|
||||||
self.initialized = true
|
self.initialized = true
|
||||||
@ -836,8 +851,16 @@ public class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
var index: Int = 0
|
var index: Int = 0
|
||||||
|
|
||||||
var hasCurrentTheme = false
|
var hasCurrentTheme = false
|
||||||
|
if item.hasNoTheme {
|
||||||
|
let selected = item.currentTheme == nil
|
||||||
|
if selected {
|
||||||
|
hasCurrentTheme = true
|
||||||
|
}
|
||||||
|
entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: nil, themeReference: nil, nightMode: item.nightMode, channelMode: item.channelMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil))
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
for theme in item.themes {
|
for theme in item.themes {
|
||||||
let selected = item.currentTheme.index == theme.index
|
let selected = item.currentTheme?.index == theme.index
|
||||||
if selected {
|
if selected {
|
||||||
hasCurrentTheme = true
|
hasCurrentTheme = true
|
||||||
}
|
}
|
||||||
@ -850,11 +873,13 @@ public class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: nil, themeReference: item.currentTheme, nightMode: false, channelMode: item.channelMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: true, theme: item.theme, strings: item.strings, wallpaper: nil))
|
entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: nil, themeReference: item.currentTheme, nightMode: false, channelMode: item.channelMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: true, theme: item.theme, strings: item.strings, wallpaper: nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
let action: (PresentationThemeReference) -> Void = { [weak self] themeReference in
|
let action: (PresentationThemeReference?) -> Void = { [weak self] themeReference in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.tapping = true
|
strongSelf.tapping = true
|
||||||
strongSelf.item?.updatedTheme(themeReference)
|
strongSelf.item?.updatedTheme(themeReference)
|
||||||
|
if let themeReference {
|
||||||
let _ = ensureThemeVisible(listNode: strongSelf.listNode, themeReference: themeReference, animated: true)
|
let _ = ensureThemeVisible(listNode: strongSelf.listNode, themeReference: themeReference, animated: true)
|
||||||
|
}
|
||||||
Queue.mainQueue().after(0.4) {
|
Queue.mainQueue().after(0.4) {
|
||||||
strongSelf.tapping = false
|
strongSelf.tapping = false
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user