[WIP] Stories

This commit is contained in:
Ali 2023-06-08 00:42:48 +04:00
parent c01f7ce04a
commit 98f7c68432
14 changed files with 606 additions and 271 deletions

View File

@ -8527,12 +8527,13 @@ public extension Api.functions.stories {
} }
} }
public extension Api.functions.stories { public extension Api.functions.stories {
static func getExpiredStories(offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.Stories>) { static func getPinnedStories(userId: Api.InputUser, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.Stories>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(150442738) buffer.appendInt32(189206839)
userId.serialize(buffer, true)
serializeInt32(offsetId, buffer: buffer, boxed: false) serializeInt32(offsetId, buffer: buffer, boxed: false)
serializeInt32(limit, buffer: buffer, boxed: false) serializeInt32(limit, buffer: buffer, boxed: false)
return (FunctionDescription(name: "stories.getExpiredStories", parameters: [("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in return (FunctionDescription(name: "stories.getPinnedStories", parameters: [("userId", String(describing: userId)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.stories.Stories? var result: Api.stories.Stories?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
@ -8543,13 +8544,12 @@ public extension Api.functions.stories {
} }
} }
public extension Api.functions.stories { public extension Api.functions.stories {
static func getPinnedStories(userId: Api.InputUser, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.Stories>) { static func getStoriesArchive(offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.Stories>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(189206839) buffer.appendInt32(526108114)
userId.serialize(buffer, true)
serializeInt32(offsetId, buffer: buffer, boxed: false) serializeInt32(offsetId, buffer: buffer, boxed: false)
serializeInt32(limit, buffer: buffer, boxed: false) serializeInt32(limit, buffer: buffer, boxed: false)
return (FunctionDescription(name: "stories.getPinnedStories", parameters: [("userId", String(describing: userId)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in return (FunctionDescription(name: "stories.getStoriesArchive", parameters: [("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.stories.Stories? var result: Api.stories.Stories?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {

View File

@ -464,7 +464,7 @@ public final class PeerStoryListContext {
let signal: Signal<Api.stories.Stories, MTRpcError> let signal: Signal<Api.stories.Stories, MTRpcError>
if isArchived { if isArchived {
signal = account.network.request(Api.functions.stories.getExpiredStories(offsetId: Int32(loadMoreToken), limit: 100)) signal = account.network.request(Api.functions.stories.getStoriesArchive(offsetId: Int32(loadMoreToken), limit: 100))
} else { } else {
signal = account.network.request(Api.functions.stories.getPinnedStories(userId: inputUser, offsetId: Int32(loadMoreToken), limit: 100)) signal = account.network.request(Api.functions.stories.getPinnedStories(userId: inputUser, offsetId: Int32(loadMoreToken), limit: 100))
} }

View File

@ -881,6 +881,43 @@ public extension TelegramEngine {
} }
} }
public func refreshStoryViews(peerId: EnginePeer.Id, ids: [Int32]) -> Signal<Never, NoError> {
if peerId != self.account.peerId {
return .complete()
}
return _internal_getStoryViews(account: self.account, ids: ids)
|> mapToSignal { views -> Signal<Never, NoError> in
return self.account.postbox.transaction { transaction -> Void in
var currentItems = transaction.getStoryItems(peerId: peerId)
for i in 0 ..< currentItems.count {
if ids.contains(currentItems[i].id) {
if case let .item(item) = currentItems[i].value.get(Stories.StoredItem.self) {
let updatedItem: Stories.StoredItem = .item(Stories.Item(
id: item.id,
timestamp: item.timestamp,
expirationTimestamp: item.expirationTimestamp,
media: item.media,
text: item.text,
entities: item.entities,
views: views[currentItems[i].id],
privacy: item.privacy,
isPinned: item.isPinned,
isExpired: item.isExpired,
isPublic: item.isPublic
))
if let entry = CodableEntry(updatedItem) {
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id)
}
}
}
}
transaction.setStoryItems(peerId: peerId, items: currentItems)
}
|> ignoreValues
}
}
public func uploadStory(media: EngineStoryInputMedia, text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, period: Int) -> Signal<StoryUploadResult, NoError> { public func uploadStory(media: EngineStoryInputMedia, text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, period: Int) -> Signal<StoryUploadResult, NoError> {
return _internal_uploadStory(account: self.account, media: media, text: text, entities: entities, pin: pin, privacy: privacy, period: period) return _internal_uploadStory(account: self.account, media: media, text: text, entities: entities, pin: pin, privacy: privacy, period: period)
} }

View File

@ -371,6 +371,7 @@ swift_library(
"//submodules/TelegramUI/Components/FullScreenEffectView", "//submodules/TelegramUI/Components/FullScreenEffectView",
"//submodules/TelegramUI/Components/ShareWithPeersScreen", "//submodules/TelegramUI/Components/ShareWithPeersScreen",
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode", "//submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode",
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen",
] + select({ ] + select({
"@build_bazel_rules_apple//apple:ios_armv7": [], "@build_bazel_rules_apple//apple:ios_armv7": [],
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,

View File

@ -151,9 +151,11 @@ public final class ChatListNavigationBar: Component {
private var applyScrollFractionAnimator: DisplayLinkAnimator? private var applyScrollFractionAnimator: DisplayLinkAnimator?
private var applyScrollFraction: CGFloat = 1.0 private var applyScrollFraction: CGFloat = 1.0
private var storiesOffsetStartFraction: CGFloat = 1.0
private var applyScrollUnlockedFraction: CGFloat = 1.0 private var applyScrollUnlockedFraction: CGFloat = 1.0
private var applyScrollStartFraction: CGFloat = 0.0
private var storiesOffsetFraction: CGFloat = 0.0 private var storiesOffsetFraction: CGFloat = 0.0
private var storiesUnlockedFraction: CGFloat = 0.0
private var storiesUnlockedStartFraction: CGFloat = 1.0
private var tabsNode: ASDisplayNode? private var tabsNode: ASDisplayNode?
private var tabsNodeIsSearch: Bool = false private var tabsNodeIsSearch: Bool = false
@ -298,8 +300,8 @@ public final class ChatListNavigationBar: Component {
} }
if self.applyScrollFractionAnimator != nil { if self.applyScrollFractionAnimator != nil {
storiesOffsetFraction = self.applyScrollFraction * storiesOffsetFraction + (1.0 - self.applyScrollFraction) * 1.0 storiesOffsetFraction = self.applyScrollFraction * storiesOffsetFraction + (1.0 - self.applyScrollFraction) * self.storiesOffsetStartFraction
storiesUnlockedOffsetFraction = self.applyScrollUnlockedFraction * storiesUnlockedOffsetFraction + (1.0 - self.applyScrollUnlockedFraction) * 1.0 storiesUnlockedOffsetFraction = self.applyScrollUnlockedFraction * storiesUnlockedOffsetFraction + (1.0 - self.applyScrollUnlockedFraction) * self.storiesUnlockedStartFraction
} }
let searchSize = CGSize(width: currentLayout.size.width, height: navigationBarSearchContentHeight) let searchSize = CGSize(width: currentLayout.size.width, height: navigationBarSearchContentHeight)
@ -322,6 +324,8 @@ public final class ChatListNavigationBar: Component {
} }
self.storiesOffsetFraction = storiesOffsetFraction self.storiesOffsetFraction = storiesOffsetFraction
self.storiesUnlockedFraction = storiesUnlockedOffsetFraction
let headerContentSize = self.headerContent.update( let headerContentSize = self.headerContent.update(
transition: headerTransition, transition: headerTransition,
component: AnyComponent(ChatListHeaderComponent( component: AnyComponent(ChatListHeaderComponent(
@ -490,19 +494,25 @@ public final class ChatListNavigationBar: Component {
} }
} }
if storiesUnlockedUpdated && component.storiesUnlocked { if storiesUnlockedUpdated, case let .curve(duration, _) = transition.animation {
self.applyScrollStartFraction = 0.0 self.applyScrollFractionAnimator?.invalidate()
let startFraction = self.storiesOffsetFraction self.applyScrollFractionAnimator = nil
self.applyScrollFraction = (1.0 - 0.0) * startFraction + 0.0 * 1.0
self.storiesOffsetStartFraction = self.storiesOffsetFraction
self.storiesUnlockedStartFraction = self.storiesUnlockedFraction
let storiesUnlocked = component.storiesUnlocked
self.applyScrollFraction = 0.0
self.applyScrollUnlockedFraction = 0.0 self.applyScrollUnlockedFraction = 0.0
self.applyScrollFractionAnimator = DisplayLinkAnimator(duration: 0.3, from: 0.0, to: 1.0, update: { [weak self] value in self.applyScrollFractionAnimator = DisplayLinkAnimator(duration: duration * UIView.animationDurationFactor(), from: 0.0, to: 1.0, update: { [weak self] value in
guard let self else { guard let self else {
return return
} }
let t = listViewAnimationCurveSystem(value) let t = listViewAnimationCurveSystem(value)
self.applyScrollFraction = (1.0 - t) * startFraction + t * 1.0 self.applyScrollFraction = t
self.applyScrollUnlockedFraction = t self.applyScrollUnlockedFraction = storiesUnlocked ? t : (1.0 - t)
if let rawScrollOffset = self.rawScrollOffset { if let rawScrollOffset = self.rawScrollOffset {
self.hasDeferredScrollOffset = true self.hasDeferredScrollOffset = true

View File

@ -0,0 +1,29 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "PeerInfoStoryGridScreen",
module_name = "PeerInfoStoryGridScreen",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/AsyncDisplayKit",
"//submodules/TelegramCore",
"//submodules/Postbox",
"//submodules/TelegramPresentationData",
"//submodules/AccountContext",
"//submodules/ComponentFlow",
"//submodules/Components/ComponentDisplayAdapters",
"//submodules/Components/ViewControllerComponent",
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode",
"//submodules/TelegramUI/Components/ChatListHeaderComponent",
"//submodules/ContextUI",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,360 @@
import Foundation
import AsyncDisplayKit
import Display
import SwiftSignalKit
import TelegramPresentationData
import AccountContext
import ComponentFlow
import TelegramCore
import PeerInfoVisualMediaPaneNode
import ViewControllerComponent
import ChatListHeaderComponent
import ContextUI
final class PeerInfoStoryGridScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
let peerId: EnginePeer.Id
let scope: PeerInfoStoryGridScreen.Scope
init(
context: AccountContext,
peerId: EnginePeer.Id,
scope: PeerInfoStoryGridScreen.Scope
) {
self.context = context
self.peerId = peerId
self.scope = scope
}
static func ==(lhs: PeerInfoStoryGridScreenComponent, rhs: PeerInfoStoryGridScreenComponent) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.peerId != rhs.peerId {
return false
}
if lhs.scope != rhs.scope {
return false
}
return true
}
final class View: UIView {
private var component: PeerInfoStoryGridScreenComponent?
private weak var state: EmptyComponentState?
private var environment: EnvironmentType?
private var paneNode: PeerInfoStoryPaneNode?
private weak var mediaGalleryContextMenu: ContextController?
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func morePressed(source: ContextReferenceContentNode) {
guard let component = self.component, let controller = self.environment?.controller(), let pane = self.paneNode else {
return
}
var items: [ContextMenuItem] = []
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let strings = presentationData.strings
var recurseGenerateAction: ((Bool) -> ContextMenuActionItem)?
let generateAction: (Bool) -> ContextMenuActionItem = { [weak pane] isZoomIn in
let nextZoomLevel = isZoomIn ? pane?.availableZoomLevels().increment : pane?.availableZoomLevels().decrement
let canZoom: Bool = nextZoomLevel != nil
return ContextMenuActionItem(id: isZoomIn ? 0 : 1, text: isZoomIn ? strings.SharedMedia_ZoomIn : strings.SharedMedia_ZoomOut, textColor: canZoom ? .primary : .disabled, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: isZoomIn ? "Chat/Context Menu/ZoomIn" : "Chat/Context Menu/ZoomOut"), color: canZoom ? theme.contextMenu.primaryColor : theme.contextMenu.primaryColor.withMultipliedAlpha(0.4))
}, action: canZoom ? { action in
guard let pane = pane, let zoomLevel = isZoomIn ? pane.availableZoomLevels().increment : pane.availableZoomLevels().decrement else {
return
}
pane.updateZoomLevel(level: zoomLevel)
if let recurseGenerateAction = recurseGenerateAction {
action.updateAction(0, recurseGenerateAction(true))
action.updateAction(1, recurseGenerateAction(false))
}
} : nil)
}
recurseGenerateAction = { isZoomIn in
return generateAction(isZoomIn)
}
items.append(.action(generateAction(true)))
items.append(.action(generateAction(false)))
if component.peerId == component.context.account.peerId, case .saved = component.scope {
var ignoreNextActions = false
//TODO:localize
items.append(.action(ContextMenuActionItem(text: "Show Archive", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/StoryArchive"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
if ignoreNextActions {
return
}
ignoreNextActions = true
a(.default)
guard let self, let component = self.component else {
return
}
self.environment?.controller()?.push(PeerInfoStoryGridScreen(context: component.context, peerId: component.peerId, scope: .archive))
})))
}
/*if photoCount != 0 && videoCount != 0 {
items.append(.separator)
let showPhotos: Bool
switch pane.contentType {
case .photo, .photoOrVideo:
showPhotos = true
default:
showPhotos = false
}
let showVideos: Bool
switch pane.contentType {
case .video, .photoOrVideo:
showVideos = true
default:
showVideos = false
}
items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowPhotos, icon: { theme in
if !showPhotos {
return nil
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak pane] _, a in
a(.default)
guard let pane = pane else {
return
}
let updatedContentType: PeerInfoVisualMediaPaneNode.ContentType
switch pane.contentType {
case .photoOrVideo:
updatedContentType = .video
case .photo:
updatedContentType = .photo
case .video:
updatedContentType = .photoOrVideo
default:
updatedContentType = pane.contentType
}
pane.updateContentType(contentType: updatedContentType)
})))
items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowVideos, icon: { theme in
if !showVideos {
return nil
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak pane] _, a in
a(.default)
guard let pane = pane else {
return
}
let updatedContentType: PeerInfoVisualMediaPaneNode.ContentType
switch pane.contentType {
case .photoOrVideo:
updatedContentType = .photo
case .photo:
updatedContentType = .photoOrVideo
case .video:
updatedContentType = .video
default:
updatedContentType = pane.contentType
}
pane.updateContentType(contentType: updatedContentType)
})))
}*/
let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
contextController.passthroughTouchEvent = { [weak self] sourceView, point in
guard let self else {
return .ignore
}
let localPoint = self.convert(sourceView.convert(point, to: nil), from: nil)
guard let localResult = self.hitTest(localPoint, with: nil) else {
return .dismiss(consume: true, result: nil)
}
var testView: UIView? = localResult
while true {
if let testViewValue = testView {
if let node = testViewValue.asyncdisplaykit_node as? PeerInfoStoryPaneNode {
node.brieflyDisableTouchActions()
return .dismiss(consume: false, result: nil)
} else {
testView = testViewValue.superview
}
} else {
break
}
}
return .dismiss(consume: true, result: nil)
}
self.mediaGalleryContextMenu = contextController
controller.presentInGlobalOverlay(contextController)
}
func update(component: PeerInfoStoryGridScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
self.component = component
self.state = state
let environment = environment[EnvironmentType.self].value
let themeUpdated = self.environment?.theme !== environment.theme
self.environment = environment
if themeUpdated {
self.backgroundColor = environment.theme.list.plainBackgroundColor
}
let paneNode: PeerInfoStoryPaneNode
if let current = self.paneNode {
paneNode = current
} else {
paneNode = PeerInfoStoryPaneNode(
context: component.context,
peerId: component.peerId,
chatLocation: .peer(id: component.peerId),
contentType: .photoOrVideo,
captureProtected: false,
isArchive: component.scope == .archive,
navigationController: { [weak self] in
guard let self else {
return nil
}
return self.environment?.controller()?.navigationController as? NavigationController
}
)
self.paneNode = paneNode
self.addSubview(paneNode.view)
}
paneNode.update(
size: availableSize,
topInset: environment.navigationHeight,
sideInset: environment.safeInsets.left,
bottomInset: environment.safeInsets.bottom,
visibleHeight: availableSize.height,
isScrollingLockedAtTop: false,
expandProgress: 1.0,
presentationData: component.context.sharedContext.currentPresentationData.with({ $0 }),
synchronous: false,
transition: transition.containedViewLayoutTransition
)
transition.setFrame(view: paneNode.view, frame: CGRect(origin: CGPoint(), size: availableSize))
return availableSize
}
}
func makeView() -> View {
return View()
}
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}
public class PeerInfoStoryGridScreen: ViewControllerComponentContainer {
public enum Scope {
case saved
case archive
}
private let context: AccountContext
private var isDismissed: Bool = false
private var moreBarButton: MoreHeaderButton?
private var moreBarButtonItem: UIBarButtonItem?
public init(
context: AccountContext,
peerId: EnginePeer.Id,
scope: Scope
) {
self.context = context
super.init(context: context, component: PeerInfoStoryGridScreenComponent(
context: context,
peerId: peerId,
scope: scope
), navigationBarAppearance: .default, theme: .default)
//TODO:localize
self.navigationItem.title = "My Stories"
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
let moreBarButton = MoreHeaderButton(color: presentationData.theme.rootController.navigationBar.buttonColor)
moreBarButton.isUserInteractionEnabled = true
self.moreBarButton = moreBarButton
moreBarButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: presentationData.theme.rootController.navigationBar.buttonColor)))
let moreBarButtonItem = UIBarButtonItem(customDisplayNode: self.moreBarButton)!
self.moreBarButtonItem = moreBarButtonItem
moreBarButton.contextAction = { [weak self] sourceNode, gesture in
guard let self else {
return
}
let _ = self
}
moreBarButton.addTarget(self, action: #selector(self.morePressed), forControlEvents: .touchUpInside)
self.navigationItem.setRightBarButton(moreBarButtonItem, animated: false)
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
}
@objc private func morePressed() {
guard let componentView = self.node.hostView.componentView as? PeerInfoStoryGridScreenComponent.View else {
return
}
guard let moreBarButton = self.moreBarButton else {
return
}
componentView.morePressed(source: moreBarButton.referenceNode)
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
}
}
private final class PeerInfoContextReferenceContentSource: ContextReferenceContentSource {
private let controller: ViewController
private let sourceNode: ContextReferenceContentNode
init(controller: ViewController, sourceNode: ContextReferenceContentNode) {
self.controller = controller
self.sourceNode = sourceNode
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -23,7 +23,6 @@ import TelegramNotices
import TelegramUIPreferences import TelegramUIPreferences
import CheckNode import CheckNode
import AppBundle import AppBundle
import ChatControllerInteraction
import InvisibleInkDustNode import InvisibleInkDustNode
import MediaPickerUI import MediaPickerUI
import StoryContainerScreen import StoryContainerScreen
@ -33,20 +32,20 @@ private let mediaBadgeBackgroundColor = UIColor(white: 0.0, alpha: 0.6)
private let mediaBadgeTextColor = UIColor.white private let mediaBadgeTextColor = UIColor.white
private final class VisualMediaItemInteraction { private final class VisualMediaItemInteraction {
let openMessage: (Message) -> Void let openItem: (EngineStoryItem) -> Void
let openMessageContextActions: (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void let openItemContextActions: (EngineStoryItem, ASDisplayNode, CGRect, ContextGesture?) -> Void
let toggleSelection: (MessageId, Bool) -> Void let toggleSelection: (Int32, Bool) -> Void
var hiddenMedia: [MessageId: [Media]] = [:] var hiddenMedia = Set<Int32>()
var selectedMessageIds: Set<MessageId>? var selectedIds: Set<Int32>?
init( init(
openMessage: @escaping (Message) -> Void, openItem: @escaping (EngineStoryItem) -> Void,
openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, openItemContextActions: @escaping (EngineStoryItem, ASDisplayNode, CGRect, ContextGesture?) -> Void,
toggleSelection: @escaping (MessageId, Bool) -> Void toggleSelection: @escaping (Int32, Bool) -> Void
) { ) {
self.openMessage = openMessage self.openItem = openItem
self.openMessageContextActions = openMessageContextActions self.openItemContextActions = openItemContextActions
self.toggleSelection = toggleSelection self.toggleSelection = toggleSelection
} }
} }
@ -483,7 +482,6 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
let directMediaImageCache: DirectMediaImageCache let directMediaImageCache: DirectMediaImageCache
let captureProtected: Bool let captureProtected: Bool
var strings: PresentationStrings var strings: PresentationStrings
let chatControllerInteraction: ChatControllerInteraction
var chatPresentationData: ChatPresentationData var chatPresentationData: ChatPresentationData
var checkNodeTheme: CheckNodeTheme var checkNodeTheme: CheckNodeTheme
@ -500,10 +498,9 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
private var shimmerImages: [CGFloat: UIImage] = [:] private var shimmerImages: [CGFloat: UIImage] = [:]
init(context: AccountContext, chatLocation: ChatLocation, chatControllerInteraction: ChatControllerInteraction, directMediaImageCache: DirectMediaImageCache, captureProtected: Bool) { init(context: AccountContext, chatLocation: ChatLocation, directMediaImageCache: DirectMediaImageCache, captureProtected: Bool) {
self.context = context self.context = context
self.chatLocation = chatLocation self.chatLocation = chatLocation
self.chatControllerInteraction = chatControllerInteraction
self.directMediaImageCache = directMediaImageCache self.directMediaImageCache = directMediaImageCache
self.captureProtected = false self.captureProtected = false
@ -674,13 +671,8 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
layer.updateDuration(duration: duration, isMin: isMin, minFactor: min(1.0, layer.bounds.height / 74.0)) layer.updateDuration(duration: duration, isMin: isMin, minFactor: min(1.0, layer.bounds.height / 74.0))
} }
if let selectionState = self.chatControllerInteraction.selectionState { //TODO:selection
//TODO:selection layer.updateSelection(theme: self.checkNodeTheme, isSelected: nil, animated: false)
let _ = selectionState
layer.updateSelection(theme: self.checkNodeTheme, isSelected: false, animated: false)
} else {
layer.updateSelection(theme: self.checkNodeTheme, isSelected: nil, animated: false)
}
layer.bind(item: item) layer.bind(item: item)
} }
@ -764,11 +756,12 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
private let context: AccountContext private let context: AccountContext
private let peerId: PeerId private let peerId: PeerId
private let chatLocation: ChatLocation private let chatLocation: ChatLocation
private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?> private let isArchive: Bool
private let chatControllerInteraction: ChatControllerInteraction
public private(set) var contentType: ContentType public private(set) var contentType: ContentType
private var contentTypePromise: ValuePromise<ContentType> private var contentTypePromise: ValuePromise<ContentType>
private let navigationController: () -> NavigationController?
public weak var parentController: ViewController? public weak var parentController: ViewController?
private let contextGestureContainerNode: ContextControllerSourceNode private let contextGestureContainerNode: ContextControllerSourceNode
@ -825,14 +818,14 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
private var presentationData: PresentationData private var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
public init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, contentType: ContentType, captureProtected: Bool) { public init(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, contentType: ContentType, captureProtected: Bool, isArchive: Bool, navigationController: @escaping () -> NavigationController?) {
self.context = context self.context = context
self.peerId = peerId self.peerId = peerId
self.chatLocation = chatLocation self.chatLocation = chatLocation
self.chatLocationContextHolder = chatLocationContextHolder
self.chatControllerInteraction = chatControllerInteraction
self.contentType = contentType self.contentType = contentType
self.contentTypePromise = ValuePromise<ContentType>(contentType) self.contentTypePromise = ValuePromise<ContentType>(contentType)
self.navigationController = navigationController
self.isArchive = isArchive
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 } self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
@ -843,12 +836,11 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
self.itemGridBinding = SparseItemGridBindingImpl( self.itemGridBinding = SparseItemGridBindingImpl(
context: context, context: context,
chatLocation: .peer(id: peerId), chatLocation: .peer(id: peerId),
chatControllerInteraction: chatControllerInteraction,
directMediaImageCache: self.directMediaImageCache, directMediaImageCache: self.directMediaImageCache,
captureProtected: captureProtected captureProtected: captureProtected
) )
self.listSource = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: false) self.listSource = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: self.isArchive)
self.calendarSource = nil self.calendarSource = nil
super.init() super.init()
@ -879,155 +871,75 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
guard let self else { guard let self else {
return return
} }
if let selectionState = self.chatControllerInteraction.selectionState { //TODO:selection
let _ = selectionState let listContext = PeerStoryListContentContextImpl(
//TODO:selection context: self.context,
/*var toggledValue = true peerId: self.peerId,
if selectionState.selectedIds.contains(item.message.id) { listContext: self.listSource,
toggledValue = false initialId: item.story.id
)
let _ = (listContext.state
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] _ in
guard let self, let navigationController = self.navigationController() else {
return
} }
strongSelf.chatControllerInteraction.toggleMessagesSelection([item.message.id], toggledValue)*/
} else {
let listContext = PeerStoryListContentContextImpl(
context: self.context,
peerId: self.peerId,
listContext: self.listSource,
initialId: item.story.id
)
let _ = (listContext.state
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] _ in
guard let self, let navigationController = self.chatControllerInteraction.navigationController() else {
return
}
var transitionIn: StoryContainerScreen.TransitionIn?
let story = item.story
var foundItemLayer: SparseItemGridLayer?
self.itemGrid.forEachVisibleItem { item in
guard let itemLayer = item.layer as? ItemLayer else {
return
}
if let listItem = itemLayer.item, listItem.story.id == story.id {
foundItemLayer = itemLayer
}
}
if let foundItemLayer {
let itemRect = self.itemGrid.frameForItem(layer: foundItemLayer)
transitionIn = StoryContainerScreen.TransitionIn(
sourceView: self.view,
sourceRect: self.itemGrid.view.convert(itemRect, to: self.view),
sourceCornerRadius: 0.0
)
}
let storyContainerScreen = StoryContainerScreen(
context: self.context,
content: listContext,
transitionIn: transitionIn,
transitionOut: { [weak self] _, itemId in
guard let self else {
return nil
}
var foundItemLayer: SparseItemGridLayer?
self.itemGrid.forEachVisibleItem { item in
guard let itemLayer = item.layer as? ItemLayer else {
return
}
if let listItem = itemLayer.item, AnyHashable(listItem.story.id) == itemId {
foundItemLayer = itemLayer
}
}
if let foundItemLayer {
let itemRect = self.itemGrid.frameForItem(layer: foundItemLayer)
return StoryContainerScreen.TransitionOut(
destinationView: self.view,
destinationRect: self.itemGrid.view.convert(itemRect, to: self.view),
destinationCornerRadius: 0.0,
destinationIsAvatar: false,
completed: {}
)
}
return nil
}
)
navigationController.pushViewController(storyContainerScreen)
})
/*let _ = (StoryChatContent.stories( var transitionIn: StoryContainerScreen.TransitionIn?
context: self.context,
storyList: self.listSource, let story = item.story
focusItem: item.story.id var foundItemLayer: SparseItemGridLayer?
) self.itemGrid.forEachVisibleItem { item in
|> take(1) guard let itemLayer = item.layer as? ItemLayer else {
|> deliverOnMainQueue).start(next: { [weak self] initialContent in
guard let self, let navigationController = self.chatControllerInteraction.navigationController() else {
return return
} }
if let listItem = itemLayer.item, listItem.story.id == story.id {
foundItemLayer = itemLayer
var transitionIn: StoryContainerScreen.TransitionIn?
let story = item.story
var foundItemLayer: SparseItemGridLayer?
self.itemGrid.forEachVisibleItem { item in
guard let itemLayer = item.layer as? ItemLayer else {
return
}
if let listItem = itemLayer.item, listItem.story.id == story.id {
foundItemLayer = itemLayer
}
} }
if let foundItemLayer { }
let itemRect = self.itemGrid.frameForItem(layer: foundItemLayer) if let foundItemLayer {
transitionIn = StoryContainerScreen.TransitionIn( let itemRect = self.itemGrid.frameForItem(layer: foundItemLayer)
sourceView: self.view, transitionIn = StoryContainerScreen.TransitionIn(
sourceRect: self.itemGrid.view.convert(itemRect, to: self.view), sourceView: self.view,
sourceCornerRadius: 0.0 sourceRect: self.itemGrid.view.convert(itemRect, to: self.view),
) sourceCornerRadius: 0.0
} )
}
let storyContainerScreen = StoryContainerScreen(
context: self.context, let storyContainerScreen = StoryContainerScreen(
initialFocusedId: AnyHashable(peerId), context: self.context,
initialContent: initialContent, content: listContext,
transitionIn: transitionIn, transitionIn: transitionIn,
transitionOut: { [weak self] _, itemId in transitionOut: { [weak self] _, itemId in
guard let self else { guard let self else {
return nil
}
var foundItemLayer: SparseItemGridLayer?
self.itemGrid.forEachVisibleItem { item in
guard let itemLayer = item.layer as? ItemLayer else {
return
}
if let listItem = itemLayer.item, AnyHashable(listItem.story.id) == itemId {
foundItemLayer = itemLayer
}
}
if let foundItemLayer {
let itemRect = self.itemGrid.frameForItem(layer: foundItemLayer)
return StoryContainerScreen.TransitionOut(
destinationView: self.view,
destinationRect: self.itemGrid.view.convert(itemRect, to: self.view),
destinationCornerRadius: 0.0,
destinationIsAvatar: false,
completed: {}
)
}
return nil return nil
} }
)
navigationController.pushViewController(storyContainerScreen) var foundItemLayer: SparseItemGridLayer?
})*/ self.itemGrid.forEachVisibleItem { item in
//TODO:open guard let itemLayer = item.layer as? ItemLayer else {
//let _ = strongSelf.chatControllerInteraction.openMessage(item.message, .default) return
} }
if let listItem = itemLayer.item, AnyHashable(listItem.story.id) == itemId {
foundItemLayer = itemLayer
}
}
if let foundItemLayer {
let itemRect = self.itemGrid.frameForItem(layer: foundItemLayer)
return StoryContainerScreen.TransitionOut(
destinationView: self.view,
destinationRect: self.itemGrid.view.convert(itemRect, to: self.view),
destinationCornerRadius: 0.0,
destinationIsAvatar: false,
completed: {}
)
}
return nil
}
)
navigationController.pushViewController(storyContainerScreen)
})
} }
self.itemGridBinding.onTagTapImpl = { [weak self] in self.itemGridBinding.onTagTapImpl = { [weak self] in
@ -1118,17 +1030,27 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
} }
self._itemInteraction = VisualMediaItemInteraction( self._itemInteraction = VisualMediaItemInteraction(
openMessage: { [weak self] message in openItem: { [weak self] _ in
let _ = self?.chatControllerInteraction.openMessage(message, .default) guard let self else {
return
}
let _ = self
}, },
openMessageContextActions: { [weak self] message, sourceNode, sourceRect, gesture in openItemContextActions: { [weak self] item, sourceNode, sourceRect, gesture in
self?.chatControllerInteraction.openMessageContextActions(message, sourceNode, sourceRect, gesture) guard let self else {
return
}
let _ = self
}, },
toggleSelection: { [weak self] id, value in toggleSelection: { [weak self] id, value in
self?.chatControllerInteraction.toggleMessagesSelection([id], value) guard let self else {
return
}
let _ = self
} }
) )
self.itemInteraction.selectedMessageIds = chatControllerInteraction.selectionState.flatMap { $0.selectedIds } //TODO:selection
//self.itemInteraction.selectedItemIds =
self.contextGestureContainerNode.isGestureEnabled = true self.contextGestureContainerNode.isGestureEnabled = true
self.contextGestureContainerNode.addSubnode(self.itemGrid) self.contextGestureContainerNode.addSubnode(self.itemGrid)
@ -1664,78 +1586,6 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
} }
private func handlePanSelection(location: CGPoint) { private func handlePanSelection(location: CGPoint) {
/*var location = location
if location.y < 0.0 {
location.y = 5.0
} else if location.y > self.frame.height {
location.y = self.frame.height - 5.0
}
var hasState = false
if let state = self.selectionPanState {
hasState = true
if let message = self.messageAtPoint(location) {
if message.id == state.initialMessageId {
if !state.toggledMessageIds.isEmpty {
self.chatControllerInteraction.toggleMessagesSelection(state.toggledMessageIds.flatMap { $0.compactMap({ $0 }) }, !state.selecting)
self.selectionPanState = (state.selecting, state.initialMessageId, [])
}
} else if state.toggledMessageIds.last?.first != message.id {
var updatedToggledMessageIds: [[EngineMessage.Id]] = []
var previouslyToggled = false
for i in (0 ..< state.toggledMessageIds.count) {
if let messageId = state.toggledMessageIds[i].first {
if messageId == message.id {
previouslyToggled = true
updatedToggledMessageIds = Array(state.toggledMessageIds.prefix(i + 1))
let messageIdsToToggle = Array(state.toggledMessageIds.suffix(state.toggledMessageIds.count - i - 1)).flatMap { $0 }
self.chatControllerInteraction.toggleMessagesSelection(messageIdsToToggle, !state.selecting)
break
}
}
}
if !previouslyToggled {
updatedToggledMessageIds = state.toggledMessageIds
let isSelected = self.chatControllerInteraction.selectionState?.selectedIds.contains(message.id) ?? false
if state.selecting != isSelected {
updatedToggledMessageIds.append([message.id])
self.chatControllerInteraction.toggleMessagesSelection([message.id], state.selecting)
}
}
self.selectionPanState = (state.selecting, state.initialMessageId, updatedToggledMessageIds)
}
}
}
guard hasState else {
return
}
let scrollingAreaHeight: CGFloat = 50.0
if location.y < scrollingAreaHeight || location.y > self.frame.height - scrollingAreaHeight {
if location.y < self.frame.height / 2.0 {
self.selectionScrollDelta = (scrollingAreaHeight - location.y) / scrollingAreaHeight
} else {
self.selectionScrollDelta = -(scrollingAreaHeight - min(scrollingAreaHeight, max(0.0, (self.frame.height - location.y)))) / scrollingAreaHeight
}
if let displayLink = self.selectionScrollDisplayLink {
displayLink.isPaused = false
} else {
if let _ = self.selectionScrollActivationTimer {
} else {
let timer = SwiftSignalKit.Timer(timeout: 0.45, repeat: false, completion: { [weak self] in
self?.setupSelectionScrolling()
}, queue: .mainQueue())
timer.start()
self.selectionScrollActivationTimer = timer
}
}
} else {
self.selectionScrollDisplayLink?.isPaused = true
self.selectionScrollActivationTimer?.invalidate()
self.selectionScrollActivationTimer = nil
}*/
} }
private var selectionScrollSkipUpdate = false private var selectionScrollSkipUpdate = false

View File

@ -340,6 +340,7 @@ public final class StoryContentContextImpl: StoryContentContext {
private var requestStoryDisposables = DisposableSet() private var requestStoryDisposables = DisposableSet()
private var preloadStoryResourceDisposables: [MediaResourceId: Disposable] = [:] private var preloadStoryResourceDisposables: [MediaResourceId: Disposable] = [:]
private var pollStoryMetadataDisposables = DisposableSet()
public init( public init(
context: AccountContext, context: AccountContext,
@ -427,6 +428,7 @@ public final class StoryContentContextImpl: StoryContentContext {
for (_, disposable) in self.preloadStoryResourceDisposables { for (_, disposable) in self.preloadStoryResourceDisposables {
disposable.dispose() disposable.dispose()
} }
self.pollStoryMetadataDisposables.dispose()
} }
private func updatePeerContexts() { private func updatePeerContexts() {
@ -581,9 +583,18 @@ public final class StoryContentContextImpl: StoryContentContext {
self.updatedPromise.set(.single(Void())) self.updatedPromise.set(.single(Void()))
var possibleItems: [(EnginePeer, EngineStoryItem)] = [] var possibleItems: [(EnginePeer, EngineStoryItem)] = []
var pollItems: [StoryKey] = []
if let slice = currentState.centralPeerContext.sliceValue { if let slice = currentState.centralPeerContext.sliceValue {
if slice.peer.id == self.context.account.peerId {
pollItems.append(StoryKey(peerId: slice.peer.id, id: slice.item.storyItem.id))
}
for item in currentState.centralPeerContext.nextItems { for item in currentState.centralPeerContext.nextItems {
possibleItems.append((slice.peer, item)) possibleItems.append((slice.peer, item))
if slice.peer.id == self.context.account.peerId {
pollItems.append(StoryKey(peerId: slice.peer.id, id: item.id))
}
} }
} }
if let nextPeerContext = currentState.nextPeerContext, let slice = nextPeerContext.sliceValue { if let nextPeerContext = currentState.nextPeerContext, let slice = nextPeerContext.sliceValue {
@ -638,9 +649,6 @@ public final class StoryContentContextImpl: StoryContentContext {
if let size = info.size { if let size = info.size {
fetchRange = (0 ..< Int64(size), .default) fetchRange = (0 ..< Int64(size), .default)
} }
#if DEBUG
fetchRange = nil
#endif
self.preloadStoryResourceDisposables[resource.resource.id] = fetchedMediaResource(mediaBox: self.context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: resource, range: fetchRange).start() self.preloadStoryResourceDisposables[resource.resource.id] = fetchedMediaResource(mediaBox: self.context.account.postbox.mediaBox, userLocation: .other, userContentType: .other, reference: resource, range: fetchRange).start()
} }
} }
@ -655,6 +663,18 @@ public final class StoryContentContextImpl: StoryContentContext {
for id in removeIds { for id in removeIds {
self.preloadStoryResourceDisposables.removeValue(forKey: id) self.preloadStoryResourceDisposables.removeValue(forKey: id)
} }
var pollIdByPeerId: [EnginePeer.Id: [Int32]] = [:]
for storyKey in pollItems.prefix(3) {
if pollIdByPeerId[storyKey.peerId] == nil {
pollIdByPeerId[storyKey.peerId] = [storyKey.id]
} else {
pollIdByPeerId[storyKey.peerId]?.append(storyKey.id)
}
}
for (peerId, ids) in pollIdByPeerId {
self.pollStoryMetadataDisposables.add(self.context.engine.messages.refreshStoryViews(peerId: peerId, ids: ids).start())
}
} }
public func resetSideStates() { public func resetSideStates() {

View File

@ -543,7 +543,7 @@ public final class StoryPeerListItemComponent: Component {
} else if let mappedRightCenter { } 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)) avatarPath.addEllipse(in: CGRect(origin: CGPoint(), size: avatarSize).insetBy(dx: -indicatorLineWidth, dy: -indicatorLineWidth).offsetBy(dx: abs(mappedRightCenter.x - indicatorCenter.x), dy: 0.0))
} }
transition.setShapeLayerPath(layer: self.avatarShapeLayer, path: avatarPath) Transition.immediate.setShapeLayerPath(layer: self.avatarShapeLayer, path: avatarPath)
Transition.immediate.setShapeLayerPath(layer: self.indicatorShapeLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineWidth * 0.5)) Transition.immediate.setShapeLayerPath(layer: self.indicatorShapeLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineWidth * 0.5))

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "StoryArchive.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.66496 0.999961C9.66496 0.632692 9.36723 0.334961 8.99996 0.334961C4.21442 0.334961 0.334961 4.21441 0.334961 8.99996C0.334961 13.7855 4.21442 17.665 8.99996 17.665C13.7855 17.665 17.665 13.7855 17.665 8.99996C17.665 5.90988 16.0476 3.1987 13.6148 1.66496H15.5C15.8672 1.66496 16.165 1.36723 16.165 0.999961C16.165 0.632692 15.8672 0.334961 15.5 0.334961H12C11.6327 0.334961 11.335 0.632692 11.335 0.999961V4.49996C11.335 4.86723 11.6327 5.16496 12 5.16496C12.3672 5.16496 12.665 4.86723 12.665 4.49996V2.64464C14.8596 3.91304 16.335 6.28482 16.335 8.99996C16.335 13.051 13.051 16.335 8.99996 16.335C4.94895 16.335 1.66496 13.051 1.66496 8.99996C1.66496 4.94895 4.94895 1.66496 8.99996 1.66496C9.36723 1.66496 9.66496 1.36723 9.66496 0.999961ZM9.66492 3.99997C9.66492 3.6327 9.36719 3.33497 8.99992 3.33497C8.63265 3.33497 8.33492 3.6327 8.33492 3.99997V8.99997C8.33492 9.17634 8.40499 9.34548 8.5297 9.4702L11.5297 12.4702C11.7894 12.7299 12.2105 12.7299 12.4701 12.4702C12.7298 12.2105 12.7298 11.7894 12.4701 11.5297L9.66492 8.72452V3.99997Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -368,7 +368,7 @@ private final class PeerInfoPendingPane {
let paneNode: PeerInfoPaneNode let paneNode: PeerInfoPaneNode
switch key { switch key {
case .stories: case .stories:
let visualPaneNode = PeerInfoStoryPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .photoOrVideo, captureProtected: captureProtected) let visualPaneNode = PeerInfoStoryPaneNode(context: context, peerId: peerId, chatLocation: chatLocation, contentType: .photoOrVideo, captureProtected: captureProtected, isArchive: false, navigationController: chatControllerInteraction.navigationController)
paneNode = visualPaneNode paneNode = visualPaneNode
visualPaneNode.openCurrentDate = { visualPaneNode.openCurrentDate = {
openMediaCalendar() openMediaCalendar()

View File

@ -87,6 +87,7 @@ import StorageUsageScreen
import AvatarEditorScreen import AvatarEditorScreen
import SendInviteLinkScreen import SendInviteLinkScreen
import PeerInfoVisualMediaPaneNode import PeerInfoVisualMediaPaneNode
import PeerInfoStoryGridScreen
enum PeerInfoAvatarEditingMode { enum PeerInfoAvatarEditingMode {
case generic case generic
@ -456,6 +457,7 @@ private enum PeerInfoSettingsSection {
case avatar case avatar
case edit case edit
case proxy case proxy
case stories
case savedMessages case savedMessages
case recentCalls case recentCalls
case devices case devices
@ -655,6 +657,7 @@ private enum SettingsSection: Int, CaseIterable {
case phone case phone
case accounts case accounts
case proxy case proxy
case stories
case shortcuts case shortcuts
case advanced case advanced
case payment case payment
@ -785,6 +788,11 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
} }
} }
//TODO:localize
items[.stories]!.append(PeerInfoScreenDisclosureItem(id: 0, text: "My Stories", icon: PresentationResourcesSettings.stickers, action: {
interaction.openSettings(.stories)
}))
items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_SavedMessages, icon: PresentationResourcesSettings.savedMessages, action: { items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_SavedMessages, icon: PresentationResourcesSettings.savedMessages, action: {
interaction.openSettings(.savedMessages) interaction.openSettings(.savedMessages)
})) }))
@ -7830,6 +7838,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil)
case .proxy: case .proxy:
self.controller?.push(proxySettingsController(context: self.context)) self.controller?.push(proxySettingsController(context: self.context))
case .stories:
self.controller?.push(PeerInfoStoryGridScreen(context: self.context, peerId: self.context.account.peerId, scope: .saved))
case .savedMessages: case .savedMessages:
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))
|> deliverOnMainQueue).start(next: { [weak self] peer in |> deliverOnMainQueue).start(next: { [weak self] peer in
@ -8671,6 +8681,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
} else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode { } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode {
node.brieflyDisableTouchActions() node.brieflyDisableTouchActions()
return .dismiss(consume: false, result: nil) return .dismiss(consume: false, result: nil)
} else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoStoryPaneNode {
node.brieflyDisableTouchActions()
return .dismiss(consume: false, result: nil)
} else { } else {
testView = testViewValue.superview testView = testViewValue.superview
} }