Custom accent color picker

This commit is contained in:
Ilya Laktyushin 2019-08-13 23:28:19 +03:00
parent cef878bcd0
commit b84aabffa6
12 changed files with 3869 additions and 3610 deletions

View file

@ -4620,3 +4620,36 @@ Any member of this group will be able to see messages in the channel.";
"Conversation.SelectedMessages_0" = "%@ Messages Selected";
"AccentColor.Title" = "Accent Color";
"Appearance.ThemePreview.ChatList.1.Name" = "Eva Summer";
"Appearance.ThemePreview.ChatList.1.Text" = "Text";
"Appearance.ThemePreview.ChatList.2.Name" = "Your inner Competition";
"Appearance.ThemePreview.ChatList.2.Text" = "Hey";
"Appearance.ThemePreview.ChatList.3.Name" = "Mike Apple";
"Appearance.ThemePreview.ChatList.3.Text" = "Mike Apple";
"Appearance.ThemePreview.ChatList.4.Name" = "Paul Newman";
"Appearance.ThemePreview.ChatList.4.Text" = "Any ideas?";
"Appearance.ThemePreview.ChatList.5.Name" = "Old Pirates";
"Appearance.ThemePreview.ChatList.5.Text" = "Yo-ho-ho";
"Appearance.ThemePreview.ChatList.6.Name" = "Kate Bright";
"Appearance.ThemePreview.ChatList.6.Text" = "Hola!";
"Appearance.ThemePreview.ChatList.7.Name" = "What";
"Appearance.ThemePreview.ChatList.7.Text" = "Hola!";
"Appearance.ThemePreview.ChatList.8.Name" = "What";
"Appearance.ThemePreview.ChatList.8.Text" = "Hola!";
"Appearance.ThemePreview.Chat.1.Text" = "Reminds me of a Chinese proverb: the best time to plant a tree was 20 years ago. The second best time is now.";
"Appearance.ThemePreview.Chat.1.ReplyName" = "Alex Cassio";
"Appearance.ThemePreview.Chat.1.ReplyText" = "Mark Twain said that ☝️";
"Appearance.ThemePreview.Chat.2.Text" = "Mark Twain said that ☝️";
"Appearance.ThemePreview.Chat.3.Text" = "Twenty years from now you will be more disappointed by the things that you didn't do than by the ones you did do, so throw off the bowlines, sail away from safe harboor, catch the trade winds in your sails.";
"Appearance.ThemePreview.Chat.4.Text" = "Nearly missed the sunrise.";

View file

@ -8,6 +8,24 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
let constructiveColor: UIColor = UIColor(rgb: 0x00c900)
let secretColor: UIColor = UIColor(rgb: 0x00b12c)
let accentColorHSV = accentColor.hsv
let outgoingPrimaryTextColor: UIColor
let outgoingSecondaryTextColor: UIColor
let outgoingLinkTextColor: UIColor
let outgoingCheckColor: UIColor
if accentColorHSV.2 > 0.83 {
outgoingPrimaryTextColor = .black
outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.6)
outgoingLinkTextColor = .black
outgoingCheckColor = .black
} else {
outgoingPrimaryTextColor = .white
outgoingSecondaryTextColor = UIColor(rgb: 0xffffff, alpha: 0.6)
outgoingLinkTextColor = .white
outgoingCheckColor = .white
}
let rootTabBar = PresentationThemeRootTabBar(
backgroundColor: UIColor(rgb: 0xf7f7f7),
separatorColor: UIColor(rgb: 0xa3a3a3),
@ -175,11 +193,11 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
let messageDay = PresentationThemeChatMessage(
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: UIColor(rgb: 0x0b8bed), fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor.withMultipliedBrightnessBy(1.2)), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor.withMultipliedBrightnessBy(1.2))),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: accentColor, highlightedFill: accentColor.withMultipliedBrightnessBy(0.7), stroke: accentColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: accentColor, highlightedFill: accentColor.withMultipliedBrightnessBy(0.7), stroke: accentColor)), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.6), linkTextColor: .white, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: .white, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.6), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.7), fileTitleColor: UIColor(rgb: 0xffffff), fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.7), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.7), mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xffffff, alpha: 0.6), radioProgress: .white, highlight: UIColor.white.withAlphaComponent(0.12), separator: UIColor(rgb: 0xffffff, alpha: 0.6), bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor.withMultipliedBrightnessBy(1.2)), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor.withMultipliedBrightnessBy(1.2))),
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xDADADE), stroke: UIColor(rgb: 0xE5E5EA)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xDADADE), stroke: UIColor(rgb: 0xE5E5EA))),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: accentColor, highlightedFill: accentColor.withMultipliedBrightnessBy(0.7), stroke: accentColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: accentColor, highlightedFill: accentColor.withMultipliedBrightnessBy(0.7), stroke: accentColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: .white, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.6), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.7), fileTitleColor: UIColor(rgb: 0xffffff), fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.7), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.7), mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xffffff, alpha: 0.6), radioProgress: .white, highlight: UIColor.white.withAlphaComponent(0.12), separator: UIColor(rgb: 0xffffff, alpha: 0.6), bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor.withMultipliedBrightnessBy(1.2)), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor.withMultipliedBrightnessBy(1.2))),
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xDADADE), stroke: UIColor(rgb: 0xE5E5EA)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xE5E5EA))),
infoPrimaryTextColor: UIColor(rgb: 0x000000),
infoLinkTextColor: UIColor(rgb: 0x004bad),
outgoingCheckColor: UIColor.white,
outgoingCheckColor: outgoingCheckColor,
mediaDateAndStatusFillColor: UIColor(rgb: 0x000000, alpha: 0.5),
mediaDateAndStatusTextColor: .white,
shareButtonFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)),

