mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Stories
This commit is contained in:
parent
c01f7ce04a
commit
98f7c68432
@ -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() {
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
],
|
||||||
|
)
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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() {
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/StoryArchive.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/StoryArchive.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "StoryArchive.svg",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
@ -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 |
@ -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()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user