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 experimentalCompatibility(Bool)
|
||||||
case enableDebugDataDisplay(Bool)
|
case enableDebugDataDisplay(Bool)
|
||||||
case acceleratedStickers(Bool)
|
case acceleratedStickers(Bool)
|
||||||
case mockICE(Bool)
|
case experimentalBackground(Bool)
|
||||||
case playerEmbedding(Bool)
|
case playerEmbedding(Bool)
|
||||||
case playlistPlayback(Bool)
|
case playlistPlayback(Bool)
|
||||||
case voiceConference
|
case voiceConference
|
||||||
@ -102,7 +102,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return DebugControllerSection.logging.rawValue
|
return DebugControllerSection.logging.rawValue
|
||||||
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
||||||
return DebugControllerSection.experiments.rawValue
|
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
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .preferredVideoCodec:
|
case .preferredVideoCodec:
|
||||||
return DebugControllerSection.videoExperiments.rawValue
|
return DebugControllerSection.videoExperiments.rawValue
|
||||||
@ -171,7 +171,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return 27
|
return 27
|
||||||
case .acceleratedStickers:
|
case .acceleratedStickers:
|
||||||
return 29
|
return 29
|
||||||
case .mockICE:
|
case .experimentalBackground:
|
||||||
return 30
|
return 30
|
||||||
case .playerEmbedding:
|
case .playerEmbedding:
|
||||||
return 31
|
return 31
|
||||||
@ -804,12 +804,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
})
|
})
|
||||||
case let .mockICE(value):
|
case let .experimentalBackground(value):
|
||||||
return ItemListSwitchItem(presentationData: presentationData, title: "mockICE", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
return ItemListSwitchItem(presentationData: presentationData, title: "Background Experiment", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||||
settings.mockICE = value
|
settings.experimentalBackground = value
|
||||||
return PreferencesEntry(settings)
|
return PreferencesEntry(settings)
|
||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
@ -926,7 +926,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
|||||||
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
|
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
|
||||||
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
||||||
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
||||||
entries.append(.mockICE(experimentalSettings.mockICE))
|
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
|
||||||
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
|
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
|
||||||
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
|
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) {
|
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 {
|
public func pictureInPictureControllerShouldProhibitBackgroundAudioPlayback(_ pictureInPictureController: AVPictureInPictureController) -> Bool {
|
||||||
@ -539,7 +554,9 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
|
|||||||
|
|
||||||
private var hiddenMediaManagerIndex: Int?
|
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.overlayController = overlayController
|
||||||
self.mediaManager = mediaManager
|
self.mediaManager = mediaManager
|
||||||
self.node = videoNode
|
self.node = videoNode
|
||||||
@ -559,7 +576,7 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
|
|||||||
contentDelegate.pictureInPictureController = pictureInPictureController
|
contentDelegate.pictureInPictureController = pictureInPictureController
|
||||||
|
|
||||||
pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = false
|
pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = false
|
||||||
pictureInPictureController.requiresLinearPlayback = true
|
pictureInPictureController.requiresLinearPlayback = !canSkip
|
||||||
pictureInPictureController.delegate = self
|
pictureInPictureController.delegate = self
|
||||||
self.pictureInPictureController = pictureInPictureController
|
self.pictureInPictureController = pictureInPictureController
|
||||||
let timer = SwiftSignalKit.Timer(timeout: 0.005, repeat: true, completion: { [weak self] in
|
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 {
|
deinit {
|
||||||
|
self.messageRemovedDisposable?.dispose()
|
||||||
self.pictureInPictureTimer?.invalidate()
|
self.pictureInPictureTimer?.invalidate()
|
||||||
self.node.setCanPlaybackWithoutHierarchy(false)
|
self.node.setCanPlaybackWithoutHierarchy(false)
|
||||||
|
|
||||||
@ -698,6 +737,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
private var dismissOnOrientationChange = false
|
private var dismissOnOrientationChange = false
|
||||||
private var keepSoundOnDismiss = false
|
private var keepSoundOnDismiss = false
|
||||||
private var hasPictureInPicture = false
|
private var hasPictureInPicture = false
|
||||||
|
|
||||||
|
private var pictureInPictureButton: UIBarButtonItem?
|
||||||
|
|
||||||
private var requiresDownload = false
|
private var requiresDownload = false
|
||||||
|
|
||||||
@ -1328,6 +1369,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
if forceEnablePiP || (!isAnimated && !disablePlayerControls && !disablePictureInPicture) {
|
if forceEnablePiP || (!isAnimated && !disablePlayerControls && !disablePictureInPicture) {
|
||||||
let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed))
|
let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed))
|
||||||
|
self.pictureInPictureButton = rightBarButtonItem
|
||||||
barButtonItems.append(rightBarButtonItem)
|
barButtonItems.append(rightBarButtonItem)
|
||||||
self.hasPictureInPicture = true
|
self.hasPictureInPicture = true
|
||||||
} else {
|
} else {
|
||||||
@ -1427,6 +1469,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
pictureInPictureNode.removeFromSupernode()
|
pictureInPictureNode.removeFromSupernode()
|
||||||
self.videoNode?.backgroundColor = .black
|
self.videoNode?.backgroundColor = .black
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.pictureInPictureButton?.isEnabled = self.pictureInPictureNode == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private func shouldAutoplayOnCentrality() -> Bool {
|
private func shouldAutoplayOnCentrality() -> Bool {
|
||||||
@ -2069,7 +2113,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
case let .message(message):
|
case let .message(message):
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
if let media = media as? TelegramMediaFile, media.isVideo {
|
if let media = media as? TelegramMediaFile, media.isVideo {
|
||||||
isNativePictureInPictureSupported = true
|
if message.id.namespace == Namespaces.Message.Cloud {
|
||||||
|
isNativePictureInPictureSupported = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -2106,7 +2152,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
break
|
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 {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
|||||||
deinit {
|
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
|
let sizeUpdated = self.validLayout != size
|
||||||
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.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25
|
||||||
}
|
}
|
||||||
|
|
||||||
|
animation.completion = { _ in
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
|
||||||
self.contentView.layer.removeAnimation(forKey: "contents")
|
self.contentView.layer.removeAnimation(forKey: "contents")
|
||||||
self.contentView.layer.add(animation, forKey: "contents")
|
self.contentView.layer.add(animation, forKey: "contents")
|
||||||
|
|
||||||
@ -405,7 +409,11 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
|||||||
for cloneNode in self.cloneNodes {
|
for cloneNode in self.cloneNodes {
|
||||||
cloneNode.value?.image = dimmedImage
|
cloneNode.value?.image = dimmedImage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
completion()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
completion()
|
||||||
}
|
}
|
||||||
} else if sizeUpdated {
|
} else if sizeUpdated {
|
||||||
let image = generateGradient(size: imageSize, colors: self.colors, positions: positions)
|
let image = generateGradient(size: imageSize, colors: self.colors, positions: positions)
|
||||||
@ -419,6 +427,10 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.validPhase = self.phase
|
self.validPhase = self.phase
|
||||||
|
|
||||||
|
completion()
|
||||||
|
} else {
|
||||||
|
completion()
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
@ -440,13 +452,14 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
|||||||
self.colors = colors
|
self.colors = colors
|
||||||
self.invalidated = true
|
self.invalidated = true
|
||||||
if let size = self.validLayout {
|
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 {
|
guard case let .animated(duration, _) = transition, duration > 0.001 else {
|
||||||
|
completion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,7 +476,9 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
|||||||
GradientBackgroundNode.sharedPhase = self.phase
|
GradientBackgroundNode.sharedPhase = self.phase
|
||||||
}
|
}
|
||||||
if let size = self.validLayout {
|
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 {
|
if let strongSelf = self {
|
||||||
strongSelf.inputFieldNode.append(character)
|
strongSelf.inputFieldNode.append(character)
|
||||||
if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode {
|
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 {
|
if let strongSelf = self {
|
||||||
let _ = strongSelf.inputFieldNode.delete()
|
let _ = strongSelf.inputFieldNode.delete()
|
||||||
if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode {
|
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()
|
self.hapticFeedback.tap()
|
||||||
let result = self.inputFieldNode.delete()
|
let result = self.inputFieldNode.delete()
|
||||||
if result, let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
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 {
|
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||||
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
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)
|
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)
|
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)
|
self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||||
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
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)
|
self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
if !iconFrame.isEmpty {
|
if !iconFrame.isEmpty {
|
||||||
@ -385,7 +385,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
|||||||
self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||||
|
|
||||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
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.inputFieldNode.animateIn()
|
||||||
self.keyboardNode.animateIn()
|
self.keyboardNode.animateIn()
|
||||||
@ -425,7 +425,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
|||||||
self.hapticFeedback.error()
|
self.hapticFeedback.error()
|
||||||
|
|
||||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
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 {
|
if let backgroundCustomNode = self.backgroundCustomNode {
|
||||||
transition.updateFrame(node: backgroundCustomNode, frame: bounds)
|
transition.updateFrame(node: backgroundCustomNode, frame: bounds)
|
||||||
if let gradientBackgroundNode = backgroundCustomNode as? GradientBackgroundNode {
|
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)
|
transition.updateFrame(view: self.effectView, frame: bounds)
|
||||||
|
@ -62,7 +62,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
|
|||||||
|
|
||||||
self.scrollNode = ASScrollNode()
|
self.scrollNode = ASScrollNode()
|
||||||
|
|
||||||
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context)
|
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||||
self.chatBackgroundNode.displaysAsynchronously = false
|
self.chatBackgroundNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.messagesContainerNode = ASDisplayNode()
|
self.messagesContainerNode = ASDisplayNode()
|
||||||
|
@ -132,7 +132,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
if currentBackgroundNode == nil {
|
if currentBackgroundNode == nil {
|
||||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context)
|
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||||
}
|
}
|
||||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
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.pageControlNode = PageControlNode(dotSpacing: 7.0, dotColor: .white, inactiveDotColor: UIColor.white.withAlphaComponent(0.4))
|
||||||
|
|
||||||
self.chatListBackgroundNode = ASDisplayNode()
|
self.chatListBackgroundNode = ASDisplayNode()
|
||||||
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context)
|
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||||
self.chatBackgroundNode.displaysAsynchronously = false
|
self.chatBackgroundNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.messagesContainerNode = ASDisplayNode()
|
self.messagesContainerNode = ASDisplayNode()
|
||||||
|
@ -174,7 +174,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
|
|||||||
|
|
||||||
if let gradientNode = self.gradientNode {
|
if let gradientNode = self.gradientNode {
|
||||||
gradientNode.frame = CGRect(origin: CGPoint(), size: size)
|
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
|
let progressDiameter: CGFloat = 50.0
|
||||||
|
@ -285,7 +285,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
|||||||
self.backgroundContainerNode = ASDisplayNode()
|
self.backgroundContainerNode = ASDisplayNode()
|
||||||
self.backgroundContainerNode.clipsToBounds = true
|
self.backgroundContainerNode.clipsToBounds = true
|
||||||
self.backgroundWrapperNode = ASDisplayNode()
|
self.backgroundWrapperNode = ASDisplayNode()
|
||||||
self.backgroundNode = createWallpaperBackgroundNode(context: context)
|
self.backgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||||
|
|
||||||
self.messagesContainerNode = ASDisplayNode()
|
self.messagesContainerNode = ASDisplayNode()
|
||||||
self.messagesContainerNode.clipsToBounds = true
|
self.messagesContainerNode.clipsToBounds = true
|
||||||
|
@ -107,7 +107,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.messagesContainerNode.clipsToBounds = true
|
self.messagesContainerNode.clipsToBounds = true
|
||||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
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.instantChatBackgroundNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.ready.set(.single(true))
|
self.ready.set(.single(true))
|
||||||
@ -121,7 +121,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.blurredNode = BlurredImageNode()
|
self.blurredNode = BlurredImageNode()
|
||||||
self.blurredNode.blurView.contentMode = .scaleAspectFill
|
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)
|
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings, doneButtonType: .set)
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
if currentBackgroundNode == nil {
|
if currentBackgroundNode == nil {
|
||||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context)
|
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||||
}
|
}
|
||||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)
|
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)
|
||||||
|
@ -136,7 +136,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
self.wrapperNode = ASDisplayNode()
|
self.wrapperNode = ASDisplayNode()
|
||||||
self.imageNode = TransformImageNode()
|
self.imageNode = TransformImageNode()
|
||||||
self.imageNode.contentAnimations = .subsequentUpdates
|
self.imageNode.contentAnimations = .subsequentUpdates
|
||||||
self.nativeNode = createWallpaperBackgroundNode(context: context)
|
self.nativeNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||||
self.cropNode = WallpaperCropNode()
|
self.cropNode = WallpaperCropNode()
|
||||||
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6))
|
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.frame = CGRect(x: 0.0, y: 0.0, width: progressDiameter, height: progressDiameter)
|
||||||
|
@ -964,7 +964,7 @@ final class MessageStoryRenderer {
|
|||||||
|
|
||||||
self.containerNode = ASDisplayNode()
|
self.containerNode = ASDisplayNode()
|
||||||
|
|
||||||
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context)
|
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||||
self.instantChatBackgroundNode.displaysAsynchronously = false
|
self.instantChatBackgroundNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.messagesContainerNode = ASDisplayNode()
|
self.messagesContainerNode = ASDisplayNode()
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data);
|
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data);
|
||||||
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size);
|
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 */
|
#endif /* Lottie_h */
|
||||||
|
@ -83,7 +83,7 @@ CGSize aspectFillSize(CGSize size, CGSize bounds) {
|
|||||||
|
|
||||||
@end
|
@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];
|
NSDate *startTime = [NSDate date];
|
||||||
|
|
||||||
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
|
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);
|
printf("parseTime = %f\n", deltaTime);
|
||||||
|
|
||||||
startTime = [NSDate date];
|
startTime = [NSDate date];
|
||||||
|
|
||||||
UIGraphicsBeginImageContextWithOptions(size, true, 1.0);
|
UIGraphicsBeginImageContextWithOptions(size, opaque, 1.0);
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
|
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
|
||||||
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));
|
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 {
|
} else {
|
||||||
pollPts = 1
|
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)
|
|> map(Optional.init)
|
||||||
|> `catch` { error -> Signal<Api.updates.ChannelDifference?, MTRpcError> in
|
|> `catch` { error -> Signal<Api.updates.ChannelDifference?, MTRpcError> in
|
||||||
switch error.errorDescription {
|
switch error.errorDescription {
|
||||||
|
@ -508,7 +508,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
default:
|
default:
|
||||||
break
|
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)
|
self.wallpaperReady.set(self.chatBackgroundNode.isReady)
|
||||||
|
|
||||||
var locationBroadcastPanelSource: LocationBroadcastPanelSource
|
var locationBroadcastPanelSource: LocationBroadcastPanelSource
|
||||||
|
@ -611,7 +611,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var adPeerId: PeerId?
|
var adPeerId: PeerId?
|
||||||
adPeerId = nil
|
adPeerId = messages.first?.author?.id
|
||||||
|
|
||||||
if strongSelf.preloadAdPeerId != adPeerId {
|
if strongSelf.preloadAdPeerId != adPeerId {
|
||||||
strongSelf.preloadAdPeerId = adPeerId
|
strongSelf.preloadAdPeerId = adPeerId
|
||||||
|
@ -99,7 +99,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 }
|
self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 }
|
||||||
|
|
||||||
self.backgroundNode = createWallpaperBackgroundNode(context: context)
|
self.backgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true)
|
||||||
self.backgroundNode.isUserInteractionEnabled = false
|
self.backgroundNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
self.panelBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.chat.inputPanel.panelBackgroundColor)
|
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 experimentalCompatibility: Bool
|
||||||
public var enableDebugDataDisplay: Bool
|
public var enableDebugDataDisplay: Bool
|
||||||
public var acceleratedStickers: Bool
|
public var acceleratedStickers: Bool
|
||||||
public var mockICE: Bool
|
public var experimentalBackground: Bool
|
||||||
|
|
||||||
public static var defaultSettings: ExperimentalUISettings {
|
public static var defaultSettings: ExperimentalUISettings {
|
||||||
return ExperimentalUISettings(
|
return ExperimentalUISettings(
|
||||||
@ -36,7 +36,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
experimentalCompatibility: false,
|
experimentalCompatibility: false,
|
||||||
enableDebugDataDisplay: false,
|
enableDebugDataDisplay: false,
|
||||||
acceleratedStickers: false,
|
acceleratedStickers: false,
|
||||||
mockICE: false
|
experimentalBackground: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
experimentalCompatibility: Bool,
|
experimentalCompatibility: Bool,
|
||||||
enableDebugDataDisplay: Bool,
|
enableDebugDataDisplay: Bool,
|
||||||
acceleratedStickers: Bool,
|
acceleratedStickers: Bool,
|
||||||
mockICE: Bool
|
experimentalBackground: Bool
|
||||||
) {
|
) {
|
||||||
self.keepChatNavigationStack = keepChatNavigationStack
|
self.keepChatNavigationStack = keepChatNavigationStack
|
||||||
self.skipReadHistory = skipReadHistory
|
self.skipReadHistory = skipReadHistory
|
||||||
@ -71,7 +71,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
self.experimentalCompatibility = experimentalCompatibility
|
self.experimentalCompatibility = experimentalCompatibility
|
||||||
self.enableDebugDataDisplay = enableDebugDataDisplay
|
self.enableDebugDataDisplay = enableDebugDataDisplay
|
||||||
self.acceleratedStickers = acceleratedStickers
|
self.acceleratedStickers = acceleratedStickers
|
||||||
self.mockICE = mockICE
|
self.experimentalBackground = experimentalBackground
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
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.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 0) != 0
|
||||||
self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 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.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 {
|
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.experimentalCompatibility ? 1 : 0) as Int32, forKey: "experimentalCompatibility")
|
||||||
try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay")
|
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.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/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
"//submodules/WallpaperResources:WallpaperResources",
|
"//submodules/WallpaperResources:WallpaperResources",
|
||||||
"//submodules/FastBlur:FastBlur",
|
"//submodules/FastBlur:FastBlur",
|
||||||
|
"//submodules/Svg:Svg",
|
||||||
|
"//submodules/GZip:GZip",
|
||||||
|
"//submodules/AppBundle:AppBundle",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -9,6 +9,9 @@ import AccountContext
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import WallpaperResources
|
import WallpaperResources
|
||||||
import FastBlur
|
import FastBlur
|
||||||
|
import Svg
|
||||||
|
import GZip
|
||||||
|
import AppBundle
|
||||||
|
|
||||||
private let motionAmount: CGFloat = 32.0
|
private let motionAmount: CGFloat = 32.0
|
||||||
|
|
||||||
@ -781,12 +784,12 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
|||||||
|
|
||||||
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
if let gradientBackgroundNode = self.gradientBackgroundNode {
|
||||||
transition.updateFrame(node: gradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
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 {
|
if let outgoingBubbleGradientBackgroundNode = self.outgoingBubbleGradientBackgroundNode {
|
||||||
transition.updateFrame(node: outgoingBubbleGradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
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)
|
self.loadPatternForSizeIfNeeded(size: size, transition: transition)
|
||||||
@ -797,21 +800,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) {
|
func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) {
|
||||||
self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation)
|
self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {})
|
||||||
self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation)
|
self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation, backwards: false, completion: {})
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) {
|
func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) {
|
||||||
@ -824,7 +814,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
|||||||
let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode(adjustSaturation: false)
|
let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode(adjustSaturation: false)
|
||||||
if let size = self.validLayout {
|
if let size = self.validLayout {
|
||||||
outgoingBubbleGradientBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
|
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
|
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 WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroundNode {
|
||||||
final class SharedStorage {
|
final class SharedStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class WallpaperComponentView: UIView {
|
final class BubbleBackgroundNodeImpl: ASDisplayNode, WallpaperBubbleBackgroundNode {
|
||||||
let updated: () -> Void
|
private let bubbleType: WallpaperBubbleType
|
||||||
|
private let contentNode: ASImageNode
|
||||||
|
|
||||||
init(updated: @escaping () -> Void) {
|
private var cleanWallpaperNode: ASDisplayNode?
|
||||||
self.updated = updated
|
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) {
|
required init?(coder: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private final class WallpaperGradiendComponentView: WallpaperComponentView {
|
func update(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||||
struct Spec {
|
self.gradientBackground.frame = CGRect(origin: CGPoint(), size: size)
|
||||||
var colors: [UInt32]
|
self.gradientBackground.updateLayout(size: size, transition: transition, extendAnimation: false, backwards: false, completion: {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class WallpaperPatternComponentView: WallpaperComponentView {
|
private final class WallpaperColorComponentView: WallpaperComponentView {
|
||||||
struct Spec {
|
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 storage: SharedStorage
|
||||||
|
|
||||||
private let staticView: UIImageView
|
private let staticView: UIImageView
|
||||||
|
private let dynamicView: UIView
|
||||||
|
private var color: WallpaperColorComponentView?
|
||||||
private var gradient: WallpaperGradiendComponentView?
|
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)
|
private let _isReady = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
var isReady: Signal<Bool, NoError> {
|
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?) {
|
init(context: AccountContext, storage: SharedStorage?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.storage = storage ?? SharedStorage()
|
self.storage = storage ?? SharedStorage()
|
||||||
|
|
||||||
self.staticView = UIImageView()
|
self.staticView = UIImageView()
|
||||||
|
self.dynamicView = UIView()
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -956,70 +1404,294 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun
|
|||||||
}
|
}
|
||||||
|
|
||||||
func update(wallpaper: TelegramWallpaper) {
|
func update(wallpaper: TelegramWallpaper) {
|
||||||
|
self.wallpaper = wallpaper
|
||||||
|
|
||||||
|
var colorSpec: WallpaperColorComponentView.Spec?
|
||||||
var gradientSpec: WallpaperGradiendComponentView.Spec?
|
var gradientSpec: WallpaperGradiendComponentView.Spec?
|
||||||
|
var imageSpec: WallpaperImageComponentView.Spec?
|
||||||
|
|
||||||
switch wallpaper {
|
switch wallpaper {
|
||||||
case let .builtin(wallpaperSettings):
|
case .builtin:
|
||||||
let _ = wallpaperSettings
|
imageSpec = WallpaperImageComponentView.Spec.builtin
|
||||||
case let .color(color):
|
case let .color(color):
|
||||||
let _ = color
|
colorSpec = WallpaperColorComponentView.Spec(color: color)
|
||||||
case let .gradient(gradient):
|
case let .gradient(gradient):
|
||||||
if gradient.colors.count >= 3 {
|
if gradient.colors.count >= 3 {
|
||||||
gradientSpec = WallpaperGradiendComponentView.Spec(colors: gradient.colors)
|
gradientSpec = WallpaperGradiendComponentView.Spec(colors: gradient.colors)
|
||||||
}
|
}
|
||||||
case let .image(representations, settings):
|
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
|
let _ = settings
|
||||||
case let .file(file):
|
case let .file(file):
|
||||||
if file.settings.colors.count >= 3 {
|
if file.settings.colors.count >= 3 {
|
||||||
gradientSpec = WallpaperGradiendComponentView.Spec(colors: file.settings.colors)
|
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 {
|
if self.color?.spec != colorSpec {
|
||||||
let gradient: WallpaperGradiendComponentView
|
if let color = self.color {
|
||||||
if let current = self.gradient {
|
self.color = nil
|
||||||
gradient = current
|
color.view.removeFromSuperview()
|
||||||
} else {
|
}
|
||||||
gradient = WallpaperGradiendComponentView(updated: { [weak self] in
|
if let colorSpec = colorSpec {
|
||||||
self?.componentsUpdated()
|
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() {
|
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() {
|
func _internalUpdateIsSettingUpWallpaper() {
|
||||||
|
self.isSettingUpWallpaper = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
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) {
|
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) {
|
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 {
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode? {
|
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)
|
return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase)
|
||||||
}
|
}
|
||||||
|
@ -1272,7 +1272,7 @@ public func themeImage(account: Account, accountManager: AccountManager<Telegram
|
|||||||
case let .pattern(data, colors, intensity):
|
case let .pattern(data, colors, intensity):
|
||||||
let wallpaperImage = generateImage(arguments.drawingSize, rotatedContext: { size, context in
|
let wallpaperImage = generateImage(arguments.drawingSize, rotatedContext: { size, context in
|
||||||
drawWallpaperGradientImage(colors.map(UIColor.init(rgb:)), context: context, size: size)
|
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.setBlendMode(.softLight)
|
||||||
context.setAlpha(abs(CGFloat(intensity)) / 100.0)
|
context.setAlpha(abs(CGFloat(intensity)) / 100.0)
|
||||||
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: arguments.drawingSize))
|
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: arguments.drawingSize))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user