mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Wallpaper experiment
This commit is contained in:
parent
0679e4dd7e
commit
cdc04c733c
@ -80,7 +80,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case experimentalCompatibility(Bool)
|
||||
case enableDebugDataDisplay(Bool)
|
||||
case acceleratedStickers(Bool)
|
||||
case mockICE(Bool)
|
||||
case experimentalBackground(Bool)
|
||||
case playerEmbedding(Bool)
|
||||
case playlistPlayback(Bool)
|
||||
case voiceConference
|
||||
@ -102,7 +102,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.logging.rawValue
|
||||
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .mockICE:
|
||||
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .preferredVideoCodec:
|
||||
return DebugControllerSection.videoExperiments.rawValue
|
||||
@ -171,7 +171,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 27
|
||||
case .acceleratedStickers:
|
||||
return 29
|
||||
case .mockICE:
|
||||
case .experimentalBackground:
|
||||
return 30
|
||||
case .playerEmbedding:
|
||||
return 31
|
||||
@ -804,12 +804,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .mockICE(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "mockICE", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
case let .experimentalBackground(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Background Experiment", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||
settings.mockICE = value
|
||||
settings.experimentalBackground = value
|
||||
return PreferencesEntry(settings)
|
||||
})
|
||||
}).start()
|
||||
@ -926,7 +926,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
|
||||
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
||||
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
||||
entries.append(.mockICE(experimentalSettings.mockICE))
|
||||
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
|
||||
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
|
||||
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
|
||||
}
|
||||
|
@ -518,7 +518,22 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
|
||||
}
|
||||
|
||||
public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, skipByInterval skipInterval: CMTime, completion completionHandler: @escaping () -> Void) {
|
||||
completionHandler()
|
||||
let node = self.node
|
||||
let _ = (self.node.status
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak node] status in
|
||||
if let node = node, let timestamp = status?.timestamp, let duration = status?.duration {
|
||||
let nextTimestamp = timestamp + skipInterval.seconds
|
||||
if nextTimestamp > duration {
|
||||
node.seek(0.0)
|
||||
node.pause()
|
||||
} else {
|
||||
node.seek(min(duration, nextTimestamp))
|
||||
}
|
||||
}
|
||||
|
||||
completionHandler()
|
||||
})
|
||||
}
|
||||
|
||||
public func pictureInPictureControllerShouldProhibitBackgroundAudioPlayback(_ pictureInPictureController: AVPictureInPictureController) -> Bool {
|
||||
@ -539,7 +554,9 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
|
||||
|
||||
private var hiddenMediaManagerIndex: Int?
|
||||
|
||||
init(overlayController: OverlayMediaController, mediaManager: MediaManager, accountId: AccountRecordId, hiddenMedia: (MessageId, Media)?, videoNode: UniversalVideoNode, willBegin: @escaping (PictureInPictureContentImpl) -> Void, didEnd: @escaping (PictureInPictureContentImpl) -> Void, expand: @escaping (@escaping () -> Void) -> Void) {
|
||||
private var messageRemovedDisposable: Disposable?
|
||||
|
||||
init(context: AccountContext, overlayController: OverlayMediaController, mediaManager: MediaManager, accountId: AccountRecordId, hiddenMedia: (MessageId, Media)?, videoNode: UniversalVideoNode, canSkip: Bool, willBegin: @escaping (PictureInPictureContentImpl) -> Void, didEnd: @escaping (PictureInPictureContentImpl) -> Void, expand: @escaping (@escaping () -> Void) -> Void) {
|
||||
self.overlayController = overlayController
|
||||
self.mediaManager = mediaManager
|
||||
self.node = videoNode
|
||||
@ -559,7 +576,7 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
|
||||
contentDelegate.pictureInPictureController = pictureInPictureController
|
||||
|
||||
pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = false
|
||||
pictureInPictureController.requiresLinearPlayback = true
|
||||
pictureInPictureController.requiresLinearPlayback = !canSkip
|
||||
pictureInPictureController.delegate = self
|
||||
self.pictureInPictureController = pictureInPictureController
|
||||
let timer = SwiftSignalKit.Timer(timeout: 0.005, repeat: true, completion: { [weak self] in
|
||||
@ -586,9 +603,31 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if let (messageId, _) = hiddenMedia {
|
||||
self.messageRemovedDisposable = (context.account.postbox.combinedView(keys: [PostboxViewKey.messages([messageId])])
|
||||
|> map { views -> Bool in
|
||||
if let view = views.views[PostboxViewKey.messages([messageId])] as? MessagesView {
|
||||
if view.messages[messageId] == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
overlayController.removePictureInPictureContent(content: strongSelf)
|
||||
strongSelf.node.canAttachContent = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.messageRemovedDisposable?.dispose()
|
||||
self.pictureInPictureTimer?.invalidate()
|
||||
self.node.setCanPlaybackWithoutHierarchy(false)
|
||||
|
||||
@ -698,6 +737,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
private var dismissOnOrientationChange = false
|
||||
private var keepSoundOnDismiss = false
|
||||
private var hasPictureInPicture = false
|
||||
|
||||
private var pictureInPictureButton: UIBarButtonItem?
|
||||
|
||||
private var requiresDownload = false
|
||||
|
||||
@ -1328,6 +1369,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
}
|
||||
if forceEnablePiP || (!isAnimated && !disablePlayerControls && !disablePictureInPicture) {
|
||||
let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed))
|
||||
self.pictureInPictureButton = rightBarButtonItem
|
||||
barButtonItems.append(rightBarButtonItem)
|
||||
self.hasPictureInPicture = true
|
||||
} else {
|
||||
@ -1427,6 +1469,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
pictureInPictureNode.removeFromSupernode()
|
||||
self.videoNode?.backgroundColor = .black
|
||||
}
|
||||
|
||||
self.pictureInPictureButton?.isEnabled = self.pictureInPictureNode == nil
|
||||
}
|
||||
|
||||
private func shouldAutoplayOnCentrality() -> Bool {
|
||||
@ -2069,7 +2113,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
case let .message(message):
|
||||
for media in message.media {
|
||||
if let media = media as? TelegramMediaFile, media.isVideo {
|
||||
isNativePictureInPictureSupported = true
|
||||
if message.id.namespace == Namespaces.Message.Cloud {
|
||||
isNativePictureInPictureSupported = true
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
@ -2106,7 +2152,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
break
|
||||
}
|
||||
|
||||
let content = PictureInPictureContentImpl(overlayController: overlayController, mediaManager: self.context.sharedContext.mediaManager, accountId: self.context.account.id, hiddenMedia: hiddenMedia, videoNode: overlayVideoNode, willBegin: { [weak self] content in
|
||||
let content = PictureInPictureContentImpl(context: self.context, overlayController: overlayController, mediaManager: self.context.sharedContext.mediaManager, accountId: self.context.account.id, hiddenMedia: hiddenMedia, videoNode: overlayVideoNode, canSkip: true, willBegin: { [weak self] content in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
@ -275,7 +275,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
deinit {
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) {
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) {
|
||||
let sizeUpdated = self.validLayout != size
|
||||
self.validLayout = size
|
||||
|
||||
@ -372,6 +372,10 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25
|
||||
}
|
||||
|
||||
animation.completion = { _ in
|
||||
completion()
|
||||
}
|
||||
|
||||
self.contentView.layer.removeAnimation(forKey: "contents")
|
||||
self.contentView.layer.add(animation, forKey: "contents")
|
||||
|
||||
@ -405,7 +409,11 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
for cloneNode in self.cloneNodes {
|
||||
cloneNode.value?.image = dimmedImage
|
||||
}
|
||||
|
||||
completion()
|
||||
}
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
} else if sizeUpdated {
|
||||
let image = generateGradient(size: imageSize, colors: self.colors, positions: positions)
|
||||
@ -419,6 +427,10 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
self.validPhase = self.phase
|
||||
|
||||
completion()
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
|
||||
transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
@ -440,13 +452,14 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
self.colors = colors
|
||||
self.invalidated = true
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: .immediate)
|
||||
self.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) {
|
||||
public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) {
|
||||
guard case let .animated(duration, _) = transition, duration > 0.001 else {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
@ -463,7 +476,9 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
GradientBackgroundNode.sharedPhase = self.phase
|
||||
}
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards)
|
||||
self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards, completion: completion)
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
if let strongSelf = self {
|
||||
strongSelf.inputFieldNode.append(character)
|
||||
if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring))
|
||||
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: false, completion: {})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,7 +102,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
if let strongSelf = self {
|
||||
let _ = strongSelf.inputFieldNode.delete()
|
||||
if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), backwards: true)
|
||||
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,7 +167,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
self.hapticFeedback.tap()
|
||||
let result = self.inputFieldNode.delete()
|
||||
if result, let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), backwards: true)
|
||||
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {})
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,7 +337,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true)
|
||||
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true, backwards: false, completion: {})
|
||||
}
|
||||
}
|
||||
self.titleNode.setAttributedText(NSAttributedString(string: self.strings.EnterPasscode_EnterPasscode, font: titleFont, textColor: .white), animation: .none)
|
||||
@ -355,7 +355,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring))
|
||||
gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring), extendAnimation: false, backwards: false, completion: {})
|
||||
self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
if !iconFrame.isEmpty {
|
||||
@ -385,7 +385,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
|
||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring))
|
||||
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: false, backwards: false, completion: {})
|
||||
}
|
||||
self.inputFieldNode.animateIn()
|
||||
self.keyboardNode.animateIn()
|
||||
@ -425,7 +425,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
self.hapticFeedback.error()
|
||||
|
||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true)
|
||||
gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true, completion: {})
|
||||
}
|
||||
}
|
||||
|
||||
@ -440,7 +440,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
if let backgroundCustomNode = self.backgroundCustomNode {
|
||||
transition.updateFrame(node: backgroundCustomNode, frame: bounds)
|
||||
if let gradientBackgroundNode = backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientBackgroundNode.updateLayout(size: bounds.size, transition: transition)
|
||||
gradientBackgroundNode.updateLayout(size: bounds.size, transition: transition, extendAnimation: false, backwards: false, completion: {})
|
||||
}
|
||||
}
|
||||
transition.updateFrame(view: self.effectView, frame: bounds)
|
||||
|
@ -62,7 +62,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
|
||||
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context)
|
||||
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||
self.chatBackgroundNode.displaysAsynchronously = false
|
||||
|
||||
self.messagesContainerNode = ASDisplayNode()
|
||||
|
@ -132,7 +132,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
return { item, params, neighbors in
|
||||
if currentBackgroundNode == nil {
|
||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context)
|
||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||
}
|
||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||
|
@ -74,7 +74,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
self.pageControlNode = PageControlNode(dotSpacing: 7.0, dotColor: .white, inactiveDotColor: UIColor.white.withAlphaComponent(0.4))
|
||||
|
||||
self.chatListBackgroundNode = ASDisplayNode()
|
||||
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context)
|
||||
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||
self.chatBackgroundNode.displaysAsynchronously = false
|
||||
|
||||
self.messagesContainerNode = ASDisplayNode()
|
||||
|
@ -174,7 +174,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
|
||||
|
||||
if let gradientNode = self.gradientNode {
|
||||
gradientNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
gradientNode.updateLayout(size: size, transition: .immediate)
|
||||
gradientNode.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {})
|
||||
}
|
||||
|
||||
let progressDiameter: CGFloat = 50.0
|
||||
|
@ -285,7 +285,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
self.backgroundContainerNode = ASDisplayNode()
|
||||
self.backgroundContainerNode.clipsToBounds = true
|
||||
self.backgroundWrapperNode = ASDisplayNode()
|
||||
self.backgroundNode = createWallpaperBackgroundNode(context: context)
|
||||
self.backgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||
|
||||
self.messagesContainerNode = ASDisplayNode()
|
||||
self.messagesContainerNode.clipsToBounds = true
|
||||
|
@ -107,7 +107,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.messagesContainerNode.clipsToBounds = true
|
||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
||||
|
||||
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context)
|
||||
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||
self.instantChatBackgroundNode.displaysAsynchronously = false
|
||||
|
||||
self.ready.set(.single(true))
|
||||
@ -121,7 +121,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.blurredNode = BlurredImageNode()
|
||||
self.blurredNode.blurView.contentMode = .scaleAspectFill
|
||||
|
||||
self.wallpaperNode = createWallpaperBackgroundNode(context: context)
|
||||
self.wallpaperNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||
|
||||
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings, doneButtonType: .set)
|
||||
|
||||
|
@ -138,7 +138,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
return { item, params, neighbors in
|
||||
if currentBackgroundNode == nil {
|
||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context)
|
||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||
}
|
||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)
|
||||
|
@ -136,7 +136,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
self.wrapperNode = ASDisplayNode()
|
||||
self.imageNode = TransformImageNode()
|
||||
self.imageNode.contentAnimations = .subsequentUpdates
|
||||
self.nativeNode = createWallpaperBackgroundNode(context: context)
|
||||
self.nativeNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||
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)
|
||||
|
@ -964,7 +964,7 @@ final class MessageStoryRenderer {
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
|
||||
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context)
|
||||
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||
self.instantChatBackgroundNode.displaysAsynchronously = false
|
||||
|
||||
self.messagesContainerNode = ASDisplayNode()
|
||||
|
@ -7,6 +7,6 @@
|
||||
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data);
|
||||
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size);
|
||||
|
||||
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor);
|
||||
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor, bool opaque);
|
||||
|
||||
#endif /* Lottie_h */
|
||||
|
@ -83,7 +83,7 @@ CGSize aspectFillSize(CGSize size, CGSize bounds) {
|
||||
|
||||
@end
|
||||
|
||||
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, UIColor *foregroundColor) {
|
||||
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, UIColor *foregroundColor, bool opaque) {
|
||||
NSDate *startTime = [NSDate date];
|
||||
|
||||
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
|
||||
@ -115,8 +115,8 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *b
|
||||
printf("parseTime = %f\n", deltaTime);
|
||||
|
||||
startTime = [NSDate date];
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(size, true, 1.0);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(size, opaque, 1.0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
|
||||
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));
|
||||
|
@ -2001,7 +2001,7 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat
|
||||
} else {
|
||||
pollPts = 1
|
||||
}
|
||||
return (network.request(Api.functions.updates.getChannelDifference(flags: 0, channel: inputChannel, filter: .channelMessagesFilterEmpty, pts: pollPts, limit: limit))
|
||||
return (network.request(Api.functions.updates.getChannelDifference(flags: 0, channel: inputChannel, filter: .channelMessagesFilterEmpty, pts: max(pollPts, 1), limit: limit))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { error -> Signal<Api.updates.ChannelDifference?, MTRpcError> in
|
||||
switch error.errorDescription {
|
||||
|
@ -508,7 +508,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
default:
|
||||
break
|
||||
}
|
||||
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, useSharedAnimationPhase: useSharedAnimationPhase)
|
||||
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: useSharedAnimationPhase, useExperimentalImplementation: self.context.sharedContext.immediateExperimentalUISettings.experimentalBackground)
|
||||
self.wallpaperReady.set(self.chatBackgroundNode.isReady)
|
||||
|
||||
var locationBroadcastPanelSource: LocationBroadcastPanelSource
|
||||
|
@ -611,7 +611,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
return
|
||||
}
|
||||
var adPeerId: PeerId?
|
||||
adPeerId = nil
|
||||
adPeerId = messages.first?.author?.id
|
||||
|
||||
if strongSelf.preloadAdPeerId != adPeerId {
|
||||
strongSelf.preloadAdPeerId = adPeerId
|
||||
|
@ -99,7 +99,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
|
||||
self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 }
|
||||
|
||||
self.backgroundNode = createWallpaperBackgroundNode(context: context)
|
||||
self.backgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true)
|
||||
self.backgroundNode.isUserInteractionEnabled = false
|
||||
|
||||
self.panelBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.chat.inputPanel.panelBackgroundColor)
|
||||
|
@ -18,7 +18,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
public var experimentalCompatibility: Bool
|
||||
public var enableDebugDataDisplay: Bool
|
||||
public var acceleratedStickers: Bool
|
||||
public var mockICE: Bool
|
||||
public var experimentalBackground: Bool
|
||||
|
||||
public static var defaultSettings: ExperimentalUISettings {
|
||||
return ExperimentalUISettings(
|
||||
@ -36,7 +36,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
experimentalCompatibility: false,
|
||||
enableDebugDataDisplay: false,
|
||||
acceleratedStickers: false,
|
||||
mockICE: false
|
||||
experimentalBackground: false
|
||||
)
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
experimentalCompatibility: Bool,
|
||||
enableDebugDataDisplay: Bool,
|
||||
acceleratedStickers: Bool,
|
||||
mockICE: Bool
|
||||
experimentalBackground: Bool
|
||||
) {
|
||||
self.keepChatNavigationStack = keepChatNavigationStack
|
||||
self.skipReadHistory = skipReadHistory
|
||||
@ -71,7 +71,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.experimentalCompatibility = experimentalCompatibility
|
||||
self.enableDebugDataDisplay = enableDebugDataDisplay
|
||||
self.acceleratedStickers = acceleratedStickers
|
||||
self.mockICE = mockICE
|
||||
self.experimentalBackground = experimentalBackground
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@ -91,7 +91,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 0) != 0
|
||||
self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 0) != 0
|
||||
self.acceleratedStickers = (try container.decodeIfPresent(Int32.self, forKey: "acceleratedStickers") ?? 0) != 0
|
||||
self.mockICE = (try container.decodeIfPresent(Int32.self, forKey: "mockICE") ?? 0) != 0
|
||||
self.experimentalBackground = (try container.decodeIfPresent(Int32.self, forKey: "experimentalBackground") ?? 0) != 0
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -111,7 +111,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
try container.encode((self.experimentalCompatibility ? 1 : 0) as Int32, forKey: "experimentalCompatibility")
|
||||
try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay")
|
||||
try container.encode((self.acceleratedStickers ? 1 : 0) as Int32, forKey: "acceleratedStickers")
|
||||
try container.encode((self.mockICE ? 1 : 0) as Int32, forKey: "mockICE")
|
||||
try container.encode((self.experimentalBackground ? 1 : 0) as Int32, forKey: "experimentalBackground")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,9 @@ swift_library(
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/WallpaperResources:WallpaperResources",
|
||||
"//submodules/FastBlur:FastBlur",
|
||||
"//submodules/Svg:Svg",
|
||||
"//submodules/GZip:GZip",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -9,6 +9,9 @@ import AccountContext
|
||||
import SwiftSignalKit
|
||||
import WallpaperResources
|
||||
import FastBlur
|
||||
import Svg
|
||||
import GZip
|
||||
import AppBundle
|
||||
|
||||
private let motionAmount: CGFloat = 32.0
|
||||
|
||||
@ -781,12 +784,12 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
|
||||
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||
transition.updateFrame(node: gradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
gradientBackgroundNode.updateLayout(size: size, transition: transition)
|
||||
gradientBackgroundNode.updateLayout(size: size, transition: transition, extendAnimation: false, backwards: false, completion: {})
|
||||
}
|
||||
|
||||
if let outgoingBubbleGradientBackgroundNode = self.outgoingBubbleGradientBackgroundNode {
|
||||
transition.updateFrame(node: outgoingBubbleGradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: transition)
|
||||
outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: transition, extendAnimation: false, backwards: false, completion: {})
|
||||
}
|
||||
|
||||
self.loadPatternForSizeIfNeeded(size: size, transition: transition)
|
||||
@ -797,21 +800,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
}
|
||||
|
||||
func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) {
|
||||
self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation)
|
||||
self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation)
|
||||
}
|
||||
|
||||
private func updateBakedBackground() {
|
||||
guard let size = self.validLayout else {
|
||||
return
|
||||
}
|
||||
let context = DrawingContext(size: size, scale: UIScreenScale, opaque: true)
|
||||
|
||||
context.withContext { context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
|
||||
self.bakedBackgroundView.image = context.generateImage()
|
||||
self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {})
|
||||
self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {})
|
||||
}
|
||||
|
||||
func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) {
|
||||
@ -824,7 +814,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode(adjustSaturation: false)
|
||||
if let size = self.validLayout {
|
||||
outgoingBubbleGradientBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate)
|
||||
outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {})
|
||||
}
|
||||
self.outgoingBubbleGradientBackgroundNode = outgoingBubbleGradientBackgroundNode
|
||||
}
|
||||
@ -898,32 +888,473 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
}
|
||||
}
|
||||
|
||||
private protocol WallpaperComponentView: AnyObject {
|
||||
var view: UIView { get }
|
||||
|
||||
func update(size: CGSize, transition: ContainedViewLayoutTransition)
|
||||
}
|
||||
|
||||
final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroundNode {
|
||||
final class SharedStorage {
|
||||
}
|
||||
|
||||
private class WallpaperComponentView: UIView {
|
||||
let updated: () -> Void
|
||||
final class BubbleBackgroundNodeImpl: ASDisplayNode, WallpaperBubbleBackgroundNode {
|
||||
private let bubbleType: WallpaperBubbleType
|
||||
private let contentNode: ASImageNode
|
||||
|
||||
init(updated: @escaping () -> Void) {
|
||||
self.updated = updated
|
||||
private var cleanWallpaperNode: ASDisplayNode?
|
||||
private var gradientWallpaperNode: GradientBackgroundNode.CloneNode?
|
||||
private weak var backgroundNode: WallpaperBackgroundNodeMergedImpl?
|
||||
private var index: SparseBag<BubbleBackgroundNodeImpl>.Index?
|
||||
|
||||
super.init(frame: CGRect())
|
||||
private var currentLayout: (rect: CGRect, containerSize: CGSize)?
|
||||
|
||||
override var frame: CGRect {
|
||||
didSet {
|
||||
if oldValue.size != self.bounds.size {
|
||||
self.contentNode.frame = self.bounds
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
cleanWallpaperNode.frame = self.bounds
|
||||
}
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
gradientWallpaperNode.frame = self.bounds
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(backgroundNode: WallpaperBackgroundNodeMergedImpl, bubbleType: WallpaperBubbleType) {
|
||||
self.backgroundNode = backgroundNode
|
||||
self.bubbleType = bubbleType
|
||||
|
||||
self.contentNode = ASImageNode()
|
||||
self.contentNode.displaysAsynchronously = false
|
||||
self.contentNode.isUserInteractionEnabled = false
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.contentNode)
|
||||
|
||||
self.index = backgroundNode.bubbleBackgroundNodeReferences.add(BubbleBackgroundNodeReference(node: self))
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let index = self.index, let backgroundNode = self.backgroundNode {
|
||||
backgroundNode.bubbleBackgroundNodeReferences.remove(index)
|
||||
}
|
||||
}
|
||||
|
||||
func updateContents() {
|
||||
guard let backgroundNode = self.backgroundNode else {
|
||||
return
|
||||
}
|
||||
|
||||
if let bubbleTheme = backgroundNode.bubbleTheme, let bubbleCorners = backgroundNode.bubbleCorners {
|
||||
let wallpaper = backgroundNode.wallpaper ?? bubbleTheme.chat.defaultWallpaper
|
||||
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: bubbleTheme, wallpaper: wallpaper, bubbleCorners: bubbleCorners)
|
||||
var needsCleanBackground = false
|
||||
switch self.bubbleType {
|
||||
case .incoming:
|
||||
self.contentNode.image = graphics.incomingBubbleGradientImage
|
||||
if graphics.incomingBubbleGradientImage == nil {
|
||||
self.contentNode.backgroundColor = bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill[0]
|
||||
} else {
|
||||
self.contentNode.backgroundColor = nil
|
||||
}
|
||||
needsCleanBackground = bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 })
|
||||
case .outgoing:
|
||||
if backgroundNode.outgoingBubbleGradientBackgroundNode != nil {
|
||||
self.contentNode.image = nil
|
||||
self.contentNode.backgroundColor = nil
|
||||
} else {
|
||||
self.contentNode.image = graphics.outgoingBubbleGradientImage
|
||||
if graphics.outgoingBubbleGradientImage == nil {
|
||||
self.contentNode.backgroundColor = bubbleTheme.chat.message.outgoing.bubble.withWallpaper.fill[0]
|
||||
} else {
|
||||
self.contentNode.backgroundColor = nil
|
||||
}
|
||||
needsCleanBackground = bubbleTheme.chat.message.outgoing.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 })
|
||||
}
|
||||
case .free:
|
||||
self.contentNode.image = nil
|
||||
self.contentNode.backgroundColor = nil
|
||||
needsCleanBackground = true
|
||||
}
|
||||
|
||||
var isInvertedGradient = false
|
||||
var hasComplexGradient = false
|
||||
switch wallpaper {
|
||||
case let .file(file):
|
||||
hasComplexGradient = file.settings.colors.count >= 3
|
||||
if let intensity = file.settings.intensity, intensity < 0 {
|
||||
isInvertedGradient = true
|
||||
}
|
||||
case let .gradient(gradient):
|
||||
hasComplexGradient = gradient.colors.count >= 3
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
var needsGradientBackground = false
|
||||
var needsWallpaperBackground = false
|
||||
|
||||
if isInvertedGradient {
|
||||
switch self.bubbleType {
|
||||
case .free:
|
||||
needsCleanBackground = false
|
||||
case .incoming, .outgoing:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if needsCleanBackground {
|
||||
if hasComplexGradient {
|
||||
needsGradientBackground = backgroundNode.gradient != nil
|
||||
} else {
|
||||
needsWallpaperBackground = true
|
||||
}
|
||||
}
|
||||
|
||||
var gradientBackgroundSource: GradientBackgroundNode? = backgroundNode.gradient?.gradientBackground
|
||||
|
||||
if case .outgoing = self.bubbleType {
|
||||
if let outgoingBubbleGradientBackgroundNode = backgroundNode.outgoingBubbleGradientBackgroundNode {
|
||||
gradientBackgroundSource = outgoingBubbleGradientBackgroundNode
|
||||
needsWallpaperBackground = false
|
||||
needsGradientBackground = true
|
||||
}
|
||||
}
|
||||
|
||||
if needsWallpaperBackground {
|
||||
if self.cleanWallpaperNode == nil {
|
||||
let cleanWallpaperNode = ASImageNode()
|
||||
cleanWallpaperNode.displaysAsynchronously = false
|
||||
self.cleanWallpaperNode = cleanWallpaperNode
|
||||
cleanWallpaperNode.frame = self.bounds
|
||||
self.insertSubnode(cleanWallpaperNode, at: 0)
|
||||
}
|
||||
if let blurredBackgroundContents = backgroundNode.blurredBackgroundContents {
|
||||
self.cleanWallpaperNode?.contents = blurredBackgroundContents.cgImage
|
||||
self.cleanWallpaperNode?.backgroundColor = backgroundNode.backgroundColor
|
||||
} else {
|
||||
self.cleanWallpaperNode?.contents = nil
|
||||
self.cleanWallpaperNode?.backgroundColor = backgroundNode.backgroundColor
|
||||
}
|
||||
} else {
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
self.cleanWallpaperNode = nil
|
||||
cleanWallpaperNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
if needsGradientBackground, let gradientBackgroundNode = gradientBackgroundSource {
|
||||
if self.gradientWallpaperNode == nil {
|
||||
let gradientWallpaperNode = GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode)
|
||||
gradientWallpaperNode.frame = self.bounds
|
||||
self.gradientWallpaperNode = gradientWallpaperNode
|
||||
self.insertSubnode(gradientWallpaperNode, at: 0)
|
||||
}
|
||||
} else {
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
self.gradientWallpaperNode = nil
|
||||
gradientWallpaperNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.contentNode.image = nil
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
self.cleanWallpaperNode = nil
|
||||
cleanWallpaperNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
if let (rect, containerSize) = self.currentLayout {
|
||||
self.update(rect: rect, within: containerSize)
|
||||
}
|
||||
}
|
||||
|
||||
func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) {
|
||||
self.currentLayout = (rect, containerSize)
|
||||
|
||||
let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height))
|
||||
|
||||
transition.updateFrame(layer: self.contentNode.layer, frame: self.bounds)
|
||||
transition.animateView {
|
||||
self.contentNode.layer.contentsRect = shiftedContentsRect
|
||||
}
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
transition.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds)
|
||||
transition.animateView {
|
||||
cleanWallpaperNode.layer.contentsRect = shiftedContentsRect
|
||||
}
|
||||
}
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
transition.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds)
|
||||
transition.animateView {
|
||||
gradientWallpaperNode.layer.contentsRect = shiftedContentsRect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
|
||||
self.currentLayout = (rect, containerSize)
|
||||
|
||||
let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height))
|
||||
|
||||
transition.updateFrame(layer: self.contentNode.layer, frame: self.bounds)
|
||||
self.contentNode.layer.contentsRect = shiftedContentsRect
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
transition.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds)
|
||||
cleanWallpaperNode.layer.contentsRect = shiftedContentsRect
|
||||
}
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
transition.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds)
|
||||
gradientWallpaperNode.layer.contentsRect = shiftedContentsRect
|
||||
}
|
||||
}
|
||||
|
||||
func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
|
||||
guard let (_, containerSize) = self.currentLayout else {
|
||||
return
|
||||
}
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: animationCurve)
|
||||
|
||||
let scaledOffset = CGPoint(x: value.x / containerSize.width, y: value.y / containerSize.height)
|
||||
transition.animateContentsRectPositionAdditive(layer: self.contentNode.layer, offset: scaledOffset)
|
||||
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
transition.animateContentsRectPositionAdditive(layer: cleanWallpaperNode.layer, offset: scaledOffset)
|
||||
}
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
transition.animateContentsRectPositionAdditive(layer: gradientWallpaperNode.layer, offset: scaledOffset)
|
||||
}
|
||||
}
|
||||
|
||||
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
|
||||
guard let (_, containerSize) = self.currentLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
let scaledOffset = CGPoint(x: 0.0, y: -value / containerSize.height)
|
||||
|
||||
self.contentNode.layer.animateSpring(from: NSValue(cgPoint: scaledOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "contentsRect.position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
cleanWallpaperNode.layer.animateSpring(from: NSValue(cgPoint: scaledOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "contentsRect.position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
|
||||
}
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
gradientWallpaperNode.layer.animateSpring(from: NSValue(cgPoint: scaledOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "contentsRect.position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class BubbleBackgroundNodeReference {
|
||||
weak var node: BubbleBackgroundNodeImpl?
|
||||
|
||||
init(node: BubbleBackgroundNodeImpl) {
|
||||
self.node = node
|
||||
}
|
||||
}
|
||||
|
||||
private final class WallpaperGradiendComponentView: WallpaperComponentView {
|
||||
struct Spec: Equatable {
|
||||
var colors: [UInt32]
|
||||
}
|
||||
|
||||
let spec: Spec
|
||||
let gradientBackground: GradientBackgroundNode
|
||||
|
||||
var view: UIView {
|
||||
return self.gradientBackground.view
|
||||
}
|
||||
|
||||
init(spec: Spec, updated: @escaping () -> Void) {
|
||||
self.spec = spec
|
||||
|
||||
self.gradientBackground = GradientBackgroundNode(colors: spec.colors.map(UIColor.init(rgb:)), useSharedAnimationPhase: true, adjustSaturation: false)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
private final class WallpaperGradiendComponentView: WallpaperComponentView {
|
||||
struct Spec {
|
||||
var colors: [UInt32]
|
||||
func update(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.gradientBackground.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.gradientBackground.updateLayout(size: size, transition: transition, extendAnimation: false, backwards: false, completion: {})
|
||||
}
|
||||
}
|
||||
|
||||
private final class WallpaperPatternComponentView: WallpaperComponentView {
|
||||
struct Spec {
|
||||
private final class WallpaperColorComponentView: WallpaperComponentView {
|
||||
struct Spec: Equatable {
|
||||
var color: UInt32
|
||||
}
|
||||
|
||||
let spec: Spec
|
||||
let backgroundView: UIView
|
||||
|
||||
var view: UIView {
|
||||
return self.backgroundView
|
||||
}
|
||||
|
||||
init(spec: Spec, updated: @escaping () -> Void) {
|
||||
self.spec = spec
|
||||
|
||||
self.backgroundView = UIView()
|
||||
self.backgroundView.backgroundColor = UIColor(rgb: spec.color)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.backgroundView.frame = CGRect(origin: CGPoint(), size: size)
|
||||
}
|
||||
}
|
||||
|
||||
private final class WallpaperImageComponentView: WallpaperComponentView {
|
||||
enum Spec: Equatable {
|
||||
case image(
|
||||
representation: TelegramMediaImageRepresentation,
|
||||
isPattern: Bool,
|
||||
intensity: CGFloat
|
||||
)
|
||||
case builtin
|
||||
}
|
||||
|
||||
let spec: Spec
|
||||
let updated: () -> Void
|
||||
let imageView: UIImageView
|
||||
var fetchDisposable: Disposable?
|
||||
var dataDisposable: Disposable?
|
||||
|
||||
var imageData: Data?
|
||||
|
||||
private var validSize: CGSize?
|
||||
|
||||
var view: UIView {
|
||||
return self.imageView
|
||||
}
|
||||
|
||||
init(context: AccountContext, spec: Spec, updated: @escaping () -> Void) {
|
||||
self.spec = spec
|
||||
self.updated = updated
|
||||
|
||||
self.imageView = UIImageView()
|
||||
self.imageView.contentMode = .scaleAspectFill
|
||||
|
||||
switch spec {
|
||||
case let .image(representation, _, _):
|
||||
self.fetchDisposable = (fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: MediaResourceReference.standalone(resource: representation.resource))
|
||||
|> deliverOnMainQueue).start()
|
||||
self.dataDisposable = (context.account.postbox.mediaBox.resourceData(representation.resource)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] dataValue in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if dataValue.complete, let data = try? Data(contentsOf: URL(fileURLWithPath: dataValue.path)) {
|
||||
strongSelf.imageData = data
|
||||
if let size = strongSelf.validSize {
|
||||
strongSelf.updateImage(size: size, data: data)
|
||||
}
|
||||
}
|
||||
})
|
||||
case .builtin:
|
||||
if let filePath = getAppBundle().path(forResource: "ChatWallpaperBuiltin0", ofType: "jpg"), let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) {
|
||||
self.imageData = data
|
||||
if let size = self.validSize {
|
||||
self.updateImage(size: size, data: data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.fetchDisposable?.dispose()
|
||||
self.dataDisposable?.dispose()
|
||||
}
|
||||
|
||||
func update(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
let sizeUpdated = self.validSize != size
|
||||
self.validSize = size
|
||||
|
||||
self.imageView.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
if sizeUpdated || self.imageView.image == nil {
|
||||
if let imageData = self.imageData {
|
||||
self.updateImage(size: size, data: imageData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateImage(size: CGSize, data: Data) {
|
||||
let scale: CGFloat
|
||||
if UIScreenScale >= 2.9 {
|
||||
scale = 2.5
|
||||
} else {
|
||||
scale = UIScreenScale
|
||||
}
|
||||
|
||||
switch self.spec {
|
||||
case let .image(_, isPattern, intensity):
|
||||
if isPattern {
|
||||
let patternBackgroundColor: UIColor
|
||||
let patternForegroundColor: UIColor
|
||||
if intensity < 0.0 {
|
||||
patternBackgroundColor = .clear
|
||||
patternForegroundColor = .black
|
||||
} else {
|
||||
patternBackgroundColor = .clear
|
||||
patternForegroundColor = .black
|
||||
}
|
||||
|
||||
if let unpackedData = TGGUnzipData(data, 2 * 1024 * 1024), let patternImage = drawSvgImage(unpackedData, CGSize(width: floor(size.width * scale), height: floor(size.height * scale)), patternBackgroundColor, patternForegroundColor, false) {
|
||||
if intensity < 0.0 {
|
||||
self.imageView.image = generateImage(patternImage.size, scale: patternImage.scale, rotatedContext: { size, context in
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
if let cgImage = patternImage.cgImage {
|
||||
context.setBlendMode(.destinationOut)
|
||||
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
||||
context.draw(cgImage, in: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
})
|
||||
self.imageView.alpha = 1.0
|
||||
self.imageView.layer.compositingFilter = nil
|
||||
self.imageView.backgroundColor = UIColor(white: 0.0, alpha: 1.0 - abs(intensity))
|
||||
} else {
|
||||
self.imageView.image = patternImage
|
||||
self.imageView.alpha = abs(intensity)
|
||||
self.imageView.layer.compositingFilter = "softLightBlendMode"
|
||||
self.imageView.backgroundColor = nil
|
||||
}
|
||||
}
|
||||
|
||||
self.updated()
|
||||
} else if let image = UIImage(data: data) {
|
||||
self.imageView.image = image
|
||||
self.imageView.layer.compositingFilter = nil
|
||||
self.imageView.alpha = 1.0
|
||||
|
||||
self.updated()
|
||||
}
|
||||
case .builtin:
|
||||
if let image = UIImage(data: data) {
|
||||
self.imageView.image = image
|
||||
self.imageView.layer.compositingFilter = nil
|
||||
self.imageView.alpha = 1.0
|
||||
|
||||
self.updated()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -931,8 +1362,17 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun
|
||||
private let storage: SharedStorage
|
||||
|
||||
private let staticView: UIImageView
|
||||
private let dynamicView: UIView
|
||||
private var color: WallpaperColorComponentView?
|
||||
private var gradient: WallpaperGradiendComponentView?
|
||||
private var pattern: WallpaperPatternComponentView?
|
||||
private var image: WallpaperImageComponentView?
|
||||
|
||||
private var blurredBackgroundContents: UIImage?
|
||||
|
||||
private var isSettingUpWallpaper: Bool = false
|
||||
|
||||
private var wallpaper: TelegramWallpaper?
|
||||
private var validLayout: CGSize?
|
||||
|
||||
private let _isReady = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
var isReady: Signal<Bool, NoError> {
|
||||
@ -944,11 +1384,19 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun
|
||||
}
|
||||
}
|
||||
|
||||
private var isAnimating: Bool = false
|
||||
|
||||
private var bubbleTheme: PresentationTheme?
|
||||
private var bubbleCorners: PresentationChatBubbleCorners?
|
||||
private var bubbleBackgroundNodeReferences = SparseBag<BubbleBackgroundNodeReference>()
|
||||
private var outgoingBubbleGradientBackgroundNode: GradientBackgroundNode?
|
||||
|
||||
init(context: AccountContext, storage: SharedStorage?) {
|
||||
self.context = context
|
||||
self.storage = storage ?? SharedStorage()
|
||||
|
||||
self.staticView = UIImageView()
|
||||
self.dynamicView = UIView()
|
||||
|
||||
super.init()
|
||||
|
||||
@ -956,70 +1404,294 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun
|
||||
}
|
||||
|
||||
func update(wallpaper: TelegramWallpaper) {
|
||||
self.wallpaper = wallpaper
|
||||
|
||||
var colorSpec: WallpaperColorComponentView.Spec?
|
||||
var gradientSpec: WallpaperGradiendComponentView.Spec?
|
||||
var imageSpec: WallpaperImageComponentView.Spec?
|
||||
|
||||
switch wallpaper {
|
||||
case let .builtin(wallpaperSettings):
|
||||
let _ = wallpaperSettings
|
||||
case .builtin:
|
||||
imageSpec = WallpaperImageComponentView.Spec.builtin
|
||||
case let .color(color):
|
||||
let _ = color
|
||||
colorSpec = WallpaperColorComponentView.Spec(color: color)
|
||||
case let .gradient(gradient):
|
||||
if gradient.colors.count >= 3 {
|
||||
gradientSpec = WallpaperGradiendComponentView.Spec(colors: gradient.colors)
|
||||
}
|
||||
case let .image(representations, settings):
|
||||
let _ = representations
|
||||
if let representation = representations.last {
|
||||
imageSpec = WallpaperImageComponentView.Spec.image(representation: representation, isPattern: false, intensity: 1.0)
|
||||
}
|
||||
let _ = settings
|
||||
case let .file(file):
|
||||
if file.settings.colors.count >= 3 {
|
||||
gradientSpec = WallpaperGradiendComponentView.Spec(colors: file.settings.colors)
|
||||
}
|
||||
if let dimensions = file.file.dimensions {
|
||||
let representation = TelegramMediaImageRepresentation(dimensions: dimensions, resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: file.file.immediateThumbnailData)
|
||||
imageSpec = WallpaperImageComponentView.Spec.image(representation: representation, isPattern: file.isPattern, intensity: CGFloat(file.settings.intensity ?? 100) / 100.0)
|
||||
}
|
||||
}
|
||||
|
||||
if let gradientSpec = gradientSpec {
|
||||
let gradient: WallpaperGradiendComponentView
|
||||
if let current = self.gradient {
|
||||
gradient = current
|
||||
} else {
|
||||
gradient = WallpaperGradiendComponentView(updated: { [weak self] in
|
||||
self?.componentsUpdated()
|
||||
})
|
||||
if self.color?.spec != colorSpec {
|
||||
if let color = self.color {
|
||||
self.color = nil
|
||||
color.view.removeFromSuperview()
|
||||
}
|
||||
if let colorSpec = colorSpec {
|
||||
let color = WallpaperColorComponentView(spec: colorSpec, updated: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.componentsUpdated()
|
||||
})
|
||||
self.color = color
|
||||
if let size = self.validLayout {
|
||||
color.update(size: size, transition: .immediate)
|
||||
}
|
||||
self.dynamicView.insertSubview(color.view, at: 0)
|
||||
|
||||
self.componentsUpdated()
|
||||
}
|
||||
}
|
||||
|
||||
if self.gradient?.spec != gradientSpec {
|
||||
if let gradient = self.gradient {
|
||||
self.gradient = nil
|
||||
gradient.view.removeFromSuperview()
|
||||
}
|
||||
if let gradientSpec = gradientSpec {
|
||||
let gradient = WallpaperGradiendComponentView(spec: gradientSpec, updated: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.componentsUpdated()
|
||||
})
|
||||
self.gradient = gradient
|
||||
if let size = self.validLayout {
|
||||
gradient.update(size: size, transition: .immediate)
|
||||
}
|
||||
self.dynamicView.insertSubview(gradient.view, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
if self.image?.spec != imageSpec {
|
||||
if let image = self.image {
|
||||
self.image = nil
|
||||
image.view.removeFromSuperview()
|
||||
}
|
||||
if let imageSpec = imageSpec {
|
||||
let image = WallpaperImageComponentView(context: self.context, spec: imageSpec, updated: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.componentsUpdated()
|
||||
})
|
||||
self.image = image
|
||||
if let size = self.validLayout {
|
||||
image.update(size: size, transition: .immediate)
|
||||
}
|
||||
if let gradient = self.gradient {
|
||||
self.dynamicView.insertSubview(image.view, aboveSubview: gradient.view)
|
||||
} else {
|
||||
self.dynamicView.insertSubview(image.view, at: 0)
|
||||
}
|
||||
}
|
||||
let _ = gradient
|
||||
let _ = gradientSpec
|
||||
} else if let gradient = self.gradient {
|
||||
self.gradient = nil
|
||||
gradient.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
private func componentsUpdated() {
|
||||
if self.isAnimating {
|
||||
if self.dynamicView.superview == nil {
|
||||
self.view.addSubview(self.dynamicView)
|
||||
self.staticView.isHidden = true
|
||||
}
|
||||
self._isReady.set(true)
|
||||
} else {
|
||||
self.staticView.isHidden = false
|
||||
self.dynamicView.removeFromSuperview()
|
||||
|
||||
if let size = self.validLayout {
|
||||
if let color = self.color {
|
||||
self.staticView.image = nil
|
||||
self.staticView.backgroundColor = color.backgroundView.backgroundColor
|
||||
} else {
|
||||
let gradientImage = self.gradient?.gradientBackground.contentView.image
|
||||
let gradientFrame = self.gradient?.gradientBackground.frame
|
||||
|
||||
let imageImage = self.image?.imageView.image
|
||||
let imageBackgroundColor = self.image?.imageView.backgroundColor
|
||||
let imageFrame = self.image?.imageView.frame
|
||||
let imageAlpha = self.image?.imageView.alpha
|
||||
let imageFilter = self.image?.imageView.layer.compositingFilter as? String
|
||||
|
||||
self.staticView.image = generateImage(size, opaque: true, scale: nil, rotatedContext: { size, context in
|
||||
UIGraphicsPushContext(context)
|
||||
|
||||
if let gradientImage = gradientImage, let gradientFrame = gradientFrame {
|
||||
gradientImage.draw(in: gradientFrame)
|
||||
}
|
||||
|
||||
if let imageImage = imageImage, let imageFrame = imageFrame, let imageAlpha = imageAlpha {
|
||||
if imageFilter == "softLightBlendMode" {
|
||||
context.setBlendMode(.softLight)
|
||||
}
|
||||
|
||||
if let imageBackgroundColor = imageBackgroundColor {
|
||||
context.setFillColor(imageBackgroundColor.cgColor)
|
||||
context.fill(imageFrame)
|
||||
}
|
||||
|
||||
context.setAlpha(imageAlpha)
|
||||
|
||||
context.translateBy(x: imageFrame.midX, y: imageFrame.midY)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
context.translateBy(x: -imageFrame.midX, y: -imageFrame.midY)
|
||||
if let cgImage = imageImage.cgImage {
|
||||
let drawingSize = imageImage.size.aspectFilled(imageFrame.size)
|
||||
context.draw(cgImage, in: CGRect(origin: CGPoint(x: imageFrame.minX + (imageFrame.width - drawingSize.width) / 2.0, y: imageFrame.minX + (imageFrame.height - drawingSize.height) / 2.0), size: drawingSize))
|
||||
}
|
||||
context.translateBy(x: imageFrame.midX, y: imageFrame.midY)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
context.translateBy(x: -imageFrame.midX, y: -imageFrame.midY)
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
context.setAlpha(1.0)
|
||||
}
|
||||
|
||||
UIGraphicsPopContext()
|
||||
})
|
||||
}
|
||||
|
||||
self._isReady.set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _internalUpdateIsSettingUpWallpaper() {
|
||||
self.isSettingUpWallpaper = true
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = size
|
||||
|
||||
self.staticView.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
if let gradient = self.gradient {
|
||||
gradient.update(size: size, transition: transition)
|
||||
}
|
||||
if let image = self.image {
|
||||
image.update(size: size, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) {
|
||||
|
||||
if let gradient = self.gradient {
|
||||
self.isAnimating = true
|
||||
self.componentsUpdated()
|
||||
gradient.gradientBackground.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.isAnimating = false
|
||||
strongSelf.componentsUpdated()
|
||||
})
|
||||
} else {
|
||||
self.isAnimating = false
|
||||
}
|
||||
}
|
||||
|
||||
func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) {
|
||||
if self.bubbleTheme !== bubbleTheme || self.bubbleCorners != bubbleCorners {
|
||||
self.bubbleTheme = bubbleTheme
|
||||
self.bubbleCorners = bubbleCorners
|
||||
|
||||
if bubbleTheme.chat.message.outgoing.bubble.withoutWallpaper.fill.count >= 3 && bubbleTheme.chat.animateMessageColors {
|
||||
if self.outgoingBubbleGradientBackgroundNode == nil {
|
||||
let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode(adjustSaturation: false)
|
||||
if let size = self.validLayout {
|
||||
outgoingBubbleGradientBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {})
|
||||
}
|
||||
self.outgoingBubbleGradientBackgroundNode = outgoingBubbleGradientBackgroundNode
|
||||
}
|
||||
self.outgoingBubbleGradientBackgroundNode?.updateColors(colors: bubbleTheme.chat.message.outgoing.bubble.withoutWallpaper.fill)
|
||||
} else if let _ = self.outgoingBubbleGradientBackgroundNode {
|
||||
self.outgoingBubbleGradientBackgroundNode = nil
|
||||
}
|
||||
|
||||
self.updateBubbles()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateBubbles() {
|
||||
for reference in self.bubbleBackgroundNodeReferences {
|
||||
reference.node?.updateContents()
|
||||
}
|
||||
}
|
||||
|
||||
func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool {
|
||||
guard let bubbleTheme = self.bubbleTheme, let bubbleCorners = self.bubbleCorners else {
|
||||
return false
|
||||
}
|
||||
if self.wallpaper == nil && !self.isSettingUpWallpaper {
|
||||
return false
|
||||
}
|
||||
|
||||
var hasPlainWallpaper = false
|
||||
let graphicsWallpaper: TelegramWallpaper
|
||||
if let wallpaper = self.wallpaper {
|
||||
switch wallpaper {
|
||||
case .color:
|
||||
hasPlainWallpaper = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
graphicsWallpaper = wallpaper
|
||||
} else {
|
||||
graphicsWallpaper = bubbleTheme.chat.defaultWallpaper
|
||||
}
|
||||
|
||||
let graphics = PresentationResourcesChat.principalGraphics(theme: bubbleTheme, wallpaper: graphicsWallpaper, bubbleCorners: bubbleCorners)
|
||||
switch type {
|
||||
case .incoming:
|
||||
if graphics.incomingBubbleGradientImage != nil {
|
||||
return true
|
||||
}
|
||||
if bubbleTheme.chat.message.incoming.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 }) {
|
||||
return !hasPlainWallpaper
|
||||
}
|
||||
case .outgoing:
|
||||
if graphics.outgoingBubbleGradientImage != nil {
|
||||
return true
|
||||
}
|
||||
if bubbleTheme.chat.message.outgoing.bubble.withWallpaper.fill.contains(where: { $0.alpha <= 0.99 }) {
|
||||
return !hasPlainWallpaper
|
||||
}
|
||||
case .free:
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode? {
|
||||
return nil
|
||||
if !self.hasBubbleBackground(for: type) {
|
||||
return nil
|
||||
}
|
||||
let node = WallpaperBackgroundNodeMergedImpl.BubbleBackgroundNodeImpl(backgroundNode: self, bubbleType: type)
|
||||
node.updateContents()
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
public func createWallpaperBackgroundNode(context: AccountContext, useSharedAnimationPhase: Bool = false) -> WallpaperBackgroundNode {
|
||||
private let sharedStorage = WallpaperBackgroundNodeMergedImpl.SharedStorage()
|
||||
|
||||
public func createWallpaperBackgroundNode(context: AccountContext, forChatDisplay: Bool, useSharedAnimationPhase: Bool = false, useExperimentalImplementation: Bool = false) -> WallpaperBackgroundNode {
|
||||
if forChatDisplay && useExperimentalImplementation {
|
||||
return WallpaperBackgroundNodeMergedImpl(context: context, storage: useSharedAnimationPhase ? sharedStorage : nil)
|
||||
}
|
||||
|
||||
return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase)
|
||||
}
|
||||
|
@ -1272,7 +1272,7 @@ public func themeImage(account: Account, accountManager: AccountManager<Telegram
|
||||
case let .pattern(data, colors, intensity):
|
||||
let wallpaperImage = generateImage(arguments.drawingSize, rotatedContext: { size, context in
|
||||
drawWallpaperGradientImage(colors.map(UIColor.init(rgb:)), context: context, size: size)
|
||||
if let unpackedData = TGGUnzipData(data, 2 * 1024 * 1024), let image = drawSvgImage(unpackedData, arguments.drawingSize, .clear, .black) {
|
||||
if let unpackedData = TGGUnzipData(data, 2 * 1024 * 1024), let image = drawSvgImage(unpackedData, arguments.drawingSize, .clear, .black, true) {
|
||||
context.setBlendMode(.softLight)
|
||||
context.setAlpha(abs(CGFloat(intensity)) / 100.0)
|
||||
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: arguments.drawingSize))
|
||||
|
Loading…
x
Reference in New Issue
Block a user