Merge commit 'c7fe52fd728ed7fd11b5ac2c401fff3b3d6de597'

This commit is contained in:
Ali 2021-08-20 21:29:56 +02:00
commit 904151cb85
29 changed files with 547 additions and 212 deletions

View File

@ -6608,3 +6608,48 @@ Sorry for the inconvenience.";
"Conversation.ForwardOptions.Messages_0" = "%@ messages"; "Conversation.ForwardOptions.Messages_0" = "%@ messages";
"Conversation.ForwardOptions.You" = "You (senders' names hidden)"; "Conversation.ForwardOptions.You" = "You (senders' names hidden)";
"Activity.ChoosingSticker" = "choosing sticker";
"WallpaperPreview.Animate" = "Animate";
"WallpaperPreview.AnimateDescription" = "Colors will move when you send messages";
"Username.InvalidStartsWithUnderscore" = "Sorry, a username can't start with an underscore.";
"Username.InvalidEndsWithUnderscore" = "Sorry, a username can't end with an underscore.";
"Channel.Username.InvalidStartsWithUnderscore" = "Channel names can't start with an underscore.";
"Channel.Username.InvalidEndsWithUnderscore" = "Channel names can't end with an underscore.";
"Group.Username.InvalidStartsWithUnderscore" = "Group names can't start with an underscore.";
"Group.Username.InvalidEndsWithUnderscore" = "Group names can't end with an underscore.";
"Appstore.Cloud" = "**Cloud-based**\nUnlimited storage for chats,\nmedia and documents.";
"Appstore.Cloud.Profile" = "**Jennifer**\n23 y.o. designer from San Francisco.";
"Appstore.Creative" = "**Creative**\nColor themes, stickers, GIFs,\nvideo messages and more.";
"Appstore.Creative.Chat" = "**You**\nSend a dice emoji to roll a die!\n**You**\nAdvance to Illinois Ave. If you pass Go, collect coffee\n**Gabriella**\nPassed!\n**You**\nOkay\nWait for me there.";
"Appstore.Creative.Chat.Name" = "**Gabriella**";
"Appstore.Fast" = "**Fast**\nSimple, reliable and synced\nacross all your devices.";
"Appstore.Fast.Chat1" = "**Alicia Torreaux**\nBob says hi.";
"Appstore.Fast.Chat2" = "**Roberto**\nSay hello to Alice.";
"Appstore.Fast.Chat3" = "**Digital Nomads**\nJennie\nWe just reached 2,500 members! WOO!";
"Appstore.Fast.Chat4" = "**Veronica**\nTable for four, 2 PM. Be there.";
"Appstore.Fast.Chat5" = "**Animal Videos**\nVote now! Moar cat videos in this channel?";
"Appstore.Fast.Chat6" = "**Little Sister**\nDon't tell mom yet, but I got the job! I'm going to ROME!";
"Appstore.Fast.Chat7" = "**James**\nCheck these out";
"Appstore.Fast.Chat8" = "**Study Group**\nEmma\nSticker";
"Appstore.Fast.Chat9" = "**Digital Nomads**";
"Appstore.Free.Chat" = "**Jessica**\nPaper airplane is lyfted by...\nWings\nPropeller\n**You**\nIs this from Mondays test?\n**Harry**\nOnlinePartyPlan.ppd\nLet's get back to planning!\n**You**\n550 MB keynote file??\n**Helene**\nHe added fireworks videos";
"Appstore.Free.Chat.Name" = "**Study Group**";
"Appstore.Open" = "**Open**\nNo ads, no fees. Open source\ncode free for everyone.";
"Appstore.Powerful" = "**Powerful**\nNo limits on the size of\ngroups and broadcasts.";
"Appstore.Powerful.Chat" = "**James**\nGood morning!\n\nDwayne joined the group\n\n**You**\nDo you have any idea what time it is?\n**Roxanne**\nIs it still morning?\nSure!\nNot sure\n**Emma**\nVoice";
"Appstore.Private" = "**Private**\nYour data is never disclosed.\nOnly you are in control.";
"Appstore.Private.Chat" = "**You**\nNo limits on the size of your cats.";
"Appstore.Private.Chat.Name" = "**Beatrice**";
"Appstore.Public" = "**Public**\nPublic channels, open groups,\nbots for integrations.";
"Appstore.Public.Chat1" = "**Financial Times**\nTruth is like the sun. You can shut it out for a time, but it aint goin away.";
"Appstore.Public.Chat2" = "**Bloomberg**\nWe'll be sending you a few big stories daily, which you can expect to start...";
"Appstore.Public.Chat3" = "**Health and Safety**\nIf you're looking for official news about the Novel Coronavirus and COVID-19";
"Appstore.Public.IV" = "We now have enough data to measure the relative effectiveness of major climate solutions. This simulator lets you see which ones would work best.\n\nBloomberg\n\nThe Best Way to Slow Global Warming? You Decide in This Climate Simulator\nIt was on Earth Day 2016 when more than 170 nations signed the Paris Agreement calling for limiting global warming \"to well below 2°C\".";
"Appstore.Secure" = "**Secure**\nAll chats are protected\nwith strong encryption.";
"Appstore.Secure.Chat" = "**Little Sister**\nAny gift ideas for mom?\n**You**A dog!\n**You**I'm serious. Let's get her a puppy. \n**You**\nI saw this!\n**Little Sister**\nI needed proof this was your idea!";
"Appstore.Secure.Chat.Name" = "**Little Sister**";

View File

@ -59,6 +59,8 @@ final class ChatListInputActivitiesNode: ASDisplayNode {
text = strings.Activity_PlayingGame text = strings.Activity_PlayingGame
case .typingText: case .typingText:
text = strings.DialogList_Typing text = strings.DialogList_Typing
case .choosingSticker:
text = strings.Activity_ChoosingSticker
case .speakingInGroupCall: case .speakingInGroupCall:
text = "" text = ""
} }
@ -77,6 +79,8 @@ final class ChatListInputActivitiesNode: ASDisplayNode {
state = .playingGame(string, lightColor) state = .playingGame(string, lightColor)
case .speakingInGroupCall: case .speakingInGroupCall:
state = .typingText(string, lightColor) state = .typingText(string, lightColor)
case .choosingSticker:
state = .choosingSticker(string, lightColor)
} }
} else { } else {
let text: String let text: String
@ -99,6 +103,8 @@ final class ChatListInputActivitiesNode: ASDisplayNode {
text = strings.DialogList_SinglePlayingGameSuffix(peerTitle).string text = strings.DialogList_SinglePlayingGameSuffix(peerTitle).string
case .typingText: case .typingText:
text = strings.DialogList_SingleTypingSuffix(peerTitle).string text = strings.DialogList_SingleTypingSuffix(peerTitle).string
case .choosingSticker:
text = ""
case .speakingInGroupCall: case .speakingInGroupCall:
text = "" text = ""
} }
@ -120,6 +126,8 @@ final class ChatListInputActivitiesNode: ASDisplayNode {
state = .playingGame(string, lightColor) state = .playingGame(string, lightColor)
case .speakingInGroupCall: case .speakingInGroupCall:
state = .typingText(string, lightColor) state = .typingText(string, lightColor)
case .choosingSticker:
state = .none
} }
} }
} else { } else {

View File

@ -0,0 +1,145 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import LegacyComponents
private func interpolate(from: CGFloat, to: CGFloat, value: CGFloat) -> CGFloat {
return (1.0 - value) * from + value * to
}
private final class ChatChoosingStickerActivityIndicatorNodeParameters: NSObject {
let color: UIColor
let progress: CGFloat
init(color: UIColor, progress: CGFloat) {
self.color = color
self.progress = progress
}
}
private class ChatChoosingStickerActivityIndicatorNode: ChatTitleActivityIndicatorNode {
override var duration: CFTimeInterval {
return 2.0
}
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
if let color = self.color {
return ChatChoosingStickerActivityIndicatorNodeParameters(color: color, progress: self.progress)
} else {
return nil
}
}
@objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
let context = UIGraphicsGetCurrentContext()!
if !isRasterizing {
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.fill(bounds)
}
guard let parameters = parameters as? ChatChoosingStickerActivityIndicatorNodeParameters else {
return
}
context.setFillColor(UIColor.red.cgColor)
// context.fill(bounds)
let color = parameters.color
context.setFillColor(color.cgColor)
context.setStrokeColor(color.cgColor)
var heightProgress: CGFloat = parameters.progress * 4.0
if heightProgress > 3.0 {
heightProgress = 4.0 - heightProgress
} else if heightProgress > 2.0 {
heightProgress = heightProgress - 2.0
heightProgress *= heightProgress
} else if heightProgress > 1.0 {
heightProgress = 2.0 - heightProgress
} else {
heightProgress *= heightProgress
}
var pupilProgress: CGFloat = parameters.progress * 4.0
if pupilProgress > 2.0 {
pupilProgress = 3.0 - pupilProgress
}
pupilProgress = min(1.0, max(0.0, pupilProgress))
pupilProgress *= pupilProgress
var positionProgress: CGFloat = parameters.progress * 2.0
if positionProgress > 1.0 {
positionProgress = 2.0 - positionProgress
}
let eyeWidth: CGFloat = 6.0
let eyeHeight: CGFloat = 11.0 - 2.0 * heightProgress
let eyeOffset: CGFloat = -1.0 + positionProgress * 2.0
let leftCenter = CGPoint(x: bounds.width / 2.0 - eyeWidth - 1.0 + eyeOffset, y: bounds.height / 2.0)
let rightCenter = CGPoint(x: bounds.width / 2.0 + 1.0 + eyeOffset, y: bounds.height / 2.0)
let pupilSize: CGFloat = 4.0
let pupilCenter = CGPoint(x: -1.0 + pupilProgress * 2.0, y: 0.0)
context.strokeEllipse(in: CGRect(x: leftCenter.x - eyeWidth / 2.0, y: leftCenter.y - eyeHeight / 2.0, width: eyeWidth, height: eyeHeight))
context.fillEllipse(in: CGRect(x: leftCenter.x - pupilSize / 2.0 + pupilCenter.x * eyeWidth / 4.0, y: leftCenter.y - pupilSize / 2.0, width: pupilSize, height: pupilSize))
context.strokeEllipse(in: CGRect(x: rightCenter.x - eyeWidth / 2.0, y: rightCenter.y - eyeHeight / 2.0, width: eyeWidth, height: eyeHeight))
context.fillEllipse(in: CGRect(x: rightCenter.x - pupilSize / 2.0 + pupilCenter.x * eyeWidth / 4.0, y: rightCenter.y - pupilSize / 2.0, width: pupilSize, height: pupilSize))
// context.strokeEllipse(in: CGRect(x: 0.0, y: 0.0, width: 10.0, height: 20.0))
// context.fillEllipse(in: CGRect(x: , y: , width: 4.0, height: 4.0))
}
}
class ChatChoosingStickerActivityContentNode: ChatTitleActivityContentNode {
private let indicatorNode: ChatChoosingStickerActivityIndicatorNode
private let advanced: Bool
init(text: NSAttributedString, color: UIColor) {
self.indicatorNode = ChatChoosingStickerActivityIndicatorNode(color: color)
var text = text
self.advanced = text.string == "choosing sticker"
if self.advanced {
let mutable = text.mutableCopy() as? NSMutableAttributedString
mutable?.replaceCharacters(in: NSMakeRange(2, 2), with: " ")
if let updated = mutable{
text = updated
}
}
super.init(text: text)
self.addSubnode(self.indicatorNode)
}
override func updateLayout(_ constrainedSize: CGSize, offset: CGFloat, alignment: NSTextAlignment) -> CGSize {
let size = self.textNode.updateLayout(constrainedSize)
let indicatorSize = CGSize(width: 24.0, height: 16.0)
let originX: CGFloat
let indicatorOriginX: CGFloat
if case .center = alignment {
if self.advanced {
originX = floorToScreenPixels((-size.width) / 2.0)
} else {
originX = floorToScreenPixels((indicatorSize.width - size.width) / 2.0)
}
} else {
originX = indicatorSize.width
}
self.textNode.frame = CGRect(origin: CGPoint(x: originX, y: 0.0), size: size)
if self.advanced {
indicatorOriginX = self.textNode.frame.minX + 14.0 + UIScreenPixel
} else {
indicatorOriginX = self.textNode.frame.minX - indicatorSize.width
}
self.indicatorNode.frame = CGRect(origin: CGPoint(x: indicatorOriginX, y: 0.0), size: indicatorSize)
return CGSize(width: size.width + indicatorSize.width, height: size.height)
}
}

