mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
b280850bbc
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -725,7 +725,7 @@ func _internal_markStoryAsSeen(account: Account, peerId: PeerId, id: Int32) -> S
|
||||
|
||||
#if DEBUG
|
||||
if "".isEmpty {
|
||||
//return .complete()
|
||||
return .complete()
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -211,6 +211,13 @@ public final class StorySubscriptionsContext {
|
||||
|
||||
if !isRefresh {
|
||||
flags |= 1 << 1
|
||||
} else {
|
||||
#if DEBUG
|
||||
if "".isEmpty {
|
||||
state = nil
|
||||
flags &= ~(1 << 0)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}*/
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user