diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index ba7081411c..a12d158d4b 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -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: diff --git a/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj b/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj index ee56911597..e7dbaa3149 100644 --- a/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj +++ b/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj @@ -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 = ""; }; 09B4A9B723102B7A005C2E08 /* EditThemeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditThemeController.swift; sourceTree = ""; }; 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 = ""; }; @@ -650,6 +652,7 @@ D03E46EB23075FD60049C28B /* WallpaperPatternPanelNode.swift */, D03E46EE23075FD60049C28B /* WallpaperSearchRecentQueries.swift */, 09B4A9B723102B7A005C2E08 /* EditThemeController.swift */, + 091EABA4231DAC7500A0EC14 /* ThemeNameGenerator.swift */, ); path = Themes; sourceTree = ""; @@ -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 */, diff --git a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift index 73486ba993..0509cbf624 100644 --- a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift +++ b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift @@ -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 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 diff --git a/submodules/SettingsUI/Sources/Themes/ThemeNameGenerator.swift b/submodules/SettingsUI/Sources/Themes/ThemeNameGenerator.swift new file mode 100644 index 0000000000..940a8bf0a9 --- /dev/null +++ b/submodules/SettingsUI/Sources/Themes/ThemeNameGenerator.swift @@ -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 "" + } +} diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift index 1f4e7e2ecf..cb04b42476 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift @@ -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 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 = { diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 0555305f43..7c29d9a472 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -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 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) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 4e3fe1233a..16e93cbdcf 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -570,7 +570,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The let resolvedWallpaper: Signal 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 } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift index d1916ca06b..57b381b796 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift @@ -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 diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 293a4adf9f..f549eb900b 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -5717,12 +5717,13 @@ public extension Api { }) } - public static func installTheme(format: String, theme: Api.InputTheme) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func installTheme(flags: Int32, format: String?, theme: Api.InputTheme?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { 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() { diff --git a/submodules/TelegramCore/TelegramCore/Themes.swift b/submodules/TelegramCore/TelegramCore/Themes.swift index a9dd85764a..34a76d6f28 100644 --- a/submodules/TelegramCore/TelegramCore/Themes.swift +++ b/submodules/TelegramCore/TelegramCore/Themes.swift @@ -178,8 +178,21 @@ private func saveUnsaveTheme(account: Account, accountManager: AccountManager, t } |> switchToLatest } -private func installTheme(account: Account, theme: TelegramTheme) -> Signal { - 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 { + 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 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 { +public func applyTheme(accountManager: AccountManager, account: Account, theme: TelegramTheme?, autoNight: Bool = false) -> Signal { return accountManager.transaction { transaction -> Signal 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() } diff --git a/submodules/TelegramCore/TelegramCore/Wallpaper.swift b/submodules/TelegramCore/TelegramCore/Wallpaper.swift index 0d5563cee6..b88fe3c6fa 100644 --- a/submodules/TelegramCore/TelegramCore/Wallpaper.swift +++ b/submodules/TelegramCore/TelegramCore/Wallpaper.swift @@ -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) } } } diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift index 35889835e6..dc9ba24d42 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift @@ -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) ), diff --git a/submodules/TelegramPresentationData/Sources/PresentationData.swift b/submodules/TelegramPresentationData/Sources/PresentationData.swift index dd3cc87ac1..daf5512ea4 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationData.swift @@ -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 diff --git a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift index b83a242581..606f1f62f6 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationTheme.swift @@ -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 } diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift index a7014f0bcc..15efb2bed3 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift @@ -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: diff --git a/submodules/TelegramUI/TelegramUI/AppDelegate.swift b/submodules/TelegramUI/TelegramUI/AppDelegate.swift index d0b273b117..36e3489bdb 100644 --- a/submodules/TelegramUI/TelegramUI/AppDelegate.swift +++ b/submodules/TelegramUI/TelegramUI/AppDelegate.swift @@ -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 { diff --git a/submodules/TelegramUI/TelegramUI/ChatController.swift b/submodules/TelegramUI/TelegramUI/ChatController.swift index 27085ab697..2429cc7261 100644 --- a/submodules/TelegramUI/TelegramUI/ChatController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatController.swift @@ -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 { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift index c58ebb4009..38167f7ae0 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -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 { diff --git a/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift index f92433fc40..11cc202126 100644 --- a/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatSendMessageActionSheetControllerNode.swift @@ -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 } diff --git a/submodules/TelegramUI/TelegramUI/ThemeUpdateManager.swift b/submodules/TelegramUI/TelegramUI/ThemeUpdateManager.swift index 2e2c6d9512..79b60e573e 100644 --- a/submodules/TelegramUI/TelegramUI/ThemeUpdateManager.swift +++ b/submodules/TelegramUI/TelegramUI/ThemeUpdateManager.swift @@ -87,7 +87,7 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager { let resolvedWallpaper: Signal 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 } diff --git a/submodules/WallpaperResources/Sources/WallpaperCache.swift b/submodules/WallpaperResources/Sources/WallpaperCache.swift index 4797136cf7..4b8495c4a5 100644 --- a/submodules/WallpaperResources/Sources/WallpaperCache.swift +++ b/submodules/WallpaperResources/Sources/WallpaperCache.swift @@ -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 { +public func cachedWallpaper(account: Account, slug: String, settings: WallpaperSettings?) -> Signal { return account.postbox.transaction { transaction -> Signal 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 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()