View File

@ -23,6 +23,7 @@ public enum ChatTitleActivityNodeState: Equatable {
case recordingVoice(NSAttributedString, UIColor) case recordingVoice(NSAttributedString, UIColor)
case recordingVideo(NSAttributedString, UIColor) case recordingVideo(NSAttributedString, UIColor)
case playingGame(NSAttributedString, UIColor) case playingGame(NSAttributedString, UIColor)
case choosingSticker(NSAttributedString, UIColor)
func contentNode() -> ChatTitleActivityContentNode? { func contentNode() -> ChatTitleActivityContentNode? {
switch self { switch self {
@ -40,6 +41,8 @@ public enum ChatTitleActivityNodeState: Equatable {
return ChatRecordingVideoActivityContentNode(text: text, color: color) return ChatRecordingVideoActivityContentNode(text: text, color: color)
case let .playingGame(text, color): case let .playingGame(text, color):
return ChatPlayingActivityContentNode(text: text, color: color) return ChatPlayingActivityContentNode(text: text, color: color)
case let .choosingSticker(text, color):
return ChatChoosingStickerActivityContentNode(text: text, color: color)
} }
} }

View File

@ -232,7 +232,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
if let current = self._dimmedImage { if let current = self._dimmedImage {
return current return current
} else if let (size, colors, positions) = self.dimmedImageParams { } else if let (size, colors, positions) = self.dimmedImageParams {
self._dimmedImage = generateGradient(size: size, colors: colors, positions: positions, adjustSaturation: 1.7) self._dimmedImage = generateGradient(size: size, colors: colors, positions: positions, adjustSaturation: self.saturation)
return self._dimmedImage return self._dimmedImage
} else { } else {
return nil return nil
@ -245,8 +245,11 @@ public final class GradientBackgroundNode: ASDisplayNode {
private let useSharedAnimationPhase: Bool private let useSharedAnimationPhase: Bool
static var sharedPhase: Int = 0 static var sharedPhase: Int = 0
public init(colors: [UIColor]? = nil, useSharedAnimationPhase: Bool = false) { private let saturation: CGFloat
public init(colors: [UIColor]? = nil, useSharedAnimationPhase: Bool = false, adjustSaturation: Bool = true) {
self.useSharedAnimationPhase = useSharedAnimationPhase self.useSharedAnimationPhase = useSharedAnimationPhase
self.saturation = adjustSaturation ? 1.7 : 1.0
self.contentView = UIImageView() self.contentView = UIImageView()
let defaultColors: [UIColor] = [ let defaultColors: [UIColor] = [
UIColor(rgb: 0x7FA381), UIColor(rgb: 0x7FA381),
@ -345,7 +348,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
images.append(generateGradient(size: imageSize, colors: self.colors, positions: morphedPositions)) images.append(generateGradient(size: imageSize, colors: self.colors, positions: morphedPositions))
if needDimmedImages { if needDimmedImages {
dimmedImages.append(generateGradient(size: imageSize, colors: self.colors, positions: morphedPositions, adjustSaturation: 1.7)) dimmedImages.append(generateGradient(size: imageSize, colors: self.colors, positions: morphedPositions, adjustSaturation: self.saturation))
} }
} }
@ -393,7 +396,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
let image = generateGradient(size: imageSize, colors: self.colors, positions: positions) let image = generateGradient(size: imageSize, colors: self.colors, positions: positions)
self.contentView.image = image self.contentView.image = image
let dimmedImage = generateGradient(size: imageSize, colors: self.colors, positions: positions, adjustSaturation: 1.7) let dimmedImage = generateGradient(size: imageSize, colors: self.colors, positions: positions, adjustSaturation: self.saturation)
self._dimmedImage = dimmedImage self._dimmedImage = dimmedImage
self.dimmedImageParams = (imageSize, self.colors, positions) self.dimmedImageParams = (imageSize, self.colors, positions)
@ -406,7 +409,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
let image = generateGradient(size: imageSize, colors: self.colors, positions: positions) let image = generateGradient(size: imageSize, colors: self.colors, positions: positions)
self.contentView.image = image self.contentView.image = image
let dimmedImage = generateGradient(size: imageSize, colors: self.colors, positions: positions, adjustSaturation: 1.7) let dimmedImage = generateGradient(size: imageSize, colors: self.colors, positions: positions, adjustSaturation: self.saturation)
self.dimmedImageParams = (imageSize, self.colors, positions) self.dimmedImageParams = (imageSize, self.colors, positions)
for cloneNode in self.cloneNodes { for cloneNode in self.cloneNodes {

View File

@ -560,9 +560,17 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
text = presentationData.strings.Channel_Username_InvalidStartsWithNumber text = presentationData.strings.Channel_Username_InvalidStartsWithNumber
} }
case .startsWithUnderscore: case .startsWithUnderscore:
text = presentationData.strings.Channel_Username_InvalidCharacters if isGroup {
text = presentationData.strings.Group_Username_InvalidStartsWithUnderscore
} else {
text = presentationData.strings.Channel_Username_InvalidStartsWithUnderscore
}
case .endsWithUnderscore: case .endsWithUnderscore:
text = presentationData.strings.Channel_Username_InvalidCharacters if isGroup {
text = presentationData.strings.Group_Username_InvalidEndsWithUnderscore
} else {
text = presentationData.strings.Channel_Username_InvalidEndsWithUnderscore
}
case .tooShort: case .tooShort:
if isGroup { if isGroup {
text = presentationData.strings.Group_Username_InvalidTooShort text = presentationData.strings.Group_Username_InvalidTooShort
@ -696,9 +704,9 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
case .startsWithDigit: case .startsWithDigit:
text = presentationData.strings.Group_Username_InvalidStartsWithNumber text = presentationData.strings.Group_Username_InvalidStartsWithNumber
case .startsWithUnderscore: case .startsWithUnderscore:
text = presentationData.strings.Channel_Username_InvalidCharacters text = presentationData.strings.Channel_Username_InvalidStartsWithUnderscore
case .endsWithUnderscore: case .endsWithUnderscore:
text = presentationData.strings.Channel_Username_InvalidCharacters text = presentationData.strings.Channel_Username_InvalidEndsWithUnderscore
case .tooShort: case .tooShort:
text = presentationData.strings.Group_Username_InvalidTooShort text = presentationData.strings.Group_Username_InvalidTooShort
case .invalidCharacters: case .invalidCharacters:

View File

@ -245,7 +245,7 @@ final class AccountManagerImpl<Types: AccountManagerTypes> {
if !self.currentRecordOperations.isEmpty || !self.currentMetadataOperations.isEmpty { if !self.currentRecordOperations.isEmpty || !self.currentMetadataOperations.isEmpty {
for (view, pipe) in self.recordsViews.copyItems() { for (view, pipe) in self.recordsViews.copyItems() {
if view.replay(operations: self.currentRecordOperations, metadataOperations: self.currentMetadataOperations) { if view.replay(operations: self.currentRecordOperations, metadataOperations: self.currentMetadataOperations) {
pipe.putNext(AccountRecordsView(view)) pipe.putNext(AccountRecordsView<Types>(view))
} }
} }
} }
@ -253,7 +253,7 @@ final class AccountManagerImpl<Types: AccountManagerTypes> {
if !self.currentUpdatedSharedDataKeys.isEmpty { if !self.currentUpdatedSharedDataKeys.isEmpty {
for (view, pipe) in self.sharedDataViews.copyItems() { for (view, pipe) in self.sharedDataViews.copyItems() {
if view.replay(accountManagerImpl: self, updatedKeys: self.currentUpdatedSharedDataKeys) { if view.replay(accountManagerImpl: self, updatedKeys: self.currentUpdatedSharedDataKeys) {
pipe.putNext(AccountSharedDataView(view)) pipe.putNext(AccountSharedDataView<Types>(view))
} }
} }
} }
@ -287,7 +287,7 @@ final class AccountManagerImpl<Types: AccountManagerTypes> {
} }
fileprivate func accountRecords() -> Signal<AccountRecordsView<Types>, NoError> { fileprivate func accountRecords() -> Signal<AccountRecordsView<Types>, NoError> {
return self.transaction(ignoreDisabled: false, { transaction -> Signal<AccountRecordsView, NoError> in return self.transaction(ignoreDisabled: false, { transaction -> Signal<AccountRecordsView<Types>, NoError> in
return self.accountRecordsInternal(transaction: transaction) return self.accountRecordsInternal(transaction: transaction)
}) })
|> switchToLatest |> switchToLatest
@ -323,9 +323,9 @@ final class AccountManagerImpl<Types: AccountManagerTypes> {
let index = self.recordsViews.add((mutableView, pipe)) let index = self.recordsViews.add((mutableView, pipe))
let queue = self.queue let queue = self.queue
return (.single(AccountRecordsView(mutableView)) return (.single(AccountRecordsView<Types>(mutableView))
|> then(pipe.signal())) |> then(pipe.signal()))
|> `catch` { _ -> Signal<AccountRecordsView, NoError> in |> `catch` { _ -> Signal<AccountRecordsView<Types>, NoError> in
} }
|> afterDisposed { [weak self] in |> afterDisposed { [weak self] in
queue.async { queue.async {
@ -337,14 +337,14 @@ final class AccountManagerImpl<Types: AccountManagerTypes> {
} }
private func sharedDataInternal(transaction: AccountManagerModifier<Types>, keys: Set<ValueBoxKey>) -> Signal<AccountSharedDataView<Types>, NoError> { private func sharedDataInternal(transaction: AccountManagerModifier<Types>, keys: Set<ValueBoxKey>) -> Signal<AccountSharedDataView<Types>, NoError> {
let mutableView = MutableAccountSharedDataView(accountManagerImpl: self, keys: keys) let mutableView = MutableAccountSharedDataView<Types>(accountManagerImpl: self, keys: keys)
let pipe = ValuePipe<AccountSharedDataView<Types>>() let pipe = ValuePipe<AccountSharedDataView<Types>>()
let index = self.sharedDataViews.add((mutableView, pipe)) let index = self.sharedDataViews.add((mutableView, pipe))
let queue = self.queue let queue = self.queue
return (.single(AccountSharedDataView(mutableView)) return (.single(AccountSharedDataView<Types>(mutableView))
|> then(pipe.signal())) |> then(pipe.signal()))
|> `catch` { _ -> Signal<AccountSharedDataView, NoError> in |> `catch` { _ -> Signal<AccountSharedDataView<Types>, NoError> in
} }
|> afterDisposed { [weak self] in |> afterDisposed { [weak self] in
queue.async { queue.async {
@ -363,7 +363,7 @@ final class AccountManagerImpl<Types: AccountManagerTypes> {
let queue = self.queue let queue = self.queue
return (.single(NoticeEntryView(mutableView)) return (.single(NoticeEntryView(mutableView))
|> then(pipe.signal())) |> then(pipe.signal()))
|> `catch` { _ -> Signal<NoticeEntryView, NoError> in |> `catch` { _ -> Signal<NoticeEntryView<Types>, NoError> in
} }
|> afterDisposed { [weak self] in |> afterDisposed { [weak self] in
queue.async { queue.async {

View File

@ -323,6 +323,8 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
var generalThemeReference: PresentationThemeReference? var generalThemeReference: PresentationThemeReference?
if case let .edit(cloudTheme) = mode { if case let .edit(cloudTheme) = mode {
generalThemeReference = PresentationThemeReference.cloud(cloudTheme).generalThemeReference generalThemeReference = PresentationThemeReference.cloud(cloudTheme).generalThemeReference
} else if case let .create(existingTheme, _) = mode, existingTheme == nil {
generalThemeReference = PresentationThemeReference.builtin(presentationData.theme.referenceTheme)
} }
let arguments = EditThemeControllerArguments(context: context, updateState: { f in let arguments = EditThemeControllerArguments(context: context, updateState: { f in

View File

@ -14,8 +14,8 @@ import MediaResources
private let randomBackgroundColors: [Int32] = [0x007aff, 0x00c2ed, 0x29b327, 0xeb6ca4, 0xf08200, 0x9472ee, 0xd33213, 0xedb400, 0x6d839e] private let randomBackgroundColors: [Int32] = [0x007aff, 0x00c2ed, 0x29b327, 0xeb6ca4, 0xf08200, 0x9472ee, 0xd33213, 0xedb400, 0x6d839e]
extension TelegramThemeSettings { extension TelegramThemeSettings {
convenience init(baseTheme: TelegramBaseTheme, accentColor: UIColor, messageColors: [UInt32], wallpaper: TelegramWallpaper?) { convenience init(baseTheme: TelegramBaseTheme, accentColor: UIColor, messageColors: [UInt32], animateMessageColors: Bool, wallpaper: TelegramWallpaper?) {
self.init(baseTheme: baseTheme, accentColor: accentColor.argb, messageColors: messageColors, wallpaper: wallpaper) self.init(baseTheme: baseTheme, accentColor: accentColor.argb, messageColors: messageColors, animateMessageColors: animateMessageColors, wallpaper: wallpaper)
} }
} }
@ -186,8 +186,8 @@ final class ThemeAccentColorController: ViewController {
if case let .edit(theme, _, generalThemeReference, _, _, completion) = strongSelf.mode { if case let .edit(theme, _, generalThemeReference, _, _, completion) = strongSelf.mode {
let _ = (prepareWallpaper let _ = (prepareWallpaper
|> deliverOnMainQueue).start(completed: { let updatedTheme: PresentationTheme |> deliverOnMainQueue).start(completed: {
let updatedTheme: PresentationTheme
var settings: TelegramThemeSettings? var settings: TelegramThemeSettings?
var hasSettings = false var hasSettings = false
var baseTheme: TelegramBaseTheme? var baseTheme: TelegramBaseTheme?
@ -201,13 +201,13 @@ final class ThemeAccentColorController: ViewController {
} }
if let themeReference = generalThemeReference { if let themeReference = generalThemeReference {
updatedTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: state.accentColor.color, backgroundColors: state.backgroundColors.map { $0.rgb }, bubbleColors: state.messagesColors.map { $0.rgb }, wallpaper: coloredWallpaper ?? state.initialWallpaper, serviceBackgroundColor: serviceBackgroundColor) ?? defaultPresentationTheme updatedTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: state.accentColor.color, backgroundColors: state.backgroundColors.map { $0.rgb }, bubbleColors: state.messagesColors.map { $0.rgb }, animateBubbleColors: state.animateMessageColors, wallpaper: coloredWallpaper ?? state.initialWallpaper, serviceBackgroundColor: serviceBackgroundColor) ?? defaultPresentationTheme
} else { } else {
updatedTheme = customizePresentationTheme(theme, editing: false, accentColor: state.accentColor.color, backgroundColors: state.backgroundColors.map { $0.rgb }, bubbleColors: state.messagesColors.map { $0.rgb }, wallpaper: state.initialWallpaper ?? coloredWallpaper) updatedTheme = customizePresentationTheme(theme, editing: false, accentColor: state.accentColor.color, backgroundColors: state.backgroundColors.map { $0.rgb }, bubbleColors: state.messagesColors.map { $0.rgb }, animateBubbleColors: state.animateMessageColors, wallpaper: state.initialWallpaper ?? coloredWallpaper)
} }
if hasSettings, let baseTheme = baseTheme { if hasSettings, let baseTheme = baseTheme {
settings = TelegramThemeSettings(baseTheme: baseTheme, accentColor: state.accentColor.color, messageColors: state.messagesColors.map { $0.rgb }, wallpaper: coloredWallpaper) settings = TelegramThemeSettings(baseTheme: baseTheme, accentColor: state.accentColor.color, messageColors: state.messagesColors.map { $0.rgb }, animateMessageColors: state.animateMessageColors, wallpaper: coloredWallpaper)
} }
completion(updatedTheme, settings) completion(updatedTheme, settings)
@ -226,7 +226,7 @@ final class ThemeAccentColorController: ViewController {
let wallpaper = coloredWallpaper ?? state.initialWallpaper let wallpaper = coloredWallpaper ?? state.initialWallpaper
let settings = TelegramThemeSettings(baseTheme: baseTheme, accentColor: state.accentColor.rgb, messageColors: state.messagesColors.map { $0.rgb }, wallpaper: wallpaper) let settings = TelegramThemeSettings(baseTheme: baseTheme, accentColor: state.accentColor.rgb, messageColors: state.messagesColors.map { $0.rgb }, animateMessageColors: state.animateMessageColors, wallpaper: wallpaper)
let baseThemeReference = PresentationThemeReference.builtin(PresentationBuiltinThemeReference(baseTheme: baseTheme)) let baseThemeReference = PresentationThemeReference.builtin(PresentationBuiltinThemeReference(baseTheme: baseTheme))
let apply: Signal<Void, CreateThemeError> let apply: Signal<Void, CreateThemeError>
@ -378,6 +378,7 @@ final class ThemeAccentColorController: ViewController {
let messageColors: [UInt32] let messageColors: [UInt32]
let defaultMessagesColor: UIColor? = nil let defaultMessagesColor: UIColor? = nil
var rotation: Int32 = 0 var rotation: Int32 = 0
let animateMessageColors: Bool
func extractWallpaperParameters(_ wallpaper: TelegramWallpaper?) { func extractWallpaperParameters(_ wallpaper: TelegramWallpaper?) {
guard let wallpaper = wallpaper else { guard let wallpaper = wallpaper else {
@ -428,7 +429,7 @@ final class ThemeAccentColorController: ViewController {
} }
} }
if case .colors(_, true) = strongSelf.mode { if case let .colors(initialThemeReference, true) = strongSelf.mode {
let themeSpecificAccentColor = settings.themeSpecificAccentColors[themeReference.index] let themeSpecificAccentColor = settings.themeSpecificAccentColors[themeReference.index]
accentColor = themeSpecificAccentColor?.color ?? defaultDayAccentColor accentColor = themeSpecificAccentColor?.color ?? defaultDayAccentColor
@ -443,6 +444,14 @@ final class ThemeAccentColorController: ViewController {
wallpaper = theme.chat.defaultWallpaper wallpaper = theme.chat.defaultWallpaper
} }
if case let .cloud(cloudTheme) = initialThemeReference, let settings = cloudTheme.theme.settings {
animateMessageColors = settings.animateMessageColors
} else if let referenceTheme = referenceTheme {
animateMessageColors = referenceTheme.chat.animateMessageColors
} else {
animateMessageColors = false
}
extractBuiltinWallpaper(wallpaper) extractBuiltinWallpaper(wallpaper)
if !wallpaper.isColorOrGradient { if !wallpaper.isColorOrGradient {
@ -486,6 +495,8 @@ final class ThemeAccentColorController: ViewController {
} else { } else {
messageColors = [] messageColors = []
} }
animateMessageColors = themeSettings.animateMessageColors
} else if case .builtin = themeReference { } else if case .builtin = themeReference {
let themeSpecificAccentColor = settings.themeSpecificAccentColors[themeReference.index] let themeSpecificAccentColor = settings.themeSpecificAccentColors[themeReference.index]
accentColor = themeSpecificAccentColor?.color ?? defaultDayAccentColor accentColor = themeSpecificAccentColor?.color ?? defaultDayAccentColor
@ -520,6 +531,8 @@ final class ThemeAccentColorController: ViewController {
messageColors = [] messageColors = []
} }
} }
animateMessageColors = false
} else { } else {
let theme = makePresentationTheme(mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, themeReference: themeReference)! let theme = makePresentationTheme(mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, themeReference: themeReference)!
@ -533,6 +546,8 @@ final class ThemeAccentColorController: ViewController {
} }
messageColors = theme.chat.message.outgoing.bubble.withWallpaper.fill.map(\.rgb) messageColors = theme.chat.message.outgoing.bubble.withWallpaper.fill.map(\.rgb)
animateMessageColors = theme.chat.animateMessageColors
} }
} }
} else if case let .edit(theme, wallpaper, _, _, _, _) = strongSelf.mode { } else if case let .edit(theme, wallpaper, _, _, _, _) = strongSelf.mode {
@ -546,13 +561,17 @@ final class ThemeAccentColorController: ViewController {
} }
messageColors = theme.chat.message.outgoing.bubble.withWallpaper.fill.map(\.rgb) messageColors = theme.chat.message.outgoing.bubble.withWallpaper.fill.map(\.rgb)
animateMessageColors = theme.chat.animateMessageColors
} else { } else {
accentColor = defaultDayAccentColor accentColor = defaultDayAccentColor
backgroundColors = [] backgroundColors = []
messageColors = [] messageColors = []
animateMessageColors = false
} }
let initialState = ThemeColorState(section: strongSelf.section, accentColor: HSBColor(color: accentColor), initialWallpaper: initialWallpaper, backgroundColors: backgroundColors.map { HSBColor(rgb: $0) }, patternWallpaper: patternWallpaper, patternIntensity: patternIntensity, defaultMessagesColor: defaultMessagesColor.flatMap { HSBColor(color: $0) }, messagesColors: messageColors.map { HSBColor(rgb: $0) }, rotation: rotation) let initialState = ThemeColorState(section: strongSelf.section, accentColor: HSBColor(color: accentColor), initialWallpaper: initialWallpaper, backgroundColors: backgroundColors.map { HSBColor(rgb: $0) }, patternWallpaper: patternWallpaper, patternIntensity: patternIntensity, animateMessageColors: animateMessageColors, defaultMessagesColor: defaultMessagesColor.flatMap { HSBColor(color: $0) }, messagesColors: messageColors.map { HSBColor(rgb: $0) }, selectedColor: 0, rotation: rotation)
strongSelf.controllerNode.updateState({ _ in strongSelf.controllerNode.updateState({ _ in
return initialState return initialState

View File

@ -48,9 +48,12 @@ struct ThemeColorState {
var patternWallpaper: TelegramWallpaper? var patternWallpaper: TelegramWallpaper?
var patternIntensity: Int32 var patternIntensity: Int32
var animateMessageColors: Bool
var defaultMessagesColor: HSBColor? var defaultMessagesColor: HSBColor?
var messagesColors: [HSBColor] var messagesColors: [HSBColor]
var selectedColor: Int
var rotation: Int32 var rotation: Int32
init() { init() {
@ -64,12 +67,14 @@ struct ThemeColorState {
self.previousPatternWallpaper = nil self.previousPatternWallpaper = nil
self.patternWallpaper = nil self.patternWallpaper = nil
self.patternIntensity = 50 self.patternIntensity = 50
self.animateMessageColors = false
self.defaultMessagesColor = nil self.defaultMessagesColor = nil
self.messagesColors = [] self.messagesColors = []
self.selectedColor = 0
self.rotation = 0 self.rotation = 0
} }
init(section: ThemeColorSection, accentColor: HSBColor, initialWallpaper: TelegramWallpaper?, backgroundColors: [HSBColor], patternWallpaper: TelegramWallpaper?, patternIntensity: Int32, defaultMessagesColor: HSBColor?, messagesColors: [HSBColor], rotation: Int32 = 0) { init(section: ThemeColorSection, accentColor: HSBColor, initialWallpaper: TelegramWallpaper?, backgroundColors: [HSBColor], patternWallpaper: TelegramWallpaper?, patternIntensity: Int32, animateMessageColors: Bool, defaultMessagesColor: HSBColor?, messagesColors: [HSBColor], selectedColor: Int = 0, rotation: Int32 = 0) {
self.section = section self.section = section
self.colorPanelCollapsed = false self.colorPanelCollapsed = false
self.displayPatternPanel = false self.displayPatternPanel = false
@ -80,8 +85,10 @@ struct ThemeColorState {
self.previousPatternWallpaper = nil self.previousPatternWallpaper = nil
self.patternWallpaper = patternWallpaper self.patternWallpaper = patternWallpaper
self.patternIntensity = patternIntensity self.patternIntensity = patternIntensity
self.animateMessageColors = animateMessageColors
self.defaultMessagesColor = defaultMessagesColor self.defaultMessagesColor = defaultMessagesColor
self.messagesColors = messagesColors self.messagesColors = messagesColors
self.selectedColor = selectedColor
self.rotation = rotation self.rotation = rotation
} }
@ -98,17 +105,21 @@ struct ThemeColorState {
if self.patternIntensity != otherState.patternIntensity { if self.patternIntensity != otherState.patternIntensity {
return false return false
} }
if self.rotation != otherState.rotation { if self.animateMessageColors != otherState.animateMessageColors {
return false return false
} }
if self.backgroundColors != otherState.backgroundColors { if self.backgroundColors != otherState.backgroundColors {
return false return false
} }
if self.messagesColors != otherState.messagesColors { if self.messagesColors != otherState.messagesColors {
return false return false
} }
if self.selectedColor != otherState.selectedColor {
return false
}
if self.rotation != otherState.rotation {
return false
}
return true return true
} }
} }
@ -324,7 +335,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.colorsButtonNode.addTarget(self, action: #selector(self.toggleColors), forControlEvents: .touchUpInside) self.colorsButtonNode.addTarget(self, action: #selector(self.toggleColors), forControlEvents: .touchUpInside)
self.playButtonNode.addTarget(self, action: #selector(self.playPressed), forControlEvents: .touchUpInside) self.playButtonNode.addTarget(self, action: #selector(self.playPressed), forControlEvents: .touchUpInside)
self.colorPanelNode.colorsChanged = { [weak self] colors, ended in self.colorPanelNode.colorsChanged = { [weak self] colors, selectedColor, ended in
if let strongSelf = self, let section = strongSelf.state.section { if let strongSelf = self, let section = strongSelf.state.section {
strongSelf.updateState({ current in strongSelf.updateState({ current in
var updated = current var updated = current
@ -339,8 +350,9 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
case .messages: case .messages:
updated.messagesColors = colors updated.messagesColors = colors
} }
updated.selectedColor = selectedColor
return updated return updated
}) }, animated: true)
} }
} }
@ -472,9 +484,9 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
if !updateOnlyWallpaper { if !updateOnlyWallpaper {
if let themeReference = mode.themeReference { if let themeReference = mode.themeReference {
updatedTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: accentColor.color, backgroundColors: backgroundColors.map { $0.rgb }, bubbleColors: messagesColors.map { $0.rgb }, serviceBackgroundColor: serviceBackgroundColor, preview: true) ?? defaultPresentationTheme updatedTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: accentColor.color, backgroundColors: backgroundColors.map { $0.rgb }, bubbleColors: messagesColors.map { $0.rgb }, animateBubbleColors: state.animateMessageColors, serviceBackgroundColor: serviceBackgroundColor, preview: true) ?? defaultPresentationTheme
} else if case let .edit(theme, _, _, _, _, _) = mode { } else if case let .edit(theme, _, _, _, _, _) = mode {
updatedTheme = customizePresentationTheme(theme, editing: false, accentColor: accentColor.color, backgroundColors: backgroundColors.map { $0.rgb }, bubbleColors: messagesColors.map { $0.rgb }) updatedTheme = customizePresentationTheme(theme, editing: false, accentColor: accentColor.color, backgroundColors: backgroundColors.map { $0.rgb }, bubbleColors: messagesColors.map { $0.rgb }, animateBubbleColors: state.animateMessageColors)
} else { } else {
updatedTheme = theme updatedTheme = theme
} }
@ -516,8 +528,6 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
strongSelf.wallpaper = wallpaper strongSelf.wallpaper = wallpaper
strongSelf.patternArguments = patternArguments strongSelf.patternArguments = patternArguments
strongSelf.colorsButtonNode.colors = backgroundColors.map { $0.color }
if !preview { if !preview {
if !backgroundColors.isEmpty { if !backgroundColors.isEmpty {
strongSelf.currentBackgroundColors = (backgroundColors, strongSelf.state.rotation, strongSelf.state.patternIntensity) strongSelf.currentBackgroundColors = (backgroundColors, strongSelf.state.rotation, strongSelf.state.patternIntensity)
@ -592,6 +602,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
} }
} }
private var crossfadeBubbles = false
func updateState(_ f: (ThemeColorState) -> ThemeColorState, animated: Bool = false) { func updateState(_ f: (ThemeColorState) -> ThemeColorState, animated: Bool = false) {
let previousState = self.state let previousState = self.state
self.state = f(self.state) self.state = f(self.state)
@ -613,14 +624,24 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
} }
} }
if (previousState.patternWallpaper != nil) != (self.state.patternWallpaper != nil) { let sectionChanged = previousState.section != self.state.section
self.patternButtonNode.setSelected(self.state.patternWallpaper != nil, animated: animated)
if self.state.section == .background {
if (previousState.patternWallpaper != nil) != (self.state.patternWallpaper != nil) || sectionChanged {
self.patternButtonNode.setSelected(self.state.patternWallpaper != nil, animated: animated)
}
} else if self.state.section == .messages {
if previousState.animateMessageColors != self.state.animateMessageColors || sectionChanged {
self.crossfadeBubbles = true
self.patternButtonNode.setSelected(self.state.animateMessageColors, animated: animated)
}
} }
let sectionChanged = previousState.section != self.state.section
if sectionChanged, let section = self.state.section { if sectionChanged, let section = self.state.section {
self.view.endEditing(true) self.view.endEditing(true)
self.patternButtonNode.title = section == .messages ? self.presentationData.strings.WallpaperPreview_Animate : self.presentationData.strings.WallpaperPreview_Pattern
var colors: [HSBColor] var colors: [HSBColor]
var defaultColor: HSBColor? var defaultColor: HSBColor?
switch section { switch section {
@ -688,6 +709,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
needsLayout = true needsLayout = true
} }
self.colorsButtonNode.colors = state.section == .messages ? state.messagesColors.map { $0.color } : state.backgroundColors.map { $0.color }
if previousState.colorPanelCollapsed != self.state.colorPanelCollapsed { if previousState.colorPanelCollapsed != self.state.colorPanelCollapsed {
animationCurve = .spring animationCurve = .spring
animationDuration = 0.45 animationDuration = 0.45
@ -730,12 +753,33 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
if (previousState.backgroundColors.count >= 2) != (self.state.backgroundColors.count >= 2) { if (previousState.backgroundColors.count >= 2) != (self.state.backgroundColors.count >= 2) {
needsLayout = true needsLayout = true
} }
if previousState.messagesColors.count != self.state.messagesColors.count {
needsLayout = true
}
if previousState.animateMessageColors != self.state.animateMessageColors {
needsLayout = true
}
if previousState.selectedColor != self.state.selectedColor {
needsLayout = true
}
if previousState.backgroundColors.count != self.state.backgroundColors.count { if let section = self.state.section {
if self.state.backgroundColors.count <= 2 { switch section {
self.playButtonNode.setImage(self.playButtonRotateImage, for: []) case .background:
} else { if previousState.backgroundColors.count != self.state.backgroundColors.count {
self.playButtonNode.setImage(self.playButtonPlayImage, for: []) if self.state.backgroundColors.count <= 2 {
self.playButtonNode.setImage(self.playButtonRotateImage, for: [])
} else {
self.playButtonNode.setImage(self.playButtonPlayImage, for: [])
}
}
case .accent:
break
case .messages:
self.playButtonNode.setImage(self.playButtonPlayImage, for: [])
} }
} }
@ -755,6 +799,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
updated.section = section updated.section = section
updated.displayPatternPanel = false updated.displayPatternPanel = false
updated.colorPanelCollapsed = false updated.colorPanelCollapsed = false
updated.selectedColor = 0
return updated return updated
}, animated: true) }, animated: true)
} }
@ -884,22 +929,11 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
} }
strongSelf.updateState({ state in strongSelf.updateState({ state in
var state = state var state = state
//if state.section == .background { state.colorPanelCollapsed = true
state.colorPanelCollapsed = true state.displayPatternPanel = false
state.displayPatternPanel = false
//}
return state return state
}, animated: true) }, animated: true)
/*if message.flags.contains(.Incoming) { }, clickThroughMessage: {
self?.updateSection(.accent)
self?.requestSectionUpdate?(.accent)
} else {
self?.updateSection(.messages)
self?.requestSectionUpdate?(.messages)
}*/
}, clickThroughMessage: { [weak self] in
self?.updateSection(.background)
self?.requestSectionUpdate?(.background)
}, backgroundNode: self.backgroundNode) }, backgroundNode: self.backgroundNode)
return item return item
} }
@ -908,6 +942,18 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
if let messageNodes = self.messageNodes { if let messageNodes = self.messageNodes {
for i in 0 ..< items.count { for i in 0 ..< items.count {
let itemNode = messageNodes[i] let itemNode = messageNodes[i]
if self.crossfadeBubbles {
if let snapshot = itemNode.view.snapshotView(afterScreenUpdates: false) {
snapshot.frame = itemNode.bounds
itemNode.view.addSubview(snapshot)
snapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshot] _ in
snapshot?.removeFromSuperview()
})
}
}
items[i].updateNode(async: { $0() }, node: { items[i].updateNode(async: { $0() }, node: {
return itemNode return itemNode
}, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in }, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in
@ -920,6 +966,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
apply(ListViewItemApply(isOnScreen: true)) apply(ListViewItemApply(isOnScreen: true))
}) })
} }
self.crossfadeBubbles = false
} else { } else {
var messageNodes: [ListViewItemNode] = [] var messageNodes: [ListViewItemNode] = []
for i in 0 ..< items.count { for i in 0 ..< items.count {
@ -928,22 +975,30 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
itemNode = node itemNode = node
apply().1(ListViewItemApply(isOnScreen: true)) apply().1(ListViewItemApply(isOnScreen: true))
}) })
//itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
messageNodes.append(itemNode!) messageNodes.append(itemNode!)
self.messagesContainerNode.addSubnode(itemNode!) self.messagesContainerNode.addSubnode(itemNode!)
} }
self.messageNodes = messageNodes self.messageNodes = messageNodes
} }
let toolbarHeight = 49.0 + layout.intrinsicInsets.bottom
var relativeOffset: CGFloat = 0.0
if !self.state.colorPanelCollapsed && self.state.section == .messages {
relativeOffset = (CGFloat(self.state.selectedColor) / CGFloat(max(1, self.state.messagesColors.count))) * (self.colorPanelNode.frame.height + toolbarHeight + 144.0) * -1.0
}
let containerSize = CGSize(width: layout.size.width, height: layout.size.height - toolbarHeight - 44.0)
var bottomOffset: CGFloat = 9.0 + bottomInset var bottomOffset: CGFloat = 9.0 + bottomInset
if let messageNodes = self.messageNodes { if let messageNodes = self.messageNodes {
for itemNode in messageNodes { for itemNode in messageNodes {
let previousFrame = itemNode.frame let previousFrame = itemNode.frame
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: itemNode.frame.size)) transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: itemNode.frame.size))
bottomOffset += itemNode.frame.height bottomOffset += itemNode.frame.height
itemNode.updateFrame(itemNode.frame, within: layout.size)
let relativeFrame = itemNode.frame.offsetBy(dx: 0.0, dy: relativeOffset)
itemNode.updateAbsoluteRect(relativeFrame, within: containerSize)
if case let .animated(duration, curve) = transition { if case let .animated(duration, curve) = transition {
itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -itemNode.frame.minY + previousFrame.minY), animationCurve: curve, duration: duration) itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -relativeFrame.minY + previousFrame.minY), animationCurve: curve, duration: duration)
} }
} }
} }
@ -954,7 +1009,6 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem) headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
} else { } else {
dateHeaderNode = headerItem.node(synchronousLoad: true) dateHeaderNode = headerItem.node(synchronousLoad: true)
//dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
self.messagesContainerNode.addSubnode(dateHeaderNode) self.messagesContainerNode.addSubnode(dateHeaderNode)
self.dateHeaderNode = dateHeaderNode self.dateHeaderNode = dateHeaderNode
} }
@ -1026,9 +1080,9 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
transition.updateBounds(node: self.backgroundWrapperNode, bounds: CGRect(origin: CGPoint(), size: layout.size)) transition.updateBounds(node: self.backgroundWrapperNode, bounds: CGRect(origin: CGPoint(), size: layout.size))
let displayOptionButtons = self.state.section == .background let displayOptionButtons = self.state.section == .background || self.state.section == .messages
var messagesBottomInset: CGFloat = bottomInset
var messagesBottomInset: CGFloat = bottomInset
if displayOptionButtons { if displayOptionButtons {
messagesBottomInset += 56.0 messagesBottomInset += 56.0
} else if chatListPreviewAvailable { } else if chatListPreviewAvailable {
@ -1054,7 +1108,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let maxButtonWidth = max(patternButtonSize.width, colorsButtonSize.width) let maxButtonWidth = max(patternButtonSize.width, colorsButtonSize.width)
let buttonSize = CGSize(width: maxButtonWidth, height: 30.0) let buttonSize = CGSize(width: maxButtonWidth, height: 30.0)
let patternAlpha: CGFloat = displayOptionButtons ? 1.0 : 0.0 var patternAlpha: CGFloat = displayOptionButtons ? 1.0 : 0.0
let colorsAlpha: CGFloat = displayOptionButtons ? 1.0 : 0.0 let colorsAlpha: CGFloat = displayOptionButtons ? 1.0 : 0.0
let patternFrame: CGRect let patternFrame: CGRect
@ -1067,15 +1121,32 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let playFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - playButtonSize.width) / 2.0), y: layout.size.height - bottomInset - 44.0 - buttonsVerticalOffset + floor((buttonSize.height - playButtonSize.height) / 2.0)), size: playButtonSize) let playFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - playButtonSize.width) / 2.0), y: layout.size.height - bottomInset - 44.0 - buttonsVerticalOffset + floor((buttonSize.height - playButtonSize.height) / 2.0)), size: playButtonSize)
let playAlpha: CGFloat let playAlpha: CGFloat
if self.state.backgroundColors.count >= 2 { var centerButtons = false
playAlpha = displayOptionButtons ? 1.0 : 0.0 if self.state.section == .background {
centerDistance += playButtonSize.width if self.state.backgroundColors.count >= 2 {
playAlpha = displayOptionButtons ? 1.0 : 0.0
centerDistance += playButtonSize.width
} else {
playAlpha = 0.0
}
} else if self.state.section == .messages {
if self.state.messagesColors.count >= 3 {
patternAlpha = 1.0
playAlpha = displayOptionButtons && self.state.animateMessageColors ? 1.0 : 0.0
if self.state.animateMessageColors {
centerDistance += playButtonSize.width
}
} else {
patternAlpha = 0.0
playAlpha = 0.0
centerButtons = true
}
} else { } else {
playAlpha = 0.0 playAlpha = 0.0
} }
patternFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonSize.width * 2.0 - centerDistance) / 2.0), y: layout.size.height - bottomInset - 44.0 - buttonsVerticalOffset), size: buttonSize) patternFrame = CGRect(origin: CGPoint(x: centerButtons ? floor((layout.size.width - buttonSize.width) / 2.0) : floor((layout.size.width - buttonSize.width * 2.0 - centerDistance) / 2.0), y: layout.size.height - bottomInset - 44.0 - buttonsVerticalOffset), size: buttonSize)
colorsFrame = CGRect(origin: CGPoint(x: patternFrame.maxX + centerDistance, y: layout.size.height - bottomInset - 44.0 - buttonsVerticalOffset), size: buttonSize) colorsFrame = CGRect(origin: CGPoint(x: centerButtons ? floor((layout.size.width - buttonSize.width) / 2.0) : patternFrame.maxX + centerDistance, y: layout.size.height - bottomInset - 44.0 - buttonsVerticalOffset), size: buttonSize)
transition.updateFrame(node: self.patternButtonNode, frame: patternFrame) transition.updateFrame(node: self.patternButtonNode, frame: patternFrame)
transition.updateAlpha(node: self.patternButtonNode, alpha: patternAlpha) transition.updateAlpha(node: self.patternButtonNode, alpha: patternAlpha)
@ -1089,35 +1160,22 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
} }
@objc private func togglePattern() { @objc private func togglePattern() {
self.view.endEditing(true) if self.state.section == .messages {
self.updateState({ current in
let wallpaper = self.state.previousPatternWallpaper ?? self.patternPanelNode.wallpapers.first var updated = current
let backgroundColors = self.currentBackgroundColors updated.animateMessageColors = !current.animateMessageColors
return updated
var appeared = false }, animated: true)
self.updateState({ current in } else {
var updated = current self.view.endEditing(true)
if !updated.displayPatternPanel {
updated.colorPanelCollapsed = false let wallpaper = self.state.previousPatternWallpaper ?? self.patternPanelNode.wallpapers.first
updated.displayPatternPanel = true let backgroundColors = self.currentBackgroundColors
if current.patternWallpaper == nil, let wallpaper = wallpaper {
updated.patternWallpaper = wallpaper var appeared = false
if updated.backgroundColors.isEmpty { self.updateState({ current in
if let backgroundColors = backgroundColors { var updated = current
updated.backgroundColors = backgroundColors.0 if !updated.displayPatternPanel {
} else {
updated.backgroundColors = []
}
}
appeared = true
}
} else {
updated.colorPanelCollapsed = true
if updated.patternWallpaper != nil {
updated.previousPatternWallpaper = updated.patternWallpaper
updated.patternWallpaper = nil
updated.displayPatternPanel = false
} else {
updated.colorPanelCollapsed = false updated.colorPanelCollapsed = false
updated.displayPatternPanel = true updated.displayPatternPanel = true
if current.patternWallpaper == nil, let wallpaper = wallpaper { if current.patternWallpaper == nil, let wallpaper = wallpaper {
@ -1129,15 +1187,36 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
updated.backgroundColors = [] updated.backgroundColors = []
} }
} }
appeared = true }
appeared = true
} else {
updated.colorPanelCollapsed = true
if updated.patternWallpaper != nil {
updated.previousPatternWallpaper = updated.patternWallpaper
updated.patternWallpaper = nil
updated.displayPatternPanel = false
} else {
updated.colorPanelCollapsed = false
updated.displayPatternPanel = true
if current.patternWallpaper == nil, let wallpaper = wallpaper {
updated.patternWallpaper = wallpaper
if updated.backgroundColors.isEmpty {
if let backgroundColors = backgroundColors {
updated.backgroundColors = backgroundColors.0
} else {
updated.backgroundColors = []
}
}
appeared = true
}
} }
} }
return updated
}, animated: true)
if appeared {
self.patternPanelNode.didAppear(initialWallpaper: wallpaper, intensity: self.state.patternIntensity)
} }
return updated
}, animated: true)
if appeared {
self.patternPanelNode.didAppear(initialWallpaper: wallpaper, intensity: self.state.patternIntensity)
} }
} }
@ -1160,12 +1239,12 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
} }
@objc private func playPressed() { @objc private func playPressed() {
if self.state.backgroundColors.count >= 3 { if self.state.backgroundColors.count >= 3 || self.state.messagesColors.count >= 3 {
self.backgroundNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring)) self.backgroundNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring))
} else { } else {
self.updateState({ state in self.updateState({ state in
var state = state var state = state
state.rotation = (state.rotation + 90) % 360 state.rotation = (state.rotation + 45) % 360
return state return state
}, animated: true) }, animated: true)
} }

