mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Cloud themes improvements
This commit is contained in:
parent
167b9289d3
commit
ec6526b07d
22 changed files with 633 additions and 177 deletions
|
|
@ -825,7 +825,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||
super.updateRevealOffset(offset: offset, transition: transition)
|
||||
|
||||
if let item = self.item, let params = self.layoutParams?.1 {
|
||||
var leftInset: CGFloat = 65.0
|
||||
var leftInset: CGFloat = 65.0 + params.leftInset
|
||||
|
||||
switch item.selection {
|
||||
case .none:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
091EABA5231DAC7500A0EC14 /* ThemeNameGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091EABA4231DAC7500A0EC14 /* ThemeNameGenerator.swift */; };
|
||||
09B4A9B823102B7A005C2E08 /* EditThemeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B723102B7A005C2E08 /* EditThemeController.swift */; };
|
||||
D03E465223075D930049C28B /* SettingsUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E465023075D930049C28B /* SettingsUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D03E466823075E660049C28B /* TabBarAccountSwitchControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E465C23075E630049C28B /* TabBarAccountSwitchControllerNode.swift */; };
|
||||
|
|
@ -191,6 +192,7 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
091EABA4231DAC7500A0EC14 /* ThemeNameGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeNameGenerator.swift; sourceTree = "<group>"; };
|
||||
09B4A9B723102B7A005C2E08 /* EditThemeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditThemeController.swift; sourceTree = "<group>"; };
|
||||
D03E464D23075D930049C28B /* SettingsUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SettingsUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D03E465023075D930049C28B /* SettingsUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsUI.h; sourceTree = "<group>"; };
|
||||
|
|
@ -650,6 +652,7 @@
|
|||
D03E46EB23075FD60049C28B /* WallpaperPatternPanelNode.swift */,
|
||||
D03E46EE23075FD60049C28B /* WallpaperSearchRecentQueries.swift */,
|
||||
09B4A9B723102B7A005C2E08 /* EditThemeController.swift */,
|
||||
091EABA4231DAC7500A0EC14 /* ThemeNameGenerator.swift */,
|
||||
);
|
||||
path = Themes;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -915,6 +918,7 @@
|
|||
D03E471523075FE40049C28B /* ThemeSettingsFontSizeItem.swift in Sources */,
|
||||
D03E46AA23075F2C0049C28B /* DataPrivacySettingsController.swift in Sources */,
|
||||
D03E467123075E660049C28B /* SettingsController.swift in Sources */,
|
||||
091EABA5231DAC7500A0EC14 /* ThemeNameGenerator.swift in Sources */,
|
||||
D03E46A923075F2C0049C28B /* BlockedPeersController.swift in Sources */,
|
||||
D03E469F23075F2C0049C28B /* TwoStepVerificationResetController.swift in Sources */,
|
||||
D03E472A230760330049C28B /* LocalizationListItem.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -247,12 +247,12 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||
switch mode {
|
||||
case .create:
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
initialState = EditThemeControllerState(mode: mode, title: "", slug: "", updatedTheme: nil, updating: false)
|
||||
initialState = EditThemeControllerState(mode: mode, title: generateThemeName(accentColor: presentationData.theme.rootController.navigationBar.buttonColor), slug: "", updatedTheme: nil, updating: false)
|
||||
previewThemePromise.set(.single(presentationData.theme.withUpdated(name: "", defaultWallpaper: presentationData.chatWallpaper)))
|
||||
case let .edit(info):
|
||||
if let file = info.theme.file, let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path)), let theme = makePresentationTheme(data: data, resolvedWallpaper: info.resolvedWallpaper) {
|
||||
if case let .file(file) = theme.chat.defaultWallpaper, file.id == 0 {
|
||||
previewThemePromise.set(cachedWallpaper(account: context.account, slug: file.slug)
|
||||
previewThemePromise.set(cachedWallpaper(account: context.account, slug: file.slug, settings: file.settings)
|
||||
|> map ({ wallpaper -> PresentationTheme in
|
||||
if let wallpaper = wallpaper {
|
||||
return theme.withUpdated(name: nil, defaultWallpaper: wallpaper.wallpaper)
|
||||
|
|
@ -287,7 +287,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||
if let url = urls.first{
|
||||
if let data = try? Data(contentsOf: url), let theme = makePresentationTheme(data: data) {
|
||||
if case let .file(file) = theme.chat.defaultWallpaper, file.id == 0 {
|
||||
let _ = (cachedWallpaper(account: context.account, slug: file.slug)
|
||||
let _ = (cachedWallpaper(account: context.account, slug: file.slug, settings: file.settings)
|
||||
|> mapToSignal { wallpaper -> Signal<TelegramWallpaper?, NoError> in
|
||||
if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper {
|
||||
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
||||
|
|
@ -447,7 +447,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||
let _ = (createTheme(account: context.account, title: state.title, resource: themeResource, thumbnailData: themeThumbnailData)
|
||||
|> deliverOnMainQueue).start(next: { next in
|
||||
if case let .result(resultTheme) = next {
|
||||
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme, install: true).start()
|
||||
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start()
|
||||
let _ = (context.sharedContext.accountManager.transaction { transaction -> Void in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
|
||||
let current: PresentationThemeSettings
|
||||
|
|
|
|||
352
submodules/SettingsUI/Sources/Themes/ThemeNameGenerator.swift
Normal file
352
submodules/SettingsUI/Sources/Themes/ThemeNameGenerator.swift
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
import UIKit
|
||||
|
||||
private let colors: [UInt32: String] = [
|
||||
0x8e0000: "Berry",
|
||||
0xdec196: "Brandy",
|
||||
0x800b47: "Cherry",
|
||||
0xff7f50: "Coral",
|
||||
0xdb5079: "Cranberry",
|
||||
0xdc143c: "Crimson",
|
||||
0xe0b0ff: "Mauve",
|
||||
0xffc0cb: "Pink",
|
||||
0xff0000: "Red",
|
||||
0xff007f: "Rose",
|
||||
0x80461b: "Russet",
|
||||
0xff2400: "Scarlet",
|
||||
0xf1f1f1: "Seashell",
|
||||
0xff3399: "Strawberry",
|
||||
0xffbf00: "Amber",
|
||||
0xeb9373: "Apricot",
|
||||
0xfbe7b2: "Banana",
|
||||
0xa1c50a: "Citrus",
|
||||
0xb06500: "Ginger",
|
||||
0xffd700: "Gold",
|
||||
0xfde910: "Lemon",
|
||||
0xffa500: "Orange",
|
||||
0xffe5b4: "Peach",
|
||||
0xff6b53: "Persimmon",
|
||||
0xe4d422: "Sunflower",
|
||||
0xf28500: "Tangerine",
|
||||
0xffc87c: "Topaz",
|
||||
0xffff00: "Yellow",
|
||||
0x384910: "Clover",
|
||||
0x83aa5d: "Cucumber",
|
||||
0x50c878: "Emerald",
|
||||
0xb5b35c: "Olive",
|
||||
0x00ff00: "Green",
|
||||
0x00a86b: "Jade",
|
||||
0x29ab87: "Jungle",
|
||||
0xbfff00: "Lime",
|
||||
0x0bda51: "Malachite",
|
||||
0x98ff98: "Mint",
|
||||
0xaddfad: "Moss",
|
||||
0x315ba1: "Azure",
|
||||
0x0000ff: "Blue",
|
||||
0x0047ab: "Cobalt",
|
||||
0x4f69c6: "Indigo",
|
||||
0x017987: "Lagoon",
|
||||
0x71d9e2: "Aquamarine",
|
||||
0x120a8f: "Ultramarine",
|
||||
0x000080: "Navy",
|
||||
0x2f519e: "Sapphire",
|
||||
0x76d7ea: "Sky",
|
||||
0x008080: "Teal",
|
||||
0x40e0d0: "Turquoise",
|
||||
0x9966cc: "Amethyst",
|
||||
0x4d0135: "Blackberry",
|
||||
0x614051: "Eggplant",
|
||||
0xc8a2c8: "Lilac",
|
||||
0xb57edc: "Lavender",
|
||||
0xccccff: "Periwinkle",
|
||||
0x843179: "Plum",
|
||||
0x660099: "Purple",
|
||||
0xd8bfd8: "Thistle",
|
||||
0xda70d6: "Orchid",
|
||||
0x240a40: "Violet",
|
||||
0x3f2109: "Bronze",
|
||||
0x370202: "Chocolate",
|
||||
0x7b3f00: "Cinnamon",
|
||||
0x301f1e: "Cocoa",
|
||||
0x706555: "Coffee",
|
||||
0x796989: "Rum",
|
||||
0x4e0606: "Mahogany",
|
||||
0x782d19: "Mocha",
|
||||
0xc2b280: "Sand",
|
||||
0x882d17: "Sienna",
|
||||
0x780109: "Maple",
|
||||
0xf0e68c: "Khaki",
|
||||
0xb87333: "Copper",
|
||||
0xb94e48: "Chestnut",
|
||||
0xeed9c4: "Almond",
|
||||
0xfffdd0: "Cream",
|
||||
0xb9f2ff: "Diamond",
|
||||
0xa98307: "Honey",
|
||||
0xfffff0: "Ivory",
|
||||
0xeae0c8: "Pearl",
|
||||
0xeff2f3: "Porcelain",
|
||||
0xd1bea8: "Vanilla",
|
||||
0xffffff: "White",
|
||||
0x808080: "Gray",
|
||||
0x000000: "Black",
|
||||
0xe8f1d4: "Chrome",
|
||||
0x36454f: "Charcoal",
|
||||
0x0c0b1d: "Ebony",
|
||||
0xc0c0c0: "Silver",
|
||||
0xf5f5f5: "Smoke",
|
||||
0x262335: "Steel",
|
||||
0x4fa83d: "Apple",
|
||||
0x80b3c4: "Glacier",
|
||||
0xfebaad: "Melon",
|
||||
0xc54b8c: "Mulberry",
|
||||
0xa9c6c2: "Opal"
|
||||
]
|
||||
|
||||
private let adjectives = [
|
||||
"Always",
|
||||
"Ancient",
|
||||
"Antique",
|
||||
"Autumn",
|
||||
"Baby",
|
||||
"Barely",
|
||||
"Baroque",
|
||||
"Blazing",
|
||||
"Blushing",
|
||||
"Bohemian",
|
||||
"Bubbly",
|
||||
"Burning",
|
||||
"Buttered",
|
||||
"Classic",
|
||||
"Clear",
|
||||
"Cool",
|
||||
"Cosmic",
|
||||
"Cotton",
|
||||
"Cozy",
|
||||
"Crystal",
|
||||
"Dark",
|
||||
"Daring",
|
||||
"Darling",
|
||||
"Dawn",
|
||||
"Dazzling",
|
||||
"Deep",
|
||||
"Deepest",
|
||||
"Delicate",
|
||||
"Delightful",
|
||||
"Divine",
|
||||
"Double",
|
||||
"Downtown",
|
||||
"Dreamy",
|
||||
"Dusky",
|
||||
"Dusty",
|
||||
"Electric",
|
||||
"Enchanted",
|
||||
"Endless",
|
||||
"Evening",
|
||||
"Fantastic",
|
||||
"Flirty",
|
||||
"Forever",
|
||||
"Frigid",
|
||||
"Frosty",
|
||||
"Frozen",
|
||||
"Gentle",
|
||||
"Golden",
|
||||
"Heavenly",
|
||||
"Hyper",
|
||||
"Icy",
|
||||
"Infinite",
|
||||
"Innocent",
|
||||
"Instant",
|
||||
"Luscious",
|
||||
"Lunar",
|
||||
"Lustrous",
|
||||
"Magic",
|
||||
"Majestic",
|
||||
"Mambo",
|
||||
"Midnight",
|
||||
"Millenium",
|
||||
"Morning",
|
||||
"Mystic",
|
||||
"Natural",
|
||||
"Neon",
|
||||
"Night",
|
||||
"Opaque",
|
||||
"Paradise",
|
||||
"Perfect",
|
||||
"Perky",
|
||||
"Polished",
|
||||
"Powerful",
|
||||
"Rich",
|
||||
"Royal",
|
||||
"Sheer",
|
||||
"Simply",
|
||||
"Sizzling",
|
||||
"Solar",
|
||||
"Sparkling",
|
||||
"Splendid",
|
||||
"Spicy",
|
||||
"Spring",
|
||||
"Stellar",
|
||||
"Sugared",
|
||||
"Summer",
|
||||
"Sunny",
|
||||
"Super",
|
||||
"Sweet",
|
||||
"Tender",
|
||||
"Tenacious",
|
||||
"Tidal",
|
||||
"Toasted",
|
||||
"Totally",
|
||||
"Tranquil",
|
||||
"Tropical",
|
||||
"True",
|
||||
"Twilight",
|
||||
"Twinkling",
|
||||
"Ultimate",
|
||||
"Ultra",
|
||||
"Uptown",
|
||||
"Velvety",
|
||||
"Vibrant",
|
||||
"Vintage",
|
||||
"Virtual",
|
||||
"Warm",
|
||||
"Warmest",
|
||||
"Whipped",
|
||||
"Wild",
|
||||
"Winsome"
|
||||
]
|
||||
|
||||
private let subjectives = [
|
||||
"Ambrosia",
|
||||
"Attack",
|
||||
"Avalanche",
|
||||
"Blast",
|
||||
"Bliss",
|
||||
"Blossom",
|
||||
"Blush",
|
||||
"Burst",
|
||||
"Butter",
|
||||
"Candy",
|
||||
"Carnival",
|
||||
"Charm",
|
||||
"Chiffon",
|
||||
"Cloud",
|
||||
"Comet",
|
||||
"Delight",
|
||||
"Dream",
|
||||
"Dust",
|
||||
"Fantasy",
|
||||
"Flame",
|
||||
"Flash",
|
||||
"Fire",
|
||||
"Freeze",
|
||||
"Frost",
|
||||
"Glade",
|
||||
"Glaze",
|
||||
"Gleam",
|
||||
"Glimmer",
|
||||
"Glitter",
|
||||
"Glow",
|
||||
"Grande",
|
||||
"Haze",
|
||||
"Highlight",
|
||||
"Ice",
|
||||
"Illusion",
|
||||
"Intrigue",
|
||||
"Jewel",
|
||||
"Jubilee",
|
||||
"Kiss",
|
||||
"Lights",
|
||||
"Lollypop",
|
||||
"Love",
|
||||
"Luster",
|
||||
"Madness",
|
||||
"Matte",
|
||||
"Mirage",
|
||||
"Mist",
|
||||
"Moon",
|
||||
"Muse",
|
||||
"Myth",
|
||||
"Nectar",
|
||||
"Nova",
|
||||
"Parfait",
|
||||
"Passion",
|
||||
"Pop",
|
||||
"Rain",
|
||||
"Reflection",
|
||||
"Rhapsody",
|
||||
"Romance",
|
||||
"Satin",
|
||||
"Sensation",
|
||||
"Silk",
|
||||
"Shine",
|
||||
"Shadow",
|
||||
"Shimmer",
|
||||
"Sky",
|
||||
"Spice",
|
||||
"Star",
|
||||
"Sugar",
|
||||
"Sunrise",
|
||||
"Sunset",
|
||||
"Sun",
|
||||
"Twist",
|
||||
"Unbound",
|
||||
"Velvet",
|
||||
"Vibrant",
|
||||
"Waters",
|
||||
"Wine",
|
||||
"Wink",
|
||||
"Wonder",
|
||||
"Zone"
|
||||
]
|
||||
|
||||
private extension UIColor {
|
||||
var colorComponents: (r: Int32, g: Int32, b: Int32) {
|
||||
var r: CGFloat = 0.0
|
||||
var g: CGFloat = 0.0
|
||||
var b: CGFloat = 0.0
|
||||
if self.getRed(&r, green: &g, blue: &b, alpha: nil) {
|
||||
return (Int32(max(0.0, r) * 255.0), Int32(max(0.0, g) * 255.0), Int32(max(0.0, b) * 255.0))
|
||||
} else if self.getWhite(&r, alpha: nil) {
|
||||
return (Int32(max(0.0, r) * 255.0), Int32(max(0.0, r) * 255.0), Int32(max(0.0, r) * 255.0))
|
||||
}
|
||||
return (0, 0, 0)
|
||||
}
|
||||
|
||||
func distance(to other: UIColor) -> Int32 {
|
||||
let e1 = self.colorComponents
|
||||
let e2 = other.colorComponents
|
||||
let rMean = (e1.r + e2.r) / 2
|
||||
let r = e1.r - e2.r
|
||||
let g = e1.g - e2.g
|
||||
let b = e1.b - e2.b
|
||||
return ((512 + rMean) * r * r) >> 8 + 4 * g * g + ((767 - rMean) * b * b) >> 8
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func generateThemeName(accentColor: UIColor) -> String {
|
||||
var nearest: (color: UInt32, distance: Int32)?
|
||||
for (color, _) in colors {
|
||||
let distance = accentColor.distance(to: UIColor(rgb: color))
|
||||
if let currentNearest = nearest {
|
||||
if distance < currentNearest.distance {
|
||||
nearest = (color, distance)
|
||||
}
|
||||
} else {
|
||||
nearest = (color, distance)
|
||||
}
|
||||
}
|
||||
|
||||
if let color = nearest?.color, let colorName = colors[color]?.capitalized {
|
||||
if arc4random() % 2 == 0 {
|
||||
return "\(adjectives[Int(arc4random()) % adjectives.count].capitalized) \(colorName)"
|
||||
} else {
|
||||
if false, arc4random() % 3 == 0 {
|
||||
return "\(adjectives[Int(arc4random()) % adjectives.count].capitalized) \(colorName) \(subjectives[Int(arc4random()) % subjectives.count].capitalized)"
|
||||
} else {
|
||||
return "\(colorName) \(subjectives[Int(arc4random()) % subjectives.count].capitalized)"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ public final class ThemePreviewController: ViewController {
|
|||
|
||||
let previewTheme = self.previewTheme
|
||||
if case let .file(file) = previewTheme.chat.defaultWallpaper, file.id == 0 {
|
||||
self.controllerNode.wallpaperPromise.set(cachedWallpaper(account: self.context.account, slug: file.slug)
|
||||
self.controllerNode.wallpaperPromise.set(cachedWallpaper(account: self.context.account, slug: file.slug, settings: file.settings)
|
||||
|> mapToSignal { wallpaper in
|
||||
return .single(wallpaper?.wallpaper ?? .color(Int32(bitPattern: previewTheme.chatList.backgroundColor.rgb)))
|
||||
})
|
||||
|
|
@ -252,7 +252,7 @@ public final class ThemePreviewController: ViewController {
|
|||
}
|
||||
|> mapToSignal { theme -> Signal<Void, NoError> in
|
||||
if case let .cloud(info) = theme {
|
||||
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: info.theme, install: true).start()
|
||||
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: info.theme).start()
|
||||
let _ = saveThemeInteractively(account: context.account, accountManager: context.sharedContext.accountManager, theme: info.theme).start()
|
||||
}
|
||||
return context.sharedContext.accountManager.transaction { transaction -> Void in
|
||||
|
|
@ -278,7 +278,7 @@ public final class ThemePreviewController: ViewController {
|
|||
}
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
|> delay(0.7, queue: Queue.mainQueue())
|
||||
|> delay(0.35, queue: Queue.mainQueue())
|
||||
|
||||
let progressDisposable = progressSignal.start()
|
||||
cancelImpl = {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import TelegramUIPreferences
|
|||
import AccountContext
|
||||
import ChatListUI
|
||||
import WallpaperResources
|
||||
import LegacyComponents
|
||||
|
||||
private func generateMaskImage(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 1.0, height: 80.0), opaque: false, rotatedContext: { size, context in
|
||||
|
|
@ -46,6 +47,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
private let chatContainerNode: ASDisplayNode
|
||||
private let instantChatBackgroundNode: WallpaperBackgroundNode
|
||||
private let remoteChatBackgroundNode: TransformImageNode
|
||||
private let blurredNode: BlurredImageNode
|
||||
private var messageNodes: [ListViewItemNode]?
|
||||
|
||||
private let toolbarNode: WallpaperGalleryToolbarNode
|
||||
|
|
@ -89,6 +91,9 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
self.remoteChatBackgroundNode = TransformImageNode()
|
||||
self.remoteChatBackgroundNode.backgroundColor = previewTheme.chatList.backgroundColor
|
||||
|
||||
self.blurredNode = BlurredImageNode()
|
||||
self.blurredNode.clipsToBounds = true
|
||||
|
||||
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings)
|
||||
|
||||
if case let .file(file) = previewTheme.chat.defaultWallpaper, file.id == 0 {
|
||||
|
|
@ -140,6 +145,26 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
apply()
|
||||
}
|
||||
|
||||
if case let .file(file) = self.previewTheme.chat.defaultWallpaper {
|
||||
if file.settings.blur {
|
||||
self.chatContainerNode.addSubnode(self.blurredNode)
|
||||
}
|
||||
}
|
||||
|
||||
self.remoteChatBackgroundNode.imageUpdated = { [weak self] image in
|
||||
if let strongSelf = self, strongSelf.blurredNode.supernode != nil {
|
||||
var image = image
|
||||
if let imageToScale = image {
|
||||
let actualSize = CGSize(width: imageToScale.size.width * imageToScale.scale, height: imageToScale.size.height * imageToScale.scale)
|
||||
if actualSize.width > 1280.0 || actualSize.height > 1280.0 {
|
||||
image = TGScaleImageToPixelSize(image, actualSize.fitted(CGSize(width: 1280.0, height: 1280.0)))
|
||||
}
|
||||
}
|
||||
strongSelf.blurredNode.image = image
|
||||
strongSelf.blurredNode.blurView.blurRadius = 45.0
|
||||
}
|
||||
}
|
||||
|
||||
self.colorDisposable = (self.wallpaperPromise.get()
|
||||
|> mapToSignal { wallpaper -> Signal<UIColor, NoError> in
|
||||
if case let .file(file) = wallpaper, file.id == 0 {
|
||||
|
|
@ -173,12 +198,17 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
}
|
||||
convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource), reference: .media(media: .standalone(media: file.file), resource: file.file.resource)))
|
||||
|
||||
let signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>
|
||||
let fileReference = FileMediaReference.standalone(media: file.file)
|
||||
let signal = wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: fileReference, representations: convertedRepresentations, alwaysShowThumbnailFirst: false, autoFetchFullSize: false)
|
||||
|> afterNext { next in
|
||||
if let _ = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.file.resource) {
|
||||
} else if let path = context.account.postbox.mediaBox.completedResourcePath(file.file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
|
||||
context.sharedContext.accountManager.mediaBox.storeResourceData(file.file.resource.id, data: data)
|
||||
if file.isPattern {
|
||||
signal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: false)
|
||||
} else {
|
||||
signal = wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: fileReference, representations: convertedRepresentations, alwaysShowThumbnailFirst: false, autoFetchFullSize: false)
|
||||
|> afterNext { next in
|
||||
if let _ = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.file.resource) {
|
||||
} else if let path = context.account.postbox.mediaBox.completedResourcePath(file.file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
|
||||
context.sharedContext.accountManager.mediaBox.storeResourceData(file.file.resource.id, data: data)
|
||||
}
|
||||
}
|
||||
}
|
||||
strongSelf.remoteChatBackgroundNode.setSignal(signal)
|
||||
|
|
@ -203,7 +233,16 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
}
|
||||
})
|
||||
|
||||
strongSelf.remoteChatBackgroundNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: displaySize, boundingSize: displaySize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil))()
|
||||
var patternColor: UIColor?
|
||||
var patternIntensity: CGFloat = 0.5
|
||||
if let color = file.settings.color {
|
||||
if let intensity = file.settings.intensity {
|
||||
patternIntensity = CGFloat(intensity) / 100.0
|
||||
}
|
||||
patternColor = UIColor(rgb: UInt32(bitPattern: color), alpha: patternIntensity)
|
||||
}
|
||||
|
||||
strongSelf.remoteChatBackgroundNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: displaySize, boundingSize: displaySize, intrinsicInsets: UIEdgeInsets(), emptyColor: patternColor))()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -402,6 +441,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
self.chatContainerNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
|
||||
self.instantChatBackgroundNode.frame = self.chatContainerNode.bounds
|
||||
self.remoteChatBackgroundNode.frame = self.chatContainerNode.bounds
|
||||
self.blurredNode.frame = self.chatContainerNode.bounds
|
||||
|
||||
self.scrollNode.view.contentSize = CGSize(width: bounds.width * 2.0, height: bounds.height)
|
||||
|
||||
|
|
|
|||
|
|
@ -570,7 +570,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||
|
||||
let resolvedWallpaper: Signal<TelegramWallpaper?, NoError>
|
||||
if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 {
|
||||
resolvedWallpaper = cachedWallpaper(account: context.account, slug: file.slug)
|
||||
resolvedWallpaper = cachedWallpaper(account: context.account, slug: file.slug, settings: file.settings)
|
||||
|> map { wallpaper -> TelegramWallpaper? in
|
||||
return wallpaper?.wallpaper
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec
|
|||
if let image = borderImages[key] {
|
||||
return image
|
||||
} else {
|
||||
let image = generateImage(CGSize(width: 30.0, height: 30.0), rotatedContext: { size, context in
|
||||
let image = generateImage(CGSize(width: 32.0, height: 32.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.setFillColor(theme.list.itemBlocksBackgroundColor.cgColor)
|
||||
context.fill(bounds)
|
||||
|
||||
context.setBlendMode(.clear)
|
||||
context.fillEllipse(in: bounds)
|
||||
context.fillEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0))
|
||||
context.setBlendMode(.normal)
|
||||
|
||||
let lineWidth: CGFloat
|
||||
|
|
@ -42,9 +42,9 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec
|
|||
|
||||
if bordered || selected {
|
||||
context.setLineWidth(lineWidth)
|
||||
context.strokeEllipse(in: bounds.insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0))
|
||||
context.strokeEllipse(in: bounds.insetBy(dx: 1.0 + lineWidth / 2.0, dy: 1.0 + lineWidth / 2.0))
|
||||
}
|
||||
})?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 15)
|
||||
})?.stretchableImage(withLeftCapWidth: 16, topCapHeight: 16)
|
||||
borderImages[key] = image
|
||||
return image
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ private func createThemeImage(theme: PresentationTheme) -> Signal<(TransformImag
|
|||
return .single(theme)
|
||||
|> map { theme -> (TransformImageArguments) -> DrawingContext? in
|
||||
return { arguments in
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil)
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: false)
|
||||
let drawingRect = arguments.drawingRect
|
||||
|
||||
context.withContext { c in
|
||||
|
|
@ -155,7 +155,7 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
|||
self.imageNode.isLayerBacked = true
|
||||
|
||||
self.overlayNode = ASImageNode()
|
||||
self.overlayNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 98.0, height: 62.0))
|
||||
self.overlayNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 100.0, height: 64.0))
|
||||
self.overlayNode.isLayerBacked = true
|
||||
|
||||
self.textNode = ASTextNode()
|
||||
|
|
@ -170,8 +170,9 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
|||
}
|
||||
|
||||
func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, longTapAction: @escaping () -> Void) {
|
||||
let updatedTheme = self.currentTheme == nil || currentTheme !== self.currentTheme!
|
||||
if case let .cloud(theme) = theme, theme.theme.file == nil {
|
||||
if self.currentTheme == nil || currentTheme !== self.currentTheme! || accentColor != self.accentColor {
|
||||
if updatedTheme || accentColor != self.accentColor {
|
||||
self.imageNode.setSignal(createThemeImage(theme: currentTheme))
|
||||
self.currentTheme = currentTheme
|
||||
self.accentColor = accentColor
|
||||
|
|
@ -183,7 +184,7 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
|||
self.accentColor = accentColor
|
||||
}
|
||||
}
|
||||
if self.currentTheme == nil || currentTheme !== self.currentTheme! || bordered != self.bordered || selected != self.selected {
|
||||
if updatedTheme || bordered != self.bordered || selected != self.selected {
|
||||
self.overlayNode.image = generateBorderImage(theme: currentTheme, bordered: bordered, selected: selected)
|
||||
self.currentTheme = currentTheme
|
||||
self.bordered = bordered
|
||||
|
|
@ -238,7 +239,7 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
|||
let applyLayout = makeLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: .clear))
|
||||
applyLayout()
|
||||
|
||||
self.overlayNode.frame = CGRect(origin: CGPoint(x: 10.0, y: 14.0), size: CGSize(width: 98.0, height: 62.0))
|
||||
self.overlayNode.frame = CGRect(origin: CGPoint(x: 9.0, y: 13.0), size: CGSize(width: 100.0, height: 64.0))
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 14.0 + 60.0 + 4.0 + 9.0), size: CGSize(width: bounds.size.width, height: 16.0))
|
||||
}
|
||||
}
|
||||
|
|
@ -295,8 +296,6 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||
}
|
||||
|
||||
func asyncLayout() -> (_ item: ThemeSettingsThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let currentItem = self.item
|
||||
|
||||
return { item, params, neighbors in
|
||||
let contentSize: CGSize
|
||||
let insets: UIEdgeInsets
|
||||
|
|
|
|||
|
|
@ -5717,12 +5717,13 @@ public extension Api {
|
|||
})
|
||||
}
|
||||
|
||||
public static func installTheme(format: String, theme: Api.InputTheme) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
public static func installTheme(flags: Int32, format: String?, theme: Api.InputTheme?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-520600676)
|
||||
serializeString(format, buffer: buffer, boxed: false)
|
||||
theme.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "account.installTheme", parameters: [("format", format), ("theme", theme)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
buffer.appendInt32(2061776695)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(format!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {theme!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "account.installTheme", parameters: [("flags", flags), ("format", format), ("theme", theme)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
|
|||
|
|
@ -178,8 +178,21 @@ private func saveUnsaveTheme(account: Account, accountManager: AccountManager, t
|
|||
} |> switchToLatest
|
||||
}
|
||||
|
||||
private func installTheme(account: Account, theme: TelegramTheme) -> Signal<Never, NoError> {
|
||||
return account.network.request(Api.functions.account.installTheme(format: themeFormat, theme: Api.InputTheme.inputTheme(id: theme.id, accessHash: theme.accessHash)))
|
||||
private func installTheme(account: Account, theme: TelegramTheme?, autoNight: Bool) -> Signal<Never, NoError> {
|
||||
var flags: Int32 = 0
|
||||
if autoNight {
|
||||
flags |= 1 << 0
|
||||
}
|
||||
|
||||
let inputTheme: Api.InputTheme?
|
||||
if let theme = theme {
|
||||
inputTheme = .inputTheme(id: theme.id, accessHash: theme.accessHash)
|
||||
flags |= 1 << 1
|
||||
} else {
|
||||
inputTheme = nil
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.account.installTheme(flags: flags, format: themeFormat, theme: inputTheme))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|
|
@ -461,14 +474,14 @@ public func deleteThemeInteractively(account: Account, accountManager: AccountMa
|
|||
return saveUnsaveTheme(account: account, accountManager: accountManager, theme: theme, unsave: true)
|
||||
}
|
||||
|
||||
public func applyTheme(accountManager: AccountManager, account: Account, theme: TelegramTheme?, install: Bool = false) -> Signal<Never, NoError> {
|
||||
public func applyTheme(accountManager: AccountManager, account: Account, theme: TelegramTheme?, autoNight: Bool = false) -> Signal<Never, NoError> {
|
||||
return accountManager.transaction { transaction -> Signal<Never, NoError> in
|
||||
transaction.updateSharedData(SharedDataKeys.themeSettings, { _ in
|
||||
return ThemeSettings(currentTheme: theme)
|
||||
})
|
||||
|
||||
if let theme = theme, install {
|
||||
return installTheme(account: account, theme: theme)
|
||||
if let theme = theme {
|
||||
return installTheme(account: account, theme: theme, autoNight: autoNight)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable {
|
|||
case let .image(representations, _):
|
||||
return .image(representations, settings)
|
||||
case let .file(id, accessHash, isCreator, isDefault, isPattern, isDark, slug, file, _):
|
||||
return .file(id: id, accessHash: accessHash, isCreator: isCreator, isDefault: isDefault, isPattern: isPattern, isDark: isDark, slug: slug, file: file, settings: settings)
|
||||
return .file(id: id, accessHash: accessHash, isCreator: isCreator, isDefault: isDefault, isPattern: settings.color != nil ? true : isPattern, isDark: isDark, slug: slug, file: file, settings: settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,11 +123,11 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||
freeMonoIconColor: mainFreeTextColor,
|
||||
itemSwitchColors: switchColors,
|
||||
itemDisclosureActions: PresentationThemeItemDisclosureActions(
|
||||
neutral1: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0x007cd6), foregroundColor: .white),
|
||||
neutral1: PresentationThemeFillForeground(fillColor: accentColor, foregroundColor: .white),
|
||||
neutral2: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xcd7800), foregroundColor: .white),
|
||||
destructive: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xc70c0c), foregroundColor: .white),
|
||||
constructive: PresentationThemeFillForeground(fillColor: constructiveColor, foregroundColor: .white),
|
||||
accent: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0x007cd6), foregroundColor: .white),
|
||||
accent: PresentationThemeFillForeground(fillColor: accentColor, foregroundColor: .white),
|
||||
warning: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xcd7800), foregroundColor: .white),
|
||||
inactive: PresentationThemeFillForeground(fillColor: accentColor.withMultiplied(hue: 1.029, saturation: 0.609, brightness: 0.3), foregroundColor: .white)
|
||||
),
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public struct PresentationAppIcon: Equatable {
|
|||
public let imageName: String
|
||||
public let isDefault: Bool
|
||||
|
||||
public init(name: String, imageName: String, isDefault: Bool) {
|
||||
public init(name: String, imageName: String, isDefault: Bool = false) {
|
||||
self.name = name
|
||||
self.imageName = imageName
|
||||
self.isDefault = isDefault
|
||||
|
|
|
|||
|
|
@ -985,10 +985,6 @@ public final class PresentationTheme: Equatable {
|
|||
switch wallpaper {
|
||||
case .image:
|
||||
defaultWallpaper = nil
|
||||
case let .file(file):
|
||||
if file.isPattern {
|
||||
defaultWallpaper = nil
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ extension TelegramWallpaper: Codable {
|
|||
if !components.isEmpty {
|
||||
slug = components[0]
|
||||
}
|
||||
if components.count > 1, !["motion", "blur"].contains(components[1]), [6,7].contains(components[1].count), let value = Int32(components[1]) {
|
||||
color = value
|
||||
if components.count > 1, !["motion", "blur"].contains(components[1]), components[1].count == 6, let value = UIColor(hexString: components[1]) {
|
||||
color = Int32(bitPattern: value.rgb)
|
||||
}
|
||||
if components.count > 2, !["motion", "blur"].contains(components[2]), [6,7].contains(components[2].count), let value = Int32(components[2]) {
|
||||
if components.count > 2, !["motion", "blur"].contains(components[2]), let value = Int32(components[2]) {
|
||||
if value >= 0 && value <= 100 {
|
||||
intensity = value
|
||||
} else {
|
||||
|
|
@ -85,18 +85,17 @@ extension TelegramWallpaper: Codable {
|
|||
components.append(file.slug)
|
||||
if file.isPattern {
|
||||
if let color = file.settings.color {
|
||||
components.removeAll()
|
||||
components.append(String(format: "%06x", color))
|
||||
}
|
||||
// if let intensity = file.settings.intensity {
|
||||
// components.append("\(intensity)")
|
||||
// }
|
||||
// if file.settings.motion {
|
||||
// components.append("motion")
|
||||
// }
|
||||
// if file.settings.blur {
|
||||
// components.append("blur")
|
||||
// }
|
||||
if let intensity = file.settings.intensity {
|
||||
components.append("\(intensity)")
|
||||
}
|
||||
}
|
||||
if file.settings.motion {
|
||||
components.append("motion")
|
||||
}
|
||||
if file.settings.blur {
|
||||
components.append("blur")
|
||||
}
|
||||
try container.encode(components.joined(separator: " "))
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -594,12 +594,12 @@ final class SharedApplicationContext {
|
|||
if #available(iOS 10.3, *) {
|
||||
var icons = [PresentationAppIcon(name: "Blue", imageName: "BlueIcon", isDefault: buildConfig.isAppStoreBuild),
|
||||
PresentationAppIcon(name: "Black", imageName: "BlackIcon", isDefault: buildConfig.isInternalBuild),
|
||||
PresentationAppIcon(name: "BlueClassic", imageName: "BlueClassicIcon", isDefault: false),
|
||||
PresentationAppIcon(name: "BlackClassic", imageName: "BlackClassicIcon", isDefault: false),
|
||||
PresentationAppIcon(name: "BlueFilled", imageName: "BlueFilledIcon", isDefault: false),
|
||||
PresentationAppIcon(name: "BlackFilled", imageName: "BlackFilledIcon", isDefault: false)]
|
||||
PresentationAppIcon(name: "BlueClassic", imageName: "BlueClassicIcon"),
|
||||
PresentationAppIcon(name: "BlackClassic", imageName: "BlackClassicIcon"),
|
||||
PresentationAppIcon(name: "BlueFilled", imageName: "BlueFilledIcon"),
|
||||
PresentationAppIcon(name: "BlackFilled", imageName: "BlackFilledIcon")]
|
||||
if buildConfig.isInternalBuild {
|
||||
icons.append(PresentationAppIcon(name: "WhiteFilled", imageName: "WhiteFilledIcon", isDefault: false))
|
||||
icons.append(PresentationAppIcon(name: "WhiteFilled", imageName: "WhiteFilledIcon"))
|
||||
}
|
||||
return icons
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3973,90 +3973,93 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
|
||||
switch self.chatLocation {
|
||||
case let .peer(peerId):
|
||||
let unreadCountsKey: PostboxViewKey = .unreadCounts(items: [.peer(peerId), .total(nil)])
|
||||
let notificationSettingsKey: PostboxViewKey = .peerNotificationSettings(peerIds: Set([peerId]))
|
||||
self.chatUnreadCountDisposable = (self.context.account.postbox.combinedView(keys: [unreadCountsKey, notificationSettingsKey])
|
||||
|> deliverOnMainQueue).start(next: { [weak self] views in
|
||||
if let strongSelf = self {
|
||||
var unreadCount: Int32 = 0
|
||||
var totalChatCount: Int32 = 0
|
||||
|
||||
let inAppSettings = strongSelf.context.sharedContext.currentInAppNotificationSettings.with { $0 }
|
||||
if let view = views.views[unreadCountsKey] as? UnreadMessageCountsView {
|
||||
if let count = view.count(for: .peer(peerId)) {
|
||||
unreadCount = count
|
||||
}
|
||||
if let (_, state) = view.total() {
|
||||
let (count, _) = renderedTotalUnreadCount(inAppSettings: inAppSettings, totalUnreadState: state)
|
||||
totalChatCount = count
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.navigateButtons.unreadCount = unreadCount
|
||||
|
||||
if let view = views.views[notificationSettingsKey] as? PeerNotificationSettingsView, let notificationSettings = view.notificationSettings[peerId] {
|
||||
var globalRemainingUnreadChatCount = totalChatCount
|
||||
if !notificationSettings.isRemovedFromTotalUnreadCount && unreadCount > 0 {
|
||||
if case .messages = inAppSettings.totalUnreadCountDisplayCategory {
|
||||
globalRemainingUnreadChatCount -= unreadCount
|
||||
} else {
|
||||
globalRemainingUnreadChatCount -= 1
|
||||
if let subject = self.subject, case .scheduledMessages = subject {
|
||||
} else {
|
||||
let unreadCountsKey: PostboxViewKey = .unreadCounts(items: [.peer(peerId), .total(nil)])
|
||||
let notificationSettingsKey: PostboxViewKey = .peerNotificationSettings(peerIds: Set([peerId]))
|
||||
self.chatUnreadCountDisposable = (self.context.account.postbox.combinedView(keys: [unreadCountsKey, notificationSettingsKey])
|
||||
|> deliverOnMainQueue).start(next: { [weak self] views in
|
||||
if let strongSelf = self {
|
||||
var unreadCount: Int32 = 0
|
||||
var totalChatCount: Int32 = 0
|
||||
|
||||
let inAppSettings = strongSelf.context.sharedContext.currentInAppNotificationSettings.with { $0 }
|
||||
if let view = views.views[unreadCountsKey] as? UnreadMessageCountsView {
|
||||
if let count = view.count(for: .peer(peerId)) {
|
||||
unreadCount = count
|
||||
}
|
||||
if let (_, state) = view.total() {
|
||||
let (count, _) = renderedTotalUnreadCount(inAppSettings: inAppSettings, totalUnreadState: state)
|
||||
totalChatCount = count
|
||||
}
|
||||
}
|
||||
|
||||
if globalRemainingUnreadChatCount > 0 {
|
||||
strongSelf.navigationItem.badge = "\(globalRemainingUnreadChatCount)"
|
||||
} else {
|
||||
strongSelf.navigationItem.badge = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.chatUnreadMentionCountDisposable = (self.context.account.viewTracker.unseenPersonalMessagesCount(peerId: peerId) |> deliverOnMainQueue).start(next: { [weak self] count in
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatDisplayNode.navigateButtons.mentionCount = count
|
||||
}
|
||||
})
|
||||
|
||||
let postbox = self.context.account.postbox
|
||||
let previousPeerCache = Atomic<[PeerId: Peer]>(value: [:])
|
||||
self.peerInputActivitiesDisposable = (self.context.account.peerInputActivities(peerId: peerId)
|
||||
|> mapToSignal { activities -> Signal<[(Peer, PeerInputActivity)], NoError> in
|
||||
var foundAllPeers = true
|
||||
var cachedResult: [(Peer, PeerInputActivity)] = []
|
||||
previousPeerCache.with { dict -> Void in
|
||||
for (peerId, activity) in activities {
|
||||
if let peer = dict[peerId] {
|
||||
cachedResult.append((peer, activity))
|
||||
} else {
|
||||
foundAllPeers = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if foundAllPeers {
|
||||
return .single(cachedResult)
|
||||
} else {
|
||||
return postbox.transaction { transaction -> [(Peer, PeerInputActivity)] in
|
||||
var result: [(Peer, PeerInputActivity)] = []
|
||||
var peerCache: [PeerId: Peer] = [:]
|
||||
for (peerId, activity) in activities {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
result.append((peer, activity))
|
||||
peerCache[peerId] = peer
|
||||
strongSelf.chatDisplayNode.navigateButtons.unreadCount = unreadCount
|
||||
|
||||
if let view = views.views[notificationSettingsKey] as? PeerNotificationSettingsView, let notificationSettings = view.notificationSettings[peerId] {
|
||||
var globalRemainingUnreadChatCount = totalChatCount
|
||||
if !notificationSettings.isRemovedFromTotalUnreadCount && unreadCount > 0 {
|
||||
if case .messages = inAppSettings.totalUnreadCountDisplayCategory {
|
||||
globalRemainingUnreadChatCount -= unreadCount
|
||||
} else {
|
||||
globalRemainingUnreadChatCount -= 1
|
||||
}
|
||||
}
|
||||
|
||||
if globalRemainingUnreadChatCount > 0 {
|
||||
strongSelf.navigationItem.badge = "\(globalRemainingUnreadChatCount)"
|
||||
} else {
|
||||
strongSelf.navigationItem.badge = ""
|
||||
}
|
||||
}
|
||||
let _ = previousPeerCache.swap(peerCache)
|
||||
return result
|
||||
}
|
||||
})
|
||||
|
||||
self.chatUnreadMentionCountDisposable = (self.context.account.viewTracker.unseenPersonalMessagesCount(peerId: peerId) |> deliverOnMainQueue).start(next: { [weak self] count in
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatDisplayNode.navigateButtons.mentionCount = count
|
||||
}
|
||||
})
|
||||
|
||||
let postbox = self.context.account.postbox
|
||||
let previousPeerCache = Atomic<[PeerId: Peer]>(value: [:])
|
||||
self.peerInputActivitiesDisposable = (self.context.account.peerInputActivities(peerId: peerId)
|
||||
|> mapToSignal { activities -> Signal<[(Peer, PeerInputActivity)], NoError> in
|
||||
var foundAllPeers = true
|
||||
var cachedResult: [(Peer, PeerInputActivity)] = []
|
||||
previousPeerCache.with { dict -> Void in
|
||||
for (peerId, activity) in activities {
|
||||
if let peer = dict[peerId] {
|
||||
cachedResult.append((peer, activity))
|
||||
} else {
|
||||
foundAllPeers = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if foundAllPeers {
|
||||
return .single(cachedResult)
|
||||
} else {
|
||||
return postbox.transaction { transaction -> [(Peer, PeerInputActivity)] in
|
||||
var result: [(Peer, PeerInputActivity)] = []
|
||||
var peerCache: [PeerId: Peer] = [:]
|
||||
for (peerId, activity) in activities {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
result.append((peer, activity))
|
||||
peerCache[peerId] = peer
|
||||
}
|
||||
}
|
||||
let _ = previousPeerCache.swap(peerCache)
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] activities in
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatTitleView?.inputActivities = (peerId, activities)
|
||||
}
|
||||
})
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] activities in
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatTitleView?.inputActivities = (peerId, activities)
|
||||
}
|
||||
})
|
||||
|
||||
self.sentMessageEventsDisposable.set(self.context.account.pendingMessageManager.deliveredMessageEvents(peerId: peerId).start(next: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
|
|
|
|||
|
|
@ -311,7 +311,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
|||
unboundSize = CGSize(width: floor(dimensions.width * 0.5), height: floor(dimensions.height * 0.5)).fitted(CGSize(width: 240.0, height: 240.0))
|
||||
} else if isTheme {
|
||||
if isSupported {
|
||||
unboundSize = CGSize(width: 160.0, height: 240.0)
|
||||
unboundSize = CGSize(width: 160.0, height: 240.0).fitted(CGSize(width: 240.0, height: 240.0))
|
||||
} else if let thumbnail = file.previewRepresentations.first {
|
||||
unboundSize = CGSize(width: floor(thumbnail.dimensions.width), height: floor(thumbnail.dimensions.height)).fitted(CGSize(width: 240.0, height: 240.0))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
|||
|
||||
let inputHeight = layout.inputHeight ?? 0.0
|
||||
var clipDelta = delta
|
||||
if inputHeight.isZero {
|
||||
if inputHeight.isZero || layout.isNonExclusive {
|
||||
clipDelta -= self.contentContainerNode.frame.height + 16.0
|
||||
}
|
||||
|
||||
|
|
@ -466,7 +466,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
|||
|
||||
let inputHeight = layout.inputHeight ?? 0.0
|
||||
var clipDelta = delta
|
||||
if inputHeight.isZero {
|
||||
if inputHeight.isZero || layout.isNonExclusive {
|
||||
clipDelta -= self.contentContainerNode.frame.height + 16.0
|
||||
}
|
||||
|
||||
|
|
@ -527,7 +527,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
|||
let contentOffset = self.scrollNode.view.contentOffset.y
|
||||
|
||||
var contentOrigin = CGPoint(x: layout.size.width - sideInset - contentSize.width - layout.safeInsets.right, y: layout.size.height - 6.0 - insets.bottom - contentSize.height)
|
||||
if inputHeight > 0.0 {
|
||||
if inputHeight > 0.0 && !layout.isNonExclusive {
|
||||
contentOrigin.y += menuHeightWithInset
|
||||
}
|
||||
contentOrigin.y = min(contentOrigin.y + contentOffset, layout.size.height - 6.0 - layout.intrinsicInsets.bottom - contentSize.height)
|
||||
|
|
@ -542,7 +542,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
|||
|
||||
let initialSendButtonFrame = self.sendButtonFrame
|
||||
var sendButtonFrame = CGRect(origin: CGPoint(x: layout.size.width - initialSendButtonFrame.width + 1.0 - UIScreenPixel - layout.safeInsets.right, y: layout.size.height - insets.bottom - initialSendButtonFrame.height), size: initialSendButtonFrame.size)
|
||||
if inputHeight.isZero {
|
||||
if inputHeight.isZero || layout.isNonExclusive {
|
||||
sendButtonFrame.origin.y -= menuHeightWithInset
|
||||
}
|
||||
sendButtonFrame.origin.y = min(sendButtonFrame.origin.y + contentOffset, layout.size.height - layout.intrinsicInsets.bottom - initialSendButtonFrame.height)
|
||||
|
|
@ -552,7 +552,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
|||
messageFrame.size.width += 32.0
|
||||
messageFrame.origin.x -= 13.0
|
||||
messageFrame.origin.y = layout.size.height - messageFrame.origin.y - messageFrame.size.height - 1.0
|
||||
if inputHeight.isZero {
|
||||
if inputHeight.isZero || layout.isNonExclusive {
|
||||
messageFrame.origin.y += menuHeightWithInset
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager {
|
|||
|
||||
let resolvedWallpaper: Signal<TelegramWallpaper?, NoError>
|
||||
if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 {
|
||||
resolvedWallpaper = cachedWallpaper(account: account, slug: file.slug)
|
||||
resolvedWallpaper = cachedWallpaper(account: account, slug: file.slug, settings: file.settings)
|
||||
|> map { wallpaper in
|
||||
return wallpaper?.wallpaper
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,12 +25,16 @@ public final class CachedWallpaper: PostboxCoding {
|
|||
|
||||
private let collectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 10000, highWaterItemCount: 20000)
|
||||
|
||||
public func cachedWallpaper(account: Account, slug: String) -> Signal<CachedWallpaper?, NoError> {
|
||||
public func cachedWallpaper(account: Account, slug: String, settings: WallpaperSettings?) -> Signal<CachedWallpaper?, NoError> {
|
||||
return account.postbox.transaction { transaction -> Signal<CachedWallpaper?, NoError> in
|
||||
let key = ValueBoxKey(length: 8)
|
||||
key.setInt64(0, value: Int64(bitPattern: slug.persistentHashValue))
|
||||
if let entry = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: ApplicationSpecificItemCacheCollectionId.cachedWallpapers, key: key)) as? CachedWallpaper {
|
||||
return .single(entry)
|
||||
if let settings = settings {
|
||||
return .single(CachedWallpaper(wallpaper: entry.wallpaper.withUpdatedSettings(settings)))
|
||||
} else {
|
||||
return .single(entry)
|
||||
}
|
||||
} else {
|
||||
return getWallpaper(account: account, slug: slug)
|
||||
|> map(Optional.init)
|
||||
|
|
@ -45,7 +49,11 @@ public func cachedWallpaper(account: Account, slug: String) -> Signal<CachedWall
|
|||
if let wallpaper = wallpaper {
|
||||
let entry = CachedWallpaper(wallpaper: wallpaper)
|
||||
transaction.putItemCacheEntry(id: id, entry: entry, collectionSpec: collectionSpec)
|
||||
return entry
|
||||
if let settings = settings {
|
||||
return CachedWallpaper(wallpaper: wallpaper.withUpdatedSettings(settings))
|
||||
} else {
|
||||
return CachedWallpaper(wallpaper: wallpaper)
|
||||
}
|
||||
} else {
|
||||
transaction.removeItemCacheEntry(id: id)
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -311,8 +311,11 @@ private func patternWallpaperDatas(account: Account, accountManager: AccountMana
|
|||
|> take(1)
|
||||
|> mapToSignal { maybeSharedData, maybeData -> Signal<(Data?, Data?, Bool), NoError> in
|
||||
if maybeSharedData.complete {
|
||||
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeSharedData.path), options: [])
|
||||
return .single((nil, loadedData, true))
|
||||
if let loadedData = try? Data(contentsOf: URL(fileURLWithPath: maybeSharedData.path), options: [.mappedRead]) {
|
||||
return .single((nil, loadedData, true))
|
||||
} else {
|
||||
return .single((nil, nil, true))
|
||||
}
|
||||
} else if maybeData.complete {
|
||||
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
|
||||
return .single((nil, loadedData, true))
|
||||
|
|
@ -806,7 +809,7 @@ public func themeImage(account: Account, accountManager: AccountManager, fileRef
|
|||
|> mapToSignal { (fullSizeData, thumbnailData) -> Signal<(PresentationTheme?, UIImage?, Data?), NoError> in
|
||||
if let fullSizeData = fullSizeData, let theme = makePresentationTheme(data: fullSizeData) {
|
||||
if case let .file(file) = theme.chat.defaultWallpaper, file.id == 0 {
|
||||
return cachedWallpaper(account: account, slug: file.slug)
|
||||
return cachedWallpaper(account: account, slug: file.slug, settings: file.settings)
|
||||
|> mapToSignal { wallpaper -> Signal<(PresentationTheme?, UIImage?, Data?), NoError> in
|
||||
if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper {
|
||||
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
||||
|
|
@ -817,11 +820,30 @@ public func themeImage(account: Account, accountManager: AccountManager, fileRef
|
|||
return .complete()
|
||||
}
|
||||
accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData)
|
||||
let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start()
|
||||
|
||||
if let image = UIImage(data: fullSizeData) {
|
||||
if file.isPattern, let color = file.settings.color, let intensity = file.settings.intensity {
|
||||
return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: color, intensity: intensity), complete: true, fetch: true)
|
||||
|> mapToSignal { data in
|
||||
if data.complete, let data = try? Data(contentsOf: URL(fileURLWithPath: data.path)), let image = UIImage(data: data) {
|
||||
return .single((theme, image, thumbnailData))
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
} else if file.settings.blur {
|
||||
return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true)
|
||||
|> mapToSignal { data in
|
||||
if data.complete, let data = try? Data(contentsOf: URL(fileURLWithPath: data.path)), let image = UIImage(data: data) {
|
||||
return .single((theme, image, thumbnailData))
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
} else if let image = UIImage(data: fullSizeData) {
|
||||
return .single((theme, image, thumbnailData))
|
||||
} else {
|
||||
return .single((theme, nil, thumbnailData))
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -907,15 +929,15 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||
let signal: Signal<(UIColor, UIColor, UIColor, UIImage?), NoError>
|
||||
if case let .builtin(theme) = theme {
|
||||
switch theme {
|
||||
case .dayClassic:
|
||||
signal = .single((UIColor(rgb: 0xd6e2ee), UIColor(rgb: 0xffffff), UIColor(rgb: 0xe1ffc7), nil))
|
||||
case .day:
|
||||
signal = .single((.white, UIColor(rgb: 0xd5dde6), accentColor ?? UIColor(rgb: 0x007aff), nil))
|
||||
case .night:
|
||||
signal = .single((.black, UIColor(rgb: 0x1f1f1f), accentColor ?? UIColor(rgb: 0x313131), nil))
|
||||
case .nightAccent:
|
||||
let accentColor = accentColor ?? UIColor(rgb: 0x007aff)
|
||||
signal = .single((accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18), accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25), accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59), nil))
|
||||
case .dayClassic:
|
||||
signal = .single((UIColor(rgb: 0xd6e2ee), UIColor(rgb: 0xffffff), UIColor(rgb: 0xe1ffc7), nil))
|
||||
case .day:
|
||||
signal = .single((.white, UIColor(rgb: 0xd5dde6), accentColor ?? UIColor(rgb: 0x007aff), nil))
|
||||
case .night:
|
||||
signal = .single((.black, UIColor(rgb: 0x1f1f1f), accentColor ?? UIColor(rgb: 0x313131), nil))
|
||||
case .nightAccent:
|
||||
let accentColor = accentColor ?? UIColor(rgb: 0x007aff)
|
||||
signal = .single((accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18), accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25), accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59), nil))
|
||||
}
|
||||
} else {
|
||||
var resource: MediaResource?
|
||||
|
|
@ -926,13 +948,13 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||
}
|
||||
if let resource = resource {
|
||||
signal = telegramThemeData(account: account, accountManager: accountManager, resource: resource, synchronousLoad: false)
|
||||
|> mapToSignal { data -> Signal<(UIColor, UIColor, UIColor, UIImage?), NoError> in
|
||||
if let data = data, let theme = makePresentationTheme(data: data) {
|
||||
var wallpaperSignal: Signal<(UIColor, UIColor, UIColor, UIImage?), NoError> = .complete()
|
||||
let backgroundColor: UIColor
|
||||
let incomingColor = theme.chat.message.incoming.bubble.withoutWallpaper.fill
|
||||
let outgoingColor = theme.chat.message.outgoing.bubble.withoutWallpaper.fill
|
||||
switch theme.chat.defaultWallpaper {
|
||||
|> mapToSignal { data -> Signal<(UIColor, UIColor, UIColor, UIImage?), NoError> in
|
||||
if let data = data, let theme = makePresentationTheme(data: data) {
|
||||
var wallpaperSignal: Signal<(UIColor, UIColor, UIColor, UIImage?), NoError> = .complete()
|
||||
let backgroundColor: UIColor
|
||||
let incomingColor = theme.chat.message.incoming.bubble.withoutWallpaper.fill
|
||||
let outgoingColor = theme.chat.message.outgoing.bubble.withoutWallpaper.fill
|
||||
switch theme.chat.defaultWallpaper {
|
||||
case .builtin:
|
||||
backgroundColor = UIColor(rgb: 0xd6e2ee)
|
||||
case let .color(color):
|
||||
|
|
@ -940,8 +962,12 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||
case .image:
|
||||
backgroundColor = .black
|
||||
case let .file(file):
|
||||
backgroundColor = theme.chatList.backgroundColor
|
||||
wallpaperSignal = cachedWallpaper(account: account, slug: file.slug)
|
||||
if file.isPattern, let color = file.settings.color {
|
||||
backgroundColor = UIColor(rgb: UInt32(bitPattern: color))
|
||||
} else {
|
||||
backgroundColor = theme.chatList.backgroundColor
|
||||
}
|
||||
wallpaperSignal = cachedWallpaper(account: account, slug: file.slug, settings: file.settings)
|
||||
|> mapToSignal { wallpaper in
|
||||
if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper {
|
||||
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
||||
|
|
@ -952,8 +978,23 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||
return .complete()
|
||||
}
|
||||
accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData)
|
||||
let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start()
|
||||
|
||||
if let image = UIImage(data: fullSizeData) {
|
||||
if file.isPattern, let color = file.settings.color, let intensity = file.settings.intensity {
|
||||
return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: color, intensity: intensity), complete: true, fetch: true)
|
||||
|> mapToSignal { _ in
|
||||
return .complete()
|
||||
}
|
||||
} else if file.settings.blur {
|
||||
return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true)
|
||||
|> mapToSignal { _ in
|
||||
if let image = UIImage(data: fullSizeData) {
|
||||
return .single((backgroundColor, incomingColor, outgoingColor, image))
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
} else if let image = UIImage(data: fullSizeData) {
|
||||
return .single((backgroundColor, incomingColor, outgoingColor, image))
|
||||
} else {
|
||||
return .complete()
|
||||
|
|
@ -963,12 +1004,12 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
|
|||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
return .single((backgroundColor, incomingColor, outgoingColor, nil))
|
||||
|> then(wallpaperSignal)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
return .single((backgroundColor, incomingColor, outgoingColor, nil))
|
||||
|> then(wallpaperSignal)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
signal = .never()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue