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

This commit is contained in:
Ilya Laktyushin 2023-07-02 02:23:31 +02:00
commit 4cc2eb7850
7 changed files with 123 additions and 25 deletions

View File

@ -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)

View File

@ -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

View File

@ -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)
}

View File

@ -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

View File

@ -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,

View File

@ -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)))

View File

@ -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