From 3bf5ad9f8a4a6f5a645f8b7d0e42217f8518284a Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 3 Apr 2023 21:01:16 +0400 Subject: [PATCH] Wallpaper improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 3 + .../Themes/CustomWallpaperPicker.swift | 2 +- .../Sources/Themes/WallpaperGalleryItem.swift | 73 ++----- .../Themes/WallpaperGalleryToolbarNode.swift | 46 +++-- .../Themes/WallpaperOptionButtonNode.swift | 194 +++++++++++++++++- .../Themes/WallpaperPatternPanelNode.swift | 16 +- .../SyncCore_TelegramMediaAction.swift | 5 + .../TelegramEngine/Themes/ChatThemes.swift | 25 +++ .../Themes/TelegramEngineThemes.swift | 4 + .../TelegramCore/Sources/Wallpapers.swift | 8 +- .../Sources/ServiceMessageStrings.swift | 7 + .../Sources/WallpaperUploadManager.swift | 2 +- 12 files changed, 295 insertions(+), 90 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 2a510497e6..3bff09516c 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9123,6 +9123,9 @@ Sorry for the inconvenience."; "Notification.YouChangedWallpaper" = "You set a new background for this chat"; "Notification.Wallpaper.View" = "View Background"; +"Notification.ChangedToSameWallpaper" = "%1$@ set the same background for this chat"; +"Notification.YouChangedToSameWallpaper" = "You set the same background for this chat"; + "Channel.AdminLog.JoinedViaFolderInviteLink" = "%1$@ joined via invite link %2$@ (community)"; "Conversation.OpenChatFolder" = "VIEW CHAT LIST"; diff --git a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift index 53fe225ab9..9a69f600dd 100644 --- a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift +++ b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift @@ -308,7 +308,7 @@ func uploadCustomPeerWallpaper(context: AccountContext, wallpaper: WallpaperGall completion() } - let _ = uploadWallpaper(account: context.account, resource: resource, settings: WallpaperSettings(blur: false, motion: mode.contains(.motion), colors: [], intensity: nil)).start(next: { status in + let _ = uploadWallpaper(account: context.account, resource: resource, settings: WallpaperSettings(blur: false, motion: mode.contains(.motion), colors: [], intensity: nil), forChat: true).start(next: { status in if case let .complete(wallpaper) = status { if case let .file(file) = wallpaper { context.account.postbox.mediaBox.copyResourceData(from: resource.id, to: file.file.resource.id, synchronous: true) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index f40e7d5747..414bce6a22 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -95,18 +95,14 @@ final class WallpaperGalleryItemNode: GalleryItemNode { private let blurredNode: BlurredImageNode let cropNode: WallpaperCropNode - private let cancelButtonBackgroundNode: NavigationBackgroundNode - private var cancelButtonNode: HighlightableButtonNode - - private let shareButtonBackgroundNode: NavigationBackgroundNode - private var shareButtonNode: HighlightableButtonNode + private var cancelButtonNode: WallpaperNavigationButtonNode + private var shareButtonNode: WallpaperNavigationButtonNode private var blurButtonNode: WallpaperOptionButtonNode private var motionButtonNode: WallpaperOptionButtonNode private var patternButtonNode: WallpaperOptionButtonNode private var colorsButtonNode: WallpaperOptionButtonNode - private var playButtonNode: HighlightableButtonNode - private let playButtonBackgroundNode: NavigationBackgroundNode + private var playButtonNode: WallpaperNavigationButtonNode private let messagesContainerNode: ASDisplayNode private var messageNodes: [ListViewItemNode]? @@ -163,21 +159,9 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.colorsButtonNode = WallpaperOptionButtonNode(title: self.presentationData.strings.WallpaperPreview_WallpaperColors, value: .colors(false, [.clear])) - self.cancelButtonBackgroundNode = NavigationBackgroundNode(color: UIColor(white: 0.0, alpha: 0.3)) - self.cancelButtonNode = HighlightableButtonNode() - self.cancelButtonNode.insertSubnode(self.cancelButtonBackgroundNode, at: 0) - self.cancelButtonNode.setAttributedTitle(NSAttributedString(string: self.presentationData.strings.Common_Cancel, font: Font.semibold(15.0), textColor: .white), for: .normal) - self.cancelButtonNode.titleNode.textShadowColor = UIColor(rgb: 0x000000, alpha: 0.1) - - self.shareButtonBackgroundNode = NavigationBackgroundNode(color: UIColor(white: 0.0, alpha: 0.3)) - self.shareButtonNode = HighlightableButtonNode() - self.shareButtonNode.insertSubnode(self.shareButtonBackgroundNode, at: 0) - self.shareButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Links/Share"), color: .white), for: .normal) - - self.playButtonBackgroundNode = NavigationBackgroundNode(color: UIColor(white: 0.0, alpha: 0.3)) - self.playButtonNode = HighlightableButtonNode() - self.playButtonNode.insertSubnode(self.playButtonBackgroundNode, at: 0) - + self.cancelButtonNode = WallpaperNavigationButtonNode(content: .text(self.presentationData.strings.Common_Cancel)) + self.shareButtonNode = WallpaperNavigationButtonNode(content: .icon(image: UIImage(bundleImageName: "Chat/Links/Share"), size: CGSize(width: 28.0, height: 28.0))) + self.playButtonPlayImage = generateImage(CGSize(width: 48.0, height: 48.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.setFillColor(UIColor.white.cgColor) @@ -205,7 +189,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.playButtonRotateImage = generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorRotateIcon"), color: .white) - self.playButtonNode.setImage(self.playButtonPlayImage, for: []) + self.playButtonNode = WallpaperNavigationButtonNode(content: .icon(image: self.playButtonPlayImage, size: CGSize(width: 48.0, height: 48.0))) super.init() @@ -245,7 +229,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.colorsButtonNode.addTarget(self, action: #selector(self.toggleColors), forControlEvents: .touchUpInside) self.playButtonNode.addTarget(self, action: #selector(self.togglePlay), forControlEvents: .touchUpInside) self.cancelButtonNode.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside) - self.shareButtonNode.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside) + self.shareButtonNode.addTarget(self, action: #selector(self.actionPressed), forControlEvents: .touchUpInside) } deinit { @@ -331,9 +315,9 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.patternButtonNode.isSelected = file.isPattern if file.isPattern && file.settings.colors.count >= 3 { - self.playButtonNode.setImage(self.playButtonPlayImage, for: []) + self.playButtonNode.setIcon(self.playButtonPlayImage) } else { - self.playButtonNode.setImage(self.playButtonRotateImage, for: []) + self.playButtonNode.setIcon(self.playButtonRotateImage) } } else if case let .gradient(gradient) = wallpaper { self.nativeNode.isHidden = false @@ -341,9 +325,9 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.patternButtonNode.isSelected = false if gradient.colors.count >= 3 { - self.playButtonNode.setImage(self.playButtonPlayImage, for: []) + self.playButtonNode.setIcon(self.playButtonPlayImage) } else { - self.playButtonNode.setImage(self.playButtonRotateImage, for: []) + self.playButtonNode.setIcon(self.playButtonRotateImage) } } else if case .color = wallpaper { self.nativeNode.isHidden = false @@ -352,19 +336,20 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } else { self.nativeNode.isHidden = true self.patternButtonNode.isSelected = false - self.playButtonNode.setImage(self.playButtonRotateImage, for: []) + self.playButtonNode.setIcon(self.playButtonRotateImage) } case .asset: self.nativeNode._internalUpdateIsSettingUpWallpaper() self.nativeNode.isHidden = true self.patternButtonNode.isSelected = false - self.playButtonNode.setImage(self.playButtonRotateImage, for: []) + self.playButtonNode.setIcon(self.playButtonRotateImage) default: self.nativeNode.isHidden = true self.patternButtonNode.isSelected = false - self.playButtonNode.setImage(self.playButtonRotateImage, for: []) + self.playButtonNode.setIcon(self.playButtonRotateImage) } + var canShare = false switch entry { case let .wallpaper(wallpaper, message): self.initialWallpaper = wallpaper @@ -389,6 +374,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { actionSignal = .single(defaultAction) colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox) isBlurrable = false + canShare = true case .gradient: displaySize = CGSize(width: 1.0, height: 1.0) contentSize = displaySize @@ -399,6 +385,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { actionSignal = .single(defaultAction) colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox) isBlurrable = false + canShare = true case let .file(file): let dimensions = file.file.dimensions ?? PixelDimensions(width: 2000, height: 4000) contentSize = dimensions.cgSize @@ -467,6 +454,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { actionSignal = .single(nil) } else { actionSignal = .single(defaultAction) + canShare = true } colorSignal = .single(UIColor(rgb: 0x000000, alpha: 0.3)) case let .image(representations, _): @@ -590,6 +578,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } self.contentSize = contentSize + self.shareButtonNode.isHidden = !canShare + if self.cropNode.supernode == nil { self.imageNode.contentMode = .scaleAspectFill self.wrapperNode.addSubnode(self.imageNode) @@ -656,16 +646,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode { strongSelf.blurButtonNode.buttonColor = color strongSelf.motionButtonNode.buttonColor = color strongSelf.colorsButtonNode.buttonColor = color - - if color == UIColor(rgb: 0x000000, alpha: 0.3) { - strongSelf.playButtonBackgroundNode.updateColor(color: UIColor(rgb: 0xf2f2f2, alpha: 0.55), transition: .immediate) - strongSelf.cancelButtonBackgroundNode.updateColor(color: UIColor(rgb: 0xf2f2f2, alpha: 0.55), transition: .immediate) - strongSelf.shareButtonBackgroundNode.updateColor(color: UIColor(rgb: 0xf2f2f2, alpha: 0.55), transition: .immediate) - } else { - strongSelf.playButtonBackgroundNode.updateColor(color: color, transition: .immediate) - strongSelf.cancelButtonBackgroundNode.updateColor(color: color, transition: .immediate) - strongSelf.shareButtonBackgroundNode.updateColor(color: color, transition: .immediate) - } })) } else if self.arguments.patternEnabled != previousArguments.patternEnabled { self.patternButtonNode.isSelected = self.arguments.patternEnabled @@ -959,9 +939,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let playFrame = CGRect(origin: CGPoint(x: centerButtonFrame.midX - playButtonSize.width / 2.0, y: centerButtonFrame.midY - playButtonSize.height / 2.0), size: playButtonSize) var playAlpha: CGFloat = 0.0 - var cancelSize = self.cancelButtonNode.measure(layout.size) - cancelSize.width += 16.0 - cancelSize.height = 28.0 + let cancelSize = self.cancelButtonNode.measure(layout.size) let cancelFrame = CGRect(origin: CGPoint(x: 16.0 + offset.x, y: 16.0), size: cancelSize) let shareFrame = CGRect(origin: CGPoint(x: layout.size.width - 16.0 - 28.0 + offset.x, y: 16.0), size: CGSize(width: 28.0, height: 28.0)) @@ -1061,18 +1039,11 @@ final class WallpaperGalleryItemNode: GalleryItemNode { transition.updateAlpha(node: self.colorsButtonNode, alpha: colorsAlpha * alpha) transition.updateFrame(node: self.playButtonNode, frame: playFrame) - transition.updateFrame(node: self.playButtonBackgroundNode, frame: CGRect(origin: CGPoint(), size: playFrame.size)) - self.playButtonBackgroundNode.update(size: playFrame.size, cornerRadius: playFrame.size.height / 2.0, transition: transition) transition.updateAlpha(node: self.playButtonNode, alpha: playAlpha * alpha) transition.updateSublayerTransformScale(node: self.playButtonNode, scale: max(0.1, playAlpha)) transition.updateFrame(node: self.cancelButtonNode, frame: cancelFrame) - transition.updateFrame(node: self.cancelButtonBackgroundNode, frame: CGRect(origin: CGPoint(), size: cancelFrame.size)) - self.cancelButtonBackgroundNode.update(size: cancelFrame.size, cornerRadius: cancelFrame.size.height / 2.0, transition: transition) - transition.updateFrame(node: self.shareButtonNode, frame: shareFrame) - transition.updateFrame(node: self.shareButtonBackgroundNode, frame: CGRect(origin: CGPoint(), size: shareFrame.size)) - self.shareButtonBackgroundNode.update(size: shareFrame.size, cornerRadius: shareFrame.size.height / 2.0, transition: transition) } private func updateMessagesLayout(layout: ContainerViewLayout, offset: CGPoint, transition: ContainedViewLayoutTransition) { diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift index 887b400e5f..e29590d657 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryToolbarNode.swift @@ -34,8 +34,10 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode { private let doneButton = HighlightTrackingButtonNode() private let doneButtonBackgroundNode: NavigationBackgroundNode - private let doneButtonBackgroundView: UIVisualEffectView + private let doneButtonTitleNode: ImmediateTextNode + private let doneButtonVibrancyView: UIVisualEffectView + private let doneButtonVibrancyTitleNode: ImmediateTextNode private let doneButtonSolidBackgroundNode: ASDisplayNode private let doneButtonSolidTitleNode: ImmediateTextNode @@ -49,26 +51,25 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode { self.cancelButtonType = cancelButtonType self.doneButtonType = doneButtonType - self.doneButtonBackgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0xf2f2f2, alpha: 0.55)) + self.doneButtonBackgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0xf2f2f2, alpha: 0.75)) self.doneButtonBackgroundNode.cornerRadius = 14.0 let blurEffect: UIBlurEffect if #available(iOS 13.0, *) { - blurEffect = UIBlurEffect(style: .systemUltraThinMaterialLight) + blurEffect = UIBlurEffect(style: .extraLight) } else { blurEffect = UIBlurEffect(style: .light) } - - self.doneButtonBackgroundView = UIVisualEffectView(effect: blurEffect) - self.doneButtonBackgroundView.clipsToBounds = true - self.doneButtonBackgroundView.layer.cornerRadius = 14.0 - self.doneButtonBackgroundView.isUserInteractionEnabled = false + self.doneButtonVibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blurEffect)) self.doneButtonTitleNode = ImmediateTextNode() self.doneButtonTitleNode.displaysAsynchronously = false - self.doneButtonTitleNode.textShadowColor = UIColor(rgb: 0x000000, alpha: 0.1) self.doneButtonTitleNode.isUserInteractionEnabled = false + self.doneButtonVibrancyTitleNode = ImmediateTextNode() + self.doneButtonVibrancyTitleNode.displaysAsynchronously = false + self.doneButtonVibrancyTitleNode.isUserInteractionEnabled = false + self.doneButtonSolidBackgroundNode = ASDisplayNode() self.doneButtonSolidBackgroundNode.alpha = 0.0 self.doneButtonSolidBackgroundNode.clipsToBounds = true @@ -86,7 +87,9 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode { super.init() self.addSubnode(self.doneButtonBackgroundNode) - self.addSubnode(self.doneButtonTitleNode) + self.doneButtonVibrancyView.contentView.addSubnode(self.doneButtonVibrancyTitleNode) + self.doneButtonBackgroundNode.view.addSubview(self.doneButtonVibrancyView) + self.doneButtonBackgroundNode.addSubnode(self.doneButtonTitleNode) self.addSubnode(self.doneButtonSolidBackgroundNode) self.addSubnode(self.doneButtonSolidTitleNode) @@ -106,8 +109,8 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode { } else { strongSelf.doneButtonBackgroundNode.layer.removeAnimation(forKey: "opacity") strongSelf.doneButtonBackgroundNode.alpha = 0.55 - strongSelf.doneButtonTitleNode.layer.removeAnimation(forKey: "opacity") - strongSelf.doneButtonTitleNode.alpha = 0.55 + strongSelf.doneButtonVibrancyTitleNode.layer.removeAnimation(forKey: "opacity") + strongSelf.doneButtonVibrancyTitleNode.alpha = 0.55 } } else { if strongSelf.isSolid { @@ -118,8 +121,8 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode { } else { strongSelf.doneButtonBackgroundNode.alpha = 1.0 strongSelf.doneButtonBackgroundNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) - strongSelf.doneButtonTitleNode.alpha = 1.0 - strongSelf.doneButtonTitleNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) + strongSelf.doneButtonVibrancyTitleNode.alpha = 1.0 + strongSelf.doneButtonVibrancyTitleNode.layer.animateAlpha(from: 0.55, to: 1.0, duration: 0.2) } } } @@ -143,6 +146,7 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode { transition.updateAlpha(node: self.doneButtonBackgroundNode, alpha: isSolid ? 0.0 : 1.0) transition.updateAlpha(node: self.doneButtonSolidBackgroundNode, alpha: isSolid ? 1.0 : 0.0) transition.updateAlpha(node: self.doneButtonTitleNode, alpha: isSolid ? 0.0 : 1.0) + transition.updateAlpha(node: self.doneButtonVibrancyTitleNode, alpha: isSolid ? 0.0 : 1.0) transition.updateAlpha(node: self.doneButtonSolidTitleNode, alpha: isSolid ? 1.0 : 0.0) } @@ -163,7 +167,8 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode { doneTitle = "" self.doneButton.isUserInteractionEnabled = false } - self.doneButtonTitleNode.attributedText = NSAttributedString(string: doneTitle, font: Font.semibold(17.0), textColor: .white) + self.doneButtonTitleNode.attributedText = NSAttributedString(string: doneTitle, font: Font.semibold(17.0), textColor: UIColor(rgb: 0x000000, alpha: 0.25)) + self.doneButtonVibrancyTitleNode.attributedText = NSAttributedString(string: doneTitle, font: Font.semibold(17.0), textColor: .white) self.doneButtonSolidBackgroundNode.backgroundColor = theme.list.itemCheckColors.fillColor self.doneButtonSolidTitleNode.attributedText = NSAttributedString(string: doneTitle, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor) @@ -177,16 +182,17 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode { self.doneButton.frame = doneFrame self.doneButtonBackgroundNode.frame = doneFrame self.doneButtonBackgroundNode.update(size: doneFrame.size, cornerRadius: 14.0, transition: transition) - self.doneButtonBackgroundView.frame = doneFrame - + self.doneButtonVibrancyView.frame = self.doneButtonBackgroundNode.bounds self.doneButtonSolidBackgroundNode.frame = doneFrame let doneTitleSize = self.doneButtonTitleNode.updateLayout(doneFrame.size) - self.doneButtonTitleNode.frame = CGRect(origin: CGPoint(x: doneFrame.minX + floorToScreenPixels((doneFrame.width - doneTitleSize.width) / 2.0), y: doneFrame.minY + floorToScreenPixels((doneFrame.height - doneTitleSize.height) / 2.0)), size: doneTitleSize) + self.doneButtonTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((doneFrame.width - doneTitleSize.width) / 2.0), y: floorToScreenPixels((doneFrame.height - doneTitleSize.height) / 2.0)), size: doneTitleSize) + + let _ = self.doneButtonVibrancyTitleNode.updateLayout(doneFrame.size) + self.doneButtonVibrancyTitleNode.frame = self.doneButtonTitleNode.frame let _ = self.doneButtonSolidTitleNode.updateLayout(doneFrame.size) - self.doneButtonSolidTitleNode.frame = self.doneButtonTitleNode.frame - + self.doneButtonSolidTitleNode.frame = self.doneButtonTitleNode.frame.offsetBy(dx: doneFrame.minX, dy: doneFrame.minY) } @objc func cancelPressed() { diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift index f7a458ec55..d6a2e55f10 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperOptionButtonNode.swift @@ -34,11 +34,156 @@ private func generateColorsImage(diameter: CGFloat, colors: [UIColor]) -> UIImag }) } +final class WallpaperNavigationButtonNode: HighlightTrackingButtonNode { + enum Content { + case icon(image: UIImage?, size: CGSize) + case text(String) + } + + private let content: Content + + private let backgroundNode: NavigationBackgroundNode + private let vibrancyView: UIVisualEffectView + + private let iconNode: ASImageNode + private let darkIconNode: ASImageNode + + private let textNode: ImmediateTextNode + private let darkTextNode: ImmediateTextNode + + func setIcon(_ image: UIImage?) { + self.iconNode.image = generateTintedImage(image: image, color: .white) + self.darkIconNode.image = generateTintedImage(image: image, color: UIColor(rgb: 0x000000, alpha: 0.25)) + } + + init(content: Content) { + self.content = content + + self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0xf2f2f2, alpha: 0.75)) + self.backgroundNode.cornerRadius = 14.0 + + let blurEffect: UIBlurEffect + if #available(iOS 13.0, *) { + blurEffect = UIBlurEffect(style: .extraLight) + } else { + blurEffect = UIBlurEffect(style: .light) + } + self.vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blurEffect)) + + self.iconNode = ASImageNode() + self.iconNode.displaysAsynchronously = false + self.iconNode.contentMode = .center + + self.darkIconNode = ASImageNode() + self.darkIconNode.displaysAsynchronously = false + self.darkIconNode.contentMode = .center + + var title: String + switch content { + case let .text(text): + title = text + self.backgroundNode.cornerRadius = 14.0 + case let .icon(icon, size): + title = "" + self.backgroundNode.cornerRadius = size.height / 2.0 + + self.iconNode.image = generateTintedImage(image: icon, color: .white) + self.darkIconNode.image = generateTintedImage(image: icon, color: UIColor(rgb: 0x000000, alpha: 0.25)) + } + + self.textNode = ImmediateTextNode() + self.textNode.attributedText = NSAttributedString(string: title, font: Font.semibold(15.0), textColor: .white) + + self.darkTextNode = ImmediateTextNode() + self.darkTextNode.attributedText = NSAttributedString(string: title, font: Font.semibold(15.0), textColor: UIColor(rgb: 0x000000, alpha: 0.25)) + + super.init() + + self.addSubnode(self.backgroundNode) + self.vibrancyView.contentView.addSubnode(self.iconNode) + self.vibrancyView.contentView.addSubnode(self.textNode) + self.backgroundNode.view.addSubview(self.vibrancyView) + self.backgroundNode.addSubnode(self.darkIconNode) + self.backgroundNode.addSubnode(self.darkTextNode) + + self.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity") + strongSelf.backgroundNode.alpha = 0.4 + + strongSelf.iconNode.layer.removeAnimation(forKey: "opacity") + strongSelf.iconNode.alpha = 0.4 + + strongSelf.textNode.layer.removeAnimation(forKey: "opacity") + strongSelf.textNode.alpha = 0.4 + } else { + strongSelf.backgroundNode.alpha = 1.0 + strongSelf.backgroundNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + + strongSelf.iconNode.alpha = 1.0 + strongSelf.iconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + + strongSelf.textNode.alpha = 1.0 + strongSelf.textNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + } + + var buttonColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.3) { + didSet { + if self.buttonColor == UIColor(rgb: 0x000000, alpha: 0.3) { + self.backgroundNode.updateColor(color: UIColor(rgb: 0xf2f2f2, alpha: 0.75), transition: .immediate) + } else { + self.backgroundNode.updateColor(color: self.buttonColor, transition: .immediate) + } + } + } + + private var textSize: CGSize? + override func measure(_ constrainedSize: CGSize) -> CGSize { + switch self.content { + case .text: + let size = self.textNode.updateLayout(constrainedSize) + let _ = self.darkTextNode.updateLayout(constrainedSize) + self.textSize = size + return CGSize(width: ceil(size.width) + 16.0, height: 28.0) + case let .icon(_, size): + return size + } + } + + override func layout() { + super.layout() + + let size = self.bounds.size + self.backgroundNode.frame = self.bounds + self.backgroundNode.update(size: self.backgroundNode.bounds.size, cornerRadius: 14.0, transition: .immediate) + self.vibrancyView.frame = self.bounds + + self.iconNode.frame = self.bounds + self.darkIconNode.frame = self.bounds + + if let textSize = self.textSize { + self.textNode.frame = CGRect(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: floorToScreenPixels((size.height - textSize.height) / 2.0), width: textSize.width, height: textSize.height) + self.darkTextNode.frame = self.textNode.frame + } + } +} + + final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { private let backgroundNode: NavigationBackgroundNode + private let vibrancyView: UIVisualEffectView + private let checkNode: CheckNode + private let darkCheckNode: CheckNode + private let colorNode: ASImageNode + private let textNode: ImmediateTextNode + private let darkTextNode: ImmediateTextNode private var textSize: CGSize? @@ -60,12 +205,14 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { self._value = .colors(newValue, colors) } self.checkNode.setSelected(newValue, animated: false) + self.darkCheckNode.setSelected(newValue, animated: false) } } var title: String { didSet { self.textNode.attributedText = NSAttributedString(string: title, font: Font.medium(13), textColor: .white) + self.darkTextNode.attributedText = NSAttributedString(string: title, font: Font.medium(13), textColor: UIColor(rgb: 0x000000, alpha: 0.25)) } } @@ -76,36 +223,60 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { self.backgroundNode = NavigationBackgroundNode(color: UIColor(rgb: 0xffffff, alpha: 0.4)) self.backgroundNode.cornerRadius = 14.0 - self.checkNode = CheckNode(theme: CheckNodeTheme(backgroundColor: .white, strokeColor: .clear, borderColor: .white, overlayBorder: false, hasInset: false, hasShadow: true, borderWidth: 1.5)) + let blurEffect: UIBlurEffect + if #available(iOS 13.0, *) { + blurEffect = UIBlurEffect(style: .extraLight) + } else { + blurEffect = UIBlurEffect(style: .light) + } + self.vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blurEffect)) + + let darkColor = UIColor(rgb: 0x000000, alpha: 0.25) + + self.checkNode = CheckNode(theme: CheckNodeTheme(backgroundColor: .white, strokeColor: .clear, borderColor: .white, overlayBorder: false, hasInset: false, hasShadow: false, borderWidth: 1.5)) self.checkNode.isUserInteractionEnabled = false + self.darkCheckNode = CheckNode(theme: CheckNodeTheme(backgroundColor: darkColor, strokeColor: .clear, borderColor: darkColor, overlayBorder: false, hasInset: false, hasShadow: false, borderWidth: 1.5)) + self.darkCheckNode.isUserInteractionEnabled = false + self.colorNode = ASImageNode() self.textNode = ImmediateTextNode() + self.textNode.displaysAsynchronously = false self.textNode.attributedText = NSAttributedString(string: title, font: Font.medium(13), textColor: .white) - self.textNode.textShadowColor = UIColor(rgb: 0x000000, alpha: 0.1) + + self.darkTextNode = ImmediateTextNode() + self.darkTextNode.displaysAsynchronously = false + self.darkTextNode.attributedText = NSAttributedString(string: title, font: Font.medium(13), textColor: UIColor(rgb: 0x000000, alpha: 0.25)) super.init() switch value { case let .check(selected): self.checkNode.isHidden = false + self.darkCheckNode.isHidden = false self.colorNode.isHidden = true self.checkNode.selected = selected + self.darkCheckNode.selected = selected case let .color(_, color): self.checkNode.isHidden = true + self.darkCheckNode.isHidden = true self.colorNode.isHidden = false self.colorNode.image = generateFilledCircleImage(diameter: 18.0, color: color) case let .colors(_, colors): self.checkNode.isHidden = true + self.darkCheckNode.isHidden = true self.colorNode.isHidden = false self.colorNode.image = generateColorsImage(diameter: 18.0, colors: colors) } self.addSubnode(self.backgroundNode) - self.addSubnode(self.checkNode) + self.vibrancyView.contentView.addSubnode(self.checkNode) + self.vibrancyView.contentView.addSubnode(self.textNode) + self.backgroundNode.view.addSubview(self.vibrancyView) + self.addSubnode(self.darkCheckNode) + self.addSubnode(self.darkTextNode) self.addSubnode(self.colorNode) - self.addSubnode(self.textNode) self.highligthedChanged = { [weak self] highlighted in if let strongSelf = self { @@ -141,7 +312,7 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { var buttonColor: UIColor = UIColor(rgb: 0x000000, alpha: 0.3) { didSet { if self.buttonColor == UIColor(rgb: 0x000000, alpha: 0.3) { - self.backgroundNode.updateColor(color: UIColor(rgb: 0xf2f2f2, alpha: 0.55), transition: .immediate) + self.backgroundNode.updateColor(color: UIColor(rgb: 0xf2f2f2, alpha: 0.75), transition: .immediate) } else { self.backgroundNode.updateColor(color: self.buttonColor, transition: .immediate) } @@ -214,6 +385,7 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { self._value = .colors(selected, colors) } self.checkNode.setSelected(selected, animated: animated) + self.darkCheckNode.setSelected(selected, animated: animated) } func setEnabled(_ enabled: Bool) { @@ -227,6 +399,7 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { override func measure(_ constrainedSize: CGSize) -> CGSize { let size = self.textNode.updateLayout(constrainedSize) + let _ = self.darkTextNode.updateLayout(constrainedSize) self.textSize = size return CGSize(width: ceil(size.width) + 48.0, height: 30.0) } @@ -236,6 +409,7 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { self.backgroundNode.frame = self.bounds self.backgroundNode.update(size: self.backgroundNode.bounds.size, cornerRadius: 15.0, transition: .immediate) + self.vibrancyView.frame = self.bounds guard let _ = self.textSize else { return @@ -244,12 +418,14 @@ final class WallpaperOptionButtonNode: HighlightTrackingButtonNode { let padding: CGFloat = 6.0 let spacing: CGFloat = 9.0 let checkSize = CGSize(width: 18.0, height: 18.0) - - self.checkNode.frame = CGRect(origin: CGPoint(x: padding, y: padding), size: checkSize) - self.colorNode.frame = CGRect(origin: CGPoint(x: padding, y: padding), size: checkSize) + let checkFrame = CGRect(origin: CGPoint(x: padding, y: padding), size: checkSize) + self.checkNode.frame = checkFrame + self.darkCheckNode.frame = checkFrame + self.colorNode.frame = checkFrame if let textSize = self.textSize { - self.textNode.frame = CGRect(x: max(padding + checkSize.width + spacing, padding + checkSize.width + floor((self.bounds.width - padding - checkSize.width - textSize.width) / 2.0) - 2.0), y: 6.0 + UIScreenPixel, width: textSize.width, height: textSize.height) + self.textNode.frame = CGRect(x: max(padding + checkSize.width + spacing, padding + checkSize.width + floor((self.bounds.width - padding - checkSize.width - textSize.width) / 2.0) - 2.0), y: floorToScreenPixels((self.bounds.height - textSize.height) / 2.0), width: textSize.width, height: textSize.height) + self.darkTextNode.frame = self.textNode.frame } } } diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift index f3bac9db4f..c4be43adef 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift @@ -419,12 +419,16 @@ final class WallpaperPatternPanelNode: ASDisplayNode { func didAppear(initialWallpaper: TelegramWallpaper? = nil, intensity: Int32? = nil) { let wallpaper: TelegramWallpaper? - switch initialWallpaper { - case var .file(file): - file.settings = self.wallpapers[0].settings ?? WallpaperSettings() - wallpaper = .file(file) - default: - wallpaper = self.wallpapers.first + if self.wallpapers.isEmpty { + wallpaper = nil + } else { + switch initialWallpaper { + case var .file(file): + file.settings = self.wallpapers[0].settings ?? WallpaperSettings() + wallpaper = .file(file) + default: + wallpaper = self.wallpapers.first + } } if let wallpaper = wallpaper { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift index 2a3205fb47..22442aba6a 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift @@ -102,6 +102,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case attachMenuBotAllowed case requestedPeer(buttonId: Int32, peerId: PeerId) case setChatWallpaper(wallpaper: TelegramWallpaper) + case setSameChatWallpaper public init(decoder: PostboxDecoder) { let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0) @@ -188,6 +189,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } else { self = .unknown } + case 34: + self = .setSameChatWallpaper default: self = .unknown } @@ -352,6 +355,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case let .setChatWallpaper(wallpaper): encoder.encodeInt32(33, forKey: "_rawValue") encoder.encode(TelegramWallpaperNativeCodable(wallpaper), forKey: "wallpaper") + case .setSameChatWallpaper: + encoder.encodeInt32(34, forKey: "_rawValue") } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift b/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift index f6530b943e..1375319350 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Themes/ChatThemes.swift @@ -152,3 +152,28 @@ func _internal_setChatWallpaper(account: Account, peerId: PeerId, wallpaper: Tel } |> switchToLatest } } + +public enum SetExistingChatWallpaperError { + case generic +} + +func _internal_setExistingChatWallpaper(account: Account, messageId: MessageId) -> Signal { + return account.postbox.transaction { transaction -> Peer? in + return transaction.getPeer(messageId.peerId) + } + |> castError(SetExistingChatWallpaperError.self) + |> mapToSignal { peer -> Signal in + guard let peer = peer, let inputPeer = apiInputPeer(peer) else { + return .complete() + } + let flags: Int32 = 1 << 1 + return account.network.request(Api.functions.messages.setChatWallPaper(flags: flags, peer: inputPeer, wallpaper: nil, settings: nil, id: messageId.id)) + |> `catch` { _ -> Signal in + return .fail(.generic) + } + |> mapToSignal { updates -> Signal in + account.stateManager.addUpdates(updates) + return .complete() + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift b/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift index 96db92da0e..7cfd04ef32 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Themes/TelegramEngineThemes.swift @@ -20,5 +20,9 @@ public extension TelegramEngine { public func setChatWallpaper(peerId: PeerId, wallpaper: TelegramWallpaper?) -> Signal { return _internal_setChatWallpaper(account: self.account, peerId: peerId, wallpaper: wallpaper) } + + public func setExistingChatWallpaper(messageId: MessageId) -> Signal { + return _internal_setExistingChatWallpaper(account: self.account, messageId: messageId) + } } } diff --git a/submodules/TelegramCore/Sources/Wallpapers.swift b/submodules/TelegramCore/Sources/Wallpapers.swift index ea09e64398..c962996430 100644 --- a/submodules/TelegramCore/Sources/Wallpapers.swift +++ b/submodules/TelegramCore/Sources/Wallpapers.swift @@ -118,7 +118,7 @@ private func uploadedWallpaper(postbox: Postbox, network: Network, resource: Med } } -public func uploadWallpaper(account: Account, resource: MediaResource, mimeType: String = "image/jpeg", settings: WallpaperSettings) -> Signal { +public func uploadWallpaper(account: Account, resource: MediaResource, mimeType: String = "image/jpeg", settings: WallpaperSettings, forChat: Bool) -> Signal { return uploadedWallpaper(postbox: account.postbox, network: account.network, resource: resource) |> mapError { _ -> UploadWallpaperError in } |> mapToSignal { result -> Signal<(UploadWallpaperStatus, MediaResource?), UploadWallpaperError> in @@ -130,7 +130,11 @@ public func uploadWallpaper(account: Account, resource: MediaResource, mimeType: case let .progress(progress): return .single((.progress(progress), result.resource)) case let .inputFile(file): - return account.network.request(Api.functions.account.uploadWallPaper(flags: 0, file: file, mimeType: mimeType, settings: apiWallpaperSettings(settings))) + var flags: Int32 = 0 + if forChat { + flags |= 1 << 0 + } + return account.network.request(Api.functions.account.uploadWallPaper(flags: flags, file: file, mimeType: mimeType, settings: apiWallpaperSettings(settings))) |> mapError { _ in return UploadWallpaperError.generic } |> map { wallpaper -> (UploadWallpaperStatus, MediaResource?) in return (.complete(TelegramWallpaper(apiWallpaper: wallpaper)), result.resource) diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index 42260d5ff4..ce0960de10 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -880,6 +880,13 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, let resultTitleString = strings.Notification_ChangedWallpaper(authorName) attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) } + case .setSameChatWallpaper: + if message.author?.id == accountPeerId { + attributedString = NSAttributedString(string: strings.Notification_YouChangedToSameWallpaper, font: titleFont, textColor: primaryTextColor) + } else { + let resultTitleString = strings.Notification_ChangedToSameWallpaper(authorName) + attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) + } case .unknown: attributedString = nil } diff --git a/submodules/TelegramUI/Sources/WallpaperUploadManager.swift b/submodules/TelegramUI/Sources/WallpaperUploadManager.swift index f0f088a636..760ca4b77d 100644 --- a/submodules/TelegramUI/Sources/WallpaperUploadManager.swift +++ b/submodules/TelegramUI/Sources/WallpaperUploadManager.swift @@ -90,7 +90,7 @@ final class WallpaperUploadManagerImpl: WallpaperUploadManager { let sharedContext = self.sharedContext let account = self.account - let uploadSignal = uploadWallpaper(account: account, resource: currentResource, settings: currentWallpaper.settings ?? WallpaperSettings()) + let uploadSignal = uploadWallpaper(account: account, resource: currentResource, settings: currentWallpaper.settings ?? WallpaperSettings(), forChat: false) |> map { result -> UploadWallpaperStatus in switch result { case let .complete(wallpaper):