This commit is contained in:
Ali 2023-07-11 19:03:25 +04:00
parent e8b3615be6
commit 6ec1f4c680
7 changed files with 412 additions and 151 deletions

View File

@ -149,6 +149,7 @@ public final class TextNodeLayoutArguments {
public let textStroke: (UIColor, CGFloat)?
public let displaySpoilers: Bool
public let displayEmbeddedItemsUnderSpoilers: Bool
public let customTruncationToken: NSAttributedString?
public init(
attributedString: NSAttributedString?,
@ -167,7 +168,8 @@ public final class TextNodeLayoutArguments {
textShadowBlur: CGFloat? = nil,
textStroke: (UIColor, CGFloat)? = nil,
displaySpoilers: Bool = false,
displayEmbeddedItemsUnderSpoilers: Bool = false
displayEmbeddedItemsUnderSpoilers: Bool = false,
customTruncationToken: NSAttributedString? = nil
) {
self.attributedString = attributedString
self.backgroundColor = backgroundColor
@ -186,6 +188,7 @@ public final class TextNodeLayoutArguments {
self.textStroke = textStroke
self.displaySpoilers = displaySpoilers
self.displayEmbeddedItemsUnderSpoilers = displayEmbeddedItemsUnderSpoilers
self.customTruncationToken = customTruncationToken
}
public func withAttributedString(_ attributedString: NSAttributedString?) -> TextNodeLayoutArguments {
@ -206,7 +209,8 @@ public final class TextNodeLayoutArguments {
textShadowBlur: self.textShadowBlur,
textStroke: self.textStroke,
displaySpoilers: self.displaySpoilers,
displayEmbeddedItemsUnderSpoilers: self.displayEmbeddedItemsUnderSpoilers
displayEmbeddedItemsUnderSpoilers: self.displayEmbeddedItemsUnderSpoilers,
customTruncationToken: self.customTruncationToken
)
}
}
@ -998,7 +1002,7 @@ open class TextNode: ASDisplayNode {
}
}
static func calculateLayout(attributedString: NSAttributedString?, minimumNumberOfLines: Int, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?, textShadowBlur: CGFloat?, textStroke: (UIColor, CGFloat)?, displaySpoilers: Bool, displayEmbeddedItemsUnderSpoilers: Bool) -> TextNodeLayout {
static func calculateLayout(attributedString: NSAttributedString?, minimumNumberOfLines: Int, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?, textShadowBlur: CGFloat?, textStroke: (UIColor, CGFloat)?, displaySpoilers: Bool, displayEmbeddedItemsUnderSpoilers: Bool, customTruncationToken: NSAttributedString?) -> TextNodeLayout {
if let attributedString = attributedString {
let stringLength = attributedString.length
@ -1168,7 +1172,17 @@ open class TextNode: ASDisplayNode {
layoutSize.height += fontLineSpacing
}
let lineRange = CFRange(location: lastLineCharacterIndex, length: stringLength - lastLineCharacterIndex)
var didClipLinebreak = false
var lineRange = CFRange(location: lastLineCharacterIndex, length: stringLength - lastLineCharacterIndex)
let nsString = (attributedString.string as NSString)
for i in lineRange.location ..< (lineRange.location + lineRange.length) {
if nsString.character(at: i) == 0x0a {
lineRange.length = max(0, i - lineRange.location)
didClipLinebreak = true
break
}
}
var brokenLineRange = CFRange(location: lastLineCharacterIndex, length: lineCharacterCount)
if brokenLineRange.location + brokenLineRange.length > attributedString.length {
brokenLineRange.length = attributedString.length - brokenLineRange.location
@ -1186,16 +1200,44 @@ open class TextNode: ASDisplayNode {
lineConstrainedSize.width -= bottomCutoutSize.width
}
if CTLineGetTypographicBounds(originalLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(originalLine) < Double(lineConstrainedSize.width) {
coreTextLine = originalLine
let truncatedTokenString: NSAttributedString
if let customTruncationToken {
truncatedTokenString = customTruncationToken
} else {
var truncationTokenAttributes: [NSAttributedString.Key : AnyObject] = [:]
truncationTokenAttributes[NSAttributedString.Key.font] = font
truncationTokenAttributes[NSAttributedString.Key(rawValue: kCTForegroundColorFromContextAttributeName as String)] = true as NSNumber
let tokenString = "\u{2026}"
let truncatedTokenString = NSAttributedString(string: tokenString, attributes: truncationTokenAttributes)
let truncationToken = CTLineCreateWithAttributedString(truncatedTokenString)
truncatedTokenString = NSAttributedString(string: tokenString, attributes: truncationTokenAttributes)
}
let truncationToken = CTLineCreateWithAttributedString(truncatedTokenString)
if CTLineGetTypographicBounds(originalLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(originalLine) < Double(lineConstrainedSize.width) {
if didClipLinebreak {
let mergedLine = NSMutableAttributedString()
mergedLine.append(attributedString.attributedSubstring(from: NSRange(location: lineRange.location, length: lineRange.length)))
mergedLine.append(truncatedTokenString)
coreTextLine = CTLineCreateWithAttributedString(mergedLine)
let runs = (CTLineGetGlyphRuns(coreTextLine) as [AnyObject]) as! [CTRun]
for run in runs {
let runAttributes: NSDictionary = CTRunGetAttributes(run)
if let _ = runAttributes["CTForegroundColorFromContext"] {
brokenLineRange.length = CTRunGetStringRange(run).location - brokenLineRange.location
break
}
}
if brokenLineRange.location + brokenLineRange.length > attributedString.length {
brokenLineRange.length = attributedString.length - brokenLineRange.location
}
truncated = true
} else {
coreTextLine = originalLine
}
} else {
coreTextLine = CTLineCreateTruncatedLine(originalLine, Double(lineConstrainedSize.width), truncationType, truncationToken) ?? truncationToken
let runs = (CTLineGetGlyphRuns(coreTextLine) as [AnyObject]) as! [CTRun]
for run in runs {
@ -1647,11 +1689,11 @@ open class TextNode: ASDisplayNode {
if stringMatch {
layout = existingLayout
} else {
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displaySpoilers: arguments.displaySpoilers, displayEmbeddedItemsUnderSpoilers: arguments.displayEmbeddedItemsUnderSpoilers)
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displaySpoilers: arguments.displaySpoilers, displayEmbeddedItemsUnderSpoilers: arguments.displayEmbeddedItemsUnderSpoilers, customTruncationToken: arguments.customTruncationToken)
updated = true
}
} else {
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displaySpoilers: arguments.displaySpoilers, displayEmbeddedItemsUnderSpoilers: arguments.displayEmbeddedItemsUnderSpoilers)
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displaySpoilers: arguments.displaySpoilers, displayEmbeddedItemsUnderSpoilers: arguments.displayEmbeddedItemsUnderSpoilers, customTruncationToken: arguments.customTruncationToken)
updated = true
}
@ -2292,11 +2334,11 @@ open class TextView: UIView {
if stringMatch {
layout = existingLayout
} else {
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displaySpoilers: arguments.displaySpoilers, displayEmbeddedItemsUnderSpoilers: arguments.displayEmbeddedItemsUnderSpoilers)
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displaySpoilers: arguments.displaySpoilers, displayEmbeddedItemsUnderSpoilers: arguments.displayEmbeddedItemsUnderSpoilers, customTruncationToken: arguments.customTruncationToken)
updated = true
}
} else {
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displaySpoilers: arguments.displaySpoilers, displayEmbeddedItemsUnderSpoilers: arguments.displayEmbeddedItemsUnderSpoilers)
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displaySpoilers: arguments.displaySpoilers, displayEmbeddedItemsUnderSpoilers: arguments.displayEmbeddedItemsUnderSpoilers, customTruncationToken: arguments.customTruncationToken)
updated = true
}

View File

@ -1009,11 +1009,13 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
var transitionIn: StoryContainerScreen.TransitionIn?
let story = item.story
var foundItem: SparseItemGridDisplayItem?
var foundItemLayer: SparseItemGridLayer?
self.itemGrid.forEachVisibleItem { item in
guard let itemLayer = item.layer as? ItemLayer else {
return
}
foundItem = item
if let listItem = itemLayer.item, listItem.story.id == story.id {
foundItemLayer = itemLayer
}
@ -1026,6 +1028,11 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
sourceCornerRadius: 0.0,
sourceIsAvatar: false
)
if let blurLayer = foundItem?.blurLayer {
let transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut))
transition.setAlpha(layer: blurLayer, alpha: 0.0)
}
}
let storyContainerScreen = StoryContainerScreen(
@ -1037,16 +1044,23 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
return nil
}
var foundItem: SparseItemGridDisplayItem?
var foundItemLayer: SparseItemGridLayer?
self.itemGrid.forEachVisibleItem { item in
guard let itemLayer = item.layer as? ItemLayer else {
return
}
foundItem = item
if let listItem = itemLayer.item, AnyHashable(listItem.story.id) == itemId {
foundItemLayer = itemLayer
}
}
if let foundItemLayer {
if let blurLayer = foundItem?.blurLayer {
let transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut))
transition.setAlpha(layer: blurLayer, alpha: 1.0)
}
let itemRect = self.itemGrid.frameForItem(layer: foundItemLayer)
return StoryContainerScreen.TransitionOut(
destinationView: self.view,
@ -1862,11 +1876,21 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
private func updateHiddenItems() {
self.itemGrid.forEachVisibleItem { item in
guard let itemLayer = item.layer as? ItemLayer, let item = itemLayer.item else {
self.itemGrid.forEachVisibleItem { itemValue in
guard let itemLayer = itemValue.layer as? ItemLayer, let item = itemLayer.item else {
return
}
itemLayer.isHidden = self.itemInteraction.hiddenMedia.contains(item.story.id)
let itemHidden = self.itemInteraction.hiddenMedia.contains(item.story.id)
itemLayer.isHidden = itemHidden
if let blurLayer = itemValue.blurLayer {
let transition = Transition.immediate
if itemHidden {
transition.setAlpha(layer: blurLayer, alpha: 0.0)
} else {
transition.setAlpha(layer: blurLayer, alpha: 1.0)
}
}
}
}

View File

@ -1584,7 +1584,7 @@ public class StoryContainerScreen: ViewControllerComponentContainer {
private var didAnimateIn: Bool = false
private var isDismissed: Bool = false
private let focusedItemPromise = Promise<StoryId?>(nil)
private let focusedItemPromise = Promise<StoryId?>()
public var focusedItem: Signal<StoryId?, NoError> {
return self.focusedItemPromise.get()
}

View File

@ -34,8 +34,14 @@ public final class StoryContentItem: Equatable {
}
}
public enum ProgressMode {
case play
case pause
case blurred
}
open class View: UIView {
open func setIsProgressPaused(_ isProgressPaused: Bool) {
open func setProgressMode(_ progressMode: ProgressMode) {
}
open func rewind() {

View File

@ -99,23 +99,36 @@ final class StoryContentCaptionComponent: Component {
self.verticalInset = verticalInset
}
}
private final class ContentItem {
var textNode: TextNodeWithEntities?
var spoilerTextNode: TextNodeWithEntities?
var linkHighlightingNode: LinkHighlightingNode?
var dustNode: InvisibleInkDustNode?
init() {
}
func update() {
}
}
final class View: UIView, UIScrollViewDelegate {
private let scrollViewContainer: UIView
private let scrollView: UIScrollView
private let collapsedText: ContentItem
private let expandedText: ContentItem
private let scrollMaskContainer: UIView
private let scrollFullMaskView: UIView
private let scrollCenterMaskView: UIView
private let scrollBottomMaskView: UIImageView
private let scrollTopMaskView: UIImageView
private let shadowGradientLayer: SimpleGradientLayer
private let shadowPlainLayer: SimpleLayer
private var textNode: TextNodeWithEntities?
private var spoilerTextNode: TextNodeWithEntities?
private var linkHighlightingNode: LinkHighlightingNode?
private var dustNode: InvisibleInkDustNode?
private var component: StoryContentCaptionComponent?
private weak var state: EmptyComponentState?
@ -125,6 +138,8 @@ final class StoryContentCaptionComponent: Component {
private var ignoreScrolling: Bool = false
private var ignoreExternalState: Bool = false
private var isExpanded: Bool = false
override init(frame: CGRect) {
self.shadowGradientLayer = SimpleGradientLayer()
self.shadowPlainLayer = SimpleLayer()
@ -154,6 +169,15 @@ final class StoryContentCaptionComponent: Component {
UIColor(white: 1.0, alpha: 0.0)
], locations: [0.0, 1.0]))
self.scrollMaskContainer.addSubview(self.scrollBottomMaskView)
self.scrollTopMaskView = UIImageView(image: generateGradientImage(size: CGSize(width: 8.0, height: 8.0), colors: [
UIColor(white: 1.0, alpha: 0.0),
UIColor(white: 1.0, alpha: 1.0)
], locations: [0.0, 1.0]))
self.scrollMaskContainer.addSubview(self.scrollTopMaskView)
self.collapsedText = ContentItem()
self.expandedText = ContentItem()
super.init(frame: frame)
@ -178,7 +202,8 @@ final class StoryContentCaptionComponent: Component {
if !self.bounds.contains(point) {
return nil
}
if let textView = self.textNode?.textNode.view {
if let textView = self.collapsedText.textNode?.textNode.view {
let textLocalPoint = self.convert(point, to: textView)
if textLocalPoint.y >= -7.0 {
return textView
@ -190,7 +215,11 @@ final class StoryContentCaptionComponent: Component {
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.expand(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
if self.isExpanded {
self.collapse(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
} else {
self.expand(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
}
}
}
@ -238,6 +267,8 @@ final class StoryContentCaptionComponent: Component {
let isExpanded = expandFraction > 0.0
self.isExpanded = isExpanded
if component.externalState.isExpanded != isExpanded {
component.externalState.isExpanded = isExpanded
@ -248,16 +279,18 @@ final class StoryContentCaptionComponent: Component {
}
@objc func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
let contentItem = self.isExpanded ? self.expandedText : self.collapsedText
switch recognizer.state {
case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation, let component = self.component, let textNode = self.textNode {
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation, let component = self.component, let textNode = contentItem.textNode {
let titleFrame = textNode.textNode.view.bounds
if titleFrame.contains(location) {
if let (index, attributes) = textNode.textNode.attributesAtPoint(CGPoint(x: location.x - titleFrame.minX, y: location.y - titleFrame.minY)) {
let action: Action?
if case .tap = gesture, let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler)], !(self.dustNode?.isRevealed ?? true) {
let convertedPoint = recognizer.view?.convert(location, to: self.dustNode?.view) ?? location
self.dustNode?.revealAtLocation(convertedPoint)
if case .tap = gesture, let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler)], !(contentItem.dustNode?.isRevealed ?? true) {
let convertedPoint = recognizer.view?.convert(location, to: contentItem.dustNode?.view) ?? location
contentItem.dustNode?.revealAtLocation(convertedPoint)
return
} else if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
var concealed = true
@ -278,19 +311,24 @@ final class StoryContentCaptionComponent: Component {
} else {
action = nil
}
guard let action else {
return
if let action {
switch gesture {
case .tap:
component.action(action)
case .longTap:
component.longTapAction(action)
default:
return
}
} else {
if case .tap = gesture {
if self.isExpanded {
self.collapse(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
} else {
self.expand(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
}
}
}
switch gesture {
case .tap:
component.action(action)
case .longTap:
component.longTapAction(action)
default:
return
}
self.expand(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
return
}
}
}
@ -300,7 +338,9 @@ final class StoryContentCaptionComponent: Component {
}
private func updateTouchesAtPoint(_ point: CGPoint?) {
guard let textNode = self.textNode else {
let contentItem = self.isExpanded ? self.expandedText : self.collapsedText
guard let textNode = contentItem.textNode else {
return
}
var rects: [CGRect]?
@ -329,20 +369,20 @@ final class StoryContentCaptionComponent: Component {
}
}
if let spoilerRects = spoilerRects, !spoilerRects.isEmpty, let dustNode = self.dustNode, !dustNode.isRevealed {
if let spoilerRects = spoilerRects, !spoilerRects.isEmpty, let dustNode = contentItem.dustNode, !dustNode.isRevealed {
} else if let rects = rects {
let linkHighlightingNode: LinkHighlightingNode
if let current = self.linkHighlightingNode {
if let current = contentItem.linkHighlightingNode {
linkHighlightingNode = current
} else {
linkHighlightingNode = LinkHighlightingNode(color: UIColor(white: 1.0, alpha: 0.5))
self.linkHighlightingNode = linkHighlightingNode
contentItem.linkHighlightingNode = linkHighlightingNode
self.scrollView.insertSubview(linkHighlightingNode.view, belowSubview: textNode.textNode.view)
}
linkHighlightingNode.frame = textNode.textNode.view.frame
linkHighlightingNode.updateRects(rects)
} else if let linkHighlightingNode = self.linkHighlightingNode {
self.linkHighlightingNode = nil
} else if let linkHighlightingNode = contentItem.linkHighlightingNode {
contentItem.linkHighlightingNode = nil
linkHighlightingNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak linkHighlightingNode] _ in
linkHighlightingNode?.removeFromSupernode()
})
@ -375,8 +415,21 @@ final class StoryContentCaptionComponent: Component {
entityFiles: component.entityFiles
)
let makeLayout = TextNodeWithEntities.asyncLayout(self.textNode)
let textLayout = makeLayout(TextNodeLayoutArguments(
let truncationToken = NSMutableAttributedString()
truncationToken.append(NSAttributedString(string: "\u{2026} ", font: Font.regular(16.0), textColor: .white))
truncationToken.append(NSAttributedString(string: "Show more", font: Font.semibold(16.0), textColor: .white))
//TODO:localize
let collapsedTextLayout = TextNodeWithEntities.asyncLayout(self.collapsedText.textNode)(TextNodeLayoutArguments(
attributedString: attributedText,
maximumNumberOfLines: 3,
truncationType: .end,
constrainedSize: textContainerSize,
textShadowColor: UIColor(white: 0.0, alpha: 0.25),
textShadowBlur: 4.0,
customTruncationToken: truncationToken
))
let expandedTextLayout = TextNodeWithEntities.asyncLayout(self.expandedText.textNode)(TextNodeLayoutArguments(
attributedString: attributedText,
maximumNumberOfLines: 0,
truncationType: .end,
@ -385,92 +438,177 @@ final class StoryContentCaptionComponent: Component {
textShadowBlur: 4.0
))
let makeSpoilerLayout = TextNodeWithEntities.asyncLayout(self.spoilerTextNode)
let spoilerTextLayoutAndApply: (TextNodeLayout, (TextNodeWithEntities.Arguments?) -> TextNodeWithEntities)?
if !textLayout.0.spoilers.isEmpty {
spoilerTextLayoutAndApply = makeSpoilerLayout(TextNodeLayoutArguments(attributedString: attributedText, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textContainerSize, textShadowColor: UIColor(white: 0.0, alpha: 0.25), textShadowBlur: 4.0, displaySpoilers: true, displayEmbeddedItemsUnderSpoilers: true))
let collapsedSpoilerTextLayoutAndApply: (TextNodeLayout, (TextNodeWithEntities.Arguments?) -> TextNodeWithEntities)?
if !collapsedTextLayout.0.spoilers.isEmpty {
collapsedSpoilerTextLayoutAndApply = TextNodeWithEntities.asyncLayout(self.collapsedText.spoilerTextNode)(TextNodeLayoutArguments(attributedString: attributedText, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textContainerSize, textShadowColor: UIColor(white: 0.0, alpha: 0.25), textShadowBlur: 4.0, displaySpoilers: true, displayEmbeddedItemsUnderSpoilers: true))
} else {
spoilerTextLayoutAndApply = nil
collapsedSpoilerTextLayoutAndApply = nil
}
let maxHeight: CGFloat = 50.0
let visibleTextHeight = min(maxHeight, textLayout.0.size.height)
let textOverflowHeight: CGFloat = textLayout.0.size.height - visibleTextHeight
let expandedSpoilerTextLayoutAndApply: (TextNodeLayout, (TextNodeWithEntities.Arguments?) -> TextNodeWithEntities)?
if !expandedTextLayout.0.spoilers.isEmpty {
expandedSpoilerTextLayoutAndApply = TextNodeWithEntities.asyncLayout(self.expandedText.spoilerTextNode)(TextNodeLayoutArguments(attributedString: attributedText, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textContainerSize, textShadowColor: UIColor(white: 0.0, alpha: 0.25), textShadowBlur: 4.0, displaySpoilers: true, displayEmbeddedItemsUnderSpoilers: true))
} else {
expandedSpoilerTextLayoutAndApply = nil
}
let visibleTextHeight = collapsedTextLayout.0.size.height
let textOverflowHeight: CGFloat = expandedTextLayout.0.size.height - visibleTextHeight
let scrollContentSize = CGSize(width: availableSize.width, height: availableSize.height + textOverflowHeight)
let textNode = textLayout.1(TextNodeWithEntities.Arguments(
context: component.context,
cache: component.context.animationCache,
renderer: component.context.animationRenderer,
placeholderColor: UIColor(white: 0.2, alpha: 1.0),
attemptSynchronous: true
))
if self.textNode !== textNode {
self.textNode?.textNode.view.removeFromSuperview()
self.textNode = textNode
if textNode.textNode.view.superview == nil {
self.scrollView.addSubview(textNode.textNode.view)
let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:)))
recognizer.tapActionAtPoint = { point in
return .waitForSingleTap
}
recognizer.highlight = { [weak self] point in
guard let self else {
return
}
self.updateTouchesAtPoint(point)
}
textNode.textNode.view.addGestureRecognizer(recognizer)
}
textNode.visibilityRect = CGRect(origin: CGPoint(), size: CGSize(width: 100000.0, height: 100000.0))
}
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: availableSize.height - visibleTextHeight - verticalInset), size: textLayout.0.size)
textNode.textNode.frame = textFrame
if let (_, spoilerTextApply) = spoilerTextLayoutAndApply {
let spoilerTextNode = spoilerTextApply(TextNodeWithEntities.Arguments(
do {
let collapsedTextNode = collapsedTextLayout.1(TextNodeWithEntities.Arguments(
context: component.context,
cache: component.context.animationCache,
renderer: component.context.animationRenderer,
placeholderColor: UIColor(white: 0.2, alpha: 1.0),
attemptSynchronous: true
))
if self.spoilerTextNode == nil {
spoilerTextNode.textNode.alpha = 0.0
spoilerTextNode.textNode.isUserInteractionEnabled = false
spoilerTextNode.textNode.contentMode = .topLeft
spoilerTextNode.textNode.contentsScale = UIScreenScale
spoilerTextNode.textNode.displaysAsynchronously = false
self.scrollView.insertSubview(spoilerTextNode.textNode.view, belowSubview: textNode.textNode.view)
if self.collapsedText.textNode !== collapsedTextNode {
self.collapsedText.textNode?.textNode.view.removeFromSuperview()
spoilerTextNode.visibilityRect = CGRect(origin: CGPoint(), size: CGSize(width: 100000.0, height: 100000.0))
self.collapsedText.textNode = collapsedTextNode
if collapsedTextNode.textNode.view.superview == nil {
self.scrollView.addSubview(collapsedTextNode.textNode.view)
let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:)))
recognizer.tapActionAtPoint = { point in
return .waitForSingleTap
}
recognizer.highlight = { [weak self] point in
guard let self else {
return
}
self.updateTouchesAtPoint(point)
}
collapsedTextNode.textNode.view.addGestureRecognizer(recognizer)
}
self.spoilerTextNode = spoilerTextNode
collapsedTextNode.visibilityRect = CGRect(origin: CGPoint(), size: CGSize(width: 100000.0, height: 100000.0))
}
self.spoilerTextNode?.textNode.frame = textFrame
let collapsedTextFrame = CGRect(origin: CGPoint(x: sideInset, y: availableSize.height - visibleTextHeight - verticalInset), size: collapsedTextLayout.0.size)
collapsedTextNode.textNode.frame = collapsedTextFrame
let dustNode: InvisibleInkDustNode
if let current = self.dustNode {
dustNode = current
} else {
dustNode = InvisibleInkDustNode(textNode: spoilerTextNode.textNode, enableAnimations: component.context.sharedContext.energyUsageSettings.fullTranslucency)
self.dustNode = dustNode
self.scrollView.insertSubview(dustNode.view, aboveSubview: spoilerTextNode.textNode.view)
if let (_, collapsedSpoilerTextApply) = collapsedSpoilerTextLayoutAndApply {
let collapsedSpoilerTextNode = collapsedSpoilerTextApply(TextNodeWithEntities.Arguments(
context: component.context,
cache: component.context.animationCache,
renderer: component.context.animationRenderer,
placeholderColor: UIColor(white: 0.2, alpha: 1.0),
attemptSynchronous: true
))
if self.collapsedText.spoilerTextNode == nil {
collapsedSpoilerTextNode.textNode.alpha = 0.0
collapsedSpoilerTextNode.textNode.isUserInteractionEnabled = false
collapsedSpoilerTextNode.textNode.contentMode = .topLeft
collapsedSpoilerTextNode.textNode.contentsScale = UIScreenScale
collapsedSpoilerTextNode.textNode.displaysAsynchronously = false
self.scrollView.insertSubview(collapsedSpoilerTextNode.textNode.view, belowSubview: collapsedTextNode.textNode.view)
collapsedSpoilerTextNode.visibilityRect = CGRect(origin: CGPoint(), size: CGSize(width: 100000.0, height: 100000.0))
self.collapsedText.spoilerTextNode = collapsedSpoilerTextNode
}
self.collapsedText.spoilerTextNode?.textNode.frame = collapsedTextFrame
let collapsedDustNode: InvisibleInkDustNode
if let current = self.collapsedText.dustNode {
collapsedDustNode = current
} else {
collapsedDustNode = InvisibleInkDustNode(textNode: collapsedSpoilerTextNode.textNode, enableAnimations: component.context.sharedContext.energyUsageSettings.fullTranslucency)
self.collapsedText.dustNode = collapsedDustNode
self.scrollView.insertSubview(collapsedDustNode.view, aboveSubview: collapsedSpoilerTextNode.textNode.view)
}
collapsedDustNode.frame = collapsedTextFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 0.0)
collapsedDustNode.update(size: collapsedDustNode.frame.size, color: .white, textColor: .white, rects: collapsedTextLayout.0.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: collapsedTextLayout.0.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
} else if let collapsedSpoilerTextNode = self.collapsedText.spoilerTextNode {
self.collapsedText.spoilerTextNode = nil
collapsedSpoilerTextNode.textNode.removeFromSupernode()
if let collapsedDustNode = self.collapsedText.dustNode {
self.collapsedText.dustNode = nil
collapsedDustNode.view.removeFromSuperview()
}
}
}
do {
let expandedTextNode = expandedTextLayout.1(TextNodeWithEntities.Arguments(
context: component.context,
cache: component.context.animationCache,
renderer: component.context.animationRenderer,
placeholderColor: UIColor(white: 0.2, alpha: 1.0),
attemptSynchronous: true
))
if self.expandedText.textNode !== expandedTextNode {
self.expandedText.textNode?.textNode.view.removeFromSuperview()
self.expandedText.textNode = expandedTextNode
if expandedTextNode.textNode.view.superview == nil {
self.scrollView.addSubview(expandedTextNode.textNode.view)
let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:)))
recognizer.tapActionAtPoint = { point in
return .waitForSingleTap
}
recognizer.highlight = { [weak self] point in
guard let self else {
return
}
self.updateTouchesAtPoint(point)
}
expandedTextNode.textNode.view.addGestureRecognizer(recognizer)
}
expandedTextNode.visibilityRect = CGRect(origin: CGPoint(), size: CGSize(width: 100000.0, height: 100000.0))
}
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 0.0)
dustNode.update(size: dustNode.frame.size, color: .white, textColor: .white, rects: textLayout.0.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.0.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
} else if let spoilerTextNode = self.spoilerTextNode {
self.spoilerTextNode = nil
spoilerTextNode.textNode.removeFromSupernode()
if let dustNode = self.dustNode {
self.dustNode = nil
dustNode.removeFromSupernode()
let expandedTextFrame = CGRect(origin: CGPoint(x: sideInset, y: availableSize.height - visibleTextHeight - verticalInset), size: expandedTextLayout.0.size)
expandedTextNode.textNode.frame = expandedTextFrame
if let (_, expandedSpoilerTextApply) = expandedSpoilerTextLayoutAndApply {
let expandedSpoilerTextNode = expandedSpoilerTextApply(TextNodeWithEntities.Arguments(
context: component.context,
cache: component.context.animationCache,
renderer: component.context.animationRenderer,
placeholderColor: UIColor(white: 0.2, alpha: 1.0),
attemptSynchronous: true
))
if self.expandedText.spoilerTextNode == nil {
expandedSpoilerTextNode.textNode.alpha = 0.0
expandedSpoilerTextNode.textNode.isUserInteractionEnabled = false
expandedSpoilerTextNode.textNode.contentMode = .topLeft
expandedSpoilerTextNode.textNode.contentsScale = UIScreenScale
expandedSpoilerTextNode.textNode.displaysAsynchronously = false
self.scrollView.insertSubview(expandedSpoilerTextNode.textNode.view, belowSubview: expandedTextNode.textNode.view)
expandedSpoilerTextNode.visibilityRect = CGRect(origin: CGPoint(), size: CGSize(width: 100000.0, height: 100000.0))
self.expandedText.spoilerTextNode = expandedSpoilerTextNode
}
self.expandedText.spoilerTextNode?.textNode.frame = expandedTextFrame
let expandedDustNode: InvisibleInkDustNode
if let current = self.expandedText.dustNode {
expandedDustNode = current
} else {
expandedDustNode = InvisibleInkDustNode(textNode: expandedSpoilerTextNode.textNode, enableAnimations: component.context.sharedContext.energyUsageSettings.fullTranslucency)
self.expandedText.dustNode = expandedDustNode
self.scrollView.insertSubview(expandedDustNode.view, aboveSubview: expandedSpoilerTextNode.textNode.view)
}
expandedDustNode.frame = expandedTextFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 0.0)
expandedDustNode.update(size: expandedDustNode.frame.size, color: .white, textColor: .white, rects: expandedTextLayout.0.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: expandedTextLayout.0.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
} else if let expandedSpoilerTextNode = self.expandedText.spoilerTextNode {
self.expandedText.spoilerTextNode = nil
expandedSpoilerTextNode.textNode.removeFromSupernode()
if let expandedDustNode = self.expandedText.dustNode {
self.expandedText.dustNode = nil
expandedDustNode.view.removeFromSuperview()
}
}
}
@ -515,12 +653,41 @@ final class StoryContentCaptionComponent: Component {
let gradientEdgeHeight: CGFloat = 18.0
transition.setFrame(view: self.scrollFullMaskView, frame: CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: availableSize.height)))
transition.setFrame(view: self.scrollCenterMaskView, frame: CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: availableSize.height - gradientEdgeHeight)))
transition.setFrame(view: self.scrollFullMaskView, frame: CGRect(origin: CGPoint(x: 0.0, y: gradientEdgeHeight), size: CGSize(width: availableSize.width, height: availableSize.height - gradientEdgeHeight)))
transition.setFrame(view: self.scrollCenterMaskView, frame: CGRect(origin: CGPoint(x: 0.0, y: gradientEdgeHeight), size: CGSize(width: availableSize.width, height: availableSize.height - gradientEdgeHeight * 2.0)))
transition.setFrame(view: self.scrollBottomMaskView, frame: CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - gradientEdgeHeight), size: CGSize(width: availableSize.width, height: gradientEdgeHeight)))
transition.setFrame(view: self.scrollTopMaskView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: gradientEdgeHeight)))
self.ignoreExternalState = false
var isExpandedTransition = transition
if transition.animation.isImmediate, let hint = transition.userData(TransitionHint.self), case .isExpandedUpdated = hint.kind {
isExpandedTransition = transition.withAnimation(.curve(duration: 0.25, curve: .easeInOut))
}
if let textNode = self.collapsedText.textNode {
isExpandedTransition.setAlpha(view: textNode.textNode.view, alpha: self.isExpanded ? 0.0 : 1.0)
}
if let spoilerTextNode = self.collapsedText.spoilerTextNode {
isExpandedTransition.setAlpha(view: spoilerTextNode.textNode.view, alpha: self.isExpanded ? 0.0 : 1.0)
}
if let dustNode = self.collapsedText.dustNode {
isExpandedTransition.setAlpha(view: dustNode.view, alpha: self.isExpanded ? 0.0 : 1.0)
}
if let textNode = self.expandedText.textNode {
isExpandedTransition.setAlpha(view: textNode.textNode.view, alpha: !self.isExpanded ? 0.0 : 1.0)
}
if let spoilerTextNode = self.expandedText.spoilerTextNode {
isExpandedTransition.setAlpha(view: spoilerTextNode.textNode.view, alpha: !self.isExpanded ? 0.0 : 1.0)
}
if let dustNode = self.expandedText.dustNode {
isExpandedTransition.setAlpha(view: dustNode.view, alpha: !self.isExpanded ? 0.0 : 1.0)
}
isExpandedTransition.setAlpha(layer: self.shadowPlainLayer, alpha: self.isExpanded ? 0.0 : 1.0)
isExpandedTransition.setAlpha(layer: self.shadowGradientLayer, alpha: self.isExpanded ? 0.0 : 1.0)
return availableSize
}
}

View File

@ -67,7 +67,7 @@ final class StoryItemContentComponent: Component {
private var unsupportedText: ComponentView<Empty>?
private var unsupportedButton: ComponentView<Empty>?
private var isProgressPaused: Bool = true
private var progressMode: StoryContentItem.ProgressMode = .pause
private var currentProgressTimer: SwiftSignalKit.Timer?
private var currentProgressTimerValue: Double = 0.0
private var videoProgressDisposable: Disposable?
@ -96,7 +96,7 @@ final class StoryItemContentComponent: Component {
guard let self else {
return
}
self.updateIsProgressPaused(update: true)
self.updateProgressMode(update: true)
}
}
@ -119,7 +119,7 @@ final class StoryItemContentComponent: Component {
if self.videoNode != nil {
return
}
if self.isProgressPaused {
if case .pause = self.progressMode {
return
}
@ -169,7 +169,17 @@ final class StoryItemContentComponent: Component {
guard let self else {
return
}
self.environment?.presentationProgressUpdated(1.0, false, true)
if self.progressMode == .blurred {
self.rewind()
if let videoNode = self.videoNode {
if self.contentLoaded {
videoNode.play()
}
}
} else {
self.environment?.presentationProgressUpdated(1.0, false, true)
}
}
videoNode.ownsContentNodeUpdated = { [weak self] value in
guard let self, let component = self.component else {
@ -206,10 +216,10 @@ final class StoryItemContentComponent: Component {
}
}
override func setIsProgressPaused(_ isProgressPaused: Bool) {
if self.isProgressPaused != isProgressPaused {
self.isProgressPaused = isProgressPaused
self.updateIsProgressPaused(update: true)
override func setProgressMode(_ progressMode: StoryContentItem.ProgressMode) {
if self.progressMode != progressMode {
self.progressMode = progressMode
self.updateProgressMode(update: true)
}
}
@ -239,9 +249,9 @@ final class StoryItemContentComponent: Component {
}
}
private func updateIsProgressPaused(update: Bool) {
private func updateProgressMode(update: Bool) {
if let videoNode = self.videoNode {
var canPlay = !self.isProgressPaused && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
var canPlay = self.progressMode != .pause && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
if let component = self.component {
if component.item.isPending {
canPlay = false
@ -261,7 +271,7 @@ final class StoryItemContentComponent: Component {
}
private func updateProgressTimer() {
var needsTimer = !self.isProgressPaused && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
var needsTimer = self.progressMode != .pause && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
if let component = self.component {
if component.item.isPending {
needsTimer = false
@ -274,7 +284,7 @@ final class StoryItemContentComponent: Component {
timeout: 1.0 / 60.0,
repeat: true,
completion: { [weak self] in
guard let self, !self.isProgressPaused, self.contentLoaded, self.hierarchyTrackingLayer.isInHierarchy else {
guard let self, self.progressMode != .pause, self.contentLoaded, self.hierarchyTrackingLayer.isInHierarchy else {
return
}
@ -288,6 +298,10 @@ final class StoryItemContentComponent: Component {
}
}
if self.progressMode != .play {
return
}
#if DEBUG && true
let currentProgressTimerLimit: Double = 10.0
#else
@ -626,7 +640,7 @@ final class StoryItemContentComponent: Component {
self.backgroundColor = UIColor(rgb: 0x181818)
}
self.updateIsProgressPaused(update: false)
self.updateProgressMode(update: false)
if reloadMedia && synchronousLoad {
print("\(CFAbsoluteTimeGetCurrent()) Synchronous: \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms")

View File

@ -877,46 +877,50 @@ public final class StoryItemSetContainerComponent: Component {
}
private func isProgressPaused() -> Bool {
return self.itemProgressMode() == .pause
}
private func itemProgressMode() -> StoryContentItem.ProgressMode {
guard let component = self.component else {
return false
return .pause
}
if component.pinchState != nil {
return true
return .pause
}
if self.inputPanelExternalState.isEditing || component.isProgressPaused || self.sendMessageContext.actionSheet != nil || self.sendMessageContext.isViewingAttachedStickers || self.contextController != nil || self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil || self.displayViewList {
return true
return .pause
}
if let reactionContextNode = self.reactionContextNode, reactionContextNode.isReactionSearchActive {
return true
return .pause
}
if self.privacyController != nil {
return true
return .pause
}
if self.isReporting {
return true
return .pause
}
if self.isEditingStory {
return true
return .pause
}
if self.sendMessageContext.attachmentController != nil {
return true
return .pause
}
if self.sendMessageContext.shareController != nil {
return true
return .pause
}
if self.sendMessageContext.tooltipScreen != nil {
return true
return .pause
}
if let navigationController = component.controller()?.navigationController as? NavigationController {
let topViewController = navigationController.topViewController
if !(topViewController is StoryContainerScreen) && !(topViewController is MediaEditorScreen) && !(topViewController is ShareWithPeersScreen) && !(topViewController is AttachmentController) {
return true
return .pause
}
}
if let captionItem = self.captionItem, captionItem.externalState.isExpanded {
return true
return .blurred
}
return false
return .play
}
private func updateScrolling(transition: Transition) {
@ -1094,13 +1098,13 @@ public final class StoryItemSetContainerComponent: Component {
itemTransition.setCornerRadius(layer: visibleItem.contentContainerView.layer, cornerRadius: 12.0 * (1.0 / itemScale))
itemTransition.setAlpha(view: visibleItem.contentContainerView, alpha: 1.0 * (1.0 - fractionDistanceToCenter) + 0.75 * fractionDistanceToCenter)
var itemProgressPaused = self.isProgressPaused()
var itemProgressMode = self.itemProgressMode()
if index != centralIndex {
itemProgressPaused = true
itemProgressMode = .pause
}
if let view = view as? StoryContentItem.View {
view.setIsProgressPaused(itemProgressPaused)
view.setProgressMode(itemProgressMode)
}
}
}
@ -1121,7 +1125,7 @@ public final class StoryItemSetContainerComponent: Component {
}
func updateIsProgressPaused() {
let isProgressPaused = self.isProgressPaused()
let progressMode = self.itemProgressMode()
var centralId: Int32?
if let component = self.component {
centralId = component.slice.item.storyItem.id
@ -1130,7 +1134,11 @@ public final class StoryItemSetContainerComponent: Component {
for (id, visibleItem) in self.visibleItems {
if let view = visibleItem.view.view {
if let view = view as? StoryContentItem.View {
view.setIsProgressPaused(isProgressPaused || id != centralId)
var itemMode = progressMode
if id != centralId {
itemMode = .pause
}
view.setProgressMode(itemMode)
}
}
}
@ -2645,7 +2653,7 @@ public final class StoryItemSetContainerComponent: Component {
}
)),
environment: {},
containerSize: CGSize(width: availableSize.width, height: contentFrame.height)
containerSize: CGSize(width: availableSize.width, height: contentFrame.height - 60.0)
)
captionItem.view.parentState = state
let captionFrame = CGRect(origin: CGPoint(x: 0.0, y: contentFrame.height - captionSize.height), size: captionSize)