View File

@ -141,7 +141,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
currentBackgroundNode = WallpaperBackgroundNode(context: item.context) currentBackgroundNode = WallpaperBackgroundNode(context: item.context)
} }
currentBackgroundNode?.update(wallpaper: item.wallpaper) currentBackgroundNode?.update(wallpaper: item.wallpaper)
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)
let insets: UIEdgeInsets let insets: UIEdgeInsets
let separatorHeight = UIScreenPixel let separatorHeight = UIScreenPixel
@ -261,13 +261,13 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
strongSelf.bottomStripeNode.isHidden = hasCorners strongSelf.bottomStripeNode.isHidden = hasCorners
} }
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
if let backgroundNode = strongSelf.backgroundNode { if let backgroundNode = strongSelf.backgroundNode {
backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0) backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0)
backgroundNode.update(wallpaper: item.wallpaper) backgroundNode.update(wallpaper: item.wallpaper)
backgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) backgroundNode.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)
backgroundNode.updateLayout(size: backgroundNode.bounds.size, transition: .immediate) backgroundNode.updateLayout(size: backgroundNode.bounds.size, transition: .immediate)
} }
strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)

View File

@ -390,7 +390,7 @@ final class WallpaperColorPanelNode: ASDisplayNode {
private var sampleItemNodes: [ColorSampleItemNode] = [] private var sampleItemNodes: [ColorSampleItemNode] = []
private let multiColorFieldNode: ColorInputFieldNode private let multiColorFieldNode: ColorInputFieldNode
var colorsChanged: (([HSBColor], Bool) -> Void)? var colorsChanged: (([HSBColor], Int, Bool) -> Void)?
var colorSelected: (() -> Void)? var colorSelected: (() -> Void)?
var rotate: (() -> Void)? var rotate: (() -> Void)?
@ -518,6 +518,7 @@ final class WallpaperColorPanelNode: ASDisplayNode {
var updateLayout = updateLayout var updateLayout = updateLayout
let previousColors = self.state.colors let previousColors = self.state.colors
let previousPreview = self.state.preview let previousPreview = self.state.preview
let previousSelection = self.state.selection
self.state = f(self.state) self.state = f(self.state)
let colorWasRemovable = self.multiColorFieldNode.isRemovable let colorWasRemovable = self.multiColorFieldNode.isRemovable
@ -548,8 +549,8 @@ final class WallpaperColorPanelNode: ASDisplayNode {
} }
} }
if self.state.colors != previousColors || self.state.preview != previousPreview { if self.state.colors != previousColors || self.state.preview != previousPreview || self.state.selection != previousSelection {
self.colorsChanged?(self.state.colors, !self.state.preview) self.colorsChanged?(self.state.colors, self.state.selection ?? 0, !self.state.preview)
} }
} }
@ -576,42 +577,12 @@ final class WallpaperColorPanelNode: ASDisplayNode {
} }
let buttonSize = CGSize(width: 26.0, height: 26.0) let buttonSize = CGSize(width: 26.0, height: 26.0)
//let middleButtonFrame = CGRect(origin: CGPoint(x: self.state.secondColor != nil ? floor((size.width - 26.0) / 2.0) : (self.state.secondColorAvailable ? size.width - rightInsetWithButton + floor((rightInsetWithButton - buttonSize.width) / 2.0) : size.width + buttonOffset), y: floor((topPanelHeight - buttonSize.height) / 2.0)), size: buttonSize)
//transition.updateFrame(node: self.rotateButton, frame: middleButtonFrame)
//transition.updateFrame(node: self.swapButton, frame: middleButtonFrame)
let canAddColors = self.state.colors.count < self.state.maximumNumberOfColors let canAddColors = self.state.colors.count < self.state.maximumNumberOfColors
transition.updateFrame(node: self.addButton, frame: CGRect(origin: CGPoint(x: size.width - rightInset - buttonSize.width, y: floor((topPanelHeight - buttonSize.height) / 2.0)), size: buttonSize)) transition.updateFrame(node: self.addButton, frame: CGRect(origin: CGPoint(x: size.width - rightInset - buttonSize.width, y: floor((topPanelHeight - buttonSize.height) / 2.0)), size: buttonSize))
transition.updateAlpha(node: self.addButton, alpha: canAddColors ? 1.0 : 0.0) transition.updateAlpha(node: self.addButton, alpha: canAddColors ? 1.0 : 0.0)
transition.updateSublayerTransformScale(node: self.addButton, scale: canAddColors ? 1.0 : 0.1) transition.updateSublayerTransformScale(node: self.addButton, scale: canAddColors ? 1.0 : 0.1)
/*let rotateButtonAlpha: CGFloat
let swapButtonAlpha: CGFloat
let addButtonAlpha: CGFloat
if let _ = self.state.secondColor {
if self.state.rotateAvailable {
rotateButtonAlpha = 1.0
swapButtonAlpha = 0.0
} else {
rotateButtonAlpha = 0.0
swapButtonAlpha = 1.0
}
addButtonAlpha = 0.0
} else {
swapButtonAlpha = 0.0
rotateButtonAlpha = 0.0
if self.state.secondColorAvailable {
addButtonAlpha = 1.0
} else {
addButtonAlpha = 0.0
}
}
transition.updateAlpha(node: self.rotateButton, alpha: rotateButtonAlpha)
transition.updateAlpha(node: self.swapButton, alpha: swapButtonAlpha)
transition.updateAlpha(node: self.addButton, alpha: addButtonAlpha)*/
func degreesToRadians(_ degrees: CGFloat) -> CGFloat { func degreesToRadians(_ degrees: CGFloat) -> CGFloat {
var degrees = degrees var degrees = degrees
if degrees >= 270.0 { if degrees >= 270.0 {

View File

@ -272,6 +272,7 @@ struct HSBColor: Equatable {
} }
let values: (h: CGFloat, s: CGFloat, b: CGFloat) let values: (h: CGFloat, s: CGFloat, b: CGFloat)
let backingColor: UIColor
var hue: CGFloat { var hue: CGFloat {
return self.values.h return self.values.h
@ -286,19 +287,22 @@ struct HSBColor: Equatable {
} }
var rgb: UInt32 { var rgb: UInt32 {
return self.color.rgb return self.color.argb
} }
init(values: (h: CGFloat, s: CGFloat, b: CGFloat)) { init(values: (h: CGFloat, s: CGFloat, b: CGFloat)) {
self.values = values self.values = values
self.backingColor = UIColor(hue: values.h, saturation: values.s, brightness: values.b, alpha: 1.0)
} }
init(hue: CGFloat, saturation: CGFloat, brightness: CGFloat) { init(hue: CGFloat, saturation: CGFloat, brightness: CGFloat) {
self.values = (h: hue, s: saturation, b: brightness) self.values = (h: hue, s: saturation, b: brightness)
self.backingColor = UIColor(hue: self.values.h, saturation: self.values.s, brightness: self.values.b, alpha: 1.0)
} }
init(color: UIColor) { init(color: UIColor) {
self.values = color.hsb self.values = color.hsb
self.backingColor = color
} }
init(rgb: UInt32) { init(rgb: UInt32) {
@ -306,7 +310,7 @@ struct HSBColor: Equatable {
} }
var color: UIColor { var color: UIColor {
return UIColor(hue: self.values.h, saturation: self.values.s, brightness: self.values.b, alpha: 1.0) return self.backingColor
} }
} }

View File

@ -880,7 +880,7 @@ public class WallpaperGalleryController: ViewController {
currentColorsPanelNode = colorsPanelNode currentColorsPanelNode = colorsPanelNode
self.overlayNode?.insertSubnode(colorsPanelNode, belowSubnode: self.toolbarNode!) self.overlayNode?.insertSubnode(colorsPanelNode, belowSubnode: self.toolbarNode!)
colorsPanelNode.colorsChanged = { [weak self] colors, _ in colorsPanelNode.colorsChanged = { [weak self] colors, _, _ in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }

View File

@ -63,8 +63,15 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode {
} }
} }
var title: String {
didSet {
self.textNode.attributedText = NSAttributedString(string: title, font: Font.medium(13), textColor: .white)
}
}
init(title: String, value: WallpaperOptionButtonValue) { init(title: String, value: WallpaperOptionButtonValue) {
self._value = value self._value = value
self.title = title
self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x000000, alpha: 0.3)) self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0x000000, alpha: 0.3))
self.backgroundNode.cornerRadius = 14.0 self.backgroundNode.cornerRadius = 14.0

