mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Stories
This commit is contained in:
parent
e81b1fee96
commit
5db3755cbf
@ -1055,7 +1055,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
)
|
||||
if let storyIndicatorView = storyIndicator.view {
|
||||
if storyIndicatorView.superview == nil {
|
||||
self.view.addSubview(storyIndicatorView)
|
||||
self.view.insertSubview(storyIndicatorView, aboveSubview: self.contentNode.view)
|
||||
}
|
||||
indicatorTransition.setFrame(view: storyIndicatorView, frame: CGRect(origin: CGPoint(x: (size.width - indicatorSize.width) * 0.5, y: (size.height - indicatorSize.height) * 0.5), size: indicatorSize))
|
||||
}
|
||||
|
@ -1255,7 +1255,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
self.avatarContainerNode.addSubnode(self.avatarNode)
|
||||
self.contextContainer.addSubnode(self.avatarContainerNode)
|
||||
self.contextContainer.addSubnode(self.onlineNode)
|
||||
self.avatarNode.addSubnode(self.onlineNode)
|
||||
|
||||
self.mainContentContainerNode.addSubnode(self.titleNode)
|
||||
self.mainContentContainerNode.addSubnode(self.authorNode)
|
||||
@ -3001,9 +3001,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let onlineFrame: CGRect
|
||||
if onlineIsVoiceChat {
|
||||
onlineFrame = CGRect(origin: CGPoint(x: avatarFrame.maxX - onlineLayout.width + 1.0 - UIScreenPixel, y: avatarFrame.maxY - onlineLayout.height + 1.0 - UIScreenPixel), size: onlineLayout)
|
||||
onlineFrame = CGRect(origin: CGPoint(x: avatarFrame.width - onlineLayout.width + 1.0 - UIScreenPixel, y: avatarFrame.height - onlineLayout.height + 1.0 - UIScreenPixel), size: onlineLayout)
|
||||
} else {
|
||||
onlineFrame = CGRect(origin: CGPoint(x: avatarFrame.maxX - onlineLayout.width - 2.0, y: avatarFrame.maxY - onlineLayout.height - 2.0), size: onlineLayout)
|
||||
onlineFrame = CGRect(origin: CGPoint(x: avatarFrame.width - onlineLayout.width - 2.0, y: avatarFrame.height - onlineLayout.height - 2.0), size: onlineLayout)
|
||||
}
|
||||
transition.updateFrame(node: strongSelf.onlineNode, frame: onlineFrame)
|
||||
|
||||
@ -3040,11 +3040,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
avatarTimerTransition = .immediate
|
||||
avatarTimerBadge = AvatarBadgeView(frame: CGRect())
|
||||
strongSelf.avatarTimerBadge = avatarTimerBadge
|
||||
strongSelf.contextContainer.view.addSubview(avatarTimerBadge)
|
||||
strongSelf.avatarNode.view.addSubview(avatarTimerBadge)
|
||||
}
|
||||
let avatarBadgeSize = CGSize(width: avatarTimerBadgeDiameter, height: avatarTimerBadgeDiameter)
|
||||
avatarTimerBadge.update(size: avatarBadgeSize, text: shortTimeIntervalString(strings: item.presentationData.strings, value: autoremoveTimeout, useLargeFormat: true))
|
||||
let avatarBadgeFrame = CGRect(origin: CGPoint(x: avatarFrame.maxX - avatarBadgeSize.width, y: avatarFrame.maxY - avatarBadgeSize.height), size: avatarBadgeSize)
|
||||
let avatarBadgeFrame = CGRect(origin: CGPoint(x: avatarFrame.width - avatarBadgeSize.width, y: avatarFrame.height - avatarBadgeSize.height), size: avatarBadgeSize)
|
||||
avatarTimerTransition.updatePosition(layer: avatarTimerBadge.layer, position: avatarBadgeFrame.center)
|
||||
avatarTimerTransition.updateBounds(layer: avatarTimerBadge.layer, bounds: CGRect(origin: CGPoint(), size: avatarBadgeFrame.size))
|
||||
avatarTimerTransition.updateTransformScale(layer: avatarTimerBadge.layer, scale: autoremoveTimeoutFraction * 1.0 + (1.0 - autoremoveTimeoutFraction) * 0.00001)
|
||||
|
@ -331,7 +331,7 @@ public extension CALayer {
|
||||
|
||||
adjustFrameRate(animation: animation)
|
||||
|
||||
self.add(animation, forKey: keyPath)
|
||||
self.add(animation, forKey: additive ? nil : keyPath)
|
||||
}
|
||||
|
||||
func animateAdditive(from: NSValue, to: NSValue, keyPath: String, key: String, timingFunction: String, mediaTimingFunction: CAMediaTimingFunction? = nil, duration: Double, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||
|
@ -209,7 +209,7 @@ final class ReactionContextBackgroundNode: ASDisplayNode {
|
||||
self.backgroundView.update(size: contentBounds.size, transition: transition)
|
||||
|
||||
if let vibrancyEffectView = self.vibrancyEffectView {
|
||||
transition.updateFrame(view: vibrancyEffectView, frame: CGRect(origin: CGPoint(x: 10.0, y: 10.0), size: contentBounds.size))
|
||||
transition.updateFrame(view: vibrancyEffectView, frame: CGRect(origin: CGPoint(x: 10.0, y: 10.0), size: contentBounds.size), beginWithCurrentState: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1123,7 +1123,12 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
var scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: self.isExpanded ? (46.0 + 54.0 - 4.0) : self.contentTopInset), size: actualBackgroundFrame.size)
|
||||
scrollFrame.origin.y += floorToScreenPixels(self.extensionDistance / 2.0)
|
||||
|
||||
transition.updateFrame(node: self.contentContainer, frame: visualBackgroundFrame, beginWithCurrentState: true)
|
||||
transition.updatePosition(node: self.contentContainer, position: visualBackgroundFrame.center, beginWithCurrentState: true)
|
||||
|
||||
if !self.contentContainer.bounds.equalTo(CGRect(origin: CGPoint(), size: visualBackgroundFrame.size)) {
|
||||
transition.updateBounds(node: self.contentContainer, bounds: CGRect(origin: CGPoint(), size: visualBackgroundFrame.size), beginWithCurrentState: true)
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.contentTintContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: visualBackgroundFrame.size), beginWithCurrentState: true)
|
||||
transition.updateFrame(view: self.contentContainerMask, frame: CGRect(origin: CGPoint(), size: visualBackgroundFrame.size), beginWithCurrentState: true)
|
||||
transition.updateFrame(node: self.scrollNode, frame: scrollFrame, beginWithCurrentState: true)
|
||||
@ -1287,11 +1292,12 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
self.backgroundNode.animateInFromAnchorRect(size: visualBackgroundFrame.size, sourceBackgroundFrame: sourceBackgroundFrame.offsetBy(dx: -visualBackgroundFrame.minX, dy: -visualBackgroundFrame.minY))
|
||||
|
||||
let xOffset = sourceBackgroundFrame.midX - visualBackgroundFrame.midX
|
||||
self.animateInInfo = (sourceBackgroundFrame.minX - visualBackgroundFrame.minX, visualBackgroundFrame.width)
|
||||
self.contentContainer.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - visualBackgroundFrame.midX, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
self.contentContainer.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: xOffset, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
self.contentContainer.layer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(x: (sourceBackgroundFrame.minX - visualBackgroundFrame.minX), y: 0.0), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: visualBackgroundFrame.size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping)
|
||||
|
||||
self.contentTintContainer.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - visualBackgroundFrame.midX, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
self.contentTintContainer.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: xOffset, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
self.contentTintContainer.layer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(x: (sourceBackgroundFrame.minX - visualBackgroundFrame.minX), y: 0.0), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: visualBackgroundFrame.size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping)
|
||||
} else if let animateOutToAnchorRect = animateOutToAnchorRect {
|
||||
let targetBackgroundFrame = self.calculateBackgroundFrame(containerSize: size, insets: backgroundInsets, anchorRect: animateOutToAnchorRect, contentSize: CGSize(width: visibleContentWidth, height: contentHeight), centerAligned: false).0
|
||||
|
@ -1700,7 +1700,7 @@ public final class SparseItemGrid: ASDisplayNode {
|
||||
if let headerTextView = headerText.view {
|
||||
if headerTextView.superview == nil {
|
||||
headerTextView.layer.anchorPoint = CGPoint()
|
||||
self.view.addSubview(headerTextView)
|
||||
self.view.insertSubview(headerTextView, at: 0)
|
||||
}
|
||||
headerTextView.center = headerTextFrame.origin
|
||||
headerTextView.bounds = CGRect(origin: CGPoint(), size: headerTextFrame.size)
|
||||
|
@ -119,6 +119,11 @@ public final class EngineStoryViewListContext {
|
||||
if let parentSource = parentSource, (parentSource.listMode == .everyone || parentSource.listMode == listMode), let parentState = parentSource.state, parentState.totalCount <= 100 {
|
||||
self.parentSource = parentSource
|
||||
|
||||
let matchesMode = parentSource.listMode == listMode
|
||||
if parentState.items.count < 100 && !matchesMode {
|
||||
parentSource.loadMore()
|
||||
}
|
||||
|
||||
self.disposable.set((parentSource.statePromise.get()
|
||||
|> mapToSignal { state -> Signal<InternalState, NoError> in
|
||||
let needUpdate: Signal<Void, NoError>
|
||||
@ -148,10 +153,10 @@ public final class EngineStoryViewListContext {
|
||||
return needUpdate
|
||||
|> mapToSignal { _ -> Signal<InternalState, NoError> in
|
||||
return account.postbox.transaction { transaction -> InternalState in
|
||||
if state.canLoadMore {
|
||||
/*if state.canLoadMore && !matchesMode {
|
||||
return InternalState(
|
||||
totalCount: 0, totalReactedCount: 0, items: [], canLoadMore: true, nextOffset: state.nextOffset)
|
||||
}
|
||||
totalCount: listMode == .everyone ? state.totalCount : 100, totalReactedCount: state.totalReactedCount, items: [], canLoadMore: true, nextOffset: state.nextOffset)
|
||||
}*/
|
||||
|
||||
var items: [Item] = []
|
||||
switch listMode {
|
||||
@ -188,6 +193,7 @@ public final class EngineStoryViewListContext {
|
||||
})
|
||||
}
|
||||
|
||||
var totalCount = items.count
|
||||
var totalReactedCount = 0
|
||||
for item in items {
|
||||
if item.reaction != nil {
|
||||
@ -195,8 +201,17 @@ public final class EngineStoryViewListContext {
|
||||
}
|
||||
}
|
||||
|
||||
if state.canLoadMore {
|
||||
totalCount = state.totalCount
|
||||
totalReactedCount = state.totalReactedCount
|
||||
}
|
||||
|
||||
return InternalState(
|
||||
totalCount: items.count, totalReactedCount: totalReactedCount, items: items, canLoadMore: false)
|
||||
totalCount: totalCount,
|
||||
totalReactedCount: totalReactedCount,
|
||||
items: items,
|
||||
canLoadMore: state.canLoadMore
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,7 +222,7 @@ public final class EngineStoryViewListContext {
|
||||
self.updateInternalState(state: state)
|
||||
}))
|
||||
} else {
|
||||
let initialState = State(totalCount: views.seenCount, totalReactedCount: views.reactedCount, items: [], loadMoreToken: LoadMoreToken(value: ""))
|
||||
let initialState = State(totalCount: listMode == .everyone ? views.seenCount : 100, totalReactedCount: views.reactedCount, items: [], loadMoreToken: LoadMoreToken(value: ""))
|
||||
let state = InternalState(totalCount: initialState.totalCount, totalReactedCount: initialState.totalReactedCount, items: initialState.items, canLoadMore: initialState.loadMoreToken != nil, nextOffset: nil)
|
||||
self.state = state
|
||||
self.statePromise.set(.single(state))
|
||||
|
@ -1104,6 +1104,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
maxLength: Int(component.context.userLimits.maxStoryCaptionLength),
|
||||
queryTypes: [.mention],
|
||||
alwaysDarkWhenHasText: false,
|
||||
resetInputContents: nil,
|
||||
nextInputMode: { _ in return nextInputMode },
|
||||
areVoiceMessagesAvailable: false,
|
||||
presentController: { [weak self] c in
|
||||
|
@ -254,6 +254,7 @@ final class StoryPreviewComponent: Component {
|
||||
maxLength: nil,
|
||||
queryTypes: [],
|
||||
alwaysDarkWhenHasText: false,
|
||||
resetInputContents: nil,
|
||||
nextInputMode: { _ in return .stickers },
|
||||
areVoiceMessagesAvailable: false,
|
||||
presentController: { _ in },
|
||||
|
@ -100,6 +100,7 @@ public final class MessageInputPanelComponent: Component {
|
||||
public let maxLength: Int?
|
||||
public let queryTypes: ContextQueryTypes
|
||||
public let alwaysDarkWhenHasText: Bool
|
||||
public let resetInputContents: SendMessageInput?
|
||||
public let nextInputMode: (Bool) -> InputMode?
|
||||
public let areVoiceMessagesAvailable: Bool
|
||||
public let presentController: (ViewController) -> Void
|
||||
@ -149,6 +150,7 @@ public final class MessageInputPanelComponent: Component {
|
||||
maxLength: Int?,
|
||||
queryTypes: ContextQueryTypes,
|
||||
alwaysDarkWhenHasText: Bool,
|
||||
resetInputContents: SendMessageInput?,
|
||||
nextInputMode: @escaping (Bool) -> InputMode?,
|
||||
areVoiceMessagesAvailable: Bool,
|
||||
presentController: @escaping (ViewController) -> Void,
|
||||
@ -198,6 +200,7 @@ public final class MessageInputPanelComponent: Component {
|
||||
self.maxLength = maxLength
|
||||
self.queryTypes = queryTypes
|
||||
self.alwaysDarkWhenHasText = alwaysDarkWhenHasText
|
||||
self.resetInputContents = resetInputContents
|
||||
self.areVoiceMessagesAvailable = areVoiceMessagesAvailable
|
||||
self.presentController = presentController
|
||||
self.presentInGlobalOverlay = presentInGlobalOverlay
|
||||
@ -265,6 +268,9 @@ public final class MessageInputPanelComponent: Component {
|
||||
if lhs.alwaysDarkWhenHasText != rhs.alwaysDarkWhenHasText {
|
||||
return false
|
||||
}
|
||||
if lhs.resetInputContents != rhs.resetInputContents {
|
||||
return false
|
||||
}
|
||||
if lhs.areVoiceMessagesAvailable != rhs.areVoiceMessagesAvailable {
|
||||
return false
|
||||
}
|
||||
@ -334,7 +340,7 @@ public final class MessageInputPanelComponent: Component {
|
||||
return true
|
||||
}
|
||||
|
||||
public enum SendMessageInput {
|
||||
public enum SendMessageInput: Equatable {
|
||||
case text(NSAttributedString)
|
||||
}
|
||||
|
||||
@ -386,6 +392,8 @@ public final class MessageInputPanelComponent: Component {
|
||||
private var component: MessageInputPanelComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
private var pendingSetMessageInput: SendMessageInput?
|
||||
|
||||
public var likeButtonView: UIView? {
|
||||
return self.likeButton.view
|
||||
}
|
||||
@ -451,6 +459,17 @@ public final class MessageInputPanelComponent: Component {
|
||||
return .text(textFieldView.getAttributedText())
|
||||
}
|
||||
|
||||
public func setSendMessageInput(value: SendMessageInput, updateState: Bool) {
|
||||
if let textFieldView = self.textField.view as? TextFieldComponent.View {
|
||||
switch value {
|
||||
case let .text(text):
|
||||
textFieldView.setAttributedText(text, updateState: updateState)
|
||||
}
|
||||
} else {
|
||||
self.pendingSetMessageInput = value
|
||||
}
|
||||
}
|
||||
|
||||
public func getAttachmentButtonView() -> UIView? {
|
||||
guard let attachmentButtonView = self.attachmentButton.view else {
|
||||
return nil
|
||||
@ -458,9 +477,9 @@ public final class MessageInputPanelComponent: Component {
|
||||
return attachmentButtonView
|
||||
}
|
||||
|
||||
public func clearSendMessageInput() {
|
||||
public func clearSendMessageInput(updateState: Bool) {
|
||||
if let textFieldView = self.textField.view as? TextFieldComponent.View {
|
||||
textFieldView.setAttributedText(NSAttributedString())
|
||||
textFieldView.setAttributedText(NSAttributedString(), updateState: updateState)
|
||||
}
|
||||
}
|
||||
|
||||
@ -649,6 +668,12 @@ public final class MessageInputPanelComponent: Component {
|
||||
textColor: UIColor(rgb: 0xffffff),
|
||||
insets: UIEdgeInsets(top: 9.0, left: 8.0, bottom: 10.0, right: 48.0),
|
||||
hideKeyboard: component.hideKeyboard,
|
||||
resetText: component.resetInputContents.flatMap { resetInputContents in
|
||||
switch resetInputContents {
|
||||
case let .text(value):
|
||||
return value
|
||||
}
|
||||
},
|
||||
formatMenuAvailability: component.isFormattingLocked ? .locked : .available,
|
||||
lockedFormatAction: {
|
||||
component.presentTextFormattingTooltip?()
|
||||
@ -714,9 +739,12 @@ public final class MessageInputPanelComponent: Component {
|
||||
} else if isEditing {
|
||||
fieldBackgroundFrame = fieldFrame
|
||||
} else {
|
||||
fieldBackgroundFrame = CGRect(origin: CGPoint(x: mediaInsets.left, y: insets.top), size: CGSize(width: availableSize.width - mediaInsets.left - insets.right, height: textFieldSize.height))
|
||||
if component.likeAction != nil && component.forwardAction != nil {
|
||||
fieldBackgroundFrame.size.width -= 49.0
|
||||
if component.forwardAction != nil && component.likeAction != nil {
|
||||
fieldBackgroundFrame = CGRect(origin: CGPoint(x: mediaInsets.left, y: insets.top), size: CGSize(width: availableSize.width - mediaInsets.left - insets.right - 49.0, height: textFieldSize.height))
|
||||
} else if component.forwardAction != nil {
|
||||
fieldBackgroundFrame = CGRect(origin: CGPoint(x: mediaInsets.left, y: insets.top), size: CGSize(width: availableSize.width - mediaInsets.left - insets.right, height: textFieldSize.height))
|
||||
} else {
|
||||
fieldBackgroundFrame = CGRect(origin: CGPoint(x: mediaInsets.left, y: insets.top), size: CGSize(width: availableSize.width - mediaInsets.left - 50.0, height: textFieldSize.height))
|
||||
}
|
||||
}
|
||||
|
||||
@ -762,13 +790,21 @@ public final class MessageInputPanelComponent: Component {
|
||||
|
||||
let size = CGSize(width: availableSize.width, height: textFieldSize.height + insets.top + insets.bottom)
|
||||
|
||||
if let textFieldView = self.textField.view {
|
||||
if let textFieldView = self.textField.view as? TextFieldComponent.View {
|
||||
if textFieldView.superview == nil {
|
||||
self.addSubview(textFieldView)
|
||||
|
||||
if let viewForOverlayContent = self.viewForOverlayContent {
|
||||
self.addSubview(viewForOverlayContent)
|
||||
}
|
||||
|
||||
if let pendingSetMessageInput = self.pendingSetMessageInput {
|
||||
self.pendingSetMessageInput = nil
|
||||
switch pendingSetMessageInput {
|
||||
case let .text(text):
|
||||
textFieldView.setAttributedText(text, updateState: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
let textFieldFrame = CGRect(origin: CGPoint(x: fieldBackgroundFrame.minX, y: fieldBackgroundFrame.maxY - textFieldSize.height), size: textFieldSize)
|
||||
transition.setFrame(view: textFieldView, frame: textFieldFrame)
|
||||
@ -1168,7 +1204,10 @@ public final class MessageInputPanelComponent: Component {
|
||||
if likeButtonView.superview == nil {
|
||||
self.addSubview(likeButtonView)
|
||||
}
|
||||
let likeButtonFrame = CGRect(origin: CGPoint(x: inputActionButtonOriginX, y: size.height - insets.bottom - baseFieldHeight + floor((baseFieldHeight - likeButtonSize.height) * 0.5)), size: likeButtonSize)
|
||||
var likeButtonFrame = CGRect(origin: CGPoint(x: inputActionButtonOriginX, y: size.height - insets.bottom - baseFieldHeight + floor((baseFieldHeight - likeButtonSize.height) * 0.5)), size: likeButtonSize)
|
||||
if component.forwardAction == nil {
|
||||
likeButtonFrame.origin.x += 3.0
|
||||
}
|
||||
transition.setPosition(view: likeButtonView, position: likeButtonFrame.center)
|
||||
transition.setBounds(view: likeButtonView, bounds: CGRect(origin: CGPoint(), size: likeButtonFrame.size))
|
||||
transition.setAlpha(view: likeButtonView, alpha: displayLikeAction ? 1.0 : 0.0)
|
||||
|
@ -90,7 +90,6 @@ public final class NavigationSearchComponent: Component {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.backgroundView = UIView()
|
||||
self.backgroundView.layer.cornerRadius = 10.0
|
||||
|
||||
self.searchIconView = UIImageView(image: UIImage(bundleImageName: "Components/Search Bar/Loupe")?.withRenderingMode(.alwaysTemplate))
|
||||
|
||||
@ -183,6 +182,9 @@ public final class NavigationSearchComponent: Component {
|
||||
let searchIconSpacing: CGFloat = 4.0
|
||||
let buttonSpacing: CGFloat = 8.0
|
||||
|
||||
let contentAlphaDistance: CGFloat = 0.35
|
||||
let contentAlpha: CGFloat = 1.0 - max(0.0, min(1.0, component.collapseFraction / contentAlphaDistance))
|
||||
|
||||
let rightInset: CGFloat
|
||||
if component.isSearchActive {
|
||||
var buttonTransition = transition
|
||||
@ -235,8 +237,10 @@ public final class NavigationSearchComponent: Component {
|
||||
rightInset = sideInset
|
||||
}
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: floor((size.height - fieldHeight) * 0.5)), size: CGSize(width: availableSize.width - sideInset - rightInset, height: fieldHeight))
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: floor((size.height - fieldHeight) * 0.5)), size: CGSize(width: availableSize.width - sideInset - rightInset, height: fieldHeight * (1.0 - component.collapseFraction)))
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
|
||||
transition.setCornerRadius(layer: self.backgroundView.layer, cornerRadius: min(10.0, backgroundFrame.height * 0.5))
|
||||
if previousComponent?.colors.background != component.colors.background {
|
||||
self.backgroundView.backgroundColor = component.colors.background
|
||||
}
|
||||
@ -255,7 +259,7 @@ public final class NavigationSearchComponent: Component {
|
||||
let searchIconSize = self.searchIconView.image?.size ?? CGSize(width: 20.0, height: 20.0)
|
||||
//let searchPlaceholderCombinedWidth = searchIconSize.width + searchIconSpacing + placeholderSize.width
|
||||
|
||||
let placeholderTextFrame = CGRect(origin: CGPoint(x: component.isSearchActive ? (backgroundFrame.minX + fieldSideInset + searchIconSize.width + searchIconSpacing) : floor(backgroundFrame.midX - placeholderSize.width * 0.5), y: backgroundFrame.minY + floor((fieldHeight - placeholderSize.height) * 0.5)), size: placeholderSize)
|
||||
let placeholderTextFrame = CGRect(origin: CGPoint(x: component.isSearchActive ? (backgroundFrame.minX + fieldSideInset + searchIconSize.width + searchIconSpacing) : floor(backgroundFrame.midX - placeholderSize.width * 0.5), y: backgroundFrame.minY + (backgroundFrame.height - placeholderSize.height) * 0.5), size: placeholderSize)
|
||||
var placeholderDeltaX: CGFloat = 0.0
|
||||
if let placeholderTextView = self.placeholderText.view {
|
||||
if placeholderTextView.superview == nil {
|
||||
@ -267,14 +271,16 @@ public final class NavigationSearchComponent: Component {
|
||||
}
|
||||
transition.setPosition(view: placeholderTextView, position: placeholderTextFrame.origin)
|
||||
transition.setBounds(view: placeholderTextView, bounds: CGRect(origin: CGPoint(), size: placeholderTextFrame.size))
|
||||
transition.setAlpha(view: placeholderTextView, alpha: contentAlpha)
|
||||
}
|
||||
|
||||
let searchIconFrame = CGRect(origin: CGPoint(x: placeholderTextFrame.minX - searchIconSpacing - searchIconSize.width, y: backgroundFrame.minY + floor((fieldHeight - searchIconSize.height) * 0.5)), size: searchIconSize)
|
||||
let searchIconFrame = CGRect(origin: CGPoint(x: placeholderTextFrame.minX - searchIconSpacing - searchIconSize.width, y: backgroundFrame.minY + (backgroundFrame.height - searchIconSize.height) * 0.5), size: searchIconSize)
|
||||
transition.setFrame(view: self.searchIconView, frame: searchIconFrame)
|
||||
transition.setAlpha(view: self.searchIconView, alpha: contentAlpha)
|
||||
|
||||
if let image = self.clearIconView.image {
|
||||
let clearSize = image.size
|
||||
let clearFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - 6.0 - clearSize.width, y: backgroundFrame.minY + floor((backgroundFrame.height - clearSize.height) / 2.0)), size: clearSize)
|
||||
let clearFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - 6.0 - clearSize.width, y: backgroundFrame.minY + (backgroundFrame.height - clearSize.height) / 2.0), size: clearSize)
|
||||
|
||||
let clearButtonFrame = CGRect(origin: CGPoint(x: clearFrame.minX - 4.0, y: backgroundFrame.minY), size: CGSize(width: clearFrame.width + 8.0, height: backgroundFrame.height))
|
||||
transition.setFrame(view: self.clearButton, frame: clearButtonFrame)
|
||||
|
@ -964,9 +964,6 @@ private final class StoryContainerScreenComponent: Component {
|
||||
}
|
||||
|
||||
self.didDisplayReactionTooltip = true
|
||||
#if !DEBUG
|
||||
let _ = ApplicationSpecificNotice.setDisplayStoryReactionTooltip(accountManager: component.context.sharedContext.accountManager).start()
|
||||
#endif
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -1558,9 +1555,6 @@ private final class StoryContainerScreenComponent: Component {
|
||||
itemSetTransition.setPosition(view: itemSetView, position: itemFrame.center.offsetBy(dx: 0.0, dy: dismissPanOffset))
|
||||
itemSetTransition.setBounds(view: itemSetView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size))
|
||||
|
||||
itemSetTransition.setPosition(view: itemSetComponentView.transitionCloneContainerView, position: itemFrame.center.offsetBy(dx: 0.0, dy: dismissPanOffset))
|
||||
itemSetTransition.setBounds(view: itemSetComponentView.transitionCloneContainerView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size))
|
||||
|
||||
itemSetTransition.setPosition(view: itemSetComponentView, position: CGRect(origin: CGPoint(), size: itemFrame.size).center)
|
||||
itemSetTransition.setBounds(view: itemSetComponentView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size))
|
||||
|
||||
|
@ -30,6 +30,8 @@ public final class StoryContentItem: Equatable {
|
||||
}
|
||||
|
||||
public final class SharedState {
|
||||
public var replyDrafts: [StoryId: NSAttributedString] = [:]
|
||||
|
||||
public init() {
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import Speak
|
||||
import TranslateUI
|
||||
import TelegramUIPreferences
|
||||
import StoryFooterPanelComponent
|
||||
import TelegramNotices
|
||||
|
||||
public final class StoryAvailableReactions: Equatable {
|
||||
let reactionItems: [ReactionItem]
|
||||
@ -349,6 +350,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
var startContentOffsetY: CGFloat = 0.0
|
||||
var accumulatedOffset: CGFloat = 0.0
|
||||
var dismissedTooltips: Bool = false
|
||||
var didLockScrolling: Bool = false
|
||||
var contentOffset: CGFloat?
|
||||
|
||||
init(fraction: CGFloat, scrollView: UIScrollView?) {
|
||||
self.fraction = fraction
|
||||
@ -423,6 +426,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
|
||||
var viewListDisplayState: ViewListDisplayState = .hidden
|
||||
private var targetViewListDisplayStateIsFull: Bool = false
|
||||
private var viewListMetrics: (minHeight: CGFloat, maxHeight: CGFloat, currentHeight: CGFloat)?
|
||||
|
||||
var isSearchActive: Bool = false
|
||||
|
||||
@ -1014,7 +1018,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
verticalPanState.fraction = fraction
|
||||
} else {
|
||||
var targetScrollView: UIScrollView?
|
||||
if self.viewListDisplayState != .hidden, let viewList = self.viewLists[component.slice.item.storyItem.id], let viewListView = viewList.view.view as? StoryItemSetViewListComponent.View {
|
||||
if case .began = recognizer.state, self.viewListDisplayState != .hidden, let viewList = self.viewLists[component.slice.item.storyItem.id], let viewListView = viewList.view.view as? StoryItemSetViewListComponent.View {
|
||||
if let hitResult = viewListView.hitTest(self.convert(recognizer.location(in: self), to: viewListView), with: nil) {
|
||||
func findTargetScrollView(target: UIView, minParent: UIView) -> UIScrollView? {
|
||||
if target === viewListView {
|
||||
@ -1041,14 +1045,42 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
self.dismissAllTooltips()
|
||||
}
|
||||
|
||||
if let scrollView = verticalPanState.scrollView {
|
||||
if let viewListMetrics = self.viewListMetrics, let scrollView = verticalPanState.scrollView {
|
||||
let relativeTranslationY = recognizer.translation(in: self).y - verticalPanState.startContentOffsetY
|
||||
let overflowY = scrollView.contentOffset.y - relativeTranslationY
|
||||
|
||||
verticalPanState.accumulatedOffset += -overflowY
|
||||
verticalPanState.accumulatedOffset = max(0.0, verticalPanState.accumulatedOffset)
|
||||
if !verticalPanState.didLockScrolling {
|
||||
if scrollView.contentOffset.y == 0.0 {
|
||||
verticalPanState.didLockScrolling = true
|
||||
}
|
||||
if let previousContentOffset = verticalPanState.contentOffset, (previousContentOffset < 0.0) != (scrollView.contentOffset.y < 0.0) {
|
||||
verticalPanState.didLockScrolling = true
|
||||
}
|
||||
}
|
||||
|
||||
if verticalPanState.accumulatedOffset > 0.0 {
|
||||
var resetContentOffset = false
|
||||
if self.viewListDisplayState == .half && verticalPanState.didLockScrolling {
|
||||
verticalPanState.accumulatedOffset += -overflowY
|
||||
if scrollView.contentOffset.y > 0.0 {
|
||||
//verticalPanState.accumulatedOffset = max(0.0, verticalPanState.accumulatedOffset)
|
||||
}
|
||||
|
||||
let viewListHeight = viewListMetrics.minHeight - verticalPanState.accumulatedOffset
|
||||
let viewListExpandDistance = viewListHeight - viewListMetrics.maxHeight
|
||||
let viewListOffsetOverflow = max(0.0, viewListExpandDistance)
|
||||
resetContentOffset = viewListOffsetOverflow <= 0.0
|
||||
//print("viewListOffsetOverflow: \(viewListOffsetOverflow)")
|
||||
|
||||
if verticalPanState.accumulatedOffset < -(viewListMetrics.maxHeight - viewListMetrics.minHeight) {
|
||||
verticalPanState.accumulatedOffset = -(viewListMetrics.maxHeight - viewListMetrics.minHeight)
|
||||
}
|
||||
} else {
|
||||
verticalPanState.accumulatedOffset += -overflowY
|
||||
verticalPanState.accumulatedOffset = max(0.0, verticalPanState.accumulatedOffset)
|
||||
}
|
||||
//print("accumulatedOffset: \(verticalPanState.accumulatedOffset)")
|
||||
|
||||
if verticalPanState.accumulatedOffset > 0.0 || resetContentOffset {
|
||||
scrollView.contentOffset = CGPoint()
|
||||
|
||||
if self.viewListDisplayState != .hidden, let viewList = self.viewLists[component.slice.item.storyItem.id], let viewListView = viewList.view.view as? StoryItemSetViewListComponent.View {
|
||||
@ -1065,6 +1097,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
verticalPanState.contentOffset = scrollView.contentOffset.y
|
||||
verticalPanState.startContentOffsetY = recognizer.translation(in: self).y
|
||||
} else {
|
||||
if verticalPanState.fraction <= -0.15 {
|
||||
@ -1099,8 +1132,15 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||
} else if verticalPanState.accumulatedOffset < 0.0 && self.targetViewListDisplayStateIsFull {
|
||||
if verticalPanState.fraction <= -0.05 || velocity.y <= -80.0 {
|
||||
self.viewListDisplayState = .full
|
||||
} else {
|
||||
self.viewListDisplayState = .half
|
||||
}
|
||||
self.state?.updated(transition: verticalPanState.accumulatedOffset == 0.0 ? .immediate : Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||
} else {
|
||||
self.state?.updated(transition: .immediate)
|
||||
self.state?.updated(transition: verticalPanState.accumulatedOffset == 0.0 ? .immediate : Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||
}
|
||||
} else {
|
||||
if verticalPanState.fraction >= 0.3 || (verticalPanState.fraction >= 0.05 && velocity.y >= 150.0) {
|
||||
@ -1654,9 +1694,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
footerAlpha = 0.0
|
||||
}
|
||||
|
||||
if case .regular = component.metrics.widthClass {
|
||||
footerAlpha *= itemAlpha
|
||||
}
|
||||
footerAlpha *= itemAlpha
|
||||
|
||||
itemTransition.setAlpha(view: footerPanelView, alpha: footerAlpha)
|
||||
}
|
||||
@ -1983,7 +2021,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
let contentContainerView = visibleItem.contentContainerView
|
||||
let unclippedContainerView = visibleItem.unclippedContainerView
|
||||
|
||||
let sourceLocalFrame = sourceView.convert(transitionOut.destinationRect, to: self)
|
||||
let sourceLocalFrame = sourceView.convert(transitionOut.destinationRect, to: self.componentContainerView)
|
||||
let innerSourceLocalFrame = CGRect(origin: CGPoint(x: sourceLocalFrame.minX - contentContainerView.frame.minX, y: sourceLocalFrame.minY - contentContainerView.frame.minY), size: sourceLocalFrame.size)
|
||||
|
||||
let contentSourceFrame = contentContainerView.frame
|
||||
@ -2280,6 +2318,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
self.sendMessageContext.tooltipScreen = tooltipScreen
|
||||
self.updateIsProgressPaused()
|
||||
component.controller()?.present(tooltipScreen, in: .current)
|
||||
|
||||
let _ = ApplicationSpecificNotice.setDisplayStoryReactionTooltip(accountManager: component.context.sharedContext.accountManager).start()
|
||||
}
|
||||
|
||||
func update(component: StoryItemSetContainerComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
@ -2287,17 +2327,46 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
|
||||
if self.component == nil {
|
||||
self.sendMessageContext.setup(context: component.context, view: self, inputPanelExternalState: self.inputPanelExternalState, keyboardInputData: component.keyboardInputData)
|
||||
|
||||
/*#if DEBUG
|
||||
class Target: NSObject {
|
||||
let f: () -> Void
|
||||
|
||||
init(_ f: @escaping () -> Void) {
|
||||
self.f = f
|
||||
}
|
||||
|
||||
@objc func event() {
|
||||
self.f()
|
||||
}
|
||||
}
|
||||
let displayLink = CADisplayLink(target: Target { [weak self] in
|
||||
self?.state?.updated(transition: .easeInOut(duration: 0.3))
|
||||
}, selector: #selector(Target.event))
|
||||
displayLink.add(to: .main, forMode: .common)
|
||||
#endif*/
|
||||
}
|
||||
|
||||
var itemChanged = false
|
||||
var resetInputContents: MessageInputPanelComponent.SendMessageInput?
|
||||
if self.component?.slice.item.storyItem.id != component.slice.item.storyItem.id {
|
||||
itemChanged = self.component != nil
|
||||
self.initializedOffset = false
|
||||
|
||||
resetInputContents = .text(NSAttributedString())
|
||||
|
||||
if let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View {
|
||||
Queue.mainQueue().justDispatch {
|
||||
inputPanelView.clearSendMessageInput()
|
||||
if let previousComponent = self.component {
|
||||
let previousInput = inputPanelView.getSendMessageInput()
|
||||
switch previousInput {
|
||||
case let .text(value):
|
||||
component.storyItemSharedState.replyDrafts[StoryId(peerId: previousComponent.slice.peer.id, id: previousComponent.slice.item.storyItem.id)] = value
|
||||
}
|
||||
}
|
||||
if let draft = component.storyItemSharedState.replyDrafts[StoryId(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id)] {
|
||||
resetInputContents = .text(draft)
|
||||
}
|
||||
component.storyItemSharedState.replyDrafts.removeValue(forKey: StoryId(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id))
|
||||
}
|
||||
|
||||
if let tooltipScreen = self.sendMessageContext.tooltipScreen {
|
||||
@ -2405,6 +2474,9 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
transition.setBounds(view: self.componentContainerView, bounds: CGRect(origin: CGPoint(), size: availableSize))
|
||||
transition.setScale(view: self.componentContainerView, scale: dismissPanScale)
|
||||
|
||||
transition.setPosition(view: self.transitionCloneContainerView, position: CGRect(origin: CGPoint(), size: availableSize).center.offsetBy(dx: 0.0, dy: dismissPanOffset))
|
||||
transition.setBounds(view: self.transitionCloneContainerView, bounds: CGRect(origin: CGPoint(), size: availableSize))
|
||||
|
||||
transition.setPosition(view: self.overlayContainerView, position: CGPoint(x: availableSize.width * 0.5, y: availableSize.height * 0.5 + dismissPanOffset))
|
||||
transition.setBounds(view: self.overlayContainerView, bounds: CGRect(origin: CGPoint(), size: availableSize))
|
||||
transition.setScale(view: self.overlayContainerView, scale: dismissPanScale)
|
||||
@ -2428,6 +2500,9 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
}
|
||||
}
|
||||
if itemChanged {
|
||||
inputPanelTransition = .immediate
|
||||
}
|
||||
|
||||
var isUnsupported = false
|
||||
var disabledPlaceholder: String?
|
||||
@ -2473,6 +2548,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
maxLength: 4096,
|
||||
queryTypes: [.mention, .emoji],
|
||||
alwaysDarkWhenHasText: component.metrics.widthClass == .regular,
|
||||
resetInputContents: resetInputContents,
|
||||
nextInputMode: { [weak self] hasText in
|
||||
if case .media = self?.sendMessageContext.currentInputMode {
|
||||
return .text
|
||||
@ -2834,6 +2910,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
viewListHeight = max(minViewListHeight, min(maxViewListHeight, viewListHeight))
|
||||
|
||||
self.targetViewListDisplayStateIsFull = viewListHeight > midViewListHeight
|
||||
self.viewListMetrics = (midViewListHeight, maxViewListHeight, viewListHeight)
|
||||
|
||||
let viewListHeightMidFraction: CGFloat = max(0.0, min(1.0, viewListHeight / midViewListHeight))
|
||||
viewListInset = defaultHeight * (1.0 - viewListHeightMidFraction) + viewListHeightMidFraction * midViewListHeight
|
||||
@ -3219,6 +3296,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.viewListMetrics = nil
|
||||
}
|
||||
var removeViewListIds: [Int32] = []
|
||||
for (id, viewList) in self.viewLists {
|
||||
|
@ -601,7 +601,8 @@ final class StoryItemSetContainerSendMessage {
|
||||
}
|
||||
}
|
||||
})
|
||||
inputPanelView.clearSendMessageInput()
|
||||
component.storyItemSharedState.replyDrafts.removeValue(forKey: StoryId(peerId: peerId, id: focusedItem.storyItem.id))
|
||||
inputPanelView.clearSendMessageInput(updateState: true)
|
||||
|
||||
self.currentInputMode = .text
|
||||
if hasFirstResponder(view) {
|
||||
@ -1259,7 +1260,16 @@ final class StoryItemSetContainerSendMessage {
|
||||
guard let inputPanelView = view.inputPanel.view as? MessageInputPanelComponent.View else {
|
||||
return
|
||||
}
|
||||
inputPanelView.clearSendMessageInput()
|
||||
inputPanelView.clearSendMessageInput(updateState: true)
|
||||
|
||||
guard let component = view.component else {
|
||||
return
|
||||
}
|
||||
let focusedItem = component.slice.item
|
||||
guard let peerId = focusedItem.peerId else {
|
||||
return
|
||||
}
|
||||
component.storyItemSharedState.replyDrafts.removeValue(forKey: StoryId(peerId: peerId, id: focusedItem.storyItem.id))
|
||||
}
|
||||
|
||||
enum AttachMenuSubject {
|
||||
|
@ -159,23 +159,27 @@ final class StoryItemSetViewListComponent: Component {
|
||||
|
||||
private struct ItemLayout: Equatable {
|
||||
var containerSize: CGSize
|
||||
var availableSize: CGSize
|
||||
var bottomInset: CGFloat
|
||||
var topInset: CGFloat
|
||||
var sideInset: CGFloat
|
||||
var itemHeight: CGFloat
|
||||
var itemCount: Int
|
||||
var premiumFooterSize: CGSize?
|
||||
var isSearchActive: Bool
|
||||
|
||||
var contentSize: CGSize
|
||||
|
||||
init(containerSize: CGSize, bottomInset: CGFloat, topInset: CGFloat, sideInset: CGFloat, itemHeight: CGFloat, itemCount: Int, premiumFooterSize: CGSize?) {
|
||||
init(containerSize: CGSize, availableSize: CGSize, bottomInset: CGFloat, topInset: CGFloat, sideInset: CGFloat, itemHeight: CGFloat, itemCount: Int, premiumFooterSize: CGSize?, isSearchActive: Bool) {
|
||||
self.containerSize = containerSize
|
||||
self.availableSize = availableSize
|
||||
self.bottomInset = bottomInset
|
||||
self.topInset = topInset
|
||||
self.sideInset = sideInset
|
||||
self.itemHeight = itemHeight
|
||||
self.itemCount = itemCount
|
||||
self.premiumFooterSize = premiumFooterSize
|
||||
self.isSearchActive = isSearchActive
|
||||
|
||||
self.contentSize = CGSize(width: containerSize.width, height: topInset + CGFloat(itemCount) * itemHeight + bottomInset)
|
||||
if let premiumFooterSize {
|
||||
@ -245,6 +249,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
let configuration: ContentConfigurationKey
|
||||
var query: String?
|
||||
|
||||
var stateComponent: StoryItemSetViewListComponent?
|
||||
var component: StoryItemSetViewListComponent?
|
||||
weak var state: EmptyComponentState?
|
||||
|
||||
@ -284,6 +289,15 @@ final class StoryItemSetViewListComponent: Component {
|
||||
|
||||
var dismissInput: (() -> Void)?
|
||||
|
||||
var navigationSearch: ComponentView<Empty>?
|
||||
var updateQuery: ((String) -> Void)?
|
||||
var navigationBarBackground: BlurredBackgroundView?
|
||||
var navigationSeparator: SimpleLayer?
|
||||
var backgroundView: UIView?
|
||||
|
||||
var navigationHeight: CGFloat?
|
||||
var navigationSearchPartHeight: CGFloat?
|
||||
|
||||
init(configuration: ContentConfigurationKey) {
|
||||
self.configuration = configuration
|
||||
|
||||
@ -329,6 +343,28 @@ final class StoryItemSetViewListComponent: Component {
|
||||
if eventCycleState.ignoreScrolling {
|
||||
targetContentOffset.pointee.y = 0.0
|
||||
}
|
||||
} else {
|
||||
if let navigationSearchPartHeight = self.navigationSearchPartHeight, navigationSearchPartHeight > 0.0 {
|
||||
if targetContentOffset.pointee.y < navigationSearchPartHeight {
|
||||
if targetContentOffset.pointee.y < navigationSearchPartHeight * 0.5 {
|
||||
targetContentOffset.pointee.y = 0.0
|
||||
} else {
|
||||
targetContentOffset.pointee.y = navigationSearchPartHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
if let navigationSearchPartHeight = self.navigationSearchPartHeight, navigationSearchPartHeight > 0.0 {
|
||||
if scrollView.contentOffset.y < navigationSearchPartHeight {
|
||||
if scrollView.contentOffset.y < navigationSearchPartHeight * 0.5 {
|
||||
scrollView.setContentOffset(CGPoint(), animated: true)
|
||||
} else {
|
||||
scrollView.setContentOffset(CGPoint(x: 0.0, y: navigationSearchPartHeight), animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,7 +379,8 @@ final class StoryItemSetViewListComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
let visibleBounds = self.scrollView.bounds.insetBy(dx: 0.0, dy: -200.0)
|
||||
let actualBounds = self.scrollView.bounds
|
||||
let visibleBounds = actualBounds.insetBy(dx: 0.0, dy: -200.0)
|
||||
|
||||
var synchronousLoad = false
|
||||
if let hint = transition.userData(PeerListItemComponent.TransitionHint.self) {
|
||||
@ -538,15 +575,80 @@ final class StoryItemSetViewListComponent: Component {
|
||||
viewList.loadMore()
|
||||
}
|
||||
}
|
||||
|
||||
let navigationSearchCollapseFraction: CGFloat
|
||||
let navigationSearchFieldCollapseFraction: CGFloat
|
||||
if itemLayout.isSearchActive {
|
||||
navigationSearchCollapseFraction = 0.0
|
||||
navigationSearchFieldCollapseFraction = 0.0
|
||||
} else if let navigationSearchPartHeight = self.navigationSearchPartHeight, navigationSearchPartHeight > 8.0 {
|
||||
let searchCollapseDistance: CGFloat = navigationSearchPartHeight
|
||||
navigationSearchCollapseFraction = max(0.0, min(1.0, actualBounds.minY / searchCollapseDistance))
|
||||
|
||||
let searchFieldCollapseDistance: CGFloat = navigationSearchPartHeight - 8.0
|
||||
navigationSearchFieldCollapseFraction = max(0.0, min(1.0, actualBounds.minY / searchFieldCollapseDistance))
|
||||
} else {
|
||||
navigationSearchCollapseFraction = 1.0
|
||||
navigationSearchFieldCollapseFraction = 1.0
|
||||
}
|
||||
|
||||
if let navigationSearch = self.navigationSearch {
|
||||
let _ = navigationSearch.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(NavigationSearchComponent(
|
||||
colors: NavigationSearchComponent.Colors(
|
||||
background: UIColor(white: 1.0, alpha: 0.05),
|
||||
inactiveForeground: UIColor(rgb: 0x8E8E93),
|
||||
foreground: .white,
|
||||
button: component.theme.rootController.navigationBar.accentTextColor
|
||||
),
|
||||
cancel: component.strings.Common_Cancel,
|
||||
placeholder: component.strings.Common_Search,
|
||||
isSearchActive: component.isSearchActive,
|
||||
collapseFraction: navigationSearchFieldCollapseFraction,
|
||||
activateSearch: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.setIsSearchActive(true)
|
||||
},
|
||||
deactivateSearch: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.setIsSearchActive(false)
|
||||
},
|
||||
updateQuery: { [weak self] query in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.updateQuery?(query)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: itemLayout.containerSize.width - component.safeInsets.left - component.safeInsets.right, height: 100.0)
|
||||
)
|
||||
}
|
||||
|
||||
if let navigationHeight = self.navigationHeight, let navigationSearchPartHeight = self.navigationSearchPartHeight, let navigationBarBackground = self.navigationBarBackground, let navigationSeparator = self.navigationSeparator, let backgroundView = self.backgroundView {
|
||||
let effectiveNavigationHeight = navigationHeight - navigationSearchPartHeight * navigationSearchCollapseFraction
|
||||
let navigationBarBackgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: itemLayout.containerSize.width, height: effectiveNavigationHeight))
|
||||
|
||||
transition.setFrame(view: navigationBarBackground, frame: navigationBarBackgroundFrame)
|
||||
navigationBarBackground.update(size: navigationBarBackgroundFrame.size, cornerRadius: 10.0, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition.containedViewLayoutTransition)
|
||||
transition.setFrame(layer: navigationSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarBackgroundFrame.height - UIScreenPixel), size: CGSize(width: navigationBarBackgroundFrame.width, height: UIScreenPixel)))
|
||||
|
||||
let navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: itemLayout.availableSize.height - itemLayout.containerSize.height + 12.0), size: CGSize(width: itemLayout.availableSize.width, height: effectiveNavigationHeight))
|
||||
transition.setFrame(view: backgroundView, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarFrame.maxY), size: CGSize(width: itemLayout.availableSize.width, height: itemLayout.availableSize.height)))
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: StoryItemSetViewListComponent, state: EmptyComponentState?, baseContentView: ContentView?, query: String?, availableSize: CGSize, visualHeight: CGFloat, sideInset: CGFloat, navigationHeight: CGFloat, transition: Transition) {
|
||||
let themeUpdated = self.component?.theme !== component.theme
|
||||
let itemUpdated = self.component?.storyItem.id != component.storyItem.id
|
||||
let viewsNilUpdated = (self.component?.storyItem.views == nil) != (component.storyItem.views == nil)
|
||||
func updateState(component: StoryItemSetViewListComponent, state: EmptyComponentState?, baseContentView: ContentView?, query: String?) {
|
||||
let itemUpdated = self.stateComponent?.storyItem.id != component.storyItem.id
|
||||
let viewsNilUpdated = (self.stateComponent?.storyItem.views == nil) != (component.storyItem.views == nil)
|
||||
let queryUpdated = self.query != query
|
||||
|
||||
self.component = component
|
||||
self.stateComponent = component
|
||||
self.state = state
|
||||
self.query = query
|
||||
|
||||
@ -627,11 +729,6 @@ final class StoryItemSetViewListComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
var synchronous = false
|
||||
if let animationHint = transition.userData(AnimationHint.self) {
|
||||
synchronous = animationHint.synchronous
|
||||
}
|
||||
|
||||
if itemUpdated || viewsNilUpdated || queryUpdated {
|
||||
self.viewListDisposable?.dispose()
|
||||
|
||||
@ -693,7 +790,6 @@ final class StoryItemSetViewListComponent: Component {
|
||||
}
|
||||
})
|
||||
applyState = true
|
||||
let _ = synchronous
|
||||
} else {
|
||||
if let _ = component.storyItem.views {
|
||||
} else {
|
||||
@ -708,10 +804,21 @@ final class StoryItemSetViewListComponent: Component {
|
||||
hasContent = true
|
||||
}
|
||||
self.hasContent = hasContent
|
||||
self.contentLoaded = true
|
||||
if self.contentLoaded != true {
|
||||
self.contentLoaded = true
|
||||
self.contentLoadedUpdated?(self.contentLoaded)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: StoryItemSetViewListComponent, availableSize: CGSize, visualHeight: CGFloat, sideInset: CGFloat, navigationHeight: CGFloat, navigationSearchPartHeight: CGFloat, isSearchActive: Bool, transition: Transition) {
|
||||
let themeUpdated = self.component?.theme !== component.theme
|
||||
self.component = component
|
||||
|
||||
self.navigationHeight = navigationHeight
|
||||
self.navigationSearchPartHeight = navigationSearchPartHeight
|
||||
|
||||
let measureItemSize = self.measureItem.update(
|
||||
transition: .immediate,
|
||||
@ -818,12 +925,14 @@ final class StoryItemSetViewListComponent: Component {
|
||||
|
||||
let itemLayout = ItemLayout(
|
||||
containerSize: CGSize(width: availableSize.width, height: visualHeight),
|
||||
availableSize: availableSize,
|
||||
bottomInset: component.safeInsets.bottom,
|
||||
topInset: navigationHeight,
|
||||
sideInset: sideInset,
|
||||
itemHeight: measureItemSize.height,
|
||||
itemCount: self.viewListState?.items.count ?? 0,
|
||||
premiumFooterSize: premiumFooterSize
|
||||
premiumFooterSize: premiumFooterSize,
|
||||
isSearchActive: isSearchActive
|
||||
)
|
||||
self.itemLayout = itemLayout
|
||||
|
||||
@ -1087,6 +1196,10 @@ final class StoryItemSetViewListComponent: Component {
|
||||
private let title = ComponentView<Empty>()
|
||||
private let orderSelector = ComponentView<Empty>()
|
||||
|
||||
private var mainViewListDisposable: Disposable?
|
||||
private var mainViewList: EngineStoryViewListContext?
|
||||
private var mainViewListState: EngineStoryViewListContext.State?
|
||||
|
||||
private var currentContentView: ContentView?
|
||||
private weak var disappearingCurrentContentView: ContentView?
|
||||
|
||||
@ -1115,8 +1228,8 @@ final class StoryItemSetViewListComponent: Component {
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
|
||||
self.addSubview(self.navigationBarBackground)
|
||||
self.layer.addSublayer(self.navigationSeparator)
|
||||
self.navigationContainerView.addSubview(self.navigationBarBackground)
|
||||
self.navigationContainerView.layer.addSublayer(self.navigationSeparator)
|
||||
self.addSubview(self.navigationContainerView)
|
||||
}
|
||||
|
||||
@ -1125,10 +1238,11 @@ final class StoryItemSetViewListComponent: Component {
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.mainViewListDisposable?.dispose()
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.backgroundView.frame.contains(point) && !self.navigationBarBackground.frame.contains(point) {
|
||||
if !self.backgroundView.frame.contains(point) && !self.navigationContainerView.frame.contains(point) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1245,12 +1359,108 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.currentSearchQuery = ""
|
||||
}
|
||||
|
||||
var updateSubState = false
|
||||
|
||||
if self.mainViewList == nil {
|
||||
self.mainViewListDisposable?.dispose()
|
||||
self.mainViewListDisposable = nil
|
||||
|
||||
if let views = component.storyItem.views {
|
||||
let viewList: EngineStoryViewListContext
|
||||
if let current = component.sharedListsContext.viewLists[StoryId(peerId: component.peerId, id: component.storyItem.id)] {
|
||||
viewList = current
|
||||
} else {
|
||||
viewList = component.context.engine.messages.storyViewList(id: component.storyItem.id, views: views, listMode: .everyone, sortMode: .reactionsFirst)
|
||||
component.sharedListsContext.viewLists[StoryId(peerId: component.peerId, id: component.storyItem.id)] = viewList
|
||||
}
|
||||
self.mainViewList = viewList
|
||||
self.mainViewListDisposable = (viewList.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] listState in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.mainViewListState = listState
|
||||
|
||||
if updateSubState {
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
self.mainViewList = nil
|
||||
self.mainViewListState = nil
|
||||
}
|
||||
}
|
||||
|
||||
let currentConfiguration = ContentConfigurationKey(listMode: self.listMode, sortMode: self.sortMode)
|
||||
if self.currentContentView?.configuration != currentConfiguration {
|
||||
let previousContentView = self.currentContentView
|
||||
self.disappearingCurrentContentView?.removeFromSuperview()
|
||||
self.disappearingCurrentContentView = self.currentContentView
|
||||
self.currentContentView = nil
|
||||
|
||||
let currentContentView = ContentView(configuration: currentConfiguration)
|
||||
self.currentContentView = currentContentView
|
||||
currentContentView.updateQuery = { [weak self] query in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if self.currentSearchQuery != query {
|
||||
self.currentSearchQuery = query
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
}
|
||||
currentContentView.isHidden = true
|
||||
currentContentView.contentLoadedUpdated = { [weak self, weak currentContentView, weak previousContentView] value in
|
||||
guard value, let self, let currentContentView else {
|
||||
return
|
||||
}
|
||||
currentContentView.isHidden = false
|
||||
if let previousContentView {
|
||||
previousContentView.removeFromSuperview()
|
||||
if self.disappearingCurrentContentView === previousContentView {
|
||||
self.disappearingCurrentContentView = nil
|
||||
}
|
||||
}
|
||||
|
||||
currentContentView.navigationSearch = self.navigationSearch
|
||||
currentContentView.navigationBarBackground = self.navigationBarBackground
|
||||
currentContentView.navigationSeparator = self.navigationSeparator
|
||||
currentContentView.backgroundView = self.backgroundView
|
||||
if updateSubState {
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let currentContentView = self.currentContentView {
|
||||
var contentViewTransition = transition
|
||||
if currentContentView.superview == nil {
|
||||
contentViewTransition = contentViewTransition.withAnimation(.none)
|
||||
self.insertSubview(currentContentView, belowSubview: self.navigationContainerView)
|
||||
}
|
||||
|
||||
contentViewTransition.setFrame(view: currentContentView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
currentContentView.updateState(
|
||||
component: component,
|
||||
state: state,
|
||||
baseContentView: nil,
|
||||
query: nil
|
||||
)
|
||||
if currentContentView.contentLoaded {
|
||||
currentContentView.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
|
||||
let visualHeight: CGFloat = max(component.minHeight, component.effectiveHeight)
|
||||
|
||||
var tabSelectorTransition = transition
|
||||
if transition.animation.isImmediate, self.tabSelector.view != nil {
|
||||
tabSelectorTransition = Transition(animation: .curve(duration: 0.35, curve: .spring))
|
||||
}
|
||||
let tabSelectorSize = self.tabSelector.update(
|
||||
transition: transition,
|
||||
transition: tabSelectorTransition,
|
||||
component: AnyComponent(TabSelectorComponent(
|
||||
colors: TabSelectorComponent.Colors(
|
||||
foreground: .white,
|
||||
@ -1273,7 +1483,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
}
|
||||
if self.listMode != listMode {
|
||||
self.listMode = listMode
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.35, curve: .spring)))
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
}
|
||||
)),
|
||||
@ -1281,10 +1491,20 @@ final class StoryItemSetViewListComponent: Component {
|
||||
containerSize: CGSize(width: availableSize.width - 10.0 * 2.0, height: 50.0)
|
||||
)
|
||||
|
||||
var currentTotalCount: Int?
|
||||
var currentTotalReactionCount: Int?
|
||||
if let mainViewListState = self.mainViewListState {
|
||||
currentTotalCount = mainViewListState.totalCount
|
||||
currentTotalReactionCount = mainViewListState.totalReactedCount
|
||||
} else {
|
||||
currentTotalCount = component.storyItem.views?.seenCount
|
||||
currentTotalReactionCount = component.storyItem.views?.reactedCount
|
||||
}
|
||||
|
||||
let titleText: String
|
||||
if let views = component.storyItem.views, views.seenCount != 0 {
|
||||
if component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) {
|
||||
titleText = component.strings.Story_ViewList_ViewerCount(Int32(views.seenCount))
|
||||
if let totalCount = currentTotalCount, let currentTotalReactionCount {
|
||||
if totalCount > 0 && totalCount > currentTotalReactionCount {
|
||||
titleText = component.strings.Story_ViewList_ViewerCount(Int32(totalCount))
|
||||
} else {
|
||||
titleText = component.strings.Story_ViewList_TitleViewers
|
||||
}
|
||||
@ -1319,44 +1539,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
|
||||
let navigationSearchSize = self.navigationSearch.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(NavigationSearchComponent(
|
||||
colors: NavigationSearchComponent.Colors(
|
||||
background: UIColor(white: 1.0, alpha: 0.05),
|
||||
inactiveForeground: UIColor(rgb: 0x8E8E93),
|
||||
foreground: .white,
|
||||
button: component.theme.rootController.navigationBar.accentTextColor
|
||||
),
|
||||
cancel: component.strings.Common_Cancel,
|
||||
placeholder: component.strings.Common_Search,
|
||||
isSearchActive: component.isSearchActive,
|
||||
collapseFraction: 1.0,
|
||||
activateSearch: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.setIsSearchActive(true)
|
||||
},
|
||||
deactivateSearch: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.setIsSearchActive(false)
|
||||
},
|
||||
updateQuery: { [weak self] query in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if self.currentSearchQuery != query {
|
||||
self.currentSearchQuery = query
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - component.safeInsets.left - component.safeInsets.right, height: 100.0)
|
||||
)
|
||||
let navigationSearchSize = CGSize(width: availableSize.width - component.safeInsets.left - component.safeInsets.right, height: 52.0)
|
||||
|
||||
var displayModeSelector = false
|
||||
var displaySearchBar = false
|
||||
@ -1365,7 +1548,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
if !component.hasPremium, component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) {
|
||||
} else {
|
||||
if let views = component.storyItem.views, views.hasList {
|
||||
if let currentContentView = self.currentContentView, let totalCount = currentContentView.totalCount {
|
||||
if let totalCount = currentTotalCount {
|
||||
if totalCount >= 20 || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment {
|
||||
displayModeSelector = true
|
||||
displaySearchBar = true
|
||||
@ -1374,13 +1557,13 @@ final class StoryItemSetViewListComponent: Component {
|
||||
displaySortSelector = true
|
||||
}
|
||||
} else {
|
||||
if views.seenCount >= 20 || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment {
|
||||
/*if views.seenCount >= 20 || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment {
|
||||
displayModeSelector = true
|
||||
displaySearchBar = true
|
||||
}
|
||||
if (views.reactedCount >= 10 && views.seenCount >= 20) || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment {
|
||||
displaySortSelector = true
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
if let privacy = component.storyItem.privacy, case .everyone = privacy.base {
|
||||
@ -1390,18 +1573,18 @@ final class StoryItemSetViewListComponent: Component {
|
||||
}
|
||||
|
||||
let navigationHeight: CGFloat
|
||||
let navigationSearchPartHeight: CGFloat
|
||||
if component.isSearchActive {
|
||||
navigationHeight = navigationSearchSize.height
|
||||
navigationSearchPartHeight = 0.0
|
||||
} else if displaySearchBar {
|
||||
navigationHeight = 56.0 + navigationSearchSize.height - 6.0
|
||||
navigationSearchPartHeight = navigationSearchSize.height - 6.0
|
||||
} else {
|
||||
navigationHeight = 56.0
|
||||
navigationSearchPartHeight = 0.0
|
||||
}
|
||||
|
||||
let navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - visualHeight + 12.0), size: CGSize(width: availableSize.width, height: navigationHeight))
|
||||
transition.setFrame(view: self.navigationBarBackground, frame: navigationBarFrame)
|
||||
self.navigationBarBackground.update(size: navigationBarFrame.size, cornerRadius: 10.0, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition.containedViewLayoutTransition)
|
||||
|
||||
if let tabSelectorView = self.tabSelector.view {
|
||||
if tabSelectorView.superview == nil {
|
||||
self.navigationContainerView.addSubview(tabSelectorView)
|
||||
@ -1432,60 +1615,26 @@ final class StoryItemSetViewListComponent: Component {
|
||||
orderSelectorView.isHidden = !displaySortSelector
|
||||
}
|
||||
|
||||
if let navigationSearchView = self.navigationSearch.view {
|
||||
if navigationSearchView.superview == nil {
|
||||
self.navigationContainerView.addSubview(navigationSearchView)
|
||||
}
|
||||
transition.setFrame(view: navigationSearchView, frame: CGRect(origin: CGPoint(x: component.safeInsets.left, y: component.isSearchActive ? 0.0 : 50.0), size: navigationSearchSize))
|
||||
transition.setAlpha(view: navigationSearchView, alpha: (displaySearchBar || component.isSearchActive) ? 1.0 : 0.0)
|
||||
}
|
||||
let navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - visualHeight + 12.0), size: CGSize(width: availableSize.width, height: navigationHeight))
|
||||
|
||||
transition.setFrame(view: self.navigationContainerView, frame: navigationBarFrame)
|
||||
transition.setFrame(layer: self.navigationSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarFrame.maxY), size: CGSize(width: availableSize.width, height: UIScreenPixel)))
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarFrame.maxY), size: CGSize(width: availableSize.width, height: availableSize.height)))
|
||||
|
||||
let currentConfiguration = ContentConfigurationKey(listMode: self.listMode, sortMode: self.sortMode)
|
||||
if self.currentContentView?.configuration != currentConfiguration {
|
||||
let previousContentView = self.currentContentView
|
||||
self.disappearingCurrentContentView?.removeFromSuperview()
|
||||
self.disappearingCurrentContentView = self.currentContentView
|
||||
self.currentContentView = nil
|
||||
|
||||
let currentContentView = ContentView(configuration: currentConfiguration)
|
||||
self.currentContentView = currentContentView
|
||||
currentContentView.isHidden = true
|
||||
currentContentView.contentLoadedUpdated = { [weak self, weak currentContentView, weak previousContentView] value in
|
||||
guard value, let self, let currentContentView else {
|
||||
return
|
||||
}
|
||||
currentContentView.isHidden = false
|
||||
if let previousContentView {
|
||||
previousContentView.removeFromSuperview()
|
||||
if self.disappearingCurrentContentView === previousContentView {
|
||||
self.disappearingCurrentContentView = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let currentContentView = self.currentContentView {
|
||||
var contentViewTransition = transition
|
||||
if currentContentView.superview == nil {
|
||||
contentViewTransition = contentViewTransition.withAnimation(.none)
|
||||
self.insertSubview(currentContentView, belowSubview: self.navigationBarBackground)
|
||||
self.insertSubview(currentContentView, belowSubview: self.navigationContainerView)
|
||||
}
|
||||
|
||||
contentViewTransition.setFrame(view: currentContentView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
currentContentView.update(
|
||||
component: component,
|
||||
state: state,
|
||||
baseContentView: nil,
|
||||
query: nil,
|
||||
availableSize: availableSize,
|
||||
visualHeight: visualHeight,
|
||||
sideInset: sideInset,
|
||||
navigationHeight: navigationHeight,
|
||||
navigationSearchPartHeight: navigationSearchPartHeight,
|
||||
isSearchActive: component.isSearchActive,
|
||||
transition: contentViewTransition
|
||||
)
|
||||
if currentContentView.contentLoaded {
|
||||
@ -1512,7 +1661,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
var contentViewTransition = transition
|
||||
if currentSearchContentView.superview == nil {
|
||||
contentViewTransition = contentViewTransition.withAnimation(.none)
|
||||
self.insertSubview(currentSearchContentView, belowSubview: self.navigationBarBackground)
|
||||
self.insertSubview(currentSearchContentView, belowSubview: self.navigationContainerView)
|
||||
}
|
||||
|
||||
currentSearchContentView.hasContentUpdated = { [weak self] hasContent in
|
||||
@ -1523,15 +1672,20 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.currentSearchContentView?.isHidden = !hasContent
|
||||
}
|
||||
contentViewTransition.setFrame(view: currentSearchContentView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
currentSearchContentView.update(
|
||||
currentSearchContentView.updateState(
|
||||
component: component,
|
||||
state: state,
|
||||
baseContentView: self.currentContentView,
|
||||
query: self.currentSearchQuery,
|
||||
query: self.currentSearchQuery
|
||||
)
|
||||
currentSearchContentView.update(
|
||||
component: component,
|
||||
availableSize: availableSize,
|
||||
visualHeight: visualHeight,
|
||||
sideInset: sideInset,
|
||||
navigationHeight: navigationHeight,
|
||||
navigationSearchPartHeight: navigationSearchPartHeight,
|
||||
isSearchActive: component.isSearchActive,
|
||||
transition: contentViewTransition
|
||||
)
|
||||
|
||||
@ -1564,13 +1718,12 @@ final class StoryItemSetViewListComponent: Component {
|
||||
transition.setFrame(view: disappearingCurrentContentView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
disappearingCurrentContentView.update(
|
||||
component: component,
|
||||
state: state,
|
||||
baseContentView: nil,
|
||||
query: disappearingCurrentContentView.query,
|
||||
availableSize: availableSize,
|
||||
visualHeight: visualHeight,
|
||||
sideInset: sideInset,
|
||||
navigationHeight: navigationHeight,
|
||||
navigationSearchPartHeight: navigationSearchPartHeight,
|
||||
isSearchActive: component.isSearchActive,
|
||||
transition: transition
|
||||
)
|
||||
}
|
||||
@ -1578,19 +1731,28 @@ final class StoryItemSetViewListComponent: Component {
|
||||
transition.setFrame(view: disappearingSearchContentView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
disappearingSearchContentView.update(
|
||||
component: component,
|
||||
state: state,
|
||||
baseContentView: nil,
|
||||
query: disappearingSearchContentView.query,
|
||||
availableSize: availableSize,
|
||||
visualHeight: visualHeight,
|
||||
sideInset: sideInset,
|
||||
navigationHeight: navigationHeight,
|
||||
navigationSearchPartHeight: navigationSearchPartHeight,
|
||||
isSearchActive: component.isSearchActive,
|
||||
transition: transition
|
||||
)
|
||||
}
|
||||
|
||||
if let navigationSearchView = self.navigationSearch.view {
|
||||
if navigationSearchView.superview == nil {
|
||||
self.navigationContainerView.addSubview(navigationSearchView)
|
||||
}
|
||||
transition.setFrame(view: navigationSearchView, frame: CGRect(origin: CGPoint(x: component.safeInsets.left, y: component.isSearchActive ? 0.0 : 50.0), size: navigationSearchSize))
|
||||
transition.setAlpha(view: navigationSearchView, alpha: (displaySearchBar || component.isSearchActive) ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
transition.setBoundsOrigin(view: self, origin: CGPoint(x: 0.0, y: -max(0.0, visualHeight - component.effectiveHeight)))
|
||||
|
||||
updateSubState = true
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +90,7 @@ public final class TextFieldComponent: Component {
|
||||
public let textColor: UIColor
|
||||
public let insets: UIEdgeInsets
|
||||
public let hideKeyboard: Bool
|
||||
public let resetText: NSAttributedString?
|
||||
public let formatMenuAvailability: FormatMenuAvailability
|
||||
public let lockedFormatAction: () -> Void
|
||||
public let present: (ViewController) -> Void
|
||||
@ -103,6 +104,7 @@ public final class TextFieldComponent: Component {
|
||||
textColor: UIColor,
|
||||
insets: UIEdgeInsets,
|
||||
hideKeyboard: Bool,
|
||||
resetText: NSAttributedString?,
|
||||
formatMenuAvailability: FormatMenuAvailability,
|
||||
lockedFormatAction: @escaping () -> Void,
|
||||
present: @escaping (ViewController) -> Void,
|
||||
@ -115,6 +117,7 @@ public final class TextFieldComponent: Component {
|
||||
self.textColor = textColor
|
||||
self.insets = insets
|
||||
self.hideKeyboard = hideKeyboard
|
||||
self.resetText = resetText
|
||||
self.formatMenuAvailability = formatMenuAvailability
|
||||
self.lockedFormatAction = lockedFormatAction
|
||||
self.present = present
|
||||
@ -140,6 +143,9 @@ public final class TextFieldComponent: Component {
|
||||
if lhs.hideKeyboard != rhs.hideKeyboard {
|
||||
return false
|
||||
}
|
||||
if lhs.resetText != rhs.resetText {
|
||||
return false
|
||||
}
|
||||
if lhs.formatMenuAvailability != rhs.formatMenuAvailability {
|
||||
return false
|
||||
}
|
||||
@ -523,11 +529,13 @@ public final class TextFieldComponent: Component {
|
||||
return self.inputState.inputText
|
||||
}
|
||||
|
||||
public func setAttributedText(_ string: NSAttributedString) {
|
||||
public func setAttributedText(_ string: NSAttributedString, updateState: Bool) {
|
||||
self.updateInputState { _ in
|
||||
return TextFieldComponent.InputState(inputText: string, selectionRange: string.length ..< string.length)
|
||||
}
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged)))
|
||||
if updateState {
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(kind: .textChanged)))
|
||||
}
|
||||
}
|
||||
|
||||
public func activateInput() {
|
||||
@ -783,6 +791,10 @@ public final class TextFieldComponent: Component {
|
||||
self.updateInputState { _ in
|
||||
return TextFieldComponent.InputState(inputText: initialText)
|
||||
}
|
||||
} else if let resetText = component.resetText {
|
||||
self.updateInputState { _ in
|
||||
return TextFieldComponent.InputState(inputText: resetText)
|
||||
}
|
||||
}
|
||||
|
||||
if self.emojiViewProvider == nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user