[WIP] Channel appearance

This commit is contained in:
Isaac
2023-12-15 22:59:33 +04:00
parent b66f748969
commit 09e025e985
59 changed files with 973 additions and 276 deletions

View File

@@ -42,6 +42,7 @@ swift_library(
"//submodules/TelegramUI/Components/EmojiStatusSelectionComponent",
"//submodules/TelegramUI/Components/DynamicCornerRadiusView",
"//submodules/Components/ComponentDisplayAdapters",
"//submodules/WallpaperResources",
],
visibility = [
"//visibility:public",

View File

@@ -28,6 +28,7 @@ import EmojiStatusSelectionComponent
import EmojiStatusComponent
import DynamicCornerRadiusView
import ComponentDisplayAdapters
import WallpaperResources
private final class EmojiActionIconComponent: Component {
let context: AccountContext
@@ -285,6 +286,25 @@ final class ChannelAppearanceScreenComponent: Component {
}
}
private struct ResolvedState {
var nameColor: PeerNameColor
var profileColor: PeerNameColor?
var replyFileId: Int64?
var backgroundFileId: Int64?
var emojiStatus: PeerEmojiStatus?
var hasChanges: Bool
init(nameColor: PeerNameColor, profileColor: PeerNameColor?, replyFileId: Int64?, backgroundFileId: Int64?, emojiStatus: PeerEmojiStatus?, hasChanges: Bool) {
self.nameColor = nameColor
self.profileColor = profileColor
self.replyFileId = replyFileId
self.backgroundFileId = backgroundFileId
self.emojiStatus = emojiStatus
self.hasChanges = hasChanges
}
}
final class View: UIView, UIScrollViewDelegate {
private let scrollView: ScrollView
private let actionButton = ComponentView<Empty>()
@@ -315,12 +335,15 @@ final class ChannelAppearanceScreenComponent: Component {
private var updatedPeerNameEmoji: Int64??
private var updatedPeerProfileColor: PeerNameColor??
private var updatedPeerProfileEmoji: Int64??
private var updatedPeerStatusEmoji: Int64??
private var updatedPeerStatus: PeerEmojiStatus??
private var requiredLevel: Int?
private var currentTheme: PresentationThemeReference?
private var resolvedCurrentTheme: (reference: PresentationThemeReference, isDark: Bool, theme: PresentationTheme, wallpaper: TelegramWallpaper?)?
private var resolvingCurrentTheme: (reference: PresentationThemeReference, isDark: Bool, disposable: Disposable)?
private var boostLevel: Int?
private var boostStatus: ChannelBoostStatus?
private var boostStatusDisposable: Disposable?
@@ -363,6 +386,7 @@ final class ChannelAppearanceScreenComponent: Component {
self.contentsDataDisposable?.dispose()
self.applyDisposable?.dispose()
self.boostStatusDisposable?.dispose()
self.resolvingCurrentTheme?.disposable.dispose()
}
func scrollToTop() {
@@ -392,23 +416,12 @@ final class ChannelAppearanceScreenComponent: Component {
transition.setAlpha(layer: self.bottomPanelSeparator, alpha: bottomNavigationAlpha)
}
private func applySettings() {
guard let component = self.component, let contentsData = self.contentsData, let peer = contentsData.peer, let requiredLevel = self.requiredLevel else {
return
}
if self.isApplyingSettings {
return
private func resolveState() -> ResolvedState? {
guard let contentsData = self.contentsData, let peer = contentsData.peer else {
return nil
}
if let boostStatus = self.boostStatus, requiredLevel > boostStatus.level {
self.displayPremiumScreen(requiredLevel: requiredLevel)
return
}
self.isApplyingSettings = true
self.state?.updated(transition: .immediate)
self.applyDisposable?.dispose()
var hasChanges = false
let nameColor: PeerNameColor
if let updatedPeerNameColor = self.updatedPeerNameColor {
@@ -418,6 +431,9 @@ final class ChannelAppearanceScreenComponent: Component {
} else {
nameColor = .blue
}
if nameColor != peer.nameColor {
hasChanges = true
}
let profileColor: PeerNameColor?
if case let .some(value) = self.updatedPeerProfileColor {
@@ -427,49 +443,98 @@ final class ChannelAppearanceScreenComponent: Component {
} else {
profileColor = nil
}
if profileColor != peer.profileColor {
hasChanges = true
}
let replyFileId: Int64?
if case let .some(value) = self.updatedPeerNameEmoji {
replyFileId = value
} else {
replyFileId = contentsData.peer?.backgroundEmojiId
replyFileId = peer.backgroundEmojiId
}
if replyFileId != peer.backgroundEmojiId {
hasChanges = true
}
let backgroundFileId: Int64?
if case let .some(value) = self.updatedPeerProfileEmoji {
backgroundFileId = value
} else {
backgroundFileId = contentsData.peer?.profileBackgroundEmojiId
backgroundFileId = peer.profileBackgroundEmojiId
}
if backgroundFileId != peer.profileBackgroundEmojiId {
hasChanges = true
}
let emojiStatus: PeerEmojiStatus?
if case let .some(value) = self.updatedPeerStatusEmoji {
if let value {
emojiStatus = PeerEmojiStatus(fileId: value, expirationDate: nil)
} else {
emojiStatus = nil
}
if case let .some(value) = self.updatedPeerStatus {
emojiStatus = value
} else {
emojiStatus = contentsData.peer?.emojiStatus
emojiStatus = peer.emojiStatus
}
if emojiStatus != peer.emojiStatus {
hasChanges = true
}
let statusFileId = emojiStatus?.fileId
let _ = statusFileId
return ResolvedState(
nameColor: nameColor,
profileColor: profileColor,
replyFileId: replyFileId,
backgroundFileId: backgroundFileId,
emojiStatus: emojiStatus,
hasChanges: hasChanges
)
}
private func applySettings() {
guard let component = self.component, let resolvedState = self.resolveState(), let requiredLevel = self.requiredLevel else {
return
}
if self.isApplyingSettings {
return
}
if let boostLevel = self.boostLevel, requiredLevel > boostLevel {
self.displayPremiumScreen(requiredLevel: requiredLevel)
return
}
if !resolvedState.hasChanges {
self.environment?.controller()?.dismiss()
return
}
self.isApplyingSettings = true
self.state?.updated(transition: .immediate)
self.applyDisposable?.dispose()
let statusFileId = resolvedState.emojiStatus?.fileId
enum ApplyError {
case generic
}
self.applyDisposable = (combineLatest([
component.context.engine.peers.updatePeerNameColorAndEmoji(peerId: component.peerId, nameColor: nameColor, backgroundEmojiId: replyFileId, profileColor: profileColor, profileBackgroundEmojiId: backgroundFileId)
component.context.engine.peers.updatePeerNameColorAndEmoji(peerId: component.peerId, nameColor: resolvedState.nameColor, backgroundEmojiId: resolvedState.replyFileId, profileColor: resolvedState.profileColor, profileBackgroundEmojiId: resolvedState.backgroundFileId)
|> ignoreValues
|> mapError { _ -> ApplyError in
return .generic
},
component.context.engine.peers.updatePeerEmojiStatus(peerId: component.peerId, fileId: statusFileId, expirationDate: nil)
|> mapError { _ -> ApplyError in
return .generic
}
])
|> deliverOnMainQueue).start(error: { [weak self] _ in
guard let self else {
guard let self, let component = self.component else {
return
}
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
self.isApplyingSettings = false
self.state?.updated(transition: .immediate)
}, completed: { [weak self] in
@@ -553,29 +618,62 @@ final class ChannelAppearanceScreenComponent: Component {
selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: currentFileId))
}
let controller = EmojiStatusSelectionController(
context: component.context,
mode: .backgroundSelection(completion: { [weak self] result in
let mappedSubject: EmojiPagerContentComponent.Subject
switch subject {
case .reply, .profile:
mappedSubject = .backgroundIcon
case .status:
mappedSubject = .channelStatus
}
let mappedMode: EmojiStatusSelectionController.Mode
switch subject {
case .status:
mappedMode = .customStatusSelection(completion: { [weak self] result, timestamp in
guard let self else {
return
}
if let result {
self.cachedIconFiles[result.fileId.id] = result
}
switch subject {
case .reply:
self.updatedPeerNameEmoji = result
case .profile:
self.updatedPeerProfileEmoji = result
case .status:
self.updatedPeerStatusEmoji = result
self.updatedPeerStatus = (result?.fileId.id).flatMap { PeerEmojiStatus(fileId: $0, expirationDate: timestamp) }
default:
break
}
self.state?.updated(transition: .spring(duration: 0.4))
}),
})
default:
mappedMode = .backgroundSelection(completion: { [weak self] result in
guard let self else {
return
}
if let result {
self.cachedIconFiles[result.fileId.id] = result
}
switch subject {
case .reply:
self.updatedPeerNameEmoji = (result?.fileId.id)
case .profile:
self.updatedPeerProfileEmoji = (result?.fileId.id)
case .status:
self.updatedPeerStatus = (result?.fileId.id).flatMap { PeerEmojiStatus(fileId: $0, expirationDate: nil) }
}
self.state?.updated(transition: .spring(duration: 0.4))
})
}
let controller = EmojiStatusSelectionController(
context: component.context,
mode: mappedMode,
sourceView: sourceView,
emojiContent: EmojiPagerContentComponent.emojiInputData(
context: component.context,
animationCache: component.context.animationCache,
animationRenderer: component.context.animationRenderer,
isStandalone: false,
subject: subject == .status ? .status : .backgroundIcon,
subject: mappedSubject,
hasTrending: false,
topReactionItems: [],
areUnicodeEmojiEnabled: false,
@@ -588,6 +686,9 @@ final class ChannelAppearanceScreenComponent: Component {
currentSelection: currentFileId,
color: color,
destinationItemView: { [weak sourceView] in
guard let sourceView else {
return nil
}
return sourceView
}
)
@@ -618,6 +719,9 @@ final class ChannelAppearanceScreenComponent: Component {
guard let self else {
return
}
if self.contentsData == nil, case let .channel(channel) = contentsData.peer {
self.boostLevel = channel.approximateBoostLevel.flatMap(Int.init)
}
self.contentsData = contentsData
if !self.isUpdating {
self.state?.updated(transition: .immediate)
@@ -631,6 +735,7 @@ final class ChannelAppearanceScreenComponent: Component {
guard let self else {
return
}
self.boostLevel = boostStatus?.level
self.boostStatus = boostStatus
if !self.isUpdating {
self.state?.updated(transition: .immediate)
@@ -638,65 +743,40 @@ final class ChannelAppearanceScreenComponent: Component {
})
}
guard let contentsData = self.contentsData, var peer = contentsData.peer else {
guard let contentsData = self.contentsData, var peer = contentsData.peer, let resolvedState = self.resolveState() else {
return availableSize
}
var requiredLevel = 1
let replyIconLevel = 5
let profileIconLevel = 7
let emojiStatusLevel = 8
var profileIconLevel = 7
var emojiStatusLevel = 8
let themeLevel = 9
let nameColor: PeerNameColor
if let updatedPeerNameColor = self.updatedPeerNameColor {
nameColor = updatedPeerNameColor
} else if let peerNameColor = peer.nameColor {
nameColor = peerNameColor
} else {
nameColor = .blue
if let data = component.context.currentAppConfiguration.with({ $0 }).data {
if let value = data["channel_profile_color_level_min"] as? Double {
profileIconLevel = Int(value)
}
if let value = data["channel_emoji_status_level_min"] as? Double {
emojiStatusLevel = Int(value)
}
}
let profileColor: PeerNameColor?
if case let .some(value) = self.updatedPeerProfileColor {
profileColor = value
} else if let peerProfileColor = peer.profileColor {
profileColor = peerProfileColor
} else {
profileColor = nil
}
let profileColor = resolvedState.profileColor
let replyFileId: Int64?
if case let .some(value) = self.updatedPeerNameEmoji {
replyFileId = value
} else {
replyFileId = contentsData.peer?.backgroundEmojiId
}
let replyFileId = resolvedState.replyFileId
if replyFileId != nil {
requiredLevel = max(requiredLevel, replyIconLevel)
}
let backgroundFileId: Int64?
if case let .some(value) = self.updatedPeerProfileEmoji {
backgroundFileId = value
} else {
backgroundFileId = contentsData.peer?.profileBackgroundEmojiId
}
if backgroundFileId != nil {
let backgroundFileId = resolvedState.backgroundFileId
if profileColor != nil || backgroundFileId != nil {
requiredLevel = max(requiredLevel, profileIconLevel)
}
let emojiStatus: PeerEmojiStatus?
if case let .some(value) = self.updatedPeerStatusEmoji {
if let value {
emojiStatus = PeerEmojiStatus(fileId: value, expirationDate: nil)
} else {
emojiStatus = nil
}
} else {
emojiStatus = contentsData.peer?.emojiStatus
}
let emojiStatus = resolvedState.emojiStatus
if emojiStatus != nil {
requiredLevel = max(requiredLevel, emojiStatusLevel)
}
@@ -712,13 +792,51 @@ final class ChannelAppearanceScreenComponent: Component {
}
}
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()
let disposable = MetaDisposable()
self.resolvingCurrentTheme = (currentTheme, environment.theme.overallDarkAppearance, disposable)
var presentationTheme: PresentationTheme?
switch currentTheme {
case .builtin:
presentationTheme = makePresentationTheme(mediaBox: component.context.sharedContext.accountManager.mediaBox, themeReference: .builtin(environment.theme.overallDarkAppearance ? .night : .dayClassic))
case let .cloud(cloudTheme):
presentationTheme = makePresentationTheme(cloudTheme: cloudTheme.theme, dark: environment.theme.overallDarkAppearance)
default:
presentationTheme = makePresentationTheme(mediaBox: component.context.sharedContext.accountManager.mediaBox, themeReference: currentTheme)
}
if let presentationTheme {
let resolvedWallpaper: Signal<TelegramWallpaper?, NoError>
if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 {
resolvedWallpaper = cachedWallpaper(account: component.context.account, slug: file.slug, settings: file.settings)
|> map { wallpaper -> TelegramWallpaper? in
return wallpaper?.wallpaper
}
} else {
resolvedWallpaper = .single(presentationTheme.chat.defaultWallpaper)
}
disposable.set((resolvedWallpaper
|> deliverOnMainQueue).startStrict(next: { [weak self] resolvedWallpaper in
guard let self, let environment = self.environment else {
return
}
self.resolvedCurrentTheme = (currentTheme, environment.theme.overallDarkAppearance, presentationTheme, resolvedWallpaper)
if !self.isUpdating {
self.state?.updated(transition: .immediate)
}
}))
}
}
if self.currentTheme != nil && self.currentTheme != chatThemes.first {
requiredLevel = max(requiredLevel, themeLevel)
}
if case let .user(user) = peer {
peer = .user(user
.withUpdatedNameColor(nameColor)
.withUpdatedNameColor(resolvedState.nameColor)
.withUpdatedProfileColor(profileColor)
.withUpdatedEmojiStatus(emojiStatus)
.withUpdatedBackgroundEmojiId(replyFileId)
@@ -726,7 +844,7 @@ final class ChannelAppearanceScreenComponent: Component {
)
} else if case let .channel(channel) = peer {
peer = .channel(channel
.withUpdatedNameColor(nameColor)
.withUpdatedNameColor(resolvedState.nameColor)
.withUpdatedProfileColor(profileColor)
.withUpdatedEmojiStatus(emojiStatus)
.withUpdatedBackgroundEmojiId(replyFileId)
@@ -750,16 +868,17 @@ final class ChannelAppearanceScreenComponent: Component {
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
//TODO:localize
let messageItem = PeerNameColorChatPreviewItem.MessageItem(
outgoing: false,
peerId: EnginePeer.Id(namespace: peer.id.namespace, id: PeerId.Id._internalFromInt64Value(0)),
author: peer.compactDisplayTitle,
photo: peer.profileImageRepresentations,
nameColor: nameColor,
nameColor: resolvedState.nameColor,
backgroundEmojiId: replyFileId,
reply: (peer.compactDisplayTitle, environment.strings.NameColor_ChatPreview_ReplyText_Channel),
linkPreview: (environment.strings.NameColor_ChatPreview_LinkSite, environment.strings.NameColor_ChatPreview_LinkTitle, environment.strings.NameColor_ChatPreview_LinkText),
text: environment.strings.NameColor_ChatPreview_MessageText_Channel
reply: (peer.compactDisplayTitle, "Reply to your channel"),
linkPreview: ("Telegram", "Link Preview", "This preview will also be tinted."),
text: "The color you select will be used for the channel's name"
)
var replyLogoContents: [AnyComponentWithIdentity<Empty>] = []
@@ -771,13 +890,22 @@ final class ChannelAppearanceScreenComponent: Component {
)),
maximumNumberOfLines: 0
))))
if replyFileId != nil, let boostStatus = self.boostStatus, boostStatus.level < replyIconLevel {
if replyFileId != nil, let boostLevel = self.boostLevel, boostLevel < replyIconLevel {
replyLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
strings: environment.strings,
level: replyIconLevel
))))
}
var chatPreviewTheme: PresentationTheme = environment.theme
var chatPreviewWallpaper: TelegramWallpaper = presentationData.chatWallpaper
if let resolvedCurrentTheme = self.resolvedCurrentTheme {
chatPreviewTheme = resolvedCurrentTheme.theme
if let wallpaper = resolvedCurrentTheme.wallpaper {
chatPreviewWallpaper = wallpaper
}
}
let replySectionSize = self.replySection.update(
transition: transition,
component: AnyComponent(ListSectionComponent(
@@ -795,13 +923,13 @@ final class ChannelAppearanceScreenComponent: Component {
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor(
itemGenerator: PeerNameColorChatPreviewItem(
context: component.context,
theme: environment.theme,
componentTheme: environment.theme,
theme: chatPreviewTheme,
componentTheme: chatPreviewTheme,
strings: environment.strings,
sectionId: 0,
fontSize: presentationData.chatFontSize,
chatBubbleCorners: presentationData.chatBubbleCorners,
wallpaper: presentationData.chatWallpaper,
wallpaper: chatPreviewWallpaper,
dateTimeFormat: environment.dateTimeFormat,
nameDisplayOrder: presentationData.nameDisplayOrder,
messageItems: [messageItem]
@@ -813,7 +941,7 @@ final class ChannelAppearanceScreenComponent: Component {
theme: environment.theme,
colors: component.context.peerNameColors,
isProfile: false,
currentColor: nameColor,
currentColor: resolvedState.nameColor,
updated: { [weak self] value in
guard let self else {
return
@@ -830,32 +958,16 @@ final class ChannelAppearanceScreenComponent: Component {
title: AnyComponent(HStack(replyLogoContents, spacing: 6.0)),
icon: AnyComponentWithIdentity(id: 0, component: AnyComponent(EmojiActionIconComponent(
context: component.context,
color: component.context.peerNameColors.get(nameColor, dark: environment.theme.overallDarkAppearance).main,
color: component.context.peerNameColors.get(resolvedState.nameColor, dark: environment.theme.overallDarkAppearance).main,
fileId: replyFileId,
file: replyFileId.flatMap { self.cachedIconFiles[$0] }
))),
action: { [weak self] view in
guard let self, let contentsData = self.contentsData, let view = view as? ListActionItemComponent.View, let iconView = view.iconView else {
guard let self, let resolvedState = self.resolveState(), let view = view as? ListActionItemComponent.View, let iconView = view.iconView else {
return
}
let nameColor: PeerNameColor
if let updatedPeerNameColor = self.updatedPeerNameColor {
nameColor = updatedPeerNameColor
} else if let peerNameColor = peer.nameColor {
nameColor = peerNameColor
} else {
nameColor = .blue
}
let currentFileId: Int64?
if case let .some(value) = self.updatedPeerNameEmoji {
currentFileId = value
} else {
currentFileId = contentsData.peer?.backgroundEmojiId
}
self.openEmojiSetup(sourceView: iconView, currentFileId: currentFileId, color: component.context.peerNameColors.get(nameColor, dark: environment.theme.overallDarkAppearance).main, subject: .reply)
self.openEmojiSetup(sourceView: iconView, currentFileId: resolvedState.replyFileId, color: component.context.peerNameColors.get(resolvedState.nameColor, dark: environment.theme.overallDarkAppearance).main, subject: .reply)
}
)))
]
@@ -884,7 +996,7 @@ final class ChannelAppearanceScreenComponent: Component {
)),
maximumNumberOfLines: 0
))))
if currentTheme != chatThemes[0], let boostStatus = self.boostStatus, boostStatus.level < themeLevel {
if currentTheme != chatThemes[0], let boostLevel = self.boostLevel, boostLevel < themeLevel {
wallpaperLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
strings: environment.strings,
level: themeLevel
@@ -915,7 +1027,8 @@ final class ChannelAppearanceScreenComponent: Component {
animatedEmojiStickers: component.context.animatedEmojiStickers,
themeSpecificAccentColors: [:],
themeSpecificChatWallpapers: [:],
nightMode: false,
nightMode: environment.theme.overallDarkAppearance,
channelMode: true,
currentTheme: currentTheme,
updatedTheme: { [weak self] value in
guard let self else {
@@ -964,7 +1077,7 @@ final class ChannelAppearanceScreenComponent: Component {
)),
maximumNumberOfLines: 0
))))
if backgroundFileId != nil, let boostStatus = self.boostStatus, boostStatus.level < profileIconLevel {
if backgroundFileId != nil, let boostLevel = self.boostLevel, boostLevel < profileIconLevel {
profileLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
strings: environment.strings,
level: profileIconLevel
@@ -1042,28 +1155,12 @@ final class ChannelAppearanceScreenComponent: Component {
file: backgroundFileId.flatMap { self.cachedIconFiles[$0] }
))),
action: { [weak self] view in
guard let self, let contentsData = self.contentsData, let view = view as? ListActionItemComponent.View, let iconView = view.iconView else {
guard let self, let resolvedState = self.resolveState(), let view = view as? ListActionItemComponent.View, let iconView = view.iconView else {
return
}
let currentFileId: Int64?
if case let .some(value) = self.updatedPeerProfileEmoji {
currentFileId = value
} else {
currentFileId = contentsData.peer?.profileBackgroundEmojiId
}
let profileColor: PeerNameColor?
if case let .some(value) = self.updatedPeerProfileColor {
profileColor = value
} else if let peerProfileColor = peer.profileColor {
profileColor = peerProfileColor
} else {
profileColor = nil
}
self.openEmojiSetup(sourceView: iconView, currentFileId: currentFileId, color: profileColor.flatMap { profileColor in
component.context.peerNameColors.getProfile(profileColor, dark: environment.theme.overallDarkAppearance, subject: .palette).main
self.openEmojiSetup(sourceView: iconView, currentFileId: resolvedState.backgroundFileId, color: resolvedState.profileColor.flatMap {
component.context.peerNameColors.getProfile($0, dark: environment.theme.overallDarkAppearance, subject: .palette).main
} ?? environment.theme.list.itemAccentColor, subject: .profile)
}
)))
@@ -1091,7 +1188,7 @@ final class ChannelAppearanceScreenComponent: Component {
)),
maximumNumberOfLines: 0
))))
if emojiStatus != nil, let boostStatus = self.boostStatus, boostStatus.level < emojiStatusLevel {
if emojiStatus != nil, let boostLevel = self.boostLevel, boostLevel < emojiStatusLevel {
emojiStatusContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent(
strings: environment.strings,
level: emojiStatusLevel
@@ -1174,18 +1271,11 @@ final class ChannelAppearanceScreenComponent: Component {
file: statusFileId.flatMap { self.cachedIconFiles[$0] }
))),
action: { [weak self] view in
guard let self, let contentsData = self.contentsData, let view = view as? ListActionItemComponent.View, let iconView = view.iconView else {
guard let self, let resolvedState = self.resolveState(), let view = view as? ListActionItemComponent.View, let iconView = view.iconView else {
return
}
let currentFileId: Int64?
if case let .some(value) = self.updatedPeerStatusEmoji {
currentFileId = value
} else {
currentFileId = contentsData.peer?.emojiStatus?.fileId
}
self.openEmojiSetup(sourceView: iconView, currentFileId: currentFileId, color: nil, subject: .status)
self.openEmojiSetup(sourceView: iconView, currentFileId: resolvedState.emojiStatus?.fileId, color: nil, subject: .status)
}
)))
]
@@ -1210,7 +1300,7 @@ final class ChannelAppearanceScreenComponent: Component {
Text(text: "Apply Changes", font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.foregroundColor)
)))
if let boostStatus = self.boostStatus, requiredLevel > boostStatus.level {
if let boostLevel = self.boostLevel, requiredLevel > boostLevel {
buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(PremiumLockButtonSubtitleComponent(
count: requiredLevel,
theme: environment.theme,
@@ -1266,6 +1356,8 @@ final class ChannelAppearanceScreenComponent: Component {
self.bottomPanelSeparator.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
transition.setFrame(layer: self.bottomPanelSeparator, frame: CGRect(origin: CGPoint(x: bottomPanelFrame.minX, y: bottomPanelFrame.minY), size: CGSize(width: bottomPanelFrame.width, height: UIScreenPixel)))
let previousBounds = self.scrollView.bounds
let contentSize = CGSize(width: availableSize.width, height: contentHeight)
if self.scrollView.frame != CGRect(origin: CGPoint(), size: availableSize) {
self.scrollView.frame = CGRect(origin: CGPoint(), size: availableSize)
@@ -1278,6 +1370,14 @@ final class ChannelAppearanceScreenComponent: Component {
self.scrollView.scrollIndicatorInsets = scrollInsets
}
if !previousBounds.isEmpty, !transition.animation.isImmediate {
let bounds = self.scrollView.bounds
if bounds.maxY != previousBounds.maxY {
let offsetY = previousBounds.maxY - bounds.maxY
transition.animateBoundsOrigin(view: self.scrollView, from: CGPoint(x: 0.0, y: offsetY), to: CGPoint(), additive: true)
}
}
self.updateScrolling(transition: transition)
return availableSize

View File

@@ -299,7 +299,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
if let currentItem, currentItem.messageItems.first?.nameColor != item.messageItems.first?.nameColor || currentItem.messageItems.first?.backgroundEmojiId != item.messageItems.first?.backgroundEmojiId {
if let currentItem, currentItem.messageItems.first?.nameColor != item.messageItems.first?.nameColor || currentItem.messageItems.first?.backgroundEmojiId != item.messageItems.first?.backgroundEmojiId || currentItem.theme !== item.theme || currentItem.wallpaper != item.wallpaper {
if let snapshot = strongSelf.view.snapshotView(afterScreenUpdates: false) {
snapshot.frame = CGRect(origin: CGPoint(x: 0.0, y: -insets.top), size: snapshot.frame.size)
strongSelf.view.addSubview(snapshot)

View File

@@ -26,6 +26,7 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
let emojiFile: TelegramMediaFile?
let themeReference: PresentationThemeReference
let nightMode: Bool
let channelMode: Bool
let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
var selected: Bool
@@ -50,6 +51,9 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
if lhs.nightMode != rhs.nightMode {
return false
}
if lhs.channelMode != rhs.channelMode {
return false
}
if lhs.themeSpecificAccentColors != rhs.themeSpecificAccentColors {
return false
}
@@ -76,7 +80,7 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable {
}
func item(context: AccountContext, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem {
return ThemeCarouselThemeIconItem(context: context, emojiFile: self.emojiFile, themeReference: self.themeReference, nightMode: self.nightMode, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action, contextAction: contextAction)
return ThemeCarouselThemeIconItem(context: context, emojiFile: self.emojiFile, themeReference: self.themeReference, nightMode: self.nightMode, channelMode: self.channelMode, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action, contextAction: contextAction)
}
}
@@ -86,6 +90,7 @@ public class ThemeCarouselThemeIconItem: ListViewItem {
public let emojiFile: TelegramMediaFile?
public let themeReference: PresentationThemeReference
public let nightMode: Bool
public let channelMode: Bool
public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
public let selected: Bool
@@ -95,11 +100,12 @@ public class ThemeCarouselThemeIconItem: ListViewItem {
public let action: (PresentationThemeReference) -> Void
public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
public init(context: AccountContext, emojiFile: TelegramMediaFile?, themeReference: PresentationThemeReference, nightMode: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], selected: Bool, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper?, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) {
public init(context: AccountContext, emojiFile: TelegramMediaFile?, themeReference: PresentationThemeReference, nightMode: Bool, channelMode: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], selected: Bool, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper?, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) {
self.context = context
self.emojiFile = emojiFile
self.themeReference = themeReference
self.nightMode = nightMode
self.channelMode = channelMode
self.themeSpecificAccentColors = themeSpecificAccentColors
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
self.selected = selected
@@ -339,6 +345,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
var updatedThemeReference = false
var updatedTheme = false
var updatedNightMode = false
var updatedChannelMode = false
var updatedWallpaper = false
var updatedSelected = false
@@ -348,6 +355,9 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
if currentItem?.nightMode != item.nightMode {
updatedNightMode = true
}
if currentItem?.channelMode != item.channelMode {
updatedChannelMode = true
}
if currentItem?.wallpaper != item.wallpaper {
updatedWallpaper = true
}
@@ -373,7 +383,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
if let strongSelf = self {
strongSelf.item = item
if updatedThemeReference || updatedWallpaper || updatedNightMode {
if updatedThemeReference || updatedWallpaper || updatedNightMode || updatedChannelMode {
var themeReference = item.themeReference
if case .builtin = themeReference, item.nightMode {
themeReference = .builtin(.night)
@@ -382,7 +392,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
let color = item.themeSpecificAccentColors[themeReference.index]
let wallpaper = item.themeSpecificChatWallpapers[themeReference.index]
strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: color, wallpaper: wallpaper ?? item.wallpaper, nightMode: item.nightMode, emoticon: true))
strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: color, wallpaper: wallpaper ?? item.wallpaper, nightMode: item.nightMode, channelMode: item.channelMode, emoticon: true))
strongSelf.imageNode.backgroundColor = nil
}
@@ -519,12 +529,13 @@ public class ThemeCarouselThemeItem: ListViewItem, ItemListItem, ListItemCompone
public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor]
public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper]
public let nightMode: Bool
public let channelMode: Bool
public let currentTheme: PresentationThemeReference
public let updatedTheme: (PresentationThemeReference) -> Void
public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
public let tag: ItemListItemTag?
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) {
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], animatedEmojiStickers: [String: [StickerPackItem]], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], nightMode: Bool, channelMode: Bool = false, currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
self.context = context
self.theme = theme
self.strings = strings
@@ -533,6 +544,7 @@ public class ThemeCarouselThemeItem: ListViewItem, ItemListItem, ListItemCompone
self.themeSpecificAccentColors = themeSpecificAccentColors
self.themeSpecificChatWallpapers = themeSpecificChatWallpapers
self.nightMode = nightMode
self.channelMode = channelMode
self.currentTheme = currentTheme
self.updatedTheme = updatedTheme
self.contextAction = contextAction
@@ -602,6 +614,9 @@ public class ThemeCarouselThemeItem: ListViewItem, ItemListItem, ListItemCompone
if lhs.nightMode != rhs.nightMode {
return false
}
if lhs.channelMode != rhs.channelMode {
return false
}
if lhs.currentTheme != rhs.currentTheme {
return false
}
@@ -827,12 +842,12 @@ public class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode {
hasCurrentTheme = true
}
let emojiFile = theme.emoticon.flatMap { item.animatedEmojiStickers[$0]?.first?.file }
entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: emojiFile, themeReference: theme, nightMode: item.nightMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil))
entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: emojiFile, themeReference: theme, nightMode: item.nightMode, channelMode: item.channelMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil))
index += 1
}
if !hasCurrentTheme {
entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: nil, themeReference: item.currentTheme, nightMode: false, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: true, theme: item.theme, strings: item.strings, wallpaper: nil))
entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: nil, themeReference: item.currentTheme, nightMode: false, channelMode: item.channelMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: true, theme: item.theme, strings: item.strings, wallpaper: nil))
}
let action: (PresentationThemeReference) -> Void = { [weak self] themeReference in