View file

@ -212,7 +212,7 @@ public final class PrincipalThemeEssentialGraphics {
let outgoingKnockout = self.outgoingBubbleGradientImage != nil
let emptyImage = UIImage()
if preview {
if false && preview {
self.chatMessageBackgroundIncomingMaskImage = messageBubbleImage(incoming: true, fillColor: UIColor.black, strokeColor: UIColor.clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true)
self.chatMessageBackgroundIncomingImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none, theme: theme, wallpaper: wallpaper, knockout: incomingKnockout)
self.chatMessageBackgroundOutgoingMaskImage = messageBubbleImage(incoming: false, fillColor: .black, strokeColor: .clear, neighbors: .none, theme: theme, wallpaper: .color(0xffffff), knockout: true, mask: true)

View file

@ -9,9 +9,13 @@ import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
private let colors: [Int32] = [0x007aff, 0x00c2ed, 0x29b327, 0xeb6ca4, 0xf08200, 0x9472ee, 0xd33213, 0xedb400, 0x6d839e]
final class ThemeAccentColorController: ViewController {
private let context: AccountContext
private let currentTheme: PresentationThemeReference
private let initialColor: UIColor
private let initialTheme: PresentationTheme
private var controllerNode: ThemeAccentColorControllerNode {
return self.displayNode as! ThemeAccentColorControllerNode
@ -20,38 +24,37 @@ final class ThemeAccentColorController: ViewController {
private var didPlayPresentationAnimation = false
private var presentationData: PresentationData
private var presentationDataDisposable: Disposable?
init(context: AccountContext, currentTheme: PresentationThemeReference) {
init(context: AccountContext, currentTheme: PresentationThemeReference, currentColor: UIColor?) {
self.context = context
self.currentTheme = currentTheme
var color: UIColor
if let currentColor = currentColor {
color = currentColor
}
else if let randomColor = colors.randomElement() {
color = UIColor(rgb: UInt32(bitPattern: randomColor))
} else {
color = defaultDayAccentColor
}
self.initialColor = color
self.initialTheme = makePresentationTheme(themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true)
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings))
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.initialTheme, presentationStrings: self.presentationData.strings))
self.title = self.presentationData.strings.AccentColor_Title
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.presentationDataDisposable = (context.sharedContext.presentationData
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self {
strongSelf.presentationData = presentationData
strongSelf.updateStrings()
}
})
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.presentationDataDisposable?.dispose()
}
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
@ -63,6 +66,46 @@ final class ThemeAccentColorController: ViewController {
}
}
override func loadDisplayNode() {
super.loadDisplayNode()
self.displayNode = ThemeAccentColorControllerNode(context: self.context, currentTheme: self.currentTheme, color: self.initialColor, theme: self.initialTheme, dismiss: { [weak self] in
if let strongSelf = self {
strongSelf.dismiss()
}
}, apply: { [weak self] in
if let strongSelf = self {
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
var themeSpecificAccentColors = current.themeSpecificAccentColors
let color = PresentationThemeAccentColor(baseColor: .custom, value: Int32(bitPattern: strongSelf.controllerNode.color))
themeSpecificAccentColors[current.theme.index] = color
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
let theme = makePresentationTheme(themeReference: strongSelf.currentTheme, accentColor: UIColor(rgb: strongSelf.controllerNode.color), serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: color.baseColor)
var chatWallpaper = current.chatWallpaper
if let wallpaper = current.themeSpecificChatWallpapers[current.theme.index], wallpaper.hasWallpaper {
} else {
chatWallpaper = theme.chat.defaultWallpaper
themeSpecificChatWallpapers[current.theme.index] = chatWallpaper
}
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: strongSelf.currentTheme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
}) |> deliverOnMainQueue).start(completed: { [weak self] in
if let strongSelf = self {
strongSelf.dismiss()
}
})
}
})
self.controllerNode.themeUpdated = { [weak self] theme in
if let strongSelf = self {
strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: theme, presentationStrings: strongSelf.presentationData.strings))
}
}
self.displayNodeDidLoad()
}
private func updateStrings() {
}