View File

@ -194,7 +194,11 @@ private func usernameSetupControllerEntries(presentationData: PresentationData,
switch error { switch error {
case .startsWithDigit: case .startsWithDigit:
statusText = presentationData.strings.Username_InvalidStartsWithNumber statusText = presentationData.strings.Username_InvalidStartsWithNumber
case .startsWithUnderscore, .endsWithUnderscore, .invalidCharacters: case .startsWithUnderscore:
statusText = presentationData.strings.Username_InvalidStartsWithUnderscore
case .endsWithUnderscore:
statusText = presentationData.strings.Username_InvalidEndsWithUnderscore
case .invalidCharacters:
statusText = presentationData.strings.Username_InvalidCharacters statusText = presentationData.strings.Username_InvalidCharacters
case .tooShort: case .tooShort:
statusText = presentationData.strings.Username_InvalidTooShort statusText = presentationData.strings.Username_InvalidTooShort

View File

@ -46,8 +46,8 @@ extension TelegramBaseTheme {
extension TelegramThemeSettings { extension TelegramThemeSettings {
convenience init?(apiThemeSettings: Api.ThemeSettings) { convenience init?(apiThemeSettings: Api.ThemeSettings) {
switch apiThemeSettings { switch apiThemeSettings {
case let .themeSettings(_, baseTheme, accentColor, messageColors, wallpaper): case let .themeSettings(flags, baseTheme, accentColor, messageColors, wallpaper):
self.init(baseTheme: TelegramBaseTheme(apiBaseTheme: baseTheme), accentColor: UInt32(bitPattern: accentColor), messageColors: messageColors?.map(UInt32.init(bitPattern:)) ?? [], wallpaper: wallpaper.flatMap(TelegramWallpaper.init(apiWallpaper:))) self.init(baseTheme: TelegramBaseTheme(apiBaseTheme: baseTheme), accentColor: UInt32(bitPattern: accentColor), messageColors: messageColors?.map(UInt32.init(bitPattern:)) ?? [], animateMessageColors: (flags & 1 << 2) != 0, wallpaper: wallpaper.flatMap(TelegramWallpaper.init(apiWallpaper:)))
} }
} }
@ -57,6 +57,10 @@ extension TelegramThemeSettings {
flags |= 1 << 0 flags |= 1 << 0
} }
if self.animateMessageColors {
flags |= 1 << 2
}
var inputWallpaper: Api.InputWallPaper? var inputWallpaper: Api.InputWallPaper?
var inputWallpaperSettings: Api.WallPaperSettings? var inputWallpaperSettings: Api.WallPaperSettings?
if let wallpaper = self.wallpaper, let inputWallpaperAndSettings = wallpaper.apiInputWallpaperAndSettings { if let wallpaper = self.wallpaper, let inputWallpaperAndSettings = wallpaper.apiInputWallpaperAndSettings {

View File

@ -129,6 +129,8 @@ private func actionFromActivity(_ activity: PeerInputActivity?) -> Api.SendMessa
return .sendMessageUploadRoundAction(progress: progress) return .sendMessageUploadRoundAction(progress: progress)
case .speakingInGroupCall: case .speakingInGroupCall:
return .speakingInGroupCallAction return .speakingInGroupCallAction
case .choosingSticker:
return .sendMessageGamePlayAction
} }
} else { } else {
return .sendMessageCancelAction return .sendMessageCancelAction

View File

@ -11,6 +11,7 @@ public enum PeerInputActivity: Comparable {
case recordingInstantVideo case recordingInstantVideo
case uploadingInstantVideo(progress: Int32) case uploadingInstantVideo(progress: Int32)
case speakingInGroupCall(timestamp: Int32) case speakingInGroupCall(timestamp: Int32)
case choosingSticker
public var key: Int32 { public var key: Int32 {
switch self { switch self {
@ -32,6 +33,8 @@ public enum PeerInputActivity: Comparable {
return 7 return 7
case .playingGame: case .playingGame:
return 8 return 8
case .choosingSticker:
return 9
} }
} }

View File

@ -24,6 +24,9 @@ public final class TelegramThemeSettings: PostboxCoding, Equatable {
if lhs.messageColors != rhs.messageColors { if lhs.messageColors != rhs.messageColors {
return false return false
} }
if lhs.animateMessageColors != rhs.animateMessageColors {
return false
}
if lhs.wallpaper != rhs.wallpaper { if lhs.wallpaper != rhs.wallpaper {
return false return false
} }
@ -33,12 +36,14 @@ public final class TelegramThemeSettings: PostboxCoding, Equatable {
public let baseTheme: TelegramBaseTheme public let baseTheme: TelegramBaseTheme
public let accentColor: UInt32 public let accentColor: UInt32
public let messageColors: [UInt32] public let messageColors: [UInt32]
public let animateMessageColors: Bool
public let wallpaper: TelegramWallpaper? public let wallpaper: TelegramWallpaper?
public init(baseTheme: TelegramBaseTheme, accentColor: UInt32, messageColors: [UInt32], wallpaper: TelegramWallpaper?) { public init(baseTheme: TelegramBaseTheme, accentColor: UInt32, messageColors: [UInt32], animateMessageColors: Bool, wallpaper: TelegramWallpaper?) {
self.baseTheme = baseTheme self.baseTheme = baseTheme
self.accentColor = accentColor self.accentColor = accentColor
self.messageColors = messageColors self.messageColors = messageColors
self.animateMessageColors = animateMessageColors
self.wallpaper = wallpaper self.wallpaper = wallpaper
} }
@ -55,6 +60,7 @@ public final class TelegramThemeSettings: PostboxCoding, Equatable {
self.messageColors = [] self.messageColors = []
} }
} }
self.animateMessageColors = decoder.decodeInt32ForKey("animateMessageColors", orElse: 0) != 0
self.wallpaper = decoder.decodeObjectForKey("wallpaper", decoder: { TelegramWallpaper(decoder: $0) }) as? TelegramWallpaper self.wallpaper = decoder.decodeObjectForKey("wallpaper", decoder: { TelegramWallpaper(decoder: $0) }) as? TelegramWallpaper
} }
@ -62,6 +68,7 @@ public final class TelegramThemeSettings: PostboxCoding, Equatable {
encoder.encodeInt32(self.baseTheme.rawValue, forKey: "baseTheme") encoder.encodeInt32(self.baseTheme.rawValue, forKey: "baseTheme")
encoder.encodeInt32(Int32(bitPattern: self.accentColor), forKey: "accent") encoder.encodeInt32(Int32(bitPattern: self.accentColor), forKey: "accent")
encoder.encodeInt32Array(self.messageColors.map(Int32.init(bitPattern:)), forKey: "messageColors") encoder.encodeInt32Array(self.messageColors.map(Int32.init(bitPattern:)), forKey: "messageColors")
encoder.encodeInt32(self.animateMessageColors ? 1 : 0, forKey: "animateMessageColors")
if let wallpaper = self.wallpaper { if let wallpaper = self.wallpaper {
encoder.encodeObject(wallpaper, forKey: "wallpaper") encoder.encodeObject(wallpaper, forKey: "wallpaper")
} else { } else {

View File

@ -4,7 +4,7 @@ import TelegramCore
import TelegramUIPreferences import TelegramUIPreferences
public let defaultDarkPresentationTheme = makeDefaultDarkPresentationTheme(preview: false) public let defaultDarkPresentationTheme = makeDefaultDarkPresentationTheme(preview: false)
public let defaultDarkColorPresentationTheme = customizeDefaultDarkPresentationTheme(theme: defaultDarkPresentationTheme, editing: false, title: nil, accentColor: UIColor(rgb: 0x007aff), backgroundColors: [], bubbleColors: [], wallpaper: nil, baseColor: nil) public let defaultDarkColorPresentationTheme = customizeDefaultDarkPresentationTheme(theme: defaultDarkPresentationTheme, editing: false, title: nil, accentColor: UIColor(rgb: 0x007aff), backgroundColors: [], bubbleColors: [], animateBubbleColors: false, wallpaper: nil, baseColor: nil)
private extension PresentationThemeBaseColor { private extension PresentationThemeBaseColor {
var colorWallpaper: (BuiltinWallpaperData, Int32, [UInt32])? { var colorWallpaper: (BuiltinWallpaperData, Int32, [UInt32])? {
@ -37,7 +37,7 @@ private extension PresentationThemeBaseColor {
} }
} }
public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], wallpaper forcedWallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil) -> PresentationTheme { public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], animateBubbleColors: Bool?, wallpaper forcedWallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil) -> PresentationTheme {
if (theme.referenceTheme != .night) { if (theme.referenceTheme != .night) {
return theme return theme
} }
@ -165,6 +165,7 @@ public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, edit
chat = chat.withUpdated( chat = chat.withUpdated(
defaultWallpaper: defaultWallpaper, defaultWallpaper: defaultWallpaper,
animateMessageColors: animateBubbleColors,
message: chat.message.withUpdated( message: chat.message.withUpdated(
incoming: chat.message.incoming.withUpdated( incoming: chat.message.incoming.withUpdated(
linkTextColor: accentColor, linkTextColor: accentColor,
@ -523,6 +524,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
let chat = PresentationThemeChat( let chat = PresentationThemeChat(
defaultWallpaper: .color(0x000000), defaultWallpaper: .color(0x000000),
animateMessageColors: false,
message: message, message: message,
serviceMessage: serviceMessage, serviceMessage: serviceMessage,
inputPanel: inputPanel, inputPanel: inputPanel,

View File

@ -38,7 +38,7 @@ private extension PresentationThemeBaseColor {
} }
} }
public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], wallpaper forcedWallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil) -> PresentationTheme { public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], animateBubbleColors: Bool?, wallpaper forcedWallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil) -> PresentationTheme {
if (theme.referenceTheme != .nightAccent) { if (theme.referenceTheme != .nightAccent) {
return theme return theme
} }
@ -303,6 +303,7 @@ public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme
chat = chat.withUpdated( chat = chat.withUpdated(
defaultWallpaper: defaultWallpaper, defaultWallpaper: defaultWallpaper,
animateMessageColors: animateBubbleColors,
message: chat.message.withUpdated( message: chat.message.withUpdated(
incoming: chat.message.incoming.withUpdated( incoming: chat.message.incoming.withUpdated(
bubble: chat.message.incoming.bubble.withUpdated( bubble: chat.message.incoming.bubble.withUpdated(
@ -784,6 +785,7 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres
let chat = PresentationThemeChat( let chat = PresentationThemeChat(
defaultWallpaper: defaultBuiltinWallpaper(data: .default, colors: [0x1b2836, 0x121a22, 0x1b2836, 0x121a22]), defaultWallpaper: defaultBuiltinWallpaper(data: .default, colors: [0x1b2836, 0x121a22, 0x1b2836, 0x121a22]),
animateMessageColors: false,
message: message, message: message,
serviceMessage: serviceMessage, serviceMessage: serviceMessage,
inputPanel: inputPanel, inputPanel: inputPanel,

View File

@ -35,7 +35,7 @@ public let defaultServiceBackgroundColor = UIColor(rgb: 0x000000, alpha: 0.2)
public let defaultPresentationTheme = makeDefaultDayPresentationTheme(serviceBackgroundColor: defaultServiceBackgroundColor, day: false, preview: false) public let defaultPresentationTheme = makeDefaultDayPresentationTheme(serviceBackgroundColor: defaultServiceBackgroundColor, day: false, preview: false)
public let defaultDayAccentColor = UIColor(rgb: 0x007ee5) public let defaultDayAccentColor = UIColor(rgb: 0x007ee5)
public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], wallpaper forcedWallpaper: TelegramWallpaper? = nil, serviceBackgroundColor: UIColor?) -> PresentationTheme { public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], animateBubbleColors: Bool?, wallpaper forcedWallpaper: TelegramWallpaper? = nil, serviceBackgroundColor: UIColor?) -> PresentationTheme {
if (theme.referenceTheme != .day && theme.referenceTheme != .dayClassic) { if (theme.referenceTheme != .day && theme.referenceTheme != .dayClassic) {
return theme return theme
} }
@ -236,6 +236,7 @@ public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, ti
chat = chat.withUpdated( chat = chat.withUpdated(
defaultWallpaper: defaultWallpaper, defaultWallpaper: defaultWallpaper,
animateMessageColors: animateBubbleColors,
message: chat.message.withUpdated( message: chat.message.withUpdated(
incoming: chat.message.incoming.withUpdated( incoming: chat.message.incoming.withUpdated(
bubble: chat.message.incoming.bubble.withUpdated( bubble: chat.message.incoming.bubble.withUpdated(
@ -738,6 +739,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
let chat = PresentationThemeChat( let chat = PresentationThemeChat(
defaultWallpaper: day ? .color(0xffffff) : defaultPatternWallpaper, defaultWallpaper: day ? .color(0xffffff) : defaultPatternWallpaper,
animateMessageColors: false,
message: day ? messageDay : message, message: day ? messageDay : message,
serviceMessage: day ? serviceMessageDay : serviceMessage, serviceMessage: day ? serviceMessageDay : serviceMessage,
inputPanel: inputPanel, inputPanel: inputPanel,

View File

@ -19,46 +19,46 @@ public func makeDefaultPresentationTheme(reference: PresentationBuiltinThemeRefe
return theme return theme
} }
public func customizePresentationTheme(_ theme: PresentationTheme, editing: Bool, title: String? = nil, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], wallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil) -> PresentationTheme { public func customizePresentationTheme(_ theme: PresentationTheme, editing: Bool, title: String? = nil, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: [UInt32], animateBubbleColors: Bool?, wallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil) -> PresentationTheme {
if accentColor == nil && bubbleColors.isEmpty && backgroundColors.isEmpty && wallpaper == nil { if accentColor == nil && bubbleColors.isEmpty && backgroundColors.isEmpty && wallpaper == nil {
return theme return theme
} }
switch theme.referenceTheme { switch theme.referenceTheme {
case .day, .dayClassic: case .day, .dayClassic:
return customizeDefaultDayTheme(theme: theme, editing: editing, title: title, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper, serviceBackgroundColor: nil) return customizeDefaultDayTheme(theme: theme, editing: editing, title: title, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors ?? false, wallpaper: wallpaper, serviceBackgroundColor: nil)
case .night: case .night:
return customizeDefaultDarkPresentationTheme(theme: theme, editing: editing, title: title, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper, baseColor: baseColor) return customizeDefaultDarkPresentationTheme(theme: theme, editing: editing, title: title, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors ?? false, wallpaper: wallpaper, baseColor: baseColor)
case .nightAccent: case .nightAccent:
return customizeDefaultDarkTintedPresentationTheme(theme: theme, editing: editing, title: title, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper, baseColor: baseColor) return customizeDefaultDarkTintedPresentationTheme(theme: theme, editing: editing, title: title, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors ?? false, wallpaper: wallpaper, baseColor: baseColor)
} }
} }
public func makePresentationTheme(settings: TelegramThemeSettings, title: String? = nil, serviceBackgroundColor: UIColor? = nil) -> PresentationTheme? { public func makePresentationTheme(settings: TelegramThemeSettings, title: String? = nil, serviceBackgroundColor: UIColor? = nil) -> PresentationTheme? {
let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: settings.baseTheme), extendingThemeReference: nil, serviceBackgroundColor: serviceBackgroundColor, preview: false) let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: settings.baseTheme), extendingThemeReference: nil, serviceBackgroundColor: serviceBackgroundColor, preview: false)
return customizePresentationTheme(defaultTheme, editing: true, title: title, accentColor: UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: settings.messageColors, wallpaper: settings.wallpaper) return customizePresentationTheme(defaultTheme, editing: true, title: title, accentColor: UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: settings.messageColors, animateBubbleColors: settings.animateMessageColors, wallpaper: settings.wallpaper)
} }
public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, extendingThemeReference: PresentationThemeReference? = nil, accentColor: UIColor? = nil, backgroundColors: [UInt32] = [], bubbleColors: [UInt32] = [], wallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil, serviceBackgroundColor: UIColor? = nil, preview: Bool = false) -> PresentationTheme? { public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, extendingThemeReference: PresentationThemeReference? = nil, accentColor: UIColor? = nil, backgroundColors: [UInt32] = [], bubbleColors: [UInt32] = [], animateBubbleColors: Bool? = nil, wallpaper: TelegramWallpaper? = nil, baseColor: PresentationThemeBaseColor? = nil, serviceBackgroundColor: UIColor? = nil, preview: Bool = false) -> PresentationTheme? {
let theme: PresentationTheme let theme: PresentationTheme
switch themeReference { switch themeReference {
case let .builtin(reference): case let .builtin(reference):
let defaultTheme = makeDefaultPresentationTheme(reference: reference, extendingThemeReference: extendingThemeReference, serviceBackgroundColor: serviceBackgroundColor, preview: preview) let defaultTheme = makeDefaultPresentationTheme(reference: reference, extendingThemeReference: extendingThemeReference, serviceBackgroundColor: serviceBackgroundColor, preview: preview)
theme = customizePresentationTheme(defaultTheme, editing: true, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper, baseColor: baseColor) theme = customizePresentationTheme(defaultTheme, editing: true, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors, wallpaper: wallpaper, baseColor: baseColor)
case let .local(info): case let .local(info):
if let path = mediaBox.completedResourcePath(info.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, themeReference: themeReference, resolvedWallpaper: info.resolvedWallpaper) { if let path = mediaBox.completedResourcePath(info.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, themeReference: themeReference, resolvedWallpaper: info.resolvedWallpaper) {
theme = customizePresentationTheme(loadedTheme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper) theme = customizePresentationTheme(loadedTheme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors, wallpaper: wallpaper)
} else { } else {
return nil return nil
} }
case let .cloud(info): case let .cloud(info):
if let settings = info.theme.settings { if let settings = info.theme.settings {
if let loadedTheme = makePresentationTheme(mediaBox: mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), extendingThemeReference: themeReference, accentColor: accentColor ?? UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: bubbleColors.isEmpty ? settings.messageColors : bubbleColors, wallpaper: wallpaper ?? settings.wallpaper, serviceBackgroundColor: serviceBackgroundColor, preview: preview) { if let loadedTheme = makePresentationTheme(mediaBox: mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), extendingThemeReference: themeReference, accentColor: accentColor ?? UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: bubbleColors.isEmpty ? settings.messageColors : bubbleColors, animateBubbleColors: animateBubbleColors ?? settings.animateMessageColors, wallpaper: wallpaper ?? settings.wallpaper, serviceBackgroundColor: serviceBackgroundColor, preview: preview) {
theme = loadedTheme theme = loadedTheme
} else { } else {
return nil return nil
} }
} else if let file = info.theme.file, let path = mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, themeReference: themeReference, resolvedWallpaper: info.resolvedWallpaper) { } else if let file = info.theme.file, let path = mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let loadedTheme = makePresentationTheme(data: data, themeReference: themeReference, resolvedWallpaper: info.resolvedWallpaper) {
theme = customizePresentationTheme(loadedTheme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, wallpaper: wallpaper) theme = customizePresentationTheme(loadedTheme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: bubbleColors, animateBubbleColors: animateBubbleColors, wallpaper: wallpaper)
} else { } else {
return nil return nil
} }

View File

@ -1066,6 +1066,7 @@ public final class PresentationThemeChatHistoryNavigation {
public final class PresentationThemeChat { public final class PresentationThemeChat {
public let defaultWallpaper: TelegramWallpaper public let defaultWallpaper: TelegramWallpaper
public let animateMessageColors: Bool
public let message: PresentationThemeChatMessage public let message: PresentationThemeChatMessage
public let serviceMessage: PresentationThemeServiceMessage public let serviceMessage: PresentationThemeServiceMessage
public let inputPanel: PresentationThemeChatInputPanel public let inputPanel: PresentationThemeChatInputPanel
@ -1073,8 +1074,9 @@ public final class PresentationThemeChat {
public let inputButtonPanel: PresentationThemeInputButtonPanel public let inputButtonPanel: PresentationThemeInputButtonPanel
public let historyNavigation: PresentationThemeChatHistoryNavigation public let historyNavigation: PresentationThemeChatHistoryNavigation
public init(defaultWallpaper: TelegramWallpaper, message: PresentationThemeChatMessage, serviceMessage: PresentationThemeServiceMessage, inputPanel: PresentationThemeChatInputPanel, inputMediaPanel: PresentationThemeInputMediaPanel, inputButtonPanel: PresentationThemeInputButtonPanel, historyNavigation: PresentationThemeChatHistoryNavigation) { public init(defaultWallpaper: TelegramWallpaper, animateMessageColors: Bool, message: PresentationThemeChatMessage, serviceMessage: PresentationThemeServiceMessage, inputPanel: PresentationThemeChatInputPanel, inputMediaPanel: PresentationThemeInputMediaPanel, inputButtonPanel: PresentationThemeInputButtonPanel, historyNavigation: PresentationThemeChatHistoryNavigation) {
self.defaultWallpaper = defaultWallpaper self.defaultWallpaper = defaultWallpaper
self.animateMessageColors = animateMessageColors
self.message = message self.message = message
self.serviceMessage = serviceMessage self.serviceMessage = serviceMessage
self.inputPanel = inputPanel self.inputPanel = inputPanel
@ -1083,8 +1085,8 @@ public final class PresentationThemeChat {
self.historyNavigation = historyNavigation self.historyNavigation = historyNavigation
} }
public func withUpdated(defaultWallpaper: TelegramWallpaper? = nil, message: PresentationThemeChatMessage? = nil, serviceMessage: PresentationThemeServiceMessage? = nil, inputPanel: PresentationThemeChatInputPanel? = nil, inputMediaPanel: PresentationThemeInputMediaPanel? = nil, inputButtonPanel: PresentationThemeInputButtonPanel? = nil, historyNavigation: PresentationThemeChatHistoryNavigation? = nil) -> PresentationThemeChat { public func withUpdated(defaultWallpaper: TelegramWallpaper? = nil, animateMessageColors: Bool? = nil, message: PresentationThemeChatMessage? = nil, serviceMessage: PresentationThemeServiceMessage? = nil, inputPanel: PresentationThemeChatInputPanel? = nil, inputMediaPanel: PresentationThemeInputMediaPanel? = nil, inputButtonPanel: PresentationThemeInputButtonPanel? = nil, historyNavigation: PresentationThemeChatHistoryNavigation? = nil) -> PresentationThemeChat {
return PresentationThemeChat(defaultWallpaper: defaultWallpaper ?? self.defaultWallpaper, message: message ?? self.message, serviceMessage: serviceMessage ?? self.serviceMessage, inputPanel: inputPanel ?? self.inputPanel, inputMediaPanel: inputMediaPanel ?? self.inputMediaPanel, inputButtonPanel: inputButtonPanel ?? self.inputButtonPanel, historyNavigation: historyNavigation ?? self.historyNavigation) return PresentationThemeChat(defaultWallpaper: defaultWallpaper ?? self.defaultWallpaper, animateMessageColors: animateMessageColors ?? self.animateMessageColors, message: message ?? self.message, serviceMessage: serviceMessage ?? self.serviceMessage, inputPanel: inputPanel ?? self.inputPanel, inputMediaPanel: inputMediaPanel ?? self.inputMediaPanel, inputButtonPanel: inputButtonPanel ?? self.inputButtonPanel, historyNavigation: historyNavigation ?? self.historyNavigation)
} }
} }

View File

@ -87,52 +87,41 @@ extension TelegramWallpaper: Codable {
blur = true blur = true
} }
if components.count >= 2 && components.count <= 5 && [6, 8].contains(components[0].count) && !optionKeys.contains(components[0]) && [6, 8].contains(components[1].count) && !optionKeys.contains(components[1]), let topColor = UIColor(hexString: components[0]), let bottomColor = UIColor(hexString: components[1]) { var slug: String?
var rotation: Int32? var colors: [UIColor] = []
if components.count > 2, components[2].count <= 3, let value = Int32(components[2]) { var intensity: Int32?
if value >= 0 && value < 360 { var rotation: Int32?
rotation = value for i in 0 ..< components.count {
} let component = components[i]
if optionKeys.contains(component) {
continue
} }
self = .gradient(TelegramWallpaper.Gradient(id: nil, colors: [topColor.argb, bottomColor.argb], settings: WallpaperSettings(blur: blur, motion: motion, rotation: rotation))) if i == 0 && component.count > 8 {
} else { slug = component
var slug: String? } else if [6, 8].contains(component.count), let color = UIColor(hexString: component) {
var colors: [UInt32] = [] colors.append(color)
var intensity: Int32? } else if component.count <= 4, let value = Int32(component) {
var rotation: Int32? if intensity == nil {
if value >= -100 && value <= 100 {
if !components.isEmpty { intensity = value
slug = components[0] } else {
} intensity = 50
if components.count > 1 {
for i in 1 ..< components.count {
let component = components[i]
if optionKeys.contains(component) {
continue
} }
if [6, 8].contains(component.count), let value = UIColor(hexString: component) { } else if rotation == nil {
colors.append(value.rgb) if value >= 0 && value < 360 {
} else if component.count <= 3, let value = Int32(component) { rotation = value
if intensity == nil {
if value >= 0 && value <= 100 {
intensity = value
} else {
intensity = 50
}
} else if rotation == nil {
if value >= 0 && value < 360 {
rotation = value
}
}
} }
} }
} }
if let slug = slug { }
self = .file(TelegramWallpaper.File(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: !colors.isEmpty, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: WallpaperDataResource(slug: slug), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: blur, motion: motion, colors: colors, intensity: intensity, rotation: rotation)))
} else { if let slug = slug {
throw PresentationThemeDecodingError.generic self = .file(TelegramWallpaper.File(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: !colors.isEmpty, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: WallpaperDataResource(slug: slug), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: blur, motion: motion, colors: colors.map { $0.argb }, intensity: intensity, rotation: rotation)))
} } else if colors.count > 1 {
self = .gradient(TelegramWallpaper.Gradient(id: nil, colors: colors.map { $0.argb }, settings: WallpaperSettings(blur: blur, motion: motion, rotation: rotation)))
} else {
throw PresentationThemeDecodingError.generic
} }
} }
} }
@ -1659,6 +1648,7 @@ extension PresentationThemeChat: Codable {
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case defaultWallpaper case defaultWallpaper
case message case message
case animateMessageColors
case serviceMessage case serviceMessage
case inputPanel case inputPanel
case inputMediaPanel case inputMediaPanel
@ -1677,6 +1667,7 @@ extension PresentationThemeChat: Codable {
} }
self.init(defaultWallpaper: wallpaper, self.init(defaultWallpaper: wallpaper,
animateMessageColors: (try? values.decode(Bool.self, forKey: .animateMessageColors)) ?? false,
message: try values.decode(PresentationThemeChatMessage.self, forKey: .message), message: try values.decode(PresentationThemeChatMessage.self, forKey: .message),
serviceMessage: try values.decode(PresentationThemeServiceMessage.self, forKey: .serviceMessage), serviceMessage: try values.decode(PresentationThemeServiceMessage.self, forKey: .serviceMessage),
inputPanel: try values.decode(PresentationThemeChatInputPanel.self, forKey: .inputPanel), inputPanel: try values.decode(PresentationThemeChatInputPanel.self, forKey: .inputPanel),
@ -1688,6 +1679,7 @@ extension PresentationThemeChat: Codable {
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
var values = encoder.container(keyedBy: CodingKeys.self) var values = encoder.container(keyedBy: CodingKeys.self)
try values.encode(self.defaultWallpaper, forKey: .defaultWallpaper) try values.encode(self.defaultWallpaper, forKey: .defaultWallpaper)
try values.encode(self.animateMessageColors, forKey: .animateMessageColors)
try values.encode(self.message, forKey: .message) try values.encode(self.message, forKey: .message)
try values.encode(self.serviceMessage, forKey: .serviceMessage) try values.encode(self.serviceMessage, forKey: .serviceMessage)
try values.encode(self.inputPanel, forKey: .inputPanel) try values.encode(self.inputPanel, forKey: .inputPanel)

View File

@ -465,6 +465,23 @@ class PresentationThemeDecoding: Decoder {
} }
guard let topContainer = self.storage.topContainer as? [Any] else { guard let topContainer = self.storage.topContainer as? [Any] else {
if let topContainer = self.storage.topContainer as? [String : Any] {
let sortedKeys = topContainer.keys.sorted(by: { lhs, rhs in
if let lhsValue = Int(lhs), let rhsValue = Int(rhs), lhsValue < rhsValue {
return true
} else {
return false
}
})
var array: [Any] = []
for key in sortedKeys {
if let value = topContainer[key] {
array.append(value)
}
}
return PresentationThemeUnkeyedDecodingContainer(referencing: self, wrapping: array)
}
throw PresentationThemeDecodingError.typeMismatch throw PresentationThemeDecodingError.typeMismatch
} }

View File

@ -341,6 +341,8 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
stringValue = strings.Activity_RecordingVideoMessage stringValue = strings.Activity_RecordingVideoMessage
case .uploadingInstantVideo: case .uploadingInstantVideo:
stringValue = strings.Activity_UploadingVideoMessage stringValue = strings.Activity_UploadingVideoMessage
case .choosingSticker:
stringValue = strings.Activity_ChoosingSticker
case .speakingInGroupCall: case .speakingInGroupCall:
stringValue = "" stringValue = ""
} }
@ -372,6 +374,8 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
state = .playingGame(string, color) state = .playingGame(string, color)
case .speakingInGroupCall: case .speakingInGroupCall:
state = .typingText(string, color) state = .typingText(string, color)
case .choosingSticker:
state = .choosingSticker(string, color)
} }
} else { } else {
if let titleContent = self.titleContent { if let titleContent = self.titleContent {

View File

@ -773,9 +773,9 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
self.bubbleTheme = bubbleTheme self.bubbleTheme = bubbleTheme
self.bubbleCorners = bubbleCorners self.bubbleCorners = bubbleCorners
if bubbleTheme.chat.message.outgoing.bubble.withoutWallpaper.fill.count >= 3 && self.context.sharedContext.immediateExperimentalUISettings.enableDebugDataDisplay { if bubbleTheme.chat.message.outgoing.bubble.withoutWallpaper.fill.count >= 3 && bubbleTheme.chat.animateMessageColors {
if self.outgoingBubbleGradientBackgroundNode == nil { if self.outgoingBubbleGradientBackgroundNode == nil {
let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode() let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode(adjustSaturation: false)
if let size = self.validLayout { if let size = self.validLayout {
outgoingBubbleGradientBackgroundNode.frame = CGRect(origin: CGPoint(), size: size) outgoingBubbleGradientBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate) outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate)