Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2023-06-06 03:41:23 +04:00
commit b280850bbc
17 changed files with 326 additions and 284 deletions

View File

@ -49,139 +49,6 @@ import StoryContainerScreen
import StoryContentComponent
import FullScreenEffectView
private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBarSearchContentNode) -> Bool {
if listNode.scroller.isDragging {
return false
}
let visibleStoriesProgress: CGFloat
do {
let fraction = navigationBarSearchContentHeight / searchNode.nominalHeight
let fromLow: CGFloat = fraction
let toLow: CGFloat = 0.0
let fromHigh: CGFloat = 1.0
let toHigh: CGFloat = 1.0
let visibleProgress: CGFloat = toLow + (searchNode.expansionProgress - fromLow) * (toHigh - toLow) / (fromHigh - fromLow)
visibleStoriesProgress = max(0.0, min(1.0, visibleProgress))
}
let visibleSearchProgress: CGFloat
do {
let fieldHeight: CGFloat = 36.0
let fraction = fieldHeight / searchNode.nominalHeight
let fullFraction = navigationBarSearchContentHeight / searchNode.nominalHeight
let fromLow: CGFloat = fullFraction - fraction
let toLow: CGFloat = 0.0
let fromHigh: CGFloat = fullFraction
let toHigh: CGFloat = 1.0
let visibleProgress: CGFloat = toLow + (searchNode.expansionProgress - fromLow) * (toHigh - toLow) / (fromHigh - fromLow)
visibleSearchProgress = max(0.0, min(1.0, visibleProgress))
}
//print("visibleStoriesProgress = \(visibleStoriesProgress)")
//print("visibleSearchProgress = \(visibleSearchProgress)")
if visibleStoriesProgress > 0.0 && visibleStoriesProgress < 1.0 {
let offset: CGFloat
if visibleStoriesProgress < 0.6 {
offset = 94.0
} else {
offset = 0.0
}
let _ = listNode.scrollToOffsetFromTop(offset, animated: true)
return true
}
if visibleSearchProgress > 0.0 && visibleSearchProgress < 1.0 {
let offset: CGFloat
if visibleSearchProgress < 0.6 {
offset = 94.0 + navigationBarSearchContentHeight
} else {
offset = 94.0
}
let _ = listNode.scrollToOffsetFromTop(offset, animated: true)
return true
}
if searchNode.expansionProgress > 0.0 && searchNode.expansionProgress < 0.2 {
let _ = listNode.scrollToOffsetFromTop(94.0 + navigationBarSearchContentHeight, animated: true)
return true
}
/*let storiesFraction = 94.0 / (navigationBarSearchContentHeight + 94.0)
var visibleStoriesProgress = max(0.0, min(1.0, searchNode.expansionProgress))
visibleStoriesProgress = (1.0 / storiesFraction) * visibleStoriesProgress
visibleStoriesProgress = max(0.0, min(1.0, visibleStoriesProgress))
let searchFieldHeight: CGFloat = 36.0
let searchFraction = searchFieldHeight / searchNode.nominalHeight
let visibleSearchProgress = max(0.0, min(1.0, searchNode.expansionProgress) - 1.0 + searchFraction) / searchFraction
if visibleSearchProgress > 0.0 && visibleSearchProgress < 1.0 {
let offset: CGFloat
if visibleSearchProgress < 0.6 {
offset = navigationBarSearchContentHeight
} else {
offset = 0.0
}
let _ = listNode.scrollToOffsetFromTop(offset, animated: true)
return true
} else if visibleStoriesProgress > 0.0 && visibleStoriesProgress < 1.0 {
let offset: CGFloat
if visibleStoriesProgress < 0.3 {
offset = navigationBarSearchContentHeight + 94.0
} else {
offset = navigationBarSearchContentHeight
}
let _ = listNode.scrollToOffsetFromTop(offset, animated: true)
return true
}
if "".isEmpty {
return false
}
if searchNode.expansionProgress > 0.0 && searchNode.expansionProgress < 1.0 {
let offset: CGFloat
if searchNode.expansionProgress < 0.6 {
offset = navigationBarSearchContentHeight
} else {
offset = 0.0
}
let _ = listNode.scrollToOffsetFromTop(offset, animated: true)
return true
} else if searchNode.expansionProgress == 1.0 {
var sortItemNode: ListViewItemNode?
var nextItemNode: ListViewItemNode?
listNode.forEachItemNode({ itemNode in
if sortItemNode == nil, let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item, case .groupReference = item.content {
sortItemNode = itemNode
} else if sortItemNode != nil && nextItemNode == nil {
nextItemNode = itemNode as? ListViewItemNode
}
})
if false, let sortItemNode = sortItemNode {
let itemFrame = sortItemNode.apparentFrame
if itemFrame.contains(CGPoint(x: 0.0, y: listNode.insets.top)) {
var scrollToItem: ListViewScrollToItem?
if itemFrame.minY + itemFrame.height * 0.6 < listNode.insets.top {
scrollToItem = ListViewScrollToItem(index: 0, position: .top(-76.0), animated: true, curve: .Default(duration: 0.3), directionHint: .Up)
} else {
scrollToItem = ListViewScrollToItem(index: 0, position: .top(0), animated: true, curve: .Default(duration: 0.3), directionHint: .Up)
}
listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: scrollToItem, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
return true
}
}
}*/
return false
}
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController
weak var sourceNode: ASDisplayNode?
@ -314,8 +181,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
private var preloadStorySubscriptionsDisposable: Disposable?
private var preloadStoryResourceDisposables: [MediaResourceId: Disposable] = [:]
private var storyListHeight: CGFloat
private var fullScreenEffectView: RippleEffectView?
public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) {
@ -349,8 +214,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.tabsNode = SparseNode()
self.tabContainerNode = ChatListFilterTabContainerNode()
self.tabsNode.addSubnode(self.tabContainerNode)
self.storyListHeight = 0.0
super.init(context: context, navigationBarPresentationData: nil, mediaAccessoryPanelVisibility: .always, locationBroadcastPanelSource: .summary, groupCallPanelSource: groupCallPanelSource)
@ -1879,8 +1742,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.storySubscriptions = storySubscriptions
let isEmpty = storySubscriptions.items.isEmpty
self.storyListHeight = isEmpty ? 0.0 : 94.0
let transition: ContainedViewLayoutTransition
if self.didAppear {
transition = .animated(duration: 0.4, curve: .spring)
@ -2647,7 +2508,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.mainContainerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.effectiveContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
}
self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: navigationBarHeight, storiesInset: self.storyListHeight, transition: transition)
self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: navigationBarHeight, storiesInset: 0.0, transition: transition)
}
override public func navigationStackConfigurationUpdated(next: [ViewController]) {
@ -3471,15 +3332,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
//TODO:swap tabs
let displaySearchFilters = true
/*if !tabsIsEmpty, let snapshotView = strongSelf.tabContainerNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = strongSelf.navigationSecondaryContentNode.frame
strongSelf.navigationSecondaryContentNode.view.superview?.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -strongSelf.storyListHeight), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
}*/
if let filterContainerNodeAndActivate = strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, displaySearchFilters: displaySearchFilters, hasDownloads: strongSelf.hasDownloads, initialFilter: filter, navigationController: strongSelf.navigationController as? NavigationController) {
let (filterContainerNode, activate) = filterContainerNodeAndActivate
@ -3524,37 +3376,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.searchTabsNode = nil
/*let tabsIsEmpty: Bool
if let (resolvedItems, displayTabsAtBottom, _) = self.tabContainerData {
tabsIsEmpty = resolvedItems.count <= 1 || displayTabsAtBottom
} else {
tabsIsEmpty = true
}
var filterContainerNode: ASDisplayNode?
if animated, let searchContentNode = self.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode {
filterContainerNode = searchContentNode.filterContainerNode
if let filterContainerNode = filterContainerNode, let snapshotView = filterContainerNode.view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = filterContainerNode.frame//.offsetBy(dx: self.navigationSecondaryContentNode.frame.minX, dy: self.navigationSecondaryContentNode.frame.minY)
filterContainerNode.view.superview?.addSubview(snapshotView)
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.storyListHeight), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
if !tabsIsEmpty {
Queue.mainQueue().after(0.01) {
self.tabContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.tabContainerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -74.0), to: .zero, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
}
}
}
}*/
var searchContentNode: NavigationBarSearchContentNode?
if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View {
searchContentNode = navigationBarView.searchContentNode
@ -3563,7 +3384,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
if let searchContentNode {
let previousFrame = searchContentNode.placeholderNode.frame
if case .chatList(.root) = self.location {
searchContentNode.placeholderNode.frame = previousFrame.offsetBy(dx: 0.0, dy: 94.0)
searchContentNode.placeholderNode.frame = previousFrame.offsetBy(dx: 0.0, dy: 79.0)
}
completion = self.chatListDisplayNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode, animated: animated)
searchContentNode.placeholderNode.frame = previousFrame

View File

@ -376,7 +376,7 @@ private final class ChatListContainerItemNode: ASDisplayNode {
self.listNode = ChatListNode(context: context, location: location, chatListFilter: filter, previewing: previewing, fillPreloadItems: controlsHistoryPreload, mode: chatListMode, theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: animationCache, animationRenderer: animationRenderer, disableAnimations: true, isInlineMode: isInlineMode)
if let controller, case .chatList(groupId: .root) = controller.location {
self.listNode.scrollHeightTopInset = navigationBarSearchContentHeight + 94.0
self.listNode.scrollHeightTopInset = navigationBarSearchContentHeight + 79.0
}
super.init()
@ -935,7 +935,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
} else if self.storiesUnlocked {
switch offset {
case let .known(value):
if value >= 94.0 {
if value >= 79.0 {
self.storiesUnlocked = false
self.onStoriesLockedUpdated?(false)
}
@ -950,7 +950,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
}
switch self.currentItemNode.visibleContentOffset() {
case let .known(value):
if value > 94.0 {
if value > 79.0 {
if self.storiesUnlocked {
self.storiesUnlocked = false
@ -1829,9 +1829,11 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
let headerContent = self.controller?.updateHeaderContent(layout: layout, transition: transition)
var tabsNode: ASDisplayNode?
var tabsNodeIsSearch = false
if let value = self.controller?.searchTabsNode {
tabsNode = value
tabsNodeIsSearch = true
} else if let value = self.controller?.tabsNode, self.controller?.hasTabs == true {
tabsNode = value
}
@ -1852,6 +1854,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
storySubscriptions: self.controller?.storySubscriptions,
uploadProgress: self.controller?.storyUploadProgress,
tabsNode: tabsNode,
tabsNodeIsSearch: tabsNodeIsSearch,
activateSearch: { [weak self] searchContentNode in
guard let self, let controller = self.controller else {
return
@ -2115,6 +2118,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
}
strongSelf.isSearchDisplayControllerActive = true
strongSelf.mainContainerNode.storiesUnlocked = false
strongSelf.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: cleanNavigationBarHeight, transition: .immediate)
strongSelf.searchDisplayController?.activate(insertSubnode: { [weak self] subnode, isSearchBar in
@ -2291,6 +2295,8 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
func setInlineChatList(inlineStackContainerNode: ChatListContainerNode?) {
if let inlineStackContainerNode = inlineStackContainerNode {
if self.inlineStackContainerNode !== inlineStackContainerNode {
self.mainContainerNode.storiesUnlocked = false
inlineStackContainerNode.leftSeparatorLayer.isHidden = false
inlineStackContainerNode.presentAlert = self.mainContainerNode.presentAlert
@ -2347,8 +2353,6 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
self.inlineStackContainerNode = nil
self.inlineStackContainerTransitionFraction = 0.0
self.mainContainerNode.contentScrollingEnded = self.contentScrollingEnded
if let _ = self.containerLayout {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)

View File

@ -1642,29 +1642,19 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
let wasIgnoringScrollingEvents = self.ignoreScrollingEvents
self.ignoreScrollingEvents = true
if topItemFound && bottomItemFound {
if self.scroller.contentSize != CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0) {
self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: completeHeight)
}
self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: completeHeight)
self.lastContentOffset = CGPoint(x: 0.0, y: -topItemEdge)
if self.scroller.contentOffset != self.lastContentOffset {
self.scroller.contentOffset = self.lastContentOffset
}
self.scroller.contentOffset = self.lastContentOffset
} else if topItemFound {
if self.scroller.contentSize != CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0) {
self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0)
}
self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0)
self.lastContentOffset = CGPoint(x: 0.0, y: -topItemEdge)
if self.scroller.contentOffset != self.lastContentOffset {
self.scroller.contentOffset = self.lastContentOffset
}
} else if bottomItemFound {
if self.scroller.contentSize != CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0) {
self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0)
}
self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0)
self.lastContentOffset = CGPoint(x: 0.0, y: infiniteScrollSize * 2.0 - bottomItemEdge)
if self.scroller.contentOffset != self.lastContentOffset {
self.scroller.contentOffset = self.lastContentOffset
}
self.scroller.contentOffset = self.lastContentOffset
} else if self.itemNodes.isEmpty {
self.scroller.contentSize = self.visibleSize
if self.lastContentOffset.y == infiniteScrollSize && self.scroller.contentOffset.y.isZero {
@ -1672,14 +1662,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
self.lastContentOffset = .zero
}
} else {
if self.scroller.contentSize != CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0) {
self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0)
}
self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0)
if abs(self.scroller.contentOffset.y - infiniteScrollSize) > infiniteScrollSize / 2.0 {
self.lastContentOffset = CGPoint(x: 0.0, y: infiniteScrollSize)
if self.scroller.contentOffset != self.lastContentOffset {
self.scroller.contentOffset = self.lastContentOffset
}
self.scroller.contentOffset = self.lastContentOffset
} else {
self.lastContentOffset = self.scroller.contentOffset
}

View File

@ -310,12 +310,28 @@ private final class MediaPlayerContext {
duration = max(duration, CMTimeGetSeconds(audioTrackFrameBuffer.duration))
}
loadedDuration = duration
let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: duration, dimensions: CGSize(), timestamp: min(max(timestamp, 0.0), duration), baseRate: self.baseRate, seekId: self.seekId, status: .buffering(initial: false, whilePlaying: action == .play, progress: 0.0, display: true), soundEnabled: self.enableSound)
let statusTimestamp: Double
if duration == 0.0 {
statusTimestamp = max(timestamp, 0.0)
} else {
statusTimestamp = min(max(timestamp, 0.0), duration)
}
let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: duration, dimensions: CGSize(), timestamp: statusTimestamp, baseRate: self.baseRate, seekId: self.seekId, status: .buffering(initial: false, whilePlaying: action == .play, progress: 0.0, display: true), soundEnabled: self.enableSound)
self.playerStatus.set(.single(status))
let _ = self.playerStatusValue.swap(status)
} else {
let duration = seekState?.duration ?? 0.0
let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: duration, dimensions: CGSize(), timestamp: min(max(timestamp, 0.0), duration), baseRate: self.baseRate, seekId: self.seekId, status: .buffering(initial: false, whilePlaying: action == .play, progress: 0.0, display: true), soundEnabled: self.enableSound)
let statusTimestamp: Double
if duration == 0.0 {
statusTimestamp = max(timestamp, 0.0)
} else {
statusTimestamp = min(max(timestamp, 0.0), duration)
}
let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: duration, dimensions: CGSize(), timestamp: statusTimestamp, baseRate: self.baseRate, seekId: self.seekId, status: .buffering(initial: false, whilePlaying: action == .play, progress: 0.0, display: true), soundEnabled: self.enableSound)
self.playerStatus.set(.single(status))
let _ = self.playerStatusValue.swap(status)
}
@ -974,7 +990,13 @@ private final class MediaPlayerContext {
if case .seeking(_, timestamp, _, _, _, _) = self.state {
reportTimestamp = timestamp
}
let status = MediaPlayerStatus(generationTimestamp: statusTimestamp, duration: duration, dimensions: CGSize(), timestamp: min(max(reportTimestamp, 0.0), duration), baseRate: self.baseRate, seekId: self.seekId, status: playbackStatus, soundEnabled: self.enableSound)
let statusTimestamp: Double
if duration == 0.0 {
statusTimestamp = max(reportTimestamp, 0.0)
} else {
statusTimestamp = min(max(reportTimestamp, 0.0), duration)
}
let status = MediaPlayerStatus(generationTimestamp: CACurrentMediaTime(), duration: duration, dimensions: CGSize(), timestamp: statusTimestamp, baseRate: self.baseRate, seekId: self.seekId, status: playbackStatus, soundEnabled: self.enableSound)
self.playerStatus.set(.single(status))
let _ = self.playerStatusValue.swap(status)
}

View File

@ -122,7 +122,7 @@ final class NetworkFrameworkTcpConnectionInterface: NSObject, MTTcpConnectionInt
}
}
connection.betterPathUpdateHandler = { [weak self] hasBetterPath in
/*connection.betterPathUpdateHandler = { [weak self] hasBetterPath in
queue.async {
guard let self = self else {
return
@ -131,7 +131,7 @@ final class NetworkFrameworkTcpConnectionInterface: NSObject, MTTcpConnectionInt
self.cancelWithError(error: nil)
}
}
}
}*/
self.connectTimeoutTimer = SwiftSignalKit.Timer(timeout: timeout, repeat: false, completion: { [weak self] in
guard let self = self else {

View File

@ -725,7 +725,7 @@ func _internal_markStoryAsSeen(account: Account, peerId: PeerId, id: Int32) -> S
#if DEBUG
if "".isEmpty {
//return .complete()
return .complete()
}
#endif

View File

@ -211,6 +211,13 @@ public final class StorySubscriptionsContext {
if !isRefresh {
flags |= 1 << 1
} else {
#if DEBUG
if "".isEmpty {
state = nil
flags &= ~(1 << 0)
}
#endif
}
}

View File

@ -731,7 +731,24 @@ public extension TelegramEngine {
}
items.sort(by: { lhs, rhs in
return lhs.lastTimestamp > rhs.lastTimestamp
if lhs.hasUnseen != rhs.hasUnseen {
if lhs.hasUnseen {
return true
} else {
return false
}
}
if lhs.peer.isPremium != rhs.peer.isPremium {
if lhs.peer.isPremium {
return true
} else {
return false
}
}
if lhs.lastTimestamp != rhs.lastTimestamp {
return lhs.lastTimestamp > rhs.lastTimestamp
}
return lhs.peer.id < rhs.peer.id
})
return EngineStorySubscriptions(accountItem: accountItem, items: items, hasMoreToken: hasMoreToken)

View File

@ -787,10 +787,6 @@ public final class ChatListHeaderComponent: Component {
return defaultResult
}
/*public func updateStories(offset: CGFloat, context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, storySubscriptions: EngineStorySubscriptions?, transition: Transition) {
}*/
private func updateContentStoryOffsets(transition: Transition) {
}
@ -879,7 +875,7 @@ public final class ChatListHeaderComponent: Component {
var sideContentWidth: CGFloat = 0.0
if let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty {
sideContentWidth = self.storyPeerListExternalState.collapsedWidth + 8.0
sideContentWidth = self.storyPeerListExternalState.collapsedWidth + 12.0
}
if let chatListTitle = primaryContent.chatListTitle {
if chatListTitle.activity {
@ -975,17 +971,17 @@ public final class ChatListHeaderComponent: Component {
self.addSubview(storyPeerListComponentView)
}
let storyPeerListMinOffset: CGFloat = -7.0
let storyPeerListMaxOffset: CGFloat = availableSize.height + 8.0
let storyPeerListMinOffset: CGFloat = -8.0
let storyPeerListMaxOffset: CGFloat = availableSize.height + 2.0
let storyPeerListPosition: CGFloat = storyPeerListMinOffset * (1.0 - component.storiesFraction) + storyPeerListMaxOffset * component.storiesFraction
var defaultStoryListX: CGFloat = 0.0
if let primaryContentView = self.primaryContentView {
defaultStoryListX = primaryContentView.centerContentOrigin - (self.storyPeerListExternalState.collapsedWidth * 0.5 + 8.0) - availableSize.width * 0.5
defaultStoryListX = primaryContentView.centerContentOrigin - (self.storyPeerListExternalState.collapsedWidth * 0.5 + 12.0) - availableSize.width * 0.5
}
storyListTransition.setFrame(view: storyPeerListComponentView, frame: CGRect(origin: CGPoint(x: -1.0 * availableSize.width * component.secondaryTransition + (1.0 - component.storiesFraction) * defaultStoryListX, y: storyPeerListPosition), size: CGSize(width: availableSize.width, height: 94.0)))
storyListTransition.setFrame(view: storyPeerListComponentView, frame: CGRect(origin: CGPoint(x: -1.0 * availableSize.width * component.secondaryTransition + (1.0 - component.storiesFraction) * defaultStoryListX, y: storyPeerListPosition), size: CGSize(width: availableSize.width, height: 79.0)))
var storyListNormalAlpha: CGFloat = 1.0
if let chatListTitle = component.primaryContent?.chatListTitle {

View File

@ -23,6 +23,7 @@ public final class ChatListNavigationBar: Component {
public let storySubscriptions: EngineStorySubscriptions?
public let uploadProgress: Float?
public let tabsNode: ASDisplayNode?
public let tabsNodeIsSearch: Bool
public let activateSearch: (NavigationBarSearchContentNode) -> Void
public let openStatusSetup: (UIView) -> Void
@ -40,6 +41,7 @@ public final class ChatListNavigationBar: Component {
storySubscriptions: EngineStorySubscriptions?,
uploadProgress: Float?,
tabsNode: ASDisplayNode?,
tabsNodeIsSearch: Bool,
activateSearch: @escaping (NavigationBarSearchContentNode) -> Void,
openStatusSetup: @escaping (UIView) -> Void
) {
@ -56,6 +58,7 @@ public final class ChatListNavigationBar: Component {
self.storySubscriptions = storySubscriptions
self.uploadProgress = uploadProgress
self.tabsNode = tabsNode
self.tabsNodeIsSearch = tabsNodeIsSearch
self.activateSearch = activateSearch
self.openStatusSetup = openStatusSetup
}
@ -97,7 +100,10 @@ public final class ChatListNavigationBar: Component {
if lhs.uploadProgress != rhs.uploadProgress {
return false
}
if lhs.tabsNode != rhs.tabsNode {
if lhs.tabsNode !== rhs.tabsNode {
return false
}
if lhs.tabsNodeIsSearch != rhs.tabsNodeIsSearch {
return false
}
return true
@ -139,7 +145,9 @@ public final class ChatListNavigationBar: Component {
private var applyScrollStartFraction: CGFloat = 0.0
private var tabsNode: ASDisplayNode?
private var tabsNodeIsSearch: Bool = false
private weak var disappearingTabsView: UIView?
private var disappearingTabsViewSearch: Bool = false
override public init(frame: CGRect) {
self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
@ -194,7 +202,7 @@ public final class ChatListNavigationBar: Component {
self.scrollStrings = component.strings
let searchOffsetDistance: CGFloat = navigationBarSearchContentHeight
let defaultStoriesOffsetDistance: CGFloat = 94.0
let defaultStoriesOffsetDistance: CGFloat = 79.0
let effectiveStoriesOffsetDistance: CGFloat
var minContentOffset: CGFloat = navigationBarSearchContentHeight
@ -214,7 +222,7 @@ public final class ChatListNavigationBar: Component {
let visibleSize = CGSize(width: currentLayout.size.width, height: max(0.0, currentLayout.size.height - clippedScrollOffset))
let previousHeight = self.backgroundView.bounds.height
let previousHeight = self.separatorLayer.position.y
self.backgroundView.update(size: CGSize(width: visibleSize.width, height: 1000.0), transition: transition.containedViewLayoutTransition)
@ -343,40 +351,54 @@ public final class ChatListNavigationBar: Component {
if component.tabsNode !== self.tabsNode {
if let tabsNode = self.tabsNode {
tabsNode.layer.anchorPoint = CGPoint()
self.tabsNode = nil
let disappearingTabsView = tabsNode.view
self.disappearingTabsViewSearch = self.tabsNodeIsSearch
self.disappearingTabsView = disappearingTabsView
transition.setAlpha(view: tabsNode.view, alpha: 0.0, completion: { [weak self, weak disappearingTabsView] _ in
guard let self, let disappearingTabsView else {
guard let self, let component = self.component, let disappearingTabsView else {
return
}
if self.tabsNode?.view !== disappearingTabsView {
if disappearingTabsView !== component.tabsNode?.view {
disappearingTabsView.removeFromSuperview()
}
})
}
}
let tabsFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height - 46.0), size: CGSize(width: visibleSize.width, height: 46.0))
var tabsFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height), size: CGSize(width: visibleSize.width, height: 46.0))
if component.tabsNode != nil {
tabsFrame.origin.y -= 46.0
}
if let disappearingTabsView = self.disappearingTabsView {
disappearingTabsView.layer.anchorPoint = CGPoint()
transition.setFrameWithAdditivePosition(view: disappearingTabsView, frame: tabsFrame)
transition.setFrameWithAdditivePosition(view: disappearingTabsView, frame: tabsFrame.offsetBy(dx: 0.0, dy: self.disappearingTabsViewSearch ? -56.0 : 0.0))
}
if let tabsNode = component.tabsNode {
self.tabsNode = tabsNode
self.tabsNodeIsSearch = component.tabsNodeIsSearch
var tabsNodeTransition = transition
if tabsNode.view.superview !== self {
tabsNode.view.layer.anchorPoint = CGPoint()
tabsNodeTransition = .immediate
tabsNode.view.alpha = 1.0
self.addSubview(tabsNode.view)
if !transition.animation.isImmediate {
tabsNode.view.alpha = 1.0
tabsNode.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
transition.animatePosition(view: tabsNode.view, from: CGPoint(x: 0.0, y: previousHeight - visibleSize.height), to: CGPoint(), additive: true)
if component.tabsNodeIsSearch {
transition.animatePosition(view: tabsNode.view, from: CGPoint(x: 0.0, y: previousHeight - visibleSize.height + 44.0), to: CGPoint(), additive: true)
} else {
transition.animatePosition(view: tabsNode.view, from: CGPoint(x: 0.0, y: previousHeight - visibleSize.height), to: CGPoint(), additive: true)
}
}
} else {
transition.setAlpha(view: tabsNode.view, alpha: 1.0)
}
tabsNodeTransition.setFrameWithAdditivePosition(view: tabsNode.view, frame: tabsFrame)
@ -419,7 +441,7 @@ public final class ChatListNavigationBar: Component {
self.effectiveStoriesInsetHeight = 0.0
} else {
if let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty, component.storiesUnlocked {
let storiesHeight: CGFloat = 94.0 * (1.0 - component.secondaryTransition)
let storiesHeight: CGFloat = 79.0 * (1.0 - component.secondaryTransition)
contentHeight += storiesHeight
self.effectiveStoriesInsetHeight = storiesHeight
} else {
@ -444,7 +466,7 @@ public final class ChatListNavigationBar: Component {
}
}
if storiesUnlockedUpdated {
if storiesUnlockedUpdated && component.storiesUnlocked {
self.applyScrollFraction = 0.0
self.applyScrollStartFraction = 0.0
self.applyScrollFractionAnimator = DisplayLinkAnimator(duration: 0.3, from: 0.0, to: 1.0, update: { [weak self] value in

View File

@ -269,9 +269,9 @@ private final class StoryContainerScreenComponent: Component {
self.itemSetPanState = nil
self.state?.updated(transition: .immediate)
if let component = self.component {
/*if let component = self.component {
component.content.resetSideStates()
}
}*/
})
}
}

View File

@ -21,11 +21,11 @@ public final class StoryContentItem {
public final class Environment: Equatable {
public let externalState: ExternalState
public let presentationProgressUpdated: (Double) -> Void
public let presentationProgressUpdated: (Double, Bool) -> Void
public init(
externalState: ExternalState,
presentationProgressUpdated: @escaping (Double) -> Void
presentationProgressUpdated: @escaping (Double, Bool) -> Void
) {
self.externalState = externalState
self.presentationProgressUpdated = presentationProgressUpdated

View File

@ -488,7 +488,7 @@ public final class StoryItemSetContainerComponent: Component {
environment: {
StoryContentItem.Environment(
externalState: visibleItem.externalState,
presentationProgressUpdated: { [weak self, weak visibleItem] progress in
presentationProgressUpdated: { [weak self, weak visibleItem] progress, canSwitch in
guard let self = self, let component = self.component else {
return
}
@ -500,7 +500,7 @@ public final class StoryItemSetContainerComponent: Component {
if let navigationStripView = self.navigationStrip.view as? MediaNavigationStripComponent.View {
navigationStripView.updateCurrentItemProgress(value: progress, transition: .immediate)
}
if progress >= 1.0 && !visibleItem.requestedNext {
if progress >= 1.0 && canSwitch && !visibleItem.requestedNext {
visibleItem.requestedNext = true
component.navigate(.next)

View File

@ -329,6 +329,8 @@ public final class StoryContentContextImpl: StoryContentContext {
private var pendingStateReadyDisposable: Disposable?
private var storySubscriptions: EngineStorySubscriptions?
private var fixedSubscriptionOrder: [EnginePeer.Id] = []
private var startedWithUnseen: Bool?
private var storySubscriptionsDisposable: Disposable?
private var requestedStoryKeys = Set<StoryKey>()
@ -350,7 +352,66 @@ public final class StoryContentContextImpl: StoryContentContext {
guard let self else {
return
}
self.storySubscriptions = storySubscriptions
let startedWithUnseen: Bool
if let current = self.startedWithUnseen {
startedWithUnseen = current
} else {
var startedWithUnseenValue = false
if let (focusedPeerId, _) = self.focusedItem, focusedPeerId == self.context.account.peerId {
} else {
var centralIndex: Int?
if let (focusedPeerId, _) = self.focusedItem {
if let index = storySubscriptions.items.firstIndex(where: { $0.peer.id == focusedPeerId }) {
centralIndex = index
}
}
if centralIndex == nil {
if let index = storySubscriptions.items.firstIndex(where: { $0.hasUnseen }) {
centralIndex = index
}
}
if centralIndex == nil {
if !storySubscriptions.items.isEmpty {
centralIndex = 0
}
}
if let centralIndex {
if storySubscriptions.items[centralIndex].hasUnseen {
startedWithUnseenValue = true
}
}
}
self.startedWithUnseen = startedWithUnseenValue
startedWithUnseen = startedWithUnseenValue
}
var sortedItems: [EngineStorySubscriptions.Item] = []
for peerId in self.fixedSubscriptionOrder {
if let index = storySubscriptions.items.firstIndex(where: { $0.peer.id == peerId }) {
sortedItems.append(storySubscriptions.items[index])
}
}
for item in storySubscriptions.items {
if !sortedItems.contains(where: { $0.peer.id == item.peer.id }) {
if startedWithUnseen {
if !item.hasUnseen {
continue
}
}
sortedItems.append(item)
}
}
self.fixedSubscriptionOrder = sortedItems.map(\.peer.id)
self.storySubscriptions = EngineStorySubscriptions(
accountItem: storySubscriptions.accountItem,
items: sortedItems,
hasMoreToken: storySubscriptions.hasMoreToken
)
self.updatePeerContexts()
})
}
@ -372,7 +433,9 @@ public final class StoryContentContextImpl: StoryContentContext {
}
private func switchToFocusedPeerId() {
if let storySubscriptions = self.storySubscriptions {
if let currentStorySubscriptions = self.storySubscriptions {
let subscriptionItems = currentStorySubscriptions.items
if self.pendingState == nil {
let loadIds: ([StoryKey]) -> Void = { [weak self] keys in
guard let self else {
@ -428,39 +491,39 @@ public final class StoryContentContextImpl: StoryContentContext {
} else {
var centralIndex: Int?
if let (focusedPeerId, _) = self.focusedItem {
if let index = storySubscriptions.items.firstIndex(where: { $0.peer.id == focusedPeerId }) {
if let index = subscriptionItems.firstIndex(where: { $0.peer.id == focusedPeerId }) {
centralIndex = index
}
}
if centralIndex == nil {
if !storySubscriptions.items.isEmpty {
if !subscriptionItems.isEmpty {
centralIndex = 0
}
}
if let centralIndex {
let centralPeerContext: PeerContext
if let currentState = self.currentState, let existingContext = currentState.findPeerContext(id: storySubscriptions.items[centralIndex].peer.id) {
if let currentState = self.currentState, let existingContext = currentState.findPeerContext(id: subscriptionItems[centralIndex].peer.id) {
centralPeerContext = existingContext
} else {
centralPeerContext = PeerContext(context: self.context, peerId: storySubscriptions.items[centralIndex].peer.id, focusedId: nil, loadIds: loadIds)
centralPeerContext = PeerContext(context: self.context, peerId: subscriptionItems[centralIndex].peer.id, focusedId: nil, loadIds: loadIds)
}
var previousPeerContext: PeerContext?
if centralIndex != 0 {
if let currentState = self.currentState, let existingContext = currentState.findPeerContext(id: storySubscriptions.items[centralIndex - 1].peer.id) {
if let currentState = self.currentState, let existingContext = currentState.findPeerContext(id: subscriptionItems[centralIndex - 1].peer.id) {
previousPeerContext = existingContext
} else {
previousPeerContext = PeerContext(context: self.context, peerId: storySubscriptions.items[centralIndex - 1].peer.id, focusedId: nil, loadIds: loadIds)
previousPeerContext = PeerContext(context: self.context, peerId: subscriptionItems[centralIndex - 1].peer.id, focusedId: nil, loadIds: loadIds)
}
}
var nextPeerContext: PeerContext?
if centralIndex != storySubscriptions.items.count - 1 {
if let currentState = self.currentState, let existingContext = currentState.findPeerContext(id: storySubscriptions.items[centralIndex + 1].peer.id) {
if centralIndex != subscriptionItems.count - 1 {
if let currentState = self.currentState, let existingContext = currentState.findPeerContext(id: subscriptionItems[centralIndex + 1].peer.id) {
nextPeerContext = existingContext
} else {
nextPeerContext = PeerContext(context: self.context, peerId: storySubscriptions.items[centralIndex + 1].peer.id, focusedId: nil, loadIds: loadIds)
nextPeerContext = PeerContext(context: self.context, peerId: subscriptionItems[centralIndex + 1].peer.id, focusedId: nil, loadIds: loadIds)
}
}

View File

@ -173,7 +173,7 @@ final class StoryItemContentComponent: Component {
guard let self else {
return
}
self.environment?.presentationProgressUpdated(1.0)
self.environment?.presentationProgressUpdated(1.0, true)
}
videoNode.ownsContentNodeUpdated = { [weak self] value in
guard let self else {
@ -244,7 +244,7 @@ final class StoryItemContentComponent: Component {
}
}
#if DEBUG// && false
#if DEBUG && false
let currentProgressTimerLimit: Double = 1 * 60.0
#else
let currentProgressTimerLimit: Double = 5.0
@ -254,7 +254,7 @@ final class StoryItemContentComponent: Component {
currentProgressTimerValue = max(0.0, min(currentProgressTimerLimit, currentProgressTimerValue))
self.currentProgressTimerValue = currentProgressTimerValue
self.environment?.presentationProgressUpdated(currentProgressTimerValue / currentProgressTimerLimit)
self.environment?.presentationProgressUpdated(currentProgressTimerValue / currentProgressTimerLimit, true)
}
}, queue: .mainQueue()
)
@ -280,10 +280,20 @@ final class StoryItemContentComponent: Component {
default:
break
}
let effectiveDuration: Double
if videoPlaybackStatus.duration > 0.0 {
effectiveDuration = videoPlaybackStatus.duration
} else if case let .file(file) = self.currentMessageMedia, let duration = file.duration {
effectiveDuration = Double(max(1, duration))
} else {
effectiveDuration = 1.0
}
if case .buffering(true, _, _, _) = videoPlaybackStatus.status {
timestampAndDuration = (nil, videoPlaybackStatus.duration)
} else if Double(0.0).isLess(than: videoPlaybackStatus.duration) {
timestampAndDuration = (videoPlaybackStatus.timestamp, videoPlaybackStatus.duration)
timestampAndDuration = (nil, effectiveDuration)
} else if effectiveDuration > 0.0 {
timestampAndDuration = (videoPlaybackStatus.timestamp, effectiveDuration)
}
var currentProgress: Double = 0.0
@ -316,7 +326,7 @@ final class StoryItemContentComponent: Component {
}
let clippedProgress = max(0.0, min(1.0, currentProgress))
self.environment?.presentationProgressUpdated(clippedProgress)
self.environment?.presentationProgressUpdated(clippedProgress, false)
}
func update(component: StoryItemContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<StoryContentItem.Environment>, transition: Transition) -> CGSize {

View File

@ -233,7 +233,8 @@ public final class StoryPeerListComponent: Component {
component.externalState.collapsedWidth = collapsedContentWidth
let visibleBounds = self.scrollView.bounds
let effectiveVisibleBounds = self.scrollView.bounds
let visibleBounds = effectiveVisibleBounds.insetBy(dx: -200.0, dy: 0.0)
var validIds: [EnginePeer.Id] = []
for i in 0 ..< self.sortedItems.count {
@ -242,12 +243,11 @@ public final class StoryPeerListComponent: Component {
let regularItemFrame = itemLayout.frame(at: i)
if !visibleBounds.intersects(regularItemFrame) {
/*if keepVisibleUntilCompletion && self.visibleItems[itemSet.peerId] != nil {
} else {*/
continue
//}
continue
}
let isReallyVisible = effectiveVisibleBounds.intersects(regularItemFrame)
validIds.append(itemSet.peer.id)
let visibleItem: VisibleItem
@ -274,16 +274,36 @@ public final class StoryPeerListComponent: Component {
itemProgress = component.uploadProgress
}
let collapsedItemFrame = CGRect(origin: CGPoint(x: collapsedContentOrigin + CGFloat(i - collapseStartIndex) * collapsedItemDistance, y: regularItemFrame.minY + collapsedItemOffsetY), size: CGSize(width: collapsedItemWidth, height: regularItemFrame.height))
let collapsedItemX: CGFloat
let collapsedItemScaleFactor: CGFloat
if i < collapseStartIndex {
collapsedItemX = collapsedContentOrigin
collapsedItemScaleFactor = 0.1
} else if i > collapseEndIndex {
collapsedItemX = collapsedContentOrigin + CGFloat(collapseEndIndex) * collapsedItemDistance - collapsedItemWidth * 0.5
collapsedItemScaleFactor = 0.1
} else {
collapsedItemX = collapsedContentOrigin + CGFloat(i - collapseStartIndex) * collapsedItemDistance
collapsedItemScaleFactor = 1.0
}
let collapsedItemFrame = CGRect(origin: CGPoint(x: collapsedItemX, y: regularItemFrame.minY + collapsedItemOffsetY), size: CGSize(width: collapsedItemWidth, height: regularItemFrame.height))
let itemFrame = regularItemFrame.interpolate(to: collapsedItemFrame, amount: component.collapseFraction)
let itemFrame: CGRect
if isReallyVisible {
itemFrame = regularItemFrame.interpolate(to: collapsedItemFrame, amount: component.collapseFraction)
} else {
itemFrame = regularItemFrame
}
var leftItemFrame: CGRect?
var rightItemFrame: CGRect?
var itemAlpha: CGFloat = 1.0
var isCollapsable: Bool = false
if i >= collapseStartIndex && i <= collapseEndIndex {
isCollapsable = true
if i != collapseStartIndex {
let regularLeftItemFrame = itemLayout.frame(at: i - 1)
let collapsedLeftItemFrame = CGRect(origin: CGPoint(x: collapsedContentOrigin + CGFloat(i - collapseStartIndex - 1) * collapsedItemDistance, y: regularLeftItemFrame.minY), size: CGSize(width: collapsedItemWidth, height: regularLeftItemFrame.height))
@ -295,7 +315,11 @@ public final class StoryPeerListComponent: Component {
rightItemFrame = regularRightItemFrame.interpolate(to: collapsedRightItemFrame, amount: component.collapseFraction)
}
} else {
itemAlpha = pow(1.0 - component.collapseFraction, 1.5)
if component.collapseFraction == 1.0 {
itemAlpha = 0.0
} else {
itemAlpha = 1.0
}
}
var leftNeighborDistance: CGFloat?
@ -318,7 +342,8 @@ public final class StoryPeerListComponent: Component {
hasUnseen: hasUnseen,
hasItems: hasItems,
progress: itemProgress,
collapseFraction: component.collapseFraction,
collapseFraction: isReallyVisible ? component.collapseFraction : 0.0,
collapsedScaleFactor: collapsedItemScaleFactor,
collapsedWidth: collapsedItemWidth,
leftNeighborDistance: leftNeighborDistance,
rightNeighborDistance: rightNeighborDistance,
@ -329,14 +354,27 @@ public final class StoryPeerListComponent: Component {
containerSize: itemLayout.itemSize
)
if let itemView = visibleItem.view.view {
if let itemView = visibleItem.view.view as? StoryPeerListItemComponent.View {
if itemView.superview == nil {
self.scrollView.addSubview(itemView)
self.scrollView.addSubview(itemView.backgroundContainer)
}
itemView.layer.zPosition = 1000.0 - CGFloat(i) * 0.01
if isCollapsable {
itemView.layer.zPosition = 1000.0 - CGFloat(i) * 0.01
itemView.backgroundContainer.layer.zPosition = 1.0
} else {
itemView.layer.zPosition = 0.5
itemView.backgroundContainer.layer.zPosition = 0.0
}
itemTransition.setFrame(view: itemView, frame: itemFrame)
itemTransition.setAlpha(view: itemView, alpha: itemAlpha)
itemTransition.setScale(view: itemView, scale: itemScale)
itemTransition.setFrame(view: itemView.backgroundContainer, frame: itemFrame)
itemTransition.setAlpha(view: itemView.backgroundContainer, alpha: itemAlpha)
itemTransition.setScale(view: itemView.backgroundContainer, scale: itemScale)
}
}
@ -344,12 +382,15 @@ public final class StoryPeerListComponent: Component {
for (id, visibleItem) in self.visibleItems {
if !validIds.contains(id) {
removedIds.append(id)
if let itemView = visibleItem.view.view {
if let itemView = visibleItem.view.view as? StoryPeerListItemComponent.View {
if keepVisibleUntilCompletion && !transition.animation.isImmediate {
transition.attachAnimation(view: itemView, id: "keep", completion: { [weak itemView] _ in
let backgroundContainer = itemView.backgroundContainer
transition.attachAnimation(view: itemView, id: "keep", completion: { [weak itemView, weak backgroundContainer] _ in
itemView?.removeFromSuperview()
backgroundContainer?.removeFromSuperview()
})
} else {
itemView.backgroundContainer.removeFromSuperview()
itemView.removeFromSuperview()
}
}
@ -423,7 +464,7 @@ public final class StoryPeerListComponent: Component {
let itemLayout = ItemLayout(
containerSize: availableSize,
containerInsets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 10.0),
containerInsets: UIEdgeInsets(top: 4.0, left: 10.0, bottom: 0.0, right: 10.0),
itemSize: CGSize(width: 60.0, height: 77.0),
itemSpacing: 24.0,
itemCount: self.sortedItems.count
@ -432,7 +473,7 @@ public final class StoryPeerListComponent: Component {
self.ignoreScrolling = true
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize))
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(x: 0.0, y: -4.0), size: CGSize(width: availableSize.width, height: availableSize.height + 4.0)))
if self.scrollView.contentSize != itemLayout.contentSize {
self.scrollView.contentSize = itemLayout.contentSize
}

View File

@ -141,6 +141,8 @@ private final class StoryProgressLayer: SimpleShapeLayer {
}
}
private var sharedAvatarBackgroundImage: UIImage?
public final class StoryPeerListItemComponent: Component {
public let context: AccountContext
public let theme: PresentationTheme
@ -150,6 +152,7 @@ public final class StoryPeerListItemComponent: Component {
public let hasItems: Bool
public let progress: Float?
public let collapseFraction: CGFloat
public let collapsedScaleFactor: CGFloat
public let collapsedWidth: CGFloat
public let leftNeighborDistance: CGFloat?
public let rightNeighborDistance: CGFloat?
@ -165,6 +168,7 @@ public final class StoryPeerListItemComponent: Component {
hasItems: Bool,
progress: Float?,
collapseFraction: CGFloat,
collapsedScaleFactor: CGFloat,
collapsedWidth: CGFloat,
leftNeighborDistance: CGFloat?,
rightNeighborDistance: CGFloat?,
@ -179,6 +183,7 @@ public final class StoryPeerListItemComponent: Component {
self.hasItems = hasItems
self.progress = progress
self.collapseFraction = collapseFraction
self.collapsedScaleFactor = collapsedScaleFactor
self.collapsedWidth = collapsedWidth
self.leftNeighborDistance = leftNeighborDistance
self.rightNeighborDistance = rightNeighborDistance
@ -211,6 +216,9 @@ public final class StoryPeerListItemComponent: Component {
if lhs.collapseFraction != rhs.collapseFraction {
return false
}
if lhs.collapsedScaleFactor != rhs.collapsedScaleFactor {
return false
}
if lhs.collapsedWidth != rhs.collapsedWidth {
return false
}
@ -224,6 +232,8 @@ public final class StoryPeerListItemComponent: Component {
}
public final class View: UIView {
let backgroundContainer: UIView
private let extractedContainerNode: ContextExtractedContentContainingNode
private let containerNode: ContextControllerSourceNode
private let extractedBackgroundView: UIImageView
@ -231,6 +241,8 @@ public final class StoryPeerListItemComponent: Component {
private let button: HighlightTrackingButton
private let avatarContainer: UIView
private let avatarBackgroundContainer: UIView
private let avatarBackgroundView: UIImageView
private var avatarNode: AvatarNode?
private var avatarAddBadgeView: UIImageView?
private let avatarShapeLayer: SimpleShapeLayer
@ -244,6 +256,9 @@ public final class StoryPeerListItemComponent: Component {
private weak var componentState: EmptyComponentState?
public override init(frame: CGRect) {
self.backgroundContainer = UIView()
self.backgroundContainer.isUserInteractionEnabled = false
self.button = HighlightTrackingButton()
self.extractedContainerNode = ContextExtractedContentContainingNode()
@ -254,6 +269,9 @@ public final class StoryPeerListItemComponent: Component {
self.avatarContainer = UIView()
self.avatarContainer.isUserInteractionEnabled = false
self.avatarBackgroundContainer = UIView()
self.avatarBackgroundView = UIImageView()
self.avatarShapeLayer = SimpleShapeLayer()
self.indicatorColorLayer = SimpleGradientLayer()
@ -272,6 +290,9 @@ public final class StoryPeerListItemComponent: Component {
self.containerNode.targetNodeForActivationProgress = self.extractedContainerNode.contentNode
self.addSubview(self.containerNode.view)
self.backgroundContainer.addSubview(self.avatarBackgroundContainer)
self.avatarBackgroundContainer.addSubview(self.avatarBackgroundView)
self.extractedContainerNode.contentNode.view.addSubview(self.button)
self.button.addSubview(self.avatarContainer)
@ -284,7 +305,6 @@ public final class StoryPeerListItemComponent: Component {
self.indicatorShapeLayer.fillColor = nil
self.indicatorShapeLayer.strokeColor = UIColor.white.cgColor
self.indicatorShapeLayer.lineWidth = 2.0
self.indicatorShapeLayer.lineCap = .round
self.button.highligthedChanged = { [weak self] highlighted in
@ -368,7 +388,7 @@ public final class StoryPeerListItemComponent: Component {
let effectiveWidth: CGFloat = (1.0 - component.collapseFraction) * availableSize.width + component.collapseFraction * component.collapsedWidth
let effectiveScale: CGFloat = 1.0 * (1.0 - component.collapseFraction) + (24.0 / 52.0) * component.collapseFraction
let effectiveScale: CGFloat = 1.0 * (1.0 - component.collapseFraction) + (25.0 / 52.0) * component.collapsedScaleFactor * component.collapseFraction
let avatarNode: AvatarNode
if let current = self.avatarNode {
@ -382,13 +402,36 @@ public final class StoryPeerListItemComponent: Component {
}
let avatarSize = CGSize(width: 52.0, height: 52.0)
let avatarBackgroundImage: UIImage?
if let sharedAvatarBackgroundImage = sharedAvatarBackgroundImage, sharedAvatarBackgroundImage.size.width == avatarSize.width {
avatarBackgroundImage = sharedAvatarBackgroundImage
} else {
avatarBackgroundImage = generateFilledCircleImage(diameter: avatarSize.width, color: .white)?.withRenderingMode(.alwaysTemplate)
}
self.avatarBackgroundView.image = avatarBackgroundImage
self.avatarBackgroundView.isHidden = component.progress != nil
if themeUpdated {
self.avatarBackgroundView.tintColor = component.theme.rootController.navigationBar.opaqueBackgroundColor
}
let avatarFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - avatarSize.width) * 0.5) + (effectiveWidth - availableSize.width) * 0.5, y: 4.0), size: avatarSize)
transition.setFrame(view: avatarNode.view, frame: CGRect(origin: CGPoint(), size: avatarFrame.size))
transition.setFrame(view: self.avatarBackgroundView, frame: CGRect(origin: CGPoint(), size: avatarFrame.size).insetBy(dx: -3.0 - UIScreenPixel * 2.0, dy: -3.0 - UIScreenPixel * 2.0))
let indicatorFrame = avatarFrame.insetBy(dx: -4.0, dy: -4.0)
let indicatorFrame = avatarFrame.insetBy(dx: -6.0, dy: -6.0)
let indicatorLineWidth: CGFloat = 2.0 * (1.0 - component.collapseFraction) + (1.33 * (1.0 / effectiveScale)) * (component.collapseFraction)
let baseLineWidth: CGFloat
let minimizedLineWidth: CGFloat = 3.0
if component.hasUnseen || component.progress != nil {
baseLineWidth = 2.0
} else {
baseLineWidth = 1.0 + UIScreenPixel
}
let indicatorLineWidth: CGFloat = baseLineWidth * (1.0 - component.collapseFraction) + minimizedLineWidth * component.collapseFraction
avatarNode.setPeer(
context: component.context,
@ -399,9 +442,13 @@ public final class StoryPeerListItemComponent: Component {
transition.setPosition(view: self.avatarContainer, position: avatarFrame.center)
transition.setBounds(view: self.avatarContainer, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size))
let scaledAvatarSize = effectiveScale * (avatarSize.width + 4.0 - indicatorLineWidth * 2.0)
transition.setPosition(view: self.avatarBackgroundContainer, position: avatarFrame.center)
transition.setBounds(view: self.avatarBackgroundContainer, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size))
let scaledAvatarSize = effectiveScale * (avatarSize.width + 4.0 - 2.0 * 2.0)
transition.setScale(view: self.avatarContainer, scale: scaledAvatarSize / avatarSize.width)
transition.setScale(view: self.avatarBackgroundContainer, scale: scaledAvatarSize / avatarSize.width)
if component.peer.id == component.context.account.peerId && !component.hasItems && component.progress == nil {
self.indicatorColorLayer.isHidden = true
@ -438,7 +485,7 @@ public final class StoryPeerListItemComponent: Component {
context.strokePath()
})
}
avatarAddBadgeTransition.setFrame(view: avatarAddBadgeView, frame: CGRect(origin: CGPoint(x: avatarFrame.width - 1.0 - badgeSize.width, y: avatarFrame.height - 1.0 - badgeSize.height), size: badgeSize))
avatarAddBadgeTransition.setFrame(view: avatarAddBadgeView, frame: CGRect(origin: CGPoint(x: avatarFrame.width - 1.0 - badgeSize.width, y: avatarFrame.height - 2.0 - badgeSize.height), size: badgeSize))
} else {
if indicatorColorLayer.isHidden {
self.indicatorColorLayer.isHidden = false
@ -450,6 +497,12 @@ public final class StoryPeerListItemComponent: Component {
}
}
let baseRadius: CGFloat = 30.0
let collapsedRadius: CGFloat = 32.0
let indicatorRadius: CGFloat = baseRadius * (1.0 - component.collapseFraction) + collapsedRadius * component.collapseFraction
self.indicatorShapeLayer.lineWidth = indicatorLineWidth
if hadUnseen != component.hasUnseen || hadProgress != (component.progress != nil) {
let locations: [CGFloat] = [0.0, 1.0]
let colors: [CGColor]
@ -486,13 +539,13 @@ public final class StoryPeerListItemComponent: Component {
avatarPath.addEllipse(in: CGRect(origin: CGPoint(), size: avatarSize).insetBy(dx: -1.0, dy: -1.0))
if component.peer.id == component.context.account.peerId && !component.hasItems && component.progress == nil {
let cutoutSize: CGFloat = 18.0 + UIScreenPixel * 2.0
avatarPath.addEllipse(in: CGRect(origin: CGPoint(x: avatarSize.width - cutoutSize + UIScreenPixel, y: avatarSize.height - cutoutSize + UIScreenPixel), size: CGSize(width: cutoutSize, height: cutoutSize)))
avatarPath.addEllipse(in: CGRect(origin: CGPoint(x: avatarSize.width - cutoutSize + UIScreenPixel, y: avatarSize.height - 1.0 - cutoutSize + UIScreenPixel), size: CGSize(width: cutoutSize, height: cutoutSize)))
} else if let mappedRightCenter {
avatarPath.addEllipse(in: CGRect(origin: CGPoint(), size: avatarSize).insetBy(dx: -indicatorLineWidth, dy: -indicatorLineWidth).offsetBy(dx: abs(mappedRightCenter.x - indicatorCenter.x), dy: 0.0))
}
self.avatarShapeLayer.path = avatarPath
self.indicatorShapeLayer.path = calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorFrame.width * 0.5 - indicatorLineWidth * 0.5)
self.indicatorShapeLayer.path = calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineWidth * 0.5)
//TODO:localize
let titleString: String
@ -524,14 +577,14 @@ public final class StoryPeerListItemComponent: Component {
environment: {},
containerSize: CGSize(width: availableSize.width + 4.0, height: 100.0)
)
let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5) + (effectiveWidth - availableSize.width) * 0.25, y: indicatorFrame.midY + (indicatorFrame.height * 0.5 + 3.0) * effectiveScale), size: titleSize)
let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5) + (effectiveWidth - availableSize.width) * 0.5, y: indicatorFrame.midY + (indicatorFrame.height * 0.5 + 3.0) * effectiveScale), size: titleSize)
if let titleView = self.title.view {
if titleView.superview == nil {
titleView.layer.anchorPoint = CGPoint()
titleView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
titleView.isUserInteractionEnabled = false
self.button.addSubview(titleView)
}
titleTransition.setPosition(view: titleView, position: titleFrame.origin)
titleTransition.setPosition(view: titleView, position: titleFrame.center)
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
transition.setScale(view: titleView, scale: effectiveScale)
transition.setAlpha(view: titleView, alpha: 1.0 - component.collapseFraction)