This commit is contained in:
Ali 2023-06-25 00:10:41 +03:00
parent f3c0a20a61
commit a7ab23f97a
12 changed files with 191 additions and 112 deletions

View File

@ -19,6 +19,7 @@ import ComponentDisplayAdapters
import ComponentFlow
import ChatFolderLinkPreviewScreen
import ChatListHeaderComponent
import StoryPeerListComponent
public enum ChatListContainerNodeFilter: Equatable {
case all
@ -927,7 +928,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
if itemNode.listNode.isTracking && !self.currentItemNode.startedScrollingAtUpperBound && self.tempTopInset == 0.0 {
if case let .known(value) = offset {
if value < -1.0 {
if let storySubscriptions = self.controller?.orderedStorySubscriptions, !storySubscriptions.items.isEmpty {
if let storySubscriptions = self.controller?.orderedStorySubscriptions, (shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions) || true) {
self.currentItemNode.startedScrollingAtUpperBound = true
self.tempTopInset = ChatListNavigationBar.storiesScrollHeight
}
@ -958,7 +959,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele
}
let tempTopInset: CGFloat
if self.currentItemNode.startedScrollingAtUpperBound {
if let storySubscriptions = self.controller?.orderedStorySubscriptions, !storySubscriptions.items.isEmpty {
if let storySubscriptions = self.controller?.orderedStorySubscriptions, (shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions) || true) {
tempTopInset = ChatListNavigationBar.storiesScrollHeight
} else {
tempTopInset = 0.0
@ -1799,7 +1800,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
return false
}
if let storySubscriptions = controller.orderedStorySubscriptions, !storySubscriptions.items.isEmpty {
if let storySubscriptions = controller.orderedStorySubscriptions, (shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions) || true) {
if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View {
if navigationBarComponentView.storiesUnlocked {
return true
@ -2060,7 +2061,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
return
}
if let storySubscriptions = self.controller?.orderedStorySubscriptions, !storySubscriptions.items.isEmpty {
if let storySubscriptions = self.controller?.orderedStorySubscriptions, (shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions) || true) {
self.tempAllowAvatarExpansion = true
self.tempDisableStoriesAnimations = !animated
self.tempNavigationScrollingTransition = animated ? .animated(duration: 0.3, curve: .spring) : .immediate

View File

@ -1162,11 +1162,17 @@ public class Account {
let extractedExpr: [Signal<AccountRunningImportantTasks, NoError>] = [
managedSynchronizeChatInputStateOperations(postbox: self.postbox, network: self.network) |> map { $0 ? AccountRunningImportantTasks.other : [] },
self.pendingMessageManager.hasPendingMessages |> map { !$0.isEmpty ? AccountRunningImportantTasks.pendingMessages : [] },
(self.pendingStoryManager?.hasPending ?? .single(false)) |> map { hasPending in hasPending ? AccountRunningImportantTasks.pendingMessages : [] },
self.pendingUpdateMessageManager.updatingMessageMedia |> map { !$0.isEmpty ? AccountRunningImportantTasks.pendingMessages : [] },
self.pendingPeerMediaUploadManager.uploadingPeerMedia |> map { !$0.isEmpty ? AccountRunningImportantTasks.pendingMessages : [] },
(self.pendingStoryManager?.hasPending ?? .single(false)) |> map {
hasPending in hasPending ? AccountRunningImportantTasks.pendingMessages : []
},
self.pendingUpdateMessageManager.updatingMessageMedia |> map {
!$0.isEmpty ? AccountRunningImportantTasks.pendingMessages : []
},
self.pendingPeerMediaUploadManager.uploadingPeerMedia |> map {
!$0.isEmpty ? AccountRunningImportantTasks.pendingMessages : []
},
self.accountPresenceManager.isPerformingUpdate() |> map { $0 ? AccountRunningImportantTasks.other : [] },
self.notificationAutolockReportManager.isPerformingUpdate() |> map { $0 ? AccountRunningImportantTasks.other : [] }
//self.notificationAutolockReportManager.isPerformingUpdate() |> map { $0 ? AccountRunningImportantTasks.other : [] }
]
let importantBackgroundOperations: [Signal<AccountRunningImportantTasks, NoError>] = extractedExpr
let importantBackgroundOperationsRunning = combineLatest(queue: Queue(), importantBackgroundOperations)

View File

@ -475,6 +475,7 @@ public final class EngineStorySubscriptions: Equatable {
public let peer: EnginePeer
public let hasUnseen: Bool
public let hasUnseenCloseFriends: Bool
public let hasPending: Bool
public let storyCount: Int
public let unseenCount: Int
public let lastTimestamp: Int32
@ -483,6 +484,7 @@ public final class EngineStorySubscriptions: Equatable {
peer: EnginePeer,
hasUnseen: Bool,
hasUnseenCloseFriends: Bool,
hasPending: Bool,
storyCount: Int,
unseenCount: Int,
lastTimestamp: Int32
@ -490,6 +492,7 @@ public final class EngineStorySubscriptions: Equatable {
self.peer = peer
self.hasUnseen = hasUnseen
self.hasUnseenCloseFriends = hasUnseenCloseFriends
self.hasPending = hasPending
self.storyCount = storyCount
self.unseenCount = unseenCount
self.lastTimestamp = lastTimestamp

View File

@ -645,6 +645,7 @@ public extension TelegramEngine {
additionalDataKeys.append(PostboxViewKey.storyItems(peerId: self.account.peerId))
additionalDataKeys.append(PostboxViewKey.storiesState(key: .peer(self.account.peerId)))
additionalDataKeys.append(PostboxViewKey.storiesState(key: .local))
var subscriptionPeerIds = storySubscriptionsView.peerIds.filter { $0 != self.account.peerId }
if !debugTimer {
@ -680,6 +681,7 @@ public extension TelegramEngine {
peer: EnginePeer(accountPeer),
hasUnseen: false,
hasUnseenCloseFriends: false,
hasPending: false,
storyCount: 0,
unseenCount: 0,
lastTimestamp: 0
@ -696,14 +698,17 @@ public extension TelegramEngine {
var hasUnseen = false
var hasUnseenCloseFriends = false
var unseenCount = 0
var hasPending = false
if let peerState = peerState {
hasUnseen = peerState.maxReadId < lastEntry.id
for item in itemsView.items {
if item.id > peerState.maxReadId {
unseenCount += 1
if case let .item(item) = item.value.get(Stories.StoredItem.self) {
}
if case let .item(item) = item.value.get(Stories.StoredItem.self) {
if item.id > peerState.maxReadId {
if item.isCloseFriends {
hasUnseenCloseFriends = true
}
@ -712,10 +717,17 @@ public extension TelegramEngine {
}
}
if let view = views.views[PostboxViewKey.storiesState(key: .local)] as? StoryStatesView, let localState = view.value?.get(Stories.LocalState.self) {
if !localState.items.isEmpty {
hasPending = true
}
}
let item = EngineStorySubscriptions.Item(
peer: EnginePeer(accountPeer),
hasUnseen: hasUnseen,
hasUnseenCloseFriends: hasUnseenCloseFriends,
hasPending: hasPending,
storyCount: itemsView.items.count,
unseenCount: unseenCount,
lastTimestamp: lastEntry.timestamp
@ -766,6 +778,7 @@ public extension TelegramEngine {
peer: EnginePeer(peer),
hasUnseen: hasUnseen,
hasUnseenCloseFriends: hasUnseenCloseFriends,
hasPending: false,
storyCount: itemsView.items.count,
unseenCount: unseenCount,
lastTimestamp: lastEntry.timestamp

View File

@ -847,14 +847,6 @@ public final class ChatListHeaderComponent: Component {
}
let sideContentWidth: CGFloat = 0.0
/*if let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty {
sideContentWidth = self.storyPeerListExternalState.collapsedWidth + 12.0
}
if let chatListTitle = primaryContent.chatListTitle {
if chatListTitle.activity {
sideContentWidth = 0.0
}
}*/
primaryContentView.update(context: component.context, theme: component.theme, strings: component.strings, content: primaryContent, backTitle: primaryContent.backTitle, sideInset: component.sideInset, sideContentWidth: sideContentWidth, sideContentFraction: (1.0 - component.storiesFraction), size: availableSize, transition: primaryContentTransition)
primaryContentTransition.setFrame(view: primaryContentView, frame: CGRect(origin: CGPoint(), size: availableSize))

View File

@ -610,44 +610,6 @@ public final class ChatListNavigationBar: Component {
return size
}
/*private func addStoriesUnlockedAnimation(duration: Double, animateScrollUnlocked: Bool) {
guard let component = self.component else {
return
}
self.applyScrollFractionAnimator?.invalidate()
self.applyScrollFractionAnimator = nil
let storiesUnlocked = component.storiesUnlocked
self.storiesOffsetStartFraction = self.storiesOffsetFraction
self.storiesUnlockedStartFraction = self.storiesUnlockedFraction
self.applyScrollFraction = 0.0
self.applyScrollUnlockedFraction = 0.0
self.applyScrollFractionAnimator = DisplayLinkAnimator(duration: duration * UIView.animationDurationFactor(), from: 0.0, to: 1.0, update: { [weak self] value in
guard let self else {
return
}
let t = listViewAnimationCurveSystem(value)
self.applyScrollFraction = t
if animateScrollUnlocked {
self.applyScrollUnlockedFraction = storiesUnlocked ? t : (1.0 - t)
}
if let rawScrollOffset = self.rawScrollOffset {
self.hasDeferredScrollOffset = true
self.applyScroll(offset: rawScrollOffset, allowAvatarsExpansion: self.currentAllowAvatarsExpansion, transition: .immediate)
}
}, completion: { [weak self] in
guard let self else {
return
}
self.applyScrollFractionAnimator?.invalidate()
self.applyScrollFractionAnimator = nil
})
}*/
}
public func makeView() -> View {

View File

@ -5,6 +5,7 @@ import ComponentFlow
import SwiftSignalKit
import TelegramCore
import Postbox
import TelegramPresentationData
public final class StoryContentItem {
public final class ExternalState {
@ -33,17 +34,20 @@ public final class StoryContentItem {
public final class Environment: Equatable {
public let externalState: ExternalState
public let sharedState: SharedState
public let theme: PresentationTheme
public let presentationProgressUpdated: (Double, Bool) -> Void
public let markAsSeen: (StoryId) -> Void
public init(
externalState: ExternalState,
sharedState: SharedState,
theme: PresentationTheme,
presentationProgressUpdated: @escaping (Double, Bool) -> Void,
markAsSeen: @escaping (StoryId) -> Void
) {
self.externalState = externalState
self.sharedState = sharedState
self.theme = theme
self.presentationProgressUpdated = presentationProgressUpdated
self.markAsSeen = markAsSeen
}
@ -55,6 +59,9 @@ public final class StoryContentItem {
if lhs.sharedState !== rhs.sharedState {
return false
}
if lhs.theme !== rhs.theme {
return false
}
return true
}
}

View File

@ -621,6 +621,7 @@ public final class StoryItemSetContainerComponent: Component {
let itemEnvironment = StoryContentItem.Environment(
externalState: visibleItem.externalState,
sharedState: component.storyItemSharedState,
theme: component.theme,
presentationProgressUpdated: { [weak self, weak visibleItem] progress, canSwitch in
guard let self = self, let component = self.component else {
return
@ -656,7 +657,6 @@ public final class StoryItemSetContainerComponent: Component {
)
if let view = visibleItem.view.view {
if view.superview == nil {
view.isUserInteractionEnabled = false
self.contentContainerView.insertSubview(view, at: 0)
}
itemTransition.setFrame(view: view, frame: CGRect(origin: CGPoint(), size: itemLayout.size))
@ -1139,6 +1139,15 @@ public final class StoryItemSetContainerComponent: Component {
}
}
}
var isUnsupported = false
var disabledPlaceholder: String?
if component.slice.peer.isService {
disabledPlaceholder = "You can't reply to this story"
} else if case .unsupported = component.slice.item.storyItem.media {
isUnsupported = true
disabledPlaceholder = "You can't reply to this story"
}
let keyboardWasHidden = self.inputPanelExternalState.isKeyboardHidden
let inputNodeVisible = self.sendMessageContext.currentInputMode == .media || hasFirstResponder(self)
@ -1374,7 +1383,7 @@ public final class StoryItemSetContainerComponent: Component {
displayGradient: false, //(component.inputHeight != 0.0 || inputNodeVisible) && component.metrics.widthClass != .regular,
bottomInset: component.inputHeight != 0.0 || inputNodeVisible ? 0.0 : bottomContentInset,
hideKeyboard: self.sendMessageContext.currentInputMode == .media,
disabledPlaceholder: component.slice.peer.isService ? "You can't reply to this story" : nil
disabledPlaceholder: disabledPlaceholder
)),
environment: {},
containerSize: CGSize(width: inputPanelAvailableWidth, height: 200.0)
@ -1976,7 +1985,7 @@ public final class StoryItemSetContainerComponent: Component {
}
}
if !component.slice.item.storyItem.text.isEmpty {
if !isUnsupported, !component.slice.item.storyItem.text.isEmpty {
var captionItemTransition = transition
let captionItem: CaptionItem
if let current = self.captionItem {

View File

@ -23,6 +23,9 @@ swift_library(
"//submodules/TelegramUniversalVideoContent",
"//submodules/AvatarNode",
"//submodules/Components/HierarchyTrackingLayer",
"//submodules/TelegramUI/Components/ButtonComponent",
"//submodules/Components/MultilineTextComponent",
"//submodules/TelegramPresentationData",
],
visibility = [
"//visibility:public",

View File

@ -437,6 +437,7 @@ public final class StoryContentContextImpl: StoryContentContext {
peer: peer,
hasUnseen: state.hasUnseen,
hasUnseenCloseFriends: state.hasUnseenCloseFriends,
hasPending: false,
storyCount: state.items.count,
unseenCount: 0,
lastTimestamp: state.items.last?.timestamp ?? 0

View File

@ -12,6 +12,9 @@ import UniversalMediaPlayer
import TelegramUniversalVideoContent
import StoryContainerScreen
import HierarchyTrackingLayer
import ButtonComponent
import MultilineTextComponent
import TelegramPresentationData
final class StoryItemContentComponent: Component {
typealias EnvironmentType = StoryContentItem.Environment
@ -38,56 +41,6 @@ final class StoryItemContentComponent: Component {
}
return true
}
/*static func preload(context: AccountContext, message: EngineMessage) -> Signal<Never, NoError> {
var messageMedia: EngineMedia?
for media in message.media {
switch media {
case let image as TelegramMediaImage:
messageMedia = .image(image)
case let file as TelegramMediaFile:
messageMedia = .file(file)
default:
break
}
}
guard let messageMedia else {
return .complete()
}
var fetchSignal: Signal<Never, NoError>?
switch messageMedia {
case let .image(image):
if let representation = image.representations.last {
fetchSignal = fetchedMediaResource(
mediaBox: context.account.postbox.mediaBox,
userLocation: .peer(message.id.peerId),
userContentType: .image,
reference: ImageMediaReference.message(message: MessageReference(message._asMessage()), media: image).resourceReference(representation.resource)
)
|> ignoreValues
|> `catch` { _ -> Signal<Never, NoError> in
return .complete()
}
}
case let .file(file):
fetchSignal = fetchedMediaResource(
mediaBox: context.account.postbox.mediaBox,
userLocation: .peer(message.id.peerId),
userContentType: .image,
reference: FileMediaReference.message(message: MessageReference(message._asMessage()), media: file).resourceReference(file.resource)
)
|> ignoreValues
|> `catch` { _ -> Signal<Never, NoError> in
return .complete()
}
default:
break
}
return fetchSignal ?? .complete()
}*/
final class View: StoryContentItem.View {
private let imageNode: TransformImageNode
@ -100,6 +53,9 @@ final class StoryItemContentComponent: Component {
private weak var state: EmptyComponentState?
private var environment: StoryContentItem.Environment?
private var unsupportedText: ComponentView<Empty>?
private var unsupportedButton: ComponentView<Empty>?
private var isProgressPaused: Bool = false
private var currentProgressTimer: SwiftSignalKit.Timer?
private var currentProgressTimerValue: Double = 0.0
@ -353,10 +309,20 @@ final class StoryItemContentComponent: Component {
self.environment?.presentationProgressUpdated(clippedProgress, false)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let unsupportedButtonView = self.unsupportedButton?.view {
if let result = unsupportedButtonView.hitTest(self.convert(point, to: unsupportedButtonView), with: event) {
return result
}
}
return nil
}
func update(component: StoryItemContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<StoryContentItem.Environment>, transition: Transition) -> CGSize {
self.component = component
self.state = state
self.environment = environment[StoryContentItem.Environment.self].value
let environment = environment[StoryContentItem.Environment.self].value
self.environment = environment
let peerReference = PeerReference(component.peer._asPeer())
@ -366,6 +332,8 @@ final class StoryItemContentComponent: Component {
messageMedia = .image(image)
case let .file(file):
messageMedia = .file(file)
case .unsupported:
self.contentLoaded = true
default:
break
}
@ -504,6 +472,99 @@ final class StoryItemContentComponent: Component {
}
}
switch component.item.media {
case .image, .file:
if let unsupportedText = self.unsupportedText {
self.unsupportedText = nil
unsupportedText.view?.removeFromSuperview()
}
if let unsupportedButton = self.unsupportedButton {
self.unsupportedButton = nil
unsupportedButton.view?.removeFromSuperview()
}
self.backgroundColor = .black
default:
var unsuportedTransition = transition
let unsupportedText: ComponentView<Empty>
if let current = self.unsupportedText {
unsupportedText = current
} else {
unsuportedTransition = .immediate
unsupportedText = ComponentView()
self.unsupportedText = unsupportedText
}
let unsupportedButton: ComponentView<Empty>
if let current = self.unsupportedButton {
unsupportedButton = current
} else {
unsuportedTransition = .immediate
unsupportedButton = ComponentView()
self.unsupportedButton = unsupportedButton
}
//TODO:localize
let unsupportedTextSize = unsupportedText.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: "This story is not supported by\nyour version of Telegram.", font: Font.regular(17.0), textColor: .white)),
horizontalAlignment: .center,
maximumNumberOfLines: 0
)),
environment: {},
containerSize: CGSize(width: availableSize.width - 16.0 * 2.0, height: availableSize.height)
)
let unsupportedButtonSize = unsupportedButton.update(
transition: unsuportedTransition,
component: AnyComponent(ButtonComponent(
background: ButtonComponent.Background(
color: environment.theme.list.itemCheckColors.fillColor,
foreground: environment.theme.list.itemCheckColors.foregroundColor,
pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.7)
),
content: AnyComponentWithIdentity(id: AnyHashable(""), component: AnyComponent(Text(text: "Update Telegram", font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.foregroundColor
))),
isEnabled: true,
displaysProgress: false,
action: { [weak self] in
guard let self, let component = self.component else {
return
}
component.context.sharedContext.applicationBindings.openAppStorePage()
}
)),
environment: {},
containerSize: CGSize(width: 240.0, height: 50.0)
)
let spacing: CGFloat = 24.0
let contentHeight = unsupportedTextSize.height + unsupportedButtonSize.height + spacing
var contentY = floor((availableSize.height - contentHeight) * 0.5)
let unsupportedTextFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - unsupportedTextSize.width) * 0.5), y: contentY), size: unsupportedTextSize)
contentY += unsupportedTextSize.height + spacing
let unsupportedButtonFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - unsupportedButtonSize.width) * 0.5), y: contentY), size: unsupportedButtonSize)
if let unsupportedTextView = unsupportedText.view {
if unsupportedTextView.superview == nil {
self.addSubview(unsupportedTextView)
}
unsuportedTransition.setPosition(view: unsupportedTextView, position: unsupportedTextFrame.center)
unsupportedTextView.bounds = CGRect(origin: CGPoint(), size: unsupportedTextFrame.size)
}
if let unsupportedButtonView = unsupportedButton.view {
if unsupportedButtonView.superview == nil {
self.addSubview(unsupportedButtonView)
}
unsuportedTransition.setFrame(view: unsupportedButtonView, frame: unsupportedButtonFrame)
}
self.backgroundColor = UIColor(rgb: 0x181818)
}
self.updateIsProgressPaused()
return availableSize

View File

@ -11,6 +11,16 @@ import SwiftSignalKit
import TelegramPresentationData
import StoryContainerScreen
public func shouldDisplayStoriesInChatListHeader(storySubscriptions: EngineStorySubscriptions) -> Bool {
if !storySubscriptions.items.isEmpty {
return true
}
if let accountItem = storySubscriptions.accountItem, (accountItem.hasUnseen || accountItem.hasPending) {
return true
}
return false
}
private func solveParabolicMotion(from sourcePoint: CGPoint, to targetPosition: CGPoint, progress: CGFloat) -> CGPoint {
if sourcePoint.y == targetPosition.y {
return sourcePoint.interpolate(to: targetPosition, amount: progress)
@ -373,12 +383,23 @@ public final class StoryPeerListComponent: Component {
}
var hasStories: Bool = false
if let storySubscriptions = component.storySubscriptions, !storySubscriptions.items.isEmpty {
if let storySubscriptions = component.storySubscriptions, shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions) {
hasStories = true
}
let _ = hasStories
let collapseStartIndex = component.useHiddenList ? 0 : 1
let collapseStartIndex: Int
if component.useHiddenList {
collapseStartIndex = 0
} else if let storySubscriptions = component.storySubscriptions {
if let accountItem = storySubscriptions.accountItem, (accountItem.hasUnseen || accountItem.hasPending) {
collapseStartIndex = 0
} else {
collapseStartIndex = 1
}
} else {
collapseStartIndex = 1
}
let collapsedItemWidth: CGFloat = 24.0
let collapsedItemDistance: CGFloat = 14.0