diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index 689d970ca8..4258505cef 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -17,6 +17,8 @@ 091346982183498A00846E49 /* InstantPageArticleNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091346972183498A00846E49 /* InstantPageArticleNode.swift */; }; 0913469A218528D200846E49 /* InstantPageTableItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09134699218528D200846E49 /* InstantPageTableItem.swift */; }; 0913469C21883C3700846E49 /* InstantPageDetailsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0913469B21883C3700846E49 /* InstantPageDetailsItem.swift */; }; + 091417F221EF4E5D00C8325A /* WallpaperGalleryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091417F121EF4E5D00C8325A /* WallpaperGalleryController.swift */; }; + 091417F421EF4F5F00C8325A /* WallpaperGalleryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091417F321EF4F5F00C8325A /* WallpaperGalleryItem.swift */; }; 091BEAB3214552D9003AEA30 /* Vision.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D02DADBE2138D76F00116225 /* Vision.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 092F368D2154AAEA001A9F49 /* SFCompactRounded-Semibold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 092F368C2154AAE9001A9F49 /* SFCompactRounded-Semibold.otf */; }; 092F36902157AB46001A9F49 /* ItemListCallListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 092F368F2157AB46001A9F49 /* ItemListCallListItem.swift */; }; @@ -1141,6 +1143,8 @@ 091346972183498A00846E49 /* InstantPageArticleNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageArticleNode.swift; sourceTree = ""; }; 09134699218528D200846E49 /* InstantPageTableItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageTableItem.swift; sourceTree = ""; }; 0913469B21883C3700846E49 /* InstantPageDetailsItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageDetailsItem.swift; sourceTree = ""; }; + 091417F121EF4E5D00C8325A /* WallpaperGalleryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallpaperGalleryController.swift; sourceTree = ""; }; + 091417F321EF4F5F00C8325A /* WallpaperGalleryItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallpaperGalleryItem.swift; sourceTree = ""; }; 092F368C2154AAE9001A9F49 /* SFCompactRounded-Semibold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SFCompactRounded-Semibold.otf"; sourceTree = ""; }; 092F368F2157AB46001A9F49 /* ItemListCallListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemListCallListItem.swift; sourceTree = ""; }; 09310D14213BC5DE0020033A /* anim_read.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = anim_read.json; sourceTree = ""; }; @@ -3148,6 +3152,8 @@ 09F664CF21EBCFB900AB7E26 /* WallpaperCropNode.swift */, 09DD5D5121ED175300D7007A /* WallpaperColorPickerNode.swift */, 0900678C21ED5EA800530762 /* WallpaperColorPanelNode.swift */, + 091417F121EF4E5D00C8325A /* WallpaperGalleryController.swift */, + 091417F321EF4F5F00C8325A /* WallpaperGalleryItem.swift */, ); name = Themes; sourceTree = ""; @@ -5871,6 +5877,7 @@ D093D7E22062F40100BC3599 /* SecureIdDocumentFormControllerNode.swift in Sources */, D0B2F7702052B5A800D3BFB9 /* InviteContactsControllerNode.swift in Sources */, D0EC6E211EB9F58900EBF1C3 /* InstantPageController.swift in Sources */, + 091417F221EF4E5D00C8325A /* WallpaperGalleryController.swift in Sources */, D0671F232143BDA6000A8AE7 /* TwoStepVerificationEmptyItem.swift in Sources */, D0EC6E221EB9F58900EBF1C3 /* InstantPageControllerNode.swift in Sources */, D0EC6E231EB9F58900EBF1C3 /* StickerPackPreviewController.swift in Sources */, @@ -5935,6 +5942,7 @@ D01C06BE1FBCAF06001561AB /* ChatMessageBubbleMosaicLayout.swift in Sources */, 0900678D21ED5EA800530762 /* WallpaperColorPanelNode.swift in Sources */, D0EC6E451EB9F58900EBF1C3 /* ItemListMultilineTextItem.swift in Sources */, + 091417F421EF4F5F00C8325A /* WallpaperGalleryItem.swift in Sources */, D02F4AE91FCF370B004DFBAE /* ChatMessageInteractiveMediaBadge.swift in Sources */, D0EC6E461EB9F58900EBF1C3 /* ItemListLoadingIndicatorEmptyStateItem.swift in Sources */, D067B4A5211C911C00796039 /* LegacyChannelIntroController.swift in Sources */, diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index 9d3791b8a9..aa4eb01e41 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -3619,7 +3619,11 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal if let button = leftNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: self.leftNavigationButton, target: self, selector: #selector(self.leftNavigationButtonAction)) { if self.leftNavigationButton != button { - self.navigationItem.setLeftBarButton(button.buttonItem, animated: transition.isAnimated) + var animated = transition.isAnimated + if let currentButton = self.leftNavigationButton?.action, currentButton == button.action { + animated = false + } + self.navigationItem.setLeftBarButton(button.buttonItem, animated: animated) self.leftNavigationButton = button } } else if let _ = self.leftNavigationButton { @@ -3629,7 +3633,11 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal if let button = rightNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: self.rightNavigationButton, target: self, selector: #selector(self.rightNavigationButtonAction), chatInfoNavigationButton: self.chatInfoNavigationButton) { if self.rightNavigationButton != button { - self.navigationItem.setRightBarButton(button.buttonItem, animated: transition.isAnimated) + var animated = transition.isAnimated + if let currentButton = self.rightNavigationButton?.action, currentButton == button.action { + animated = false + } + self.navigationItem.setRightBarButton(button.buttonItem, animated: animated) self.rightNavigationButton = button } } else if let _ = self.rightNavigationButton { diff --git a/TelegramUI/ChatControllerBackgroundNode.swift b/TelegramUI/ChatControllerBackgroundNode.swift index eb366fb5b3..d2889081f6 100644 --- a/TelegramUI/ChatControllerBackgroundNode.swift +++ b/TelegramUI/ChatControllerBackgroundNode.swift @@ -11,7 +11,7 @@ final class ChatBackgroundNode: ASDisplayNode { didSet { if oldValue != self.parallaxEnabled { if self.parallaxEnabled { - let amount = 16.0 + let amount = 24.0 let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis) horizontal.minimumRelativeValue = -amount @@ -57,10 +57,10 @@ final class ChatBackgroundNode: ASDisplayNode { } } -private var backgroundImageForWallpaper: (TelegramWallpaper, PresentationWallpaperMode, UIImage)? +private var backgroundImageForWallpaper: (TelegramWallpaper, WallpaperPresentationOptions, UIImage)? private var serviceBackgroundColorForWallpaper: (TelegramWallpaper, UIColor)? -func chatControllerBackgroundImage(wallpaper: TelegramWallpaper, mode: PresentationWallpaperMode = .still, postbox: Postbox) -> UIImage? { +func chatControllerBackgroundImage(wallpaper: TelegramWallpaper, mode: WallpaperPresentationOptions = [], postbox: Postbox) -> UIImage? { var backgroundImage: UIImage? if wallpaper == backgroundImageForWallpaper?.0, mode == backgroundImageForWallpaper?.1 { backgroundImage = backgroundImageForWallpaper?.2 @@ -77,7 +77,7 @@ func chatControllerBackgroundImage(wallpaper: TelegramWallpaper, mode: Presentat }) case let .image(representations): if let largest = largestImageRepresentation(representations) { - if case .blurred = mode { + if mode.contains(.blur) { var image: UIImage? let _ = postbox.mediaBox.cachedResourceRepresentation(largest.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true, attemptSynchronously: true).start(next: { data in if data.complete { @@ -91,7 +91,7 @@ func chatControllerBackgroundImage(wallpaper: TelegramWallpaper, mode: Presentat } } case let .file(file): - if case .blurred = mode { + if mode.contains(.blur) { var image: UIImage? let _ = postbox.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true, attemptSynchronously: true).start(next: { data in if data.complete { @@ -255,7 +255,7 @@ func backgroundContrastColor(for image: Signal) -> Signal ChatPresentationInterfaceState { + func updatedChatWallpaper(_ chatWallpaper: TelegramWallpaper, mode: WallpaperPresentationOptions) -> ChatPresentationInterfaceState { return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: chatWallpaper, chatWallpaperMode: mode, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } } diff --git a/TelegramUI/OpenChatMessage.swift b/TelegramUI/OpenChatMessage.swift index bc96375a52..147709703d 100644 --- a/TelegramUI/OpenChatMessage.swift +++ b/TelegramUI/OpenChatMessage.swift @@ -410,7 +410,7 @@ func openChatWallpaper(account: Account, message: Message, present: @escaping (V let _ = (resolveUrl(account: account, url: content.url) |> deliverOnMainQueue).start(next: { resolvedUrl in if case let .wallpaper(parameter) = resolvedUrl { - let source: WallpaperListPreviewSource + let source: WallpaperListSource switch parameter { case let .slug(slug): source = .slug(slug, content.file) diff --git a/TelegramUI/PermissionInfoItem.swift b/TelegramUI/PermissionInfoItem.swift index 160bcdc8f4..2b5e75d7d5 100644 --- a/TelegramUI/PermissionInfoItem.swift +++ b/TelegramUI/PermissionInfoItem.swift @@ -304,7 +304,7 @@ class PermissionInfoItemNode: ListViewItemNode { strongSelf.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: strongSelf.titleNode.frame.maxY + 9.0), size: textLayout.size) - strongSelf.closeButton.frame = CGRect(x: params.width - rightInset - 21.0, y: 10.0, width: 32.0, height: 32.0) + strongSelf.closeButton.frame = CGRect(x: params.width - rightInset - 26.0, y: 10.0, width: 32.0, height: 32.0) } }) } diff --git a/TelegramUI/PresentationData.swift b/TelegramUI/PresentationData.swift index 319c9ba0cc..7cf1b64ee1 100644 --- a/TelegramUI/PresentationData.swift +++ b/TelegramUI/PresentationData.swift @@ -46,7 +46,7 @@ public final class PresentationData: Equatable { public let strings: PresentationStrings public let theme: PresentationTheme public let chatWallpaper: TelegramWallpaper - public let chatWallpaperMode: PresentationWallpaperMode + public let chatWallpaperMode: WallpaperPresentationOptions public let volumeControlStatusBarIcons: PresentationVolumeControlStatusBarIcons public let fontSize: PresentationFontSize public let dateTimeFormat: PresentationDateTimeFormat @@ -54,7 +54,7 @@ public final class PresentationData: Equatable { public let nameSortOrder: PresentationPersonNameOrder public let disableAnimations: Bool - public init(strings: PresentationStrings, theme: PresentationTheme, chatWallpaper: TelegramWallpaper, chatWallpaperMode: PresentationWallpaperMode, volumeControlStatusBarIcons: PresentationVolumeControlStatusBarIcons, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, nameSortOrder: PresentationPersonNameOrder, disableAnimations: Bool) { + public init(strings: PresentationStrings, theme: PresentationTheme, chatWallpaper: TelegramWallpaper, chatWallpaperMode: WallpaperPresentationOptions, volumeControlStatusBarIcons: PresentationVolumeControlStatusBarIcons, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, nameSortOrder: PresentationPersonNameOrder, disableAnimations: Bool) { self.strings = strings self.theme = theme self.chatWallpaper = chatWallpaper @@ -236,7 +236,7 @@ public func currentPresentationDataAndSettings(postbox: Postbox) -> Signal Signal Signal PresentationData { let nameSortOrder = currentPersonNameSortOrder() let themeSettings = PresentationThemeSettings.defaultSettings - return PresentationData(strings: defaultPresentationStrings, theme: defaultPresentationTheme, chatWallpaper: .builtin, chatWallpaperMode: .still, volumeControlStatusBarIcons: volumeControlStatusBarIcons(), fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations) + return PresentationData(strings: defaultPresentationStrings, theme: defaultPresentationTheme, chatWallpaper: .builtin, chatWallpaperMode: [], volumeControlStatusBarIcons: volumeControlStatusBarIcons(), fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations) } diff --git a/TelegramUI/PresentationThemeSettings.swift b/TelegramUI/PresentationThemeSettings.swift index dd1791e8ef..a26a3c9b3a 100644 --- a/TelegramUI/PresentationThemeSettings.swift +++ b/TelegramUI/PresentationThemeSettings.swift @@ -10,10 +10,19 @@ public enum PresentationBuiltinThemeReference: Int32 { case nightAccent = 3 } -public enum PresentationWallpaperMode: Int32 { - case still - case perspective - case blurred +public struct WallpaperPresentationOptions: OptionSet { + public var rawValue: Int32 + + public init(rawValue: Int32) { + self.rawValue = rawValue + } + + public init() { + self.rawValue = 0 + } + + public static let motion = WallpaperPresentationOptions(rawValue: 1 << 0) + public static let blur = WallpaperPresentationOptions(rawValue: 1 << 1) } public enum PresentationThemeReference: PostboxCoding, Equatable { @@ -147,7 +156,7 @@ public struct AutomaticThemeSwitchSetting: PostboxCoding, Equatable { public struct PresentationThemeSettings: PreferencesEntry { public var chatWallpaper: TelegramWallpaper - public var chatWallpaperMode: PresentationWallpaperMode + public var chatWallpaperOptions: WallpaperPresentationOptions public var theme: PresentationThemeReference public var themeAccentColor: Int32? public var fontSize: PresentationFontSize @@ -164,12 +173,12 @@ public struct PresentationThemeSettings: PreferencesEntry { } public static var defaultSettings: PresentationThemeSettings { - return PresentationThemeSettings(chatWallpaper: .builtin, chatWallpaperMode: .still, theme: .builtin(.dayClassic), themeAccentColor: nil, fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .none, theme: .nightAccent), disableAnimations: true) + return PresentationThemeSettings(chatWallpaper: .builtin, chatWallpaperOptions: [], theme: .builtin(.dayClassic), themeAccentColor: nil, fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .none, theme: .nightAccent), disableAnimations: true) } - public init(chatWallpaper: TelegramWallpaper, chatWallpaperMode: PresentationWallpaperMode, theme: PresentationThemeReference, themeAccentColor: Int32?, fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, disableAnimations: Bool) { + public init(chatWallpaper: TelegramWallpaper, chatWallpaperOptions: WallpaperPresentationOptions, theme: PresentationThemeReference, themeAccentColor: Int32?, fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, disableAnimations: Bool) { self.chatWallpaper = chatWallpaper - self.chatWallpaperMode = chatWallpaperMode + self.chatWallpaperOptions = chatWallpaperOptions self.theme = theme self.themeAccentColor = themeAccentColor self.fontSize = fontSize @@ -179,7 +188,7 @@ public struct PresentationThemeSettings: PreferencesEntry { public init(decoder: PostboxDecoder) { self.chatWallpaper = (decoder.decodeObjectForKey("w", decoder: { TelegramWallpaper(decoder: $0) }) as? TelegramWallpaper) ?? .builtin - self.chatWallpaperMode = PresentationWallpaperMode(rawValue: decoder.decodeInt32ForKey("m", orElse: 0)) ?? .still + self.chatWallpaperOptions = WallpaperPresentationOptions(rawValue: decoder.decodeInt32ForKey("o", orElse: 0)) self.theme = decoder.decodeObjectForKey("t", decoder: { PresentationThemeReference(decoder: $0) }) as! PresentationThemeReference self.themeAccentColor = decoder.decodeOptionalInt32ForKey("themeAccentColor") self.fontSize = PresentationFontSize(rawValue: decoder.decodeInt32ForKey("f", orElse: PresentationFontSize.regular.rawValue)) ?? .regular @@ -189,7 +198,7 @@ public struct PresentationThemeSettings: PreferencesEntry { public func encode(_ encoder: PostboxEncoder) { encoder.encodeObject(self.chatWallpaper, forKey: "w") - encoder.encodeInt32(self.chatWallpaperMode.rawValue, forKey: "m") + encoder.encodeInt32(self.chatWallpaperOptions.rawValue, forKey: "o") encoder.encodeObject(self.theme, forKey: "t") if let themeAccentColor = self.themeAccentColor { encoder.encodeInt32(themeAccentColor, forKey: "themeAccentColor") @@ -210,7 +219,7 @@ public struct PresentationThemeSettings: PreferencesEntry { } public static func ==(lhs: PresentationThemeSettings, rhs: PresentationThemeSettings) -> Bool { - return lhs.chatWallpaper == rhs.chatWallpaper && lhs.chatWallpaperMode == rhs.chatWallpaperMode && lhs.theme == rhs.theme && lhs.themeAccentColor == rhs.themeAccentColor && lhs.fontSize == rhs.fontSize && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting && lhs.disableAnimations == rhs.disableAnimations + return lhs.chatWallpaper == rhs.chatWallpaper && lhs.chatWallpaperOptions == rhs.chatWallpaperOptions && lhs.theme == rhs.theme && lhs.themeAccentColor == rhs.themeAccentColor && lhs.fontSize == rhs.fontSize && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting && lhs.disableAnimations == rhs.disableAnimations } } diff --git a/TelegramUI/RadialCheckContentNode.swift b/TelegramUI/RadialCheckContentNode.swift index 07304d9364..1c959bded5 100644 --- a/TelegramUI/RadialCheckContentNode.swift +++ b/TelegramUI/RadialCheckContentNode.swift @@ -47,7 +47,7 @@ final class RadialCheckContentNode: RadialStatusContentNode { self.isLayerBacked = true } - func animateProgress() { + func animateProgress(delay: Double) { self.animationCompletionTimer?.invalidate() self.animationCompletionTimer = nil let animation = POPBasicAnimation() @@ -64,6 +64,7 @@ final class RadialCheckContentNode: RadialStatusContentNode { animation.toValue = 1.0 as NSNumber animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) animation.duration = 0.25 + animation.beginTime = delay animation.completionBlock = { [weak self] _, _ in if let strongSelf = self { strongSelf.animationCompletionTimer?.invalidate() @@ -163,10 +164,10 @@ final class RadialCheckContentNode: RadialStatusContentNode { self.layer.animateScale(from: 1.0, to: 0.6, duration: duration, removeOnCompletion: false) } - override func animateIn(from: RadialStatusNodeState) { - self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) - self.layer.animateScale(from: 0.7, to: 1.0, duration: duration) - self.animateProgress() + override func animateIn(from: RadialStatusNodeState, delay: Double) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration, delay: delay) + self.layer.animateScale(from: 0.7, to: 1.0, duration: duration, delay: delay) + self.animateProgress(delay: delay) } } diff --git a/TelegramUI/RadialCloudProgressContentNode.swift b/TelegramUI/RadialCloudProgressContentNode.swift index cef8d36e53..3644030ce2 100644 --- a/TelegramUI/RadialCloudProgressContentNode.swift +++ b/TelegramUI/RadialCloudProgressContentNode.swift @@ -294,8 +294,8 @@ final class RadialCloudProgressContentNode: RadialStatusContentNode { self.cancelNode.layer.animateScale(from: 1.0, to: 0.3, duration: 0.15, removeOnCompletion: false) } - override func animateIn(from: RadialStatusNodeState) { - self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) - self.cancelNode.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15) + override func animateIn(from: RadialStatusNodeState, delay: Double) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay) + self.cancelNode.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, delay: delay) } } diff --git a/TelegramUI/RadialDownloadContentNode.swift b/TelegramUI/RadialDownloadContentNode.swift index ec0bc70d36..ae0701cb08 100644 --- a/TelegramUI/RadialDownloadContentNode.swift +++ b/TelegramUI/RadialDownloadContentNode.swift @@ -116,11 +116,8 @@ final class RadialDownloadContentNode: RadialStatusContentNode { let diameter = min(bounds.size.width, bounds.size.height) let factor = diameter / 50.0 - var lineWidth: CGFloat = 2.0 - if diameter < 24.0 { - lineWidth = 1.3 - } - + let lineWidth: CGFloat = max(1.6, 2.25 * factor) + self.leftLine.lineWidth = lineWidth self.rightLine.lineWidth = lineWidth self.arrowBody.lineWidth = lineWidth @@ -142,34 +139,35 @@ final class RadialDownloadContentNode: RadialStatusContentNode { private let duration: Double = 0.2 - override func prepareAnimateOut(completion: @escaping () -> Void) { + override func prepareAnimateOut(completion: @escaping (Double) -> Void) { let bounds = self.bounds let diameter = min(bounds.size.width, bounds.size.height) let factor = diameter / 50.0 var bodyPath = UIBezierPath() - if let path = try? svgPath("M1.20125335,62.2095675 C1.78718228,62.9863141 2.3877868,63.7395876 3.00158591,64.4690754 C22.1087455,87.1775489 54.0019347,86.8368674 54.0066002,54.0178571 L54.0066002,0.625 ", scale: CGPoint(x: 0.333333 * factor, y: 0.333333 * factor), offset: CGPoint(x: 7.0 * factor, y: (17.0 - UIScreenPixel) * factor)) { + + if let path = try? svgPath("M1.10890748,47.3077093 C2.74202161,51.7201715 4.79761832,55.7299828 7.15775768,59.3122505 C25.4413606,87.0634763 62.001605,89.1563513 62.0066002,54.0178571 L62.0066002,0.625 ", scale: CGPoint(x: 0.333333 * factor, y: 0.333333 * factor), offset: CGPoint(x: (4.0 + UIScreenPixel) * factor, y: (17.0 - UIScreenPixel) * factor)) { bodyPath = path } self.arrowBody.path = bodyPath.cgPath - self.arrowBody.strokeStart = 0.62 + self.arrowBody.strokeStart = 0.65 self.leftLine.animateStrokeEnd(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) self.rightLine.animateStrokeEnd(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) - self.leftLine.animateAlpha(from: 1.0, to: 0.0, duration: 0.23, delay: 0.07, removeOnCompletion: false) { finished in - completion() + self.leftLine.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.07, removeOnCompletion: false) { finished in + completion(0.0) } - self.rightLine.animateAlpha(from: 1.0, to: 0.0, duration: 0.23, delay: 0.07, removeOnCompletion: false) + self.rightLine.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.07, removeOnCompletion: false) } override func animateOut(to: RadialStatusNodeState, completion: @escaping () -> Void) { - self.arrowBody.animateStrokeStart(from: 0.62, to: 0.0, duration: 0.5, removeOnCompletion: false, completion: { _ in + self.arrowBody.animateStrokeStart(from: 0.65, to: 0.0, duration: 0.5, removeOnCompletion: false, completion: { _ in completion() }) self.arrowBody.animateStrokeEnd(from: 1.0, to: 0.0, duration: 0.5, removeOnCompletion: false, completion: nil) - self.arrowBody.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, delay: 0.4, removeOnCompletion: false) + self.arrowBody.animateAlpha(from: 1.0, to: 0.0, duration: 0.01, delay: 0.4, removeOnCompletion: false) } override func prepareAnimateIn(from: RadialStatusNodeState?) { @@ -178,28 +176,28 @@ final class RadialDownloadContentNode: RadialStatusContentNode { let factor = diameter / 50.0 var bodyPath = UIBezierPath() - if let path = try? svgPath("M1.20125335,62.2095675 C1.78718228,62.9863141 2.3877868,63.7395876 3.00158591,64.4690754 C22.1087455,87.1775489 54.0019347,86.8368674 54.0066002,54.0178571 L54.0066002,0.625 ", scale: CGPoint(x: -0.333333 * factor, y: 0.333333 * factor), offset: CGPoint(x: 43.0 * factor, y: (17.0 - UIScreenPixel) * factor)) { + if let path = try? svgPath("M1.10890748,47.3077093 C2.74202161,51.7201715 4.79761832,55.7299828 7.15775768,59.3122505 C25.4413606,87.0634763 62.001605,89.1563513 62.0066002,54.0178571 L62.0066002,0.625 ", scale: CGPoint(x: -0.333333 * factor, y: 0.333333 * factor), offset: CGPoint(x: (46.0 - UIScreenPixel) * factor, y: (17.0 - UIScreenPixel) * factor)) { bodyPath = path } self.arrowBody.path = bodyPath.cgPath - self.arrowBody.strokeStart = 0.62 + self.arrowBody.strokeStart = 0.65 } - override func animateIn(from: RadialStatusNodeState) { + override func animateIn(from: RadialStatusNodeState, delay: Double) { if case .progress = from { - self.arrowBody.animateStrokeStart(from: 0.62, to: 0.62, duration: 0.25, removeOnCompletion: false, completion: nil) - self.arrowBody.animateStrokeEnd(from: 0.62, to: 1.0, duration: 0.25, removeOnCompletion: false, completion: nil) + self.arrowBody.animateStrokeStart(from: 0.65, to: 0.65, duration: 0.25, delay: delay, removeOnCompletion: false, completion: nil) + self.arrowBody.animateStrokeEnd(from: 0.65, to: 1.0, duration: 0.25, delay: delay, removeOnCompletion: false, completion: nil) - self.leftLine.animateStrokeEnd(from: 0.0, to: 1.0, duration: 0.25, delay: 0.0, removeOnCompletion: false) - self.rightLine.animateStrokeEnd(from: 0.0, to: 1.0, duration: 0.25, delay: 0.0, removeOnCompletion: false) + self.leftLine.animateStrokeEnd(from: 0.0, to: 1.0, duration: 0.25, delay: delay, removeOnCompletion: false) + self.rightLine.animateStrokeEnd(from: 0.0, to: 1.0, duration: 0.25, delay: delay, removeOnCompletion: false) - self.arrowBody.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.0, removeOnCompletion: false) - self.leftLine.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.0, removeOnCompletion: false) - self.rightLine.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.0, removeOnCompletion: false) + self.arrowBody.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: delay, removeOnCompletion: false) + self.leftLine.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: delay, removeOnCompletion: false) + self.rightLine.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: delay, removeOnCompletion: false) } else { - self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) - self.layer.animateScale(from: 0.7, to: 1.0, duration: duration) + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration, delay: delay) + self.layer.animateScale(from: 0.7, to: 1.0, duration: duration, delay: delay) } } } diff --git a/TelegramUI/RadialProgressContentNode.swift b/TelegramUI/RadialProgressContentNode.swift index 4317777ad8..02d3d807b6 100644 --- a/TelegramUI/RadialProgressContentNode.swift +++ b/TelegramUI/RadialProgressContentNode.swift @@ -152,14 +152,16 @@ private final class RadialProgressContentSpinnerNode: ASDisplayNode { super.willEnterHierarchy() let basicAnimation = CABasicAnimation(keyPath: "transform.rotation.z") - basicAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) basicAnimation.duration = 1.5 - let fromValue = Float.pi + 0.33 + var fromValue = Float.pi + 0.58 + if let presentation = self.layer.presentation(), let value = (presentation.value(forKeyPath: "transform.rotation.z") as? NSNumber)?.floatValue { + fromValue = value + } basicAnimation.fromValue = NSNumber(value: fromValue) basicAnimation.toValue = NSNumber(value: fromValue + Float.pi * 2.0) basicAnimation.repeatCount = Float.infinity basicAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) - basicAnimation.beginTime = 0.0 //1.0 + basicAnimation.beginTime = 0.0 self.layer.add(basicAnimation, forKey: "progressRotation") } @@ -274,14 +276,6 @@ final class RadialProgressContentNode: RadialStatusContentNode { } } - override func enqueueReadyForTransition(_ f: @escaping () -> Void) { - if self.spinnerNode.isAnimatingProgress && self.progress == 1.0 { - self.enqueuedReadyForTransition = f - } else { - f() - } - } - override func layout() { super.layout() @@ -291,10 +285,9 @@ final class RadialProgressContentNode: RadialStatusContentNode { self.cancelNode.frame = bounds } - override func prepareAnimateOut(completion: @escaping () -> Void) { - self.cancelNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.15, removeOnCompletion: false, completion: { _ in - completion() - }) + override func prepareAnimateOut(completion: @escaping (Double) -> Void) { + self.cancelNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.15, removeOnCompletion: false, completion: { _ in }) + completion(0.0) } override func animateOut(to: RadialStatusNodeState, completion: @escaping () -> Void) { @@ -308,11 +301,11 @@ final class RadialProgressContentNode: RadialStatusContentNode { self.spinnerNode.progress = self.progress } - override func animateIn(from: RadialStatusNodeState) { + override func animateIn(from: RadialStatusNodeState, delay: Double) { if case .download = from { } else { - self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: delay) } - self.cancelNode.layer.animateScale(from: 0.2, to: 1.0, duration: 0.2) + self.cancelNode.layer.animateScale(from: 0.2, to: 1.0, duration: 0.2, delay: delay) } } diff --git a/TelegramUI/RadialStatusContentNode.swift b/TelegramUI/RadialStatusContentNode.swift index 68d503edb9..cf00068680 100644 --- a/TelegramUI/RadialStatusContentNode.swift +++ b/TelegramUI/RadialStatusContentNode.swift @@ -9,8 +9,8 @@ class RadialStatusContentNode: ASDisplayNode { private let duration: Double = 0.2 - func prepareAnimateOut(completion: @escaping () -> Void) { - completion() + func prepareAnimateOut(completion: @escaping (Double) -> Void) { + completion(0.0) } func animateOut(to: RadialStatusNodeState, completion: @escaping () -> Void) { @@ -23,8 +23,8 @@ class RadialStatusContentNode: ASDisplayNode { func prepareAnimateIn(from: RadialStatusNodeState?) { } - func animateIn(from: RadialStatusNodeState) { - self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) - self.layer.animateScale(from: 0.2, to: 1.0, duration: duration) + func animateIn(from: RadialStatusNodeState, delay: Double) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration, delay: delay) + self.layer.animateScale(from: 0.2, to: 1.0, duration: duration, delay: delay) } } diff --git a/TelegramUI/RadialStatusNode.swift b/TelegramUI/RadialStatusNode.swift index d04a7d32d6..12731aebf4 100644 --- a/TelegramUI/RadialStatusNode.swift +++ b/TelegramUI/RadialStatusNode.swift @@ -173,23 +173,18 @@ public final class RadialStatusNode: ASControlNode { } } }) - previousContentNode.prepareAnimateOut(completion: { + previousContentNode.prepareAnimateOut(completion: { delay in if let contentNode = strongSelf.contentNode { strongSelf.addSubnode(contentNode) contentNode.frame = strongSelf.bounds contentNode.prepareAnimateIn(from: fromState) if strongSelf.isNodeLoaded { contentNode.layout() - contentNode.animateIn(from: fromState) + contentNode.animateIn(from: fromState, delay: delay) } } - if backgroundColor != nil { - strongSelf.transitionToBackgroundColor(backgroundColor, previousContentNode: previousContentNode, animated: animated, completion: completion) - } - }) - if backgroundColor == nil { strongSelf.transitionToBackgroundColor(backgroundColor, previousContentNode: previousContentNode, animated: animated, completion: completion) - } + }) } else { previousContentNode.removeFromSupernode() strongSelf.contentNode = strongSelf.nextContentNode diff --git a/TelegramUI/RadialStatusSecretTimeoutContentNode.swift b/TelegramUI/RadialStatusSecretTimeoutContentNode.swift index 36f9e7fa80..3a0b7b9c5a 100644 --- a/TelegramUI/RadialStatusSecretTimeoutContentNode.swift +++ b/TelegramUI/RadialStatusSecretTimeoutContentNode.swift @@ -96,8 +96,8 @@ final class RadialStatusSecretTimeoutContentNode: RadialStatusContentNode { }) } - override func animateIn(from: RadialStatusNodeState) { - self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + override func animateIn(from: RadialStatusNodeState, delay: Double) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay) } override func willEnterHierarchy() { diff --git a/TelegramUI/SearchBarNode.swift b/TelegramUI/SearchBarNode.swift index 9f054da5cb..4fb303c025 100644 --- a/TelegramUI/SearchBarNode.swift +++ b/TelegramUI/SearchBarNode.swift @@ -388,7 +388,7 @@ class SearchBarNode: ASDisplayNode, UITextFieldDelegate { } if self.theme != theme { self.backgroundNode.backgroundColor = theme.background - if fieldStyle != .modern { + if self.fieldStyle != .modern { self.separatorNode.backgroundColor = theme.separator } self.textBackgroundNode.image = generateBackground(foregroundColor: theme.inputFill, diameter: self.fieldStyle.cornerDiameter) diff --git a/TelegramUI/SettingsThemeWallpaperNode.swift b/TelegramUI/SettingsThemeWallpaperNode.swift index 5302db8fb3..98b6417ccd 100644 --- a/TelegramUI/SettingsThemeWallpaperNode.swift +++ b/TelegramUI/SettingsThemeWallpaperNode.swift @@ -100,7 +100,6 @@ final class SettingsThemeWallpaperNode: ASDisplayNode { convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: .standalone(resource: representation.resource))) } let dimensions = file.file.dimensions ?? CGSize(width: 100.0, height: 100.0) - convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource), reference: .standalone(resource: file.file.resource))) self.imageNode.setSignal(chatAvatarGalleryPhoto(account: account, fileReference: .standalone(media: file.file), representations: convertedRepresentations, autoFetchFullSize: true)) let apply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: dimensions.aspectFilled(size), boundingSize: size, intrinsicInsets: UIEdgeInsets())) apply() @@ -116,13 +115,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode { let apply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: largestImageRepresentation(representations)!.dimensions.aspectFilled(size), boundingSize: size, intrinsicInsets: UIEdgeInsets())) apply() case let .file(file): - var convertedRepresentations: [ImageRepresentationWithReference] = [] - for representation in file.file.previewRepresentations { - convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: .standalone(resource: representation.resource))) - } let dimensions = file.file.dimensions ?? CGSize(width: 100.0, height: 100.0) - convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource), reference: .standalone(resource: file.file.resource))) - self.imageNode.setSignal(chatAvatarGalleryPhoto(account: account, fileReference: .standalone(media: file.file), representations: convertedRepresentations, autoFetchFullSize: true)) let apply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: dimensions.aspectFilled(size), boundingSize: size, intrinsicInsets: UIEdgeInsets())) apply() } diff --git a/TelegramUI/SystemVideoContent.swift b/TelegramUI/SystemVideoContent.swift index 533f2345d5..44227cb529 100644 --- a/TelegramUI/SystemVideoContent.swift +++ b/TelegramUI/SystemVideoContent.swift @@ -112,9 +112,9 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent } self.player.addObserver(self, forKeyPath: "rate", options: [], context: nil) - playerItem.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil) - playerItem.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil) - playerItem.addObserver(self, forKeyPath: "playbackBufferFull", options: .new, context: nil) + self.playerItem.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil) + self.playerItem.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil) + self.playerItem.addObserver(self, forKeyPath: "playbackBufferFull", options: .new, context: nil) self._bufferingStatus.set(.single(nil)) } diff --git a/TelegramUI/TelegramRootController.swift b/TelegramUI/TelegramRootController.swift index b7e8981733..0c614da7a5 100644 --- a/TelegramUI/TelegramRootController.swift +++ b/TelegramUI/TelegramRootController.swift @@ -76,20 +76,18 @@ public final class TelegramRootController: NavigationController { self.rootTabController = tabBarController self.pushViewController(tabBarController, animated: false) - ///TESTBED - - guard let controller = self.viewControllers.last as? ViewController else { - return - } - - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.15) { - //(controller.navigationController as? NavigationController)?.pushViewController(ThemeGridController(account: self.account)) - +// guard let controller = self.viewControllers.last as? ViewController else { +// return +// } +// +// DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.15) { +// //(controller.navigationController as? NavigationController)?.pushViewController(ThemeGridController(account: self.account)) +// // let wrapperNode = ASDisplayNode() // let bounds = controller.displayNode.bounds // wrapperNode.frame = bounds // wrapperNode.backgroundColor = .gray -// //controller.displayNode.addSubnode(wrapperNode) +// controller.displayNode.addSubnode(wrapperNode) // // let radialStatusSize: CGFloat = 50.0 // let statusNode = RadialStatusNode(backgroundNodeColor: UIColor(rgb: 0x000000, alpha: 0.6)) @@ -107,16 +105,21 @@ public final class TelegramRootController: NavigationController { // smth = false // //statusNode.transitionToState(.play(color), animated: true, completion: {}) // statusNode.transitionToState(.download(.white), animated: true, completion: {}) -// //statusNode.transitionToState(.none, animated: true, completion: {}) +//// statusNode.transitionToState(.progress(color: color, lineWidth: nil, value: 1.0, cancelEnabled: true), animated: true, completion: {}) +//// statusNode.transitionToState(.none, animated: true, completion: { +//// DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.85) { +//// statusNode.transitionToState(.download(.white), animated: true, completion: {}) +//// } +//// }) // } else { // smth = true -// statusNode.transitionToState(.progress(color: color, lineWidth: nil, value: 0.3, cancelEnabled: true), animated: true, completion: {}) +// statusNode.transitionToState(.progress(color: color, lineWidth: nil, value: 0.1, cancelEnabled: true), animated: true, completion: {}) // } // } // } // button.addTarget(self, action: #selector(self.mock), forControlEvents: .touchUpInside) // statusNode.transitionToState(.download(.white), animated: false, completion: {}) - } +// } } @objc func mock() { diff --git a/TelegramUI/ThemeGalleryController.swift b/TelegramUI/ThemeGalleryController.swift index 5d6b6cc2d3..4438e244f4 100644 --- a/TelegramUI/ThemeGalleryController.swift +++ b/TelegramUI/ThemeGalleryController.swift @@ -220,7 +220,7 @@ class ThemeGalleryController: ViewController { wallpaper = value } let _ = (updatePresentationThemeSettingsInteractively(postbox: strongSelf.account.postbox, { current in - return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperMode: .still, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperOptions: [], theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) }) |> deliverOnMainQueue).start(completed: { self?.dismiss(forceAway: true) }) diff --git a/TelegramUI/ThemeGridController.swift b/TelegramUI/ThemeGridController.swift index 3d898281f3..9445451910 100644 --- a/TelegramUI/ThemeGridController.swift +++ b/TelegramUI/ThemeGridController.swift @@ -103,6 +103,8 @@ final class ThemeGridController: ViewController { override func loadDisplayNode() { self.displayNode = ThemeGridControllerNode(account: self.account, presentationData: self.presentationData, presentPreviewController: { [weak self] source in if let strongSelf = self { + //let controller = WallpaperGalleryController(account: strongSelf.account, source: source) + //self?.present(controller, in: .window(.root), with: nil, blockInteraction: true) let controller = WallpaperListPreviewController(account: strongSelf.account, source: source) controller.apply = { [weak self, weak controller] wallpaper, mode, cropRect in if let strongSelf = self { @@ -236,7 +238,7 @@ final class ThemeGridController: ViewController { self.displayNodeDidLoad() } - private func uploadCustomWallpaper(_ wallpaper: WallpaperEntry, mode: PresentationWallpaperMode, cropRect: CGRect?) { + private func uploadCustomWallpaper(_ wallpaper: WallpaperEntry, mode: WallpaperPresentationOptions, cropRect: CGRect?) { let imageSignal: Signal switch wallpaper { case .wallpaper: @@ -305,7 +307,7 @@ final class ThemeGridController: ViewController { let account = self.account let updateWallpaper: (TelegramWallpaper) -> Void = { wallpaper in let _ = (updatePresentationThemeSettingsInteractively(postbox: account.postbox, { current in - return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperMode: mode, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperOptions: mode, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) })).start() } @@ -315,7 +317,7 @@ final class ThemeGridController: ViewController { let _ = uploadWallpaper(account: account, resource: resource).start(next: { status in if case let .complete(wallpaper) = status { - if case .blurred = mode, case let .file(_, _, _, _, file, _) = wallpaper { + if mode.contains(.blur), case let .file(_, _, _, _, file, _) = wallpaper { let _ = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: { updateWallpaper(wallpaper) }) @@ -326,7 +328,7 @@ final class ThemeGridController: ViewController { }) } - if case .blurred = mode { + if mode.contains(.blur) { let _ = account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: { completion() }) diff --git a/TelegramUI/ThemeGridControllerNode.swift b/TelegramUI/ThemeGridControllerNode.swift index 2324f19341..16b7eb1be7 100644 --- a/TelegramUI/ThemeGridControllerNode.swift +++ b/TelegramUI/ThemeGridControllerNode.swift @@ -121,7 +121,7 @@ final class ThemeGridControllerNode: ASDisplayNode { private var presentationData: PresentationData private var controllerInteraction: ThemeGridControllerInteraction? - private let presentPreviewController: (WallpaperListPreviewSource) -> Void + private let presentPreviewController: (WallpaperListSource) -> Void private let presentGallery: () -> Void private let presentColors: () -> Void private let emptyStateUpdated: (Bool) -> Void @@ -161,7 +161,7 @@ final class ThemeGridControllerNode: ASDisplayNode { private var disposable: Disposable? - init(account: Account, presentationData: PresentationData, presentPreviewController: @escaping (WallpaperListPreviewSource) -> Void, presentGallery: @escaping () -> Void, presentColors: @escaping () -> Void, emptyStateUpdated: @escaping (Bool) -> Void, deleteWallpapers: @escaping ([TelegramWallpaper], @escaping () -> Void) -> Void, shareWallpapers: @escaping ([TelegramWallpaper]) -> Void, popViewController: @escaping () -> Void) { + init(account: Account, presentationData: PresentationData, presentPreviewController: @escaping (WallpaperListSource) -> Void, presentGallery: @escaping () -> Void, presentColors: @escaping () -> Void, emptyStateUpdated: @escaping (Bool) -> Void, deleteWallpapers: @escaping ([TelegramWallpaper], @escaping () -> Void) -> Void, shareWallpapers: @escaping ([TelegramWallpaper]) -> Void, popViewController: @escaping () -> Void) { self.account = account self.presentationData = presentationData self.presentPreviewController = presentPreviewController @@ -217,7 +217,7 @@ final class ThemeGridControllerNode: ASDisplayNode { if let entries = entries, !entries.isEmpty { let wallpapers = entries.map { $0.wallpaper } - var mode: PresentationWallpaperMode? + var mode: WallpaperPresentationOptions? if wallpaper == strongSelf.presentationData.chatWallpaper { mode = strongSelf.presentationData.chatWallpaperMode } diff --git a/TelegramUI/ThemeSettingsChatPreviewItem.swift b/TelegramUI/ThemeSettingsChatPreviewItem.swift index da297d8eb0..abdd13bfd4 100644 --- a/TelegramUI/ThemeSettingsChatPreviewItem.swift +++ b/TelegramUI/ThemeSettingsChatPreviewItem.swift @@ -13,11 +13,11 @@ class ThemeSettingsChatPreviewItem: ListViewItem, ItemListItem { let sectionId: ItemListSectionId let fontSize: PresentationFontSize let wallpaper: TelegramWallpaper - let wallpaperMode: PresentationWallpaperMode + let wallpaperMode: WallpaperPresentationOptions let dateTimeFormat: PresentationDateTimeFormat let nameDisplayOrder: PresentationPersonNameOrder - init(account: Account, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, wallpaperMode: PresentationWallpaperMode, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) { + init(account: Account, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, wallpaperMode: WallpaperPresentationOptions, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) { self.account = account self.theme = theme self.componentTheme = componentTheme @@ -139,7 +139,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { }) case let .image(representations): if let largest = largestImageRepresentation(representations) { - if case .blurred = item.wallpaperMode { + if item.wallpaperMode.contains(.blur) { var image: UIImage? let _ = item.account.postbox.mediaBox.cachedResourceRepresentation(largest.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true, attemptSynchronously: true).start(next: { data in if data.complete { @@ -153,7 +153,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { } } case let .file(file): - if case .blurred = item.wallpaperMode { + if item.wallpaperMode.contains(.blur) { var image: UIImage? let _ = item.account.postbox.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true, attemptSynchronously: true).start(next: { data in if data.complete { diff --git a/TelegramUI/ThemeSettingsController.swift b/TelegramUI/ThemeSettingsController.swift index c4f59c9e4f..57c35e5616 100644 --- a/TelegramUI/ThemeSettingsController.swift +++ b/TelegramUI/ThemeSettingsController.swift @@ -35,7 +35,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { case fontSizeHeader(PresentationTheme, String) case fontSize(PresentationTheme, PresentationFontSize) case chatPreviewHeader(PresentationTheme, String) - case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationWallpaperMode, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder) + case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, WallpaperPresentationOptions, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder) case wallpaper(PresentationTheme, String) case accentColor(PresentationTheme, String, Int32) case autoNightTheme(PresentationTheme, String, String) @@ -210,7 +210,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { } } -private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeAccentColor: Int32?, autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, wallpaperMode: PresentationWallpaperMode, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, disableAnimations: Bool) -> [ThemeSettingsControllerEntry] { +private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeAccentColor: Int32?, autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, wallpaperMode: WallpaperPresentationOptions, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, disableAnimations: Bool) -> [ThemeSettingsControllerEntry] { var entries: [ThemeSettingsControllerEntry] = [] entries.append(.fontSizeHeader(presentationData.theme, strings.Appearance_TextSize)) @@ -270,25 +270,25 @@ public func themeSettingsController(account: Account) -> ViewController { wallpaper = .color(0x18222D) theme = .builtin(.nightAccent) } - return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperMode: .still, theme: theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperOptions: [], theme: theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) }).start() }, selectFontSize: { size in let _ = updatePresentationThemeSettingsInteractively(postbox: account.postbox, { current in - return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, chatWallpaperMode: current.chatWallpaperMode, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, chatWallpaperOptions: current.chatWallpaperOptions, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) }).start() }, openWallpaperSettings: { pushControllerImpl?(ThemeGridController(account: account)) }, openAccentColor: { color in presentControllerImpl?(ThemeAccentColorActionSheet(account: account, currentValue: color, applyValue: { color in let _ = updatePresentationThemeSettingsInteractively(postbox: account.postbox, { current in - return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, chatWallpaperMode: current.chatWallpaperMode, theme: current.theme, themeAccentColor: color, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, chatWallpaperOptions: current.chatWallpaperOptions, theme: current.theme, themeAccentColor: color, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) }).start() })) }, openAutoNightTheme: { pushControllerImpl?(themeAutoNightSettingsController(account: account)) }, disableAnimations: { disabled in let _ = updatePresentationThemeSettingsInteractively(postbox: account.postbox, { current in - return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, chatWallpaperMode: current.chatWallpaperMode, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: disabled) + return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, chatWallpaperOptions: current.chatWallpaperOptions, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: disabled) }).start() }) @@ -304,7 +304,7 @@ public func themeSettingsController(account: Account) -> ViewController { let theme: PresentationTheme let fontSize: PresentationFontSize let wallpaper: TelegramWallpaper - let wallpaperMode: PresentationWallpaperMode + let wallpaperMode: WallpaperPresentationOptions let strings: PresentationStrings let dateTimeFormat: PresentationDateTimeFormat let disableAnimations: Bool @@ -324,7 +324,7 @@ public func themeSettingsController(account: Account) -> ViewController { } } wallpaper = settings.chatWallpaper - wallpaperMode = settings.chatWallpaperMode + wallpaperMode = settings.chatWallpaperOptions fontSize = settings.fontSize if let localizationSettings = preferences.values[localizationSettingsKey] as? LocalizationSettings { diff --git a/TelegramUI/WallpaperColorPickerNode.swift b/TelegramUI/WallpaperColorPickerNode.swift index 41527d1e3f..5e6b0e339b 100644 --- a/TelegramUI/WallpaperColorPickerNode.swift +++ b/TelegramUI/WallpaperColorPickerNode.swift @@ -291,11 +291,19 @@ final class WallpaperColorPickerNode: ASDisplayNode { return } + let location = recognizer.location(in: recognizer.view) let transition = recognizer.translation(in: recognizer.view) - let newHue = max(0.0, min(1.0, self.colorHSV.0 + transition.x / size.width)) - let newSaturation = max(0.0, min(1.0, self.colorHSV.1 - transition.y / (size.height - 66.0))) - self.colorHSV.0 = newHue - self.colorHSV.1 = newSaturation + if recognizer.state == .began { + let newHue = max(0.0, min(1.0, location.x / size.width)) + let newSaturation = max(0.0, min(1.0, (1.0 - location.y / (size.height - 66.0)))) + self.colorHSV.0 = newHue + self.colorHSV.1 = newSaturation + } else { + let newHue = max(0.0, min(1.0, self.colorHSV.0 + transition.x / size.width)) + let newSaturation = max(0.0, min(1.0, self.colorHSV.1 - transition.y / (size.height - 66.0))) + self.colorHSV.0 = newHue + self.colorHSV.1 = newSaturation + } switch recognizer.state { case .began: diff --git a/TelegramUI/WallpaperGalleryController.swift b/TelegramUI/WallpaperGalleryController.swift new file mode 100644 index 0000000000..bc3e251296 --- /dev/null +++ b/TelegramUI/WallpaperGalleryController.swift @@ -0,0 +1,302 @@ +import Foundation +import Display +import QuickLook +import Postbox +import SwiftSignalKit +import AsyncDisplayKit +import TelegramCore +import Photos + +enum WallpaperListType { + case wallpapers(WallpaperPresentationOptions?) + case colors +} + +enum WallpaperListSource { + case list(wallpapers: [TelegramWallpaper], central: TelegramWallpaper, type: WallpaperListType) + case wallpaper(TelegramWallpaper) + case slug(String, TelegramMediaFile?) + case asset(PHAsset, UIImage?) + case contextResult(ChatContextResult) + case customColor(Int32?) +} + +enum WallpaperGalleryEntry: Equatable { + case wallpaper(TelegramWallpaper) + case asset(PHAsset, UIImage?) + case contextResult(ChatContextResult) + + public static func ==(lhs: WallpaperGalleryEntry, rhs: WallpaperGalleryEntry) -> Bool { + switch lhs { + case let .wallpaper(wallpaper): + if case .wallpaper(wallpaper) = rhs { + return true + } else { + return false + } + case let .asset(lhsAsset, _): + if case let .asset(rhsAsset, _) = rhs, lhsAsset.localIdentifier == rhsAsset.localIdentifier { + return true + } else { + return false + } + case let .contextResult(lhsResult): + if case let .contextResult(rhsResult) = rhs, lhsResult.id == rhsResult.id { + return true + } else { + return false + } + } + } +} + +class WallpaperGalleryController: ViewController { + private var galleryNode: GalleryControllerNode { + return self.displayNode as! GalleryControllerNode + } + + private let account: Account + private let source: WallpaperListSource + var apply: ((WallpaperEntry, WallpaperPresentationOptions, CGRect?) -> Void)? + + private let _ready = Promise() + override var ready: Promise { + return self._ready + } + private var didSetReady = false + + private let disposable = MetaDisposable() + + private var presentationData: PresentationData + private var presentationDataDisposable: Disposable? + + private var entries: [WallpaperGalleryEntry] = [] + private var centralEntryIndex: Int? + + private let centralItemControlsColor = Promise() + private let centralItemStatus = Promise() + private let centralItemAttributesDisposable = DisposableSet(); + + private var validLayout: (ContainerViewLayout, CGFloat)? + + private var toolbarNode: ThemeGalleryToolbarNode? + + init(account: Account, source: WallpaperListSource) { + self.account = account + self.source = source + self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData)) + + self.title = self.presentationData.strings.Wallpaper_Title + self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style + + switch source { + case let .list(wallpapers, central, type): + self.entries = wallpapers.map { .wallpaper($0) } + self.centralEntryIndex = wallpapers.index(of: central)! + + //if case let .wallpapers(wallpaperMode) = type, let mode = wallpaperMode { + // self.segmentedControl.selectedSegmentIndex = Int(clamping: mode.rawValue) + //} + case let .slug(slug, file): + if let file = file { + self.entries = [.wallpaper(.file(id: 0, accessHash: 0, isCreator: false, slug: slug, file: file, color: nil))] + self.centralEntryIndex = 0 + } + case let .wallpaper(wallpaper): + self.entries = [.wallpaper(wallpaper)] + self.centralEntryIndex = 0 + case let .asset(asset, thumbnailImage): + self.entries = [.asset(asset, thumbnailImage)] + self.centralEntryIndex = 0 + case let .contextResult(result): + self.entries = [.contextResult(result)] + self.centralEntryIndex = 0 + case let .customColor(color): + let initialColor = color ?? 0x000000 + self.entries = [.wallpaper(.color(initialColor))] + self.centralEntryIndex = 0 + } + +// let initialEntries: [ThemeGalleryEntry] = wallpapers.map { ThemeGalleryEntry.wallpaper($0) } +// let entriesSignal: Signal<[ThemeGalleryEntry], NoError> = .single(initialEntries) +// +// self.disposable.set((entriesSignal |> deliverOnMainQueue).start(next: { [weak self] entries in +// if let strongSelf = self { +// strongSelf.entries = entries +// strongSelf.centralEntryIndex = wallpapers.index(of: centralWallpaper)! +// if strongSelf.isViewLoaded { +// strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({ ThemeGalleryItem(account: account, entry: $0) }), centralItemIndex: strongSelf.centralEntryIndex, keepFirst: true) +// +// let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in +// strongSelf?.didSetReady = true +// } +// strongSelf._ready.set(ready |> map { true }) +// } +// } +// })) + + self.presentationDataDisposable = (account.telegramApplicationContext.presentationData + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + if let strongSelf = self { + let previousTheme = strongSelf.presentationData.theme + let previousStrings = strongSelf.presentationData.strings + + strongSelf.presentationData = presentationData + if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { + strongSelf.updateThemeAndStrings() + } + } + }) + + //self.centralItemAttributesDisposable.add(self.centralItemTitleView.get().start(next: { [weak self] titleView in + // self?.navigationItem.titleView = titleView + //})) + +// self.centralItemAttributesDisposable.add(self.centralItemFooterContentNode.get().start(next: { [weak self] footerContentNode in +// self?.galleryNode.updatePresentationState({ +// $0.withUpdatedFooterContentNode(footerContentNode) +// }, transition: .immediate) +// })) + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.disposable.dispose() + self.presentationDataDisposable?.dispose() + self.centralItemAttributesDisposable.dispose() + } + + private func updateThemeAndStrings() { + self.title = self.presentationData.strings.Wallpaper_Title + self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style + self.toolbarNode?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings) + } + + @objc func donePressed() { + self.dismiss(forceAway: false) + } + + private func dismiss(forceAway: Bool) { + let completion = { [weak self] in + self?.presentingViewController?.dismiss(animated: false, completion: nil) + } + + //self.galleryNode.modalAnimateOut(completion: completion) + } + + override func loadDisplayNode() { + let controllerInteraction = GalleryControllerInteraction(presentController: { [weak self] controller, arguments in + if let strongSelf = self { + strongSelf.present(controller, in: .window(.root), with: arguments, blockInteraction: true) + } + }, dismissController: { [weak self] in + self?.dismiss(forceAway: true) + }, replaceRootController: { controller, ready in + }) + self.displayNode = GalleryControllerNode(controllerInteraction: controllerInteraction, pageGap: 0.0) + self.displayNodeDidLoad() + + self.galleryNode.statusBar = self.statusBar + self.galleryNode.navigationBar = self.navigationBar + + self.galleryNode.transitionDataForCentralItem = { [weak self] in +// if let strongSelf = self { +// if let centralItemNode = strongSelf.galleryNode.pager.centralItemNode(), let presentationArguments = strongSelf.presentationArguments as? ThemePreviewControllerPresentationArguments { +// if let transitionArguments = presentationArguments.transitionArguments(strongSelf.entries[centralItemNode.index]) { +// return (transitionArguments.transitionNode, transitionArguments.addToTransitionSurface) +// } +// } +// } + return nil + } + self.galleryNode.dismiss = { [weak self] in + self?.presentingViewController?.dismiss(animated: false, completion: nil) + } + + self.galleryNode.pager.centralItemIndexUpdated = { [weak self] index in + if let strongSelf = self { + if let index = index { + if let node = strongSelf.galleryNode.pager.centralItemNode() { + //strongSelf.centralItemTitle.set(node.title()) + } + } + } + } + + self.galleryNode.backgroundNode.backgroundColor = nil + self.galleryNode.backgroundNode.isOpaque = false + self.galleryNode.isBackgroundExtendedOverNavigationBar = true + + let presentationData = self.account.telegramApplicationContext.currentPresentationData.with { $0 } + let toolbarNode = ThemeGalleryToolbarNode(theme: presentationData.theme, strings: presentationData.strings) + self.toolbarNode = toolbarNode + self.galleryNode.addSubnode(toolbarNode) + self.galleryNode.toolbarNode = toolbarNode + toolbarNode.cancel = { [weak self] in + //self?.dismiss(forceAway: true) + } + toolbarNode.done = { [weak self] in +// if let strongSelf = self { +// if let centralItemNode = strongSelf.galleryNode.pager.centralItemNode() { +// if !strongSelf.entries.isEmpty { +// let wallpaper: TelegramWallpaper +// switch strongSelf.entries[centralItemNode.index] { +// case let .wallpaper(value): +// wallpaper = value +// } +// let _ = (updatePresentationThemeSettingsInteractively(postbox: strongSelf.account.postbox, { current in +// return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperOptions: [], theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) +// }) |> deliverOnMainQueue).start(completed: { +// self?.dismiss(forceAway: true) +// }) +// } +// } +// } + } + + let ready = self.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak self] _ in + self?.didSetReady = true + } + self._ready.set(ready |> map { true }) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + self.galleryNode.modalAnimateIn() + } + + override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + self.galleryNode.frame = CGRect(origin: CGPoint(), size: layout.size) + self.galleryNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition) + + transition.updateFrame(node: self.toolbarNode!, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - 49.0 - layout.intrinsicInsets.bottom), size: CGSize(width: layout.size.width, height: 49.0 + layout.intrinsicInsets.bottom))) + self.toolbarNode!.updateLayout(size: CGSize(width: layout.size.width, height: 49.0), layout: layout, transition: transition) + + let replace = self.validLayout == nil + self.validLayout = (layout, 0.0) + + if replace { + self.galleryNode.pager.replaceItems(self.entries.map({ WallpaperGalleryItem(account: self.account, entry: $0) }), centralItemIndex: self.centralEntryIndex) + } + } +} + +private extension GalleryControllerNode { + func modalAnimateIn(completion: (() -> Void)? = nil) { + self.layer.animatePosition(from: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + } + + func modalAnimateOut(completion: (() -> Void)? = nil) { + self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), duration: 0.2, timingFunction: kCAMediaTimingFunctionEaseInEaseOut, removeOnCompletion: false, completion: { _ in + completion?() + }) + } +} diff --git a/TelegramUI/WallpaperGalleryItem.swift b/TelegramUI/WallpaperGalleryItem.swift new file mode 100644 index 0000000000..4a1e0afac3 --- /dev/null +++ b/TelegramUI/WallpaperGalleryItem.swift @@ -0,0 +1,369 @@ +import Foundation +import Display +import AsyncDisplayKit +import SwiftSignalKit +import Postbox +import TelegramCore +import LegacyComponents + +class WallpaperGalleryItem: GalleryItem { + let account: Account + let entry: WallpaperGalleryEntry + + init(account: Account, entry: WallpaperGalleryEntry) { + self.account = account + self.entry = entry + } + + func node() -> GalleryItemNode { + let node = WallpaperGalleryItemNode(account: self.account) + node.setEntry(self.entry) + return node + } + + func updateNode(node: GalleryItemNode) { + if let node = node as? WallpaperGalleryItemNode { + node.setEntry(self.entry) + } + } + + func thumbnailItem() -> (Int64, GalleryThumbnailItem)? { + return nil + } +} + +let progressDiameter: CGFloat = 50.0 + +final class WallpaperGalleryItemNode: GalleryItemNode { + private let account: Account + private var entry: WallpaperGalleryEntry? + private var contentSize: CGSize? + + let wrapperNode: ASDisplayNode + let imageNode: TransformImageNode + private let statusNode: RadialStatusNode + private let blurredNode: BlurredImageNode + let cropNode: WallpaperCropNode + + fileprivate let _ready = Promise() + private let fetchDisposable = MetaDisposable() + private let statusDisposable = MetaDisposable() + + let controlsColor = Promise(.white) + let status = Promise(.Local) + + init(account: Account) { + self.account = account + + self.wrapperNode = ASDisplayNode() + self.imageNode = TransformImageNode() + self.imageNode.contentAnimations = .subsequentUpdates + self.cropNode = WallpaperCropNode() + self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6)) + self.statusNode.frame = CGRect(x: 0.0, y: 0.0, width: progressDiameter, height: progressDiameter) + self.statusNode.isUserInteractionEnabled = false + + self.blurredNode = BlurredImageNode() + + super.init() + + self.clipsToBounds = true + self.backgroundColor = .black + + self.imageNode.imageUpdated = { [weak self] _ in + self?._ready.set(.single(Void())) + } + + self.imageNode.view.contentMode = .scaleAspectFill + self.imageNode.clipsToBounds = true + + self.addSubnode(self.wrapperNode) + self.addSubnode(self.statusNode) + } + + deinit { + self.fetchDisposable.dispose() + self.statusDisposable.dispose() + } + + var cropRect: CGRect? { + guard let entry = self.entry else { + return nil + } + switch entry { + case .asset, .contextResult: + return self.cropNode.cropRect + default: + return nil + } + } + + override func ready() -> Signal { + return self._ready.get() + } + + fileprivate func setEntry(_ entry: WallpaperGalleryEntry) { + if self.entry != entry { + self.entry = entry + + let signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError> + let fetchSignal: Signal + let statusSignal: Signal + let displaySize: CGSize + let contentSize: CGSize + + switch entry { + case let .wallpaper(wallpaper): + switch wallpaper { + case .builtin: + displaySize = CGSize(width: 640.0, height: 1136.0) + contentSize = displaySize + signal = settingsBuiltinWallpaperImage(account: account) + fetchSignal = .complete() + statusSignal = .single(.Local) + case let .color(color): + displaySize = CGSize(width: 1.0, height: 1.0) + contentSize = displaySize + signal = .never() + fetchSignal = .complete() + statusSignal = .single(.Local) + self.backgroundColor = UIColor(rgb: UInt32(bitPattern: color)) + case let .file(file): + let dimensions = file.file.dimensions ?? CGSize(width: 100.0, height: 100.0) + contentSize = dimensions + displaySize = dimensions.dividedByScreenScale().integralFloor + + var convertedRepresentations: [ImageRepresentationWithReference] = [] + for representation in file.file.previewRepresentations { + convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: .standalone(resource: representation.resource))) + } + convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource), reference: .standalone(resource: file.file.resource))) + signal = chatMessageImageFile(account: account, fileReference: .standalone(media: file.file), thumbnail: false) + fetchSignal = fetchedMediaResource(postbox: account.postbox, reference: convertedRepresentations[convertedRepresentations.count - 1].reference) + statusSignal = account.postbox.mediaBox.resourceStatus(file.file.resource) + case let .image(representations): + if let largestSize = largestImageRepresentation(representations) { + contentSize = largestSize.dimensions + displaySize = largestSize.dimensions.dividedByScreenScale().integralFloor + + let convertedRepresentations: [ImageRepresentationWithReference] = representations.map({ ImageRepresentationWithReference(representation: $0, reference: .wallpaper(resource: $0.resource)) }) + signal = chatAvatarGalleryPhoto(account: account, representations: convertedRepresentations) + + if let largestIndex = convertedRepresentations.index(where: { $0.representation == largestSize }) { + fetchSignal = fetchedMediaResource(postbox: account.postbox, reference: convertedRepresentations[largestIndex].reference) + } else { + fetchSignal = .complete() + } + statusSignal = account.postbox.mediaBox.resourceStatus(largestSize.resource) + } else { + displaySize = CGSize(width: 1.0, height: 1.0) + contentSize = displaySize + signal = .never() + fetchSignal = .complete() + statusSignal = .single(.Local) + } + } + self.cropNode.removeFromSupernode() + case let .asset(asset, _): + let dimensions = CGSize(width: asset.pixelWidth, height: asset.pixelHeight) + contentSize = dimensions + displaySize = dimensions.dividedByScreenScale().integralFloor + signal = photoWallpaper(postbox: account.postbox, photoLibraryResource: PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: arc4random64())) + fetchSignal = .complete() + statusSignal = .single(.Local) + self.wrapperNode.addSubnode(self.cropNode) + case let .contextResult(result): + var imageDimensions: CGSize? + var imageResource: TelegramMediaResource? + var thumbnailDimensions: CGSize? + var thumbnailResource: TelegramMediaResource? + switch result { + case let .externalReference(_, _, _, _, _, _, content, thumbnail, _): + if let content = content { + imageResource = content.resource + } + if let thumbnail = thumbnail { + thumbnailResource = thumbnail.resource + thumbnailDimensions = thumbnail.dimensions + } + if let dimensions = content?.dimensions { + imageDimensions = dimensions + } + case let .internalReference(_, _, _, _, _, image, _, _): + if let image = image { + if let imageRepresentation = imageRepresentationLargerThan(image.representations, size: CGSize(width: 1000.0, height: 800.0)) { + imageDimensions = imageRepresentation.dimensions + imageResource = imageRepresentation.resource + } + if let thumbnailRepresentation = imageRepresentationLargerThan(image.representations, size: CGSize(width: 200.0, height: 100.0)) { + thumbnailDimensions = thumbnailRepresentation.dimensions + thumbnailResource = thumbnailRepresentation.resource + } + } + } + + if let imageResource = imageResource, let imageDimensions = imageDimensions { + contentSize = imageDimensions + displaySize = imageDimensions.dividedByScreenScale().integralFloor + + var representations: [TelegramMediaImageRepresentation] = [] + if let thumbnailResource = thumbnailResource, let thumbnailDimensions = thumbnailDimensions { + representations.append(TelegramMediaImageRepresentation(dimensions: thumbnailDimensions, resource: thumbnailResource)) + } + representations.append(TelegramMediaImageRepresentation(dimensions: imageDimensions, resource: imageResource)) + let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil) + + signal = chatMessagePhoto(postbox: account.postbox, photoReference: .standalone(media: tmpImage)) + fetchSignal = fetchedMediaResource(postbox: account.postbox, reference: .media(media: .standalone(media: tmpImage), resource: imageResource)) + statusSignal = account.postbox.mediaBox.resourceStatus(imageResource) + } else { + displaySize = CGSize(width: 1.0, height: 1.0) + contentSize = displaySize + signal = .never() + fetchSignal = .complete() + statusSignal = .single(.Local) + } + self.wrapperNode.addSubnode(self.cropNode) + } + self.contentSize = contentSize + + if self.cropNode.supernode == nil { + self.imageNode.contentMode = .scaleAspectFill + self.wrapperNode.addSubnode(self.imageNode) + } else { + self.imageNode.contentMode = .scaleToFill + } + + let imagePromise = Promise() + self.imageNode.setSignal(signal, dispatchOnDisplayLink: false) + self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: displaySize, boundingSize: displaySize, intrinsicInsets: UIEdgeInsets()))() + self.imageNode.imageUpdated = { [weak self] image in + if let strongSelf = self { + var image = image + if let scaledImage = image { + if scaledImage.size.width > 2048.0 || scaledImage.size.height > 2048.0 { + image = TGScaleImageToPixelSize(image, scaledImage.size.fitted(CGSize(width: 2048.0, height: 2048.0))) + } + } + strongSelf.blurredNode.image = image + imagePromise.set(.single(image)) + } + } + self.fetchDisposable.set(fetchSignal.start()) + + let statusForegroundColor = UIColor.white + self.statusDisposable.set((statusSignal + |> deliverOnMainQueue).start(next: { [weak self] status in + if let strongSelf = self { + let state: RadialStatusNodeState + switch status { + case let .Fetching(_, progress): + let adjustedProgress = max(progress, 0.027) + state = .progress(color: statusForegroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: false) + case .Local: + state = .none + case .Remote: + state = .progress(color: statusForegroundColor, lineWidth: nil, value: 0.027, cancelEnabled: false) + } + strongSelf.statusNode.transitionToState(state, completion: {}) + } + })) + + let controlsColorSignal: Signal + if case let .wallpaper(wallpaper) = entry { + controlsColorSignal = chatBackgroundContrastColor(wallpaper: wallpaper, postbox: account.postbox) + } else { + controlsColorSignal = backgroundContrastColor(for: imagePromise.get()) + } + self.controlsColor.set(.single(.white) |> then(controlsColorSignal)) + self.status.set(statusSignal) + } + } + + func setParallaxEnabled(_ enabled: Bool) { + if enabled { + let amount = 24.0 + + let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis) + horizontal.minimumRelativeValue = -amount + horizontal.maximumRelativeValue = amount + + let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis) + vertical.minimumRelativeValue = -amount + vertical.maximumRelativeValue = amount + + let group = UIMotionEffectGroup() + group.motionEffects = [horizontal, vertical] + self.wrapperNode.view.addMotionEffect(group) + } else { + for effect in self.imageNode.view.motionEffects { + self.wrapperNode.view.removeMotionEffect(effect) + } + } + } + + func setBlurEnabled(_ enabled: Bool, animated: Bool) { + let blurRadius: CGFloat = 45.0 + + if enabled { + if self.blurredNode.supernode == nil { + if self.cropNode.supernode != nil { + self.blurredNode.frame = self.imageNode.bounds + self.imageNode.addSubnode(self.blurredNode) + } else { + self.blurredNode.frame = self.imageNode.frame + self.addSubnode(self.blurredNode) + } + } + + if animated { + self.blurredNode.blurView.blurRadius = 0.0 + UIView.animate(withDuration: 0.3, delay: 0.0, options: UIViewAnimationOptions(rawValue: 7 << 16), animations: { + self.blurredNode.blurView.blurRadius = blurRadius + }, completion: nil) + } else { + self.blurredNode.blurView.blurRadius = blurRadius + } + } else { + if self.blurredNode.supernode != nil { + if animated { + UIView.animate(withDuration: 0.3, delay: 0.0, options: UIViewAnimationOptions(rawValue: 7 << 16), animations: { + self.blurredNode.blurView.blurRadius = 0.0 + }, completion: { finished in + if finished { + self.blurredNode.removeFromSupernode() + } + }) + } else { + self.blurredNode.removeFromSupernode() + } + } + } + } + + override func visibilityUpdated(isVisible: Bool) { + super.visibilityUpdated(isVisible: isVisible) + } + + override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) + + self.wrapperNode.frame = CGRect(origin: CGPoint(), size: layout.size) + if self.cropNode.supernode == nil { + self.imageNode.frame = self.wrapperNode.bounds + self.blurredNode.frame = self.imageNode.frame + } else { + self.cropNode.frame = self.wrapperNode.bounds + self.cropNode.containerLayoutUpdated(layout, transition: transition) + + if self.cropNode.supernode != nil, let contentSize = self.contentSize, self.cropNode.zoomableContent == nil { + let fittedSize = TGScaleToFit(self.cropNode.bounds.size, contentSize) + self.cropNode.zoomableContent = (contentSize, self.imageNode) + self.cropNode.zoom(to: CGRect(x: (contentSize.width - fittedSize.width) / 2.0, y: (contentSize.height - fittedSize.height) / 2.0, width: fittedSize.width, height: fittedSize.height)) + } + self.blurredNode.frame = self.imageNode.bounds + } + + self.statusNode.frame = CGRect(x: layout.safeInsets.left + floorToScreenPixels((layout.size.width - layout.safeInsets.left - layout.safeInsets.right - progressDiameter) / 2.0), y: floorToScreenPixels((layout.size.height - progressDiameter) / 2.0), width: progressDiameter, height: progressDiameter) + } +} diff --git a/TelegramUI/WallpaperListPreviewController.swift b/TelegramUI/WallpaperListPreviewController.swift index fd70bbb734..1956c1dcf3 100644 --- a/TelegramUI/WallpaperListPreviewController.swift +++ b/TelegramUI/WallpaperListPreviewController.swift @@ -6,20 +6,6 @@ import TelegramCore import SwiftSignalKit import Photos -enum WallpaperListType { - case wallpapers(PresentationWallpaperMode?) - case colors -} - -enum WallpaperListPreviewSource { - case list(wallpapers: [TelegramWallpaper], central: TelegramWallpaper, type: WallpaperListType) - case wallpaper(TelegramWallpaper) - case slug(String, TelegramMediaFile?) - case asset(PHAsset, UIImage?) - case contextResult(ChatContextResult) - case customColor(Int32?) -} - final class WallpaperListPreviewController: ViewController { private var controllerNode: WallpaperListPreviewControllerNode { return self.displayNode as! WallpaperListPreviewControllerNode @@ -31,7 +17,7 @@ final class WallpaperListPreviewController: ViewController { } private let account: Account - private let source: WallpaperListPreviewSource + private let source: WallpaperListSource private var presentationData: PresentationData private var presentationDataDisposable: Disposable? @@ -41,9 +27,9 @@ final class WallpaperListPreviewController: ViewController { private var didPlayPresentationAnimation = false - var apply: ((WallpaperEntry, PresentationWallpaperMode, CGRect?) -> Void)? + var apply: ((WallpaperEntry, WallpaperPresentationOptions, CGRect?) -> Void)? - init(account: Account, source: WallpaperListPreviewSource) { + init(account: Account, source: WallpaperListSource) { self.account = account self.source = source @@ -121,7 +107,7 @@ final class WallpaperListPreviewController: ViewController { case let .wallpaper(wallpaper): let completion: () -> Void = { let _ = (updatePresentationThemeSettingsInteractively(postbox: strongSelf.account.postbox, { current in - return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperMode: mode, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperOptions: mode, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations) }) |> deliverOnMainQueue).start(completed: { self?.dismiss() @@ -133,7 +119,7 @@ final class WallpaperListPreviewController: ViewController { let _ = installWallpaper(account: strongSelf.account, wallpaper: wallpaper).start() } - if case .blurred = mode { + if mode.contains(.blur) { var resource: MediaResource? switch wallpaper { case let .file(file): diff --git a/TelegramUI/WallpaperListPreviewControllerNode.swift b/TelegramUI/WallpaperListPreviewControllerNode.swift index 8b808240d3..f929eaf791 100644 --- a/TelegramUI/WallpaperListPreviewControllerNode.swift +++ b/TelegramUI/WallpaperListPreviewControllerNode.swift @@ -195,7 +195,7 @@ private final class WallpaperBackgroundNode: ASDisplayNode { self.imageNode.contentMode = .scaleAspectFill self.wrapperNode.addSubnode(self.imageNode) } - self.wrapperNode.addSubnode(self.statusNode) + self.addSubnode(self.statusNode) let imagePromise = Promise() self.imageNode.setSignal(signal, dispatchOnDisplayLink: false) @@ -258,7 +258,7 @@ private final class WallpaperBackgroundNode: ASDisplayNode { func setParallaxEnabled(_ enabled: Bool) { if enabled { - let amount = 16.0 + let amount = 24.0 let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis) horizontal.minimumRelativeValue = -amount @@ -342,9 +342,9 @@ private final class WallpaperBackgroundNode: ASDisplayNode { final class WallpaperListPreviewControllerNode: ViewControllerTracingNode { private let account: Account private var presentationData: PresentationData - private let source: WallpaperListPreviewSource + private let source: WallpaperListSource private let dismiss: () -> Void - private let apply: (WallpaperEntry, PresentationWallpaperMode, CGRect?) -> Void + private let apply: (WallpaperEntry, WallpaperPresentationOptions, CGRect?) -> Void private var validLayout: (ContainerViewLayout, CGFloat)? @@ -380,7 +380,7 @@ final class WallpaperListPreviewControllerNode: ViewControllerTracingNode { } private var visibleBackgroundNodesOffset: CGFloat = 0.0 - init(account: Account, presentationData: PresentationData, source: WallpaperListPreviewSource, dismiss: @escaping () -> Void, apply: @escaping (WallpaperEntry, PresentationWallpaperMode, CGRect?) -> Void) { + init(account: Account, presentationData: PresentationData, source: WallpaperListSource, dismiss: @escaping () -> Void, apply: @escaping (WallpaperEntry, WallpaperPresentationOptions, CGRect?) -> Void) { self.account = account self.presentationData = presentationData self.source = source @@ -843,15 +843,13 @@ final class WallpaperListPreviewControllerNode: ViewControllerTracingNode { } @objc private func indexChanged() { - guard let mode = PresentationWallpaperMode(rawValue: Int32(self.segmentedControl.selectedSegmentIndex)) else { - return - } - + let index = self.segmentedControl.selectedSegmentIndex + if let node = self.centralNode() { - if mode == .perspective { + if index == 1 { node.setParallaxEnabled(true) node.setBlurEnabled(false, animated: true) - } else if mode == .blurred { + } else if index == 2 { node.setParallaxEnabled(false) node.setBlurEnabled(true, animated: true) } else { @@ -867,16 +865,16 @@ final class WallpaperListPreviewControllerNode: ViewControllerTracingNode { @objc private func applyPressed() { if let wallpaper = self.centralWallpaper { - let mode: PresentationWallpaperMode + var options: WallpaperPresentationOptions = [] switch self.segmentedControl.selectedSegmentIndex { case 1: - mode = .perspective + options.insert(.motion) case 2: - mode = .blurred + options.insert(.blur) default: - mode = .still + break } - self.apply(wallpaper, mode, self.centralNode()?.cropRect) + self.apply(wallpaper, options, self.centralNode()?.cropRect) } } }