View file

@ -11,6 +11,7 @@ import AccountContext
final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate {
private let context: AccountContext
private var theme: PresentationTheme
private let currentTheme: PresentationThemeReference
private var presentationData: PresentationData
@ -27,27 +28,37 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
private var colorPanelNode: WallpaperColorPanelNode
private let toolbarNode: WallpaperGalleryToolbarNode
private var validLayout: (ContainerViewLayout, CGFloat)?
private var validLayout: (ContainerViewLayout, CGFloat, CGFloat)?
private var serviceColorDisposable: Disposable?
private var colorDisposable: Disposable?
private let colorValue = ValuePromise<UIColor>(ignoreRepeated: true)
init(context: AccountContext, currentTheme: PresentationThemeReference, dismiss: @escaping () -> Void, apply: @escaping () -> Void) {
var themeUpdated: ((PresentationTheme) -> Void)?
var color: UInt32 {
return self.colorPanelNode.color.rgb
}
init(context: AccountContext, currentTheme: PresentationThemeReference, color: UIColor, theme: PresentationTheme, dismiss: @escaping () -> Void, apply: @escaping () -> Void) {
self.context = context
self.currentTheme = currentTheme
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.theme = theme
self.colorValue.set(color)
self.scrollNode = ASScrollNode()
self.pageControlBackgroundNode = ASDisplayNode()
self.pageControlBackgroundNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.3)
self.pageControlBackgroundNode.cornerRadius = 6.0
self.pageControlNode = PageControlNode(dotColor: self.presentationData.theme.chatList.unreadBadgeActiveBackgroundColor, inactiveDotColor: self.presentationData.theme.list.pageIndicatorInactiveColor)
self.pageControlNode = PageControlNode(dotColor: self.theme.chatList.unreadBadgeActiveBackgroundColor, inactiveDotColor: self.presentationData.theme.list.pageIndicatorInactiveColor)
self.chatListBackgroundNode = ASDisplayNode()
self.chatBackgroundNode = ASDisplayNode()
self.colorPanelNode = WallpaperColorPanelNode(theme: presentationData.theme, strings: presentationData.strings)
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.presentationData.theme, strings: self.presentationData.strings)
self.colorPanelNode = WallpaperColorPanelNode(theme: self.theme, strings: self.presentationData.strings)
self.colorPanelNode.color = color
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.theme, strings: self.presentationData.strings)
super.init()
@ -77,12 +88,9 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.colorPanelNode.colorChanged = { [weak self] color, ended in
if let strongSelf = self {
//strongSelf.updateEntries(color: color, preview: !ended)
strongSelf.colorValue.set(color)
}
}
//if case let .customColor(colorValue) = self.source, let color = colorValue {
// colorPanelNode.color = UIColor(rgb: UInt32(bitPattern: color))
//}
self.toolbarNode.cancel = {
dismiss()
@ -91,7 +99,34 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
apply()
}
self.colorDisposable = (chatServiceBackgroundColor(wallpaper: self.presentationData.theme.chat.defaultWallpaper, mediaBox: context.account.postbox.mediaBox)
self.colorDisposable = (self.colorValue.get()
|> map { color -> PresentationTheme in
return makePresentationTheme(themeReference: currentTheme, accentColor: color, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: nil, preview: true)
}
|> deliverOnMainQueue).start(next: { [weak self] theme in
if let strongSelf = self {
strongSelf.theme = theme
strongSelf.themeUpdated?(theme)
strongSelf.colorPanelNode.updateTheme(theme)
strongSelf.toolbarNode.updateThemeAndStrings(theme: theme, strings: strongSelf.presentationData.strings)
if case let .color(value) = theme.chat.defaultWallpaper {
strongSelf.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
strongSelf.chatListBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
strongSelf.chatBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
}
if let (layout, navigationBarHeight, messagesBottomInset) = strongSelf.validLayout {
strongSelf.pageControlNode.dotColor = theme.chatList.unreadBadgeActiveBackgroundColor
strongSelf.pageControlNode.inactiveDotColor = theme.list.pageIndicatorInactiveColor
strongSelf.updateChatsLayout(layout: layout, topInset: navigationBarHeight, transition: .immediate)
strongSelf.updateMessagesLayout(layout: layout, bottomInset: messagesBottomInset, transition: .immediate)
}
}
})
self.serviceColorDisposable = (chatServiceBackgroundColor(wallpaper: self.presentationData.theme.chat.defaultWallpaper, mediaBox: context.account.postbox.mediaBox)
|> deliverOnMainQueue).start(next: { [weak self] color in
if let strongSelf = self {
if strongSelf.presentationData.theme.chat.defaultWallpaper.hasWallpaper {
@ -105,6 +140,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
deinit {
self.colorDisposable?.dispose()
self.serviceColorDisposable?.dispose()
}
override func didLoad() {
@ -137,18 +173,21 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {})
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
let chatListPresentationData = ChatListPresentationData(theme: self.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
let peers = SimpleDictionary<PeerId, Peer>()
let messages = SimpleDictionary<MessageId, Message>()
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 1)
let peer1 = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 1), accessHash: nil, firstName: "Alicia", lastName: "Torreaux", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer2 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 2), accessHash: nil, firstName: "Roberto", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer3 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: 3), accessHash: nil, firstName: "Veronica", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer2 = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let timestamp = Int32(Date(timeInterval: -600, since: Date()).timeIntervalSince1970)
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 1), namespace: 0, id: 0), timestamp: 66003)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 1), namespace: 0, id: 0), timestamp: timestamp)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer1.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Bob says hi.", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 2), namespace: 0, id: 0), timestamp: 66000)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 2), namespace: 0, id: 0), timestamp: timestamp - 60)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer2.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 60, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Say hello to Alice 👋", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 2), namespace: 0, id: 0), timestamp: timestamp - 180)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer3.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp - 180, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Table for four, 2 PM. Be there.", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer3), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
if let chatNodes = self.chatNodes {
@ -198,27 +237,27 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let otherPeerId = self.context.account.peerId
var peers = SimpleDictionary<PeerId, Peer>()
var messages = SimpleDictionary<MessageId, Message>()
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_1_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_1_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3)
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_ReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let controllerInteraction = ChatControllerInteraction.default
let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: self.presentationData.theme, wallpaper: self.presentationData.theme.chat.defaultWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: false, largeEmoji: false)
let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: self.theme, wallpaper: self.theme.chat.defaultWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: false, largeEmoji: false)
items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: "", attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false))
items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false))
items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false))
items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false))
items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false))
items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false))
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 14, title: nil, performer: nil, waveform: MemoryBuffer())]
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: nil, attributes: voiceAttributes)
items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.playing), fetchStatus: .Local)), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false))
items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false))
items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
if let messageNodes = self.messageNodes {
@ -268,19 +307,32 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.scrollNode.frame = bounds
let toolbarHeight = 49.0 + layout.intrinsicInsets.bottom
self.chatListBackgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
self.chatBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height)
self.chatListBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height)
self.chatBackgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
self.scrollNode.view.contentSize = CGSize(width: bounds.width * 2.0, height: bounds.height)
transition.updateFrame(node: self.toolbarNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: 49.0 + layout.intrinsicInsets.bottom)))
self.toolbarNode.updateLayout(size: CGSize(width: layout.size.width, height: 49.0), layout: layout, transition: transition)
var bottomInset = toolbarHeight
let metrics = DeviceMetrics.forScreenSize(layout.size)
let standardInputHeight = metrics?.standardInputHeight(inLandscape: false) ?? 216.0
let height = standardInputHeight - bottomInset + 47.0
let colorPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomInset - height), size: CGSize(width: layout.size.width, height: height))
bottomInset += height
transition.updateFrame(node: self.colorPanelNode, frame: colorPanelFrame)
self.colorPanelNode.updateLayout(size: colorPanelFrame.size, keyboardHeight: layout.inputHeight ?? 0.0, transition: transition)
let messagesBottomInset = bottomInset + 36.0
self.updateChatsLayout(layout: layout, topInset: navigationBarHeight, transition: transition)
self.updateMessagesLayout(layout: layout, bottomInset: toolbarHeight + 66.0, transition: transition)
self.updateMessagesLayout(layout: layout, bottomInset: messagesBottomInset, transition: transition)
self.validLayout = (layout, navigationBarHeight, messagesBottomInset)
let pageControlSize = self.pageControlNode.measure(CGSize(width: bounds.width, height: 100.0))
let pageControlFrame = CGRect(origin: CGPoint(x: floor((bounds.width - pageControlSize.width) / 2.0), y: layout.size.height - toolbarHeight - 42.0), size: pageControlSize)
let pageControlFrame = CGRect(origin: CGPoint(x: floor((bounds.width - pageControlSize.width) / 2.0), y: layout.size.height - bottomInset - 24.0), size: pageControlSize)
self.pageControlNode.frame = pageControlFrame
self.pageControlBackgroundNode.frame = CGRect(x: pageControlFrame.minX - 11.0, y: pageControlFrame.minY - 12.0, width: pageControlFrame.width + 22.0, height: 30.0)
}

