mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
4cc2eb7850
@ -1737,7 +1737,7 @@ public final class EngineStoryViewListContext {
|
||||
}
|
||||
|
||||
init(account: Account, storyId: Int32, views: EngineStoryItem.Views) {
|
||||
let queue = Queue()
|
||||
let queue = Queue.mainQueue()
|
||||
self.queue = queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return Impl(queue: queue, account: account, storyId: storyId, views: views)
|
||||
|
@ -861,7 +861,7 @@ public extension TelegramEngine {
|
||||
let _ = accountPeer
|
||||
let _ = storiesStateView
|
||||
|
||||
var sortedItems: [(peer: Peer, item: Stories.Item)] = []
|
||||
var sortedItems: [(peer: Peer, item: Stories.Item, hasUnseen: Bool, lastTimestamp: Int32)] = []
|
||||
|
||||
for peerId in storySubscriptionsView.peerIds {
|
||||
guard let peerView = views.views[PostboxViewKey.basicPeer(peerId)] as? BasicPeerView else {
|
||||
@ -878,21 +878,48 @@ public extension TelegramEngine {
|
||||
}
|
||||
|
||||
var nextItem: Stories.StoredItem? = itemsView.items.first?.value.get(Stories.StoredItem.self)
|
||||
let lastTimestamp = itemsView.items.last?.value.get(Stories.StoredItem.self)?.timestamp
|
||||
|
||||
let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self)
|
||||
var hasUnseen = false
|
||||
if let peerState = peerState {
|
||||
if let item = itemsView.items.first(where: { $0.id > peerState.maxReadId }) {
|
||||
hasUnseen = true
|
||||
nextItem = item.value.get(Stories.StoredItem.self)
|
||||
}
|
||||
}
|
||||
|
||||
if let nextItem = nextItem, case let .item(item) = nextItem {
|
||||
sortedItems.append((peer, item))
|
||||
if let nextItem = nextItem, case let .item(item) = nextItem, let lastTimestamp {
|
||||
sortedItems.append((peer, item, hasUnseen, lastTimestamp))
|
||||
}
|
||||
}
|
||||
|
||||
sortedItems.sort(by: { lhs, rhs in
|
||||
return lhs.item.timestamp > rhs.item.timestamp
|
||||
if lhs.hasUnseen != rhs.hasUnseen {
|
||||
if lhs.hasUnseen {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if EnginePeer(lhs.peer).isService != EnginePeer(rhs.peer).isService {
|
||||
if EnginePeer(lhs.peer).isService {
|
||||
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
|
||||
})
|
||||
|
||||
var nextPriority: Int = 0
|
||||
|
@ -337,7 +337,7 @@ public final class ChatListNavigationBar: Component {
|
||||
if allowAvatarsExpansion && transition.animation.isImmediate {
|
||||
if self.storiesUnlocked != storiesUnlocked {
|
||||
if storiesUnlocked {
|
||||
HapticFeedback().impact(.veryLight)
|
||||
HapticFeedback().tap()
|
||||
} else {
|
||||
HapticFeedback().impact(.veryLight)
|
||||
}
|
||||
|
@ -183,6 +183,8 @@ private final class StoryContainerScreenComponent: Component {
|
||||
|
||||
private var availableReactions: StoryAvailableReactions?
|
||||
|
||||
private let sharedViewListsContext = StoryItemSetViewListComponent.SharedListsContext()
|
||||
|
||||
private var isAnimatingOut: Bool = false
|
||||
private var didAnimateOut: Bool = false
|
||||
|
||||
@ -448,10 +450,10 @@ private final class StoryContainerScreenComponent: Component {
|
||||
self.verticalPanState = nil
|
||||
var updateState = true
|
||||
|
||||
if translation.y > 100.0 || velocity.y > 10.0 {
|
||||
if translation.y > 200.0 || (translation.y > 100.0 && velocity.y > 200.0) {
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
|
||||
self.environment?.controller()?.dismiss()
|
||||
} else if translation.y < -100.0 || velocity.y < -40.0 {
|
||||
} else if translation.y < -200.0 || (translation.y < -100.0 && velocity.y < -100.0) {
|
||||
if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] {
|
||||
if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View {
|
||||
if itemSetComponentView.activateInput() {
|
||||
@ -980,7 +982,8 @@ private final class StoryContainerScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
},
|
||||
keyboardInputData: self.inputMediaNodeDataPromise.get()
|
||||
keyboardInputData: self.inputMediaNodeDataPromise.get(),
|
||||
sharedViewListsContext: self.sharedViewListsContext
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: itemSetContainerSize
|
||||
|
@ -98,8 +98,9 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
public let controller: () -> ViewController?
|
||||
public let toggleAmbientMode: () -> Void
|
||||
public let keyboardInputData: Signal<ChatEntityKeyboardInputNode.InputData, NoError>
|
||||
let sharedViewListsContext: StoryItemSetViewListComponent.SharedListsContext
|
||||
|
||||
public init(
|
||||
init(
|
||||
context: AccountContext,
|
||||
externalState: ExternalState,
|
||||
storyItemSharedState: StoryContentItem.SharedState,
|
||||
@ -125,7 +126,8 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
markAsSeen: @escaping (StoryId) -> Void,
|
||||
controller: @escaping () -> ViewController?,
|
||||
toggleAmbientMode: @escaping () -> Void,
|
||||
keyboardInputData: Signal<ChatEntityKeyboardInputNode.InputData, NoError>
|
||||
keyboardInputData: Signal<ChatEntityKeyboardInputNode.InputData, NoError>,
|
||||
sharedViewListsContext: StoryItemSetViewListComponent.SharedListsContext
|
||||
) {
|
||||
self.context = context
|
||||
self.externalState = externalState
|
||||
@ -153,6 +155,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
self.controller = controller
|
||||
self.toggleAmbientMode = toggleAmbientMode
|
||||
self.keyboardInputData = keyboardInputData
|
||||
self.sharedViewListsContext = sharedViewListsContext
|
||||
}
|
||||
|
||||
public static func ==(lhs: StoryItemSetContainerComponent, rhs: StoryItemSetContainerComponent) -> Bool {
|
||||
@ -1705,12 +1708,16 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
let viewListSize = viewList.view.update(
|
||||
transition: viewListTransition.withUserData(PeerListItemComponent.TransitionHint(
|
||||
synchronousLoad: false
|
||||
)).withUserData(StoryItemSetViewListComponent.AnimationHint(
|
||||
synchronous: false
|
||||
)),
|
||||
component: AnyComponent(StoryItemSetViewListComponent(
|
||||
externalState: viewList.externalState,
|
||||
context: component.context,
|
||||
theme: component.theme,
|
||||
strings: component.strings,
|
||||
sharedListsContext: component.sharedViewListsContext,
|
||||
peerId: component.slice.peer.id,
|
||||
safeInsets: component.safeInsets,
|
||||
storyItem: component.slice.item.storyItem,
|
||||
outerExpansionFraction: outerExpansionFraction,
|
||||
|
@ -4,6 +4,7 @@ import Display
|
||||
import ComponentFlow
|
||||
import MultilineTextComponent
|
||||
import TelegramCore
|
||||
import Postbox
|
||||
import TelegramPresentationData
|
||||
import ComponentDisplayAdapters
|
||||
import AccountContext
|
||||
@ -14,6 +15,14 @@ import StoryFooterPanelComponent
|
||||
import PeerListItemComponent
|
||||
|
||||
final class StoryItemSetViewListComponent: Component {
|
||||
final class AnimationHint {
|
||||
let synchronous: Bool
|
||||
|
||||
init(synchronous: Bool) {
|
||||
self.synchronous = synchronous
|
||||
}
|
||||
}
|
||||
|
||||
final class ExternalState {
|
||||
fileprivate(set) var minimizedHeight: CGFloat = 0.0
|
||||
fileprivate(set) var effectiveHeight: CGFloat = 0.0
|
||||
@ -22,10 +31,19 @@ final class StoryItemSetViewListComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
final class SharedListsContext {
|
||||
var viewLists: [StoryId: EngineStoryViewListContext] = [:]
|
||||
|
||||
init() {
|
||||
}
|
||||
}
|
||||
|
||||
let externalState: ExternalState
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let sharedListsContext: SharedListsContext
|
||||
let peerId: EnginePeer.Id
|
||||
let safeInsets: UIEdgeInsets
|
||||
let storyItem: EngineStoryItem
|
||||
let outerExpansionFraction: CGFloat
|
||||
@ -40,6 +58,8 @@ final class StoryItemSetViewListComponent: Component {
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
sharedListsContext: SharedListsContext,
|
||||
peerId: EnginePeer.Id,
|
||||
safeInsets: UIEdgeInsets,
|
||||
storyItem: EngineStoryItem,
|
||||
outerExpansionFraction: CGFloat,
|
||||
@ -53,6 +73,8 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.sharedListsContext = sharedListsContext
|
||||
self.peerId = peerId
|
||||
self.safeInsets = safeInsets
|
||||
self.storyItem = storyItem
|
||||
self.outerExpansionFraction = outerExpansionFraction
|
||||
@ -70,6 +92,9 @@ final class StoryItemSetViewListComponent: Component {
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.peerId != rhs.peerId {
|
||||
return false
|
||||
}
|
||||
if lhs.safeInsets != rhs.safeInsets {
|
||||
return false
|
||||
}
|
||||
@ -175,7 +200,6 @@ final class StoryItemSetViewListComponent: Component {
|
||||
|
||||
private var ignoreScrolling: Bool = false
|
||||
|
||||
private var viewList: EngineStoryViewListContext?
|
||||
private var viewListDisposable: Disposable?
|
||||
private var viewListState: EngineStoryViewListContext.State?
|
||||
private var requestedLoadMoreToken: EngineStoryViewListContext.LoadMoreToken?
|
||||
@ -495,7 +519,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.visiblePlaceholderViews.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
if let viewList = self.viewList, let viewListState = self.viewListState, viewListState.loadMoreToken != nil, visibleBounds.maxY >= self.scrollView.contentSize.height - 200.0 {
|
||||
if let viewList = component.sharedListsContext.viewLists[StoryId(peerId: component.peerId, id: component.storyItem.id)], let viewListState = self.viewListState, viewListState.loadMoreToken != nil, visibleBounds.maxY >= self.scrollView.contentSize.height - 200.0 {
|
||||
if self.requestedLoadMoreToken != viewListState.loadMoreToken {
|
||||
self.requestedLoadMoreToken = viewListState.loadMoreToken
|
||||
viewList.loadMore()
|
||||
@ -510,6 +534,11 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
var synchronous = false
|
||||
if let animationHint = transition.userData(AnimationHint.self) {
|
||||
synchronous = animationHint.synchronous
|
||||
}
|
||||
|
||||
let minimizedHeight = min(availableSize.height, 500.0)
|
||||
|
||||
if themeUpdated {
|
||||
@ -520,24 +549,37 @@ final class StoryItemSetViewListComponent: Component {
|
||||
|
||||
if itemUpdated {
|
||||
self.viewListState = nil
|
||||
self.viewList = nil
|
||||
self.viewListDisposable?.dispose()
|
||||
|
||||
if let views = component.storyItem.views {
|
||||
let viewList = component.context.engine.messages.storyViewList(id: component.storyItem.id, views: views)
|
||||
self.viewList = viewList
|
||||
let viewList: EngineStoryViewListContext
|
||||
if let current = component.sharedListsContext.viewLists[StoryId(peerId: component.peerId, id: component.storyItem.id)] {
|
||||
viewList = current
|
||||
} else {
|
||||
viewList = component.context.engine.messages.storyViewList(id: component.storyItem.id, views: views)
|
||||
component.sharedListsContext.viewLists[StoryId(peerId: component.peerId, id: component.storyItem.id)] = viewList
|
||||
}
|
||||
|
||||
var applyState = false
|
||||
var firstTime = true
|
||||
self.viewListDisposable = (viewList.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] listState in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if firstTime {
|
||||
firstTime = false
|
||||
self.ignoreScrolling = true
|
||||
self.scrollView.setContentOffset(CGPoint(), animated: false)
|
||||
self.ignoreScrolling = false
|
||||
}
|
||||
self.viewListState = listState
|
||||
if applyState {
|
||||
self.state?.updated(transition: Transition.immediate.withUserData(PeerListItemComponent.TransitionHint(synchronousLoad: true)))
|
||||
self.state?.updated(transition: Transition.immediate.withUserData(PeerListItemComponent.TransitionHint(synchronousLoad: false)))
|
||||
}
|
||||
})
|
||||
applyState = true
|
||||
let _ = synchronous
|
||||
}
|
||||
}
|
||||
|
||||
@ -611,15 +653,17 @@ final class StoryItemSetViewListComponent: Component {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: 200.0)
|
||||
)
|
||||
if let navigationPanelView = self.navigationPanel.view {
|
||||
if let navigationPanelView = self.navigationPanel.view as? StoryFooterPanelComponent.View {
|
||||
if navigationPanelView.superview == nil {
|
||||
self.addSubview(navigationPanelView)
|
||||
self.insertSubview(navigationPanelView.externalContainerView, belowSubview: self.navigationBarBackground)
|
||||
}
|
||||
|
||||
let expandedNavigationPanelFrame = CGRect(origin: CGPoint(x: navigationBarFrame.minX, y: navigationBarFrame.minY + 4.0), size: navigationPanelSize)
|
||||
let collapsedNavigationPanelFrame = CGRect(origin: CGPoint(x: navigationBarFrame.minX, y: navigationBarFrame.minY - navigationPanelSize.height - component.safeInsets.bottom - 1.0), size: navigationPanelSize)
|
||||
|
||||
transition.setFrame(view: navigationPanelView, frame: collapsedNavigationPanelFrame.interpolate(to: expandedNavigationPanelFrame, amount: dismissFraction))
|
||||
transition.setFrame(view: navigationPanelView.externalContainerView, frame: collapsedNavigationPanelFrame.interpolate(to: expandedNavigationPanelFrame, amount: dismissFraction))
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarFrame.maxY), size: CGSize(width: availableSize.width, height: availableSize.height)))
|
||||
|
@ -49,7 +49,7 @@ public final class StoryFooterPanelComponent: Component {
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let viewStatsButton: HighlightableButton
|
||||
private let viewStatsButton: HighlightTrackingButton
|
||||
private let viewStatsText = ComponentView<Empty>()
|
||||
private let viewStatsExpandedText = ComponentView<Empty>()
|
||||
private let deleteButton = ComponentView<Empty>()
|
||||
@ -67,18 +67,34 @@ public final class StoryFooterPanelComponent: Component {
|
||||
private var uploadProgress: Float = 0.0
|
||||
private var uploadProgressDisposable: Disposable?
|
||||
|
||||
public let externalContainerView: UIView
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.viewStatsButton = HighlightableButton()
|
||||
self.viewStatsButton = HighlightTrackingButton()
|
||||
|
||||
self.avatarsContext = AnimatedAvatarSetContext()
|
||||
self.avatarsNode = AnimatedAvatarSetNode()
|
||||
|
||||
self.externalContainerView = UIView()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.avatarsNode.view.isUserInteractionEnabled = false
|
||||
self.viewStatsButton.addSubview(self.avatarsNode.view)
|
||||
self.externalContainerView.addSubview(self.avatarsNode.view)
|
||||
self.addSubview(self.viewStatsButton)
|
||||
|
||||
self.viewStatsButton.highligthedChanged = { [weak self] highlighted in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if highlighted {
|
||||
self.avatarsNode.view.alpha = 0.7
|
||||
self.viewStatsText.view?.alpha = 0.7
|
||||
} else {
|
||||
self.avatarsNode.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2)
|
||||
self.viewStatsText.view?.layer.animateAlpha(from: 0.7, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
self.viewStatsButton.addTarget(self, action: #selector(self.viewStatsPressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
@ -224,7 +240,8 @@ public final class StoryFooterPanelComponent: Component {
|
||||
let avatarsSize = self.avatarsNode.update(context: component.context, content: avatarsContent, itemSize: CGSize(width: 30.0, height: 30.0), animated: false, synchronousLoad: true)
|
||||
|
||||
let avatarsNodeFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - avatarsSize.height) * 0.5)), size: avatarsSize)
|
||||
self.avatarsNode.frame = avatarsNodeFrame
|
||||
self.avatarsNode.position = avatarsNodeFrame.center
|
||||
self.avatarsNode.bounds = CGRect(origin: CGPoint(), size: avatarsNodeFrame.size)
|
||||
transition.setAlpha(view: self.avatarsNode.view, alpha: avatarsAlpha)
|
||||
if !avatarsSize.width.isZero {
|
||||
leftOffset = avatarsNodeFrame.maxX + avatarSpacing
|
||||
@ -269,7 +286,7 @@ public final class StoryFooterPanelComponent: Component {
|
||||
if let viewStatsTextView = self.viewStatsText.view {
|
||||
if viewStatsTextView.superview == nil {
|
||||
viewStatsTextView.isUserInteractionEnabled = false
|
||||
self.viewStatsButton.addSubview(viewStatsTextView)
|
||||
self.externalContainerView.addSubview(viewStatsTextView)
|
||||
}
|
||||
transition.setPosition(view: viewStatsTextView, position: viewStatsTextFrame.center)
|
||||
transition.setBounds(view: viewStatsTextView, bounds: CGRect(origin: CGPoint(), size: viewStatsTextFrame.size))
|
||||
@ -281,7 +298,7 @@ public final class StoryFooterPanelComponent: Component {
|
||||
if let viewStatsExpandedTextView = self.viewStatsExpandedText.view {
|
||||
if viewStatsExpandedTextView.superview == nil {
|
||||
viewStatsExpandedTextView.isUserInteractionEnabled = false
|
||||
self.viewStatsButton.addSubview(viewStatsExpandedTextView)
|
||||
self.addSubview(viewStatsExpandedTextView)
|
||||
}
|
||||
transition.setPosition(view: viewStatsExpandedTextView, position: viewStatsExpandedTextFrame.center)
|
||||
transition.setBounds(view: viewStatsExpandedTextView, bounds: CGRect(origin: CGPoint(), size: viewStatsExpandedTextFrame.size))
|
||||
@ -313,7 +330,7 @@ public final class StoryFooterPanelComponent: Component {
|
||||
)
|
||||
if let deleteButtonView = self.deleteButton.view {
|
||||
if deleteButtonView.superview == nil {
|
||||
self.addSubview(deleteButtonView)
|
||||
self.externalContainerView.addSubview(deleteButtonView)
|
||||
}
|
||||
transition.setFrame(view: deleteButtonView, frame: CGRect(origin: CGPoint(x: rightContentOffset - deleteButtonSize.width, y: floor((size.height - deleteButtonSize.height) * 0.5)), size: deleteButtonSize))
|
||||
rightContentOffset -= deleteButtonSize.width + 8.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user