mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Story optimizations
This commit is contained in:
parent
a35f38c0f6
commit
4353823603
@ -1027,6 +1027,8 @@ public protocol AccountContext: AnyObject {
|
|||||||
|
|
||||||
var userLimits: EngineConfiguration.UserLimits { get }
|
var userLimits: EngineConfiguration.UserLimits { get }
|
||||||
|
|
||||||
|
var imageCache: AnyObject? { get }
|
||||||
|
|
||||||
func storeSecureIdPassword(password: String)
|
func storeSecureIdPassword(password: String)
|
||||||
func getStoredSecureIdPassword() -> String?
|
func getStoredSecureIdPassword() -> String?
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ swift_library(
|
|||||||
"//submodules/FastBlur:FastBlur",
|
"//submodules/FastBlur:FastBlur",
|
||||||
"//submodules/ComponentFlow",
|
"//submodules/ComponentFlow",
|
||||||
"//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent",
|
"//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent",
|
||||||
|
"//submodules/DirectMediaImageCache",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -13,6 +13,7 @@ import Emoji
|
|||||||
import Accelerate
|
import Accelerate
|
||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
import AvatarStoryIndicatorComponent
|
import AvatarStoryIndicatorComponent
|
||||||
|
import DirectMediaImageCache
|
||||||
|
|
||||||
private let deletedIcon = UIImage(bundleImageName: "Avatar/DeletedIcon")?.precomposed()
|
private let deletedIcon = UIImage(bundleImageName: "Avatar/DeletedIcon")?.precomposed()
|
||||||
private let phoneIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/PhoneIcon"), color: .white)
|
private let phoneIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/PhoneIcon"), color: .white)
|
||||||
@ -228,6 +229,19 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
]
|
]
|
||||||
|
|
||||||
public final class ContentNode: ASDisplayNode {
|
public final class ContentNode: ASDisplayNode {
|
||||||
|
private struct Params: Equatable {
|
||||||
|
let peerId: EnginePeer.Id?
|
||||||
|
let resourceId: String?
|
||||||
|
|
||||||
|
init(
|
||||||
|
peerId: EnginePeer.Id?,
|
||||||
|
resourceId: String?
|
||||||
|
) {
|
||||||
|
self.peerId = peerId
|
||||||
|
self.resourceId = resourceId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var font: UIFont {
|
public var font: UIFont {
|
||||||
didSet {
|
didSet {
|
||||||
if oldValue.pointSize != font.pointSize {
|
if oldValue.pointSize != font.pointSize {
|
||||||
@ -255,6 +269,9 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
public var unroundedImage: UIImage?
|
public var unroundedImage: UIImage?
|
||||||
private var currentImage: UIImage?
|
private var currentImage: UIImage?
|
||||||
|
|
||||||
|
private var params: Params?
|
||||||
|
private var loadDisposable: Disposable?
|
||||||
|
|
||||||
public var badgeView: AvatarBadgeView? {
|
public var badgeView: AvatarBadgeView? {
|
||||||
didSet {
|
didSet {
|
||||||
if self.badgeView !== oldValue {
|
if self.badgeView !== oldValue {
|
||||||
@ -319,6 +336,10 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.loadDisposable?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
override public func didLoad() {
|
override public func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
|
|
||||||
@ -496,6 +517,58 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setPeerV2(
|
||||||
|
context genericContext: AccountContext,
|
||||||
|
account: Account? = nil,
|
||||||
|
theme: PresentationTheme,
|
||||||
|
peer: EnginePeer?,
|
||||||
|
authorOfMessage: MessageReference? = nil,
|
||||||
|
overrideImage: AvatarNodeImageOverride? = nil,
|
||||||
|
emptyColor: UIColor? = nil,
|
||||||
|
clipStyle: AvatarNodeClipStyle = .round,
|
||||||
|
synchronousLoad: Bool = false,
|
||||||
|
displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0),
|
||||||
|
storeUnrounded: Bool = false
|
||||||
|
) {
|
||||||
|
let smallProfileImage = peer?.smallProfileImage
|
||||||
|
let params = Params(
|
||||||
|
peerId: peer?.id,
|
||||||
|
resourceId: smallProfileImage?.resource.id.stringRepresentation
|
||||||
|
)
|
||||||
|
if self.params == params {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.params = params
|
||||||
|
|
||||||
|
switch clipStyle {
|
||||||
|
case .none:
|
||||||
|
self.imageNode.clipsToBounds = false
|
||||||
|
self.imageNode.cornerRadius = 0.0
|
||||||
|
case .round:
|
||||||
|
self.imageNode.clipsToBounds = true
|
||||||
|
self.imageNode.cornerRadius = displayDimensions.height * 0.5
|
||||||
|
case .roundedRect:
|
||||||
|
self.imageNode.clipsToBounds = true
|
||||||
|
self.imageNode.cornerRadius = displayDimensions.height * 0.25
|
||||||
|
}
|
||||||
|
|
||||||
|
if let imageCache = genericContext.imageCache as? DirectMediaImageCache, let peer, let smallProfileImage = peer.smallProfileImage, let peerReference = PeerReference(peer._asPeer()) {
|
||||||
|
if let result = imageCache.getAvatarImage(peer: peerReference, resource: MediaResourceReference.avatar(peer: peerReference, resource: smallProfileImage.resource), immediateThumbnail: peer.profileImageRepresentations.first?.immediateThumbnailData, size: Int(displayDimensions.width * UIScreenScale), synchronous: synchronousLoad) {
|
||||||
|
if let image = result.image {
|
||||||
|
self.imageNode.contents = image.cgImage
|
||||||
|
}
|
||||||
|
if let loadSignal = result.loadSignal {
|
||||||
|
self.loadDisposable = (loadSignal |> deliverOnMainQueue).start(next: { [weak self] image in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.imageNode.contents = image?.cgImage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func setPeer(
|
public func setPeer(
|
||||||
context genericContext: AccountContext,
|
context genericContext: AccountContext,
|
||||||
account: Account? = nil,
|
account: Account? = nil,
|
||||||
@ -657,6 +730,10 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
context.fill(bounds)
|
context.fill(bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !(parameters is AvatarNodeParameters) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let colors: [UIColor]
|
let colors: [UIColor]
|
||||||
if let parameters = parameters as? AvatarNodeParameters {
|
if let parameters = parameters as? AvatarNodeParameters {
|
||||||
colors = parameters.colors
|
colors = parameters.colors
|
||||||
@ -925,6 +1002,32 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func setPeerV2(
|
||||||
|
context genericContext: AccountContext,
|
||||||
|
theme: PresentationTheme,
|
||||||
|
peer: EnginePeer?,
|
||||||
|
authorOfMessage: MessageReference? = nil,
|
||||||
|
overrideImage: AvatarNodeImageOverride? = nil,
|
||||||
|
emptyColor: UIColor? = nil,
|
||||||
|
clipStyle: AvatarNodeClipStyle = .round,
|
||||||
|
synchronousLoad: Bool = false,
|
||||||
|
displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0),
|
||||||
|
storeUnrounded: Bool = false
|
||||||
|
) {
|
||||||
|
self.contentNode.setPeerV2(
|
||||||
|
context: genericContext,
|
||||||
|
theme: theme,
|
||||||
|
peer: peer,
|
||||||
|
authorOfMessage: authorOfMessage,
|
||||||
|
overrideImage: overrideImage,
|
||||||
|
emptyColor: emptyColor,
|
||||||
|
clipStyle: clipStyle,
|
||||||
|
synchronousLoad: synchronousLoad,
|
||||||
|
displayDimensions: displayDimensions,
|
||||||
|
storeUnrounded: storeUnrounded
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func setPeer(
|
public func setPeer(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
account: Account? = nil,
|
account: Account? = nil,
|
||||||
|
@ -492,6 +492,33 @@ public final class DirectMediaImageCache {
|
|||||||
return GetMediaResult(image: resultImage, blurredImage: blurredImage, loadSignal: self.getLoadSignal(width: width, aspectRatio: aspectRatio, userLocation: userLocation, userContentType: .image, resource: resource.resource, resourceSizeLimit: resource.size))
|
return GetMediaResult(image: resultImage, blurredImage: blurredImage, loadSignal: self.getLoadSignal(width: width, aspectRatio: aspectRatio, userLocation: userLocation, userContentType: .image, resource: resource.resource, resourceSizeLimit: resource.size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func getAvatarImageSynchronous(peer: PeerReference, resource: MediaResourceReference, immediateThumbnail: Data?, size: Int, includeBlurred: Bool) -> GetMediaResult? {
|
||||||
|
let immediateThumbnailData: Data? = immediateThumbnail
|
||||||
|
|
||||||
|
var blurredImage: UIImage?
|
||||||
|
if includeBlurred, let data = immediateThumbnailData.flatMap(decodeTinyThumbnail), let image = loadImage(data: data), let blurredImageValue = generateBlurredThumbnail(image: image, adjustSaturation: true) {
|
||||||
|
blurredImage = blurredImageValue
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultImage: UIImage?
|
||||||
|
if let data = try? Data(contentsOf: URL(fileURLWithPath: self.getCachePath(resourceId: resource.resource.id, imageType: .square(width: size, aspectRatio: 1.0)))), let image = loadImage(data: data) {
|
||||||
|
return GetMediaResult(image: image, blurredImage: blurredImage, loadSignal: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resultImage == nil {
|
||||||
|
if let data = try? Data(contentsOf: URL(fileURLWithPath: self.getCachePath(resourceId: resource.resource.id, imageType: .blurredThumbnail))), let image = loadImage(data: data) {
|
||||||
|
resultImage = image
|
||||||
|
} else if let data = immediateThumbnailData.flatMap(decodeTinyThumbnail), let image = loadImage(data: data) {
|
||||||
|
if let blurredImageValue = generateBlurredThumbnail(image: image) {
|
||||||
|
resultImage = blurredImageValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetMediaResult(image: resultImage, blurredImage: blurredImage, loadSignal: self.getLoadSignal(width: size, aspectRatio: 1.0, userLocation: .other, userContentType: .avatar, resource: resource, resourceSizeLimit: 1 * 1024 * 1024))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public func getImage(peer: PeerReference, story: EngineStoryItem, media: Media, width: Int, aspectRatio: CGFloat, possibleWidths: [Int], includeBlurred: Bool = false, synchronous: Bool) -> GetMediaResult? {
|
public func getImage(peer: PeerReference, story: EngineStoryItem, media: Media, width: Int, aspectRatio: CGFloat, possibleWidths: [Int], includeBlurred: Bool = false, synchronous: Bool) -> GetMediaResult? {
|
||||||
if synchronous {
|
if synchronous {
|
||||||
return self.getImageSynchronous(peer: peer, story: story, userLocation: .peer(peer.id), media: media, width: width, aspectRatio: aspectRatio, possibleWidths: possibleWidths, includeBlurred: includeBlurred)
|
return self.getImageSynchronous(peer: peer, story: story, userLocation: .peer(peer.id), media: media, width: width, aspectRatio: aspectRatio, possibleWidths: possibleWidths, includeBlurred: includeBlurred)
|
||||||
@ -530,4 +557,37 @@ public final class DirectMediaImageCache {
|
|||||||
|> runOn(.concurrentDefaultQueue()))
|
|> runOn(.concurrentDefaultQueue()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func getAvatarImage(peer: PeerReference, resource: MediaResourceReference, immediateThumbnail: Data?, size: Int, includeBlurred: Bool = false, synchronous: Bool) -> GetMediaResult? {
|
||||||
|
if synchronous {
|
||||||
|
return self.getAvatarImageSynchronous(peer: peer, resource: resource, immediateThumbnail: immediateThumbnail, size: size, includeBlurred: includeBlurred)
|
||||||
|
} else {
|
||||||
|
var blurredImage: UIImage?
|
||||||
|
if includeBlurred, let data = immediateThumbnail.flatMap(decodeTinyThumbnail), let image = loadImage(data: data), let blurredImageValue = generateBlurredThumbnail(image: image, adjustSaturation: true) {
|
||||||
|
blurredImage = blurredImageValue
|
||||||
|
}
|
||||||
|
return GetMediaResult(image: nil, blurredImage: blurredImage, loadSignal: Signal { subscriber in
|
||||||
|
let result = self.getAvatarImageSynchronous(peer: peer, resource: resource, immediateThumbnail: immediateThumbnail, size: size, includeBlurred: includeBlurred)
|
||||||
|
guard let result = result else {
|
||||||
|
subscriber.putNext(nil)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
|
||||||
|
if let image = result.image {
|
||||||
|
subscriber.putNext(image)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let signal = result.loadSignal {
|
||||||
|
return signal.start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion)
|
||||||
|
} else {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> runOn(.concurrentDefaultQueue()))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -451,6 +451,14 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func hasFirstResponder() -> Bool {
|
||||||
|
if let textFieldView = self.textField.view as? TextFieldComponent.View {
|
||||||
|
return textFieldView.hasFirstResponder()
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func getSendMessageInput() -> SendMessageInput {
|
public func getSendMessageInput() -> SendMessageInput {
|
||||||
guard let textFieldView = self.textField.view as? TextFieldComponent.View else {
|
guard let textFieldView = self.textField.view as? TextFieldComponent.View else {
|
||||||
return .text(NSAttributedString())
|
return .text(NSAttributedString())
|
||||||
|
@ -574,8 +574,8 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
|
|||||||
return self.chatPresentationData.theme.theme.list.itemPlainSeparatorColor
|
return self.chatPresentationData.theme.theme.list.itemPlainSeparatorColor
|
||||||
}
|
}
|
||||||
|
|
||||||
func createLayer() -> SparseItemGridLayer? {
|
func createLayer(item: SparseItemGrid.Item) -> SparseItemGridLayer? {
|
||||||
if self.captureProtected {
|
if let item = item as? VisualMediaItem, item.story.isForwardingDisabled {
|
||||||
return CaptureProtectedItemLayer()
|
return CaptureProtectedItemLayer()
|
||||||
} else {
|
} else {
|
||||||
return GenericItemLayer()
|
return GenericItemLayer()
|
||||||
|
@ -941,7 +941,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
return self.chatPresentationData.theme.theme.list.itemPlainSeparatorColor
|
return self.chatPresentationData.theme.theme.list.itemPlainSeparatorColor
|
||||||
}
|
}
|
||||||
|
|
||||||
func createLayer() -> SparseItemGridLayer? {
|
func createLayer(item: SparseItemGrid.Item) -> SparseItemGridLayer? {
|
||||||
if self.useListItems {
|
if self.useListItems {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -501,7 +501,22 @@ public final class PeerListItemComponent: Component {
|
|||||||
}
|
}
|
||||||
let _ = clipStyle
|
let _ = clipStyle
|
||||||
let _ = synchronousLoad
|
let _ = synchronousLoad
|
||||||
|
|
||||||
|
if peer.smallProfileImage != nil {
|
||||||
|
self.avatarNode.setPeerV2(
|
||||||
|
context: component.context,
|
||||||
|
theme: component.theme,
|
||||||
|
peer: peer,
|
||||||
|
authorOfMessage: nil,
|
||||||
|
overrideImage: nil,
|
||||||
|
emptyColor: nil,
|
||||||
|
clipStyle: .round,
|
||||||
|
synchronousLoad: synchronousLoad,
|
||||||
|
displayDimensions: CGSize(width: avatarSize, height: avatarSize)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
self.avatarNode.setPeer(context: component.context, theme: component.theme, peer: peer, clipStyle: clipStyle, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: avatarSize, height: avatarSize))
|
self.avatarNode.setPeer(context: component.context, theme: component.theme, peer: peer, clipStyle: clipStyle, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: avatarSize, height: avatarSize))
|
||||||
|
}
|
||||||
self.avatarNode.setStoryStats(storyStats: component.storyStats.flatMap { storyStats -> AvatarNode.StoryStats in
|
self.avatarNode.setStoryStats(storyStats: component.storyStats.flatMap { storyStats -> AvatarNode.StoryStats in
|
||||||
return AvatarNode.StoryStats(
|
return AvatarNode.StoryStats(
|
||||||
totalCount: storyStats.totalCount == 0 ? 0 : 1,
|
totalCount: storyStats.totalCount == 0 ? 0 : 1,
|
||||||
|
@ -1210,7 +1210,9 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
if component.content.stateValue?.slice == nil {
|
if component.content.stateValue?.slice == nil {
|
||||||
self.environment?.controller()?.dismiss()
|
self.environment?.controller()?.dismiss()
|
||||||
} else {
|
} else {
|
||||||
|
let startTime = CFAbsoluteTimeGetCurrent()
|
||||||
self.state?.updated(transition: .immediate)
|
self.state?.updated(transition: .immediate)
|
||||||
|
print("update time: \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
@ -2325,6 +2325,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
func update(component: StoryItemSetContainerComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
func update(component: StoryItemSetContainerComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
let isFirstTime = self.component == nil
|
let isFirstTime = self.component == nil
|
||||||
|
|
||||||
|
let startTime1 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
if self.component == nil {
|
if self.component == nil {
|
||||||
self.sendMessageContext.setup(context: component.context, view: self, inputPanelExternalState: self.inputPanelExternalState, keyboardInputData: component.keyboardInputData)
|
self.sendMessageContext.setup(context: component.context, view: self, inputPanelExternalState: self.inputPanelExternalState, keyboardInputData: component.keyboardInputData)
|
||||||
|
|
||||||
@ -2470,6 +2472,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
component.externalState.dismissFraction = dismissFraction
|
component.externalState.dismissFraction = dismissFraction
|
||||||
|
|
||||||
|
let startTime2 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
transition.setPosition(view: self.componentContainerView, position: CGPoint(x: availableSize.width * 0.5, y: availableSize.height * 0.5 + dismissPanOffset))
|
transition.setPosition(view: self.componentContainerView, position: CGPoint(x: availableSize.width * 0.5, y: availableSize.height * 0.5 + dismissPanOffset))
|
||||||
transition.setBounds(view: self.componentContainerView, bounds: CGRect(origin: CGPoint(), size: availableSize))
|
transition.setBounds(view: self.componentContainerView, bounds: CGRect(origin: CGPoint(), size: availableSize))
|
||||||
transition.setScale(view: self.componentContainerView, scale: dismissPanScale)
|
transition.setScale(view: self.componentContainerView, scale: dismissPanScale)
|
||||||
@ -2481,6 +2485,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
transition.setBounds(view: self.overlayContainerView, bounds: CGRect(origin: CGPoint(), size: availableSize))
|
transition.setBounds(view: self.overlayContainerView, bounds: CGRect(origin: CGPoint(), size: availableSize))
|
||||||
transition.setScale(view: self.overlayContainerView, scale: dismissPanScale)
|
transition.setScale(view: self.overlayContainerView, scale: dismissPanScale)
|
||||||
|
|
||||||
|
let startTime21 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
var bottomContentInset: CGFloat
|
var bottomContentInset: CGFloat
|
||||||
if !component.safeInsets.bottom.isZero {
|
if !component.safeInsets.bottom.isZero {
|
||||||
bottomContentInset = component.safeInsets.bottom + 1.0
|
bottomContentInset = component.safeInsets.bottom + 1.0
|
||||||
@ -2532,11 +2538,30 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
inputPlaceholder = .plain(component.strings.Story_InputPlaceholderReplyPrivately)
|
inputPlaceholder = .plain(component.strings.Story_InputPlaceholderReplyPrivately)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let startTime22 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
|
var currentHasFirstResponder = false
|
||||||
|
if let reactionContextNode = self.reactionContextNode {
|
||||||
|
if hasFirstResponder(reactionContextNode.view) {
|
||||||
|
currentHasFirstResponder = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View {
|
||||||
|
if inputPanelView.hasFirstResponder() {
|
||||||
|
currentHasFirstResponder = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var keyboardHeight = component.deviceMetrics.standardInputHeight(inLandscape: false)
|
var keyboardHeight = component.deviceMetrics.standardInputHeight(inLandscape: false)
|
||||||
let keyboardWasHidden = self.inputPanelExternalState.isKeyboardHidden
|
let keyboardWasHidden = self.inputPanelExternalState.isKeyboardHidden
|
||||||
let inputNodeVisible = self.sendMessageContext.currentInputMode == .media || hasFirstResponder(self)
|
let inputNodeVisible = self.sendMessageContext.currentInputMode == .media || currentHasFirstResponder
|
||||||
self.inputPanel.parentState = state
|
self.inputPanel.parentState = state
|
||||||
let inputPanelSize = self.inputPanel.update(
|
var inputPanelSize: CGSize?
|
||||||
|
|
||||||
|
let startTime23 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
|
if component.slice.peer.id != component.context.account.peerId {
|
||||||
|
inputPanelSize = self.inputPanel.update(
|
||||||
transition: inputPanelTransition,
|
transition: inputPanelTransition,
|
||||||
component: AnyComponent(MessageInputPanelComponent(
|
component: AnyComponent(MessageInputPanelComponent(
|
||||||
externalState: self.inputPanelExternalState,
|
externalState: self.inputPanelExternalState,
|
||||||
@ -2745,6 +2770,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: inputPanelAvailableWidth, height: 200.0)
|
containerSize: CGSize(width: inputPanelAvailableWidth, height: 200.0)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let startTime3 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
var inputPanelInset: CGFloat = component.containerInsets.bottom
|
var inputPanelInset: CGFloat = component.containerInsets.bottom
|
||||||
var inputHeight = component.inputHeight
|
var inputHeight = component.inputHeight
|
||||||
@ -2816,7 +2844,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
inputPanelBottomInset = bottomContentInset
|
inputPanelBottomInset = bottomContentInset
|
||||||
if case .regular = component.metrics.widthClass {
|
if case .regular = component.metrics.widthClass {
|
||||||
bottomContentInset += 60.0
|
bottomContentInset += 60.0
|
||||||
} else {
|
} else if let inputPanelSize {
|
||||||
bottomContentInset += inputPanelSize.height
|
bottomContentInset += inputPanelSize.height
|
||||||
}
|
}
|
||||||
inputPanelIsOverlay = false
|
inputPanelIsOverlay = false
|
||||||
@ -2832,6 +2860,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
let minimizedHeight = max(100.0, availableSize.height - (325.0 + 12.0))
|
let minimizedHeight = max(100.0, availableSize.height - (325.0 + 12.0))
|
||||||
let defaultHeight = 60.0 + component.safeInsets.bottom + 1.0
|
let defaultHeight = 60.0 + component.safeInsets.bottom + 1.0
|
||||||
|
|
||||||
|
let startTime4 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
var validViewListIds: [Int32] = []
|
var validViewListIds: [Int32] = []
|
||||||
if component.slice.peer.id == component.context.account.peerId, let currentIndex = component.slice.allItems.firstIndex(where: { $0.storyItem.id == component.slice.item.storyItem.id }) {
|
if component.slice.peer.id == component.context.account.peerId, let currentIndex = component.slice.allItems.firstIndex(where: { $0.storyItem.id == component.slice.item.storyItem.id }) {
|
||||||
var visibleViewListIds: [Int32] = [component.slice.item.storyItem.id]
|
var visibleViewListIds: [Int32] = [component.slice.item.storyItem.id]
|
||||||
@ -3310,6 +3340,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
self.viewLists.removeValue(forKey: id)
|
self.viewLists.removeValue(forKey: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let startTime5 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
let itemSize = CGSize(width: availableSize.width, height: ceil(availableSize.width * 1.77778))
|
let itemSize = CGSize(width: availableSize.width, height: ceil(availableSize.width * 1.77778))
|
||||||
let contentDefaultBottomInset: CGFloat = bottomContentInset
|
let contentDefaultBottomInset: CGFloat = bottomContentInset
|
||||||
|
|
||||||
@ -3465,6 +3497,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let startTime6 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
let soundButtonState = isSilentVideo || component.isAudioMuted
|
let soundButtonState = isSilentVideo || component.isAudioMuted
|
||||||
let soundButtonSize = self.soundButton.update(
|
let soundButtonSize = self.soundButton.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
@ -3748,12 +3782,17 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let startTime7 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
let topGradientHeight: CGFloat = 90.0
|
let topGradientHeight: CGFloat = 90.0
|
||||||
let topContentGradientRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentFrame.width, height: topGradientHeight))
|
let topContentGradientRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentFrame.width, height: topGradientHeight))
|
||||||
transition.setPosition(view: self.topContentGradientView, position: topContentGradientRect.center)
|
transition.setPosition(view: self.topContentGradientView, position: topContentGradientRect.center)
|
||||||
transition.setBounds(view: self.topContentGradientView, bounds: CGRect(origin: CGPoint(), size: topContentGradientRect.size))
|
transition.setBounds(view: self.topContentGradientView, bounds: CGRect(origin: CGPoint(), size: topContentGradientRect.size))
|
||||||
|
|
||||||
|
var inputPanelFrameValue: CGRect?
|
||||||
|
if let inputPanelSize {
|
||||||
let inputPanelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - inputPanelSize.width) / 2.0), y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize)
|
let inputPanelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - inputPanelSize.width) / 2.0), y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize)
|
||||||
|
inputPanelFrameValue = inputPanelFrame
|
||||||
var inputPanelAlpha: CGFloat = component.slice.peer.id == component.context.account.peerId || component.hideUI || self.isEditingStory ? 0.0 : 1.0
|
var inputPanelAlpha: CGFloat = component.slice.peer.id == component.context.account.peerId || component.hideUI || self.isEditingStory ? 0.0 : 1.0
|
||||||
if case .regular = component.metrics.widthClass {
|
if case .regular = component.metrics.widthClass {
|
||||||
inputPanelAlpha *= component.visibilityFraction
|
inputPanelAlpha *= component.visibilityFraction
|
||||||
@ -3772,6 +3811,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
inputPanelTransition.setFrame(view: inputPanelView, frame: inputPanelFrame.offsetBy(dx: 0.0, dy: inputPanelOffset))
|
inputPanelTransition.setFrame(view: inputPanelView, frame: inputPanelFrame.offsetBy(dx: 0.0, dy: inputPanelOffset))
|
||||||
transition.setAlpha(view: inputPanelView, alpha: inputPanelAlpha)
|
transition.setAlpha(view: inputPanelView, alpha: inputPanelAlpha)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let captionItem = self.captionItem, captionItem.itemId != component.slice.item.storyItem.id {
|
if let captionItem = self.captionItem, captionItem.itemId != component.slice.item.storyItem.id {
|
||||||
self.captionItem = nil
|
self.captionItem = nil
|
||||||
@ -3915,7 +3955,11 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
likeRect.origin.x -= 30.0
|
likeRect.origin.x -= 30.0
|
||||||
reactionsAnchorRect = likeRect
|
reactionsAnchorRect = likeRect
|
||||||
} else {
|
} else {
|
||||||
reactionsAnchorRect = CGRect(origin: CGPoint(x: inputPanelFrame.maxX - 40.0, y: inputPanelFrame.minY + 9.0), size: CGSize(width: 32.0, height: 32.0)).insetBy(dx: -4.0, dy: -4.0)
|
if let inputPanelFrameValue {
|
||||||
|
reactionsAnchorRect = CGRect(origin: CGPoint(x: inputPanelFrameValue.maxX - 40.0, y: inputPanelFrameValue.minY + 9.0), size: CGSize(width: 32.0, height: 32.0)).insetBy(dx: -4.0, dy: -4.0)
|
||||||
|
} else {
|
||||||
|
reactionsAnchorRect = CGRect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var effectiveDisplayReactions = false
|
var effectiveDisplayReactions = false
|
||||||
@ -4297,7 +4341,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let bottomGradientHeight = inputPanelSize.height + 32.0
|
let bottomGradientHeight = (inputPanelSize?.height ?? 0.0) + 32.0
|
||||||
transition.setFrame(layer: self.bottomContentGradientLayer, frame: CGRect(origin: CGPoint(x: contentFrame.minX, y: availableSize.height - inputHeight - bottomGradientHeight), size: CGSize(width: contentFrame.width, height: bottomGradientHeight)))
|
transition.setFrame(layer: self.bottomContentGradientLayer, frame: CGRect(origin: CGPoint(x: contentFrame.minX, y: availableSize.height - inputHeight - bottomGradientHeight), size: CGSize(width: contentFrame.width, height: bottomGradientHeight)))
|
||||||
//transition.setAlpha(layer: self.bottomContentGradientLayer, alpha: inputPanelIsOverlay ? 1.0 : 0.0)
|
//transition.setAlpha(layer: self.bottomContentGradientLayer, alpha: inputPanelIsOverlay ? 1.0 : 0.0)
|
||||||
transition.setAlpha(layer: self.bottomContentGradientLayer, alpha: 0.0)
|
transition.setAlpha(layer: self.bottomContentGradientLayer, alpha: 0.0)
|
||||||
@ -4354,10 +4398,14 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let startTime8 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
self.ignoreScrolling = false
|
self.ignoreScrolling = false
|
||||||
|
|
||||||
self.updateScrolling(transition: itemsTransition)
|
self.updateScrolling(transition: itemsTransition)
|
||||||
|
|
||||||
|
let startTime9 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
if let focusedItem, let visibleItem = self.visibleItems[focusedItem.storyItem.id], let index = focusedItem.position {
|
if let focusedItem, let visibleItem = self.visibleItems[focusedItem.storyItem.id], let index = focusedItem.position {
|
||||||
let navigationStripSideInset: CGFloat = 8.0
|
let navigationStripSideInset: CGFloat = 8.0
|
||||||
let navigationStripTopInset: CGFloat = 8.0
|
let navigationStripTopInset: CGFloat = 8.0
|
||||||
@ -4396,8 +4444,27 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
component.externalState.derivedMediaSize = contentFrame.size
|
component.externalState.derivedMediaSize = contentFrame.size
|
||||||
if component.slice.peer.id == component.context.account.peerId {
|
if component.slice.peer.id == component.context.account.peerId {
|
||||||
component.externalState.derivedBottomInset = availableSize.height - itemsContainerFrame.maxY
|
component.externalState.derivedBottomInset = availableSize.height - itemsContainerFrame.maxY
|
||||||
|
} else if let inputPanelFrameValue {
|
||||||
|
component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrameValue.minY, contentFrame.maxY)
|
||||||
} else {
|
} else {
|
||||||
component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrame.minY, contentFrame.maxY)
|
component.externalState.derivedBottomInset = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
if !"".isEmpty {
|
||||||
|
print("inner update time:\n" +
|
||||||
|
" 1: \((CFAbsoluteTimeGetCurrent() - startTime1) * 1000.0) ms\n" +
|
||||||
|
" 2: \((CFAbsoluteTimeGetCurrent() - startTime2) * 1000.0) ms\n" +
|
||||||
|
" 2.1: \((CFAbsoluteTimeGetCurrent() - startTime21) * 1000.0) ms\n" +
|
||||||
|
" 2.2: \((CFAbsoluteTimeGetCurrent() - startTime22) * 1000.0) ms\n" +
|
||||||
|
" 2.3: \((CFAbsoluteTimeGetCurrent() - startTime23) * 1000.0) ms\n" +
|
||||||
|
" 3: \((CFAbsoluteTimeGetCurrent() - startTime3) * 1000.0) ms\n" +
|
||||||
|
" 4: \((CFAbsoluteTimeGetCurrent() - startTime4) * 1000.0) ms\n" +
|
||||||
|
" 5: \((CFAbsoluteTimeGetCurrent() - startTime5) * 1000.0) ms\n" +
|
||||||
|
" 6: \((CFAbsoluteTimeGetCurrent() - startTime6) * 1000.0) ms\n" +
|
||||||
|
" 7: \((CFAbsoluteTimeGetCurrent() - startTime7) * 1000.0) ms\n" +
|
||||||
|
" 8: \((CFAbsoluteTimeGetCurrent() - startTime8) * 1000.0) ms\n" +
|
||||||
|
" 9: \((CFAbsoluteTimeGetCurrent() - startTime9) * 1000.0) ms\n"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return contentSize
|
return contentSize
|
||||||
|
@ -380,7 +380,7 @@ final class StoryItemSetViewListComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let actualBounds = self.scrollView.bounds
|
let actualBounds = self.scrollView.bounds
|
||||||
let visibleBounds = actualBounds.insetBy(dx: 0.0, dy: -200.0)
|
let visibleBounds = actualBounds//.insetBy(dx: 0.0, dy: -200.0)
|
||||||
|
|
||||||
var synchronousLoad = false
|
var synchronousLoad = false
|
||||||
if let hint = transition.userData(PeerListItemComponent.TransitionHint.self) {
|
if let hint = transition.userData(PeerListItemComponent.TransitionHint.self) {
|
||||||
@ -402,6 +402,12 @@ final class StoryItemSetViewListComponent: Component {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*if "".isEmpty {
|
||||||
|
if index > range.lowerBound - 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
let itemFrame = itemLayout.itemFrame(for: index)
|
let itemFrame = itemLayout.itemFrame(for: index)
|
||||||
|
|
||||||
if index >= viewListState.items.count {
|
if index >= viewListState.items.count {
|
||||||
|
@ -264,6 +264,10 @@ public final class TextFieldComponent: Component {
|
|||||||
self.updateEntities()
|
self.updateEntities()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func hasFirstResponder() -> Bool {
|
||||||
|
return self.textView.isFirstResponder
|
||||||
|
}
|
||||||
|
|
||||||
public func insertText(_ text: NSAttributedString) {
|
public func insertText(_ text: NSAttributedString) {
|
||||||
self.updateInputState { state in
|
self.updateInputState { state in
|
||||||
return state.insertText(text)
|
return state.insertText(text)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user