From a6f39e5a77affb59aad2e8b76952a337d90f5fe6 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 21 May 2021 20:42:10 +0400 Subject: [PATCH] UI and theme improvements --- .../Sources/BotCheckoutControllerNode.swift | 6 +- .../Sources/ChatListSearchListPaneNode.swift | 2 +- .../ContainedViewLayoutTransition.swift | 24 + submodules/Display/Source/NavigationBar.swift | 2 +- .../Sources/Items/ChatImageGalleryItem.swift | 2 +- .../Sources/GradientBackground.swift | 8 +- .../Sources/SoftwareGradientBackground.swift | 33 +- .../SearchBarNode/Sources/SearchBarNode.swift | 6 +- .../BubbleSettingsController.swift | 1 - .../TextSizeSelectionController.swift | 1 - .../Themes/CustomWallpaperPicker.swift | 2 +- .../Sources/Themes/EditThemeController.swift | 2 +- .../Themes/SettingsThemeWallpaperNode.swift | 93 ++- .../Themes/ThemeAccentColorController.swift | 48 +- .../ThemeAccentColorControllerNode.swift | 261 ++++----- .../Sources/Themes/ThemeColorPresets.swift | 6 +- .../Sources/Themes/ThemeGridController.swift | 4 +- .../Themes/ThemeGridControllerNode.swift | 34 +- .../Themes/ThemePreviewControllerNode.swift | 18 +- .../Themes/ThemeSettingsChatPreviewItem.swift | 4 +- .../Themes/ThemeSettingsController.swift | 21 +- .../Themes/WallpaperColorPanelNode.swift | 528 +++++++----------- .../Themes/WallpaperGalleryController.swift | 77 +-- .../Sources/Themes/WallpaperGalleryItem.swift | 57 +- .../Themes/WallpaperPatternPanelNode.swift | 9 +- .../Sources/ShareController.swift | 1 - .../SyncCore/Sources/TelegramWallpaper.swift | 166 +++--- submodules/TelegramApi/Sources/Api0.swift | 6 +- submodules/TelegramApi/Sources/Api2.swift | 48 +- submodules/TelegramApi/Sources/Api3.swift | 20 +- .../Sources/ApiUtils/Wallpaper.swift | 72 ++- .../Sources/State/AppUpdate.swift | 2 +- .../TelegramCore/Sources/Wallpapers.swift | 6 +- .../ChatControllerBackgroundNode.swift | 34 +- .../DefaultDarkPresentationTheme.swift | 12 +- .../DefaultDarkTintedPresentationTheme.swift | 10 +- .../Sources/DefaultDayPresentationTheme.swift | 20 +- .../Sources/MakePresentationTheme.swift | 10 +- .../Sources/PresentationData.swift | 39 +- .../Sources/PresentationThemeCodable.swift | 28 +- .../Sources/WallpaperUtils.swift | 4 +- .../TelegramUI/Sources/ChatController.swift | 52 +- .../Sources/ChatControllerNode.swift | 14 - .../ChatHistorySearchContainerNode.swift | 2 +- .../ChatMessageAnimatedStickerItemNode.swift | 38 +- .../Sources/ChatMessageBubbleItemNode.swift | 14 +- .../ChatMessageInstantVideoItemNode.swift | 2 +- ...atMessageInteractiveInstantVideoNode.swift | 6 +- .../Sources/ChatMessageReplyInfoNode.swift | 87 ++- .../Sources/ChatMessageStickerItemNode.swift | 30 +- .../Sources/ChatMessageTransitionNode.swift | 106 ++-- .../ChatRecentActionsControllerNode.swift | 1 - .../Sources/ChatTextInputPanelNode.swift | 19 +- .../LegacyInstantVideoController.swift | 3 + .../TelegramUI/Sources/OpenChatMessage.swift | 2 +- .../TelegramUI/Sources/OpenResolvedUrl.swift | 4 +- .../TelegramUI/Sources/UpgradedAccounts.swift | 4 +- .../Sources/GroupCallContext.swift | 40 +- submodules/TgVoipWebrtc/tgcalls | 2 +- .../Sources/WallpaperBackgroundNode.swift | 78 +-- submodules/WallpaperResources/BUILD | 1 + .../Sources/WallpaperResources.swift | 118 ++-- 62 files changed, 1142 insertions(+), 1208 deletions(-) diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift index 0f128abe6a..8a4d8e6c88 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift @@ -1192,12 +1192,8 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz } if !liabilityNoticeAccepted { - let messageId = self.messageId let botPeer: Signal = self.context.account.postbox.transaction { transaction -> Peer? in - if let message = transaction.getMessage(messageId) { - return message.author - } - return nil + return transaction.getPeer(paymentForm.paymentBotId) } let _ = (combineLatest(ApplicationSpecificNotice.getBotPaymentLiability(accountManager: self.context.sharedContext.accountManager, peerId: paymentForm.paymentBotId), botPeer, self.context.account.postbox.loadedPeerWithId(paymentForm.providerId)) |> deliverOnMainQueue).start(next: { [weak self] value, botPeer, providerPeer in diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 37b6fb3053..726bd80cdd 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -515,7 +515,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { let header = ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) let selection: ChatHistoryMessageSelection = selected.flatMap { .selectable(selected: $0) } ?? .none if let tagMask = tagMask, tagMask != .photoOrVideo { - return ListMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: .builtin(nil, WallpaperSettings())), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), context: context, chatLocation: .peer(peer.peerId), interaction: listInteraction, message: message, selection: selection, displayHeader: enableHeaders && !displayCustomHeader, customHeader: nil, hintIsLink: tagMask == .webPage, isGlobalSearchResult: true) + return ListMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: .builtin(WallpaperSettings())), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), context: context, chatLocation: .peer(peer.peerId), interaction: listInteraction, message: message, selection: selection, displayHeader: enableHeaders && !displayCustomHeader, customHeader: nil, hintIsLink: tagMask == .webPage, isGlobalSearchResult: true) } else { return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, filterData: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(messages: [message], peer: peer, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) } diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index 167f7f5a04..8cced35d9e 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -1318,6 +1318,30 @@ public struct CombinedTransition { completeKey(.positionY, result) }) } + + public func animatePositionAdditive(layer: CALayer, offset: CGPoint, to toOffset: CGPoint = CGPoint(), removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) { + enum Keys: CaseIterable { + case positionX, positionY + } + var remainingKeys = Keys.allCases + var completedValue = true + let completeKey: (Keys, Bool) -> Void = { key, completed in + remainingKeys.removeAll(where: { $0 == key }) + if !completed { + completedValue = false + } + if remainingKeys.isEmpty { + completion?(completedValue) + } + } + + self.horizontal.animatePositionAdditive(layer: layer, offset: CGPoint(x: offset.x, y: 0.0), to: CGPoint(x: toOffset.x, y: 0.0), completion: { result in + completeKey(.positionX, result) + }) + self.vertical.animatePositionAdditive(layer: layer, offset: CGPoint(x: 0.0, y: offset.y), to: CGPoint(x: 0.0, y: toOffset.y), completion: { result in + completeKey(.positionY, result) + }) + } } public extension ContainedViewLayoutTransition { diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 701965ae0b..1d9a359e8e 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -158,7 +158,7 @@ public final class NavigationBackgroundNode: ASDisplayNode { } for subview in effectView.subviews { - if subview.description.contains("_UIVisualEffectSubview") { + if subview.description.contains("VisualEffectSubview") { subview.isHidden = true } } diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 61111be0e2..4ffe741eb3 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -487,7 +487,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { surfaceCopyView.frame = transformedSurfaceFrame } - self.view.insertSubview(copyView, belowSubview: self.scrollNode.view) + //self.view.insertSubview(copyView, belowSubview: self.scrollNode.view) copyView.frame = transformedSelfFrame copyView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) diff --git a/submodules/GradientBackground/Sources/GradientBackground.swift b/submodules/GradientBackground/Sources/GradientBackground.swift index 1e82731be4..392c2f1416 100644 --- a/submodules/GradientBackground/Sources/GradientBackground.swift +++ b/submodules/GradientBackground/Sources/GradientBackground.swift @@ -3,12 +3,6 @@ import UIKit import Display import AsyncDisplayKit -public protocol GradientBackgroundNode: ASDisplayNode { - func updateColors(colors: [UIColor]) - func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) - func animateEvent(transition: ContainedViewLayoutTransition) -} - public func createGradientBackgroundNode() -> GradientBackgroundNode { - return SoftwareGradientBackgroundNode() + return GradientBackgroundNode() } diff --git a/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift index 172653963e..f199180434 100644 --- a/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift +++ b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift @@ -116,7 +116,23 @@ private func generateGradient(size: CGSize, colors: [UIColor], positions: [CGPoi return context.generateImage()! } -final class SoftwareGradientBackgroundNode: ASDisplayNode, GradientBackgroundNode { +public final class GradientBackgroundNode: ASDisplayNode { + private static let basePositions: [CGPoint] = [ + CGPoint(x: 0.80, y: 0.10), + CGPoint(x: 0.60, y: 0.20), + CGPoint(x: 0.35, y: 0.25), + CGPoint(x: 0.25, y: 0.60), + CGPoint(x: 0.20, y: 0.90), + CGPoint(x: 0.40, y: 0.80), + CGPoint(x: 0.65, y: 0.75), + CGPoint(x: 0.75, y: 0.40) + ] + + public static func generatePreview(size: CGSize, colors: [UIColor]) -> UIImage { + let positions = gatherPositions(shiftArray(array: GradientBackgroundNode.basePositions, offset: 0)) + return generateGradient(size: size, colors: colors, positions: positions) + } + private var phase: Int = 0 private let contentView: UIImageView @@ -161,25 +177,14 @@ final class SoftwareGradientBackgroundNode: ASDisplayNode, GradientBackgroundNod let imageSize = size.fitted(CGSize(width: 80.0, height: 80.0)).integralFloor - let basePositions: [CGPoint] = [ - CGPoint(x: 0.80, y: 0.10), - CGPoint(x: 0.60, y: 0.20), - CGPoint(x: 0.35, y: 0.25), - CGPoint(x: 0.25, y: 0.60), - CGPoint(x: 0.20, y: 0.90), - CGPoint(x: 0.40, y: 0.80), - CGPoint(x: 0.65, y: 0.75), - CGPoint(x: 0.75, y: 0.40) - ] - - let positions = gatherPositions(shiftArray(array: basePositions, offset: self.phase % 8)) + let positions = gatherPositions(shiftArray(array: GradientBackgroundNode.basePositions, offset: self.phase % 8)) if let validPhase = self.validPhase { if validPhase != self.phase || self.invalidated { self.validPhase = self.phase self.invalidated = false - let previousPositions = gatherPositions(shiftArray(array: basePositions, offset: validPhase % 8)) + let previousPositions = gatherPositions(shiftArray(array: GradientBackgroundNode.basePositions, offset: validPhase % 8)) if case let .animated(duration, curve) = transition, duration > 0.001 { var images: [UIImage] = [] diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index 6f6c447077..c5e2208fdd 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -1045,7 +1045,9 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { separatorCompleted = true intermediateCompletion() }) - + + self.textBackgroundNode.isHidden = true + self.textBackgroundNode.layer.animateFrame(from: self.textBackgroundNode.frame, to: targetTextBackgroundFrame, duration: duration, timingFunction: timingFunction, removeOnCompletion: false, completion: { _ in textBackgroundCompleted = true intermediateCompletion() @@ -1057,7 +1059,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { transitionBackgroundNode.backgroundColor = node.backgroundNode.backgroundColor transitionBackgroundNode.cornerRadius = node.backgroundNode.cornerRadius self.insertSubnode(transitionBackgroundNode, aboveSubnode: self.textBackgroundNode) - transitionBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration / 2.0, removeOnCompletion: false) + //transitionBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration / 2.0, removeOnCompletion: false) transitionBackgroundNode.layer.animateFrame(from: self.textBackgroundNode.frame, to: targetTextBackgroundFrame, duration: duration, timingFunction: timingFunction, removeOnCompletion: false) let textFieldFrame = self.textField.frame diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index 4cbb2f731e..0ba964acf0 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -70,7 +70,6 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel self.messagesContainerNode.clipsToBounds = true self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) - self.chatBackgroundNode.image = chatControllerBackgroundImage(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false) self.chatBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper) self.toolbarNode = BubbleSettingsToolbarNode(presentationThemeSettings: self.presentationThemeSettings, presentationData: self.presentationData) diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index a611646bfe..065a2222ba 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -82,7 +82,6 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView self.messagesContainerNode.clipsToBounds = true self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) - self.chatBackgroundNode.image = chatControllerBackgroundImage(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false) self.chatBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper) self.toolbarNode = TextSelectionToolbarNode(presentationThemeSettings: self.presentationThemeSettings, presentationData: self.presentationData) diff --git a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift index 8c57830570..c706d19b1a 100644 --- a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift +++ b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift @@ -174,7 +174,7 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE } let apply: () -> Void = { - let settings = WallpaperSettings(blur: mode.contains(.blur), motion: mode.contains(.motion), color: nil, intensity: nil) + let settings = WallpaperSettings(blur: mode.contains(.blur), motion: mode.contains(.motion), colors: [], intensity: nil) let wallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil), TelegramMediaImageRepresentation(dimensions: PixelDimensions(croppedImage.size), resource: resource, progressiveSizes: [], immediateThumbnailData: nil)], settings) updateWallpaper(wallpaper) DispatchQueue.main.async { diff --git a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift index 1adc11460b..e18b60b3b5 100644 --- a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift +++ b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift @@ -514,7 +514,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll let prepare: Signal if let resolvedWallpaper = resolvedWallpaper, case let .file(file) = resolvedWallpaper, resolvedWallpaper.isPattern { let resource = file.file.resource - let representation = CachedPatternWallpaperRepresentation(color: file.settings.color ?? 0xd6e2ee, bottomColor: file.settings.bottomColor, intensity: file.settings.intensity ?? 50, rotation: file.settings.rotation) + let representation = CachedPatternWallpaperRepresentation(color: file.settings.colors.count >= 1 ? file.settings.colors[0] : 0xd6e2ee, bottomColor: file.settings.colors.count >= 2 ? file.settings.colors[1] : file.settings.colors[0], intensity: file.settings.intensity ?? 50, rotation: file.settings.rotation) var data: Data? if let path = context.account.postbox.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) { diff --git a/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift b/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift index 998ed754a7..c5206365d5 100644 --- a/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift +++ b/submodules/SettingsUI/Sources/Themes/SettingsThemeWallpaperNode.swift @@ -35,7 +35,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode { private var arguments: PatternWallpaperArguments? let buttonNode = HighlightTrackingButtonNode() - let backgroundNode = ASDisplayNode() + let backgroundNode = ASImageNode() let imageNode = TransformImageNode() private var gradientNode: GradientBackgroundNode? private let statusNode: RadialStatusNode @@ -74,18 +74,39 @@ final class SettingsThemeWallpaperNode: ASDisplayNode { self.backgroundNode.frame = CGRect(origin: CGPoint(), size: size) self.imageNode.frame = CGRect(origin: CGPoint(), size: size) - if case let .builtin(gradient, _) = wallpaper { - if self.gradientNode == nil { + var colors: [UInt32] = [] + if case let .gradient(value, _) = wallpaper { + colors = value + } else if case let .file(file) = wallpaper { + colors = file.settings.colors + } else if case let .color(color) = wallpaper { + colors = [color] + } + if colors.count >= 3 { + if let gradientNode = self.gradientNode { + gradientNode.updateColors(colors: colors.map { UIColor(rgb: $0) }) + } else { let gradientNode = createGradientBackgroundNode() - if let gradient = gradient { - gradientNode.updateColors(colors: gradient.colors.map { UIColor(rgb: $0) }) - } + gradientNode.isUserInteractionEnabled = false self.gradientNode = gradientNode - self.addSubnode(gradientNode) + gradientNode.updateColors(colors: colors.map { UIColor(rgb: $0) }) + self.insertSubnode(gradientNode, aboveSubnode: self.backgroundNode) + } + + self.backgroundNode.image = nil + } else { + if let gradientNode = self.gradientNode { + self.gradientNode = nil + gradientNode.removeFromSupernode() + } + + if colors.count >= 2 { + self.backgroundNode.image = generateGradientImage(size: CGSize(width: 80.0, height: 80.0), colors: colors.map(UIColor.init(rgb:)), locations: [0.0, 1.0], direction: .vertical) + self.backgroundNode.backgroundColor = nil + } else if colors.count >= 1 { + self.backgroundNode.image = nil + self.backgroundNode.backgroundColor = UIColor(rgb: colors[0]) } - } else if let gradientNode = self.gradientNode { - self.gradientNode = nil - gradientNode.removeFromSupernode() } if let gradientNode = self.gradientNode { @@ -105,72 +126,46 @@ final class SettingsThemeWallpaperNode: ASDisplayNode { self.wallpaper = wallpaper switch wallpaper { case .builtin: - self.imageNode.isHidden = false - self.backgroundNode.isHidden = true + self.imageNode.alpha = 1.0 self.imageNode.setSignal(settingsBuiltinWallpaperImage(account: context.account)) let apply = self.imageNode.asyncLayout()(TransformImageArguments(corners: corners, imageSize: CGSize(), boundingSize: size, intrinsicInsets: UIEdgeInsets())) apply() - case let .color(color): - let theme = context.sharedContext.currentPresentationData.with { $0 }.theme - let uiColor = UIColor(rgb: color) - if uiColor.distance(to: theme.list.itemBlocksBackgroundColor) < 200 { - self.imageNode.isHidden = false - self.backgroundNode.isHidden = true - self.imageNode.setSignal(whiteColorImage(theme: theme, color: uiColor)) - let apply = self.imageNode.asyncLayout()(TransformImageArguments(corners: corners, imageSize: CGSize(), boundingSize: size, intrinsicInsets: UIEdgeInsets())) - apply() - } else { - self.imageNode.isHidden = true - self.backgroundNode.isHidden = false - self.backgroundNode.backgroundColor = UIColor(rgb: color) - } - case let .gradient(topColor, bottomColor, _): - self.imageNode.isHidden = false - self.backgroundNode.isHidden = true - self.imageNode.setSignal(gradientImage([UIColor(rgb: topColor), UIColor(rgb: bottomColor)])) - let apply = self.imageNode.asyncLayout()(TransformImageArguments(corners: corners, imageSize: CGSize(), boundingSize: size, intrinsicInsets: UIEdgeInsets())) - apply() case let .image(representations, _): - self.imageNode.isHidden = false - self.backgroundNode.isHidden = true - let convertedRepresentations: [ImageRepresentationWithReference] = representations.map({ ImageRepresentationWithReference(representation: $0, reference: .wallpaper(wallpaper: nil, resource: $0.resource)) }) + self.imageNode.alpha = 10 self.imageNode.setSignal(wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, thumbnail: true, autoFetchFullSize: true, synchronousLoad: synchronousLoad)) let apply = self.imageNode.asyncLayout()(TransformImageArguments(corners: corners, imageSize: largestImageRepresentation(representations)!.dimensions.cgSize.aspectFilled(size), boundingSize: size, intrinsicInsets: UIEdgeInsets())) apply() case let .file(file): - self.imageNode.isHidden = false - let convertedRepresentations : [ImageRepresentationWithReference] = file.file.previewRepresentations.map { ImageRepresentationWithReference(representation: $0, reference: .wallpaper(wallpaper: .slug(file.slug), resource: $0.resource)) } let imageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError> if wallpaper.isPattern { - self.backgroundNode.isHidden = false - var patternColors: [UIColor] = [] var patternColor = UIColor(rgb: 0xd6e2ee, alpha: 0.5) var patternIntensity: CGFloat = 0.5 - if let color = file.settings.color { + if !file.settings.colors.isEmpty { if let intensity = file.settings.intensity { patternIntensity = CGFloat(intensity) / 100.0 } - patternColor = UIColor(rgb: color, alpha: patternIntensity) + patternColor = UIColor(rgb: file.settings.colors[0], alpha: patternIntensity) patternColors.append(patternColor) - if let bottomColor = file.settings.bottomColor { - patternColors.append(UIColor(rgb: bottomColor, alpha: patternIntensity)) + if file.settings.colors.count >= 2 { + patternColors.append(UIColor(rgb: file.settings.colors[1], alpha: patternIntensity)) } } - - self.backgroundNode.backgroundColor = patternColor - self.arguments = PatternWallpaperArguments(colors: patternColors, rotation: file.settings.rotation) + + self.imageNode.alpha = CGFloat(file.settings.intensity ?? 50) / 100.0 + + self.arguments = PatternWallpaperArguments(colors: [.clear], rotation: nil, customPatternColor: UIColor(white: 0.0, alpha: 0.3)) imageSignal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .thumbnail, autoFetchFullSize: true) } else { - self.backgroundNode.isHidden = true - + self.imageNode.alpha = 1.0 + imageSignal = wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, thumbnail: true, autoFetchFullSize: true, synchronousLoad: synchronousLoad) } self.imageNode.setSignal(imageSignal, attemptSynchronously: synchronousLoad) @@ -178,6 +173,8 @@ final class SettingsThemeWallpaperNode: ASDisplayNode { let dimensions = file.file.dimensions ?? PixelDimensions(width: 100, height: 100) let apply = self.imageNode.asyncLayout()(TransformImageArguments(corners: corners, imageSize: dimensions.cgSize.aspectFilled(size), boundingSize: size, intrinsicInsets: UIEdgeInsets(), custom: self.arguments)) apply() + default: + break } } else if let wallpaper = self.wallpaper { switch wallpaper { diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift index 9860c35054..8af418e841 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift @@ -154,20 +154,16 @@ final class ThemeAccentColorController: ViewController { }, apply: { [weak self] state, serviceBackgroundColor in if let strongSelf = self { let context = strongSelf.context - let initialAccentColor = strongSelf.initialAccentColor let autoNightModeTriggered = strongSelf.presentationData.autoNightModeTriggered var coloredWallpaper: TelegramWallpaper? - if let backgroundColors = state.backgroundColors { - let color = backgroundColors.0.argb - let bottomColor = backgroundColors.1.flatMap { $0.argb } - + if !state.backgroundColors.isEmpty { if let patternWallpaper = state.patternWallpaper { - coloredWallpaper = patternWallpaper.withUpdatedSettings(WallpaperSettings(motion: state.motion, color: color, bottomColor: bottomColor, intensity: state.patternIntensity, rotation: state.rotation)) - } else if let bottomColor = bottomColor { - coloredWallpaper = .gradient(color, bottomColor, WallpaperSettings(motion: state.motion, rotation: state.rotation)) + coloredWallpaper = patternWallpaper.withUpdatedSettings(WallpaperSettings(motion: state.motion, colors: state.backgroundColors, intensity: state.patternIntensity, rotation: state.rotation)) + } else if state.backgroundColors.count >= 2 { + coloredWallpaper = .gradient(state.backgroundColors, WallpaperSettings(motion: state.motion, rotation: state.rotation)) } else { - coloredWallpaper = .color(color) + coloredWallpaper = .color(state.backgroundColors[0]) } } @@ -175,9 +171,9 @@ final class ThemeAccentColorController: ViewController { let apply: Signal let prepareWallpaper: Signal - if let patternWallpaper = state.patternWallpaper, case let .file(file) = patternWallpaper, let backgroundColors = state.backgroundColors { + if let patternWallpaper = state.patternWallpaper, case let .file(file) = patternWallpaper, !state.backgroundColors.isEmpty { let resource = file.file.resource - let representation = CachedPatternWallpaperRepresentation(color: backgroundColors.0.argb, bottomColor: backgroundColors.1.flatMap { $0.argb }, intensity: state.patternIntensity, rotation: state.rotation) + let representation = CachedPatternWallpaperRepresentation(color: state.backgroundColors.count >= 1 ? state.backgroundColors[0] : 0, bottomColor: state.backgroundColors.count >= 2 ? state.backgroundColors[1] : 0, intensity: state.patternIntensity, rotation: state.rotation) var data: Data? if let path = strongSelf.context.account.postbox.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) { @@ -396,7 +392,7 @@ final class ThemeAccentColorController: ViewController { let accentColor: UIColor var initialWallpaper: TelegramWallpaper? - var backgroundColors: (UIColor, UIColor?)? + var backgroundColors: [UInt32] = [] var patternWallpaper: TelegramWallpaper? var patternIntensity: Int32 = 50 var motion = false @@ -411,27 +407,27 @@ final class ThemeAccentColorController: ViewController { if case let .file(file) = wallpaper, wallpaper.isPattern { var patternColor = UIColor(rgb: 0xd6e2ee, alpha: 0.4) var bottomColor: UIColor? - if let color = file.settings.color { + if !file.settings.colors.isEmpty { if let intensity = file.settings.intensity { patternIntensity = intensity } - patternColor = UIColor(rgb: color) - if let bottomColorValue = file.settings.bottomColor { - bottomColor = UIColor(rgb: bottomColorValue) + patternColor = UIColor(rgb: file.settings.colors[0]) + if file.settings.colors.count >= 2 { + bottomColor = UIColor(rgb: file.settings.colors[1]) } } patternWallpaper = wallpaper - backgroundColors = (patternColor, bottomColor) + backgroundColors = file.settings.colors motion = file.settings.motion rotation = file.settings.rotation ?? 0 } else if case let .color(color) = wallpaper { - backgroundColors = (UIColor(rgb: color), nil) - } else if case let .gradient(topColor, bottomColor, settings) = wallpaper { - backgroundColors = (UIColor(rgb: topColor), UIColor(rgb: bottomColor)) + backgroundColors = [color] + } else if case let .gradient(colors, settings) = wallpaper { + backgroundColors = colors motion = settings.motion rotation = settings.rotation ?? 0 } else { - backgroundColors = nil + backgroundColors = [] } } @@ -439,7 +435,7 @@ final class ThemeAccentColorController: ViewController { var wallpaper: TelegramWallpaper func extractBuiltinWallpaper(_ currentWallpaper: TelegramWallpaper) { - if case let .builtin(_, settings) = currentWallpaper { + if case let .builtin(settings) = currentWallpaper { var defaultPatternWallpaper: TelegramWallpaper? for wallpaper in wallpapers { @@ -450,7 +446,7 @@ final class ThemeAccentColorController: ViewController { } if let defaultPatternWallpaper = defaultPatternWallpaper { - wallpaper = defaultPatternWallpaper.withUpdatedSettings(WallpaperSettings(blur: settings.blur, motion: settings.motion, color: 0xd6e2ee, bottomColor: nil, intensity: 40, rotation: nil)) + wallpaper = defaultPatternWallpaper.withUpdatedSettings(WallpaperSettings(blur: settings.blur, motion: settings.motion, colors: [0xd6e2ee], intensity: 40, rotation: nil)) } } } @@ -475,7 +471,7 @@ final class ThemeAccentColorController: ViewController { } if let initialBackgroundColor = strongSelf.initialBackgroundColor { - backgroundColors = (initialBackgroundColor, nil) + backgroundColors = [initialBackgroundColor.rgb] } else { extractWallpaperParameters(wallpaper) } @@ -539,7 +535,7 @@ final class ThemeAccentColorController: ViewController { } if let initialBackgroundColor = strongSelf.initialBackgroundColor { - backgroundColors = (initialBackgroundColor, nil) + backgroundColors = [initialBackgroundColor.rgb] } else { extractWallpaperParameters(wallpaper) } @@ -601,7 +597,7 @@ final class ThemeAccentColorController: ViewController { } } else { accentColor = defaultDayAccentColor - backgroundColors = nil + backgroundColors = [] messageColors = nil } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 6c3af69366..239a149ae3 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -12,6 +12,7 @@ import ChatListUI import AccountContext import WallpaperResources import PresentationDataUtils +import WallpaperBackgroundNode private func generateMaskImage(color: UIColor) -> UIImage? { return generateImage(CGSize(width: 1.0, height: 80.0), opaque: false, rotatedContext: { size, context in @@ -41,7 +42,7 @@ struct ThemeColorState { var accentColor: UIColor var initialWallpaper: TelegramWallpaper? - var backgroundColors: (UIColor, UIColor?)? + var backgroundColors: [UInt32] fileprivate var preview: Bool fileprivate var previousPatternWallpaper: TelegramWallpaper? @@ -60,7 +61,7 @@ struct ThemeColorState { self.displayPatternPanel = false self.accentColor = .clear self.initialWallpaper = nil - self.backgroundColors = nil + self.backgroundColors = [] self.preview = false self.previousPatternWallpaper = nil self.patternWallpaper = nil @@ -71,7 +72,7 @@ struct ThemeColorState { self.rotation = 0 } - init(section: ThemeColorSection, accentColor: UIColor, initialWallpaper: TelegramWallpaper?, backgroundColors: (UIColor, UIColor?)?, patternWallpaper: TelegramWallpaper?, patternIntensity: Int32, motion: Bool, defaultMessagesColor: UIColor?, messagesColors: (UIColor, UIColor?)?, rotation: Int32 = 0) { + init(section: ThemeColorSection, accentColor: UIColor, initialWallpaper: TelegramWallpaper?, backgroundColors: [UInt32], patternWallpaper: TelegramWallpaper?, patternIntensity: Int32, motion: Bool, defaultMessagesColor: UIColor?, messagesColors: (UIColor, UIColor?)?, rotation: Int32 = 0) { self.section = section self.colorPanelCollapsed = false self.displayPatternPanel = false @@ -104,20 +105,10 @@ struct ThemeColorState { if self.rotation != otherState.rotation { return false } - if let lhsBackgroundColors = self.backgroundColors, let rhsBackgroundColors = otherState.backgroundColors { - if lhsBackgroundColors.0 != rhsBackgroundColors.0 { - return false - } - if let lhsSecondColor = lhsBackgroundColors.1, let rhsSecondColor = rhsBackgroundColors.1 { - if lhsSecondColor != rhsSecondColor { - return false - } - } else if (lhsBackgroundColors.1 == nil) != (rhsBackgroundColors.1 == nil) { - return false - } - } else if (self.backgroundColors == nil) != (otherState.backgroundColors == nil) { + if self.backgroundColors != otherState.backgroundColors { return false } + if let lhsMessagesColors = self.messagesColors, let rhsMessagesColors = otherState.messagesColors { if lhsMessagesColors.0 != rhsMessagesColors.0 { return false @@ -137,11 +128,11 @@ struct ThemeColorState { } private func calcPatternColors(for state: ThemeColorState) -> [UIColor] { - if let backgroundColors = state.backgroundColors { + if state.backgroundColors.count >= 1 { let patternIntensity = CGFloat(state.patternIntensity) / 100.0 - let topPatternColor = backgroundColors.0.withAlphaComponent(patternIntensity) - if let bottomColor = backgroundColors.1 { - let bottomPatternColor = bottomColor.withAlphaComponent(patternIntensity) + let topPatternColor = UIColor(rgb: state.backgroundColors[0]).withAlphaComponent(patternIntensity) + if state.backgroundColors.count >= 2 { + let bottomPatternColor = UIColor(rgb: state.backgroundColors[1]).withAlphaComponent(patternIntensity) return [topPatternColor, bottomPatternColor] } else { return [topPatternColor, topPatternColor] @@ -175,8 +166,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate private let maskNode: ASImageNode private let backgroundContainerNode: ASDisplayNode private let backgroundWrapperNode: ASDisplayNode - private let immediateBackgroundNode: ASImageNode - private let signalBackgroundNode: TransformImageNode + private let backgroundNode: WallpaperBackgroundNode private let messagesContainerNode: ASDisplayNode private var dateHeaderNode: ListViewItemHeaderNode? private var messageNodes: [ListViewItemNode]? @@ -193,7 +183,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate private let serviceBackgroundColorPromise = Promise() private var wallpaperDisposable = MetaDisposable() - private var currentBackgroundColors: (UIColor, UIColor?, Int32?)? + private var currentBackgroundColors: ([UInt32], Int32?)? private var currentBackgroundPromise = Promise<(UIColor, UIColor?)?>() private var patternWallpaper: TelegramWallpaper? @@ -252,9 +242,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate self.backgroundContainerNode = ASDisplayNode() self.backgroundContainerNode.clipsToBounds = true self.backgroundWrapperNode = ASDisplayNode() - self.immediateBackgroundNode = ASImageNode() - self.signalBackgroundNode = TransformImageNode() - self.signalBackgroundNode.displaysAsynchronously = false + self.backgroundNode = WallpaperBackgroundNode(context: context) self.messagesContainerNode = ASDisplayNode() self.messagesContainerNode.clipsToBounds = true @@ -303,20 +291,12 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate self.scrollNode.addSubnode(self.messagesContainerNode) self.backgroundContainerNode.addSubnode(self.backgroundWrapperNode) - self.backgroundWrapperNode.addSubnode(self.immediateBackgroundNode) - self.backgroundWrapperNode.addSubnode(self.signalBackgroundNode) - - self.signalBackgroundNode.imageUpdated = { [weak self] _ in - if let strongSelf = self { - strongSelf.ready.set(.single(true)) - strongSelf.signalBackgroundNode.contentAnimations = [] - } - } + self.backgroundWrapperNode.addSubnode(self.backgroundNode) self.motionButtonNode.addTarget(self, action: #selector(self.toggleMotion), forControlEvents: .touchUpInside) self.patternButtonNode.addTarget(self, action: #selector(self.togglePattern), forControlEvents: .touchUpInside) - self.colorPanelNode.colorAdded = { [weak self] in + /*self.colorPanelNode.colorAdded = { [weak self] in if let strongSelf = self { strongSelf.signalBackgroundNode.contentAnimations = [.subsequentUpdates] } @@ -326,27 +306,23 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate if let strongSelf = self { strongSelf.signalBackgroundNode.contentAnimations = [.subsequentUpdates] } - } + }*/ - self.colorPanelNode.colorsChanged = { [weak self] firstColor, secondColor, ended in + self.colorPanelNode.colorsChanged = { [weak self] colors, ended in if let strongSelf = self, let section = strongSelf.state.section { strongSelf.updateState({ current in var updated = current updated.preview = !ended switch section { case .accent: - if let firstColor = firstColor { - updated.accentColor = firstColor + if let firstColor = colors.first { + updated.accentColor = UIColor(rgb: firstColor) } case .background: - if let firstColor = firstColor { - updated.backgroundColors = (firstColor, secondColor) - } else { - updated.backgroundColors = nil - } + updated.backgroundColors = colors case .messages: - if let firstColor = firstColor { - updated.messagesColors = (firstColor, secondColor) + if colors.count >= 1 { + updated.messagesColors = (UIColor(rgb: colors[0]), colors.count >= 2 ? UIColor(rgb: colors[1]) : nil) } else { updated.messagesColors = nil } @@ -423,47 +399,38 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate } } } - + self.stateDisposable = (self.statePromise.get() |> deliverOn(self.queue) |> mapToThrottled { next -> Signal in return .single(next) |> then(.complete() |> delay(0.0166667, queue: self.queue)) } - |> map { [weak self] state -> (PresentationTheme?, (TelegramWallpaper, UIImage?, Signal<(TransformImageArguments) -> DrawingContext?, NoError>?, (() -> Void)?), UIColor, (UIColor, UIColor?)?, PatternWallpaperArguments, Bool) in + |> map { [weak self] state -> (PresentationTheme?, TelegramWallpaper, UIColor, [UInt32], PatternWallpaperArguments, Bool) in let accentColor = state.accentColor var backgroundColors = state.backgroundColors let messagesColors = state.messagesColors - - var wallpaper: TelegramWallpaper - var wallpaperImage: UIImage? - var wallpaperSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? - var singleBackgroundColor: UIColor? - + var wallpaper: TelegramWallpaper + var updateOnlyWallpaper = false if state.section == .background && state.preview { updateOnlyWallpaper = true } - - if let backgroundColors = backgroundColors { + + if !backgroundColors.isEmpty { if let patternWallpaper = state.patternWallpaper, case let .file(file) = patternWallpaper { - let color = backgroundColors.0.argb - let bottomColor = backgroundColors.1.flatMap { $0.argb } - wallpaper = patternWallpaper.withUpdatedSettings(WallpaperSettings(motion: state.motion, color: color, bottomColor: bottomColor, intensity: state.patternIntensity, rotation: state.rotation)) - + wallpaper = patternWallpaper.withUpdatedSettings(WallpaperSettings(motion: state.motion, colors: backgroundColors, intensity: state.patternIntensity, rotation: state.rotation)) + let dimensions = file.file.dimensions ?? PixelDimensions(width: 100, height: 100) var convertedRepresentations: [ImageRepresentationWithReference] = [] for representation in file.file.previewRepresentations { convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: .wallpaper(wallpaper: .slug(file.slug), resource: representation.resource))) } convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: nil), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource))) - - wallpaperSignal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true) - } else if let bottomColor = backgroundColors.1 { - wallpaper = .gradient(backgroundColors.0.argb, bottomColor.argb, WallpaperSettings(rotation: state.rotation)) - wallpaperSignal = gradientImage([backgroundColors.0, bottomColor], rotation: state.rotation) + } else if backgroundColors.count >= 2 { + wallpaper = .gradient(backgroundColors, WallpaperSettings(rotation: state.rotation)) } else { - wallpaper = .color(backgroundColors.0.argb) + wallpaper = .color(backgroundColors.first ?? 0xffffff) } } else if let themeReference = mode.themeReference, case let .builtin(theme) = themeReference, state.initialWallpaper == nil { var suggestedWallpaper: TelegramWallpaper @@ -471,25 +438,23 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate case .dayClassic: let topColor = accentColor.withMultiplied(hue: 1.010, saturation: 0.414, brightness: 0.957) let bottomColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.867, brightness: 0.965) - suggestedWallpaper = .gradient(topColor.argb, bottomColor.argb, WallpaperSettings()) - wallpaperSignal = gradientImage([topColor, bottomColor], rotation: state.rotation) - backgroundColors = (topColor, bottomColor) + suggestedWallpaper = .gradient([topColor.rgb, bottomColor.rgb], WallpaperSettings()) + backgroundColors = [topColor.rgb, bottomColor.rgb] case .nightAccent: let color = accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18) - suggestedWallpaper = .color(color.argb) - backgroundColors = (color, nil) + suggestedWallpaper = .color(color.rgb) + backgroundColors = [color.rgb] default: - suggestedWallpaper = .builtin(nil, WallpaperSettings()) + suggestedWallpaper = .builtin(WallpaperSettings()) } wallpaper = suggestedWallpaper } else { - wallpaper = state.initialWallpaper ?? .builtin(nil, WallpaperSettings()) - wallpaperImage = chatControllerBackgroundImage(theme: nil, wallpaper: wallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: false) + wallpaper = state.initialWallpaper ?? .builtin(WallpaperSettings()) } - - let serviceBackgroundColor = serviceColor(for: (wallpaper, wallpaperImage)) + + let serviceBackgroundColor = serviceColor(for: (wallpaper, nil)) let updatedTheme: PresentationTheme? - + if !updateOnlyWallpaper { if let themeReference = mode.themeReference { updatedTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: messagesColors, serviceBackgroundColor: serviceBackgroundColor, preview: true) ?? defaultPresentationTheme @@ -498,29 +463,22 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate } else { updatedTheme = theme } - + let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: updatedTheme!, wallpaper: wallpaper, bubbleCorners: bubbleCorners) } else { updatedTheme = nil } - + let patternArguments = PatternWallpaperArguments(colors: calcPatternColors(for: state), rotation: wallpaper.settings?.rotation ?? 0, preview: state.preview) - - var wallpaperApply: (() -> Void)? - if let strongSelf = self, wallpaper.isPattern, let (layout, _, _) = strongSelf.validLayout { - let makeImageLayout = strongSelf.signalBackgroundNode.asyncLayout() - wallpaperApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: wallpaper.dimensions ?? layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: patternArguments)) - } - - return (updatedTheme, (wallpaper, wallpaperImage, wallpaperSignal, wallpaperApply), serviceBackgroundColor, backgroundColors, patternArguments, state.preview) + + return (updatedTheme, wallpaper, serviceBackgroundColor, backgroundColors, patternArguments, state.preview) } - |> deliverOnMainQueue).start(next: { [weak self] theme, wallpaperImageAndSignal, serviceBackgroundColor, backgroundColors, patternArguments, preview in + |> deliverOnMainQueue).start(next: { [weak self] theme, wallpaper, serviceBackgroundColor, backgroundColors, patternArguments, preview in guard let strongSelf = self else { return } - let (wallpaper, wallpaperImage, wallpaperSignal, wallpaperApply) = wallpaperImageAndSignal - - if let theme = theme { + + if let theme = theme { strongSelf.theme = theme strongSelf.themeUpdated?(theme) strongSelf.themePromise.set(.single(theme)) @@ -529,55 +487,25 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate strongSelf.chatListBackgroundNode.backgroundColor = theme.chatList.backgroundColor strongSelf.maskNode.image = generateMaskImage(color: theme.chatList.backgroundColor) } - + strongSelf.serviceBackgroundColor = serviceBackgroundColor strongSelf.serviceBackgroundColorPromise.set(.single(serviceBackgroundColor)) - - if case let .color(value) = wallpaper { - strongSelf.backgroundColor = UIColor(rgb: value) - strongSelf.immediateBackgroundNode.backgroundColor = UIColor(rgb: value) - strongSelf.immediateBackgroundNode.image = nil - strongSelf.signalBackgroundNode.isHidden = true - strongSelf.signalBackgroundNode.contentAnimations = [] - strongSelf.signalBackgroundNode.reset() - strongSelf.patternWallpaper = nil - strongSelf.ready.set(.single(true) ) - } else if let wallpaperImage = wallpaperImage { - strongSelf.immediateBackgroundNode.image = wallpaperImage - strongSelf.signalBackgroundNode.isHidden = true - strongSelf.signalBackgroundNode.contentAnimations = [] - strongSelf.signalBackgroundNode.reset() - strongSelf.patternWallpaper = nil - strongSelf.ready.set(.single(true) ) - } else if let wallpaperSignal = wallpaperSignal { - strongSelf.signalBackgroundNode.contentMode = .scaleToFill - strongSelf.signalBackgroundNode.isHidden = false - - if case let .file(file) = wallpaper, let (layout, _, _) = strongSelf.validLayout { - wallpaperApply?() - - if let previousWallpaper = strongSelf.patternWallpaper, case let .file(previousFile) = previousWallpaper, file.id == previousFile.id { - } else { - strongSelf.signalBackgroundNode.setSignal(wallpaperSignal) - strongSelf.patternWallpaper = wallpaper - } - } else { - strongSelf.signalBackgroundNode.setSignal(wallpaperSignal) - strongSelf.patternWallpaper = nil - } - } + + strongSelf.backgroundNode.update(wallpaper: wallpaper) + strongSelf.ready.set(.single(true)) + strongSelf.wallpaper = wallpaper strongSelf.patternArguments = patternArguments - + if !preview { - if let backgroundColors = backgroundColors { - strongSelf.currentBackgroundColors = (backgroundColors.0, backgroundColors.1, strongSelf.state.rotation) + if !backgroundColors.isEmpty { + strongSelf.currentBackgroundColors = (backgroundColors, strongSelf.state.rotation) } else { strongSelf.currentBackgroundColors = nil } strongSelf.patternPanelNode.backgroundColors = strongSelf.currentBackgroundColors } - + if let _ = theme, let (layout, navigationBarHeight, messagesBottomInset) = strongSelf.validLayout { strongSelf.updateChatsLayout(layout: layout, topInset: navigationBarHeight, transition: .immediate) strongSelf.updateMessagesLayout(layout: layout, bottomInset: messagesBottomInset, transition: .immediate) @@ -655,13 +583,11 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate if sectionChanged, let section = self.state.section { self.view.endEditing(true) - let firstColor: UIColor? - let secondColor: UIColor? + var colors: [UInt32] var defaultColor: UIColor? switch section { case .accent: - firstColor = self.state.accentColor ?? defaultDayAccentColor - secondColor = nil + colors = [self.state.accentColor.rgb] case .background: if let themeReference = self.mode.themeReference, case let .builtin(theme) = themeReference { switch theme { @@ -673,15 +599,10 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate break } } - if let backgroundColors = self.state.backgroundColors { - firstColor = backgroundColors.0 - secondColor = backgroundColors.1 - } else if previousState.initialWallpaper != nil, let image = self.immediateBackgroundNode.image { - firstColor = averageColor(from: image) - secondColor = nil + if !self.state.backgroundColors.isEmpty { + colors = self.state.backgroundColors } else { - firstColor = nil - secondColor = nil + colors = [] } case .messages: if let defaultMessagesColor = self.state.defaultMessagesColor { @@ -692,16 +613,42 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate defaultColor = self.state.accentColor } if let messagesColors = self.state.messagesColors { - firstColor = messagesColors.0 - secondColor = messagesColors.1 + if let second = messagesColors.1 { + colors = [messagesColors.0.rgb, second.rgb] + } else { + colors = [messagesColors.0.rgb] + } } else { - firstColor = nil - secondColor = nil + colors = [] } } + if colors.isEmpty, let defaultColor = defaultColor { + colors = [defaultColor.rgb] + } + + let maximumNumberOfColors: Int + switch self.state.section { + case .accent: + maximumNumberOfColors = 1 + case .background: + maximumNumberOfColors = 4 + case .messages: + maximumNumberOfColors = 2 + default: + maximumNumberOfColors = 1 + } + self.colorPanelNode.updateState({ _ in - return WallpaperColorPanelNodeState(selection: colorPanelCollapsed ? .none : .index(0), firstColor: firstColor, defaultColor: defaultColor, secondColor: secondColor, secondColorAvailable: self.state.section != .accent, rotateAvailable: self.state.section == .background, rotation: self.state.rotation, preview: false, simpleGradientGeneration: self.state.section == .messages, multiColors: []) + return WallpaperColorPanelNodeState( + selection: colorPanelCollapsed ? nil : 0, + colors: colors, + maximumNumberOfColors: maximumNumberOfColors, + rotateAvailable: self.state.section == .background, + rotation: self.state.rotation, + preview: false, + simpleGradientGeneration: self.state.section == .messages + ) }, animated: animated) needsLayout = true @@ -714,7 +661,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate self.colorPanelNode.updateState({ current in var updated = current - updated.selection = colorPanelCollapsed ? .none : .index(0) + updated.selection = colorPanelCollapsed ? nil : 0 return updated }, animated: animated) } @@ -1008,16 +955,13 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let backgroundSize = CGSize(width: bounds.width, height: bounds.height - (colorPanelHeight - colorPanelOffset)) transition.updateFrame(node: self.backgroundContainerNode, frame: CGRect(origin: CGPoint(), size: backgroundSize)) - - let makeImageLayout = self.signalBackgroundNode.asyncLayout() - let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: self.patternWallpaper?.dimensions ?? layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: self.patternArguments)) - let _ = imageApply() + + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size)) + self.backgroundNode.updateLayout(size: self.backgroundNode.bounds.size, transition: transition) transition.updatePosition(node: self.backgroundWrapperNode, position: CGPoint(x: backgroundSize.width / 2.0, y: backgroundSize.height / 2.0)) transition.updateBounds(node: self.backgroundWrapperNode, bounds: CGRect(origin: CGPoint(), size: layout.size)) - transition.updateFrame(node: self.immediateBackgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - transition.updateFrame(node: self.signalBackgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size)) let displayOptionButtons = self.state.section == .background var messagesBottomInset: CGFloat = 0.0 @@ -1052,6 +996,9 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let rightButtonFrame = CGRect(origin: CGPoint(x: ceil(layout.size.width / 2.0 + 10.0), y: layout.size.height - bottomInset - 44.0), size: buttonSize) var hasMotion: Bool = self.state.patternWallpaper != nil || self.state.displayPatternPanel + if self.state.backgroundColors.count >= 3 { + hasMotion = false + } var patternAlpha: CGFloat = displayOptionButtons ? 1.0 : 0.0 var motionAlpha: CGFloat = displayOptionButtons && hasMotion ? 1.0 : 0.0 @@ -1096,11 +1043,11 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate updated.displayPatternPanel = true if current.patternWallpaper == nil, let wallpaper = wallpaper { updated.patternWallpaper = wallpaper - if updated.backgroundColors == nil { + if updated.backgroundColors.isEmpty { if let backgroundColors = backgroundColors { - updated.backgroundColors = (backgroundColors.0, backgroundColors.1) + updated.backgroundColors = backgroundColors.0 } else { - updated.backgroundColors = nil + updated.backgroundColors = [] } } appeared = true diff --git a/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift b/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift index ceb0836926..dc5578e00d 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeColorPresets.swift @@ -4,7 +4,11 @@ import SyncCore import TelegramUIPreferences private func patternWallpaper(slug: String, topColor: UInt32, bottomColor: UInt32?, intensity: Int32?, rotation: Int32?) -> TelegramWallpaper { - return TelegramWallpaper.file(id: 0, accessHash: 0, isCreator: false, isDefault: true, isPattern: true, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(color: topColor, bottomColor: bottomColor, intensity: intensity ?? 50, rotation: rotation)) + var colors: [UInt32] = [topColor] + if let bottomColor = bottomColor { + colors.append(bottomColor) + } + return TelegramWallpaper.file(id: 0, accessHash: 0, isCreator: false, isDefault: true, isPattern: true, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(colors: colors, intensity: intensity ?? 50, rotation: rotation)) } var dayClassicColorPresets: [PresentationThemeAccentColor] = [ diff --git a/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift b/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift index 9ab87b1ff7..d94433119f 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeGridController.swift @@ -299,8 +299,8 @@ final class ThemeGridController: ViewController { case let .file(_, _, _, _, isPattern, _, slug, _, settings): var options: [String] = [] if isPattern { - if let color = settings.color { - options.append("bg_color=\(UIColor(rgb: color).hexString)") + if settings.colors.count >= 1 { + options.append("bg_color=\(UIColor(rgb: settings.colors[0]).hexString)") } if let intensity = settings.intensity { options.append("intensity=\(intensity)") diff --git a/submodules/SettingsUI/Sources/Themes/ThemeGridControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeGridControllerNode.swift index 35662a8959..8100db63b7 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeGridControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeGridControllerNode.swift @@ -57,10 +57,10 @@ final class ThemeGridControllerInteraction { private struct ThemeGridControllerEntry: Comparable, Identifiable { enum StableId: Hashable { - case builtin([UInt32]) + case builtin case color(UInt32) - case gradient(UInt32, UInt32) - case file(Int64, UInt32, Int32) + case gradient([UInt32]) + case file(Int64, [UInt32], Int32) case image(String) } @@ -79,14 +79,14 @@ private struct ThemeGridControllerEntry: Comparable, Identifiable { var stableId: StableId { switch self.wallpaper { - case let .builtin(gradient, _): - return .builtin(gradient?.colors ?? []) + case .builtin: + return .builtin case let .color(color): return .color(color) - case let .gradient(topColor, bottomColor, _): - return .gradient(topColor, bottomColor) + case let .gradient(colors, _): + return .gradient(colors) case let .file(id, _, _, _, _, _, _, _, settings): - return .file(id, settings.color ?? 0, settings.intensity ?? 0) + return .file(id, settings.colors, settings.intensity ?? 0) case let .image(representations, _): if let largest = largestImageRepresentation(representations) { return .image(largest.resource.id.uniqueId) @@ -364,10 +364,8 @@ final class ThemeGridControllerNode: ASDisplayNode { var index = 1 var isSelectedEditable = true - if case let .builtin(gradient, _) = presentationData.chatWallpaper { - if gradient == nil { - isSelectedEditable = false - } + if case .builtin = presentationData.chatWallpaper { + isSelectedEditable = false } else if presentationData.chatWallpaper.isBasicallyEqual(to: presentationData.theme.chat.defaultWallpaper) { isSelectedEditable = false } @@ -384,13 +382,13 @@ final class ThemeGridControllerNode: ASDisplayNode { } if !entries.contains(where: { entry in - if case .builtin = entry.wallpaper { + if case .gradient(defaultBuiltinWallpaperGradientColors.map(\.rgb), _) = entry.wallpaper { return true } else { return false } }) { - let entry = ThemeGridControllerEntry(index: 1, wallpaper: .builtin(nil, WallpaperSettings(motion: true)), isEditable: false, isSelected: false) + let entry = ThemeGridControllerEntry(index: 1, wallpaper: .gradient(defaultBuiltinWallpaperGradientColors.map(\.rgb), WallpaperSettings()), isEditable: false, isSelected: false) if !entries.contains(where: { $0.stableId == entry.stableId }) { entries.insert(entry, at: index) index += 1 @@ -413,7 +411,7 @@ final class ThemeGridControllerNode: ASDisplayNode { } for wallpaper in sortedWallpapers { - if case let .file(file) = wallpaper, deletedWallpaperSlugs.contains(file.slug) || (wallpaper.isPattern && file.settings.color == nil) { + if case let .file(file) = wallpaper, deletedWallpaperSlugs.contains(file.slug) || (wallpaper.isPattern && file.settings.colors.isEmpty) { continue } let selected = presentationData.chatWallpaper.isBasicallyEqual(to: wallpaper) @@ -422,10 +420,8 @@ final class ThemeGridControllerNode: ASDisplayNode { isDefault = true } var isEditable = true - if case let .builtin(gradient, _) = wallpaper { - if gradient == nil { - isEditable = false - } + if case .builtin = wallpaper { + isEditable = false } if !selected && !isDefault { let entry = ThemeGridControllerEntry(index: index, wallpaper: wallpaper, isEditable: isEditable, isSelected: false) diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 67f959881d..75ed2608da 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -107,10 +107,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.instantChatBackgroundNode.displaysAsynchronously = false let wallpaper = initialWallpaper ?? previewTheme.chat.defaultWallpaper - self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: previewTheme, wallpaper: wallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper) - if self.instantChatBackgroundNode.image != nil { - self.ready.set(.single(true)) - } + + self.ready.set(.single(true)) self.instantChatBackgroundNode.update(wallpaper: wallpaper) self.instantChatBackgroundNode.view.contentMode = .scaleAspectFill @@ -235,7 +233,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let fileReference = FileMediaReference.standalone(media: file.file) if wallpaper.isPattern { signal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: false) - } else if strongSelf.instantChatBackgroundNode.image == nil { + }/* else if strongSelf.instantChatBackgroundNode.image == nil { signal = wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: fileReference, representations: convertedRepresentations, alwaysShowThumbnailFirst: false, autoFetchFullSize: false) |> afterNext { next in if let _ = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.file.resource) { @@ -243,7 +241,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { context.sharedContext.accountManager.mediaBox.storeResourceData(file.file.resource.id, data: data) } } - } else { + }*/ else { signal = .complete() } strongSelf.remoteChatBackgroundNode.setSignal(signal) @@ -269,14 +267,14 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { }) var patternArguments: PatternWallpaperArguments? - if let color = file.settings.color { + if !file.settings.colors.isEmpty { var patternIntensity: CGFloat = 0.5 if let intensity = file.settings.intensity { patternIntensity = CGFloat(intensity) / 100.0 } - var patternColors = [UIColor(rgb: color, alpha: patternIntensity)] - if let bottomColor = file.settings.bottomColor { - patternColors.append(UIColor(rgb: bottomColor, alpha: patternIntensity)) + var patternColors = [UIColor(rgb: file.settings.colors[0], alpha: patternIntensity)] + if file.settings.colors.count >= 2 { + patternColors.append(UIColor(rgb: file.settings.colors[1], alpha: patternIntensity)) } patternArguments = PatternWallpaperArguments(colors: patternColors, rotation: file.settings.rotation) } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 9a9b1ea3b5..70b4a09ce0 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -226,7 +226,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { strongSelf.insertSubnode(backgroundNode, at: 0) } - if let updatedBackgroundSignal = updatedBackgroundSignal { + /*if let updatedBackgroundSignal = updatedBackgroundSignal { strongSelf.disposable.set((updatedBackgroundSignal |> deliverOnMainQueue).start(next: { [weak self] image in if let strongSelf = self, let (image, final) = image, let backgroundNode = strongSelf.backgroundNode { @@ -244,7 +244,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { strongSelf.finalImage = final } })) - } + }*/ strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index df7b0508c6..7c27c86b75 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -374,6 +374,19 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { } } colorItems.append(contentsOf: colors.map { .color($0) }) + if let index = colorItems.firstIndex(where: { item in + if case .default = item { + return true + } else { + return false + } + }) { + if index > 0 { + let item = colorItems[index] + colorItems.remove(at: index) + colorItems.insert(item, at: 1) + } + } return ThemeSettingsAccentColorItem(theme: theme, sectionId: self.section, generalThemeReference: generalThemeReference, themeReference: currentTheme, colors: colorItems, currentColor: currentColor, updated: { color in if let color = color { @@ -632,7 +645,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The effectiveWallpaper = wallpaper } else { let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: reference, accentColor: accentColor?.color, bubbleColors: accentColor?.customBubbleColors, wallpaper: accentColor?.wallpaper) - effectiveWallpaper = theme?.chat.defaultWallpaper ?? .builtin(nil, WallpaperSettings()) + effectiveWallpaper = theme?.chat.defaultWallpaper ?? .builtin(WallpaperSettings()) } return (accentColor, effectiveWallpaper) } @@ -643,7 +656,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The if let wallpaper = cachedWallpaper?.wallpaper, case let .file(file) = wallpaper { return (accentColor, wallpaper) } else { - return (accentColor, .builtin(nil, WallpaperSettings())) + return (accentColor, .builtin(WallpaperSettings())) } } } else { @@ -833,7 +846,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The } else { theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: generalThemeReference, accentColor: accentColor?.accentColor, bubbleColors: accentColor?.customBubbleColors, wallpaper: accentColor?.wallpaper) } - effectiveWallpaper = theme?.chat.defaultWallpaper ?? .builtin(nil, WallpaperSettings()) + effectiveWallpaper = theme?.chat.defaultWallpaper ?? .builtin(WallpaperSettings()) } let wallpaperSignal: Signal @@ -1227,7 +1240,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The |> mapToSignal { cachedWallpaper in if let wallpaper = cachedWallpaper?.wallpaper, case let .file(file) = wallpaper { let resource = file.file.resource - let representation = CachedPatternWallpaperRepresentation(color: file.settings.color ?? 0xd6e2ee, bottomColor: file.settings.bottomColor, intensity: file.settings.intensity ?? 50, rotation: file.settings.rotation) + let representation = CachedPatternWallpaperRepresentation(color: file.settings.colors.count >= 1 ? file.settings.colors[0] : 0xd6e2ee, bottomColor: file.settings.colors.count >= 2 ? file.settings.colors[1] : 0xd6e2ee, intensity: file.settings.intensity ?? 50, rotation: file.settings.rotation) let _ = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)).start() diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift index e3587f6838..2833d44dcd 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperColorPanelNode.swift @@ -33,13 +33,6 @@ private func textInputBackgroundImage(fieldColor: UIColor, strokeColor: UIColor, private func generateSwatchBorderImage(theme: PresentationTheme) -> UIImage? { return nil - return generateImage(CGSize(width: 21.0, height: 21.0), rotatedContext: { size, context in - let bounds = CGRect(origin: CGPoint(), size: size) - context.clear(bounds) - context.setLineWidth(1.0) - context.setStrokeColor(theme.chat.inputPanel.inputControlColor.cgColor) - context.strokeEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0)) - }) } private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { @@ -61,7 +54,7 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { var colorSelected: (() -> Void)? private var color: UIColor? - + private var isDefault = false { didSet { self.updateSelectionVisibility() @@ -73,16 +66,6 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { self.removeButton.isUserInteractionEnabled = self.isRemovable } } - - var isSelected: Bool = false { - didSet { - self.updateSelectionVisibility() - self.gestureRecognizer?.isEnabled = !self.isSelected - if !self.isSelected { - self.textFieldNode.textField.resignFirstResponder() - } - } - } private var previousIsDefault: Bool? private var previousColor: UIColor? @@ -152,7 +135,7 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { self.textFieldNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) self.textFieldNode.textField.tintColor = self.theme.list.itemAccentColor - let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapped)) + let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapped(_:))) self.view.addGestureRecognizer(gestureRecognizer) self.gestureRecognizer = gestureRecognizer } @@ -205,12 +188,12 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { } self.colorRemoved?() - self.removeButton.layer.removeAnimation(forKey: "opacity") - self.removeButton.alpha = 1.0 } - @objc private func tapped() { - self.colorSelected?() + @objc private func tapped(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.colorSelected?() + } } @objc internal func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { @@ -253,18 +236,13 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { } func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { - if self.isSelected { - self.skipEndEditing = false - self.previousColor = self.color - self.previousIsDefault = self.isDefault - - textField.textColor = self.theme.chat.inputPanel.inputTextColor - - return true - } else { - self.colorSelected?() - return false - } + self.skipEndEditing = false + self.previousColor = self.color + self.previousIsDefault = self.isDefault + + textField.textColor = self.theme.chat.inputPanel.inputTextColor + + return true } @objc func textFieldDidEndEditing(_ textField: UITextField) { @@ -298,7 +276,7 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { } private func updateSelectionVisibility() { - self.selectionNode.isHidden = !self.isSelected || self.isDefault + self.selectionNode.isHidden = true } func updateLayout(size: CGSize, condensed: Bool, transition: ContainedViewLayoutTransition) { @@ -314,7 +292,7 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { if self.displaySwatch { textPadding = condensed ? 31.0 : 37.0 } else { - textPadding = 6.0 + textPadding = 12.0 } transition.updateFrame(node: self.textBackgroundNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)) @@ -328,50 +306,35 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate { let removeSize = CGSize(width: 33.0, height: 33.0) 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) + self.removeButton.alpha = self.isRemovable ? 1.0 : 0.0 } } -enum WallpaperColorPanelNodeSelectionState: Equatable { - case none - case index(Int) -} - -struct WallpaperColorPanelNodeState { - var selection: WallpaperColorPanelNodeSelectionState - var firstColor: UIColor? - var defaultColor: UIColor? - var secondColor: UIColor? - var secondColorAvailable: Bool +struct WallpaperColorPanelNodeState: Equatable { + var selection: Int? + var colors: [UInt32] + var maximumNumberOfColors: Int var rotateAvailable: Bool var rotation: Int32 var preview: Bool var simpleGradientGeneration: Bool - - var multiColors: [UIColor] } private final class ColorSampleItemNode: ASImageNode { private struct State: Equatable { var color: UInt32 var size: CGSize + var isSelected: Bool } private var action: () -> Void private var validState: State? - private let selectionNode: ASImageNode - init(action: @escaping () -> Void) { self.action = action - self.selectionNode = ASImageNode() - self.selectionNode.isUserInteractionEnabled = false - super.init() - self.addSubnode(self.selectionNode) - self.isUserInteractionEnabled = true self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) } @@ -383,15 +346,29 @@ private final class ColorSampleItemNode: ASImageNode { } func update(size: CGSize, color: UIColor, isSelected: Bool) { - self.selectionNode.frame = CGRect(origin: CGPoint(), size: size).insetBy(dx: 2.0, dy: 2.0) - self.selectionNode.isHidden = !isSelected - - let state = State(color: color.rgb, size: size) + let state = State(color: color.rgb, size: size, isSelected: isSelected) if self.validState != state { self.validState = state - self.image = generateFilledCircleImage(diameter: size.width, color: color, strokeColor: UIColor(white: 0.0, alpha: 0.1), strokeWidth: UIScreenPixel, backgroundColor: nil) - self.selectionNode.image = generateFilledCircleImage(diameter: self.selectionNode.frame.width, color: color, strokeColor: .white, strokeWidth: 2.0, backgroundColor: nil) + self.image = generateImage(CGSize(width: size.width, height: size.height), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(color.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + + context.setBlendMode(.softLight) + context.setStrokeColor(UIColor(white: 0.0, alpha: 0.3).cgColor) + context.setLineWidth(UIScreenPixel) + context.strokeEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: UIScreenPixel, dy: UIScreenPixel)) + + if isSelected { + context.setBlendMode(.copy) + context.setStrokeColor(UIColor.clear.cgColor) + let lineWidth: CGFloat = 2.0 + context.setLineWidth(lineWidth) + let inset: CGFloat = 2.0 + lineWidth / 2.0 + context.strokeEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset)) + } + }) } } } @@ -404,8 +381,6 @@ final class WallpaperColorPanelNode: ASDisplayNode { private let backgroundNode: NavigationBackgroundNode private let topSeparatorNode: ASDisplayNode private let bottomSeparatorNode: ASDisplayNode - private let firstColorFieldNode: ColorInputFieldNode - private let secondColorFieldNode: ColorInputFieldNode private let rotateButton: HighlightableButtonNode private let swapButton: HighlightableButtonNode private let addButton: HighlightableButtonNode @@ -415,8 +390,7 @@ final class WallpaperColorPanelNode: ASDisplayNode { private var sampleItemNodes: [ColorSampleItemNode] = [] private let multiColorFieldNode: ColorInputFieldNode - var colorsChanged: ((UIColor?, UIColor?, Bool) -> Void)? - var multiColorsChanged: (([UIColor]) -> Void)? + var colorsChanged: (([UInt32], Bool) -> Void)? var colorSelected: (() -> Void)? var rotate: (() -> Void)? @@ -446,21 +420,25 @@ final class WallpaperColorPanelNode: ASDisplayNode { self.swapButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorSwapIcon"), color: theme.chat.inputPanel.panelControlColor), for: .normal) self.addButton = HighlightableButtonNode() self.addButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorAddIcon"), color: theme.chat.inputPanel.panelControlColor), for: .normal) - - self.firstColorFieldNode = ColorInputFieldNode(theme: theme) - self.secondColorFieldNode = ColorInputFieldNode(theme: theme) + self.multiColorFieldNode = ColorInputFieldNode(theme: theme, displaySwatch: false) - self.state = WallpaperColorPanelNodeState(selection: .index(0), firstColor: nil, secondColor: nil, secondColorAvailable: false, rotateAvailable: false, rotation: 0, preview: false, simpleGradientGeneration: false, multiColors: []) + self.state = WallpaperColorPanelNodeState( + selection: 0, + colors: [], + maximumNumberOfColors: 1, + rotateAvailable: false, + rotation: 0, + preview: false, + simpleGradientGeneration: false + ) super.init() self.addSubnode(self.backgroundNode) self.addSubnode(self.topSeparatorNode) self.addSubnode(self.bottomSeparatorNode) - self.addSubnode(self.firstColorFieldNode) self.addSubnode(self.multiColorFieldNode) - self.addSubnode(self.secondColorFieldNode) self.addSubnode(self.doneButton) self.addSubnode(self.colorPickerNode) @@ -471,8 +449,52 @@ final class WallpaperColorPanelNode: ASDisplayNode { self.rotateButton.addTarget(self, action: #selector(self.rotatePressed), forControlEvents: .touchUpInside) self.swapButton.addTarget(self, action: #selector(self.swapPressed), forControlEvents: .touchUpInside) self.addButton.addTarget(self, action: #selector(self.addPressed), forControlEvents: .touchUpInside) + + self.multiColorFieldNode.colorChanged = { [weak self] color, ended in + if let strongSelf = self { + strongSelf.updateState({ current in + var updated = current + updated.preview = !ended + if let index = strongSelf.state.selection { + updated.colors[index] = color.rgb + } + return updated + }) + } + } + self.multiColorFieldNode.colorRemoved = { [weak self] in + if let strongSelf = self { + strongSelf.colorRemoved?() + strongSelf.updateState({ current in + var updated = current + if let index = strongSelf.state.selection { + updated.colors.remove(at: index) + if updated.colors.isEmpty { + updated.selection = nil + } else { + updated.selection = max(0, min(index - 1, updated.colors.count - 1)) + } + } + return updated + }, animated: strongSelf.state.colors.count >= 2) + } + } + /*self.multiColorFieldNode.colorSelected = { [weak self] in + if let strongSelf = self { + strongSelf.secondColorFieldNode.setSkipEndEditingIfNeeded() + strongSelf.updateState({ current in + var updated = current + if updated.selection != .none { + updated.selection = .index(0) + } + return updated + }) + + strongSelf.colorSelected?() + } + }*/ - self.firstColorFieldNode.colorChanged = { [weak self] color, ended in + /*self.firstColorFieldNode.colorChanged = { [weak self] color, ended in if let strongSelf = self { strongSelf.updateState({ current in var updated = current @@ -545,28 +567,15 @@ final class WallpaperColorPanelNode: ASDisplayNode { strongSelf.colorSelected?() } - } + }*/ self.colorPickerNode.colorChanged = { [weak self] color in if let strongSelf = self { strongSelf.updateState({ current in var updated = current updated.preview = true - switch strongSelf.state.selection { - case .index(0): - updated.firstColor = color - case .index(1): - updated.secondColor = color - default: - break - } - switch strongSelf.state.selection { - case let .index(index): - if updated.multiColors.count > index { - updated.multiColors[index] = color - } - default: - break + if let index = strongSelf.state.selection { + updated.colors[index] = color.rgb } return updated }, updateLayout: false) @@ -577,21 +586,8 @@ final class WallpaperColorPanelNode: ASDisplayNode { strongSelf.updateState({ current in var updated = current updated.preview = false - switch strongSelf.state.selection { - case .index(0): - updated.firstColor = color - case .index(1): - updated.secondColor = color - default: - break - } - switch strongSelf.state.selection { - case let .index(index): - if updated.multiColors.count > index { - updated.multiColors[index] = color - } - default: - break + if let index = strongSelf.state.selection { + updated.colors[index] = color.rgb } return updated }, updateLayout: false) @@ -604,161 +600,44 @@ final class WallpaperColorPanelNode: ASDisplayNode { self.backgroundNode.color = self.theme.chat.inputPanel.panelBackgroundColor self.topSeparatorNode.backgroundColor = self.theme.chat.inputPanel.panelSeparatorColor self.bottomSeparatorNode.backgroundColor = self.theme.chat.inputPanel.panelSeparatorColor - self.firstColorFieldNode.updateTheme(theme) - self.secondColorFieldNode.updateTheme(theme) + self.multiColorFieldNode.updateTheme(theme) } func updateState(_ f: (WallpaperColorPanelNodeState) -> WallpaperColorPanelNodeState, updateLayout: Bool = true, animated: Bool = true) { var updateLayout = updateLayout - let previousFirstColor = self.state.firstColor - let previousSecondColor = self.state.secondColor - let previousMultiColors = self.state.multiColors + let previousColors = self.state.colors let previousPreview = self.state.preview 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) - } - - let firstColorWasRemovable = self.firstColorFieldNode.isRemovable - self.firstColorFieldNode.isRemovable = self.state.secondColor != nil || (self.state.defaultColor != nil && self.state.firstColor != nil) - if firstColorWasRemovable != self.firstColorFieldNode.isRemovable { + let colorWasRemovable = self.multiColorFieldNode.isRemovable + self.multiColorFieldNode.isRemovable = self.state.colors.count > 1 + if colorWasRemovable != self.multiColorFieldNode.isRemovable { updateLayout = true } if updateLayout, let size = self.validLayout { - if self.state.multiColors.isEmpty { - switch self.state.selection { - case .index(0): - self.colorPickerNode.color = firstColor - case .index(1): - if let secondColor = secondColor { - self.colorPickerNode.color = secondColor - } - default: - break - } - } else { - switch self.state.selection { - case let .index(index): - self.colorPickerNode.color = self.state.multiColors[index] - default: - break - } + if let index = self.state.selection { + self.colorPickerNode.color = UIColor(rgb: self.state.colors[index]) } self.updateLayout(size: size, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate) } - if self.state.multiColors.isEmpty { - if self.state.firstColor?.argb != previousFirstColor?.argb || self.state.secondColor?.argb != previousSecondColor?.argb || self.state.preview != previousPreview { - self.colorsChanged?(firstColorIsDefault ? nil : firstColor, secondColor, !self.state.preview) - } - } else { - switch state.selection { - case let .index(index): - self.multiColorFieldNode.setColor(self.state.multiColors[index], update: false) - default: - break - } + if let index = state.selection { + self.multiColorFieldNode.setColor(UIColor(rgb: self.state.colors[index]), update: false) + } - for i in 0 ..< state.multiColors.count { - if i < self.sampleItemNodes.count { - self.sampleItemNodes[i].update(size: self.sampleItemNodes[i].bounds.size, color: state.multiColors[i], isSelected: state.selection == .index(i)) - } + for i in 0 ..< state.colors.count { + if i < self.sampleItemNodes.count { + self.sampleItemNodes[i].update(size: self.sampleItemNodes[i].bounds.size, color: UIColor(rgb: state.colors[i]), isSelected: state.selection == i) } + } - var updated = false - if self.state.multiColors.count != previousMultiColors.count { - updated = true - } else { - for i in 0 ..< self.state.multiColors.count { - if !self.state.multiColors[i].isEqual(previousMultiColors[i]) { - updated = true - } - } - } - if updated { - self.multiColorsChanged?(self.state.multiColors) - } + if self.state.colors != previousColors || self.state.preview != previousPreview { + self.colorsChanged?(self.state.colors, !self.state.preview) } } - 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() - } - } - - let middleButton: ASDisplayNode - if self.rotateButton.alpha > 1.0 { - middleButton = self.rotateButton - } else { - middleButton = self.swapButton - } - if let buttonSnapshotView = middleButton.view.snapshotContentTree() { - buttonSnapshotView.frame = middleButton.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.rotateButton.alpha = 0.0 - self.swapButton.alpha = 0.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 @@ -787,13 +666,18 @@ final class WallpaperColorPanelNode: ASDisplayNode { let buttonSize = CGSize(width: 26.0, height: 26.0) let buttonOffset: CGFloat = (rightInsetWithButton - 13.0) / 2.0 - let middleButtonFrame = 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) + //let middleButtonFrame = 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) - transition.updateFrame(node: self.rotateButton, frame: middleButtonFrame) - transition.updateFrame(node: self.swapButton, frame: middleButtonFrame) - transition.updateFrame(node: self.addButton, frame: middleButtonFrame) + //transition.updateFrame(node: self.rotateButton, frame: middleButtonFrame) + //transition.updateFrame(node: self.swapButton, frame: middleButtonFrame) + + let canAddColors = self.state.colors.count < self.state.maximumNumberOfColors + + transition.updateFrame(node: self.addButton, frame: CGRect(origin: CGPoint(x: size.width - rightInset - buttonSize.width, y: floor((topPanelHeight - buttonSize.height) / 2.0)), size: buttonSize)) + transition.updateAlpha(node: self.addButton, alpha: canAddColors ? 1.0 : 0.0) + transition.updateSublayerTransformScale(node: self.addButton, scale: canAddColors ? 1.0 : 0.1) - let rotateButtonAlpha: CGFloat + /*let rotateButtonAlpha: CGFloat let swapButtonAlpha: CGFloat let addButtonAlpha: CGFloat if let _ = self.state.secondColor { @@ -816,10 +700,9 @@ final class WallpaperColorPanelNode: ASDisplayNode { } transition.updateAlpha(node: self.rotateButton, alpha: rotateButtonAlpha) transition.updateAlpha(node: self.swapButton, alpha: swapButtonAlpha) - transition.updateAlpha(node: self.addButton, alpha: addButtonAlpha) + transition.updateAlpha(node: self.addButton, alpha: addButtonAlpha)*/ - func degreesToRadians(_ degrees: CGFloat) -> CGFloat - { + func degreesToRadians(_ degrees: CGFloat) -> CGFloat { var degrees = degrees if degrees >= 270.0 { degrees = degrees - 360.0 @@ -829,77 +712,66 @@ final class WallpaperColorPanelNode: ASDisplayNode { transition.updateTransformRotation(node: self.rotateButton, angle: degreesToRadians(CGFloat(self.state.rotation)), beginWithCurrentState: true, completion: nil) - if self.state.multiColors.isEmpty { - self.firstColorFieldNode.isHidden = false - self.secondColorFieldNode.isHidden = false - self.rotateButton.isHidden = false - self.swapButton.isHidden = false - self.multiColorFieldNode.isHidden = true + self.rotateButton.isHidden = true + self.swapButton.isHidden = true + self.multiColorFieldNode.isHidden = false - self.firstColorFieldNode.isRemovable = self.state.secondColor != nil || (self.state.defaultColor != nil && self.state.firstColor != nil) - self.secondColorFieldNode.isRemovable = true + let sampleItemSize: CGFloat = 32.0 + let sampleItemSpacing: CGFloat = 15.0 - self.firstColorFieldNode.isSelected = self.state.selection == .index(0) - self.secondColorFieldNode.isSelected = self.state.selection == .index(1) + var nextSampleX = leftInset - 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, 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, condensed: condensedLayout, transition: transition) - - for itemNode in self.sampleItemNodes { - itemNode.removeFromSupernode() - } - self.sampleItemNodes.removeAll() - } else { - self.firstColorFieldNode.isHidden = true - self.secondColorFieldNode.isHidden = true - self.rotateButton.isHidden = true - self.swapButton.isHidden = true - self.multiColorFieldNode.isHidden = false - - let sampleItemSize: CGFloat = 32.0 - let sampleItemSpacing: CGFloat = 15.0 - - var nextSampleX = leftInset - - for i in 0 ..< self.state.multiColors.count { - let itemNode: ColorSampleItemNode - if self.sampleItemNodes.count < i { - itemNode = self.sampleItemNodes[i] - } else { - itemNode = ColorSampleItemNode(action: { [weak self] in - guard let strongSelf = self else { - return - } - let index = i - strongSelf.updateState({ state in - var state = state - state.selection = .index(index) - return state - }) + for i in 0 ..< self.state.colors.count { + var animateIn = false + let itemNode: ColorSampleItemNode + if self.sampleItemNodes.count > i { + itemNode = self.sampleItemNodes[i] + } else { + itemNode = ColorSampleItemNode(action: { [weak self] in + guard let strongSelf = self else { + return + } + let index = i + strongSelf.updateState({ state in + var state = state + state.selection = index + return state }) - self.sampleItemNodes.append(itemNode) - self.insertSubnode(itemNode, aboveSubnode: self.multiColorFieldNode) - } - - if i != 0 { - nextSampleX += sampleItemSpacing - } - itemNode.frame = CGRect(origin: CGPoint(x: nextSampleX, y: (topPanelHeight - sampleItemSize) / 2.0), size: CGSize(width: sampleItemSize, height: sampleItemSize)) - nextSampleX += sampleItemSize - itemNode.update(size: itemNode.bounds.size, color: self.state.multiColors[i], isSelected: self.state.selection == .index(i)) + }) + self.sampleItemNodes.append(itemNode) + self.insertSubnode(itemNode, aboveSubnode: self.multiColorFieldNode) + animateIn = true } - let fieldX = nextSampleX + sampleItemSpacing + if i != 0 { + nextSampleX += sampleItemSpacing + } + itemNode.frame = CGRect(origin: CGPoint(x: nextSampleX, y: (topPanelHeight - sampleItemSize) / 2.0), size: CGSize(width: sampleItemSize, height: sampleItemSize)) + nextSampleX += sampleItemSize + itemNode.update(size: itemNode.bounds.size, color: UIColor(rgb: self.state.colors[i]), isSelected: self.state.selection == i) - let fieldFrame = CGRect(x: fieldX, y: (topPanelHeight - fieldHeight) / 2.0, width: size.width - fieldX - leftInset, height: fieldHeight) - transition.updateFrame(node: self.multiColorFieldNode, frame: fieldFrame) - self.multiColorFieldNode.updateLayout(size: fieldFrame.size, condensed: false, transition: transition) + if animateIn { + transition.animateTransformScale(node: itemNode, from: 0.1) + itemNode.alpha = 0.0 + transition.updateAlpha(node: itemNode, alpha: 1.0) + } } + if self.sampleItemNodes.count > self.state.colors.count { + for i in self.state.colors.count ..< self.sampleItemNodes.count { + let itemNode = self.sampleItemNodes[i] + transition.updateTransformScale(node: itemNode, scale: 0.1) + transition.updateAlpha(node: itemNode, alpha: 0.0, completion: { [weak itemNode] _ in + itemNode?.removeFromSupernode() + }) + } + self.sampleItemNodes.removeSubrange(self.state.colors.count ..< self.sampleItemNodes.count) + } + + let fieldX = nextSampleX + sampleItemSpacing + + let fieldFrame = CGRect(x: fieldX, y: (topPanelHeight - fieldHeight) / 2.0, width: size.width - fieldX - leftInset - (canAddColors ? (buttonSize.width + sampleItemSpacing) : 0.0), height: fieldHeight) + transition.updateFrame(node: self.multiColorFieldNode, frame: fieldFrame) + self.multiColorFieldNode.updateLayout(size: fieldFrame.size, condensed: false, 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)) @@ -920,21 +792,49 @@ final class WallpaperColorPanelNode: ASDisplayNode { } @objc private func swapPressed() { - self.updateState({ current in + /*self.updateState({ current in var updated = current if let secondColor = current.secondColor { updated.firstColor = secondColor updated.secondColor = current.firstColor } return updated - }) + })*/ } @objc private func addPressed() { self.colorSelected?() self.colorAdded?() + + self.multiColorFieldNode.setSkipEndEditingIfNeeded() + + self.updateState({ current in + var current = current + if current.colors.count < current.maximumNumberOfColors { + if current.colors.isEmpty { + current.colors.append(0xffffff) + } else if current.simpleGradientGeneration { + var hsb = UIColor(rgb: current.colors[0]).hsb + if hsb.1 > 0.5 { + hsb.1 -= 0.15 + } else { + hsb.1 += 0.15 + } + if hsb.0 > 0.5 { + hsb.0 -= 0.05 + } else { + hsb.0 += 0.05 + } + current.colors.append(UIColor(hue: hsb.0, saturation: hsb.1, brightness: hsb.2, alpha: 1.0).rgb) + } else { + current.colors.append(current.colors[current.colors.count - 1]) + } + current.selection = current.colors.count - 1 + } + return current + }) - self.firstColorFieldNode.setSkipEndEditingIfNeeded() + /*self.firstColorFieldNode.setSkipEndEditingIfNeeded() self.updateState({ current in var updated = current @@ -964,7 +864,7 @@ final class WallpaperColorPanelNode: ASDisplayNode { } return updated - }) + })*/ } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift index e7b2785ee7..18f78b4645 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift @@ -157,8 +157,16 @@ private func updatedFileWallpaper(id: Int64? = nil, accessHash: Int64? = nil, sl if let secondColor = secondColor { secondColorValue = secondColor.argb } + + var colors: [UInt32] = [] + if let firstColorValue = firstColorValue { + colors.append(firstColorValue) + } + if let secondColorValue = secondColorValue { + colors.append(secondColorValue) + } - return .file(id: id ?? 0, accessHash: accessHash ?? 0, isCreator: false, isDefault: false, isPattern: isPattern, isDark: false, slug: slug, file: file, settings: WallpaperSettings(color: firstColorValue, bottomColor: secondColorValue, intensity: intensityValue, rotation: rotation)) + return .file(id: id ?? 0, accessHash: accessHash ?? 0, isCreator: false, isDefault: false, isPattern: isPattern, isDark: false, slug: slug, file: file, settings: WallpaperSettings(colors: colors, intensity: intensityValue, rotation: rotation)) } public class WallpaperGalleryController: ViewController { @@ -319,7 +327,7 @@ public class WallpaperGalleryController: ViewController { self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) self.toolbarNode?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings) self.patternPanelNode?.updateTheme(self.presentationData.theme) - self.patternPanelNode?.backgroundColors = self.presentationData.theme.overallDarkAppearance ? (self.presentationData.theme.list.blocksBackgroundColor, nil, nil) : nil + self.patternPanelNode?.backgroundColors = self.presentationData.theme.overallDarkAppearance ? ([self.presentationData.theme.list.blocksBackgroundColor.rgb], nil) : nil self.colorsPanelNode?.updateTheme(self.presentationData.theme) } @@ -444,7 +452,7 @@ public class WallpaperGalleryController: ViewController { let completion: (TelegramWallpaper) -> Void = { wallpaper in let baseSettings = wallpaper.settings - let updatedSettings = WallpaperSettings(blur: options.contains(.blur), motion: options.contains(.motion), color: baseSettings?.color, bottomColor: baseSettings?.bottomColor, additionalColors: baseSettings?.additionalColors ?? [], intensity: baseSettings?.intensity) + let updatedSettings = WallpaperSettings(blur: options.contains(.blur), motion: options.contains(.motion), colors: baseSettings?.colors ?? [], intensity: baseSettings?.intensity) let wallpaper = wallpaper.withUpdatedSettings(updatedSettings) let autoNightModeTriggered = strongSelf.presentationData.autoNightModeTriggered @@ -501,8 +509,8 @@ public class WallpaperGalleryController: ViewController { } } } else if case let .file(file) = wallpaper, let resource = resource { - if wallpaper.isPattern, let color = file.settings.color, let intensity = file.settings.intensity { - let representation = CachedPatternWallpaperRepresentation(color: color, bottomColor: file.settings.bottomColor, intensity: intensity, rotation: file.settings.rotation) + if wallpaper.isPattern, !file.settings.colors.isEmpty, let intensity = file.settings.intensity { + let representation = CachedPatternWallpaperRepresentation(color: file.settings.colors[0], bottomColor: file.settings.colors.count >= 2 ? file.settings.colors[1] : file.settings.colors[0], intensity: intensity, rotation: file.settings.rotation) var data: Data? if let path = strongSelf.context.account.postbox.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) { @@ -557,15 +565,6 @@ public class WallpaperGalleryController: ViewController { settings.motion = options.contains(.motion) updatedWallpaper = updatedWallpaper.withUpdatedSettings(settings) } - if case let .builtin(_, settings) = updatedWallpaper { - var gradient: TelegramWallpaper.Gradient? - if let gradientColors = gradientColors { - if gradientColors != defaultBuiltinWallpaperGradientColors.map({ $0.rgb }) { - gradient = TelegramWallpaper.Gradient(colors: gradientColors) - } - } - updatedWallpaper = .builtin(gradient, settings) - } applyWallpaper(updatedWallpaper) } default: @@ -638,16 +637,13 @@ public class WallpaperGalleryController: ViewController { if let colors = colors { strongSelf.colorsPanelNode?.updateState({ _ in return WallpaperColorPanelNodeState( - selection: .index(0), - firstColor: colors[0], - defaultColor: colors[0], - secondColor: colors[1], - secondColorAvailable: true, + selection: 0, + colors: colors.map(\.rgb), + maximumNumberOfColors: 4, rotateAvailable: false, rotation: 0, preview: false, - simpleGradientGeneration: false, - multiColors: colors + simpleGradientGeneration: false ) }, animated: false) } else { @@ -657,14 +653,14 @@ public class WallpaperGalleryController: ViewController { } } - if let entry = self.currentEntry(), case let .wallpaper(wallpaper, _) = entry, case let .file(_, _, _, _, true, _, _, _ , settings) = wallpaper, let color = settings.color { + if let entry = self.currentEntry(), case let .wallpaper(wallpaper, _) = entry, case let .file(_, _, _, _, true, _, _, _ , settings) = wallpaper, !settings.colors.isEmpty { if self.patternPanelNode?.backgroundColors != nil, let snapshotView = self.patternPanelNode?.scrollNode.view.snapshotContentTree() { self.patternPanelNode?.view.addSubview(snapshotView) snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) { [weak snapshotView] _ in snapshotView?.removeFromSuperview() } } - self.patternPanelNode?.backgroundColors = (UIColor(rgb: color), nil, nil) + self.patternPanelNode?.backgroundColors = ([settings.colors[0]], nil) } } } @@ -720,13 +716,13 @@ public class WallpaperGalleryController: ViewController { if case let .color(color) = wallpaper { entryColor = color } else if case let .file(file) = wallpaper { - entryColor = file.settings.color + entryColor = file.settings.colors.first } } if let entryColor = entryColor { if let pattern = pattern, case let .file(file) = pattern { - let newSettings = WallpaperSettings(blur: file.settings.blur, motion: file.settings.motion, color: entryColor, intensity: intensity) + let newSettings = WallpaperSettings(blur: file.settings.blur, motion: file.settings.motion, colors: [entryColor], intensity: intensity) let newWallpaper = TelegramWallpaper.file(id: file.id, accessHash: file.accessHash, isCreator: file.isCreator, isDefault: file.isDefault, isPattern: pattern.isPattern, isDark: file.isDark, slug: file.slug, file: file.file, settings: newSettings) updatedEntries.append(.wallpaper(newWallpaper, nil)) } else { @@ -766,21 +762,12 @@ public class WallpaperGalleryController: ViewController { switch patternInitialWallpaper { case .color: strongSelf.updateEntries(pattern: pattern, intensity: intensity, preview: preview) - case let .builtin(gradient, _): - if let pattern = pattern, case let .file(file) = pattern { - var gradientColors = gradient?.colors ?? defaultBuiltinWallpaperGradientColors.map({ $0.rgb }) - - let newSettings = WallpaperSettings(blur: false, motion: false, color: gradientColors[0], bottomColor: gradientColors[1], additionalColors: Array(gradientColors.dropFirst(2)), intensity: intensity) - let newWallpaper = TelegramWallpaper.file(id: file.id, accessHash: file.accessHash, isCreator: file.isCreator, isDefault: file.isDefault, isPattern: pattern.isPattern, isDark: file.isDark, slug: file.slug, file: file.file, settings: newSettings) - - strongSelf.updateEntries(wallpaper: newWallpaper, preview: preview) - } default: break } } } - patternPanelNode.backgroundColors = self.presentationData.theme.overallDarkAppearance ? (self.presentationData.theme.list.blocksBackgroundColor, nil, nil) : nil + patternPanelNode.backgroundColors = self.presentationData.theme.overallDarkAppearance ? ([self.presentationData.theme.list.blocksBackgroundColor.rgb], nil) : nil self.patternPanelNode = patternPanelNode currentPatternPanelNode = patternPanelNode self.overlayNode?.insertSubnode(patternPanelNode, belowSubnode: self.toolbarNode!) @@ -795,7 +782,7 @@ public class WallpaperGalleryController: ViewController { currentColorsPanelNode = colorsPanelNode self.overlayNode?.insertSubnode(colorsPanelNode, belowSubnode: self.toolbarNode!) - colorsPanelNode.multiColorsChanged = { [weak self] colors in + colorsPanelNode.colorsChanged = { [weak self] colors, _ in guard let strongSelf = self else { return } @@ -803,12 +790,10 @@ public class WallpaperGalleryController: ViewController { return } - var wallpaper: TelegramWallpaper = .builtin(TelegramWallpaper.Gradient(colors: colors.map { - $0.rgb - }), WallpaperSettings(blur: false, motion: false, color: nil, bottomColor: nil, intensity: nil, rotation: nil)) + var wallpaper: TelegramWallpaper = .gradient(colors, WallpaperSettings(blur: false, motion: false, colors: [], intensity: nil, rotation: nil)) if case .file = currentWallpaper { - wallpaper = currentWallpaper.withUpdatedSettings(WallpaperSettings(blur: false, motion: false, color: colors[0].rgb, bottomColor: colors[1].rgb, additionalColors: colors.dropFirst(2).map({ $0.rgb }), intensity: nil, rotation: nil)) + wallpaper = currentWallpaper.withUpdatedSettings(WallpaperSettings(blur: false, motion: false, colors: colors, intensity: nil, rotation: nil)) } strongSelf.updateEntries(wallpaper: wallpaper) @@ -893,11 +878,11 @@ public class WallpaperGalleryController: ViewController { }) case let .file(_, _, _, _, isPattern, _, slug, _, settings): if isPattern { - if let color = settings.color { - if let bottomColor = settings.bottomColor { - options.append("bg_color=\(UIColor(rgb: color).hexString)-\(UIColor(rgb: bottomColor).hexString)") + if !settings.colors.isEmpty { + if settings.colors.count >= 2 { + options.append("bg_color=\(UIColor(rgb: settings.colors[0]).hexString)-\(UIColor(rgb: settings.colors[1]).hexString)") } else { - options.append("bg_color=\(UIColor(rgb: color).hexString)") + options.append("bg_color=\(UIColor(rgb: settings.colors[0]).hexString)") } } if let intensity = settings.intensity { @@ -916,8 +901,8 @@ public class WallpaperGalleryController: ViewController { controller = ShareController(context: context, subject: .url("https://t.me/bg/\(slug)\(optionsString)")) case let .color(color): controller = ShareController(context: context, subject: .url("https://t.me/bg/\(UIColor(rgb: color).hexString)")) - case let .gradient(topColor, bottomColor, _): - controller = ShareController(context: context, subject:. url("https://t.me/bg/\(UIColor(rgb: topColor).hexString)-\(UIColor(rgb: bottomColor).hexString)")) + case let .gradient(colors, _): + controller = ShareController(context: context, subject:. url("https://t.me/bg/\(UIColor(rgb: colors[0]).hexString)-\(UIColor(rgb: colors[1]).hexString)")) default: break } diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 0955ca888b..d14fdba6d6 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -274,10 +274,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { switch entry { case let .wallpaper(wallpaper, _): - if case .builtin = wallpaper { - self.nativeNode.isHidden = false - self.nativeNode.update(wallpaper: wallpaper) - } else if case let .file(_, _, _, _, isPattern, _, _, _, settings) = wallpaper, isPattern, !settings.additionalColors.isEmpty { + if case let .file(_, _, _, _, isPattern, _, _, _, settings) = wallpaper, isPattern, settings.colors.count >= 3 { self.nativeNode.isHidden = false self.nativeNode.update(wallpaper: wallpaper) } else { @@ -292,11 +289,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.initialWallpaper = wallpaper switch wallpaper { - case let .builtin(gradient, _): - let gradientColors = self.calculateGradientColors() ?? defaultBuiltinWallpaperGradientColors - - self.colorsButtonNode.colors = gradientColors - + case .builtin: displaySize = CGSize(width: 1308.0, height: 2688.0).fitted(CGSize(width: 1280.0, height: 1280.0)).dividedByScreenScale().integralFloor contentSize = displaySize signal = settingsBuiltinWallpaperImage(account: self.context.account) @@ -315,10 +308,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode { actionSignal = .single(defaultAction) colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox) isBlurrable = false - case let .gradient(topColor, bottomColor, settings): + case let .gradient(colors, settings): displaySize = CGSize(width: 1.0, height: 1.0) contentSize = displaySize - signal = gradientImage([UIColor(rgb: topColor), UIColor(rgb: bottomColor)], rotation: settings.rotation) + signal = gradientImage([UIColor(rgb: colors[0]), UIColor(rgb: colors[1])], rotation: settings.rotation) fetchSignal = .complete() statusSignal = .single(.Local) subtitleSignal = .single(nil) @@ -345,15 +338,15 @@ final class WallpaperGalleryItemNode: GalleryItemNode { var patternColor = UIColor(rgb: 0xd6e2ee, alpha: 0.5) var patternIntensity: CGFloat = 0.5 - if let color = file.settings.color { + if !file.settings.colors.isEmpty { if let intensity = file.settings.intensity { patternIntensity = CGFloat(intensity) / 100.0 } - patternColor = UIColor(rgb: color, alpha: patternIntensity) + patternColor = UIColor(rgb: file.settings.colors[0], alpha: patternIntensity) patternColors.append(patternColor) - if let bottomColor = file.settings.bottomColor { - patternColors.append(UIColor(rgb: bottomColor, alpha: patternIntensity)) + if file.settings.colors.count >= 2 { + patternColors.append(UIColor(rgb: file.settings.colors[1], alpha: patternIntensity)) } } @@ -361,7 +354,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.backgroundColor = patternColor.withAlphaComponent(1.0) - if let previousEntry = previousEntry, case let .wallpaper(wallpaper, _) = previousEntry, case let .file(previousFile) = wallpaper, file.id == previousFile.id && (file.settings.color != previousFile.settings.color || file.settings.intensity != previousFile.settings.intensity) && self.colorPreview == self.arguments.colorPreview { + if let previousEntry = previousEntry, case let .wallpaper(wallpaper, _) = previousEntry, case let .file(previousFile) = wallpaper, file.id == previousFile.id && (file.settings.colors != previousFile.settings.colors || file.settings.intensity != previousFile.settings.intensity) && self.colorPreview == self.arguments.colorPreview { let makeImageLayout = self.imageNode.asyncLayout() Queue.concurrentDefaultQueue().async { @@ -381,7 +374,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.colorPreview = self.arguments.colorPreview - if !file.settings.additionalColors.isEmpty { + if file.settings.colors.count >= 3 { signal = .single({ _ in nil }) } else { signal = patternWallpaperImage(account: self.context.account, accountManager: self.context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true) @@ -745,19 +738,9 @@ final class WallpaperGalleryItemNode: GalleryItemNode { switch entry { case let .wallpaper(wallpaper, _): switch wallpaper { - case let .builtin(gradient, _): - let gradientColors = gradient?.colors.map({ color in - return UIColor(rgb: color) - }) ?? defaultBuiltinWallpaperGradientColors - return gradientColors case let .file(_, _, _, _, _, _, _, _, settings): - if let topColor = settings.color, let bottomColor = settings.bottomColor, settings.additionalColors.count == 2 { - return [ - UIColor(rgb: topColor), - UIColor(rgb: bottomColor), - UIColor(rgb: settings.additionalColors[0]), - UIColor(rgb: settings.additionalColors[1]) - ] + if settings.colors.count >= 3 { + return settings.colors.map(UIColor.init(rgb:)) } else { return nil } @@ -896,17 +879,9 @@ final class WallpaperGalleryItemNode: GalleryItemNode { motionFrame = rightButtonFrame case let .wallpaper(wallpaper, _): switch wallpaper { - case let .builtin(gradient, _): - self.colorsButtonNode.colors = self.calculateGradientColors() - - motionAlpha = 0.0 - patternAlpha = 1.0 - - patternFrame = leftButtonFrame.offsetBy(dx: -centerOffset, dy: 0.0) - colorsFrame = colorsFrame.offsetBy(dx: centerOffset, dy: 0.0) - playAlpha = 1.0 - - colorsAlpha = 1.0 + case .builtin: + motionAlpha = 1.0 + motionFrame = centerButtonFrame case .color: patternAlpha = 1.0 if self.patternButtonNode.isSelected { @@ -922,7 +897,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { case .gradient: motionAlpha = 1.0 case let .file(file): - if !file.settings.additionalColors.isEmpty { + if file.settings.colors.count >= 3 { self.colorsButtonNode.colors = self.calculateGradientColors() motionAlpha = 0.0 diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift index ef72a560ee..bd7c071a3d 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift @@ -189,10 +189,9 @@ final class WallpaperPatternPanelNode: ASDisplayNode { } } - var backgroundColors: (UIColor, UIColor?, Int32?)? = nil { + var backgroundColors: ([UInt32], Int32?)? = nil { didSet { - if oldValue?.0.rgb != self.backgroundColors?.0.rgb || oldValue?.1?.rgb != self.backgroundColors?.1?.rgb - || oldValue?.2 != self.backgroundColors?.2 { + if oldValue?.0 != self.backgroundColors?.0 || oldValue?.1 != self.backgroundColors?.1 { self.updateWallpapers() } } @@ -285,7 +284,7 @@ final class WallpaperPatternPanelNode: ASDisplayNode { node.removeFromSupernode() } - let backgroundColors = self.backgroundColors ?? (UIColor(rgb: 0xd6e2ee), nil, nil) + let backgroundColors = self.backgroundColors ?? ([0xd6e2ee], nil) var selectedFileId: Int64? if let currentWallpaper = self.currentWallpaper, case let .file(file) = currentWallpaper { @@ -299,7 +298,7 @@ final class WallpaperPatternPanelNode: ASDisplayNode { var updatedWallpaper = wallpaper if case let .file(file) = updatedWallpaper { - let settings = WallpaperSettings(color: backgroundColors.0.rgb, bottomColor: backgroundColors.1.flatMap { $0.rgb }, intensity: 100, rotation: backgroundColors.2) + let settings = WallpaperSettings(colors: backgroundColors.0, intensity: 100, rotation: backgroundColors.1) updatedWallpaper = .file(id: file.id, accessHash: file.accessHash, isCreator: file.isCreator, isDefault: file.isDefault, isPattern: updatedWallpaper.isPattern, isDark: file.isDark, slug: file.slug, file: file.file, settings: settings) } diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 0f794a6893..2d1cc1eaac 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -987,7 +987,6 @@ final class MessageStoryRenderer { self.instantChatBackgroundNode = WallpaperBackgroundNode(context: context) self.instantChatBackgroundNode.displaysAsynchronously = false - self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper) self.messagesContainerNode = ASDisplayNode() self.messagesContainerNode.clipsToBounds = true diff --git a/submodules/SyncCore/Sources/TelegramWallpaper.swift b/submodules/SyncCore/Sources/TelegramWallpaper.swift index 350b2a0b9b..c7c02cab2e 100644 --- a/submodules/SyncCore/Sources/TelegramWallpaper.swift +++ b/submodules/SyncCore/Sources/TelegramWallpaper.swift @@ -3,18 +3,14 @@ import Postbox public struct WallpaperSettings: PostboxCoding, Equatable { public var blur: Bool public var motion: Bool - public var color: UInt32? - public var bottomColor: UInt32? - public var additionalColors: [UInt32] + public var colors: [UInt32] public var intensity: Int32? public var rotation: Int32? - public init(blur: Bool = false, motion: Bool = false, color: UInt32? = nil, bottomColor: UInt32? = nil, additionalColors: [UInt32] = [], intensity: Int32? = nil, rotation: Int32? = nil) { + public init(blur: Bool = false, motion: Bool = false, colors: [UInt32] = [], intensity: Int32? = nil, rotation: Int32? = nil) { self.blur = blur self.motion = motion - self.color = color - self.bottomColor = bottomColor - self.additionalColors = additionalColors + self.colors = colors self.intensity = intensity self.rotation = rotation } @@ -22,9 +18,16 @@ public struct WallpaperSettings: PostboxCoding, Equatable { public init(decoder: PostboxDecoder) { self.blur = decoder.decodeInt32ForKey("b", orElse: 0) != 0 self.motion = decoder.decodeInt32ForKey("m", orElse: 0) != 0 - self.color = decoder.decodeOptionalInt32ForKey("c").flatMap { UInt32(bitPattern: $0) } - self.bottomColor = decoder.decodeOptionalInt32ForKey("bc").flatMap { UInt32(bitPattern: $0) } - self.additionalColors = decoder.decodeInt32ArrayForKey("additionalColors").map { UInt32(bitPattern: $0) } + if let topColor = decoder.decodeOptionalInt32ForKey("c").flatMap(UInt32.init(bitPattern:)) { + var colors: [UInt32] = [topColor] + if let bottomColor = decoder.decodeOptionalInt32ForKey("bc").flatMap(UInt32.init(bitPattern:)) { + colors.append(bottomColor) + } + self.colors = colors + } else { + self.colors = decoder.decodeInt32ArrayForKey("colors").map(UInt32.init(bitPattern:)) + } + self.intensity = decoder.decodeOptionalInt32ForKey("i") self.rotation = decoder.decodeOptionalInt32ForKey("r") } @@ -32,17 +35,7 @@ public struct WallpaperSettings: PostboxCoding, Equatable { public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt32(self.blur ? 1 : 0, forKey: "b") encoder.encodeInt32(self.motion ? 1 : 0, forKey: "m") - if let color = self.color { - encoder.encodeInt32(Int32(bitPattern: color), forKey: "c") - } else { - encoder.encodeNil(forKey: "c") - } - if let bottomColor = self.bottomColor { - encoder.encodeInt32(Int32(bitPattern: bottomColor), forKey: "bc") - } else { - encoder.encodeNil(forKey: "bc") - } - encoder.encodeInt32Array(self.additionalColors.map(Int32.init(bitPattern:)), forKey: "additionalColors") + encoder.encodeInt32Array(self.colors.map(Int32.init(bitPattern:)), forKey: "colors") if let intensity = self.intensity { encoder.encodeInt32(intensity, forKey: "i") } else { @@ -62,72 +55,61 @@ public struct WallpaperSettings: PostboxCoding, Equatable { if lhs.motion != rhs.motion { return false } - if lhs.color != rhs.color { - return false - } - if lhs.bottomColor != rhs.bottomColor { - return false - } - if lhs.additionalColors != rhs.additionalColors { + if lhs.colors != rhs.colors { return false } if lhs.intensity != rhs.intensity { - return false - } + return false + } if lhs.rotation != rhs.rotation { - return false - } + return false + } return true } } public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable { - public struct Gradient: Equatable, PostboxCoding { - public var colors: [UInt32] - - public init(colors: [UInt32]) { - self.colors = colors - } - - public init(decoder: PostboxDecoder) { - self.colors = decoder.decodeInt32ArrayForKey("colors").map({ UInt32(bitPattern: $0) }) - } - - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt32Array(self.colors.map({ Int32(bitPattern: $0) }), forKey: "colors") - } - } - - case builtin(Gradient?, WallpaperSettings) + case builtin(WallpaperSettings) case color(UInt32) - case gradient(UInt32, UInt32, WallpaperSettings) + case gradient([UInt32], WallpaperSettings) case image([TelegramMediaImageRepresentation], WallpaperSettings) case file(id: Int64, accessHash: Int64, isCreator: Bool, isDefault: Bool, isPattern: Bool, isDark: Bool, slug: String, file: TelegramMediaFile, settings: WallpaperSettings) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("v", orElse: 0) { - case 0: - let gradient = decoder.decodeObjectForKey("gradient", decoder: { Gradient(decoder: $0) }) as? Gradient - let settings = decoder.decodeObjectForKey("settings", decoder: { WallpaperSettings(decoder: $0) }) as? WallpaperSettings ?? WallpaperSettings() - self = .builtin(gradient, settings) - case 1: - self = .color(UInt32(bitPattern: decoder.decodeInt32ForKey("c", orElse: 0))) - case 2: - let settings = decoder.decodeObjectForKey("settings", decoder: { WallpaperSettings(decoder: $0) }) as? WallpaperSettings ?? WallpaperSettings() - self = .image(decoder.decodeObjectArrayWithDecoderForKey("i"), settings) - case 3: - let settings = decoder.decodeObjectForKey("settings", decoder: { WallpaperSettings(decoder: $0) }) as? WallpaperSettings ?? WallpaperSettings() - if let file = decoder.decodeObjectForKey("file", decoder: { TelegramMediaFile(decoder: $0) }) as? TelegramMediaFile { - self = .file(id: decoder.decodeInt64ForKey("id", orElse: 0), accessHash: decoder.decodeInt64ForKey("accessHash", orElse: 0), isCreator: decoder.decodeInt32ForKey("isCreator", orElse: 0) != 0, isDefault: decoder.decodeInt32ForKey("isDefault", orElse: 0) != 0, isPattern: decoder.decodeInt32ForKey("isPattern", orElse: 0) != 0, isDark: decoder.decodeInt32ForKey("isDark", orElse: 0) != 0, slug: decoder.decodeStringForKey("slug", orElse: ""), file: file, settings: settings) - } else { - self = .color(0xffffff) - } - case 4: - let settings = decoder.decodeObjectForKey("settings", decoder: { WallpaperSettings(decoder: $0) }) as? WallpaperSettings ?? WallpaperSettings() - self = .gradient(UInt32(bitPattern: decoder.decodeInt32ForKey("c1", orElse: 0)), UInt32(bitPattern: decoder.decodeInt32ForKey("c2", orElse: 0)), settings) - default: - assertionFailure() + case 0: + let settings = decoder.decodeObjectForKey("settings", decoder: { WallpaperSettings(decoder: $0) }) as? WallpaperSettings ?? WallpaperSettings() + self = .builtin(settings) + case 1: + self = .color(UInt32(bitPattern: decoder.decodeInt32ForKey("c", orElse: 0))) + case 2: + let settings = decoder.decodeObjectForKey("settings", decoder: { WallpaperSettings(decoder: $0) }) as? WallpaperSettings ?? WallpaperSettings() + self = .image(decoder.decodeObjectArrayWithDecoderForKey("i"), settings) + case 3: + let settings = decoder.decodeObjectForKey("settings", decoder: { WallpaperSettings(decoder: $0) }) as? WallpaperSettings ?? WallpaperSettings() + if let file = decoder.decodeObjectForKey("file", decoder: { TelegramMediaFile(decoder: $0) }) as? TelegramMediaFile { + self = .file(id: decoder.decodeInt64ForKey("id", orElse: 0), accessHash: decoder.decodeInt64ForKey("accessHash", orElse: 0), isCreator: decoder.decodeInt32ForKey("isCreator", orElse: 0) != 0, isDefault: decoder.decodeInt32ForKey("isDefault", orElse: 0) != 0, isPattern: decoder.decodeInt32ForKey("isPattern", orElse: 0) != 0, isDark: decoder.decodeInt32ForKey("isDark", orElse: 0) != 0, slug: decoder.decodeStringForKey("slug", orElse: ""), file: file, settings: settings) + } else { self = .color(0xffffff) + } + case 4: + let settings = decoder.decodeObjectForKey("settings", decoder: { WallpaperSettings(decoder: $0) }) as? WallpaperSettings ?? WallpaperSettings() + + var colors: [UInt32] = [] + + if let topColor = decoder.decodeOptionalInt32ForKey("c1").flatMap(UInt32.init(bitPattern:)) { + colors.append(topColor) + if let bottomColor = decoder.decodeOptionalInt32ForKey("c2").flatMap(UInt32.init(bitPattern:)) { + colors.append(bottomColor) + } + } else { + colors = decoder.decodeInt32ArrayForKey("colors").map(UInt32.init(bitPattern:)) + } + + self = .gradient(colors, settings) + default: + assertionFailure() + self = .color(0xffffff) } } @@ -142,21 +124,15 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable { public func encode(_ encoder: PostboxEncoder) { switch self { - case let .builtin(gradient, settings): + case let .builtin(settings): encoder.encodeInt32(0, forKey: "v") - if let gradient = gradient { - encoder.encodeObject(gradient, forKey: "gradient") - } else { - encoder.encodeNil(forKey: "gradient") - } encoder.encodeObject(settings, forKey: "settings") case let .color(color): encoder.encodeInt32(1, forKey: "v") encoder.encodeInt32(Int32(bitPattern: color), forKey: "c") - case let .gradient(topColor, bottomColor, settings): + case let .gradient(colors, settings): encoder.encodeInt32(4, forKey: "v") - encoder.encodeInt32(Int32(bitPattern: topColor), forKey: "c1") - encoder.encodeInt32(Int32(bitPattern: bottomColor), forKey: "c2") + encoder.encodeInt32Array(colors.map(Int32.init(bitPattern:)), forKey: "colors") encoder.encodeObject(settings, forKey: "settings") case let .image(representations, settings): encoder.encodeInt32(2, forKey: "v") @@ -178,8 +154,8 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable { public static func ==(lhs: TelegramWallpaper, rhs: TelegramWallpaper) -> Bool { switch lhs { - case let .builtin(gradient, settings): - if case .builtin(gradient, settings) = rhs { + case let .builtin(settings): + if case .builtin(settings) = rhs { return true } else { return false @@ -190,8 +166,8 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable { } else { return false } - case let .gradient(topColor, bottomColor, settings): - if case .gradient(topColor, bottomColor, settings) = rhs { + case let .gradient(colors, settings): + if case .gradient(colors, settings) = rhs { return true } else { return false @@ -213,8 +189,8 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable { public func isBasicallyEqual(to wallpaper: TelegramWallpaper) -> Bool { switch self { - case let .builtin(gradient, _): - if case .builtin(gradient, _) = wallpaper { + case .builtin: + if case .builtin = wallpaper { return true } else { return false @@ -225,8 +201,8 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable { } else { return false } - case let .gradient(topColor, bottomColor, _): - if case .gradient(topColor, bottomColor, _) = wallpaper { + case let .gradient(colors, _): + if case .gradient(colors, _) = wallpaper { return true } else { return false @@ -238,7 +214,7 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable { return false } case let .file(_, _, _, _, _, _, lhsSlug, _, lhsSettings): - if case let .file(_, _, _, _, _, _, rhsSlug, _, rhsSettings) = wallpaper, lhsSlug == rhsSlug, lhsSettings.color == rhsSettings.color && lhsSettings.intensity == rhsSettings.intensity { + if case let .file(_, _, _, _, _, _, rhsSlug, _, rhsSettings) = wallpaper, lhsSlug == rhsSlug, lhsSettings.colors == rhsSettings.colors && lhsSettings.intensity == rhsSettings.intensity { return true } else { return false @@ -248,7 +224,7 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable { public var settings: WallpaperSettings? { switch self { - case let .builtin(_, settings), let .gradient(_, _, settings), let .image(_, settings), let .file(_, _, _, _, _, _, _, _, settings): + case let .builtin(settings), let .gradient(_, settings), let .image(_, settings), let .file(_, _, _, _, _, _, _, _, settings): return settings default: return nil @@ -257,16 +233,16 @@ public enum TelegramWallpaper: OrderedItemListEntryContents, Equatable { public func withUpdatedSettings(_ settings: WallpaperSettings) -> TelegramWallpaper { switch self { - case let .builtin(gradient, _): - return .builtin(gradient, settings) + case .builtin: + return .builtin(settings) case .color: return self - case let .gradient(topColor, bottomColor, _): - return .gradient(topColor, bottomColor, settings) + case let .gradient(colors, _): + return .gradient(colors, settings) case let .image(representations, _): return .image(representations, settings) case let .file(id, accessHash, isCreator, isDefault, isPattern, isDark, slug, file, _): - return .file(id: id, accessHash: accessHash, isCreator: isCreator, isDefault: isDefault, isPattern: settings.color != nil ? true : isPattern, isDark: isDark, slug: slug, file: file, settings: settings) + return .file(id: id, accessHash: accessHash, isCreator: isCreator, isDefault: isDefault, isPattern: settings.colors.isEmpty ? isPattern : true, isDark: isDark, slug: slug, file: file, settings: settings) } } } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 7b747827f1..157cda2a0e 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -116,10 +116,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1945767479] = { return Api.help.SupportName.parse_supportName($0) } dict[-288727837] = { return Api.LangPackLanguage.parse_langPackLanguage($0) } dict[-567037804] = { return Api.VideoSize.parse_videoSize($0) } - dict[497489295] = { return Api.help.AppUpdate.parse_appUpdate($0) } + dict[-860107216] = { return Api.help.AppUpdate.parse_appUpdate($0) } dict[-1000708810] = { return Api.help.AppUpdate.parse_noAppUpdate($0) } dict[-209337866] = { return Api.LangPackDifference.parse_langPackDifference($0) } - dict[84438264] = { return Api.WallPaperSettings.parse_wallPaperSettings($0) } + dict[499236004] = { return Api.WallPaperSettings.parse_wallPaperSettings($0) } dict[-1519029347] = { return Api.EmojiURL.parse_emojiURL($0) } dict[1611985938] = { return Api.StatsGroupTopAdmin.parse_statsGroupTopAdmin($0) } dict[-541588713] = { return Api.channels.ChannelParticipant.parse_channelParticipant($0) } @@ -261,7 +261,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-2112423005] = { return Api.Update.parse_updateTheme($0) } dict[-2027964103] = { return Api.Update.parse_updateGeoLiveViewed($0) } dict[1448076945] = { return Api.Update.parse_updateLoginToken($0) } - dict[1123585836] = { return Api.Update.parse_updateMessagePollVote($0) } + dict[938909451] = { return Api.Update.parse_updateMessagePollVote($0) } dict[654302845] = { return Api.Update.parse_updateDialogFilter($0) } dict[-1512627963] = { return Api.Update.parse_updateDialogFilterOrder($0) } dict[889491791] = { return Api.Update.parse_updateDialogFilters($0) } diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index dd8176ad24..4b8997261f 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -3302,17 +3302,19 @@ public extension Api { } public enum WallPaperSettings: TypeConstructorDescription { - case wallPaperSettings(flags: Int32, backgroundColor: Int32?, secondBackgroundColor: Int32?, intensity: Int32?, rotation: Int32?) + case wallPaperSettings(flags: Int32, backgroundColor: Int32?, secondBackgroundColor: Int32?, thirdBackgroundColor: Int32?, fourthBackgroundColor: Int32?, intensity: Int32?, rotation: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .wallPaperSettings(let flags, let backgroundColor, let secondBackgroundColor, let intensity, let rotation): + case .wallPaperSettings(let flags, let backgroundColor, let secondBackgroundColor, let thirdBackgroundColor, let fourthBackgroundColor, let intensity, let rotation): if boxed { - buffer.appendInt32(84438264) + buffer.appendInt32(499236004) } serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {serializeInt32(backgroundColor!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 4) != 0 {serializeInt32(secondBackgroundColor!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {serializeInt32(thirdBackgroundColor!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 6) != 0 {serializeInt32(fourthBackgroundColor!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 3) != 0 {serializeInt32(intensity!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 4) != 0 {serializeInt32(rotation!, buffer: buffer, boxed: false)} break @@ -3321,8 +3323,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .wallPaperSettings(let flags, let backgroundColor, let secondBackgroundColor, let intensity, let rotation): - return ("wallPaperSettings", [("flags", flags), ("backgroundColor", backgroundColor), ("secondBackgroundColor", secondBackgroundColor), ("intensity", intensity), ("rotation", rotation)]) + case .wallPaperSettings(let flags, let backgroundColor, let secondBackgroundColor, let thirdBackgroundColor, let fourthBackgroundColor, let intensity, let rotation): + return ("wallPaperSettings", [("flags", flags), ("backgroundColor", backgroundColor), ("secondBackgroundColor", secondBackgroundColor), ("thirdBackgroundColor", thirdBackgroundColor), ("fourthBackgroundColor", fourthBackgroundColor), ("intensity", intensity), ("rotation", rotation)]) } } @@ -3334,16 +3336,22 @@ public extension Api { var _3: Int32? if Int(_1!) & Int(1 << 4) != 0 {_3 = reader.readInt32() } var _4: Int32? - if Int(_1!) & Int(1 << 3) != 0 {_4 = reader.readInt32() } + if Int(_1!) & Int(1 << 5) != 0 {_4 = reader.readInt32() } var _5: Int32? - if Int(_1!) & Int(1 << 4) != 0 {_5 = reader.readInt32() } + if Int(_1!) & Int(1 << 6) != 0 {_5 = reader.readInt32() } + var _6: Int32? + if Int(_1!) & Int(1 << 3) != 0 {_6 = reader.readInt32() } + var _7: Int32? + if Int(_1!) & Int(1 << 4) != 0 {_7 = reader.readInt32() } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 4) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.WallPaperSettings.wallPaperSettings(flags: _1!, backgroundColor: _2, secondBackgroundColor: _3, intensity: _4, rotation: _5) + let _c4 = (Int(_1!) & Int(1 << 5) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 6) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.WallPaperSettings.wallPaperSettings(flags: _1!, backgroundColor: _2, secondBackgroundColor: _3, thirdBackgroundColor: _4, fourthBackgroundColor: _5, intensity: _6, rotation: _7) } else { return nil @@ -4662,7 +4670,7 @@ public extension Api { case updateTheme(theme: Api.Theme) case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32) case updateLoginToken - case updateMessagePollVote(pollId: Int64, userId: Int32, options: [Buffer]) + case updateMessagePollVote(pollId: Int64, userId: Int32, options: [Buffer], qts: Int32) case updateDialogFilter(flags: Int32, id: Int32, filter: Api.DialogFilter?) case updateDialogFilterOrder(order: [Int32]) case updateDialogFilters @@ -5291,9 +5299,9 @@ public extension Api { } break - case .updateMessagePollVote(let pollId, let userId, let options): + case .updateMessagePollVote(let pollId, let userId, let options, let qts): if boxed { - buffer.appendInt32(1123585836) + buffer.appendInt32(938909451) } serializeInt64(pollId, buffer: buffer, boxed: false) serializeInt32(userId, buffer: buffer, boxed: false) @@ -5302,6 +5310,7 @@ public extension Api { for item in options { serializeBytes(item, buffer: buffer, boxed: false) } + serializeInt32(qts, buffer: buffer, boxed: false) break case .updateDialogFilter(let flags, let id, let filter): if boxed { @@ -5632,8 +5641,8 @@ public extension Api { return ("updateGeoLiveViewed", [("peer", peer), ("msgId", msgId)]) case .updateLoginToken: return ("updateLoginToken", []) - case .updateMessagePollVote(let pollId, let userId, let options): - return ("updateMessagePollVote", [("pollId", pollId), ("userId", userId), ("options", options)]) + case .updateMessagePollVote(let pollId, let userId, let options, let qts): + return ("updateMessagePollVote", [("pollId", pollId), ("userId", userId), ("options", options), ("qts", qts)]) case .updateDialogFilter(let flags, let id, let filter): return ("updateDialogFilter", [("flags", flags), ("id", id), ("filter", filter)]) case .updateDialogFilterOrder(let order): @@ -6904,11 +6913,14 @@ public extension Api { if let _ = reader.readInt32() { _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) } + var _4: Int32? + _4 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateMessagePollVote(pollId: _1!, userId: _2!, options: _3!) + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.Update.updateMessagePollVote(pollId: _1!, userId: _2!, options: _3!, qts: _4!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index f27b347425..da75f10e38 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -1815,14 +1815,14 @@ public struct help { } public enum AppUpdate: TypeConstructorDescription { - case appUpdate(flags: Int32, id: Int32, version: String, text: String, entities: [Api.MessageEntity], document: Api.Document?, url: String?) + case appUpdate(flags: Int32, id: Int32, version: String, text: String, entities: [Api.MessageEntity], document: Api.Document?, url: String?, sticker: Api.Document?) case noAppUpdate public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .appUpdate(let flags, let id, let version, let text, let entities, let document, let url): + case .appUpdate(let flags, let id, let version, let text, let entities, let document, let url, let sticker): if boxed { - buffer.appendInt32(497489295) + buffer.appendInt32(-860107216) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) @@ -1835,6 +1835,7 @@ public struct help { } if Int(flags) & Int(1 << 1) != 0 {document!.serialize(buffer, true)} if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {sticker!.serialize(buffer, true)} break case .noAppUpdate: if boxed { @@ -1847,8 +1848,8 @@ public struct help { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .appUpdate(let flags, let id, let version, let text, let entities, let document, let url): - return ("appUpdate", [("flags", flags), ("id", id), ("version", version), ("text", text), ("entities", entities), ("document", document), ("url", url)]) + case .appUpdate(let flags, let id, let version, let text, let entities, let document, let url, let sticker): + return ("appUpdate", [("flags", flags), ("id", id), ("version", version), ("text", text), ("entities", entities), ("document", document), ("url", url), ("sticker", sticker)]) case .noAppUpdate: return ("noAppUpdate", []) } @@ -1873,6 +1874,10 @@ public struct help { } } var _7: String? if Int(_1!) & Int(1 << 2) != 0 {_7 = parseString(reader) } + var _8: Api.Document? + if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.Document + } } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -1880,8 +1885,9 @@ public struct help { let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.help.AppUpdate.appUpdate(flags: _1!, id: _2!, version: _3!, text: _4!, entities: _5!, document: _6, url: _7) + let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { + return Api.help.AppUpdate.appUpdate(flags: _1!, id: _2!, version: _3!, text: _4!, entities: _5!, document: _6, url: _7, sticker: _8) } else { return nil diff --git a/submodules/TelegramCore/Sources/ApiUtils/Wallpaper.swift b/submodules/TelegramCore/Sources/ApiUtils/Wallpaper.swift index 4d41fa0368..179a20ef14 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/Wallpaper.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/Wallpaper.swift @@ -8,16 +8,31 @@ import SyncCore extension WallpaperSettings { init(apiWallpaperSettings: Api.WallPaperSettings) { switch apiWallpaperSettings { - case let .wallPaperSettings(flags, backgroundColor, secondBackgroundColor, intensity, rotation): - self = WallpaperSettings(blur: (flags & 1 << 1) != 0, motion: (flags & 1 << 2) != 0, color: backgroundColor.flatMap { UInt32(bitPattern: $0) }, bottomColor: secondBackgroundColor.flatMap { UInt32(bitPattern: $0) }, intensity: intensity, rotation: rotation) + case let .wallPaperSettings(flags, backgroundColor, secondBackgroundColor, thirdBackgroundColor, fourthBackgroundColor, intensity, rotation): + var colors: [UInt32] = [] + if let backgroundColor = backgroundColor { + colors.append(UInt32(bitPattern: backgroundColor)) + } + if let secondBackgroundColor = secondBackgroundColor { + colors.append(UInt32(bitPattern: secondBackgroundColor)) + } + if let thirdBackgroundColor = thirdBackgroundColor { + colors.append(UInt32(bitPattern: thirdBackgroundColor)) + } + if let fourthBackgroundColor = fourthBackgroundColor { + colors.append(UInt32(bitPattern: fourthBackgroundColor)) + } + self = WallpaperSettings(blur: (flags & 1 << 1) != 0, motion: (flags & 1 << 2) != 0, colors: colors, intensity: intensity, rotation: rotation) } } } func apiWallpaperSettings(_ wallpaperSettings: WallpaperSettings) -> Api.WallPaperSettings { var flags: Int32 = 0 - if let _ = wallpaperSettings.color { + var backgroundColor: Int32? + if wallpaperSettings.colors.count >= 1 { flags |= (1 << 0) + backgroundColor = Int32(bitPattern: wallpaperSettings.colors[0]) } if wallpaperSettings.blur { flags |= (1 << 1) @@ -28,10 +43,22 @@ func apiWallpaperSettings(_ wallpaperSettings: WallpaperSettings) -> Api.WallPap if let _ = wallpaperSettings.intensity { flags |= (1 << 3) } - if let _ = wallpaperSettings.bottomColor { + var secondBackgroundColor: Int32? + if wallpaperSettings.colors.count >= 2 { flags |= (1 << 4) + secondBackgroundColor = Int32(bitPattern: wallpaperSettings.colors[1]) } - return .wallPaperSettings(flags: flags, backgroundColor: wallpaperSettings.color.flatMap { Int32(bitPattern: $0) }, secondBackgroundColor: wallpaperSettings.bottomColor.flatMap { Int32(bitPattern: $0) }, intensity: wallpaperSettings.intensity, rotation: wallpaperSettings.rotation ?? 0) + var thirdBackgroundColor: Int32? + if wallpaperSettings.colors.count >= 3 { + flags |= (1 << 5) + thirdBackgroundColor = Int32(bitPattern: wallpaperSettings.colors[2]) + } + var fourthBackgroundColor: Int32? + if wallpaperSettings.colors.count >= 4 { + flags |= (1 << 6) + fourthBackgroundColor = Int32(bitPattern: wallpaperSettings.colors[3]) + } + return .wallPaperSettings(flags: flags, backgroundColor: backgroundColor, secondBackgroundColor: secondBackgroundColor, thirdBackgroundColor: thirdBackgroundColor, fourthBackgroundColor: fourthBackgroundColor, intensity: wallpaperSettings.intensity, rotation: wallpaperSettings.rotation ?? 0) } extension TelegramWallpaper { @@ -50,12 +77,15 @@ extension TelegramWallpaper { //assertionFailure() self = .color(0xffffff) } - case let .wallPaperNoFile(flags, settings): - if let settings = settings, case let .wallPaperSettings(flags, backgroundColor, secondBackgroundColor, intensity, rotation) = settings { - if let color = backgroundColor, let bottomColor = secondBackgroundColor { - self = .gradient(UInt32(bitPattern: color), UInt32(bitPattern: bottomColor), WallpaperSettings(rotation: rotation)) - } else if let color = backgroundColor { - self = .color(UInt32(bitPattern: color)) + case let .wallPaperNoFile(_, settings): + if let settings = settings, case let .wallPaperSettings(_, backgroundColor, secondBackgroundColor, thirdBackgroundColor, fourthBackgroundColor, _, rotation) = settings { + let colors: [UInt32] = ([backgroundColor, secondBackgroundColor, thirdBackgroundColor, fourthBackgroundColor] as [Int32?]).compactMap({ color -> UInt32? in + return color.flatMap(UInt32.init(bitPattern:)) + }) + if colors.count > 1 { + self = .gradient(colors, WallpaperSettings(rotation: rotation)) + } else if colors.count == 1 { + self = .color(UInt32(bitPattern: colors[0])) } else { self = .color(0xffffff) } @@ -68,16 +98,16 @@ extension TelegramWallpaper { var apiInputWallpaperAndSettings: (Api.InputWallPaper?, Api.WallPaperSettings)? { switch self { - case .builtin: - return nil - case let .file(file): - return (.inputWallPaperSlug(slug: file.slug), apiWallpaperSettings(file.settings)) - case let .color(color): - return (.inputWallPaperNoFile, apiWallpaperSettings(WallpaperSettings(color: color))) - case let .gradient(topColor, bottomColor, settings): - return (.inputWallPaperNoFile, apiWallpaperSettings(WallpaperSettings(color: topColor, bottomColor: bottomColor, rotation: settings.rotation))) - default: - return nil + case .builtin: + return nil + case let .file(_, _, _, _, _, _, slug, _, settings): + return (.inputWallPaperSlug(slug: slug), apiWallpaperSettings(settings)) + case let .color(color): + return (.inputWallPaperNoFile, apiWallpaperSettings(WallpaperSettings(colors: [color]))) + case let .gradient(colors, settings): + return (.inputWallPaperNoFile, apiWallpaperSettings(WallpaperSettings(colors: colors, rotation: settings.rotation))) + default: + return nil } } } diff --git a/submodules/TelegramCore/Sources/State/AppUpdate.swift b/submodules/TelegramCore/Sources/State/AppUpdate.swift index 5ab4dc5c7d..546b6b8778 100644 --- a/submodules/TelegramCore/Sources/State/AppUpdate.swift +++ b/submodules/TelegramCore/Sources/State/AppUpdate.swift @@ -16,7 +16,7 @@ public struct AppUpdateInfo: Equatable { extension AppUpdateInfo { init?(apiAppUpdate: Api.help.AppUpdate) { switch apiAppUpdate { - case let .appUpdate(flags, _, version, text, entities, _, _): + case let .appUpdate(flags, _, version, text, entities, _, _, _): self.blocking = (flags & (1 << 0)) != 0 self.version = version self.text = text diff --git a/submodules/TelegramCore/Sources/Wallpapers.swift b/submodules/TelegramCore/Sources/Wallpapers.swift index 46899116f4..4557a74894 100644 --- a/submodules/TelegramCore/Sources/Wallpapers.swift +++ b/submodules/TelegramCore/Sources/Wallpapers.swift @@ -19,14 +19,14 @@ public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate: if case let .file(file) = wallpaper, !file.isDefault { } else if !addedBuiltin { addedBuiltin = true - items.append(.builtin(nil, WallpaperSettings())) + items.append(.builtin(WallpaperSettings())) } items.append(wallpaper) } if !addedBuiltin { addedBuiltin = true - items.append(.builtin(nil, WallpaperSettings())) + items.append(.builtin(WallpaperSettings())) } if items == current { @@ -60,7 +60,7 @@ public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate: let configuration = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedWallpapersConfiguration, key: ValueBoxKey(length: 0))) as? CachedWallpapersConfiguration let items = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudWallpapers) if items.count == 0 { - return ([.builtin(nil, WallpaperSettings())], 0) + return ([.builtin(WallpaperSettings())], 0) } else { return (items.map { $0.contents as! TelegramWallpaper }, configuration?.hash) } diff --git a/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift b/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift index f72ccd56f1..887df43235 100644 --- a/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift +++ b/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift @@ -42,9 +42,9 @@ public func chatControllerBackgroundImage(theme: PresentationTheme?, wallpaper i context.setFillColor(UIColor(argb: color).withAlphaComponent(1.0).cgColor) context.fill(CGRect(origin: CGPoint(), size: size)) }) - case let .gradient(topColor, bottomColor, settings): + case let .gradient(colors, settings): backgroundImage = generateImage(CGSize(width: 640.0, height: 1280.0), rotatedContext: { size, context in - let gradientColors = [UIColor(argb: topColor).cgColor, UIColor(argb: bottomColor).cgColor] as CFArray + let gradientColors = [UIColor(argb: colors.count >= 1 ? colors[0] : 0).cgColor, UIColor(argb: colors.count >= 2 ? colors[1] : 0).cgColor] as CFArray var locations: [CGFloat] = [0.0, 1.0] let colorSpace = CGColorSpaceCreateDeviceRGB() @@ -73,9 +73,9 @@ public func chatControllerBackgroundImage(theme: PresentationTheme?, wallpaper i } } case let .file(file): - if wallpaper.isPattern, let color = file.settings.color, let intensity = file.settings.intensity { + if wallpaper.isPattern, !file.settings.colors.isEmpty, let intensity = file.settings.intensity { var image: UIImage? - let _ = mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: color, bottomColor: file.settings.bottomColor, intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true, attemptSynchronously: true).start(next: { data in + let _ = mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: file.settings.colors[0], bottomColor: file.settings.colors.count >= 2 ? file.settings.colors[1] : file.settings.colors[0], intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true, attemptSynchronously: true).start(next: { data in if data.complete { image = UIImage(contentsOfFile: data.path)?.precomposed() } @@ -135,17 +135,17 @@ public func chatControllerBackgroundImageSignal(wallpaper: TelegramWallpaper, me |> afterNext { image in cacheWallpaper(image?.0) } - case let .gradient(topColor, bottomColor, settings): - return .single((generateImage(CGSize(width: 640.0, height: 1280.0), rotatedContext: { size, context in - let gradientColors = [UIColor(argb: topColor).cgColor, UIColor(argb: bottomColor).cgColor] as CFArray + case let .gradient(colors, settings): + return .single((generateImage(CGSize(width: 640.0, height: 1280.0).fitted(CGSize(width: 100.0, height: 100.0)), rotatedContext: { size, context in + let gradientColors = [UIColor(rgb: colors.count >= 1 ? colors[0] : 0).cgColor, UIColor(rgb: colors.count >= 2 ? colors[1] : 0).cgColor] as CFArray var locations: [CGFloat] = [0.0, 1.0] let colorSpace = CGColorSpaceCreateDeviceRGB() let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! - context.translateBy(x: 320.0, y: 640.0) + context.translateBy(x: size.width / 2.0, y: size.height / 2.0) context.rotate(by: CGFloat(settings.rotation ?? 0) * CGFloat.pi / 180.0) - context.translateBy(x: -320.0, y: -640.0) + context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) }), true)) @@ -174,8 +174,8 @@ public func chatControllerBackgroundImageSignal(wallpaper: TelegramWallpaper, me } } case let .file(file): - if wallpaper.isPattern, let color = file.settings.color, let intensity = file.settings.intensity { - let representation = CachedPatternWallpaperRepresentation(color: color, bottomColor: file.settings.bottomColor, intensity: intensity, rotation: file.settings.rotation) + if wallpaper.isPattern, !file.settings.colors.isEmpty, let intensity = file.settings.intensity { + let representation = CachedPatternWallpaperRepresentation(color: file.settings.colors.count >= 1 ? file.settings.colors[0] : 0, bottomColor: file.settings.colors.count >= 2 ? file.settings.colors[1] : 0, intensity: intensity, rotation: file.settings.rotation) let effectiveMediaBox: MediaBox if FileManager.default.fileExists(atPath: mediaBox.cachedRepresentationCompletePath(file.file.resource.id, representation: representation)) { @@ -191,16 +191,16 @@ public func chatControllerBackgroundImageSignal(wallpaper: TelegramWallpaper, me return .single((UIImage(contentsOfFile: data.path)?.precomposed(), true)) } else { let interimWallpaper: TelegramWallpaper - if let secondColor = file.settings.bottomColor { - interimWallpaper = .gradient(color, secondColor, file.settings) + if file.settings.colors.count >= 2 { + interimWallpaper = .gradient(file.settings.colors, file.settings) } else { - interimWallpaper = .color(color) + interimWallpaper = .color(file.settings.colors.count >= 1 ? file.settings.colors[0] : 0) } let settings = file.settings let interrimImage = generateImage(CGSize(width: 640.0, height: 1280.0), rotatedContext: { size, context in - if let color = settings.color { - let gradientColors = [UIColor(argb: color).cgColor, UIColor(argb: settings.bottomColor ?? color).cgColor] as CFArray + if file.settings.colors.count >= 1 { + let gradientColors = [UIColor(argb: file.settings.colors[0]).cgColor, UIColor(argb: file.settings.colors.count >= 2 ? file.settings.colors[1] : file.settings.colors[0]).cgColor] as CFArray var locations: [CGFloat] = [0.0, 1.0] let colorSpace = CGColorSpaceCreateDeviceRGB() @@ -214,7 +214,7 @@ public func chatControllerBackgroundImageSignal(wallpaper: TelegramWallpaper, me } }) - return .single((interrimImage, false)) |> then(effectiveMediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: color, bottomColor: file.settings.bottomColor, intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true, attemptSynchronously: false) + return .single((interrimImage, false)) |> then(effectiveMediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: file.settings.colors[0], bottomColor: file.settings.colors.count >= 1 ? file.settings.colors[1] : file.settings.colors[0], intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true, attemptSynchronously: false) |> map { data -> (UIImage?, Bool)? in return (UIImage(contentsOfFile: data.path)?.precomposed(), true) }) diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift index 30002d5575..bed17883dc 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift @@ -5,9 +5,9 @@ import SyncCore import TelegramUIPreferences public let defaultDarkPresentationTheme = makeDefaultDarkPresentationTheme(preview: false) -public let defaultDarkColorPresentationTheme = customizeDefaultDarkPresentationTheme(theme: defaultDarkPresentationTheme, editing: false, title: nil, accentColor: UIColor(rgb: 0x007aff), backgroundColors: nil, bubbleColors: nil, wallpaper: nil) +public let defaultDarkColorPresentationTheme = customizeDefaultDarkPresentationTheme(theme: defaultDarkPresentationTheme, editing: false, title: nil, accentColor: UIColor(rgb: 0x007aff), backgroundColors: [], bubbleColors: nil, wallpaper: nil) -public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: (UIColor, UIColor?)?, bubbleColors: (UIColor, UIColor?)?, wallpaper forcedWallpaper: TelegramWallpaper? = nil) -> PresentationTheme { +public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: (UIColor, UIColor?)?, wallpaper forcedWallpaper: TelegramWallpaper? = nil) -> PresentationTheme { if (theme.referenceTheme != .night) { return theme } @@ -82,11 +82,11 @@ public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, edit var defaultWallpaper: TelegramWallpaper? if let forcedWallpaper = forcedWallpaper { defaultWallpaper = forcedWallpaper - } else if let backgroundColors = backgroundColors { - if let secondColor = backgroundColors.1 { - defaultWallpaper = .gradient(backgroundColors.0.argb, secondColor.argb, WallpaperSettings()) + } else if !backgroundColors.isEmpty { + if backgroundColors.count >= 2 { + defaultWallpaper = .gradient(backgroundColors, WallpaperSettings()) } else { - defaultWallpaper = .color(backgroundColors.0.argb) + defaultWallpaper = .color(backgroundColors[0]) } } diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift index c28cbefddf..bb3f6c8495 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift @@ -7,7 +7,7 @@ import TelegramUIPreferences private let defaultDarkTintedAccentColor = UIColor(rgb: 0x2ea6ff) public let defaultDarkTintedPresentationTheme = makeDefaultDarkTintedPresentationTheme(preview: false) -public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: (UIColor, UIColor?)?, bubbleColors: (UIColor, UIColor?)?, wallpaper forcedWallpaper: TelegramWallpaper? = nil) -> PresentationTheme { +public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: (UIColor, UIColor?)?, wallpaper forcedWallpaper: TelegramWallpaper? = nil) -> PresentationTheme { if (theme.referenceTheme != .nightAccent) { return theme } @@ -223,11 +223,11 @@ public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme var defaultWallpaper: TelegramWallpaper? if let forcedWallpaper = forcedWallpaper { defaultWallpaper = forcedWallpaper - } else if let backgroundColors = backgroundColors { - if let secondColor = backgroundColors.1 { - defaultWallpaper = .gradient(backgroundColors.0.argb, secondColor.argb, WallpaperSettings()) + } else if !backgroundColors.isEmpty { + if backgroundColors.count >= 2 { + defaultWallpaper = .gradient(backgroundColors, WallpaperSettings()) } else { - defaultWallpaper = .color(backgroundColors.0.argb) + defaultWallpaper = .color(backgroundColors[0]) } } else if let forcedWallpaper = suggestedWallpaper { defaultWallpaper = forcedWallpaper diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index 3fdae04918..91fd8b5abe 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -8,7 +8,7 @@ public let defaultServiceBackgroundColor = UIColor(rgb: 0x000000, alpha: 0.3) public let defaultPresentationTheme = makeDefaultDayPresentationTheme(serviceBackgroundColor: defaultServiceBackgroundColor, day: false, preview: false) public let defaultDayAccentColor = UIColor(rgb: 0x007ee5) -public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: (UIColor, UIColor?)?, bubbleColors: (UIColor, UIColor?)?, wallpaper forcedWallpaper: TelegramWallpaper? = nil, serviceBackgroundColor: UIColor?) -> PresentationTheme { +public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, title: String?, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: (UIColor, UIColor?)?, wallpaper forcedWallpaper: TelegramWallpaper? = nil, serviceBackgroundColor: UIColor?) -> PresentationTheme { if (theme.referenceTheme != .day && theme.referenceTheme != .dayClassic) { return theme } @@ -41,10 +41,10 @@ public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, ti let topColor = accentColor.withMultiplied(hue: 1.010, saturation: 0.414, brightness: 0.957) let bottomColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.867, brightness: 0.965) - suggestedWallpaper = .gradient(topColor.argb, bottomColor.argb, WallpaperSettings()) + suggestedWallpaper = .gradient([topColor.argb, bottomColor.argb], WallpaperSettings()) } else { bubbleColors = (UIColor(rgb: 0xe1ffc7), nil) - suggestedWallpaper = .builtin(nil, WallpaperSettings()) + suggestedWallpaper = .gradient(defaultBuiltinWallpaperGradientColors.map(\.rgb), WallpaperSettings()) } } } @@ -201,11 +201,11 @@ public func customizeDefaultDayTheme(theme: PresentationTheme, editing: Bool, ti var defaultWallpaper: TelegramWallpaper? if let forcedWallpaper = forcedWallpaper { defaultWallpaper = forcedWallpaper - } else if let backgroundColors = backgroundColors { - if let secondColor = backgroundColors.1 { - defaultWallpaper = .gradient(backgroundColors.0.argb, secondColor.argb, WallpaperSettings()) + } else if !backgroundColors.isEmpty { + if backgroundColors.count >= 2 { + defaultWallpaper = .gradient(backgroundColors, WallpaperSettings()) } else { - defaultWallpaper = .color(backgroundColors.0.argb) + defaultWallpaper = .color(backgroundColors[0]) } } else if let forcedWallpaper = suggestedWallpaper { defaultWallpaper = forcedWallpaper @@ -345,13 +345,13 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio secondaryTextColor: UIColor(rgb: 0x787878), controlColor: UIColor(rgb: 0x7e8791), accentTextColor: UIColor(rgb: 0x007ee5), - blurredBackgroundColor: UIColor(rgb: 0xf2f2f2, alpha: 0.8), + blurredBackgroundColor: UIColor(rgb: 0xf2f2f2, alpha: 0.9), opaqueBackgroundColor: UIColor(rgb: 0xf7f7f7).mixedWith(.white, alpha: 0.14), separatorColor: UIColor(rgb: 0xc8c7cc), badgeBackgroundColor: UIColor(rgb: 0xff3b30), badgeStrokeColor: UIColor(rgb: 0xff3b30), badgeTextColor: UIColor(rgb: 0xffffff), - segmentedBackgroundColor: UIColor(rgb: 0xe9e9e9), + segmentedBackgroundColor: UIColor(rgb: 0x000000, alpha: 0.06), segmentedForegroundColor: UIColor(rgb: 0xf7f7f7), segmentedTextColor: UIColor(rgb: 0x000000), segmentedDividerColor: UIColor(rgb: 0xd6d6dc), @@ -714,7 +714,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio ) let chat = PresentationThemeChat( - defaultWallpaper: day ? .color(0xffffff) : .builtin(nil, WallpaperSettings(motion: true)), + defaultWallpaper: day ? .color(0xffffff) : .gradient(defaultBuiltinWallpaperGradientColors.map(\.rgb), WallpaperSettings()), message: day ? messageDay : message, serviceMessage: day ? serviceMessageDay : serviceMessage, inputPanel: inputPanel, diff --git a/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift b/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift index 6bd5dbfcec..584e2c7416 100644 --- a/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift @@ -19,8 +19,8 @@ public func makeDefaultPresentationTheme(reference: PresentationBuiltinThemeRefe return theme } -public func customizePresentationTheme(_ theme: PresentationTheme, editing: Bool, title: String? = nil, accentColor: UIColor?, backgroundColors: (UIColor, UIColor?)?, bubbleColors: (UIColor, UIColor?)?, wallpaper: TelegramWallpaper? = nil) -> PresentationTheme { - if accentColor == nil && bubbleColors == nil && backgroundColors == nil && wallpaper == nil { +public func customizePresentationTheme(_ theme: PresentationTheme, editing: Bool, title: String? = nil, accentColor: UIColor?, backgroundColors: [UInt32], bubbleColors: (UIColor, UIColor?)?, wallpaper: TelegramWallpaper? = nil) -> PresentationTheme { + if accentColor == nil && bubbleColors == nil && backgroundColors.isEmpty && wallpaper == nil { return theme } switch theme.referenceTheme { @@ -37,10 +37,10 @@ public func customizePresentationTheme(_ theme: PresentationTheme, editing: Bool public func makePresentationTheme(settings: TelegramThemeSettings, title: String? = nil, serviceBackgroundColor: UIColor? = nil) -> PresentationTheme? { let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: settings.baseTheme), extendingThemeReference: nil, serviceBackgroundColor: serviceBackgroundColor, preview: false) - return customizePresentationTheme(defaultTheme, editing: true, title: title, accentColor: UIColor(argb: settings.accentColor), backgroundColors: nil, bubbleColors: settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: settings.wallpaper) + return customizePresentationTheme(defaultTheme, editing: true, title: title, accentColor: UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: settings.wallpaper) } -public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, extendingThemeReference: PresentationThemeReference? = nil, accentColor: UIColor? = nil, backgroundColors: (UIColor, UIColor?)? = nil, bubbleColors: (UIColor, UIColor?)? = nil, wallpaper: TelegramWallpaper? = nil, serviceBackgroundColor: UIColor? = nil, preview: Bool = false) -> PresentationTheme? { +public func makePresentationTheme(mediaBox: MediaBox, themeReference: PresentationThemeReference, extendingThemeReference: PresentationThemeReference? = nil, accentColor: UIColor? = nil, backgroundColors: [UInt32] = [], bubbleColors: (UIColor, UIColor?)? = nil, wallpaper: TelegramWallpaper? = nil, serviceBackgroundColor: UIColor? = nil, preview: Bool = false) -> PresentationTheme? { let theme: PresentationTheme switch themeReference { case let .builtin(reference): @@ -54,7 +54,7 @@ public func makePresentationTheme(mediaBox: MediaBox, themeReference: Presentati } case let .cloud(info): if let settings = info.theme.settings { - if let loadedTheme = makePresentationTheme(mediaBox: mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), extendingThemeReference: themeReference, accentColor: accentColor ?? UIColor(argb: settings.accentColor), backgroundColors: nil, bubbleColors: bubbleColors ?? settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: wallpaper ?? settings.wallpaper, serviceBackgroundColor: serviceBackgroundColor, preview: preview) { + if let loadedTheme = makePresentationTheme(mediaBox: mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), extendingThemeReference: themeReference, accentColor: accentColor ?? UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: bubbleColors ?? settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: wallpaper ?? settings.wallpaper, serviceBackgroundColor: serviceBackgroundColor, preview: preview) { theme = loadedTheme } else { return nil diff --git a/submodules/TelegramPresentationData/Sources/PresentationData.swift b/submodules/TelegramPresentationData/Sources/PresentationData.swift index 05513dc704..3b022413e7 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationData.swift @@ -444,9 +444,13 @@ public func serviceColor(for wallpaper: (TelegramWallpaper, UIImage?)) -> UIColo return UIColor(rgb: 0x748391, alpha: 0.45) case let .color(color): return serviceColor(with: UIColor(argb: color)) - case let .gradient(topColor, bottomColor, _): - let mixedColor = UIColor(argb: topColor).mixedWith(UIColor(argb: bottomColor), alpha: 0.5) - return serviceColor(with: mixedColor) + case let .gradient(colors, _): + if colors.count == 2 { + let mixedColor = UIColor(argb: colors[0]).mixedWith(UIColor(argb: colors[1]), alpha: 0.5) + return serviceColor(with: mixedColor) + } else { + return UIColor(rgb: 0x000000, alpha: 0.3) + } case .image: if let image = wallpaper.1 { return serviceColor(with: averageColor(from: image)) @@ -455,10 +459,10 @@ public func serviceColor(for wallpaper: (TelegramWallpaper, UIImage?)) -> UIColo } case let .file(file): if wallpaper.0.isPattern { - if let color = file.settings.color { - var mixedColor = UIColor(argb: color) - if let bottomColor = file.settings.bottomColor { - mixedColor = mixedColor.mixedWith(UIColor(argb: bottomColor), alpha: 0.5) + if file.settings.colors.count >= 1 && file.settings.colors.count <= 2 { + var mixedColor = UIColor(argb: file.settings.colors[0]) + if file.settings.colors.count >= 2 { + mixedColor = mixedColor.mixedWith(UIColor(argb: file.settings.colors[1]), alpha: 0.5) } return serviceColor(with: mixedColor) } else { @@ -499,9 +503,14 @@ public func chatServiceBackgroundColor(wallpaper: TelegramWallpaper, mediaBox: M return .single(UIColor(rgb: 0x000000, alpha: 0.2)) case let .color(color): return .single(serviceColor(with: UIColor(argb: color))) - case let .gradient(topColor, bottomColor, _): - let mixedColor = UIColor(argb: topColor).mixedWith(UIColor(rgb: bottomColor), alpha: 0.5) - return .single(serviceColor(with: mixedColor)) + case let .gradient(colors, _): + if colors.count == 2 { + let mixedColor = UIColor(argb: colors[0]).mixedWith(UIColor(argb: colors[1]), alpha: 0.5) + return .single( + serviceColor(with: mixedColor)) + } else { + return .single(UIColor(rgb: 0x000000, alpha: 0.3)) + } case let .image(representations, _): if let largest = largestImageRepresentation(representations) { return Signal { subscriber in @@ -524,10 +533,10 @@ public func chatServiceBackgroundColor(wallpaper: TelegramWallpaper, mediaBox: M } case let .file(file): if wallpaper.isPattern { - if let color = file.settings.color { - var mixedColor = UIColor(argb: color) - if let bottomColor = file.settings.bottomColor { - mixedColor = mixedColor.mixedWith(UIColor(rgb: bottomColor), alpha: 0.5) + if file.settings.colors.count >= 1 && file.settings.colors.count <= 2 { + var mixedColor = UIColor(argb: file.settings.colors[0]) + if file.settings.colors.count >= 2 { + mixedColor = mixedColor.mixedWith(UIColor(argb: file.settings.colors[1]), alpha: 0.5) } return .single(serviceColor(with: mixedColor)) } else { @@ -681,7 +690,7 @@ public func defaultPresentationData() -> PresentationData { let chatBubbleCorners = PresentationChatBubbleCorners(mainRadius: CGFloat(themeSettings.chatBubbleSettings.mainRadius), auxiliaryRadius: CGFloat(themeSettings.chatBubbleSettings.auxiliaryRadius), mergeBubbleCorners: themeSettings.chatBubbleSettings.mergeBubbleCorners) - return PresentationData(strings: defaultPresentationStrings, theme: defaultPresentationTheme, autoNightModeTriggered: false, chatWallpaper: .builtin(nil, WallpaperSettings()), chatFontSize: chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, reduceMotion: themeSettings.reduceMotion, largeEmoji: themeSettings.largeEmoji) + return PresentationData(strings: defaultPresentationStrings, theme: defaultPresentationTheme, autoNightModeTriggered: false, chatWallpaper: defaultPresentationTheme.chat.defaultWallpaper, chatFontSize: chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, reduceMotion: themeSettings.reduceMotion, largeEmoji: themeSettings.largeEmoji) } public extension PresentationData { diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift index 4578820207..19ac9d32f6 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift @@ -40,7 +40,7 @@ extension TelegramWallpaper: Codable { if let value = try? values.decode(String.self) { switch value.lowercased() { case "builtin": - self = .builtin(nil, WallpaperSettings()) + self = .builtin(WallpaperSettings()) default: let optionKeys = ["motion", "blur"] @@ -65,7 +65,7 @@ extension TelegramWallpaper: Codable { } } - self = .gradient(topColor.argb, bottomColor.argb, WallpaperSettings(blur: blur, motion: motion, rotation: rotation)) + self = .gradient([topColor.argb, bottomColor.argb], WallpaperSettings(blur: blur, motion: motion, rotation: rotation)) } else { var slug: String? var color: UInt32? @@ -104,7 +104,14 @@ extension TelegramWallpaper: Codable { } } if let slug = slug { - self = .file(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: color != nil, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: blur, motion: motion, color: color, bottomColor: bottomColor, intensity: intensity, rotation: rotation)) + var colors: [UInt32] = [] + if let color = color { + colors.append(color) + } + if let bottomColor = bottomColor { + colors.append(bottomColor) + } + self = .file(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: color != nil, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: blur, motion: motion, colors: colors, intensity: intensity, rotation: rotation)) } else { throw PresentationThemeDecodingError.generic } @@ -123,10 +130,11 @@ extension TelegramWallpaper: Codable { try container.encode("builtin") case let .color(color): try container.encode(String(format: "%06x", color)) - case let .gradient(topColor, bottomColor, settings): + case let .gradient(colors, settings): var components: [String] = [] - components.append(String(format: "%06x", topColor)) - components.append(String(format: "%06x", bottomColor)) + for color in colors { + components.append(String(format: "%06x", color)) + } if let rotation = settings.rotation { components.append("\(rotation)") } @@ -141,14 +149,14 @@ extension TelegramWallpaper: Codable { var components: [String] = [] components.append(file.slug) if self.isPattern { - if let color = file.settings.color { - components.append(String(format: "%06x", color)) + if file.settings.colors.count >= 1 { + components.append(String(format: "%06x", file.settings.colors[0])) } if let intensity = file.settings.intensity { components.append("\(intensity)") } - if let bottomColor = file.settings.bottomColor { - components.append(String(format: "%06x", bottomColor)) + if file.settings.colors.count >= 2 { + components.append(String(format: "%06x", file.settings.colors[1])) } if let rotation = file.settings.rotation, rotation != 0 { components.append("\(rotation)") diff --git a/submodules/TelegramPresentationData/Sources/WallpaperUtils.swift b/submodules/TelegramPresentationData/Sources/WallpaperUtils.swift index 23bc0158dc..f3afab6527 100644 --- a/submodules/TelegramPresentationData/Sources/WallpaperUtils.swift +++ b/submodules/TelegramPresentationData/Sources/WallpaperUtils.swift @@ -8,8 +8,8 @@ public extension TelegramWallpaper { switch self { case .image: return false - case let .file(file): - if self.isPattern, file.settings.color == 0xffffff || file.settings.color == 0xffffffff { + case let .file(_, _, _, _, _, _, _, _, settings): + if self.isPattern, settings.colors.count == 1 && (settings.colors[0] == 0xffffff || settings.colors[0] == 0xffffffff) { return true } else { return false diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index c865812ab1..474399ba46 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -4527,7 +4527,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else { isScheduledMessages = false } - strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationLayout(layout: validLayout).navigationFrame.maxY, transition: .animated(duration: strongSelf.chatDisplayNode.messageTransitionNode.hasScheduledTransitions ? 0.5 : 0.18, curve: strongSelf.chatDisplayNode.messageTransitionNode.hasScheduledTransitions ? .custom(0.33, 0.0, 0.0, 1.0) : .easeInOut), listViewTransaction: { updateSizeAndInsets, _, _, _ in + let duration: Double = strongSelf.chatDisplayNode.messageTransitionNode.hasScheduledTransitions ? ChatMessageTransitionNode.animationDuration : 0.18 + let curve: ContainedViewLayoutTransitionCurve = strongSelf.chatDisplayNode.messageTransitionNode.hasScheduledTransitions ? ChatMessageTransitionNode.verticalAnimationCurve : .easeInOut + let controlPoints: (Float, Float, Float, Float) = strongSelf.chatDisplayNode.messageTransitionNode.hasScheduledTransitions ? ChatMessageTransitionNode.verticalAnimationControlPoints : (0.5, 0.33, 0.0, 0.0) + + strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationLayout(layout: validLayout).navigationFrame.maxY, transition: .animated(duration: duration, curve: curve), listViewTransaction: { updateSizeAndInsets, _, _, _ in var options = transition.options let _ = options.insert(.Synchronous) let _ = options.insert(.LowLatency) @@ -4553,9 +4557,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var scrollToItem: ListViewScrollToItem? if isScheduledMessages, let insertedIndex = insertedIndex { - scrollToItem = ListViewScrollToItem(index: insertedIndex, position: .visible, animated: true, curve: .Custom(duration: 0.5, 0.33, 0.0, 0.0, 1.0), directionHint: .Down) + scrollToItem = ListViewScrollToItem(index: insertedIndex, position: .visible, animated: true, curve: .Custom(duration: duration, controlPoints.0, controlPoints.1, controlPoints.2, controlPoints.3), directionHint: .Down) } else if transition.historyView.originalView.laterId == nil { - scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Custom(duration: 0.5, 0.33, 0.0, 0.0, 1.0), directionHint: .Up) + scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Custom(duration: duration, controlPoints.0, controlPoints.1, controlPoints.2, controlPoints.3), directionHint: .Up) } var stationaryItemRange: (Int, Int)? @@ -11559,48 +11563,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return nil } - func previewingCommit(_ viewControllerToCommit: UIViewController) { - if let gallery = viewControllerToCommit as? AvatarGalleryController { - self.chatDisplayNode.dismissInput() - gallery.setHintWillBePresentedInPreviewingContext(false) - self.present(gallery, in: .window(.root), with: AvatarGalleryControllerPresentationArguments(animated: false, transitionArguments: { _ in - return nil - })) - } else if let gallery = viewControllerToCommit as? GalleryController { - self.chatDisplayNode.dismissInput() - gallery.setHintWillBePresentedInPreviewingContext(false) - - self.present(gallery, in: .window(.root), with: GalleryControllerPresentationArguments(animated: false, transitionArguments: { [weak self] messageId, media in - if let strongSelf = self { - var selectedTransitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? - strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in - if let itemNode = itemNode as? ChatMessageItemView { - if let result = itemNode.transitionNode(id: messageId, media: media) { - selectedTransitionNode = result - } - } - } - if let selectedTransitionNode = selectedTransitionNode { - return GalleryTransitionArguments(transitionNode: selectedTransitionNode, addToTransitionSurface: { view in - if let strongSelf = self { - strongSelf.chatDisplayNode.historyNode.view.superview?.insertSubview(view, aboveSubview: strongSelf.chatDisplayNode.historyNode.view) - } - }) - } - } - return nil - })) - } - - if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - if let safariController = viewControllerToCommit as? SFSafariViewController { - if let window = self.effectiveNavigationController?.view.window { - window.rootViewController?.present(safariController, animated: true) - } - } - } - } - private func presentBanMessageOptions(accountPeerId: PeerId, author: Peer, messageIds: Set, options: ChatAvailableMessageActionOptions) { let peerId = self.chatLocation.peerId do { diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index baa1adc6b2..f1bde10726 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -81,7 +81,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } let backgroundNode: WallpaperBackgroundNode - let backgroundImageDisposable = MetaDisposable() let historyNode: ChatHistoryListNode var blurredHistoryNode: ASImageNode? let reactionContainerNode: ReactionSelectionParentNode @@ -352,12 +351,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } - self.backgroundImageDisposable.set(chatControllerBackgroundImageSignal(wallpaper: chatPresentationInterfaceState.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, accountMediaBox: context.account.postbox.mediaBox).start(next: { [weak self] image in - if let strongSelf = self, let (image, _) = image { - strongSelf.backgroundNode.image = image - } - })) - self.interactiveEmojisDisposable = (self.context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]) |> map { preferencesView -> InteractiveEmojiConfiguration in let appConfiguration: AppConfiguration = preferencesView.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue @@ -436,7 +429,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } deinit { - self.backgroundImageDisposable.dispose() self.interactiveEmojisDisposable?.dispose() self.openStickersDisposable?.dispose() self.displayVideoUnmuteTipDisposable?.dispose() @@ -1596,12 +1588,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let themeUpdated = self.chatPresentationInterfaceState.theme !== chatPresentationInterfaceState.theme if self.chatPresentationInterfaceState.chatWallpaper != chatPresentationInterfaceState.chatWallpaper { - self.backgroundImageDisposable.set(chatControllerBackgroundImageSignal(wallpaper: chatPresentationInterfaceState.chatWallpaper, mediaBox: self.context.sharedContext.accountManager.mediaBox, accountMediaBox: self.context.account.postbox.mediaBox).start(next: { [weak self] image in - if let strongSelf = self, let (image, final) = image { - strongSelf.backgroundNode.image = image - } - })) - self.backgroundNode.image = chatControllerBackgroundImage(theme: chatPresentationInterfaceState.theme, wallpaper: chatPresentationInterfaceState.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: self.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper) self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper) } diff --git a/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift b/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift index 7dc2c3308e..bfdfe79955 100644 --- a/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistorySearchContainerNode.swift @@ -89,7 +89,7 @@ private enum ChatHistorySearchEntry: Comparable, Identifiable { func item(context: AccountContext, peerId: PeerId, interaction: ChatControllerInteraction) -> ListViewItem { switch self { case let .message(message, theme, strings, dateTimeFormat, fontSize): - return ListMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: .builtin(nil, WallpaperSettings())), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, disableAnimations: false, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), context: context, chatLocation: .peer(peerId), interaction: ListMessageItemInteraction(controllerInteraction: interaction), message: message, selection: .none, displayHeader: true) + return ListMessageItem(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: .builtin(WallpaperSettings())), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, disableAnimations: false, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), context: context, chatLocation: .peer(peerId), interaction: ListMessageItemInteraction(controllerInteraction: interaction), message: message, selection: .none, displayHeader: true) } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index 8a3d85a359..2ccf6bcf25 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1680,7 +1680,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { self.contextSourceNode.contentNode.addSubnode(accessoryItemNode) } - func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, transition: ContainedViewLayoutTransition) { + func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, transition: CombinedTransition) { guard let _ = self.item else { return } @@ -1710,14 +1710,14 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { y: sourceCenter.y - self.imageNode.frame.midY ) - transition.animatePositionAdditive(node: self.imageNode, offset: offset) - transition.animateTransformScale(node: self.imageNode, from: sourceScale) + transition.animatePositionAdditive(layer: self.imageNode.layer, offset: offset) + transition.horizontal.animateTransformScale(node: self.imageNode, from: sourceScale) if let animationNode = self.animationNode { - transition.animatePositionAdditive(node: animationNode, offset: offset) - transition.animateTransformScale(node: animationNode, from: sourceScale) + transition.animatePositionAdditive(layer: animationNode.layer, offset: offset) + transition.horizontal.animateTransformScale(node: animationNode, from: sourceScale) } - transition.animatePositionAdditive(node: self.placeholderNode, offset: offset) - transition.animateTransformScale(node: self.placeholderNode, from: sourceScale) + transition.animatePositionAdditive(layer: self.placeholderNode.layer, offset: offset) + transition.horizontal.animateTransformScale(node: self.placeholderNode, from: sourceScale) let inverseScale = 1.0 / sourceScale @@ -1725,7 +1725,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { x: -offset.x - localSourceOffset.x * (inverseScale - 1.0), y: -offset.y - localSourceOffset.y * (inverseScale - 1.0) ), removeOnCompletion: false) - transition.updateTransformScale(layer: textInput.contentView.layer, scale: 1.0 / sourceScale) + transition.horizontal.updateTransformScale(layer: textInput.contentView.layer, scale: 1.0 / sourceScale) textInput.contentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { _ in textInput.contentView.removeFromSuperview() @@ -1740,7 +1740,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.16) } - func animateContentFromStickerGridItem(stickerSource: ChatMessageTransitionNode.Sticker, transition: ContainedViewLayoutTransition) { + func animateContentFromStickerGridItem(stickerSource: ChatMessageTransitionNode.Sticker, transition: CombinedTransition) { guard let _ = self.item else { return } @@ -1785,14 +1785,14 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { y: sourceCenter.y - self.imageNode.frame.midY ) - transition.animatePositionAdditive(node: self.imageNode, offset: offset) - transition.animateTransformScale(node: self.imageNode, from: sourceScale) + transition.animatePositionAdditive(layer: self.imageNode.layer, offset: offset) + transition.horizontal.animateTransformScale(node: self.imageNode, from: sourceScale) if let animationNode = self.animationNode { - transition.animatePositionAdditive(node: animationNode, offset: offset) - transition.animateTransformScale(node: animationNode, from: sourceScale) + transition.animatePositionAdditive(layer: animationNode.layer, offset: offset) + transition.horizontal.animateTransformScale(node: animationNode, from: sourceScale) } - transition.animatePositionAdditive(node: self.placeholderNode, offset: offset) - transition.animateTransformScale(node: self.placeholderNode, from: sourceScale) + transition.animatePositionAdditive(layer: self.placeholderNode.layer, offset: offset) + transition.horizontal.animateTransformScale(node: self.placeholderNode, from: sourceScale) let inverseScale = 1.0 / sourceScale @@ -1801,7 +1801,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { x: -offset.x - localSourceOffset.x * (inverseScale - 1.0), y: -offset.y - localSourceOffset.y * (inverseScale - 1.0) ), removeOnCompletion: false) - transition.updateTransformScale(layer: snapshotView.layer, scale: 1.0 / sourceScale) + transition.horizontal.updateTransformScale(layer: snapshotView.layer, scale: 1.0 / sourceScale) snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.08, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView?.removeFromSuperview() @@ -1830,13 +1830,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } - func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, transition: ContainedViewLayoutTransition) { + func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, transition: CombinedTransition) { if let replyInfoNode = self.replyInfoNode { let localRect = self.contextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view) - let offset = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, horizontalTransition: transition, verticalTransition: transition) + let offset = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, transition: transition) if let replyBackgroundNode = self.replyBackgroundNode { - transition.animatePositionAdditive(node: replyBackgroundNode, offset: offset) + transition.animatePositionAdditive(layer: replyBackgroundNode.layer, offset: offset) replyBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 59ab495f78..a820cf5c14 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -690,14 +690,14 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } } - func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, horizontalTransition: ContainedViewLayoutTransition, verticalTransition: ContainedViewLayoutTransition) { + func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, transition: CombinedTransition) { if let replyInfoNode = self.replyInfoNode { let localRect = self.mainContextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view) - let _ = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, horizontalTransition: horizontalTransition, verticalTransition: verticalTransition) + let _ = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, unclippedTransitionNode: self.mainContextSourceNode.contentNode, localRect: localRect, transition: transition) } } - func animateFromMicInput(micInputNode: UIView, transition: ContainedViewLayoutTransition) -> ContextExtractedContentContainingNode? { + func animateFromMicInput(micInputNode: UIView, transition: CombinedTransition) -> ContextExtractedContentContainingNode? { for contentNode in self.contentNodes { if let contentNode = contentNode as? ChatMessageFileBubbleContentNode { if let statusContainerNode = contentNode.interactiveFileNode.statusContainerNode { @@ -706,11 +706,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode micInputNode.center = CGPoint(x: statusContainerNode.contentRect.midX, y: statusContainerNode.contentRect.midY) statusContainerNode.contentNode.view.addSubview(micInputNode) - transition.updateAlpha(layer: micInputNode.layer, alpha: 0.0, completion: { [weak micInputNode] _ in + transition.horizontal.updateAlpha(layer: micInputNode.layer, alpha: 0.0, completion: { [weak micInputNode] _ in micInputNode?.removeFromSuperview() }) - transition.animateTransformScale(node: statusContainerNode.contentNode, from: 1.0 / scale) + transition.horizontal.animateTransformScale(node: statusContainerNode.contentNode, from: 1.0 / scale) return statusContainerNode } @@ -719,7 +719,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode return nil } - func animateContentFromMediaInput(snapshotView: UIView, horizontalTransition: ContainedViewLayoutTransition, verticalTransition: ContainedViewLayoutTransition) { + func animateContentFromMediaInput(snapshotView: UIView, transition: CombinedTransition) { self.mainContextSourceNode.contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) } @@ -2429,7 +2429,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode strongSelf.replyInfoNode = replyInfoNode var animateFrame = true if replyInfoNode.supernode == nil { - strongSelf.mainContextSourceNode.contentNode.addSubnode(replyInfoNode) + strongSelf.clippingNode.addSubnode(replyInfoNode) animateFrame = false } let previousReplyInfoNodeFrame = replyInfoNode.frame diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index d77afd6a67..6b1c9ce3f9 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -1008,7 +1008,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } - func animateFromSnapshot(snapshotView: UIView, transition: ContainedViewLayoutTransition) { + func animateFromSnapshot(snapshotView: UIView, transition: CombinedTransition) { snapshotView.frame = self.interactiveVideoNode.view.convert(snapshotView.frame, from: self.contextSourceNode.contentNode.view) self.interactiveVideoNode.animateFromSnapshot(snapshotView: snapshotView, transition: transition) } diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index 68466f689c..c4bb25cfdb 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -833,7 +833,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } } - func animateFromSnapshot(snapshotView: UIView, transition: ContainedViewLayoutTransition) { + func animateFromSnapshot(snapshotView: UIView, transition: CombinedTransition) { guard let videoFrame = self.videoFrame else { return } @@ -844,11 +844,11 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { self.view.addSubview(snapshotView) - transition.updateAlpha(layer: snapshotView.layer, alpha: 0.0, completion: { [weak snapshotView] _ in + transition.horizontal.updateAlpha(layer: snapshotView.layer, alpha: 0.0, completion: { [weak snapshotView] _ in snapshotView?.removeFromSuperview() }) - transition.animateTransformScale(node: self, from: 1.0 / scale) + transition.horizontal.animateTransformScale(node: self, from: 1.0 / scale) self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.18) if let durationNode = self.durationNode { diff --git a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift index 8aded002eb..240ba43779 100644 --- a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift @@ -230,24 +230,50 @@ class ChatMessageReplyInfoNode: ASDisplayNode { } } - func animateFromInputPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, localRect: CGRect, horizontalTransition: ContainedViewLayoutTransition, verticalTransition: ContainedViewLayoutTransition) -> CGPoint { + func animateFromInputPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, unclippedTransitionNode: ASDisplayNode? = nil, localRect: CGRect, transition: CombinedTransition) -> CGPoint { + let sourceParentNode = ASDisplayNode() + + let sourceParentOffset: CGPoint + + if let unclippedTransitionNode = unclippedTransitionNode { + unclippedTransitionNode.addSubnode(sourceParentNode) + sourceParentNode.frame = sourceReplyPanel.relativeSourceRect + sourceParentOffset = self.view.convert(CGPoint(), to: sourceParentNode.view) + sourceParentNode.clipsToBounds = true + + let panelOffset = sourceReplyPanel.relativeTargetRect.minY - sourceReplyPanel.relativeSourceRect.minY + + sourceParentNode.frame = sourceParentNode.frame.offsetBy(dx: 0.0, dy: panelOffset) + sourceParentNode.bounds = sourceParentNode.bounds.offsetBy(dx: 0.0, dy: panelOffset) + transition.vertical.animatePositionAdditive(layer: sourceParentNode.layer, offset: CGPoint(x: 0.0, y: -panelOffset)) + transition.vertical.animateOffsetAdditive(layer: sourceParentNode.layer, offset: -panelOffset) + } else { + self.addSubnode(sourceParentNode) + sourceParentOffset = CGPoint() + } + + sourceParentNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak sourceParentNode] _ in + sourceParentNode?.removeFromSupernode() + }) + if let titleNode = self.titleNode { let offset = CGPoint( x: localRect.minX + sourceReplyPanel.titleNode.frame.minX - titleNode.frame.minX, y: localRect.minY + sourceReplyPanel.titleNode.frame.midY - titleNode.frame.midY ) - horizontalTransition.animatePositionAdditive(node: titleNode, offset: offset) + transition.horizontal.animatePositionAdditive(node: titleNode, offset: CGPoint(x: offset.x, y: 0.0)) + transition.vertical.animatePositionAdditive(node: titleNode, offset: CGPoint(x: 0.0, y: offset.y)) - self.addSubnode(sourceReplyPanel.titleNode) + sourceParentNode.addSubnode(sourceReplyPanel.titleNode) titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) - sourceReplyPanel.titleNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak sourceReplyPanel] _ in - sourceReplyPanel?.titleNode.removeFromSupernode() - }) - sourceReplyPanel.titleNode.frame = sourceReplyPanel.titleNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) - horizontalTransition.animatePositionAdditive(node: sourceReplyPanel.titleNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + sourceReplyPanel.titleNode.frame = sourceReplyPanel.titleNode.frame + .offsetBy(dx: sourceParentOffset.x, dy: sourceParentOffset.y) + .offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) + transition.horizontal.animatePositionAdditive(node: sourceReplyPanel.titleNode, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false) + transition.vertical.animatePositionAdditive(node: sourceReplyPanel.titleNode, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false) } if let textNode = self.textNode { @@ -256,17 +282,18 @@ class ChatMessageReplyInfoNode: ASDisplayNode { y: localRect.minY + sourceReplyPanel.textNode.frame.midY - textNode.frame.midY ) - horizontalTransition.animatePositionAdditive(node: textNode, offset: offset) + transition.horizontal.animatePositionAdditive(node: textNode, offset: CGPoint(x: offset.x, y: 0.0)) + transition.vertical.animatePositionAdditive(node: textNode, offset: CGPoint(x: 0.0, y: offset.y)) - self.addSubnode(sourceReplyPanel.textNode) + sourceParentNode.addSubnode(sourceReplyPanel.textNode) textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) - sourceReplyPanel.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak sourceReplyPanel] _ in - sourceReplyPanel?.textNode.removeFromSupernode() - }) - sourceReplyPanel.textNode.frame = sourceReplyPanel.textNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) - horizontalTransition.animatePositionAdditive(node: sourceReplyPanel.textNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + sourceReplyPanel.textNode.frame = sourceReplyPanel.textNode.frame + .offsetBy(dx: sourceParentOffset.x, dy: sourceParentOffset.y) + .offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) + transition.horizontal.animatePositionAdditive(node: sourceReplyPanel.textNode, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false) + transition.vertical.animatePositionAdditive(node: sourceReplyPanel.textNode, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false) } if let imageNode = self.imageNode { @@ -275,17 +302,18 @@ class ChatMessageReplyInfoNode: ASDisplayNode { y: localRect.minY + sourceReplyPanel.imageNode.frame.midY - imageNode.frame.midY ) - horizontalTransition.animatePositionAdditive(node: imageNode, offset: offset) + transition.horizontal.animatePositionAdditive(node: imageNode, offset: CGPoint(x: offset.x, y: 0.0)) + transition.vertical.animatePositionAdditive(node: imageNode, offset: CGPoint(x: 0.0, y: offset.y)) - self.addSubnode(sourceReplyPanel.imageNode) + sourceParentNode.addSubnode(sourceReplyPanel.imageNode) imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) - sourceReplyPanel.imageNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak sourceReplyPanel] _ in - sourceReplyPanel?.imageNode.removeFromSupernode() - }) - sourceReplyPanel.imageNode.frame = sourceReplyPanel.imageNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) - horizontalTransition.animatePositionAdditive(node: sourceReplyPanel.imageNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + sourceReplyPanel.imageNode.frame = sourceReplyPanel.imageNode.frame + .offsetBy(dx: sourceParentOffset.x, dy: sourceParentOffset.y) + .offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) + transition.horizontal.animatePositionAdditive(node: sourceReplyPanel.imageNode, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false) + transition.vertical.animatePositionAdditive(node: sourceReplyPanel.imageNode, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false) } do { @@ -296,17 +324,18 @@ class ChatMessageReplyInfoNode: ASDisplayNode { y: localRect.minY + sourceReplyPanel.lineNode.frame.minY - lineNode.frame.minY ) - horizontalTransition.animatePositionAdditive(node: lineNode, offset: offset) + transition.horizontal.animatePositionAdditive(node: lineNode, offset: CGPoint(x: offset.x, y: 0.0)) + transition.vertical.animatePositionAdditive(node: lineNode, offset: CGPoint(x: 0.0, y: offset.y)) - self.addSubnode(sourceReplyPanel.lineNode) + sourceParentNode.addSubnode(sourceReplyPanel.lineNode) lineNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) - sourceReplyPanel.lineNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak sourceReplyPanel] _ in - sourceReplyPanel?.lineNode.removeFromSupernode() - }) - sourceReplyPanel.lineNode.frame = sourceReplyPanel.lineNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) - horizontalTransition.animatePositionAdditive(node: sourceReplyPanel.lineNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + sourceReplyPanel.lineNode.frame = sourceReplyPanel.lineNode.frame + .offsetBy(dx: sourceParentOffset.x, dy: sourceParentOffset.y) + .offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) + transition.horizontal.animatePositionAdditive(node: sourceReplyPanel.lineNode, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false) + transition.vertical.animatePositionAdditive(node: sourceReplyPanel.lineNode, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false) return offset } diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index a360f6a152..3789e562d7 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -1199,7 +1199,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { self.contextSourceNode.contentNode.addSubnode(accessoryItemNode) } - func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, transition: ContainedViewLayoutTransition) { + func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, transition: CombinedTransition) { guard let _ = self.item else { return } @@ -1229,10 +1229,10 @@ class ChatMessageStickerItemNode: ChatMessageItemView { y: sourceCenter.y - self.imageNode.frame.midY ) - transition.animatePositionAdditive(node: self.imageNode, offset: offset) - transition.animateTransformScale(node: self.imageNode, from: sourceScale) - transition.animatePositionAdditive(node: self.placeholderNode, offset: offset) - transition.animateTransformScale(node: self.placeholderNode, from: sourceScale) + transition.animatePositionAdditive(layer: self.imageNode.layer, offset: offset) + transition.horizontal.animateTransformScale(node: self.imageNode, from: sourceScale) + transition.animatePositionAdditive(layer: self.placeholderNode.layer, offset: offset) + transition.horizontal.animateTransformScale(node: self.placeholderNode, from: sourceScale) let inverseScale = 1.0 / sourceScale @@ -1240,7 +1240,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { x: -offset.x - localSourceOffset.x * (inverseScale - 1.0), y: -offset.y - localSourceOffset.y * (inverseScale - 1.0) ), removeOnCompletion: false) - transition.updateTransformScale(layer: textInput.contentView.layer, scale: 1.0 / sourceScale) + transition.horizontal.updateTransformScale(layer: textInput.contentView.layer, scale: 1.0 / sourceScale) textInput.contentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { _ in textInput.contentView.removeFromSuperview() @@ -1252,7 +1252,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.16) } - func animateContentFromStickerGridItem(stickerSource: ChatMessageTransitionNode.Sticker, transition: ContainedViewLayoutTransition) { + func animateContentFromStickerGridItem(stickerSource: ChatMessageTransitionNode.Sticker, transition: CombinedTransition) { guard let _ = self.item else { return } @@ -1297,10 +1297,10 @@ class ChatMessageStickerItemNode: ChatMessageItemView { y: sourceCenter.y - self.imageNode.frame.midY ) - transition.animatePositionAdditive(node: self.imageNode, offset: offset) - transition.animateTransformScale(node: self.imageNode, from: sourceScale) - transition.animatePositionAdditive(node: self.placeholderNode, offset: offset) - transition.animateTransformScale(node: self.placeholderNode, from: sourceScale) + transition.animatePositionAdditive(layer: self.imageNode.layer, offset: offset) + transition.horizontal.animateTransformScale(node: self.imageNode, from: sourceScale) + transition.animatePositionAdditive(layer: self.placeholderNode.layer, offset: offset) + transition.horizontal.animateTransformScale(node: self.placeholderNode, from: sourceScale) let inverseScale = 1.0 / sourceScale @@ -1309,7 +1309,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { x: -offset.x - localSourceOffset.x * (inverseScale - 1.0), y: -offset.y - localSourceOffset.y * (inverseScale - 1.0) ), removeOnCompletion: false) - transition.updateTransformScale(layer: snapshotView.layer, scale: 1.0 / sourceScale) + transition.horizontal.updateTransformScale(layer: snapshotView.layer, scale: 1.0 / sourceScale) snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.06, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView?.removeFromSuperview() @@ -1335,13 +1335,13 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } } - func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, transition: ContainedViewLayoutTransition) { + func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, transition: CombinedTransition) { if let replyInfoNode = self.replyInfoNode { let localRect = self.contextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view) - let offset = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, horizontalTransition: transition, verticalTransition: transition) + let offset = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, transition: transition) if let replyBackgroundNode = self.replyBackgroundNode { - transition.animatePositionAdditive(node: replyBackgroundNode, offset: offset) + transition.animatePositionAdditive(layer: replyBackgroundNode.layer, offset: offset) replyBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift index 5f518c7f8c..27ace02de0 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift @@ -82,19 +82,27 @@ private final class OverlayTransitionContainerController: ViewController, Standa } final class ChatMessageTransitionNode: ASDisplayNode { + static let animationDuration: Double = 0.3 + + static let verticalAnimationControlPoints: (Float, Float, Float, Float) = (0.19919472913616398, 0.010644531250000006, 0.27920937042459737, 0.91025390625) + static let verticalAnimationCurve: ContainedViewLayoutTransitionCurve = .custom(verticalAnimationControlPoints.0, verticalAnimationControlPoints.1, verticalAnimationControlPoints.2, verticalAnimationControlPoints.3) + static let horizontalAnimationCurve: ContainedViewLayoutTransitionCurve = .custom(0.23, 1.0, 0.32, 1.0) + final class ReplyPanel { let titleNode: ASDisplayNode let textNode: ASDisplayNode let lineNode: ASDisplayNode let imageNode: ASDisplayNode let relativeSourceRect: CGRect + let relativeTargetRect: CGRect - init(titleNode: ASDisplayNode, textNode: ASDisplayNode, lineNode: ASDisplayNode, imageNode: ASDisplayNode, relativeSourceRect: CGRect) { + init(titleNode: ASDisplayNode, textNode: ASDisplayNode, lineNode: ASDisplayNode, imageNode: ASDisplayNode, relativeSourceRect: CGRect, relativeTargetRect: CGRect) { self.titleNode = titleNode self.textNode = textNode self.lineNode = lineNode self.imageNode = imageNode self.relativeSourceRect = relativeSourceRect + self.relativeTargetRect = relativeTargetRect } } @@ -207,8 +215,8 @@ final class ChatMessageTransitionNode: ASDisplayNode { } func beginAnimation() { - let verticalDuration: Double = 0.5 - let horizontalDuration: Double = verticalDuration * 0.5 + let verticalDuration: Double = ChatMessageTransitionNode.animationDuration + let horizontalDuration: Double = verticalDuration// * 0.5 let delay: Double = 0.0 var updatedContentAreaInScreenSpace = self.getContentAreaInScreenSpace() @@ -253,48 +261,57 @@ final class ChatMessageTransitionNode: ASDisplayNode { var sourceReplyPanel: ReplyPanel? if let replyPanel = replyPanel, let replyPanelParentView = replyPanel.view.superview { - var replySourceAbsoluteFrame = replyPanelParentView.convert(replyPanel.originalFrameBeforeDismissed ?? replyPanel.frame, to: self.view) + let replyPanelFrame = replyPanel.originalFrameBeforeDismissed ?? replyPanel.frame + var replySourceAbsoluteFrame = replyPanelParentView.convert(replyPanelFrame, to: self.view) + replySourceAbsoluteFrame.origin.x -= sourceAbsoluteRect.minX - self.contextSourceNode.contentRect.minX replySourceAbsoluteFrame.origin.y -= sourceAbsoluteRect.minY - self.contextSourceNode.contentRect.minY - sourceReplyPanel = ReplyPanel(titleNode: replyPanel.titleNode, textNode: replyPanel.textNode, lineNode: replyPanel.lineNode, imageNode: replyPanel.imageNode, relativeSourceRect: replySourceAbsoluteFrame) + var globalTargetFrame = replySourceAbsoluteFrame.offsetBy(dx: 0.0, dy: replyPanelFrame.height) + + globalTargetFrame.origin.x += sourceAbsoluteRect.minX - targetAbsoluteRect.minX + globalTargetFrame.origin.y += sourceAbsoluteRect.minY - targetAbsoluteRect.minY + + sourceReplyPanel = ReplyPanel(titleNode: replyPanel.titleNode, textNode: replyPanel.textNode, lineNode: replyPanel.lineNode, imageNode: replyPanel.imageNode, relativeSourceRect: replySourceAbsoluteFrame, relativeTargetRect: globalTargetFrame) } self.itemNode.cancelInsertionAnimations() - let transition: ContainedViewLayoutTransition = .animated(duration: horizontalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) - let verticalTransition: ContainedViewLayoutTransition = .animated(duration: verticalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) + let horizontalCurve = ChatMessageTransitionNode.horizontalAnimationCurve + let horizontalTransition: ContainedViewLayoutTransition = .animated(duration: horizontalDuration, curve: horizontalCurve) + let verticalCurve = ChatMessageTransitionNode.verticalAnimationCurve + let verticalTransition: ContainedViewLayoutTransition = .animated(duration: verticalDuration, curve: verticalCurve) - let combinedTransition = CombinedTransition(horizontal: transition, vertical: verticalTransition) + let combinedTransition = CombinedTransition(horizontal: horizontalTransition, vertical: verticalTransition) if let itemNode = self.itemNode as? ChatMessageBubbleItemNode { itemNode.animateContentFromTextInputField(textInput: textInput, transition: combinedTransition) if let sourceReplyPanel = sourceReplyPanel { - itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, horizontalTransition: transition, verticalTransition: verticalTransition) + itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: combinedTransition) } } else if let itemNode = self.itemNode as? ChatMessageAnimatedStickerItemNode { - itemNode.animateContentFromTextInputField(textInput: textInput, transition: transition) + itemNode.animateContentFromTextInputField(textInput: textInput, transition: combinedTransition) if let sourceReplyPanel = sourceReplyPanel { - itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: transition) + itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: combinedTransition) } } else if let itemNode = self.itemNode as? ChatMessageStickerItemNode { - itemNode.animateContentFromTextInputField(textInput: textInput, transition: transition) + itemNode.animateContentFromTextInputField(textInput: textInput, transition: combinedTransition) if let sourceReplyPanel = sourceReplyPanel { - itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: transition) + itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: combinedTransition) } } self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY) self.contextSourceNode.updateAbsoluteRect?(self.containerNode.frame, UIScreen.main.bounds.size) - self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.maxY - targetAbsoluteRect.maxY), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true, completion: { [weak self] _ in + self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.maxY - targetAbsoluteRect.maxY), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: verticalCurve.mediaTimingFunction, additive: true, force: true, completion: { [weak self] _ in guard let strongSelf = self else { return } strongSelf.endAnimation() }) - self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true) - self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), .custom(0.33, 0.0, 0.0, 1.0), horizontalDuration) - self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.maxY - targetAbsoluteRect.maxY), .custom(0.33, 0.0, 0.0, 1.0), verticalDuration) + self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: horizontalCurve.mediaTimingFunction, additive: true) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), horizontalCurve, horizontalDuration) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.maxY - targetAbsoluteRect.maxY), verticalCurve, verticalDuration) case let .stickerMediaInput(stickerMediaInput, replyPanel): self.itemNode.cancelInsertionAnimations() @@ -328,37 +345,37 @@ final class ChatMessageTransitionNode: ASDisplayNode { replySourceAbsoluteFrame.origin.x -= sourceAbsoluteRect.midX - self.contextSourceNode.contentRect.midX replySourceAbsoluteFrame.origin.y -= sourceAbsoluteRect.midY - self.contextSourceNode.contentRect.midY - sourceReplyPanel = ReplyPanel(titleNode: replyPanel.titleNode, textNode: replyPanel.textNode, lineNode: replyPanel.lineNode, imageNode: replyPanel.imageNode, relativeSourceRect: replySourceAbsoluteFrame) + sourceReplyPanel = ReplyPanel(titleNode: replyPanel.titleNode, textNode: replyPanel.textNode, lineNode: replyPanel.lineNode, imageNode: replyPanel.imageNode, relativeSourceRect: replySourceAbsoluteFrame, relativeTargetRect: replySourceAbsoluteFrame.offsetBy(dx: 0.0, dy: replySourceAbsoluteFrame.height)) } - let transition: ContainedViewLayoutTransition = .animated(duration: horizontalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) + let combinedTransition = CombinedTransition(horizontal: .animated(duration: horizontalDuration, curve: ChatMessageTransitionNode.horizontalAnimationCurve), vertical: .animated(duration: verticalDuration, curve: ChatMessageTransitionNode.verticalAnimationCurve)) if let itemNode = self.itemNode as? ChatMessageAnimatedStickerItemNode { - itemNode.animateContentFromStickerGridItem(stickerSource: stickerSource, transition: transition) + itemNode.animateContentFromStickerGridItem(stickerSource: stickerSource, transition: combinedTransition) if let sourceAnimationNode = stickerSource.animationNode { itemNode.animationNode?.setFrameIndex(sourceAnimationNode.currentFrameIndex) } if let sourceReplyPanel = sourceReplyPanel { - itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: transition) + itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: combinedTransition) } } else if let itemNode = self.itemNode as? ChatMessageStickerItemNode { - itemNode.animateContentFromStickerGridItem(stickerSource: stickerSource, transition: transition) + itemNode.animateContentFromStickerGridItem(stickerSource: stickerSource, transition: combinedTransition) if let sourceReplyPanel = sourceReplyPanel { - itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: transition) + itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: combinedTransition) } } self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY) self.contextSourceNode.updateAbsoluteRect?(self.containerNode.frame, UIScreen.main.bounds.size) - self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true, completion: { [weak self] _ in + self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.verticalAnimationCurve.mediaTimingFunction, additive: true, force: true, completion: { [weak self] _ in guard let strongSelf = self else { return } strongSelf.endAnimation() }) - self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), .custom(0.33, 0.0, 0.0, 1.0), horizontalDuration) - self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), .custom(0.33, 0.0, 0.0, 1.0), verticalDuration) - self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), ChatMessageTransitionNode.horizontalAnimationCurve, horizontalDuration) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), ChatMessageTransitionNode.verticalAnimationCurve, verticalDuration) + self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.horizontalAnimationCurve.mediaTimingFunction, additive: true) switch stickerMediaInput { case .inputPanel: @@ -379,17 +396,17 @@ final class ChatMessageTransitionNode: ASDisplayNode { container.isHidden = true - let transition: ContainedViewLayoutTransition = .animated(duration: horizontalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) + let combinedTransition = CombinedTransition(horizontal: .animated(duration: horizontalDuration, curve: ChatMessageTransitionNode.horizontalAnimationCurve), vertical: .animated(duration: verticalDuration, curve: ChatMessageTransitionNode.verticalAnimationCurve)) if let itemNode = self.itemNode as? ChatMessageBubbleItemNode { - if let contextContainer = itemNode.animateFromMicInput(micInputNode: snapshotView, transition: transition) { + if let contextContainer = itemNode.animateFromMicInput(micInputNode: snapshotView, transition: combinedTransition) { self.containerNode.addSubnode(contextContainer.contentNode) let targetAbsoluteRect = contextContainer.view.convert(contextContainer.contentRect, to: self.view) self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -contextContainer.contentRect.minX, dy: -contextContainer.contentRect.minY) contextContainer.updateAbsoluteRect?(self.containerNode.frame, UIScreen.main.bounds.size) - self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true, completion: { [weak self, weak contextContainer, weak container] _ in + self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.verticalAnimationCurve.mediaTimingFunction, additive: true, force: true, completion: { [weak self, weak contextContainer, weak container] _ in guard let strongSelf = self else { return } @@ -404,13 +421,13 @@ final class ChatMessageTransitionNode: ASDisplayNode { strongSelf.endAnimation() }) - self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true) + self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.horizontalAnimationCurve.mediaTimingFunction, additive: true) } } } } case let .videoMessage(videoMessage): - let transition: ContainedViewLayoutTransition = .animated(duration: verticalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) + let combinedTransition = CombinedTransition(horizontal: .animated(duration: horizontalDuration, curve: ChatMessageTransitionNode.horizontalAnimationCurve), vertical: .animated(duration: verticalDuration, curve: ChatMessageTransitionNode.verticalAnimationCurve)) if let itemNode = self.itemNode as? ChatMessageInstantVideoItemNode { itemNode.cancelInsertionAnimations() @@ -426,9 +443,9 @@ final class ChatMessageTransitionNode: ASDisplayNode { videoMessage.view.frame = videoMessage.view.frame.offsetBy(dx: targetAbsoluteRect.midX - sourceAbsoluteRect.midX, dy: targetAbsoluteRect.midY - sourceAbsoluteRect.midY) self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY) - self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true) + self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.horizontalAnimationCurve.mediaTimingFunction, additive: true, force: true) - self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, completion: { [weak self] _ in + self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.verticalAnimationCurve.mediaTimingFunction, additive: true, completion: { [weak self] _ in guard let strongSelf = self else { return } @@ -436,7 +453,7 @@ final class ChatMessageTransitionNode: ASDisplayNode { strongSelf.endAnimation() }) - itemNode.animateFromSnapshot(snapshotView: videoMessage.view, transition: transition) + itemNode.animateFromSnapshot(snapshotView: videoMessage.view, transition: combinedTransition) } case let .mediaInput(mediaInput): if let snapshotView = mediaInput.extractSnapshot() { @@ -452,11 +469,10 @@ final class ChatMessageTransitionNode: ASDisplayNode { let sourceBackgroundAbsoluteRect = snapshotView.frame let sourceAbsoluteRect = CGRect(origin: CGPoint(x: sourceBackgroundAbsoluteRect.midX - self.contextSourceNode.contentRect.size.width / 2.0, y: sourceBackgroundAbsoluteRect.midY - self.contextSourceNode.contentRect.size.height / 2.0), size: self.contextSourceNode.contentRect.size) - let transition: ContainedViewLayoutTransition = .animated(duration: verticalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) - let verticalTransition: ContainedViewLayoutTransition = .animated(duration: horizontalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) + let combinedTransition = CombinedTransition(horizontal: .animated(duration: horizontalDuration, curve: ChatMessageTransitionNode.horizontalAnimationCurve), vertical: .animated(duration: verticalDuration, curve: ChatMessageTransitionNode.verticalAnimationCurve)) if let itemNode = self.itemNode as? ChatMessageBubbleItemNode { - itemNode.animateContentFromMediaInput(snapshotView: snapshotView, horizontalTransition: verticalTransition, verticalTransition: transition) + itemNode.animateContentFromMediaInput(snapshotView: snapshotView, transition: combinedTransition) } self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY) @@ -466,24 +482,24 @@ final class ChatMessageTransitionNode: ASDisplayNode { self.contextSourceNode.updateAbsoluteRect?(self.containerNode.frame, UIScreen.main.bounds.size) - self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true) - self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true, completion: { [weak self] _ in + self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.horizontalAnimationCurve.mediaTimingFunction, additive: true, force: true) + self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: ChatMessageTransitionNode.verticalAnimationCurve.mediaTimingFunction, additive: true, force: true, completion: { [weak self] _ in guard let strongSelf = self else { return } strongSelf.endAnimation() }) - verticalTransition.animateTransformScale(node: self.contextSourceNode.contentNode, from: CGPoint(x: sourceBackgroundAbsoluteRect.width / targetAbsoluteRect.width, y: sourceBackgroundAbsoluteRect.height / targetAbsoluteRect.height)) + combinedTransition.horizontal.animateTransformScale(node: self.contextSourceNode.contentNode, from: CGPoint(x: sourceBackgroundAbsoluteRect.width / targetAbsoluteRect.width, y: sourceBackgroundAbsoluteRect.height / targetAbsoluteRect.height)) - verticalTransition.updateTransformScale(layer: snapshotView.layer, scale: CGPoint(x: 1.0 / (sourceBackgroundAbsoluteRect.width / targetAbsoluteRect.width), y: 1.0 / (sourceBackgroundAbsoluteRect.height / targetAbsoluteRect.height))) + combinedTransition.horizontal.updateTransformScale(layer: snapshotView.layer, scale: CGPoint(x: 1.0 / (sourceBackgroundAbsoluteRect.width / targetAbsoluteRect.width), y: 1.0 / (sourceBackgroundAbsoluteRect.height / targetAbsoluteRect.height))) snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView?.removeFromSuperview() }) - self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), .custom(0.33, 0.0, 0.0, 1.0), horizontalDuration) - self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.maxY - targetAbsoluteRect.maxY), .custom(0.33, 0.0, 0.0, 1.0), verticalDuration) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), ChatMessageTransitionNode.horizontalAnimationCurve, horizontalDuration) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.maxY - targetAbsoluteRect.maxY), ChatMessageTransitionNode.verticalAnimationCurve, verticalDuration) } } } @@ -606,7 +622,7 @@ final class ChatMessageTransitionNode: ASDisplayNode { animatingItemNode.frame = self.bounds animatingItemNode.beginAnimation() - self.onTransitionEvent(.animated(duration: 0.5, curve: .custom(0.33, 0.0, 0.0, 1.0))) + self.onTransitionEvent(.animated(duration: ChatMessageTransitionNode.animationDuration, curve: ChatMessageTransitionNode.verticalAnimationCurve)) } } diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index bc66e72492..08932027df 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -127,7 +127,6 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { super.init() - self.backgroundNode.image = chatControllerBackgroundImage(theme: self.state.theme, wallpaper: self.state.chatWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper) self.backgroundNode.update(wallpaper: self.state.chatWallpaper) self.addSubnode(self.backgroundNode) diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 66c901917d..db297ace30 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -2210,14 +2210,25 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { let backgroundView = UIImageView(image: backgroundImage) backgroundView.frame = self.textInputBackgroundNode.frame - //let previousTintColor = textInputNode.view.tintColor - //textInputNode.view.tintColor = .clear + func updateIsCaretHidden(view: UIView, isHidden: Bool) { + if String(describing: type(of: view)).contains("TextSelectionView") { + view.isHidden = isHidden + } else { + for subview in view.subviews { + updateIsCaretHidden(view: subview, isHidden: isHidden) + } + } + } + + updateIsCaretHidden(view: textInputNode.view, isHidden: true) guard let contentView = textInputNode.view.snapshotView(afterScreenUpdates: true) else { - //textInputNode.view.tintColor = previousTintColor + updateIsCaretHidden(view: textInputNode.view, isHidden: false) return nil } - //textInputNode.view.tintColor = previousTintColor + + updateIsCaretHidden(view: textInputNode.view, isHidden: false) + contentView.frame = textInputNode.frame return ChatMessageTransitionNode.Source.TextInput( diff --git a/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift b/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift index bbf2aa8b02..cc92ef8e82 100644 --- a/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift +++ b/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift @@ -54,6 +54,9 @@ final class InstantVideoController: LegacyController, StandalonePresentableContr func bindCaptureController(_ captureController: TGVideoMessageCaptureController?) { self.captureController = captureController if let captureController = captureController { + captureController.view.disablesInteractiveKeyboardGestureRecognizer = true + captureController.view.disablesInteractiveTransitionGestureRecognizer = true + captureController.micLevel = { [weak self] (level: CGFloat) -> Void in self?.micLevelValue.set(Float(level)) } diff --git a/submodules/TelegramUI/Sources/OpenChatMessage.swift b/submodules/TelegramUI/Sources/OpenChatMessage.swift index 01809d3e85..ddf9a413ef 100644 --- a/submodules/TelegramUI/Sources/OpenChatMessage.swift +++ b/submodules/TelegramUI/Sources/OpenChatMessage.swift @@ -263,7 +263,7 @@ func openChatWallpaper(context: AccountContext, message: Message, present: @esca case let .color(color): source = .wallpaper(.color(color.argb), nil, nil, nil, nil, nil, message) case let .gradient(topColor, bottomColor, rotation): - source = .wallpaper(.gradient(topColor.argb, bottomColor.argb, WallpaperSettings(rotation: rotation)), nil, nil, nil, nil, rotation, message) + source = .wallpaper(.gradient([topColor.argb, bottomColor.argb], WallpaperSettings(rotation: rotation)), nil, nil, nil, nil, rotation, message) } let controller = WallpaperGalleryController(context: context, source: source) diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index 282e5f2185..9b5cea8e26 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -311,7 +311,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur case let .color(color): signal = .single(.color(color.argb)) case let .gradient(topColor, bottomColor, rotation): - signal = .single(.gradient(topColor.argb, bottomColor.argb, WallpaperSettings(rotation: rotation))) + signal = .single(.gradient([topColor.argb, bottomColor.argb], WallpaperSettings(rotation: rotation))) } let _ = (signal @@ -400,7 +400,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur navigationController?.pushViewController(previewController) } } else if let settings = dataAndTheme.1 { - if let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), accentColor: UIColor(argb: settings.accentColor), backgroundColors: nil, bubbleColors: settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: settings.wallpaper) { + if let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), accentColor: UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: settings.wallpaper) { let previewController = ThemePreviewController(context: context, previewTheme: theme, source: .theme(dataAndTheme.2)) navigationController?.pushViewController(previewController) } diff --git a/submodules/TelegramUI/Sources/UpgradedAccounts.swift b/submodules/TelegramUI/Sources/UpgradedAccounts.swift index 1198e7b2fb..7ab947c193 100644 --- a/submodules/TelegramUI/Sources/UpgradedAccounts.swift +++ b/submodules/TelegramUI/Sources/UpgradedAccounts.swift @@ -166,8 +166,8 @@ public func upgradedAccounts(accountManager: AccountManager, rootPath: String, e accountManager.mediaBox.storeResourceData(file.file.resource.id, data: data) let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start() if wallpaper.isPattern { - if let color = file.settings.color, let intensity = file.settings.intensity { - let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: color, bottomColor: file.settings.bottomColor, intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true).start() + if !file.settings.colors.isEmpty, let intensity = file.settings.intensity { + let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: file.settings.colors[0], bottomColor: file.settings.colors.count >= 2 ? file.settings.colors[1] : file.settings.colors[0], intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true).start() } } else { if file.settings.blur { diff --git a/submodules/TelegramVoip/Sources/GroupCallContext.swift b/submodules/TelegramVoip/Sources/GroupCallContext.swift index 9f5863f3ad..ffdd8185ee 100644 --- a/submodules/TelegramVoip/Sources/GroupCallContext.swift +++ b/submodules/TelegramVoip/Sources/GroupCallContext.swift @@ -187,7 +187,7 @@ public final class OngoingGroupCallContext { } } - public struct VideoChannel { + public struct VideoChannel: Equatable { public enum Quality { case thumbnail case medium @@ -216,6 +216,8 @@ public final class OngoingGroupCallContext { let isMuted = ValuePromise(true, ignoreRepeated: true) let isNoiseSuppressionEnabled = ValuePromise(true, ignoreRepeated: true) let audioLevels = ValuePipe<[(AudioLevelKey, Float, Bool)]>() + + private var currentRequestedVideoChannels: [VideoChannel] = [] private var broadcastPartsSource: BroadcastPartSource? @@ -370,22 +372,26 @@ public final class OngoingGroupCallContext { } func setRequestedVideoChannels(_ channels: [VideoChannel]) { - self.context.setRequestedVideoChannels(channels.map { channel -> OngoingGroupCallRequestedVideoChannel in - let mappedQuality: OngoingGroupCallRequestedVideoQuality - switch channel.quality { - case .thumbnail: - mappedQuality = .thumbnail - case .medium: - mappedQuality = .medium - case .full: - mappedQuality = .full - } - return OngoingGroupCallRequestedVideoChannel( - audioSsrc: channel.audioSsrc, - videoInformation: channel.videoDescription, - quality: mappedQuality - ) - }) + if self.currentRequestedVideoChannels != channels { + self.currentRequestedVideoChannels = channels + + self.context.setRequestedVideoChannels(channels.map { channel -> OngoingGroupCallRequestedVideoChannel in + let mappedQuality: OngoingGroupCallRequestedVideoQuality + switch channel.quality { + case .thumbnail: + mappedQuality = .thumbnail + case .medium: + mappedQuality = .medium + case .full: + mappedQuality = .full + } + return OngoingGroupCallRequestedVideoChannel( + audioSsrc: channel.audioSsrc, + videoInformation: channel.videoDescription, + quality: mappedQuality + ) + }) + } } func stop() { diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index f7319dc6b8..e66d8a025c 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit f7319dc6b8cf10fc1eec3b21a7d26f92a43fb956 +Subproject commit e66d8a025c9a5c6b1097c0017af18ff6a085d8e9 diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index cd637a78a8..1d83f90eed 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -51,12 +51,6 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } } } - - public var image: UIImage? { - didSet { - self.contentNode.contents = self.image?.cgImage - } - } public var rotation: CGFloat = 0.0 { didSet { @@ -99,6 +93,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self.clipsToBounds = true self.contentNode.frame = self.bounds self.addSubnode(self.contentNode) + self.addSubnode(self.patternImageNode) } deinit { @@ -112,50 +107,69 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } self.wallpaper = wallpaper - if case let .builtin(gradient, _) = wallpaper { + var gradientColors: [UInt32] = [] + var gradientAngle: Int32 = 0 + + if case let .color(color) = wallpaper { + gradientColors = [color] + } else if case let .gradient(colors, settings) = wallpaper { + gradientColors = colors + gradientAngle = settings.rotation ?? 0 + } else if case let .file(_, _, _, _, isPattern, _, _, _, settings) = wallpaper, isPattern { + gradientColors = settings.colors + gradientAngle = settings.rotation ?? 0 + } + + if gradientColors.count >= 3 { if self.gradientBackgroundNode == nil { let gradientBackgroundNode = createGradientBackgroundNode() self.gradientBackgroundNode = gradientBackgroundNode self.insertSubnode(gradientBackgroundNode, aboveSubnode: self.contentNode) gradientBackgroundNode.addSubnode(self.patternImageNode) } - self.gradientBackgroundNode?.updateColors(colors: gradient?.colors.map({ color -> UIColor in + self.gradientBackgroundNode?.updateColors(colors: gradientColors.map { color -> UIColor in return UIColor(rgb: color) - }) ?? defaultBuiltinWallpaperGradientColors) - self.contentNode.isHidden = true - } else if case let .file(_, _, _, _, isPattern, _, _, _, settings) = wallpaper, isPattern, !settings.additionalColors.isEmpty { - if self.gradientBackgroundNode == nil { - let gradientBackgroundNode = createGradientBackgroundNode() - self.gradientBackgroundNode = gradientBackgroundNode - self.insertSubnode(gradientBackgroundNode, aboveSubnode: self.contentNode) - gradientBackgroundNode.addSubnode(self.patternImageNode) - } - var colors: [UInt32] = [] - colors.append(settings.color ?? 0) - colors.append(settings.bottomColor ?? 0) - colors.append(contentsOf: settings.additionalColors) - self.gradientBackgroundNode?.updateColors(colors: colors.map({ color -> UIColor in - return UIColor(rgb: color) - })) - self.contentNode.isHidden = true + }) + + self.contentNode.backgroundColor = nil + self.contentNode.contents = nil + self.motionEnabled = false } else { if let gradientBackgroundNode = self.gradientBackgroundNode { self.gradientBackgroundNode = nil gradientBackgroundNode.removeFromSupernode() + self.insertSubnode(self.patternImageNode, aboveSubnode: self.contentNode) } - if case .gradient = wallpaper { - self.imageContentMode = .scaleToFill - } else { - self.imageContentMode = .scaleAspectFill - } self.motionEnabled = wallpaper.settings?.motion ?? false - self.contentNode.isHidden = false + if gradientColors.count >= 2 { + self.contentNode.backgroundColor = nil + self.contentNode.contents = generateImage(CGSize(width: 100.0, height: 200.0), rotatedContext: { size, context in + let gradientColors = [UIColor(rgb: gradientColors[0]).cgColor, UIColor(rgb: gradientColors[1]).cgColor] as CFArray + + var locations: [CGFloat] = [0.0, 1.0] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! + + context.translateBy(x: size.width / 2.0, y: size.height / 2.0) + context.rotate(by: CGFloat(gradientAngle) * CGFloat.pi / 180.0) + context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) + })?.cgImage + } else if gradientColors.count >= 1 { + self.contentNode.backgroundColor = UIColor(rgb: gradientColors[0]) + self.contentNode.contents = nil + } else { + self.contentNode.backgroundColor = .white + self.contentNode.contents = chatControllerBackgroundImage(theme: nil, wallpaper: wallpaper, mediaBox: self.context.sharedContext.accountManager.mediaBox, knockoutMode: false)?.cgImage + self.contentNode.isHidden = false + } } switch wallpaper { - case let .file(id, _, _, _, isPattern, _, _, file, settings): + case let .file(id, _, _, _, isPattern, _, _, file, settings) where isPattern: var updated = true if let previousWallpaper = previousWallpaper { switch previousWallpaper { diff --git a/submodules/WallpaperResources/BUILD b/submodules/WallpaperResources/BUILD index e5aa740c94..45408d2b8f 100644 --- a/submodules/WallpaperResources/BUILD +++ b/submodules/WallpaperResources/BUILD @@ -20,6 +20,7 @@ swift_library( "//submodules/PersistentStringHash:PersistentStringHash", "//submodules/AppBundle:AppBundle", "//submodules/Svg:Svg", + "//submodules/GradientBackground:GradientBackground", ], visibility = [ "//visibility:public", diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index 0332fe9a7c..e60c0ef07d 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -15,6 +15,7 @@ import TelegramPresentationData import TelegramUIPreferences import AppBundle import Svg +import GradientBackground public func wallpaperDatas(account: Account, accountManager: AccountManager, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], alwaysShowThumbnailFirst: Bool = false, thumbnail: Bool = false, onlyFullSize: Bool = false, autoFetchFullSize: Bool = false, synchronousLoad: Bool = false) -> Signal<(Data?, Data?, Bool), NoError> { if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = largestImageRepresentation(representations.map({ $0.representation })), let smallestIndex = representations.firstIndex(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.firstIndex(where: { $0.representation == largestRepresentation }) { @@ -765,12 +766,23 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallp case let .color(color): c.setFillColor(UIColor(rgb: color).cgColor) c.fill(drawingRect) - case let .gradient(topColor, bottomColor, _): - let gradientColors = [UIColor(rgb: topColor), UIColor(rgb: bottomColor)].map { $0.cgColor } as CFArray - var locations: [CGFloat] = [0.0, 1.0] - let colorSpace = CGColorSpaceCreateDeviceRGB() - let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! - c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: drawingRect.height), end: CGPoint(x: 0.0, y: 0.0), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) + case let .gradient(colors, _): + if colors.count >= 3 { + let image = GradientBackgroundNode.generatePreview(size: CGSize(width: 60.0, height: 60.0), colors: colors.map(UIColor.init(rgb:))) + c.draw(image.cgImage!, in: drawingRect) + } else if colors.count >= 2 { + let gradientColors = colors.map({ UIColor(rgb: $0).cgColor }) as CFArray + var locations: [CGFloat] = [0.0, 1.0] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! + c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: drawingRect.height), end: CGPoint(x: 0.0, y: 0.0), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) + } else if colors.count >= 1 { + let gradientColors = [UIColor(rgb: colors[0]), UIColor(rgb: colors[0])] as CFArray + var locations: [CGFloat] = [0.0, 1.0] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! + c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: drawingRect.height), end: CGPoint(x: 0.0, y: 0.0), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) + } case .file: c.setFillColor(theme.chatList.backgroundColor.cgColor) c.fill(drawingRect) @@ -966,7 +978,7 @@ public func themeImage(account: Account, accountManager: AccountManager, source: } } case let .settings(settings): - theme = .single((makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), accentColor: UIColor(argb: settings.accentColor), backgroundColors: nil, bubbleColors: settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: settings.wallpaper, serviceBackgroundColor: nil, preview: false), nil)) + theme = .single((makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), accentColor: UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: settings.wallpaper, serviceBackgroundColor: nil, preview: false), nil)) } let data = theme @@ -986,8 +998,8 @@ public func themeImage(account: Account, accountManager: AccountManager, source: accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData) let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start() - if wallpaper.wallpaper.isPattern, let color = file.settings.color, let intensity = file.settings.intensity { - return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: color, bottomColor: file.settings.bottomColor, intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true) + if wallpaper.wallpaper.isPattern, !file.settings.colors.isEmpty, let intensity = file.settings.intensity { + return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: file.settings.colors[0], bottomColor: file.settings.colors.count >= 2 ? file.settings.colors[1] : file.settings.colors[0], intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true) |> mapToSignal { data in if data.complete, let data = try? Data(contentsOf: URL(fileURLWithPath: data.path)), let image = UIImage(data: data) { return .single((theme, image, thumbnailData)) @@ -1090,7 +1102,7 @@ public func themeImage(account: Account, accountManager: AccountManager, source: } public func themeIconImage(account: Account, accountManager: AccountManager, theme: PresentationThemeReference, color: PresentationThemeAccentColor?, wallpaper: TelegramWallpaper? = nil) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { - let colorsSignal: Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> + let colorsSignal: Signal<((UIColor, UIColor?, [UInt32]), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> if case let .builtin(theme) = theme { let incomingColor: UIColor let outgoingColor: (UIColor, UIColor) @@ -1103,8 +1115,10 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the incomingColor = UIColor(rgb: 0xffffff) if let accentColor = accentColor { if let wallpaper = wallpaper, case let .file(file) = wallpaper { - topBackgroundColor = file.settings.color.flatMap { UIColor(rgb: $0) } ?? UIColor(rgb: 0xd6e2ee) - bottomBackgroundColor = file.settings.bottomColor.flatMap { UIColor(rgb: $0) } + topBackgroundColor = file.settings.colors.first.flatMap { UIColor(rgb: $0) } ?? UIColor(rgb: 0xd6e2ee) + if file.settings.colors.count >= 2 { + bottomBackgroundColor = UIColor(rgb: file.settings.colors[1]) + } } else { if let bubbleColors = bubbleColors { topBackgroundColor = UIColor(rgb: 0xd6e2ee) @@ -1146,32 +1160,41 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the let accentBubbleColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59) outgoingColor = bubbleColors ?? (accentBubbleColor, accentBubbleColor) } - + + var colors: [UInt32] = [] var rotation: Int32? if let wallpaper = wallpaper { switch wallpaper { case let .color(color): + colors = [color] topBackgroundColor = UIColor(rgb: color) - case let .gradient(topColor, bottomColor, settings): - topBackgroundColor = UIColor(rgb: topColor) - bottomBackgroundColor = UIColor(rgb: bottomColor) + case let .gradient(colorsValue, settings): + colors = colorsValue + if colors.count >= 1 { + topBackgroundColor = UIColor(rgb: colors[0]) + } + if colors.count >= 2 { + bottomBackgroundColor = UIColor(rgb: colors[1]) + } rotation = settings.rotation case let .file(file): - if let color = file.settings.color { - topBackgroundColor = UIColor(rgb: color) - bottomBackgroundColor = file.settings.bottomColor.flatMap { UIColor(rgb: $0) } + colors = file.settings.colors + if !file.settings.colors.isEmpty { + topBackgroundColor = UIColor(rgb: file.settings.colors[0]) + if file.settings.colors.count >= 2 { + bottomBackgroundColor = UIColor(rgb: file.settings.colors[1]) + } } rotation = file.settings.rotation default: + colors = [0xd6e2ee] topBackgroundColor = UIColor(rgb: 0xd6e2ee) } } - colorsSignal = .single(((topBackgroundColor, bottomBackgroundColor), (incomingColor, incomingColor), outgoingColor, nil, rotation)) + colorsSignal = .single(((topBackgroundColor, bottomBackgroundColor, colors), (incomingColor, incomingColor), outgoingColor, nil, rotation)) } else { - var resource: MediaResource? var reference: MediaResourceReference? - var defaultWallpaper: TelegramWallpaper? if case let .local(theme) = theme { reference = .standalone(resource: theme.resource) } else if case let .cloud(theme) = theme, let resource = theme.theme.file?.resource { @@ -1181,7 +1204,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the let themeSignal: Signal if case let .cloud(theme) = theme, let settings = theme.theme.settings { themeSignal = Signal { subscriber in - let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), accentColor: UIColor(argb: settings.accentColor), backgroundColors: nil, bubbleColors: settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: settings.wallpaper, serviceBackgroundColor: nil, preview: false) + let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), accentColor: UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: settings.wallpaper, serviceBackgroundColor: nil, preview: false) subscriber.putNext(theme) subscriber.putCompletion() @@ -1201,42 +1224,54 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the } colorsSignal = themeSignal - |> mapToSignal { theme -> Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> in + |> mapToSignal { theme -> Signal<((UIColor, UIColor?, [UInt32]), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> in if let theme = theme { - var wallpaperSignal: Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> = .complete() + var wallpaperSignal: Signal<((UIColor, UIColor?, [UInt32]), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> = .complete() var rotation: Int32? - var backgroundColor: (UIColor, UIColor?) + var backgroundColor: (UIColor, UIColor?, [UInt32]) let incomingColor = (theme.chat.message.incoming.bubble.withoutWallpaper.fill, theme.chat.message.incoming.bubble.withoutWallpaper.gradientFill) let outgoingColor = (theme.chat.message.outgoing.bubble.withoutWallpaper.fill, theme.chat.message.outgoing.bubble.withoutWallpaper.gradientFill) switch theme.chat.defaultWallpaper { case .builtin: - backgroundColor = (UIColor(rgb: 0xd6e2ee), nil) + backgroundColor = (UIColor(rgb: 0xd6e2ee), nil, []) case let .color(color): - backgroundColor = (UIColor(rgb: color), nil) - case let .gradient(topColor, bottomColor, settings): - backgroundColor = (UIColor(rgb: topColor), UIColor(rgb: bottomColor)) + backgroundColor = (UIColor(rgb: color), nil, []) + case let .gradient(colors, settings): + if colors.count >= 2 { + backgroundColor = (UIColor(rgb: colors[0]), UIColor(rgb: colors[1]), colors) + } else { + backgroundColor = (.white, nil, []) + } rotation = settings.rotation case .image: - backgroundColor = (.black, nil) + backgroundColor = (.black, nil, []) case let .file(file): rotation = file.settings.rotation - if let color = file.settings.color { - backgroundColor = (UIColor(rgb: color), file.settings.bottomColor.flatMap { UIColor(rgb: $0) }) + if !file.settings.colors.isEmpty { + var bottomColor: UIColor? + if file.settings.colors.count >= 2 { + bottomColor = UIColor(rgb: file.settings.colors[1]) + } + backgroundColor = (UIColor(rgb: file.settings.colors[0]), bottomColor, file.settings.colors) } else { - backgroundColor = (theme.chatList.backgroundColor, nil) + backgroundColor = (theme.chatList.backgroundColor, nil, []) } wallpaperSignal = cachedWallpaper(account: account, slug: file.slug, settings: file.settings) |> mapToSignal { wallpaper in if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper { var effectiveBackgroundColor = backgroundColor - if let color = file.settings.color { - effectiveBackgroundColor = (UIColor(rgb: color), file.settings.bottomColor.flatMap { UIColor(rgb: $0) }) + if !file.settings.colors.isEmpty { + var bottomColor: UIColor? + if file.settings.colors.count >= 2 { + bottomColor = UIColor(rgb: file.settings.colors[1]) + } + effectiveBackgroundColor = (UIColor(rgb: file.settings.colors[0]), bottomColor, file.settings.colors) } var convertedRepresentations: [ImageRepresentationWithReference] = [] convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: nil), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource))) return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false) - |> mapToSignal { _, fullSizeData, complete -> Signal<((UIColor, UIColor?), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> in + |> mapToSignal { _, fullSizeData, complete -> Signal<((UIColor, UIColor?, [UInt32]), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> in guard complete, let fullSizeData = fullSizeData else { return .complete() } @@ -1244,8 +1279,8 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start() if wallpaper.wallpaper.isPattern { - if let color = file.settings.color, let intensity = file.settings.intensity { - return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: color, bottomColor: file.settings.bottomColor, intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true) + if !file.settings.colors.isEmpty, let intensity = file.settings.intensity { + return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(color: file.settings.colors[0], bottomColor: file.settings.colors.count >= 2 ? file.settings.colors[1] : file.settings.colors[0], intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true) |> mapToSignal { _ in return .single((effectiveBackgroundColor, incomingColor, outgoingColor, nil, rotation)) } @@ -1286,7 +1321,10 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the let drawingRect = arguments.drawingRect context.withContext { c in - if let secondBackgroundColor = colors.0.1 { + if colors.0.2.count >= 3 { + let image = GradientBackgroundNode.generatePreview(size: CGSize(width: 60.0, height: 60.0), colors: colors.0.2.map(UIColor.init(rgb:))) + c.draw(image.cgImage!, in: drawingRect) + } else if let secondBackgroundColor = colors.0.1 { let gradientColors = [colors.0.0, secondBackgroundColor].map { $0.cgColor } as CFArray var locations: [CGFloat] = [0.0, 1.0] let colorSpace = CGColorSpaceCreateDeviceRGB()