diff --git a/submodules/Display/Display/WallpaperBackgroundNode.swift b/submodules/Display/Display/WallpaperBackgroundNode.swift index 442845769a..c7996502d0 100644 --- a/submodules/Display/Display/WallpaperBackgroundNode.swift +++ b/submodules/Display/Display/WallpaperBackgroundNode.swift @@ -66,10 +66,8 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self.addSubnode(self.contentNode) } - override public func layout() { - super.layout() - self.contentNode.bounds = self.bounds - self.contentNode.position = CGPoint(x: self.bounds.midX, y: self.bounds.midY) - self.updateScale() + public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { + transition.updatePosition(node: self.contentNode, position: CGPoint(x: size.width / 2.0, y: size.height / 2.0)) + transition.updateBounds(node: self.contentNode, bounds: CGRect(origin: CGPoint(), size: size)) } } diff --git a/submodules/Display/Display/WindowContent.swift b/submodules/Display/Display/WindowContent.swift index 12426cf2ef..2a23c98239 100644 --- a/submodules/Display/Display/WindowContent.swift +++ b/submodules/Display/Display/WindowContent.swift @@ -125,8 +125,8 @@ private func encodeText(_ string: String, _ key: Int) -> String { return result } -public func doesViewTreeDisableInteractiveTransitionGestureRecognizer(_ view: UIView) -> Bool { - if view.disablesInteractiveTransitionGestureRecognizer { +public func doesViewTreeDisableInteractiveTransitionGestureRecognizer(_ view: UIView, keyboardOnly: Bool = false) -> Bool { + if view.disablesInteractiveTransitionGestureRecognizer && !keyboardOnly { return true } if view.disablesInteractiveKeyboardGestureRecognizer { @@ -136,7 +136,7 @@ public func doesViewTreeDisableInteractiveTransitionGestureRecognizer(_ view: UI return true } if let superview = view.superview { - return doesViewTreeDisableInteractiveTransitionGestureRecognizer(superview) + return doesViewTreeDisableInteractiveTransitionGestureRecognizer(superview, keyboardOnly: keyboardOnly) } return false } @@ -1065,7 +1065,7 @@ public class Window1 { if let inputHeight = self.windowLayout.inputHeight, !inputHeight.isZero, keyboardGestureBeginLocation.y < self.windowLayout.size.height - inputHeight - (accessoryHeight ?? 0.0) { var enableGesture = true if let view = self.hostView.containerView.hitTest(location, with: nil) { - if doesViewTreeDisableInteractiveTransitionGestureRecognizer(view) { + if doesViewTreeDisableInteractiveTransitionGestureRecognizer(view, keyboardOnly: true) { enableGesture = false } } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index 4292da40e1..a043fda3a9 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -119,6 +119,8 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { super.init(layerBacked: false, dynamicBounce: false) + self.clipsToBounds = true + self.addSubnode(self.containerNode) self.tooltipContainerNode.addSubnode(self.textNode) @@ -134,8 +136,13 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in var updatedBackgroundImage: UIImage? + var backgroundImageContentMode = UIView.ContentMode.scaleAspectFill if currentItem?.wallpaper != item.wallpaper { updatedBackgroundImage = chatControllerBackgroundImage(theme: item.theme, wallpaper: item.wallpaper, mediaBox: item.context.sharedContext.accountManager.mediaBox, knockoutMode: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper) + + if case .gradient = item.wallpaper { + backgroundImageContentMode = .scaleToFill + } } let insets: UIEdgeInsets @@ -222,6 +229,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { if let updatedBackgroundImage = updatedBackgroundImage { strongSelf.backgroundNode.image = updatedBackgroundImage + strongSelf.backgroundNode.contentMode = backgroundImageContentMode } strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor @@ -263,8 +271,9 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil - strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) - strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) + let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + strongSelf.backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0) + strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) diff --git a/submodules/SettingsUI/Sources/Themes/GenerateGradientColors.swift b/submodules/SettingsUI/Sources/Themes/GenerateGradientColors.swift new file mode 100644 index 0000000000..145e065e64 --- /dev/null +++ b/submodules/SettingsUI/Sources/Themes/GenerateGradientColors.swift @@ -0,0 +1,345 @@ +import UIKit +import Display + +private let colorPairs: [(UInt32, UInt32)] = [ + (0xbdc3c7, 0x2c3e50), + (0xee9ca7, 0xffdde1), + (0x2193b0, 0x6dd5ed), + (0xb92b27, 0x1565c0), + (0x373b44, 0x4286f4), + (0xff0099, 0x493240), + (0x8e2de2, 0x4a00e0), + (0x1f4037, 0x99f2c8), + (0xf953c6, 0xb91d73), + (0xc31432, 0x240b36), + (0xf12711, 0xf5af19), + (0x659999, 0xf4791f), + (0xdd3e54, 0x6be585), + (0x8360c3, 0x2ebf91), + (0x544a7d, 0xffd452), + (0x009fff, 0xec2f4b), + (0x654ea3, 0xeaafc8), + (0xff416c, 0xff4b2b), + (0xa8ff78, 0x78ffd6), + (0xed213a, 0x93291e), + (0xfdc830, 0xf37335), + (0x00b4db, 0x0083b0), + (0xffefba, 0xffffff), + (0x005aa7, 0xfffde4), + (0xda4453, 0x89216b), + (0x636363, 0xa2ab58), + (0xad5389, 0x3c1053), + (0xa8c0ff, 0x3f2b96), + (0x333333, 0xdd1818), + (0x4e54c8, 0x8f94fb), + (0xbc4e9c, 0xf80759), + (0x3e5151, 0xdecba4), + (0x11998e, 0x38ef7d), + (0x108dc7, 0xef8e38), + (0xfc5c7d, 0x6a82fb), + (0xfc466b, 0x3f5efb), + (0xc94b4b, 0x4b134f), + (0x23074d, 0xcc5333), + (0xfffbd5, 0xb20a2c), + (0x00b09b, 0x96c93d), + (0xd3cce3, 0xe9e4f0), + (0x3c3b3f, 0x605c3c), + (0xcac531, 0xf3f9a7), + (0x800080, 0xffc0cb), + (0x00f260, 0x0575e6), + (0xfc4a1a, 0xf7b733), + (0x74ebd5, 0xacb6e5), + (0x6d6027, 0xd3cbb8), + (0xe1eec3, 0xf05053), + (0x22c1c3, 0xfdbb2d), + (0xff9966, 0xff5e62), + (0x7f00ff, 0xe100ff), + (0xc9d6ff, 0xe2e2e2), + (0x396afc, 0x2948ff), + (0xd9a7c7, 0xfffcdc), + (0x06beb6, 0x48b1bf), + (0x642b73, 0xc6426e), + (0x1c92d2, 0xf2fcfe), + (0x000000, 0x0f9b0f), + (0x36d1dc, 0x5b86e5), + (0xcb356b, 0xbd3f32), + (0x283c86, 0x45a247), + (0xef3b36, 0xffffff), + (0xc0392b, 0x8e44ad), + (0x159957, 0x155799), + (0x000046, 0x1cb5e0), + (0x007991, 0x78ffd6), + (0x56ccf2, 0x2f80ed), + (0xf2994a, 0xf2c94c), + (0xeb5757, 0x000000), + (0xe44d26, 0xf16529), + (0x4ac29a, 0xbdfff3), + (0xb2fefa, 0x0ed2f7), + (0x30e8bf, 0xff8235), + (0xd66d75, 0xe29587), + (0x20002c, 0xcbb4d4), + (0xc33764, 0x1d2671), + (0xf7971e, 0xffd200), + (0x34e89e, 0x0f3443), + (0x6190e8, 0xa7bfe8), + (0x44a08d, 0x093637), + (0x200122, 0x6f0000), + (0x0575e6, 0x021b79), + (0x4568dc, 0xb06ab3), + (0x43c6ac, 0x191654), + (0x093028, 0x237a57), + (0x43c6ac, 0xf8ffae), + (0xffafbd, 0xffc3a0), + (0xf0f2f0, 0x000c40), + (0xe8cbc0, 0x636fa4), + (0xdce35b, 0x45b649), + (0xc0c0aa, 0x1cefff), + (0xdbe6f6, 0xc5796d), + (0x3494e6, 0xec6ead), + (0x67b26f, 0x4ca2cd), + (0xf3904f, 0x3b4371), + (0xee0979, 0xff6a00), + (0x41295a, 0x2f0743), + (0xf4c4f3, 0xfc67fa), + (0x00c3ff, 0xffff1c), + (0xff7e5f, 0xfeb47b), + (0xfffc00, 0xffffff), + (0xff00cc, 0x333399), + (0xde6161, 0x2657eb), + (0xef32d9, 0x89fffd), + (0x3a6186, 0x89253e), + (0x4ecdc4, 0x556270), + (0xa1ffce, 0xfaffd1), + (0xbe93c5, 0x7bc6cc), + (0xbdc3c7, 0x2c3e50), + (0xffd89b, 0x19547b), + (0x808080, 0x3fada8), + (0xfceabb, 0xf8b500), + (0xf85032, 0xe73827), + (0xf79d00, 0x64f38c), + (0xcb2d3e, 0xef473a), + (0x56ab2f, 0xa8e063), + (0x000428, 0x004e92), + (0x42275a, 0x734b6d), + (0x141e30, 0x243b55), + (0xf00000, 0xdc281e), + (0x2c3e50, 0xfd746c), + (0x2c3e50, 0x4ca1af), + (0xe96443, 0x904e95), + (0x0b486b, 0xf56217), + (0x3a7bd5, 0x3a6073), + (0x00d2ff, 0x928dab), + (0x2196f3, 0xf44336), + (0xff5f6d, 0xffc371), + (0xff4b1f, 0xff9068), + (0x16bffd, 0xcb3066), + (0xeecda3, 0xef629f), + (0x1d4350, 0xa43931), + (0xa80077, 0x66ff00), + (0xf7ff00, 0xdb36a4), + (0xff4b1f, 0x1fddff), + (0xba5370, 0xf4e2d8), + (0xe0eafc, 0xcfdef3), + (0x4ca1af, 0xc4e0e5), + (0x000000, 0x434343), + (0x4b79a1, 0x283e51), + (0x834d9b, 0xd04ed6), + (0x0099f7, 0xf11712), + (0x2980b9, 0x2c3e50), + (0x5a3f37, 0x2c7744), + (0x4da0b0, 0xd39d38), + (0x5614b0, 0xdbd65c), + (0x2f7336, 0xaa3a38), + (0x1e3c72, 0x2a5298), + (0x114357, 0xf29492), + (0xfd746c, 0xff9068), + (0xeacda3, 0xd6ae7b), + (0x6a3093, 0xa044ff), + (0x457fca, 0x5691c8), + (0xb24592, 0xf15f79), + (0xc02425, 0xf0cb35), + (0x403a3e, 0xbe5869), + (0xc2e59c, 0x64b3f4), + (0xffb75e, 0xed8f03), + (0x8e0e00, 0x1f1c18), + (0x76b852, 0x8dc26f), + (0x673ab7, 0x512da8), + (0x00c9ff, 0x92fe9d), + (0xf46b45, 0xeea849), + (0x005c97, 0x363795), + (0xe53935, 0xe35d5b), + (0xfc00ff, 0x00dbde), + (0x2c3e50, 0x3498db), + (0xccccb2, 0x757519), + (0x304352, 0xd7d2cc), + (0xee9ca7, 0xffdde1), + (0xba8b02, 0x181818), + (0x525252, 0x3d72b4), + (0x004ff9, 0xfff94c), + (0x6a9113, 0x141517), + (0xf1f2b5, 0x135058), + (0xd1913c, 0xffd194), + (0x7b4397, 0xdc2430), + (0x8e9eab, 0xeef2f3), + (0x136a8a, 0x267871), + (0x00bf8f, 0x001510), + (0xff0084, 0x33001b), + (0x6441a5, 0x2a0845), + (0xffb347, 0xffcc33), + (0x43cea2, 0x185a9d), + (0xffa17f, 0x00223e), + (0x360033, 0x0b8793), + (0x948e99, 0x2e1437), + (0x1e130c, 0x9a8478), + (0xd38312, 0xa83279), + (0x73c8a9, 0x373b44), + (0xabbaab, 0xffffff), + (0xfdfc47, 0x24fe41), + (0x83a4d4, 0xb6fbff), + (0x485563, 0x29323c), + (0x52c234, 0x061700), + (0xfe8c00, 0xf83600), + (0x00c6ff, 0x0072ff), + (0x70e1f5, 0xffd194), + (0x556270, 0xff6b6b), + (0x9d50bb, 0x6e48aa), + (0x780206, 0x061161), + (0xb3ffab, 0x12fff7), + (0xaaffa9, 0x11ffbd), + (0x000000, 0xe74c3c), + (0xf0c27b, 0x4b1248), + (0xff4e50, 0xf9d423), + (0xadd100, 0x7b920a), + (0xfbd3e9, 0xbb377d), + (0x606c88, 0x3f4c6b), + (0xc9ffbf, 0xffafbd), + (0x649173, 0xdbd5a4), + (0xb993d6, 0x8ca6db), + (0x870000, 0x190a05), + (0x00d2ff, 0x3a7bd5), + (0xd3959b, 0xbfe6ba), + (0xdad299, 0xb0dab9), + (0xf2709c, 0xff9472), + (0xe6dada, 0x274046), + (0x5d4157, 0xa8caba), + (0xddd6f3, 0xfaaca8), + (0x616161, 0x9bc5c3), + (0x50c9c3, 0x96deda), + (0x215f00, 0xe4e4d9), + (0xc21500, 0xffc500), + (0xefefbb, 0xd4d3dd), + (0xffeeee, 0xddefbb), + (0x666600, 0x999966), + (0xde6262, 0xffb88c), + (0xe9d362, 0x333333), + (0xd53369, 0xcbad6d), + (0xa73737, 0x7a2828), + (0xf857a6, 0xff5858), + (0x4b6cb7, 0x182848), + (0xfc354c, 0x0abfbc), + (0x414d0b, 0x727a17), + (0xe43a15, 0xe65245), + (0xc04848, 0x480048), + (0x5f2c82, 0x49a09d), + (0xec6f66, 0xf3a183), + (0x7474bf, 0x348ac7), + (0xece9e6, 0xffffff), + (0xdae2f8, 0xd6a4a4), + (0xed4264, 0xffedbc), + (0xdc2424, 0x4a569d), + (0x24c6dc, 0x514a9d), + (0x283048, 0x859398), + (0x3d7eaa, 0xffe47a), + (0x1cd8d2, 0x93edc7), + (0x232526, 0x414345), + (0x757f9a, 0xd7dde8), + (0x5c258d, 0x4389a2), + (0x134e5e, 0x71b280), + (0x2bc0e4, 0xeaecc6), + (0x085078, 0x85d8ce), + (0x4776e6, 0x8e54e9), + (0x614385, 0x516395), + (0x1f1c2c, 0x928dab), + (0x16222a, 0x3a6073), + (0xff8008, 0xffc837), + (0x1d976c, 0x93f9b9), + (0xeb3349, 0xf45c43), + (0xdd5e89, 0xf7bb97), + (0x4cb8c4, 0x3cd3ad), + (0x1d2b64, 0xf8cdda), + (0xff512f, 0xf09819), + (0x1a2980, 0x26d0ce), + (0xaa076b, 0x61045f), + (0xff512f, 0xdd2476), + (0xf09819, 0xedde5d), + (0x403b4a, 0xe7e9bb), + (0xe55d87, 0x5fc3e4), + (0x003973, 0xe5e5be), + (0x3ca55c, 0xb5ac49), + (0x348f50, 0x56b4d3), + (0xda22ff, 0x9733ee), + (0x02aab0, 0x00cdac), + (0xede574, 0xe1f5c4), + (0xd31027, 0xea384d), + (0x16a085, 0xf4d03f), + (0x603813, 0xb29f94), + (0xe52d27, 0xb31217), + (0xff6e7f, 0xbfe9ff), + (0x314755, 0x26a0da), + (0x2b5876, 0x4e4376), + (0xe65c00, 0xf9d423), + (0x2193b0, 0x6dd5ed), + (0xcc2b5e, 0x753a88), + (0xec008c, 0xfc6767), + (0x1488cc, 0x2b32b2), + (0x00467f, 0xa5cc82), + (0x076585, 0xffffff), + (0xbbd2c5, 0x536976), + (0x9796f0, 0xfbc7d4), + (0xb79891, 0x94716b), + (0x536976, 0x292e49), + (0xacb6e5, 0x86fde8), + (0xffe000, 0x799f0c), + (0x00416a, 0xe4e5e6), + (0xffe259, 0xffa751), + (0x799f0c, 0xacbb78), + (0x334d50, 0xcbcaa5), + (0xf7f8f8, 0xacbb78), + (0xffe000, 0x799f0c), + (0x00416a, 0xe4e5e6) +] + +func generateGradientColors(color: UIColor) -> (UIColor, UIColor) { + var nearest: (colors: (lhs: UInt32, rhs: UInt32), distance: Int32)? + for (lhs, rhs) in colorPairs { + let lhsDistance = color.distance(to: UIColor(rgb: lhs)) + let rhsDistance = color.distance(to: UIColor(rgb: lhs)) + if let currentNearest = nearest { + if lhsDistance < currentNearest.distance || rhsDistance < currentNearest.distance { + if lhsDistance < rhsDistance { + nearest = ((lhs, rhs), lhsDistance) + } else { + nearest = ((rhs, lhs), rhsDistance) + } + } + } else { + if lhsDistance < rhsDistance { + nearest = ((lhs, rhs), lhsDistance) + } else { + nearest = ((rhs, lhs), rhsDistance) + } + } + } + + if let colors = nearest?.colors { + var colorHsv = color.hsv + var similarColorHsv = UIColor(rgb: colors.0).hsv + var complementingColorHsv = UIColor(rgb: colors.1).hsv + + var correction = (similarColorHsv.0 > 0.0 ? colorHsv.0 / similarColorHsv.0 : 1.0, similarColorHsv.1 > 0.0 ? colorHsv.1 / similarColorHsv.1 : 1.0, similarColorHsv.2 > 0.0 ? colorHsv.2 / similarColorHsv.2 : 1.0) + var correctedComplementingColor = UIColor(hue: min(1.0, complementingColorHsv.0 * correction.0), saturation: min(1.0, complementingColorHsv.1 * correction.1), brightness: min(1.0, complementingColorHsv.2 * correction.2), alpha: 1.0) + return (color, correctedComplementingColor) + } else { + return (color, color) + } +} diff --git a/submodules/SettingsUI/Sources/Themes/ThemeNameGenerator.swift b/submodules/SettingsUI/Sources/Themes/GenerateThemeName.swift similarity index 98% rename from submodules/SettingsUI/Sources/Themes/ThemeNameGenerator.swift rename to submodules/SettingsUI/Sources/Themes/GenerateThemeName.swift index f2c391e63d..7c54bdf1f7 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeNameGenerator.swift +++ b/submodules/SettingsUI/Sources/Themes/GenerateThemeName.swift @@ -103,7 +103,7 @@ private let colors: [UInt32: String] = [ 0x54a5f8: "Blue" ] -private let adjectives = [ +private let adjectives: [String] = [ "Ancient", "Antique", "Autumn", @@ -213,7 +213,7 @@ private let adjectives = [ "Winsome" ] -private let subjectives = [ +private let subjectives: [String] = [ "Ambrosia", "Attack", "Avalanche", @@ -301,7 +301,7 @@ 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 let currentNearest = nearest { if distance < currentNearest.distance { nearest = (color, distance) } @@ -312,7 +312,6 @@ func generateThemeName(accentColor: UIColor) -> String { if let color = nearest?.color, let colorName = colors[color]?.capitalized { if arc4random() % 2 == 0 { - return "\((adjectives.randomElement() ?? "").capitalized) \(colorName)" } else { return "\(colorName) \((subjectives.randomElement() ?? "").capitalized)" diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift index 302d7adcae..d416afcb43 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift @@ -143,6 +143,7 @@ final class ThemeAccentColorController: ViewController { self.controllerNode.themeUpdated = { [weak self] theme in if let strongSelf = self { strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: theme, presentationStrings: strongSelf.presentationData.strings)) + strongSelf.segmentedTitleView.theme = theme } } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 0253e3fb73..4e974ca6b7 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -203,19 +203,25 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate case .accent: strongSelf.updateState({ current in var updated = current - updated.accentColor = firstColor + if let firstColor = firstColor { + updated.accentColor = firstColor + } return updated }) case .background: strongSelf.updateState({ current in var updated = current - updated.backgroundColors = (firstColor, secondColor) + if let firstColor = firstColor { + updated.backgroundColors = (firstColor, secondColor) + } return updated }) case .messages: strongSelf.updateState({ current in var updated = current - updated.messagesColors = (firstColor, secondColor) + if let firstColor = firstColor { + updated.messagesColors = (firstColor, secondColor) + } return updated }) } @@ -313,6 +319,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate override func didLoad() { super.didLoad() + self.scrollNode.view.bounces = false self.scrollNode.view.disablesInteractiveTransitionGestureRecognizer = true self.scrollNode.view.showsHorizontalScrollIndicator = false self.scrollNode.view.isPagingEnabled = true @@ -615,7 +622,10 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate self.colorPanelNode.updateLayout(size: colorPanelFrame.size, transition: transition) transition.updateFrame(node: self.messagesContainerNode, frame: CGRect(x: 0.0, y: navigationBarHeight, width: bounds.width, height: bounds.height - bottomInset - navigationBarHeight)) - transition.updateFrame(node: self.chatBackgroundNode, frame: CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height - (colorPanelHeight - colorPanelOffset))) + + let backgroundSize = CGSize(width: bounds.width, height: bounds.height - (colorPanelHeight - colorPanelOffset)) + transition.updateFrame(node: self.chatBackgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundSize)) + self.chatBackgroundNode.updateLayout(size: backgroundSize, transition: transition) var messagesBottomInset: CGFloat = 0.0 if pageControlAlpha > 0.0 { diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift index 15a7a620f4..122fab80a7 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift @@ -212,7 +212,7 @@ public final class ThemePreviewController: ViewController { var resolvedWallpaper: TelegramWallpaper? - let signal = theme + let setup = theme |> mapToSignal { theme -> Signal in guard let theme = theme else { return .complete() @@ -317,7 +317,7 @@ public final class ThemePreviewController: ViewController { } var cancelImpl: (() -> Void)? - let progressSignal = Signal { [weak self] subscriber in + let progress = Signal { [weak self] subscriber in let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { cancelImpl?() })) @@ -331,11 +331,11 @@ public final class ThemePreviewController: ViewController { |> runOn(Queue.mainQueue()) |> delay(0.35, queue: Queue.mainQueue()) - let progressDisposable = progressSignal.start() + let progressDisposable = progress.start() cancelImpl = { disposable.set(nil) } - disposable.set((signal + disposable.set((setup |> afterDisposed { Queue.mainQueue().async { progressDisposable.dispose() @@ -343,7 +343,9 @@ public final class ThemePreviewController: ViewController { } |> deliverOnMainQueue).start(completed: {[weak self] in if let strongSelf = self { - strongSelf.dismiss() + Queue.mainQueue().after(0.3) { + strongSelf.dismiss() + } } })) } diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 79a38434e7..23467efb44 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -279,6 +279,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { override func didLoad() { super.didLoad() + self.scrollNode.view.bounces = false self.scrollNode.view.disablesInteractiveTransitionGestureRecognizer = true self.scrollNode.view.showsHorizontalScrollIndicator = false self.scrollNode.view.isPagingEnabled = true @@ -552,6 +553,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.messagesContainerNode.frame = self.chatContainerNode.bounds self.instantChatBackgroundNode.frame = self.chatContainerNode.bounds + self.instantChatBackgroundNode.updateLayout(size: self.instantChatBackgroundNode.bounds.size, transition: .immediate) self.remoteChatBackgroundNode.frame = self.chatContainerNode.bounds self.blurredNode.frame = self.chatContainerNode.bounds diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 89a7c23cb2..3ca7dbd57b 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -123,6 +123,8 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { super.init(layerBacked: false, dynamicBounce: false) + self.clipsToBounds = true + self.addSubnode(self.containerNode) } @@ -133,8 +135,13 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { return { item, params, neighbors in var updatedBackgroundImage: UIImage? + var backgroundImageContentMode = UIView.ContentMode.scaleAspectFill if currentItem?.wallpaper != item.wallpaper { updatedBackgroundImage = chatControllerBackgroundImage(theme: item.theme, wallpaper: item.wallpaper, mediaBox: item.context.sharedContext.accountManager.mediaBox, knockoutMode: item.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper) + + if case .gradient = item.wallpaper { + backgroundImageContentMode = .scaleToFill + } } let insets: UIEdgeInsets @@ -216,6 +223,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { if let updatedBackgroundImage = updatedBackgroundImage { strongSelf.backgroundNode.image = updatedBackgroundImage + strongSelf.backgroundNode.contentMode = backgroundImageContentMode } strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor @@ -258,8 +266,9 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil - strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) - strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) + let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + strongSelf.backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0) + strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 95d7258394..43ea8533b2 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -477,14 +477,16 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers var themeSpecificAccentColors = current.themeSpecificAccentColors + var themeSpecificBubbleColors = current.themeSpecificBubbleColors themeSpecificAccentColors[currentTheme.index] = color + themeSpecificBubbleColors[currentTheme.index] = nil if let wallpaper = current.themeSpecificChatWallpapers[currentTheme.index], wallpaper.hasWallpaper { } else { themeSpecificChatWallpapers[currentTheme.index] = theme.chat.defaultWallpaper } - return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificBubbleColors: current.themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(theme: current.theme, themeSpecificAccentColors: themeSpecificAccentColors, themeSpecificBubbleColors: themeSpecificBubbleColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, useSystemFont: current.useSystemFont, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) }).start() }, openAccentColorPicker: { themeReference in let controller = ThemeAccentColorController(context: context, themeReference: themeReference, section: .accent) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift index 536dbd05f2..a4fcbebe61 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift @@ -31,10 +31,36 @@ private func textInputBackgroundImage(fieldColor: UIColor, strokeColor: UIColor, } } +private func generateSwatchImage(theme: PresentationTheme, color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 21.0, height: 21.0), rotatedContext: { size, context in + let bounds = CGRect(origin: CGPoint(), size: size) + context.clear(bounds) + + let fillColor = color + var strokeColor: UIColor? + let inputBackgroundColor = theme.chat.inputPanel.inputBackgroundColor + if fillColor.distance(to: inputBackgroundColor) < 200 { + strokeColor = theme.chat.inputPanel.inputStrokeColor + if strokeColor!.distance(to: inputBackgroundColor) < 200 { + strokeColor = theme.chat.inputPanel.inputControlColor + } + } + + context.setFillColor(fillColor.cgColor) + context.setLineWidth(1.0) + + context.fillEllipse(in: bounds) + if let strokeColor = strokeColor { + context.setStrokeColor(strokeColor.cgColor) + context.strokeEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0)) + } + }) +} + private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { private var theme: PresentationTheme - private let swatchNode: ASDisplayNode + private let swatchNode: ASImageNode private let removeButton: HighlightableButtonNode private let textBackgroundNode: ASImageNode private let selectionNode: ASDisplayNode @@ -48,18 +74,14 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { var colorRemoved: (() -> Void)? var colorSelected: (() -> Void)? + private var color: UIColor? + private var isDefault = false { didSet { self.updateSelectionVisibility() } } - var color: UIColor = .white { - didSet { - self.setColor(self.color, update: false) - } - } - var isRemovable: Bool = false { didSet { self.removeButton.isUserInteractionEnabled = self.isRemovable @@ -78,7 +100,9 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { private var previousIsDefault: Bool? private var previousColor: UIColor? - private var validLayout: CGSize? + private var validLayout: (CGSize, Bool)? + + private var returned = false init(theme: PresentationTheme) { self.theme = theme @@ -99,8 +123,9 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { self.prefixNode = ASTextNode() self.prefixNode.attributedText = NSAttributedString(string: "#", font: Font.regular(17.0), textColor: self.theme.chat.inputPanel.inputTextColor) - self.swatchNode = ASDisplayNode() - self.swatchNode.cornerRadius = 10.5 + self.swatchNode = ASImageNode() + self.swatchNode.displaysAsynchronously = false + self.swatchNode.displayWithoutProcessing = true self.removeButton = HighlightableButtonNode() self.removeButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorRemoveIcon"), color: theme.chat.inputPanel.inputControlColor), for: .normal) @@ -150,21 +175,24 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { } func setColor(_ color: UIColor, isDefault: Bool = false, update: Bool = true, ended: Bool = true) { + self.color = color self.isDefault = isDefault let text = color.hexString.uppercased() self.textFieldNode.textField.text = text self.textFieldNode.textField.textColor = isDefault ? self.theme.chat.inputPanel.inputPlaceholderColor : self.theme.chat.inputPanel.inputTextColor - if let size = self.validLayout { + if let (size, _) = self.validLayout { self.updateSelectionLayout(size: size, transition: .immediate) } if update { self.colorChanged?(color, ended) } - self.swatchNode.backgroundColor = color + self.swatchNode.image = generateSwatchImage(theme: self.theme, color: color) } @objc private func removePressed() { self.colorRemoved?() + self.removeButton.layer.removeAnimation(forKey: "opacity") + self.removeButton.alpha = 1.0 } @objc private func tapped() { @@ -178,7 +206,11 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { textField.text = updated.uppercased() textField.textColor = self.theme.chat.inputPanel.inputTextColor - if let size = self.validLayout { + if updated.count == 6, let color = UIColor(hexString: updated) { + self.setColor(color) + } + + if let (size, _) = self.validLayout { self.updateSelectionLayout(size: size, transition: .immediate) } } @@ -190,18 +222,25 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { self.setColor(color) } - if let size = self.validLayout { + if let (size, _) = self.validLayout { self.updateSelectionLayout(size: size, transition: .immediate) } } @objc func textFieldShouldReturn(_ textField: UITextField) -> Bool { + self.returned = true + if let text = self.textFieldNode.textField.text, text.count == 6, let color = UIColor(hexString: text) { + self.setColor(color) + } else { + self.setColor(self.previousColor ?? .black, isDefault: self.previousIsDefault ?? false) + } self.textFieldNode.resignFirstResponder() return false } func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { if self.isSelected { + self.returned = false self.previousColor = self.color self.previousIsDefault = self.isDefault @@ -215,9 +254,7 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { } @objc func textFieldDidEndEditing(_ textField: UITextField) { - if let text = self.textFieldNode.textField.text, text.count == 6, let color = UIColor(hexString: text) { - self.setColor(color) - } else { + if !self.returned { self.setColor(self.previousColor ?? .black, isDefault: self.previousIsDefault ?? false) } } @@ -225,28 +262,31 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { private func updateSelectionLayout(size: CGSize, transition: ContainedViewLayoutTransition) { self.measureNode.attributedText = NSAttributedString(string: self.textFieldNode.textField.text ?? "", font: self.textFieldNode.textField.font) let size = self.measureNode.updateLayout(size) - transition.updateFrame(node: self.selectionNode, frame: CGRect(x: 47.0, y: 6.0, width: max(45.0, size.width), height: 20.0)) + transition.updateFrame(node: self.selectionNode, frame: CGRect(x: self.textFieldNode.frame.minX, y: 6.0, width: max(45.0, size.width), height: 20.0)) } private func updateSelectionVisibility() { self.selectionNode.isHidden = !self.isSelected || self.isDefault } - func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { - self.validLayout = size + func updateLayout(size: CGSize, condensed: Bool, transition: ContainedViewLayoutTransition) { + self.validLayout = (size, condensed) transition.updateFrame(node: self.swatchNode, frame: CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: 21.0, height: 21.0))) + let textPadding: CGFloat = condensed ? 31.0 : 37.0 + transition.updateFrame(node: self.textBackgroundNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)) - transition.updateFrame(node: self.textFieldNode, frame: CGRect(x: 47.0, y: 1.0, width: size.width - 61.0, height: size.height - 2.0)) + transition.updateFrame(node: self.textFieldNode, frame: CGRect(x: textPadding + 10.0, y: 1.0, width: size.width - (21.0 + textPadding), height: size.height - 2.0)) self.updateSelectionLayout(size: size, transition: transition) let prefixSize = self.prefixNode.measure(size) - transition.updateFrame(node: self.prefixNode, frame: CGRect(origin: CGPoint(x: 37.0 - UIScreenPixel, y: 6.0), size: prefixSize)) + transition.updateFrame(node: self.prefixNode, frame: CGRect(origin: CGPoint(x: textPadding - UIScreenPixel, y: 6.0), size: prefixSize)) let removeSize = CGSize(width: 33.0, height: 33.0) - transition.updateFrame(node: self.removeButton, frame: CGRect(origin: CGPoint(x: size.width - removeSize.width, y: 0.0), size: removeSize)) + let removeOffset: CGFloat = condensed ? 3.0 : 0.0 + transition.updateFrame(node: self.removeButton, frame: CGRect(origin: CGPoint(x: size.width - removeSize.width + removeOffset, y: 0.0), size: removeSize)) transition.updateAlpha(node: self.removeButton, alpha: self.isRemovable ? 1.0 : 0.0) } } @@ -280,7 +320,7 @@ final class WallpaperColorPanelNode: ASDisplayNode { private let doneButton: HighlightableButtonNode private let colorPickerNode: WallpaperColorPickerNode - var colorsChanged: ((UIColor, UIColor?, Bool) -> Void)? + var colorsChanged: ((UIColor?, UIColor?, Bool) -> Void)? var colorSelected: (() -> Void)? private var validLayout: CGSize? @@ -341,21 +381,23 @@ final class WallpaperColorPanelNode: ASDisplayNode { strongSelf.updateState({ current in var updated = current updated.selection = .first - if let defaultColor = current.defaultColor { + if let defaultColor = current.defaultColor, updated.secondColor == nil { updated.firstColor = nil } else { updated.firstColor = updated.secondColor ?? updated.firstColor } updated.secondColor = nil return updated - }, animated: strongSelf.state.defaultColor == nil) + }, animated: strongSelf.state.secondColor != nil) } } self.firstColorFieldNode.colorSelected = { [weak self] in if let strongSelf = self { strongSelf.updateState({ current in var updated = current - updated.selection = .first + if updated.selection != .none { + updated.selection = .first + } return updated }) @@ -376,7 +418,9 @@ final class WallpaperColorPanelNode: ASDisplayNode { if let strongSelf = self { strongSelf.updateState({ current in var updated = current - updated.selection = .first + if updated.selection != .none { + updated.selection = .first + } updated.secondColor = nil return updated }) @@ -423,7 +467,7 @@ final class WallpaperColorPanelNode: ASDisplayNode { break } return updated - }, updateLayout: true) + }, updateLayout: false) } } } @@ -443,15 +487,21 @@ final class WallpaperColorPanelNode: ASDisplayNode { self.state = f(self.state) let firstColor: UIColor + var firstColorIsDefault = false if let color = self.state.firstColor { firstColor = color } else if let defaultColor = self.state.defaultColor { firstColor = defaultColor + firstColorIsDefault = true } else { firstColor = .white } let secondColor = self.state.secondColor + if secondColor == nil && previousSecondColor != nil && firstColor == previousSecondColor && animated { + self.animateLeftColorFieldOut() + } + self.firstColorFieldNode.setColor(firstColor, isDefault: self.state.firstColor == nil, update: false) if let secondColor = secondColor { self.secondColorFieldNode.setColor(secondColor, update: false) @@ -473,13 +523,66 @@ final class WallpaperColorPanelNode: ASDisplayNode { } if self.state.firstColor?.rgb != previousFirstColor?.rgb || self.state.secondColor?.rgb != previousSecondColor?.rgb { - self.colorsChanged?(firstColor, secondColor, updateLayout) + self.colorsChanged?(firstColorIsDefault ? nil : firstColor, secondColor, updateLayout) } } + private func animateLeftColorFieldOut() { + guard let size = self.validLayout else { + return + } + + let condensedLayout = size.width < 375.0 + let leftInset: CGFloat + let fieldSpacing: CGFloat + if condensedLayout { + leftInset = 6.0 + fieldSpacing = 40.0 + } else { + leftInset = 15.0 + fieldSpacing = 45.0 + } + let rightInsetWithButton: CGFloat = 42.0 + + let offset: CGFloat = -(self.secondColorFieldNode.frame.minX - leftInset) + + if let fieldSnapshotView = self.firstColorFieldNode.view.snapshotView(afterScreenUpdates: false) { + fieldSnapshotView.frame = self.firstColorFieldNode.frame + self.view.addSubview(fieldSnapshotView) + + fieldSnapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: offset, y: 0.0), duration: 0.3, delay: 0.0, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, force: false) { _ in + fieldSnapshotView.removeFromSuperview() + } + } + + if let buttonSnapshotView = self.swapButton.view.snapshotContentTree() { + buttonSnapshotView.frame = self.swapButton.frame + self.view.addSubview(buttonSnapshotView) + + buttonSnapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: offset, y: 0.0), duration: 0.3, delay: 0.0, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, force: false) { _ in + buttonSnapshotView.removeFromSuperview() + } + } + + self.swapButton.alpha = 0.0 + + let buttonOffset: CGFloat = (rightInsetWithButton - 13.0) / 2.0 + var buttonFrame = self.addButton.frame + buttonFrame.origin.x = size.width + self.addButton.frame = buttonFrame + self.addButton.alpha = 1.0 + + self.firstColorFieldNode.frame = self.secondColorFieldNode.frame + + var fieldFrame = self.secondColorFieldNode.frame + fieldFrame.origin.x = fieldFrame.maxX + fieldSpacing + self.secondColorFieldNode.frame = fieldFrame + } + func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { self.validLayout = size + let condensedLayout = size.width < 375.0 let separatorHeight = UIScreenPixel let topPanelHeight: CGFloat = 47.0 transition.updateFrame(node: self.backgroundNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: topPanelHeight)) @@ -487,11 +590,20 @@ final class WallpaperColorPanelNode: ASDisplayNode { transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(x: 0.0, y: topPanelHeight, width: size.width, height: separatorHeight)) let fieldHeight: CGFloat = 33.0 - let leftInset: CGFloat = 15.0 - let rightInset: CGFloat = 15.0 + let leftInset: CGFloat + let rightInset: CGFloat + let fieldSpacing: CGFloat + if condensedLayout { + leftInset = 6.0 + rightInset = 6.0 + fieldSpacing = 40.0 + } else { + leftInset = 15.0 + rightInset = 15.0 + fieldSpacing = 45.0 + } let rightInsetWithButton: CGFloat = 42.0 - let fieldSpacing: CGFloat = 45.0 - + let buttonSize = CGSize(width: 26.0, height: 26.0) let buttonOffset: CGFloat = (rightInsetWithButton - 13.0) / 2.0 let swapButtonFrame = CGRect(origin: CGPoint(x: self.state.secondColor != nil ? floor((size.width - 26.0) / 2.0) : (self.state.secondColorAvailable ? size.width - rightInsetWithButton + floor((rightInsetWithButton - buttonSize.width) / 2.0) : size.width + buttonOffset), y: floor((topPanelHeight - buttonSize.height) / 2.0)), size: buttonSize) @@ -523,11 +635,11 @@ final class WallpaperColorPanelNode: ASDisplayNode { let firstFieldFrame = CGRect(x: leftInset, y: (topPanelHeight - fieldHeight) / 2.0, width: self.state.secondColor != nil ? floorToScreenPixels((size.width - fieldSpacing) / 2.0) - leftInset : size.width - leftInset - (self.state.secondColorAvailable ? rightInsetWithButton : rightInset), height: fieldHeight) transition.updateFrame(node: self.firstColorFieldNode, frame: firstFieldFrame) - self.firstColorFieldNode.updateLayout(size: firstFieldFrame.size, transition: transition) + self.firstColorFieldNode.updateLayout(size: firstFieldFrame.size, condensed: condensedLayout, transition: transition) let secondFieldFrame = CGRect(x: firstFieldFrame.maxX + fieldSpacing, y: (topPanelHeight - fieldHeight) / 2.0, width: firstFieldFrame.width, height: fieldHeight) transition.updateFrame(node: self.secondColorFieldNode, frame: secondFieldFrame) - self.secondColorFieldNode.updateLayout(size: secondFieldFrame.size, transition: transition) + self.secondColorFieldNode.updateLayout(size: secondFieldFrame.size, condensed: condensedLayout, transition: transition) let colorPickerSize = CGSize(width: size.width, height: size.height - topPanelHeight - separatorHeight) transition.updateFrame(node: self.colorPickerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight + separatorHeight), size: colorPickerSize)) @@ -555,9 +667,9 @@ final class WallpaperColorPanelNode: ASDisplayNode { let firstColor = current.firstColor ?? current.defaultColor if let color = firstColor { updated.firstColor = color - - var hsv = color.hsv - updated.secondColor = UIColor(hue: hsv.0, saturation: hsv.1, brightness: hsv.2 < 0.4 ? hsv.2 + 0.4 : hsv.2 - 0.4 , alpha: 1.0) + updated.secondColor = generateGradientColors(color: color).1 + //var hsv = color.hsv + //updated.secondColor = UIColor(hue: hsv.0, saturation: hsv.1, brightness: hsv.2 < 0.4 ? hsv.2 + 0.4 : hsv.2 - 0.4 , alpha: 1.0) } return updated diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift index cb634251e3..8c2eec13af 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift @@ -342,7 +342,7 @@ public class WallpaperGalleryController: ViewController { let colorPanelNode = WallpaperColorPanelNode(theme: presentationData.theme, strings: presentationData.strings) colorPanelNode.colorsChanged = { [weak self] color, _, ended in - if let strongSelf = self { + if let strongSelf = self , let color = color { strongSelf.updateEntries(color: color, preview: !ended) } } diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift index 3092476207..8d1e6927ac 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift @@ -11,6 +11,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, bubbleColors: (UICo let badgeTextColor: UIColor let secondaryBadgeTextColor: UIColor let outgoingBubbleFillColor: UIColor + var outgoingBubbleFillGradientColor: UIColor let outgoingBubbleHighlightedFillColor: UIColor let outgoingScamColor: UIColor @@ -21,44 +22,54 @@ private func makeDarkPresentationTheme(accentColor: UIColor, bubbleColors: (UICo var accentColor = accentColor - if accentColor.rgb == UIColor.white.rgb { - badgeFillColor = .white - badgeTextColor = .black - secondaryBadgeTextColor = .black + if accentColor.rgb == UIColor.white.rgb && bubbleColors == nil { + badgeFillColor = UIColor(rgb: 0xffffff) + badgeTextColor = UIColor(rgb: 0x000000) + secondaryBadgeTextColor = UIColor(rgb: 0x000000) outgoingBubbleFillColor = UIColor(rgb: 0x313131) + outgoingBubbleFillGradientColor = outgoingBubbleFillColor outgoingBubbleHighlightedFillColor = UIColor(rgb: 0x464646) outgoingScamColor = destructiveColor - outgoingPrimaryTextColor = .white + outgoingPrimaryTextColor = UIColor(rgb: 0xffffff) outgoingSecondaryTextColor = UIColor(rgb: 0xffffff, alpha: 0.5) - outgoingLinkTextColor = .white + outgoingLinkTextColor = UIColor(rgb: 0xffffff) outgoingCheckColor = UIColor(rgb: 0xffffff, alpha: 0.5) } else { badgeFillColor = destructiveColor - badgeTextColor = .white - outgoingBubbleFillColor = accentColor + badgeTextColor = UIColor(rgb: 0xffffff) + + if let bubbleColors = bubbleColors { + outgoingBubbleFillColor = bubbleColors.0 + outgoingBubbleFillGradientColor = bubbleColors.1 ?? bubbleColors.0 + } else { + outgoingBubbleFillColor = accentColor.withMultiplied(hue: 0.966, saturation: 0.61, brightness: 0.98) + outgoingBubbleFillGradientColor = accentColor + } outgoingBubbleHighlightedFillColor = accentColor.withMultipliedBrightnessBy(1.421) - let lightness = accentColor.lightness - if lightness > 0.7 { - outgoingScamColor = .black - - secondaryBadgeTextColor = .black - outgoingPrimaryTextColor = .black + let outgoingBubbleLightnessColor = outgoingBubbleFillColor.mixedWith(outgoingBubbleFillGradientColor, alpha: 0.5) + + if outgoingBubbleLightnessColor.lightness > 0.7 { + outgoingScamColor = UIColor(rgb: 0x000000) + outgoingPrimaryTextColor = UIColor(rgb: 0x000000) outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.5) - outgoingLinkTextColor = .black + outgoingLinkTextColor = UIColor(rgb: 0x000000) outgoingCheckColor = UIColor(rgb: 0x000000, alpha: 0.5) } else { - outgoingScamColor = .white - - secondaryBadgeTextColor = .white - outgoingPrimaryTextColor = .white + outgoingScamColor = UIColor(rgb: 0xffffff) + outgoingPrimaryTextColor = UIColor(rgb: 0xffffff) outgoingSecondaryTextColor = UIColor(rgb: 0xffffff, alpha: 0.5) - outgoingLinkTextColor = .white + outgoingLinkTextColor = UIColor(rgb: 0xffffff) outgoingCheckColor = UIColor(rgb: 0xffffff, alpha: 0.5) - - let hsv = accentColor.hsv - accentColor = UIColor(hue: hsv.0, saturation: hsv.1, brightness: max(hsv.2, 0.55), alpha: 1.0) + } + + if accentColor.lightness > 0.7 { + secondaryBadgeTextColor = UIColor(rgb: 0x000000) + } else { + secondaryBadgeTextColor = UIColor(rgb: 0xffffff) + let accentColorHsv = accentColor.hsv + accentColor = UIColor(hue: accentColorHsv.0, saturation: accentColorHsv.1, brightness: max(accentColorHsv.2, 0.55), alpha: 1.0) } } @@ -221,7 +232,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, bubbleColors: (UICo let message = PresentationThemeChatMessage( incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.4), mediaControlInnerBackgroundColor: UIColor(rgb: 0x262628), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0x000000), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor), - outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white), + outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white), freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f))), infoPrimaryTextColor: .white, infoLinkTextColor: accentColor, diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift index d17bdc34e2..312e80cce8 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift @@ -31,14 +31,38 @@ private func makeDarkPresentationTheme(accentColor: UIColor, bubbleColors: (UICo let mainFreeTextColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.097, brightness: 0.56) - var outgoingBubbleFillColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59) - var outgoingBubbleFillGradientColor = outgoingBubbleFillColor + let outgoingBubbleFillColor: UIColor + let outgoingBubbleFillGradientColor: UIColor + let outgoingBubbleHighlightedFillColor: UIColor + let outgoingScamColor: UIColor + let outgoingPrimaryTextColor: UIColor + let outgoingSecondaryTextColor: UIColor + let outgoingLinkTextColor: UIColor + let outgoingCheckColor: UIColor if let bubbleColors = bubbleColors { outgoingBubbleFillColor = bubbleColors.0 outgoingBubbleFillGradientColor = bubbleColors.1 ?? bubbleColors.0 + } else { + outgoingBubbleFillGradientColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59) + outgoingBubbleFillColor = outgoingBubbleFillGradientColor.withMultiplied(hue: 0.966, saturation: 0.61, brightness: 0.98) } + let outgoingBubbleLightnessColor = outgoingBubbleFillColor.mixedWith(outgoingBubbleFillGradientColor, alpha: 0.5) + if outgoingBubbleLightnessColor.lightness > 0.7 { + outgoingScamColor = UIColor(rgb: 0x000000) + outgoingPrimaryTextColor = UIColor(rgb: 0x000000) + outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.5) + outgoingLinkTextColor = UIColor(rgb: 0x000000) + outgoingCheckColor = UIColor(rgb: 0x000000, alpha: 0.5) + } else { + outgoingScamColor = UIColor(rgb: 0xffffff) + outgoingPrimaryTextColor = UIColor(rgb: 0xffffff) + outgoingSecondaryTextColor = UIColor(rgb: 0xffffff, alpha: 0.5) + outgoingLinkTextColor = UIColor(rgb: 0xffffff) + outgoingCheckColor = UIColor(rgb: 0xffffff, alpha: 0.5) + } + let highlightedIncomingBubbleColor = accentColor.withMultiplied(hue: 1.03, saturation: 0.463, brightness: 0.29) let highlightedOutgoingBubbleColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.609, brightness: 0.63) @@ -206,13 +230,13 @@ private func makeDarkPresentationTheme(accentColor: UIColor, bubbleColors: (UICo let message = PresentationThemeChatMessage( incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), mediaControlInnerBackgroundColor: mainBackgroundColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor), - outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: .white, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white), + outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white), freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), infoPrimaryTextColor: UIColor(rgb: 0xffffff), infoLinkTextColor: accentColor, - outgoingCheckColor: accentColor.withMultiplied(hue: 0.99, saturation: 0.743, brightness: 1.0), + outgoingCheckColor: outgoingCheckColor, mediaDateAndStatusFillColor: UIColor(white: 0.0, alpha: 0.5), - mediaDateAndStatusTextColor: .white, + mediaDateAndStatusTextColor: UIColor(rgb: 0xffffff), shareButtonFillColor: PresentationThemeVariableColor(color: additionalBackgroundColor.withAlphaComponent(0.5)), shareButtonStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), shareButtonForegroundColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2)), diff --git a/submodules/TelegramUI/TelegramUI/ChatController.swift b/submodules/TelegramUI/TelegramUI/ChatController.swift index f024923958..3edce1e253 100644 --- a/submodules/TelegramUI/TelegramUI/ChatController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatController.swift @@ -1610,25 +1610,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } }, scheduleCurrentMessage: { [weak self] in - if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { - let mode: ChatScheduleTimeControllerMode - if peer.id == strongSelf.context.account.peerId { - mode = .reminders - } else { - mode = .scheduledMessages(sendWhenOnlineAvailable: peer.id.namespace == Namespaces.Peer.CloudUser) - } - - let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, completion: { [weak self] scheduleTime in + if let strongSelf = self { + strongSelf.presentScheduleTimePicker(completion: { [weak self] time in if let strongSelf = self { - strongSelf.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleTime, completion: { [weak self] in + strongSelf.chatDisplayNode.sendCurrentMessage(scheduleTime: time, completion: { [weak self] in if let strongSelf = self, !strongSelf.presentationInterfaceState.isScheduledMessages { strongSelf.openScheduledMessages() } }) } }) - strongSelf.chatDisplayNode.dismissInput() - strongSelf.present(controller, in: .window(.root)) } }, sendScheduledMessagesNow: { [weak self] messageIds in if let strongSelf = self { @@ -1642,21 +1633,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } }, editScheduledMessagesTime: { [weak self] messageIds in - if let strongSelf = self, let messageId = messageIds.first, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { - let mode: ChatScheduleTimeControllerMode - if peer.id == strongSelf.context.account.peerId { - mode = .reminders - } else { - mode = .scheduledMessages(sendWhenOnlineAvailable: peer.id.namespace == Namespaces.Peer.CloudUser) - } - + if let strongSelf = self, let messageId = messageIds.first { let _ = (strongSelf.context.account.postbox.transaction { transaction -> Message? in return transaction.getMessage(messageId) } |> deliverOnMainQueue).start(next: { [weak self] message in guard let strongSelf = self, let message = message else { return } - let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, currentTime: message.timestamp, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, completion: { [weak self] scheduleTime in + strongSelf.presentScheduleTimePicker(selectedTime: message.timestamp, completion: { [weak self] time in if let strongSelf = self { var entities: TextEntitiesMessageAttribute? for attribute in message.attributes { @@ -1665,14 +1649,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G break } } - let signal = requestEditMessage(account: strongSelf.context.account, messageId: messageId, text: message.text, media: .keep, entities: entities, disableUrlPreview: false, scheduleTime: scheduleTime) + let signal = requestEditMessage(account: strongSelf.context.account, messageId: messageId, text: message.text, media: .keep, entities: entities, disableUrlPreview: false, scheduleTime: time) strongSelf.editMessageDisposable.set((signal |> deliverOnMainQueue).start(next: { result in }, error: { error in })) } }) - strongSelf.chatDisplayNode.dismissInput() - strongSelf.present(controller, in: .window(.root)) }) } }, performTextSelectionAction: { [weak self] _, text, action in @@ -5511,24 +5493,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } }, presentSchedulePicker: { [weak self] done in - guard let strongSelf = self else { - return - } - let mode: ChatScheduleTimeControllerMode - if peer.id == strongSelf.context.account.peerId { - mode = .reminders - } else { - mode = .scheduledMessages(sendWhenOnlineAvailable: peer.id.namespace == Namespaces.Peer.CloudUser) - } - let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, completion: { [weak self] time in - if let strongSelf = self { - done(time) - if !strongSelf.presentationInterfaceState.isScheduledMessages { - strongSelf.openScheduledMessages() + if let strongSelf = self { + strongSelf.presentScheduleTimePicker(completion: { [weak self] time in + if let strongSelf = self { + done(time) + if !strongSelf.presentationInterfaceState.isScheduledMessages { + strongSelf.openScheduledMessages() + } } - } - }) - strongSelf.present(controller, in: .window(.root)) + }) + } }) } }, openFileGallery: { @@ -5558,24 +5532,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentMultipleFilesDisabled, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }, presentSchedulePicker: { [weak self] done in - guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { - return + if let strongSelf = self { + strongSelf.presentScheduleTimePicker(completion: { [weak self] time in + if let strongSelf = self { + done(time) + if !strongSelf.presentationInterfaceState.isScheduledMessages { + strongSelf.openScheduledMessages() + } + } + }) } - let mode: ChatScheduleTimeControllerMode - if peer.id == strongSelf.context.account.peerId { - mode = .reminders - } else { - mode = .scheduledMessages(sendWhenOnlineAvailable: peer.id.namespace == Namespaces.Peer.CloudUser) - } - let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, completion: { [weak self] time in - if let strongSelf = self { - done(time) - if !strongSelf.presentationInterfaceState.isScheduledMessages { - strongSelf.openScheduledMessages() - } - } - }) - strongSelf.present(controller, in: .window(.root)) }, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in if !inputText.string.isEmpty { //strongSelf.clearInputText() @@ -5758,24 +5724,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }, presentSchedulePicker: { [weak self] done in - guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { - return + if let strongSelf = self { + strongSelf.presentScheduleTimePicker(completion: { [weak self] time in + if let strongSelf = self { + done(time) + if !strongSelf.presentationInterfaceState.isScheduledMessages { + strongSelf.openScheduledMessages() + } + } + }) } - let mode: ChatScheduleTimeControllerMode - if peer.id == strongSelf.context.account.peerId { - mode = .reminders - } else { - mode = .scheduledMessages(sendWhenOnlineAvailable: peer.id.namespace == Namespaces.Peer.CloudUser) - } - let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, completion: { [weak self] time in - if let strongSelf = self { - done(time) - if !strongSelf.presentationInterfaceState.isScheduledMessages { - strongSelf.openScheduledMessages() - } - } - }) - strongSelf.present(controller, in: .window(.root)) }) controller.descriptionGenerator = legacyAssetPickerItemGenerator() controller.completionBlock = { [weak legacyController] signals, silentPosting, scheduleTime in @@ -6048,19 +6006,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G donateSendMessageIntent(account: self.context.account, sharedContext: self.context.sharedContext, peerIds: [peerId]) } else { - let mode: ChatScheduleTimeControllerMode - if peerId == self.context.account.peerId { - mode = .reminders - } else { - mode = .scheduledMessages(sendWhenOnlineAvailable: peerId.namespace == Namespaces.Peer.CloudUser) - } - let controller = ChatScheduleTimeController(context: self.context, mode: mode, minimalTime: self.presentationInterfaceState.slowmodeState?.timeout, dismissByTapOutside: false, completion: { [weak self] time in + self.presentScheduleTimePicker(dismissByTapOutside: false, completion: { [weak self] time in if let strongSelf = self { strongSelf.sendMessages(strongSelf.transformEnqueueMessages(messages, silentPosting: false, scheduleTime: time), commit: true) } }) - self.chatDisplayNode.dismissInput() - self.present(controller, in: .window(.root)) } } @@ -8182,6 +8132,35 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.effectiveNavigationController?.pushViewController(controller) } + private func presentScheduleTimePicker(selectedTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) { + guard case let .peer(peerId) = self.chatLocation else { + return + } + let _ = (self.context.account.viewTracker.peerView(peerId) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] peerView in + guard let strongSelf = self, let peer = peerViewMainPeer(peerView) else { + return + } + var sendWhenOnlineAvailable = false + if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case .present = presence.status { + sendWhenOnlineAvailable = true + } + + let mode: ChatScheduleTimeControllerMode + if peerId == strongSelf.context.account.peerId { + mode = .reminders + } else { + mode = .scheduledMessages(sendWhenOnlineAvailable: sendWhenOnlineAvailable) + } + let controller = ChatScheduleTimeController(context: strongSelf.context, peerId: peerId, mode: mode, currentTime: selectedTime, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, dismissByTapOutside: dismissByTapOutside, completion: { time in + completion(time) + }) + strongSelf.chatDisplayNode.dismissInput() + strongSelf.present(controller, in: .window(.root)) + }) + } + private var effectiveNavigationController: NavigationController? { if let navigationController = self.navigationController as? NavigationController { return navigationController diff --git a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift index 0c81d90476..9527d3ef92 100644 --- a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift @@ -759,6 +759,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } transition.updateFrame(node: self.backgroundNode, frame: contentBounds) + self.backgroundNode.updateLayout(size: contentBounds.size, transition: transition) transition.updateFrame(node: self.historyNodeContainer, frame: contentBounds) transition.updateBounds(node: self.historyNode, bounds: CGRect(origin: CGPoint(), size: contentBounds.size)) transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0)) diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift b/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift index 013a616244..55a5b9ff7f 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageDateHeader.swift @@ -197,8 +197,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode { let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let _ = apply() - self.labelNode.frame = CGRect(origin: CGPoint(), size: size.size) - + self.setNeedsLayout() } diff --git a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift index 6638eb88e1..476b4f34e5 100644 --- a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift @@ -21,7 +21,9 @@ final class ChatScheduleTimeController: ViewController { private var animatedIn = false private let context: AccountContext + private let peerId: PeerId private let mode: ChatScheduleTimeControllerMode + private let editingTime: Bool private let currentTime: Int32? private let minimalTime: Int32? private let dismissByTapOutside: Bool @@ -29,10 +31,12 @@ final class ChatScheduleTimeController: ViewController { private var presentationDataDisposable: Disposable? - init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32? = nil, minimalTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) { + init(context: AccountContext, peerId: PeerId, mode: ChatScheduleTimeControllerMode, currentTime: Int32? = nil, minimalTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) { self.context = context + self.peerId = peerId self.mode = mode - self.currentTime = currentTime + self.editingTime = currentTime != nil + self.currentTime = currentTime != scheduleWhenOnlineTimestamp ? currentTime : nil self.minimalTime = minimalTime self.dismissByTapOutside = dismissByTapOutside self.completion = completion @@ -64,8 +68,27 @@ final class ChatScheduleTimeController: ViewController { override public func loadDisplayNode() { self.displayNode = ChatScheduleTimeControllerNode(context: self.context, mode: self.mode, currentTime: self.currentTime, minimalTime: self.minimalTime, dismissByTapOutside: self.dismissByTapOutside) self.controllerNode.completion = { [weak self] time in - self?.completion(time != scheduleWhenOnlineTimestamp ? time + 5 : time) - self?.dismiss() + guard let strongSelf = self else { + return + } + if time == scheduleWhenOnlineTimestamp { + let _ = (strongSelf.context.account.viewTracker.peerView(strongSelf.peerId) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] peerView in + guard let strongSelf = self, let peer = peerViewMainPeer(peerView) else { + return + } + let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) + if !strongSelf.editingTime, let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case let .present(statusTimestamp) = presence.status, statusTimestamp >= timestamp { + strongSelf.completion(0) + } else { + strongSelf.completion(time) + } + }) + } else { + strongSelf.completion(time + 5) + } + strongSelf.dismiss() } self.controllerNode.dismiss = { [weak self] in self?.presentingViewController?.dismiss(animated: false, completion: nil) diff --git a/submodules/TelegramUI/TelegramUI/DeviceContactDataManager.swift b/submodules/TelegramUI/TelegramUI/DeviceContactDataManager.swift index 89292acabb..b7f37aacbf 100644 --- a/submodules/TelegramUI/TelegramUI/DeviceContactDataManager.swift +++ b/submodules/TelegramUI/TelegramUI/DeviceContactDataManager.swift @@ -106,7 +106,8 @@ private final class DeviceContactDataModernContext: DeviceContactDataContext { CNContactUrlAddressesKey as CNKeyDescriptor, CNContactOrganizationNameKey as CNKeyDescriptor, CNContactJobTitleKey as CNKeyDescriptor, - CNContactDepartmentNameKey as CNKeyDescriptor + CNContactDepartmentNameKey as CNKeyDescriptor, + CNContactNoteKey as CNKeyDescriptor ] guard let contact = try? self.store.unifiedContact(withIdentifier: stableId, keysToFetch: keysToFetch) else { @@ -128,7 +129,8 @@ private final class DeviceContactDataModernContext: DeviceContactDataContext { CNContactUrlAddressesKey as CNKeyDescriptor, CNContactOrganizationNameKey as CNKeyDescriptor, CNContactJobTitleKey as CNKeyDescriptor, - CNContactDepartmentNameKey as CNKeyDescriptor + CNContactDepartmentNameKey as CNKeyDescriptor, + CNContactNoteKey as CNKeyDescriptor ] guard let current = try? self.store.unifiedContact(withIdentifier: stableId, keysToFetch: keysToFetch) else { @@ -254,7 +256,8 @@ private final class DeviceContactDataModernContext: DeviceContactDataContext { CNContactUrlAddressesKey as CNKeyDescriptor, CNContactOrganizationNameKey as CNKeyDescriptor, CNContactJobTitleKey as CNKeyDescriptor, - CNContactDepartmentNameKey as CNKeyDescriptor + CNContactDepartmentNameKey as CNKeyDescriptor, + CNContactNoteKey as CNKeyDescriptor ] guard let current = try? self.store.unifiedContact(withIdentifier: stableId, keysToFetch: keysToFetch) else {