View file

@ -131,13 +131,16 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
let peers = SimpleDictionary<PeerId, Peer>()
let messages = SimpleDictionary<MessageId, Message>()
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 1)
let peer1 = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer1 = TelegramUser(id: peerId, accessHash: nil, firstName: "Alicia", lastName: "Torreaux", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer2 = TelegramUser(id: peerId, accessHash: nil, firstName: "Roberto", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let peer2 = TelegramUser(id: peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
let timestamp = Int32(Date(timeInterval: -600, since: Date()).timeIntervalSince1970)
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 1), namespace: 0, id: 0), timestamp: 66003)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 1), namespace: 0, id: 0), timestamp: 66003)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Bob says hi.", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer1), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 2), namespace: 0, id: 0), timestamp: 66000)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 2), namespace: 0, id: 0), timestamp: 66000)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Say hello to Alice 👋", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(namespace: 0, id: 2), namespace: 0, id: 0), timestamp: 66000)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer1, text: "Say hello to Alice 👋", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer2), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
if let chatNodes = self.chatNodes {

View file

@ -11,11 +11,13 @@ import ItemListUI
private func generateSwatchImage(color: PresentationThemeAccentColor, selected: Bool) -> UIImage? {
return generateImage(CGSize(width: 40.0, height: 40.0), rotatedContext: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
let fillColor = color.color
let strokeColor = color.baseColor.color
var strokeColor = color.baseColor.color
if strokeColor == .clear {
strokeColor = fillColor
}
context.setFillColor(fillColor.cgColor)
context.setStrokeColor(strokeColor.cgColor)
@ -30,6 +32,36 @@ private func generateSwatchImage(color: PresentationThemeAccentColor, selected:
})?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 15)
}
private func generateCustomSwatchImage() -> UIImage? {
return generateImage(CGSize(width: 42.0, height: 42.0), rotatedContext: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
let dotSize = CGSize(width: 10.0, height: 10.0)
context.setFillColor(UIColor(rgb: 0xd33213).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 14.0, y: 16.0), size: dotSize))
context.setFillColor(UIColor(rgb: 0xf08200).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 14.0, y: 0.0), size: dotSize))
context.setFillColor(UIColor(rgb: 0xedb400).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 28.0, y: 8.0), size: dotSize))
context.setFillColor(UIColor(rgb: 0x70bb23).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 28.0, y: 24.0), size: dotSize))
context.setFillColor(UIColor(rgb: 0x5396fa).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 14.0, y: 32.0), size: dotSize))
context.setFillColor(UIColor(rgb: 0x9472ee).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 24.0), size: dotSize))
context.setFillColor(UIColor(rgb: 0xeb6ca4).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 8.0), size: dotSize))
})
}
class ThemeSettingsAccentColorItem: ListViewItem, ItemListItem {
var sectionId: ItemListSectionId
@ -37,13 +69,15 @@ class ThemeSettingsAccentColorItem: ListViewItem, ItemListItem {
let colors: [PresentationThemeBaseColor]
let currentColor: PresentationThemeAccentColor
let updated: (PresentationThemeAccentColor) -> Void
let openColorPicker: () -> Void
let tag: ItemListItemTag?
init(theme: PresentationTheme, sectionId: ItemListSectionId, colors: [PresentationThemeBaseColor], currentColor: PresentationThemeAccentColor, updated: @escaping (PresentationThemeAccentColor) -> Void, tag: ItemListItemTag? = nil) {
init(theme: PresentationTheme, sectionId: ItemListSectionId, colors: [PresentationThemeBaseColor], currentColor: PresentationThemeAccentColor, updated: @escaping (PresentationThemeAccentColor) -> Void, openColorPicker: @escaping () -> Void, tag: ItemListItemTag? = nil) {
self.theme = theme
self.colors = colors
self.currentColor = currentColor
self.updated = updated
self.openColorPicker = openColorPicker
self.tag = tag
self.sectionId = sectionId
}
@ -118,7 +152,7 @@ private final class ThemeSettingsAccentColorNode : ASDisplayNode {
override func layout() {
super.layout()
self.iconNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 40.0, height: 40.0))
self.iconNode.frame = self.bounds
}
}
@ -132,7 +166,8 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
private let bottomStripeNode: ASDisplayNode
private let scrollNode: ASScrollNode
private var nodes: [ThemeSettingsAccentColorNode] = []
private var colorNodes: [ThemeSettingsAccentColorNode] = []
private let customNode: HighlightableButtonNode
private var item: ThemeSettingsAccentColorItem?
private var layoutParams: ListViewItemLayoutParams?
@ -153,9 +188,15 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
self.scrollNode = ASScrollNode()
self.customNode = HighlightableButtonNode()
super.init(layerBacked: false, dynamicBounce: false)
self.customNode.setImage(generateCustomSwatchImage(), for: .normal)
self.customNode.addTarget(self, action: #selector(customPressed), forControlEvents: .touchUpInside)
self.addSubnode(self.scrollNode)
self.scrollNode.addSubnode(self.customNode)
}
override func didLoad() {
@ -164,6 +205,10 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
self.scrollNode.view.showsHorizontalScrollIndicator = false
}
@objc func customPressed() {
self.item?.openColorPicker()
}
private func scrollToNode(_ node: ThemeSettingsAccentColorNode, animated: Bool) {
let bounds = self.scrollNode.view.bounds
let frame = node.frame.insetBy(dx: -48.0, dy: 0.0)
@ -243,11 +288,11 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
var i = 0
for color in item.colors {
let imageNode: ThemeSettingsAccentColorNode
if strongSelf.nodes.count > i {
imageNode = strongSelf.nodes[i]
if strongSelf.colorNodes.count > i {
imageNode = strongSelf.colorNodes[i]
} else {
imageNode = ThemeSettingsAccentColorNode()
strongSelf.nodes.append(imageNode)
strongSelf.colorNodes.append(imageNode)
strongSelf.scrollNode.addSubnode(imageNode)
updated = true
}
@ -258,10 +303,10 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
accentColor = item.currentColor
selectedNode = imageNode
} else {
accentColor = PresentationThemeAccentColor(baseColor: color, value: 0.5)
accentColor = PresentationThemeAccentColor(baseColor: color)
}
imageNode.setup(color: accentColor, selected: selected, action: { [weak self, weak imageNode, weak selectedNode] in
imageNode.setup(color: accentColor, selected: selected, action: { [weak self, weak imageNode] in
item.updated(accentColor)
if let imageNode = imageNode {
self?.scrollToNode(imageNode, animated: true)
@ -274,17 +319,17 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
i += 1
}
for k in (i ..< strongSelf.nodes.count).reversed() {
let node = strongSelf.nodes[k]
strongSelf.nodes.remove(at: k)
strongSelf.customNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 9.0), size: CGSize(width: 42.0, height: 42.0))
for k in (i ..< strongSelf.colorNodes.count).reversed() {
let node = strongSelf.colorNodes[k]
strongSelf.colorNodes.remove(at: k)
node.removeFromSupernode()
}
if let lastNode = strongSelf.nodes.last {
let contentSize = CGSize(width: lastNode.frame.maxX + nodeInset, height: strongSelf.scrollNode.frame.height)
if strongSelf.scrollNode.view.contentSize != contentSize {
strongSelf.scrollNode.view.contentSize = contentSize
}
let contentSize = CGSize(width: strongSelf.customNode.frame.maxX + nodeInset, height: strongSelf.scrollNode.frame.height)
if strongSelf.scrollNode.view.contentSize != contentSize {
strongSelf.scrollNode.view.contentSize = contentSize
}
if updated, let selectedNode = selectedNode {

View file

@ -15,17 +15,19 @@ private final class ThemeSettingsControllerArguments {
let selectFontSize: (PresentationFontSize) -> Void
let openWallpaperSettings: () -> Void
let selectAccentColor: (PresentationThemeAccentColor) -> Void
let openAccentColorPicker: (PresentationThemeReference, PresentationThemeAccentColor?) -> Void
let openAutoNightTheme: () -> Void
let toggleLargeEmoji: (Bool) -> Void
let disableAnimations: (Bool) -> Void
let selectAppIcon: (String) -> Void
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void) {
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void) {
self.context = context
self.selectTheme = selectTheme
self.selectFontSize = selectFontSize
self.openWallpaperSettings = openWallpaperSettings
self.selectAccentColor = selectAccentColor
self.openAccentColorPicker = openAccentColorPicker
self.openAutoNightTheme = openAutoNightTheme
self.toggleLargeEmoji = toggleLargeEmoji
self.disableAnimations = disableAnimations
@ -231,7 +233,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
arguments.openWallpaperSettings()
})
case let .accentColor(theme, currentTheme, _, color):
var defaultColor = PresentationThemeAccentColor(baseColor: .blue, value: 0.5)
var defaultColor = PresentationThemeAccentColor(baseColor: .blue)
var colors = PresentationThemeBaseColor.allCases
if case let .builtin(name) = currentTheme {
if name == .night || name == .nightAccent {
@ -239,13 +241,19 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
}
if name == .night {
colors = colors.filter { $0 != .gray }
defaultColor = PresentationThemeAccentColor(baseColor: .white, value: 0.5)
defaultColor = PresentationThemeAccentColor(baseColor: .white)
} else {
colors = colors.filter { $0 != .white }
}
}
return ThemeSettingsAccentColorItem(theme: theme, sectionId: self.section, colors: colors, currentColor: color ?? defaultColor, updated: { color in
let currentColor = color ?? defaultColor
if currentColor.baseColor != .custom {
colors = colors.filter { $0 != .custom }
}
return ThemeSettingsAccentColorItem(theme: theme, sectionId: self.section, colors: colors, currentColor: currentColor, updated: { color in
arguments.selectAccentColor(color)
}, openColorPicker: {
arguments.openAccentColorPicker(currentTheme, currentColor)
}, tag: ThemeSettingsEntryTag.accentColor)
case let .autoNightTheme(theme, text, value):
return ItemListDisclosureItem(theme: theme, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
@ -339,6 +347,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
}
var pushControllerImpl: ((ViewController) -> Void)?
var presentControllerImpl: ((ViewController, Any?) -> Void)?
let _ = telegramWallpapers(postbox: context.account.postbox, network: context.account.network).start()
@ -399,6 +408,9 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
}).start()
}, openAccentColorPicker: { themeReference, currentColor in
let controller = ThemeAccentColorController(context: context, currentTheme: themeReference, currentColor: currentColor?.color)
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}, openAutoNightTheme: {
pushControllerImpl?(themeAutoNightSettingsController(context: context))
}, toggleLargeEmoji: { largeEmoji in
@ -445,6 +457,9 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
pushControllerImpl = { [weak controller] c in
(controller?.navigationController as? NavigationController)?.pushViewController(c)
}
presentControllerImpl = { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a)
}
return controller
}

View file

@ -32,7 +32,7 @@ private func textInputBackgroundImage(backgroundColor: UIColor, fieldColor: UICo
}
final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
private let theme: PresentationTheme
private var theme: PresentationTheme
private let backgroundNode: ASDisplayNode
private let topSeparatorNode: ASDisplayNode
@ -68,7 +68,7 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
self.bottomSeparatorNode.backgroundColor = theme.chat.inputPanel.panelSeparatorColor
self.textBackgroundNode = ASImageNode()
self.textBackgroundNode.image = textInputBackgroundImage(backgroundColor: theme.chat.inputPanel.panelBackgroundColor, fieldColor: theme.chat.inputPanel.inputBackgroundColor, strokeColor: theme.chat.inputPanel.inputStrokeColor, diameter: 33.0)
self.textBackgroundNode.image = textInputBackgroundImage(backgroundColor: theme.chat.inputPanel.panelBackgroundColor, fieldColor: theme.chat.inputPanel.inputBackgroundColor, strokeColor: theme.chat.inputPanel.inputStrokeColor, diameter: 33.0)
self.textFieldNode = TextFieldNode()
self.prefixNode = ASTextNode()
@ -116,6 +116,17 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
self.textFieldNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0)
}
func updateTheme(_ theme: PresentationTheme) {
self.theme = theme
self.backgroundNode.backgroundColor = self.theme.chat.inputPanel.panelBackgroundColor
self.topSeparatorNode.backgroundColor = self.theme.chat.inputPanel.panelSeparatorColor
self.bottomSeparatorNode.backgroundColor = self.theme.chat.inputPanel.panelSeparatorColor
self.textBackgroundNode.image = textInputBackgroundImage(backgroundColor: self.theme.chat.inputPanel.panelBackgroundColor, fieldColor: self.theme.chat.inputPanel.inputBackgroundColor, strokeColor: self.theme.chat.inputPanel.inputStrokeColor, diameter: 33.0)
self.textFieldNode.textField.textColor = self.theme.chat.inputPanel.inputTextColor
self.textFieldNode.textField.keyboardAppearance = self.theme.chat.inputPanel.keyboardColor.keyboardAppearance
}
private func setColor(_ color: UIColor, updatePicker: Bool = true, ended: Bool = true) {
self.textFieldNode.textField.text = color.hexString.uppercased()
if updatePicker {

View file

@ -177,6 +177,7 @@ public enum PresentationThemeBaseColor: Int32, CaseIterable {
case gray
case black
case white
case custom
public var color: UIColor {
let value: UInt32
@ -203,6 +204,8 @@ public enum PresentationThemeBaseColor: Int32, CaseIterable {
value = 0x000000
case .white:
value = 0xffffff
case .custom:
return .clear
}
return UIColor(rgb: value)
}
@ -231,27 +234,41 @@ public enum PresentationThemeBaseColor: Int32, CaseIterable {
return (UIColor(rgb: 0x000000), UIColor(rgb: 0x000000))
case .white:
return (UIColor(rgb: 0xffffff), UIColor(rgb: 0xffffff))
case .custom:
return (UIColor(rgb: 0x000000), UIColor(rgb: 0x000000))
}
}
}
public struct PresentationThemeAccentColor: PostboxCoding, Equatable {
public var baseColor: PresentationThemeBaseColor
public var value: Int32?
public init(baseColor: PresentationThemeBaseColor, value: CGFloat) {
public init(baseColor: PresentationThemeBaseColor, value: Int32? = nil) {
self.baseColor = baseColor
self.value = value
}
public init(decoder: PostboxDecoder) {
self.baseColor = PresentationThemeBaseColor(rawValue: decoder.decodeInt32ForKey("b", orElse: 0)) ?? .blue
self.value = decoder.decodeOptionalInt32ForKey("c")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.baseColor.rawValue, forKey: "b")
if let value = self.value {
encoder.encodeInt32(value, forKey: "c")
} else {
encoder.encodeNil(forKey: "c")
}
}
public var color: UIColor {
return self.baseColor.color
if let value = self.value {
return UIColor(rgb: UInt32(bitPattern: value))
} else {
return self.baseColor.color
}
}
}
@ -341,7 +358,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
default:
baseColor = .blue
}
self.themeSpecificAccentColors[PresentationThemeReference.builtin(.day).index] = PresentationThemeAccentColor(baseColor: baseColor, value: 0.5)
self.themeSpecificAccentColors[PresentationThemeReference.builtin(.day).index] = PresentationThemeAccentColor(baseColor: baseColor)
}
self.fontSize = PresentationFontSize(rawValue: decoder.decodeInt32ForKey("f", orElse: PresentationFontSize.regular.rawValue)) ?? .regular