mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
380e6c4210
52 changed files with 2891 additions and 623 deletions
|
|
@ -1,6 +1,6 @@
|
|||
http_file = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
|
||||
|
||||
bazel_dep(name = "bazel_features", version = "1.29.0")
|
||||
bazel_dep(name = "bazel_features", version = "1.30.0")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.7.1")
|
||||
bazel_dep(name = "platforms", version = "0.0.11")
|
||||
|
||||
|
|
|
|||
4
MODULE.bazel.lock
generated
4
MODULE.bazel.lock
generated
|
|
@ -20,9 +20,9 @@
|
|||
"https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.29.0/MODULE.bazel": "fc2373c9825b71bcd06b60ef19b1715a42fa58edc4e39086375f47aa277e45ba",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.29.0/source.json": "714dbd96075890f20f4f158ed9fa7fd1080bf6a6e0d5a89a05e2765e3ed74260",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
|
||||
|
|
|
|||
|
|
@ -1736,7 +1736,6 @@ ios_application(
|
|||
|
||||
xcodeproj(
|
||||
name = "Telegram_xcodeproj",
|
||||
build_mode = "bazel",
|
||||
bazel_path = telegram_bazel_path,
|
||||
project_name = "Telegram",
|
||||
tags = ["manual"],
|
||||
|
|
|
|||
|
|
@ -91,8 +91,8 @@ def locate_bazel(base_path, cache_host_or_path, cache_dir):
|
|||
if test_sha256 == versions.bazel_version_sha256:
|
||||
shutil.copyfile(temp_output_file.name, bazel_path)
|
||||
elif resolved_cache_path is not None:
|
||||
(cache_cas_id, cache_cas_name) = cache_cas_name(versions.bazel_version_sha256)
|
||||
cached_path = '{}/cas/{}/{}'.format(resolved_cache_path, cache_cas_id, cache_cas_name)
|
||||
(cache_cas_id, cache_cas_name_value) = cache_cas_name(versions.bazel_version_sha256)
|
||||
cached_path = '{}/cas/{}/{}'.format(resolved_cache_path, cache_cas_id, cache_cas_name_value)
|
||||
if os.path.isfile(cached_path):
|
||||
shutil.copyfile(cached_path, bazel_path)
|
||||
|
||||
|
|
@ -137,8 +137,8 @@ def locate_bazel(base_path, cache_host_or_path, cache_dir):
|
|||
)
|
||||
], check_result=False)
|
||||
elif resolved_cache_path is not None:
|
||||
(cache_cas_id, cache_cas_name) = cache_cas_name(versions.bazel_version_sha256)
|
||||
cached_path = '{}/cas/{}/{}'.format(resolved_cache_path, cache_cas_id, cache_cas_name)
|
||||
(cache_cas_id, cache_cas_name_value) = cache_cas_name(versions.bazel_version_sha256)
|
||||
cached_path = '{}/cas/{}/{}'.format(resolved_cache_path, cache_cas_id, cache_cas_name_value)
|
||||
os.makedirs(os.path.dirname(cached_path), exist_ok=True)
|
||||
shutil.copyfile(bazel_path, cached_path)
|
||||
|
||||
|
|
|
|||
|
|
@ -330,6 +330,7 @@ public enum ResolvedUrl {
|
|||
case messageLink(link: TelegramResolvedMessageLink?)
|
||||
case stars
|
||||
case shareStory(Int64)
|
||||
case storyFolder(peerId: PeerId, id: Int64)
|
||||
}
|
||||
|
||||
public enum ResolveUrlResult {
|
||||
|
|
@ -667,6 +668,7 @@ public enum PeerInfoControllerMode {
|
|||
case myProfileGifts
|
||||
case groupsInCommon
|
||||
case monoforum(EnginePeer.Id)
|
||||
case storyAlbum(id: Int64)
|
||||
}
|
||||
|
||||
public enum ContactListActionItemInlineIconPosition {
|
||||
|
|
@ -719,6 +721,7 @@ public enum ChatListSearchFilter: Equatable {
|
|||
case topics
|
||||
case channels
|
||||
case apps
|
||||
case globalPosts
|
||||
case media
|
||||
case downloads
|
||||
case links
|
||||
|
|
@ -740,22 +743,24 @@ public enum ChatListSearchFilter: Equatable {
|
|||
return 2
|
||||
case .apps:
|
||||
return 3
|
||||
case .media:
|
||||
case .globalPosts:
|
||||
return 4
|
||||
case .downloads:
|
||||
case .media:
|
||||
return 5
|
||||
case .links:
|
||||
case .downloads:
|
||||
return 6
|
||||
case .files:
|
||||
case .links:
|
||||
return 7
|
||||
case .music:
|
||||
case .files:
|
||||
return 8
|
||||
case .voice:
|
||||
case .music:
|
||||
return 9
|
||||
case .instantVideo:
|
||||
case .voice:
|
||||
return 10
|
||||
case .publicPosts:
|
||||
case .instantVideo:
|
||||
return 11
|
||||
case .publicPosts:
|
||||
return 12
|
||||
case let .peer(peerId, _, _, _):
|
||||
return peerId.id._internalGetInt64Value()
|
||||
case let .date(_, date, _):
|
||||
|
|
|
|||
|
|
@ -115,6 +115,8 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/AvatarUploadToastScreen",
|
||||
"//submodules/TelegramUI/Components/Ads/AdsInfoScreen",
|
||||
"//submodules/TelegramUI/Components/Ads/AdsReportScreen",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/AnimatedTextComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -345,6 +345,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||
key = .channels
|
||||
case .apps:
|
||||
key = .apps
|
||||
case .globalPosts:
|
||||
key = .globalPosts
|
||||
case .media:
|
||||
key = .media
|
||||
case .downloads:
|
||||
|
|
@ -388,7 +390,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||
switch filter {
|
||||
case let .filter(filter):
|
||||
switch filter {
|
||||
case .downloads, .channels, .apps:
|
||||
case .downloads, .channels, .apps, .globalPosts:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
|
|
@ -675,6 +677,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
|||
filterKey = .channels
|
||||
case .apps:
|
||||
filterKey = .apps
|
||||
case .globalPosts:
|
||||
filterKey = .globalPosts
|
||||
case .media:
|
||||
filterKey = .media
|
||||
case .downloads:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ private final class ItemNode: ASDisplayNode {
|
|||
private let iconNode: ASImageNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let titleActiveNode: ImmediateTextNode
|
||||
private var titleBadgeView: UIImageView?
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
|
||||
private var selectionFraction: CGFloat = 0.0
|
||||
|
|
@ -74,6 +75,7 @@ private final class ItemNode: ASDisplayNode {
|
|||
self.selectionFraction = selectionFraction
|
||||
|
||||
let title: String
|
||||
var titleBadge: String?
|
||||
let icon: UIImage?
|
||||
|
||||
let color = presentationData.theme.list.itemSecondaryTextColor
|
||||
|
|
@ -90,6 +92,11 @@ private final class ItemNode: ASDisplayNode {
|
|||
case .apps:
|
||||
title = presentationData.strings.ChatList_Search_FilterApps
|
||||
icon = nil
|
||||
case .globalPosts:
|
||||
//TODO:localize
|
||||
title = "Posts"
|
||||
titleBadge = "NEW"
|
||||
icon = nil
|
||||
case .media:
|
||||
title = presentationData.strings.ChatList_Search_FilterMedia
|
||||
icon = nil
|
||||
|
|
@ -133,6 +140,38 @@ private final class ItemNode: ASDisplayNode {
|
|||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(14.0), textColor: color)
|
||||
self.titleActiveNode.attributedText = NSAttributedString(string: title, font: Font.medium(14.0), textColor: presentationData.theme.list.itemAccentColor)
|
||||
|
||||
if let titleBadge {
|
||||
let titleBadgeView: UIImageView
|
||||
if let current = self.titleBadgeView {
|
||||
titleBadgeView = current
|
||||
} else {
|
||||
titleBadgeView = UIImageView()
|
||||
self.titleBadgeView = titleBadgeView
|
||||
self.view.addSubview(titleBadgeView)
|
||||
|
||||
let labelText = NSAttributedString(string: titleBadge, font: Font.medium(11.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||
let labelBounds = labelText.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil)
|
||||
let labelSize = CGSize(width: ceil(labelBounds.width), height: ceil(labelBounds.height))
|
||||
let badgeSize = CGSize(width: labelSize.width + 8.0, height: labelSize.height + 2.0 + 1.0)
|
||||
titleBadgeView.image = generateImage(badgeSize, rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
let rect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - UIScreenPixel * 2.0))
|
||||
|
||||
context.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 5.0).cgPath)
|
||||
context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor)
|
||||
context.fillPath()
|
||||
|
||||
UIGraphicsPushContext(context)
|
||||
labelText.draw(at: CGPoint(x: 4.0, y: 1.0 + UIScreenPixel))
|
||||
UIGraphicsPopContext()
|
||||
})
|
||||
}
|
||||
} else if let titleBadgeView = self.titleBadgeView {
|
||||
self.titleBadgeView = nil
|
||||
titleBadgeView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let selectionAlpha: CGFloat = selectionFraction * selectionFraction
|
||||
let deselectionAlpha: CGFloat = 1.0// - selectionFraction
|
||||
transition.updateAlpha(node: self.titleNode, alpha: deselectionAlpha)
|
||||
|
|
@ -163,8 +202,15 @@ private final class ItemNode: ASDisplayNode {
|
|||
let titleFrame = CGRect(origin: CGPoint(x: -self.titleNode.insets.left + iconInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize)
|
||||
self.titleNode.frame = titleFrame
|
||||
self.titleActiveNode.frame = titleFrame
|
||||
|
||||
return titleSize.width - self.titleNode.insets.left - self.titleNode.insets.right + iconInset
|
||||
|
||||
var width = titleSize.width - self.titleNode.insets.left - self.titleNode.insets.right + iconInset
|
||||
|
||||
if let titleBadgeView = self.titleBadgeView, let image = titleBadgeView.image {
|
||||
width += 4.0 + image.size.width
|
||||
titleBadgeView.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: titleFrame.minY + floorToScreenPixels((titleFrame.height - image.size.height) * 0.5) + 1.0), size: image.size)
|
||||
}
|
||||
|
||||
return width
|
||||
}
|
||||
|
||||
func updateArea(size: CGSize, sideInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -55,6 +55,7 @@ public enum ChatListSearchPaneKey {
|
|||
case publicPosts
|
||||
case channels
|
||||
case apps
|
||||
case globalPosts
|
||||
case media
|
||||
case downloads
|
||||
case links
|
||||
|
|
@ -77,6 +78,8 @@ extension ChatListSearchPaneKey {
|
|||
return .channels
|
||||
case .apps:
|
||||
return .apps
|
||||
case .globalPosts:
|
||||
return .globalPosts
|
||||
case .media:
|
||||
return .media
|
||||
case .downloads:
|
||||
|
|
@ -107,6 +110,9 @@ func defaultAvailableSearchPanes(isForum: Bool, hasDownloads: Bool, hasPublicPos
|
|||
}
|
||||
result.append(.channels)
|
||||
result.append(.apps)
|
||||
if !isForum {
|
||||
result.append(.globalPosts)
|
||||
}
|
||||
result.append(contentsOf: [.media, .downloads, .links, .files, .music, .voice])
|
||||
|
||||
if !hasDownloads {
|
||||
|
|
@ -232,7 +238,10 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD
|
|||
}
|
||||
return
|
||||
}
|
||||
#if DEBUG
|
||||
#else
|
||||
self.isAdjacentLoadingEnabled = true
|
||||
#endif
|
||||
if self.currentPanes[key] != nil {
|
||||
self.currentPaneKey = key
|
||||
|
||||
|
|
|
|||
|
|
@ -650,7 +650,8 @@ final class HashtagSearchControllerNode: ASDisplayNode, ASGestureRecognizerDeleg
|
|||
}
|
||||
return self.controller?.navigationController as? NavigationController
|
||||
},
|
||||
listContext: storySearchContext
|
||||
listContext: storySearchContext,
|
||||
initialStoryFolderId: nil
|
||||
)
|
||||
self.storySearchPaneNode = storySearchPaneNode
|
||||
if let storySearchView = self.storySearchComponentView?.view {
|
||||
|
|
|
|||
|
|
@ -1462,7 +1462,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
|
|||
|
||||
#if DEBUG
|
||||
//debugSaveState(basePath: basePath + "/db", name: "previous2")
|
||||
//debugRestoreState(basePath: basePath + "/db", name: "previous2")
|
||||
debugRestoreState(basePath: basePath + "/db", name: "previous2")
|
||||
#endif
|
||||
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
|
|
|
|||
|
|
@ -1550,7 +1550,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||
context: context,
|
||||
initialPrivacy: EngineStoryPrivacy(base: .nobody, additionallyIncludePeers: state.peers),
|
||||
stateContext: stateContext,
|
||||
completion: { _, privacy ,_, _, _, _ in
|
||||
completion: { _, privacy ,_, _, _, _, _ in
|
||||
updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.peers = privacy.additionallyIncludePeers
|
||||
|
|
@ -1589,7 +1589,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||
context: context,
|
||||
initialPrivacy: EngineStoryPrivacy(base: .nobody, additionallyIncludePeers: state.peers),
|
||||
stateContext: stateContext,
|
||||
completion: { _, privacy ,_, _, _, _ in
|
||||
completion: { _, privacy ,_, _, _, _, _ in
|
||||
updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.channels = privacy.additionallyIncludePeers
|
||||
|
|
|
|||
|
|
@ -3511,15 +3511,18 @@ public extension Api.functions.channels {
|
|||
}
|
||||
}
|
||||
public extension Api.functions.channels {
|
||||
static func searchPosts(hashtag: String, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
|
||||
static func searchPosts(flags: Int32, hashtag: String?, query: String?, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32, allowPaidStars: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-778069893)
|
||||
serializeString(hashtag, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(-221973939)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(hashtag!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(query!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(offsetRate, buffer: buffer, boxed: false)
|
||||
offsetPeer.serialize(buffer, true)
|
||||
serializeInt32(offsetId, buffer: buffer, boxed: false)
|
||||
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "channels.searchPosts", parameters: [("hashtag", String(describing: hashtag)), ("offsetRate", String(describing: offsetRate)), ("offsetPeer", String(describing: offsetPeer)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt64(allowPaidStars!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "channels.searchPosts", parameters: [("flags", String(describing: flags)), ("hashtag", String(describing: hashtag)), ("query", String(describing: query)), ("offsetRate", String(describing: offsetRate)), ("offsetPeer", String(describing: offsetPeer)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit)), ("allowPaidStars", String(describing: allowPaidStars))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.Messages?
|
||||
if let signature = reader.readInt32() {
|
||||
|
|
|
|||
|
|
@ -5115,7 +5115,8 @@ func replayFinalState(
|
|||
isMy: item.isMy,
|
||||
myReaction: updatedReaction,
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
authorId: item.authorId,
|
||||
folderIds: item.folderIds
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
updatedPeerEntries[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: item.expirationTimestamp, isCloseFriends: item.isCloseFriends)
|
||||
|
|
@ -5149,7 +5150,8 @@ func replayFinalState(
|
|||
isMy: item.isMy,
|
||||
myReaction: MessageReaction.Reaction(apiReaction: reaction),
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
authorId: item.authorId,
|
||||
folderIds: item.folderIds
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
transaction.setStory(id: StoryId(peerId: peerId, id: id), value: entry)
|
||||
|
|
|
|||
|
|
@ -350,7 +350,8 @@ private final class StoryStatsPublicForwardsContextImpl {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
resultForwards.append(.story(EnginePeer(peer), mappedItem))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ public extension TelegramEngine {
|
|||
return false
|
||||
}
|
||||
}.map(EngineRenderedPeer.init)
|
||||
case .globalPosts:
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -559,7 +559,8 @@ public final class EngineStoryViewListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
),
|
||||
storyStats: transaction.getPeerStoryStats(peerId: peer.id)
|
||||
)))
|
||||
|
|
@ -599,7 +600,8 @@ public final class EngineStoryViewListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
authorId: item.authorId,
|
||||
folderIds: item.folderIds
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
transaction.setStory(id: StoryId(peerId: account.peerId, id: storyId), value: entry)
|
||||
|
|
@ -639,7 +641,8 @@ public final class EngineStoryViewListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
authorId: item.authorId,
|
||||
folderIds: item.folderIds
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
|
|
@ -756,7 +759,8 @@ public final class EngineStoryViewListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
),
|
||||
storyStats: transaction.getPeerStoryStats(peerId: peer.id)
|
||||
)))
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ public extension Stories {
|
|||
case randomId
|
||||
case forwardInfo
|
||||
case uploadInfo
|
||||
case folders
|
||||
}
|
||||
|
||||
public let target: PendingTarget
|
||||
|
|
@ -114,6 +115,7 @@ public extension Stories {
|
|||
public let period: Int32
|
||||
public let randomId: Int64
|
||||
public let forwardInfo: PendingForwardInfo?
|
||||
public let folders: [Int64]
|
||||
public let uploadInfo: StoryUploadInfo?
|
||||
|
||||
public init(
|
||||
|
|
@ -131,6 +133,7 @@ public extension Stories {
|
|||
period: Int32,
|
||||
randomId: Int64,
|
||||
forwardInfo: PendingForwardInfo?,
|
||||
folders: [Int64],
|
||||
uploadInfo: StoryUploadInfo?
|
||||
) {
|
||||
self.target = target
|
||||
|
|
@ -147,6 +150,7 @@ public extension Stories {
|
|||
self.period = period
|
||||
self.randomId = randomId
|
||||
self.forwardInfo = forwardInfo
|
||||
self.folders = folders
|
||||
self.uploadInfo = uploadInfo
|
||||
}
|
||||
|
||||
|
|
@ -176,6 +180,8 @@ public extension Stories {
|
|||
|
||||
self.forwardInfo = try container.decodeIfPresent(PendingForwardInfo.self, forKey: .forwardInfo)
|
||||
|
||||
self.folders = try container.decodeIfPresent([Int64].self, forKey: .folders) ?? []
|
||||
|
||||
self.uploadInfo = try container.decodeIfPresent(StoryUploadInfo.self, forKey: .uploadInfo)
|
||||
}
|
||||
|
||||
|
|
@ -205,6 +211,7 @@ public extension Stories {
|
|||
try container.encode(self.period, forKey: .period)
|
||||
try container.encode(self.randomId, forKey: .randomId)
|
||||
try container.encodeIfPresent(self.forwardInfo, forKey: .forwardInfo)
|
||||
try container.encode(self.folders, forKey: .folders)
|
||||
try container.encodeIfPresent(self.uploadInfo, forKey: .uploadInfo)
|
||||
}
|
||||
|
||||
|
|
@ -245,6 +252,9 @@ public extension Stories {
|
|||
if lhs.forwardInfo != rhs.forwardInfo {
|
||||
return false
|
||||
}
|
||||
if lhs.folders != rhs.folders {
|
||||
return false
|
||||
}
|
||||
if lhs.uploadInfo != rhs.uploadInfo {
|
||||
return false
|
||||
}
|
||||
|
|
@ -473,7 +483,7 @@ final class PendingStoryManager {
|
|||
let partTotalProgress = 1.0 / Float(uploadInfo.total)
|
||||
pendingItemContext.progress = Float(uploadInfo.index) * partTotalProgress
|
||||
}
|
||||
pendingItemContext.disposable = (_internal_uploadStoryImpl(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, stateManager: self.stateManager, messageMediaPreuploadManager: self.messageMediaPreuploadManager, revalidationContext: self.revalidationContext, auxiliaryMethods: self.auxiliaryMethods, toPeerId: toPeerId, stableId: stableId, media: firstItem.media, mediaAreas: firstItem.mediaAreas, text: firstItem.text, entities: firstItem.entities, embeddedStickers: firstItem.embeddedStickers, pin: firstItem.pin, privacy: firstItem.privacy, isForwardingDisabled: firstItem.isForwardingDisabled, period: Int(firstItem.period), randomId: firstItem.randomId, forwardInfo: firstItem.forwardInfo)
|
||||
pendingItemContext.disposable = (_internal_uploadStoryImpl(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, stateManager: self.stateManager, messageMediaPreuploadManager: self.messageMediaPreuploadManager, revalidationContext: self.revalidationContext, auxiliaryMethods: self.auxiliaryMethods, toPeerId: toPeerId, stableId: stableId, media: firstItem.media, mediaAreas: firstItem.mediaAreas, text: firstItem.text, entities: firstItem.entities, embeddedStickers: firstItem.embeddedStickers, pin: firstItem.pin, privacy: firstItem.privacy, isForwardingDisabled: firstItem.isForwardingDisabled, period: Int(firstItem.period), folders: firstItem.folders, randomId: firstItem.randomId, forwardInfo: firstItem.forwardInfo)
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] event in
|
||||
guard let `self` = self else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -473,38 +473,67 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation
|
|||
folderId = nil
|
||||
}
|
||||
|
||||
if case let .general(scope, _, _, _) = location {
|
||||
switch scope {
|
||||
case .everywhere:
|
||||
break
|
||||
case .channels:
|
||||
flags |= (1 << 1)
|
||||
case .groups:
|
||||
flags |= (1 << 2)
|
||||
case .privateChats:
|
||||
flags |= (1 << 3)
|
||||
if case let .general(scope, _, _, _) = location, case let .globalPosts(allowPaidStars) = scope {
|
||||
remoteSearchResult = account.postbox.transaction { transaction -> (Int32, MessageIndex?, Api.InputPeer) in
|
||||
var lowerBound: MessageIndex?
|
||||
if let state = state, let message = state.main.messages.last {
|
||||
lowerBound = message.index
|
||||
}
|
||||
if let lowerBound = lowerBound, let peer = transaction.getPeer(lowerBound.id.peerId), let inputPeer = apiInputPeer(peer) {
|
||||
return (state?.main.nextRate ?? 0, lowerBound, inputPeer)
|
||||
} else {
|
||||
return (0, lowerBound, .inputPeerEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let filter: Api.MessagesFilter = tags.flatMap { messageFilterForTagMask($0) } ?? .inputMessagesFilterEmpty
|
||||
remoteSearchResult = account.postbox.transaction { transaction -> (Int32, MessageIndex?, Api.InputPeer) in
|
||||
var lowerBound: MessageIndex?
|
||||
if let state = state, let message = state.main.messages.last {
|
||||
lowerBound = message.index
|
||||
|> mapToSignal { (nextRate, lowerBound, inputPeer) in
|
||||
var flags: Int32 = 0
|
||||
flags |= 1 << 1
|
||||
if allowPaidStars != nil {
|
||||
flags |= 1 << 2
|
||||
}
|
||||
return account.network.request(Api.functions.channels.searchPosts(flags: flags, hashtag: nil, query: query, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit, allowPaidStars: allowPaidStars.flatMap(Int64.init)), automaticFloodWait: false)
|
||||
|> map { result -> (Api.messages.Messages?, Api.messages.Messages?) in
|
||||
return (result, nil)
|
||||
}
|
||||
|> `catch` { _ -> Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> in
|
||||
return .single((nil, nil))
|
||||
}
|
||||
}
|
||||
if let lowerBound = lowerBound, let peer = transaction.getPeer(lowerBound.id.peerId), let inputPeer = apiInputPeer(peer) {
|
||||
return (state?.main.nextRate ?? 0, lowerBound, inputPeer)
|
||||
} else {
|
||||
return (0, lowerBound, .inputPeerEmpty)
|
||||
} else {
|
||||
if case let .general(scope, _, _, _) = location {
|
||||
switch scope {
|
||||
case .everywhere:
|
||||
break
|
||||
case .channels:
|
||||
flags |= (1 << 1)
|
||||
case .groups:
|
||||
flags |= (1 << 2)
|
||||
case .privateChats:
|
||||
flags |= (1 << 3)
|
||||
case .globalPosts:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|> mapToSignal { (nextRate, lowerBound, inputPeer) in
|
||||
return account.network.request(Api.functions.messages.searchGlobal(flags: flags, folderId: folderId, q: query, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit), automaticFloodWait: false)
|
||||
|> map { result -> (Api.messages.Messages?, Api.messages.Messages?) in
|
||||
return (result, nil)
|
||||
let filter: Api.MessagesFilter = tags.flatMap { messageFilterForTagMask($0) } ?? .inputMessagesFilterEmpty
|
||||
remoteSearchResult = account.postbox.transaction { transaction -> (Int32, MessageIndex?, Api.InputPeer) in
|
||||
var lowerBound: MessageIndex?
|
||||
if let state = state, let message = state.main.messages.last {
|
||||
lowerBound = message.index
|
||||
}
|
||||
if let lowerBound = lowerBound, let peer = transaction.getPeer(lowerBound.id.peerId), let inputPeer = apiInputPeer(peer) {
|
||||
return (state?.main.nextRate ?? 0, lowerBound, inputPeer)
|
||||
} else {
|
||||
return (0, lowerBound, .inputPeerEmpty)
|
||||
}
|
||||
}
|
||||
|> `catch` { _ -> Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> in
|
||||
return .single((nil, nil))
|
||||
|> mapToSignal { (nextRate, lowerBound, inputPeer) in
|
||||
return account.network.request(Api.functions.messages.searchGlobal(flags: flags, folderId: folderId, q: query, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit), automaticFloodWait: false)
|
||||
|> map { result -> (Api.messages.Messages?, Api.messages.Messages?) in
|
||||
return (result, nil)
|
||||
}
|
||||
|> `catch` { _ -> Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> in
|
||||
return .single((nil, nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .sentMedia(tags):
|
||||
|
|
@ -594,7 +623,7 @@ func _internal_searchHashtagPosts(account: Account, hashtag: String, state: Sear
|
|||
}
|
||||
}
|
||||
|> mapToSignal { (nextRate, lowerBound, inputPeer) in
|
||||
return account.network.request(Api.functions.channels.searchPosts(hashtag: hashtag, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit), automaticFloodWait: false)
|
||||
return account.network.request(Api.functions.channels.searchPosts(flags: 1 << 0, hashtag: hashtag, query: nil, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit, allowPaidStars: nil), automaticFloodWait: false)
|
||||
|> map { result -> (Api.messages.Messages?, Api.messages.Messages?) in
|
||||
return (result, nil)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -263,6 +263,7 @@ public enum Stories {
|
|||
case myReaction
|
||||
case forwardInfo
|
||||
case authorId
|
||||
case folderIds
|
||||
}
|
||||
|
||||
public let id: Int32
|
||||
|
|
@ -287,6 +288,7 @@ public enum Stories {
|
|||
public let myReaction: MessageReaction.Reaction?
|
||||
public let forwardInfo: ForwardInfo?
|
||||
public let authorId: PeerId?
|
||||
public let folderIds: [Int64]?
|
||||
|
||||
public init(
|
||||
id: Int32,
|
||||
|
|
@ -310,7 +312,8 @@ public enum Stories {
|
|||
isMy: Bool,
|
||||
myReaction: MessageReaction.Reaction?,
|
||||
forwardInfo: ForwardInfo?,
|
||||
authorId: PeerId?
|
||||
authorId: PeerId?,
|
||||
folderIds: [Int64]?
|
||||
) {
|
||||
self.id = id
|
||||
self.timestamp = timestamp
|
||||
|
|
@ -334,6 +337,7 @@ public enum Stories {
|
|||
self.myReaction = myReaction
|
||||
self.forwardInfo = forwardInfo
|
||||
self.authorId = authorId
|
||||
self.folderIds = folderIds
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
|
|
@ -381,6 +385,7 @@ public enum Stories {
|
|||
self.myReaction = try container.decodeIfPresent(MessageReaction.Reaction.self, forKey: .myReaction)
|
||||
self.forwardInfo = try container.decodeIfPresent(ForwardInfo.self, forKey: .forwardInfo)
|
||||
self.authorId = try container.decodeIfPresent(Int64.self, forKey: .authorId).flatMap { PeerId($0) }
|
||||
self.folderIds = try container.decodeIfPresent([Int64].self, forKey: .folderIds)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
|
|
@ -422,6 +427,7 @@ public enum Stories {
|
|||
try container.encodeIfPresent(self.myReaction, forKey: .myReaction)
|
||||
try container.encodeIfPresent(self.forwardInfo, forKey: .forwardInfo)
|
||||
try container.encodeIfPresent(self.authorId?.toInt64(), forKey: .authorId)
|
||||
try container.encodeIfPresent(self.folderIds, forKey: .folderIds)
|
||||
}
|
||||
|
||||
public static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||
|
|
@ -497,6 +503,9 @@ public enum Stories {
|
|||
if lhs.authorId != rhs.authorId {
|
||||
return false
|
||||
}
|
||||
if lhs.folderIds != rhs.folderIds {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -988,7 +997,7 @@ public struct StoryUploadInfo: Codable, Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
func _internal_uploadStory(account: Account, target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64, forwardInfo: Stories.PendingForwardInfo?, uploadInfo: StoryUploadInfo? = nil) -> Signal<Int32, NoError> {
|
||||
func _internal_uploadStory(account: Account, target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64, forwardInfo: Stories.PendingForwardInfo?, folders: [Int64], uploadInfo: StoryUploadInfo? = nil) -> Signal<Int32, NoError> {
|
||||
let inputMedia = prepareUploadStoryContent(account: account, media: media)
|
||||
|
||||
return (account.postbox.transaction { transaction in
|
||||
|
|
@ -1017,6 +1026,7 @@ func _internal_uploadStory(account: Account, target: Stories.PendingTarget, medi
|
|||
period: Int32(period),
|
||||
randomId: randomId,
|
||||
forwardInfo: forwardInfo,
|
||||
folders: folders,
|
||||
uploadInfo: uploadInfo
|
||||
))
|
||||
transaction.setLocalStoryState(state: CodableEntry(currentState))
|
||||
|
|
@ -1064,6 +1074,7 @@ func _internal_cancelStoryUpload(account: Account, stableId: Int32) {
|
|||
period: currentState.items[i].period,
|
||||
randomId: currentState.items[i].randomId,
|
||||
forwardInfo: currentState.items[i].forwardInfo,
|
||||
folders: currentState.items[i].folders,
|
||||
uploadInfo: StoryUploadInfo(
|
||||
groupingId: groupingId,
|
||||
index: newIndex,
|
||||
|
|
@ -1141,6 +1152,7 @@ func _internal_uploadStoryImpl(
|
|||
privacy: EngineStoryPrivacy,
|
||||
isForwardingDisabled: Bool,
|
||||
period: Int,
|
||||
folders: [Int64],
|
||||
randomId: Int64,
|
||||
forwardInfo: Stories.PendingForwardInfo?
|
||||
) -> Signal<StoryUploadResult, NoError> {
|
||||
|
|
@ -1224,6 +1236,10 @@ func _internal_uploadStoryImpl(
|
|||
fwdFromStory = forwardInfo.storyId
|
||||
}
|
||||
|
||||
if !folders.isEmpty {
|
||||
flags |= 1 << 8
|
||||
}
|
||||
|
||||
return network.request(Api.functions.stories.sendStory(
|
||||
flags: flags,
|
||||
peer: inputPeer,
|
||||
|
|
@ -1236,7 +1252,7 @@ func _internal_uploadStoryImpl(
|
|||
period: Int32(period),
|
||||
fwdFromId: fwdFromId,
|
||||
fwdFromStory: fwdFromStory,
|
||||
albums: nil
|
||||
albums: folders.isEmpty ? nil : folders.map(Int32.init(clamping:))
|
||||
))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
|
|
@ -1287,7 +1303,8 @@ func _internal_uploadStoryImpl(
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: fromId?.peerId
|
||||
authorId: fromId?.peerId,
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||
items.append(StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends))
|
||||
|
|
@ -1695,7 +1712,8 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
authorId: item.authorId,
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||
transaction.setStory(id: storyId, value: entry)
|
||||
|
|
@ -1727,7 +1745,8 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
authorId: item.authorId,
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
|
|
@ -1923,7 +1942,8 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
authorId: item.authorId,
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
|
|
@ -1954,7 +1974,8 @@ func _internal_updateStoriesArePinned(account: Account, peerId: PeerId, ids: [In
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
authorId: item.authorId,
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
updatedItems.append(updatedItem)
|
||||
}
|
||||
|
|
@ -2073,7 +2094,12 @@ extension Stories.Item.ForwardInfo {
|
|||
extension Stories.StoredItem {
|
||||
init?(apiStoryItem: Api.StoryItem, existingItem: Stories.Item? = nil, peerId: PeerId, transaction: Transaction) {
|
||||
switch apiStoryItem {
|
||||
case let .storyItem(flags, id, date, fromId, forwardFrom, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction, _):
|
||||
case let .storyItem(flags, id, date, fromId, forwardFrom, expireDate, caption, entities, media, mediaAreas, privacy, views, sentReaction, albums):
|
||||
var folderIds: [Int64]?
|
||||
if let albums {
|
||||
folderIds = albums.map(Int64.init)
|
||||
}
|
||||
|
||||
let parsedMedia = textMediaAndExpirationTimerFromApiMedia(media, peerId).media
|
||||
if let parsedMedia = parsedMedia {
|
||||
var parsedPrivacy: Stories.Item.Privacy?
|
||||
|
|
@ -2185,7 +2211,8 @@ extension Stories.StoredItem {
|
|||
isMy: mergedIsMy,
|
||||
myReaction: mergedMyReaction,
|
||||
forwardInfo: mergedForwardInfo,
|
||||
authorId: fromId?.peerId
|
||||
authorId: fromId?.peerId,
|
||||
folderIds: folderIds
|
||||
)
|
||||
self = .item(item)
|
||||
} else {
|
||||
|
|
@ -2262,7 +2289,8 @@ func _internal_getStoryById(accountPeerId: PeerId, postbox: Postbox, network: Ne
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2739,7 +2767,8 @@ func _internal_setStoryReaction(account: Account, peerId: EnginePeer.Id, id: Int
|
|||
isMy: item.isMy,
|
||||
myReaction: reaction,
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
authorId: item.authorId,
|
||||
folderIds: item.folderIds
|
||||
))
|
||||
updatedItemValue = updatedItem
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
|
|
@ -2773,7 +2802,8 @@ func _internal_setStoryReaction(account: Account, peerId: EnginePeer.Id, id: Int
|
|||
isMy: item.isMy,
|
||||
myReaction: reaction,
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
authorId: item.authorId,
|
||||
folderIds: item.folderIds
|
||||
))
|
||||
updatedItemValue = updatedItem
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
|
|
|
|||
|
|
@ -86,8 +86,9 @@ public final class EngineStoryItem: Equatable {
|
|||
public let myReaction: MessageReaction.Reaction?
|
||||
public let forwardInfo: ForwardInfo?
|
||||
public let author: EnginePeer?
|
||||
public let folderIds: [Int64]?
|
||||
|
||||
public init(id: Int32, timestamp: Int32, expirationTimestamp: Int32, media: EngineMedia, alternativeMediaList: [EngineMedia], mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool, isPending: Bool, isCloseFriends: Bool, isContacts: Bool, isSelectedContacts: Bool, isForwardingDisabled: Bool, isEdited: Bool, isMy: Bool, myReaction: MessageReaction.Reaction?, forwardInfo: ForwardInfo?, author: EnginePeer?) {
|
||||
public init(id: Int32, timestamp: Int32, expirationTimestamp: Int32, media: EngineMedia, alternativeMediaList: [EngineMedia], mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool, isPending: Bool, isCloseFriends: Bool, isContacts: Bool, isSelectedContacts: Bool, isForwardingDisabled: Bool, isEdited: Bool, isMy: Bool, myReaction: MessageReaction.Reaction?, forwardInfo: ForwardInfo?, author: EnginePeer?, folderIds: [Int64]?) {
|
||||
self.id = id
|
||||
self.timestamp = timestamp
|
||||
self.expirationTimestamp = expirationTimestamp
|
||||
|
|
@ -111,6 +112,7 @@ public final class EngineStoryItem: Equatable {
|
|||
self.myReaction = myReaction
|
||||
self.forwardInfo = forwardInfo
|
||||
self.author = author
|
||||
self.folderIds = folderIds
|
||||
}
|
||||
|
||||
public static func ==(lhs: EngineStoryItem, rhs: EngineStoryItem) -> Bool {
|
||||
|
|
@ -183,6 +185,9 @@ public final class EngineStoryItem: Equatable {
|
|||
if lhs.author != rhs.author {
|
||||
return false
|
||||
}
|
||||
if lhs.folderIds != rhs.folderIds {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -237,7 +242,8 @@ public extension EngineStoryItem {
|
|||
|
||||
myReaction: self.myReaction,
|
||||
forwardInfo: self.forwardInfo?.storedForwardInfo,
|
||||
authorId: self.author?.id
|
||||
authorId: self.author?.id,
|
||||
folderIds: self.folderIds
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -679,6 +685,8 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
return (nil, [], [], 0, [:], [], false)
|
||||
}
|
||||
|
||||
let mainFolders: [State.Folder] = cachedMain.folders
|
||||
|
||||
let cached: CachedPeerStoryListHead
|
||||
if let folderId {
|
||||
let key = ValueBoxKey(length: 8 + 1 + 8)
|
||||
|
|
@ -733,7 +741,8 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
items.append(State.Item(
|
||||
id: StoryId(peerId: peerId, id: mappedItem.id),
|
||||
|
|
@ -768,7 +777,7 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
|
||||
let peerReference = transaction.getPeer(peerId).flatMap(PeerReference.init)
|
||||
|
||||
return (peerReference, items, cached.pinnedIds, Int(cached.totalCount), allEntityFiles, cached.folders, true)
|
||||
return (peerReference, items, cached.pinnedIds, Int(cached.totalCount), allEntityFiles, mainFolders, true)
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] peerReference, items, pinnedIds, totalCount, allEntityFiles, folders, hasCache in
|
||||
guard let self else {
|
||||
|
|
@ -828,103 +837,81 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
let accountPeerId = account.peerId
|
||||
let isArchived = self.isArchived
|
||||
let folderId = self.folderId
|
||||
let folders = self.stateValue.availableFolders
|
||||
let queue = self.queue
|
||||
self.requestDisposable = (self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<([State.Item], Int, PeerReference?, Bool), NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .single(([], 0, nil, false))
|
||||
|> mapToSignal { inputPeer -> Signal<([State.Item], Int, PeerReference?, [State.Folder], Bool), NoError> in
|
||||
guard let inputPeer else {
|
||||
return .single(([], 0, nil, [], false))
|
||||
}
|
||||
|
||||
let signal: Signal<Api.stories.Stories, MTRpcError>
|
||||
if isArchived {
|
||||
signal = account.network.request(Api.functions.stories.getStoriesArchive(peer: inputPeer, offsetId: Int32(loadMoreToken), limit: Int32(limit)))
|
||||
var signal: Signal<Api.stories.Stories?, MTRpcError>
|
||||
var additionalFolders: Signal<Api.stories.Albums?, MTRpcError> = .single(nil)
|
||||
if let folderId {
|
||||
signal = account.network.request(Api.functions.stories.getAlbumStories(peer: inputPeer, albumId: Int32(clamping: folderId), offset: Int32(loadMoreToken), limit: Int32(limit))) |> map(Optional.init)
|
||||
} else {
|
||||
signal = account.network.request(Api.functions.stories.getPinnedStories(peer: inputPeer, offsetId: Int32(loadMoreToken), limit: Int32(limit)))
|
||||
if isArchived {
|
||||
signal = account.network.request(Api.functions.stories.getStoriesArchive(peer: inputPeer, offsetId: Int32(loadMoreToken), limit: Int32(limit))) |> map(Optional.init)
|
||||
} else {
|
||||
signal = account.network.request(Api.functions.stories.getPinnedStories(peer: inputPeer, offsetId: Int32(loadMoreToken), limit: Int32(limit))) |> map(Optional.init)
|
||||
additionalFolders = account.network.request(Api.functions.stories.getAlbums(peer: inputPeer, hash: 0))
|
||||
|> map(Optional.init)
|
||||
}
|
||||
}
|
||||
return signal
|
||||
|> map { result -> Api.stories.Stories? in
|
||||
return result
|
||||
|
||||
#if DEBUG
|
||||
if folderId != nil {
|
||||
signal = signal |> delay(2.0, queue: queue)
|
||||
}
|
||||
|> `catch` { _ -> Signal<Api.stories.Stories?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<([State.Item], Int, PeerReference?, Bool), NoError> in
|
||||
guard let result = result else {
|
||||
return .single(([], 0, nil, false))
|
||||
#endif
|
||||
|
||||
return combineLatest(
|
||||
signal
|
||||
|> `catch` { _ -> Signal<Api.stories.Stories?, NoError> in
|
||||
return .single(nil)
|
||||
},
|
||||
additionalFolders
|
||||
|> `catch` { _ -> Signal<Api.stories.Albums?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
)
|
||||
|> mapToSignal { result, updatedFolders -> Signal<([State.Item], Int, PeerReference?, [State.Folder], Bool), NoError> in
|
||||
guard let result else {
|
||||
return .single(([], 0, nil, [], false))
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> ([State.Item], Int, PeerReference?, Bool) in
|
||||
return account.postbox.transaction { transaction -> ([State.Item], Int, PeerReference?, [State.Folder], Bool) in
|
||||
var storyItems: [State.Item] = []
|
||||
var totalCount: Int = 0
|
||||
var hasMore: Bool = false
|
||||
var folderItems: [State.Folder] = []
|
||||
|
||||
if let folderId {
|
||||
let key = ValueBoxKey(length: 8 + 1 + 8)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: isArchived ? 1 : 0)
|
||||
key.setInt64(8 + 1, value: folderId)
|
||||
|
||||
var updatedItems: [Stories.StoredItem] = []
|
||||
if let currentEntry = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key))?.get(CachedPeerStoryListHead.self) {
|
||||
updatedItems = currentEntry.items
|
||||
}
|
||||
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: updatedItems, pinnedIds: [], totalCount: Int32(updatedItems.count), folders: [])) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
|
||||
for item in updatedItems {
|
||||
if case let .item(item) = item, let media = item.media {
|
||||
let mappedItem = EngineStoryItem(
|
||||
id: item.id,
|
||||
timestamp: item.timestamp,
|
||||
expirationTimestamp: item.expirationTimestamp,
|
||||
media: EngineMedia(media),
|
||||
alternativeMediaList: item.alternativeMediaList.map(EngineMedia.init),
|
||||
mediaAreas: item.mediaAreas,
|
||||
text: item.text,
|
||||
entities: item.entities,
|
||||
views: item.views.flatMap { views in
|
||||
return EngineStoryItem.Views(
|
||||
seenCount: views.seenCount,
|
||||
reactedCount: views.reactedCount,
|
||||
forwardCount: views.forwardCount,
|
||||
seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in
|
||||
return transaction.getPeer(id).flatMap(EnginePeer.init)
|
||||
},
|
||||
reactions: views.reactions,
|
||||
hasList: views.hasList
|
||||
)
|
||||
},
|
||||
privacy: item.privacy.flatMap(EngineStoryPrivacy.init),
|
||||
isPinned: item.isPinned,
|
||||
isExpired: item.isExpired,
|
||||
isPublic: item.isPublic,
|
||||
isPending: false,
|
||||
isCloseFriends: item.isCloseFriends,
|
||||
isContacts: item.isContacts,
|
||||
isSelectedContacts: item.isSelectedContacts,
|
||||
isForwardingDisabled: item.isForwardingDisabled,
|
||||
isEdited: item.isEdited,
|
||||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
)
|
||||
storyItems.append(State.Item(
|
||||
id: StoryId(peerId: peerId, id: mappedItem.id),
|
||||
storyItem: mappedItem,
|
||||
peer: nil
|
||||
))
|
||||
if let updatedFolders {
|
||||
switch updatedFolders {
|
||||
case let .albums(_, albums):
|
||||
for album in albums {
|
||||
switch album {
|
||||
case let .storyAlbum(_, albumId, title, iconPhoto, iconVideo):
|
||||
let _ = iconPhoto
|
||||
let _ = iconVideo
|
||||
folderItems.append(State.Folder(
|
||||
id: Int64(albumId),
|
||||
title: title
|
||||
))
|
||||
}
|
||||
}
|
||||
case .albumsNotModified:
|
||||
break
|
||||
}
|
||||
} else {
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: 0)
|
||||
|
||||
totalCount = storyItems.count
|
||||
hasMore = false
|
||||
|
||||
return (storyItems, totalCount, transaction.getPeer(peerId).flatMap(PeerReference.init), hasMore)
|
||||
if let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key))?.get(CachedPeerStoryListHead.self) {
|
||||
folderItems = cached.folders
|
||||
}
|
||||
}
|
||||
|
||||
switch result {
|
||||
|
|
@ -973,7 +960,8 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
storyItems.append(State.Item(
|
||||
id: StoryId(peerId: peerId, id: mappedItem.id),
|
||||
|
|
@ -985,20 +973,38 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
}
|
||||
|
||||
if loadMoreToken == 0 {
|
||||
let key: ValueBoxKey
|
||||
if let folderId {
|
||||
key = ValueBoxKey(length: 8 + 1 + 8)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: isArchived ? 1 : 0)
|
||||
key.setInt64(8 + 1, value: folderId)
|
||||
} else {
|
||||
key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: isArchived ? 1 : 0)
|
||||
}
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: storyItems.prefix(100).map { .item($0.storyItem.asStoryItem()) }, pinnedIds: pinnedIds, totalCount: count, folders: folderId == nil ? folderItems : [])) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
} else if folderId == nil && updatedFolders != nil {
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: isArchived ? 1 : 0)
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: storyItems.prefix(100).map { .item($0.storyItem.asStoryItem()) }, pinnedIds: pinnedIds, totalCount: count, folders: folders)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
key.setInt8(8, value: 0)
|
||||
|
||||
if let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key))?.get(CachedPeerStoryListHead.self) {
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: cached.items, pinnedIds: cached.pinnedIds, totalCount: cached.totalCount, folders: folderItems)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (storyItems, totalCount, transaction.getPeer(peerId).flatMap(PeerReference.init), hasMore)
|
||||
return (storyItems, totalCount, transaction.getPeer(peerId).flatMap(PeerReference.init), folderItems, hasMore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] storyItems, totalCount, peerReference, hasMore in
|
||||
|> deliverOn(queue)).start(next: { [weak self] storyItems, totalCount, peerReference, folderItems, hasMore in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -1022,6 +1028,8 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
updatedState.items.append(item)
|
||||
}
|
||||
|
||||
updatedState.availableFolders = folderItems
|
||||
|
||||
if updatedState.peerReference == nil {
|
||||
updatedState.peerReference = peerReference
|
||||
}
|
||||
|
|
@ -1147,7 +1155,8 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) },
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
), peer: nil)
|
||||
finalUpdatedState = updatedState
|
||||
}
|
||||
|
|
@ -1196,7 +1205,8 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) },
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
), peer: nil)
|
||||
finalUpdatedState = updatedState
|
||||
} else {
|
||||
|
|
@ -1247,7 +1257,8 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) },
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
), peer: nil))
|
||||
updatedState.items.sort(by: { lhs, rhs in
|
||||
let lhsPinned = updatedState.pinnedIds.firstIndex(of: lhs.storyItem.id)
|
||||
|
|
@ -1304,7 +1315,8 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) },
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
), peer: nil))
|
||||
updatedState.items.sort(by: { lhs, rhs in
|
||||
let lhsPinned = updatedState.pinnedIds.firstIndex(of: lhs.storyItem.id)
|
||||
|
|
@ -1376,55 +1388,143 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
})
|
||||
}
|
||||
|
||||
func addFolder(title: String) -> Int64 {
|
||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||
|
||||
var state = self.stateValue
|
||||
state.availableFolders.append(StoryListContextState.Folder(id: id, title: title))
|
||||
self.stateValue = state
|
||||
|
||||
func addFolder(title: String, items: [EngineStoryItem], completion: @escaping (Int64?) -> Void) -> Disposable {
|
||||
let peerId = self.peerId
|
||||
let isArchived = self.isArchived
|
||||
let items = state.items
|
||||
let pinnedIds = state.pinnedIds
|
||||
let totalCount = state.totalCount
|
||||
let folders = state.availableFolders
|
||||
let _ = (self.account.postbox.transaction { transaction -> Void in
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: isArchived ? 1 : 0)
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: items.prefix(100).map { .item($0.storyItem.asStoryItem()) }, pinnedIds: pinnedIds, totalCount: Int32(totalCount), folders: folders)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
return (self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Api.StoryAlbum?, NoError> in
|
||||
guard let inputPeer else {
|
||||
return .single(nil)
|
||||
}
|
||||
}).start()
|
||||
|
||||
return id
|
||||
return self.account.network.request(Api.functions.stories.createAlbum(
|
||||
peer: inputPeer, title: title, stories: items.map(\.id)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.StoryAlbum?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let result {
|
||||
switch result {
|
||||
case let .storyAlbum(_, albumId, _, _, _):
|
||||
var state = self.stateValue
|
||||
state.availableFolders.insert(StoryListContextState.Folder(id: Int64(albumId), title: title), at: 0)
|
||||
self.stateValue = state
|
||||
|
||||
let peerId = self.peerId
|
||||
let isArchived = self.isArchived
|
||||
let items = state.items
|
||||
let pinnedIds = state.pinnedIds
|
||||
let totalCount = state.totalCount
|
||||
let folders = state.availableFolders
|
||||
let _ = (self.account.postbox.transaction { transaction -> Void in
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: isArchived ? 1 : 0)
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: items.prefix(100).map { .item($0.storyItem.asStoryItem()) }, pinnedIds: pinnedIds, totalCount: Int32(totalCount), folders: folders)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
|
||||
let mappedItems = items.map { item in
|
||||
return item.storyItem.asStoryItem()
|
||||
}
|
||||
|
||||
let folderKey = ValueBoxKey(length: 8 + 1 + 8)
|
||||
folderKey.setInt64(0, value: peerId.toInt64())
|
||||
folderKey.setInt8(8, value: 0)
|
||||
folderKey.setInt64(8 + 1, value: Int64(albumId))
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: mappedItems.prefix(100).map { .item($0) }, pinnedIds: [], totalCount: Int32(mappedItems.count), folders: [])) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: folderKey), entry: entry)
|
||||
}
|
||||
} |> deliverOn(self.queue)).start(completed: {
|
||||
completion(Int64(albumId))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func removeFolder(id: Int64) {
|
||||
func removeFolder(id: Int64) -> Disposable {
|
||||
var state = self.stateValue
|
||||
if let index = state.availableFolders.firstIndex(where: { $0.id == id }) {
|
||||
state.availableFolders.remove(at: index)
|
||||
}
|
||||
self.stateValue = state
|
||||
|
||||
let account = self.account
|
||||
let peerId = self.peerId
|
||||
let isArchived = self.isArchived
|
||||
let items = state.items
|
||||
let pinnedIds = state.pinnedIds
|
||||
let totalCount = state.totalCount
|
||||
let folders = state.availableFolders
|
||||
let _ = (self.account.postbox.transaction { transaction -> Void in
|
||||
return (account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: isArchived ? 1 : 0)
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: items.prefix(100).map { .item($0.storyItem.asStoryItem()) }, pinnedIds: pinnedIds, totalCount: Int32(totalCount), folders: folders)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Never, NoError> in
|
||||
guard let inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
return account.network.request(Api.functions.stories.deleteAlbum(peer: inputPeer, albumId: Int32(clamping: id)))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> ignoreValues
|
||||
}).start()
|
||||
}
|
||||
|
||||
func reorderFolders(ids: [Int64]) {
|
||||
func renameFolder(id: Int64, title: String) -> Disposable {
|
||||
var state = self.stateValue
|
||||
if let index = state.availableFolders.firstIndex(where: { $0.id == id }) {
|
||||
state.availableFolders[index] = State.Folder(id: state.availableFolders[index].id, title: title)
|
||||
}
|
||||
self.stateValue = state
|
||||
let updatedFolders = state.availableFolders
|
||||
|
||||
let account = self.account
|
||||
let peerId = self.peerId
|
||||
return (account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: 0)
|
||||
|
||||
if let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key))?.get(CachedPeerStoryListHead.self) {
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: cached.items, pinnedIds: cached.pinnedIds, totalCount: cached.totalCount, folders: updatedFolders)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
}
|
||||
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Never, NoError> in
|
||||
guard let inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
var flags: Int32 = 0
|
||||
flags |= 1 << 0
|
||||
return account.network.request(Api.functions.stories.updateAlbum(flags: flags, peer: inputPeer, albumId: Int32(clamping: id), title: title, deleteStories: nil, addStories: nil, order: nil))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.StoryAlbum?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> ignoreValues
|
||||
}).start()
|
||||
}
|
||||
|
||||
func reorderFolders(ids: [Int64]) -> Disposable {
|
||||
var state = self.stateValue
|
||||
var folders: [State.Folder] = []
|
||||
for id in ids {
|
||||
|
|
@ -1440,24 +1540,51 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
state.availableFolders = folders
|
||||
self.stateValue = state
|
||||
|
||||
let account = self.account
|
||||
let peerId = self.peerId
|
||||
let isArchived = self.isArchived
|
||||
let items = state.items
|
||||
let pinnedIds = state.pinnedIds
|
||||
let totalCount = state.totalCount
|
||||
let _ = (self.account.postbox.transaction { transaction -> Void in
|
||||
return (account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: isArchived ? 1 : 0)
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: items.prefix(100).map { .item($0.storyItem.asStoryItem()) }, pinnedIds: pinnedIds, totalCount: Int32(totalCount), folders: folders)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Never, NoError> in
|
||||
guard let inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
return account.network.request(Api.functions.stories.reorderAlbums(peer: inputPeer, order: ids.map(Int32.init(clamping:))))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> ignoreValues
|
||||
}).start()
|
||||
}
|
||||
|
||||
func addToFolder(id: Int64, items: [EngineStoryItem]) {
|
||||
func addToFolder(id: Int64, items: [EngineStoryItem]) -> Disposable {
|
||||
if self.folderId == id {
|
||||
var state = self.stateValue
|
||||
for item in items {
|
||||
state.items.removeAll(where: { $0.id.id == item.id })
|
||||
state.items.insert(StoryListContextState.Item(
|
||||
id: StoryId(peerId: self.peerId, id: item.id),
|
||||
storyItem: item,
|
||||
peer: nil
|
||||
), at: 0)
|
||||
}
|
||||
state.totalCount = state.items.count
|
||||
self.stateValue = state
|
||||
}
|
||||
|
||||
let account = self.account
|
||||
let peerId = self.peerId
|
||||
let _ = (self.account.postbox.transaction { transaction -> Void in
|
||||
return (account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
let key = ValueBoxKey(length: 8 + 1 + 8)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: 0)
|
||||
|
|
@ -1518,7 +1645,8 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
return .unknown(name: name, isModified: isModified)
|
||||
}
|
||||
},
|
||||
authorId: item.author?.id
|
||||
authorId: item.author?.id,
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
if !updatedItems.contains(where: { $0.id == mappedItem.id }) {
|
||||
updatedItems.insert(mappedItem, at: 0)
|
||||
|
|
@ -1528,26 +1656,35 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
if let entry = CodableEntry(CachedPeerStoryListHead(items: updatedItems.map { .item($0) }, pinnedIds: [], totalCount: Int32(updatedItems.count), folders: [])) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Never, NoError> in
|
||||
guard let inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
var flags: Int32 = 0
|
||||
flags |= 1 << 2
|
||||
return account.network.request(Api.functions.stories.updateAlbum(flags: flags, peer: inputPeer, albumId: Int32(clamping: id), title: nil, deleteStories: nil, addStories: items.map(\.id), order: nil))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.StoryAlbum?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> ignoreValues
|
||||
}).start()
|
||||
|
||||
}
|
||||
|
||||
func removeFromFolder(id: Int64, itemIds: [Int32]) -> Disposable {
|
||||
if self.folderId == id {
|
||||
var state = self.stateValue
|
||||
for item in items {
|
||||
state.items.removeAll(where: { $0.id.id == item.id })
|
||||
state.items.insert(StoryListContextState.Item(
|
||||
id: StoryId(peerId: self.peerId, id: item.id),
|
||||
storyItem: item,
|
||||
peer: nil
|
||||
), at: 0)
|
||||
}
|
||||
state.items.removeAll(where: { itemIds.contains($0.id.id) })
|
||||
state.totalCount = state.items.count
|
||||
self.stateValue = state
|
||||
}
|
||||
}
|
||||
|
||||
func removeFromFolder(id: Int64, itemIds: [Int32]) {
|
||||
|
||||
let account = self.account
|
||||
let peerId = self.peerId
|
||||
let _ = (self.account.postbox.transaction { transaction -> Void in
|
||||
return (account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
let key = ValueBoxKey(length: 8 + 1 + 8)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: 0)
|
||||
|
|
@ -1573,23 +1710,52 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
if let entry = CodableEntry(CachedPeerStoryListHead(items: updatedItems.map { .item($0) }, pinnedIds: [], totalCount: Int32(updatedItems.count), folders: [])) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Never, NoError> in
|
||||
guard let inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
var flags: Int32 = 0
|
||||
flags |= 1 << 1
|
||||
return account.network.request(Api.functions.stories.updateAlbum(flags: flags, peer: inputPeer, albumId: Int32(clamping: id), title: nil, deleteStories: itemIds, addStories: nil, order: nil))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.StoryAlbum?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> ignoreValues
|
||||
}).start()
|
||||
}
|
||||
|
||||
func reorderItemsInFolder(itemIds: [Int32]) -> Disposable {
|
||||
guard let id = self.folderId else {
|
||||
return EmptyDisposable
|
||||
}
|
||||
|
||||
if self.folderId == id {
|
||||
var state = self.stateValue
|
||||
state.items.removeAll(where: { itemIds.contains($0.id.id) })
|
||||
state.totalCount = state.items.count
|
||||
|
||||
let previousItems = state.items
|
||||
var updatedItems: [State.Item] = []
|
||||
for itemId in itemIds {
|
||||
if let index = previousItems.firstIndex(where: { $0.id.id == itemId }) {
|
||||
updatedItems.append(previousItems[index])
|
||||
}
|
||||
}
|
||||
for item in previousItems {
|
||||
if !updatedItems.contains(where: { $0.id == item.id }) {
|
||||
updatedItems.append(item)
|
||||
}
|
||||
}
|
||||
|
||||
state.items = updatedItems
|
||||
self.stateValue = state
|
||||
}
|
||||
}
|
||||
|
||||
func reorderItemsInFolder(itemIds: [Int32]) {
|
||||
guard let id = self.folderId else {
|
||||
return
|
||||
}
|
||||
|
||||
let account = self.account
|
||||
let peerId = self.peerId
|
||||
let _ = (self.account.postbox.transaction { transaction -> Void in
|
||||
return (account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
let key = ValueBoxKey(length: 8 + 1 + 8)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: 0)
|
||||
|
|
@ -1622,27 +1788,22 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
if let entry = CodableEntry(CachedPeerStoryListHead(items: updatedItems.map { .item($0) }, pinnedIds: [], totalCount: Int32(updatedItems.count), folders: [])) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
}).start()
|
||||
|
||||
if self.folderId == id {
|
||||
var state = self.stateValue
|
||||
|
||||
let previousItems = state.items
|
||||
var updatedItems: [State.Item] = []
|
||||
for itemId in itemIds {
|
||||
if let index = previousItems.firstIndex(where: { $0.id.id == itemId }) {
|
||||
updatedItems.append(previousItems[index])
|
||||
}
|
||||
}
|
||||
for item in previousItems {
|
||||
if !updatedItems.contains(where: { $0.id == item.id }) {
|
||||
updatedItems.append(item)
|
||||
}
|
||||
}
|
||||
|
||||
state.items = updatedItems
|
||||
self.stateValue = state
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Never, NoError> in
|
||||
guard let inputPeer else {
|
||||
return .complete()
|
||||
}
|
||||
var flags: Int32 = 0
|
||||
flags |= 1 << 3
|
||||
return account.network.request(Api.functions.stories.updateAlbum(flags: flags, peer: inputPeer, albumId: Int32(clamping: id), title: nil, deleteStories: nil, addStories: nil, order: itemIds))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.StoryAlbum?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> ignoreValues
|
||||
}).start()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1676,61 +1837,100 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
}
|
||||
}
|
||||
|
||||
public func addFolder(title: String, completion: @escaping (Int64) -> Void) {
|
||||
public func addFolder(title: String, items: [EngineStoryItem], completion: @escaping (Int64?) -> Void) -> Disposable {
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
completion(impl.addFolder(title: title))
|
||||
disposable.set(impl.addFolder(title: title, items: items, completion: completion))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
|
||||
public func removeFolder(id: Int64) {
|
||||
public func removeFolder(id: Int64) -> Disposable {
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
impl.removeFolder(id: id)
|
||||
disposable.set(impl.removeFolder(id: id))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
|
||||
public func reorderFolders(ids: [Int64]) {
|
||||
public func renameFolder(id: Int64, title: String) -> Disposable {
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
impl.reorderFolders(ids: ids)
|
||||
disposable.set(impl.renameFolder(id: id, title: title))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
|
||||
public func addToFolder(id: Int64, items: [EngineStoryItem]) {
|
||||
public func reorderFolders(ids: [Int64]) -> Disposable {
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
impl.addToFolder(id: id, items: items)
|
||||
disposable.set(impl.reorderFolders(ids: ids))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
|
||||
public func removeFromFolder(id: Int64, itemIds: [Int32]) {
|
||||
public func addToFolder(id: Int64, items: [EngineStoryItem]) -> Disposable {
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
impl.removeFromFolder(id: id, itemIds: itemIds)
|
||||
disposable.set(impl.addToFolder(id: id, items: items))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
|
||||
public func reorderItemsInFolder(itemIds: [Int32]) {
|
||||
public func removeFromFolder(id: Int64, itemIds: [Int32]) -> Disposable {
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
impl.reorderItemsInFolder(itemIds: itemIds)
|
||||
disposable.set(impl.removeFromFolder(id: id, itemIds: itemIds))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
|
||||
public static func addFolderExternal(account: Account, peerId: PeerId, title: String) -> Int64 {
|
||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||
|
||||
let _ = (account.postbox.transaction { transaction -> Void in
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: 0)
|
||||
|
||||
|
||||
let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key))?.get(CachedPeerStoryListHead.self)
|
||||
var folders = cached?.folders ?? []
|
||||
folders.append(StoryListContextState.Folder(id: id, title: title))
|
||||
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: cached?.items ?? [], pinnedIds: cached?.pinnedIds ?? [], totalCount: cached?.totalCount ?? 0, folders: folders)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
public func reorderItemsInFolder(itemIds: [Int32]) -> Disposable {
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.reorderItemsInFolder(itemIds: itemIds))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
|
||||
public static func addFolderExternal(account: Account, peerId: PeerId, title: String, completion: @escaping (Int64?) -> Void) -> Disposable {
|
||||
return (account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Api.StoryAlbum?, NoError> in
|
||||
guard let inputPeer else {
|
||||
return .single(nil)
|
||||
}
|
||||
}).start()
|
||||
|
||||
return id
|
||||
return account.network.request(Api.functions.stories.createAlbum(
|
||||
peer: inputPeer, title: title, stories: []))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.StoryAlbum?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
}).start(next: { result in
|
||||
if let result {
|
||||
switch result {
|
||||
case let .storyAlbum(_, albumId, _, _, _):
|
||||
let _ = (account.postbox.transaction { transaction -> Void in
|
||||
let key = ValueBoxKey(length: 8 + 1)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt8(8, value: 0)
|
||||
|
||||
let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key))?.get(CachedPeerStoryListHead.self)
|
||||
var folders = cached?.folders ?? []
|
||||
folders.append(StoryListContextState.Folder(id: Int64(albumId), title: title))
|
||||
|
||||
if let entry = CodableEntry(CachedPeerStoryListHead(items: cached?.items ?? [], pinnedIds: cached?.pinnedIds ?? [], totalCount: cached?.totalCount ?? 0, folders: folders)) {
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry)
|
||||
}
|
||||
}).start(completed: {
|
||||
completion(Int64(albumId))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public static func folderPreviews(peerId: EnginePeer.Id, account: Account) -> Signal<(peer: PeerReference, [(folder: StoryListContext.State.Folder, item: EngineStoryItem?)]), NoError> {
|
||||
|
|
@ -1789,7 +1989,8 @@ public final class PeerStoryListContext: StoryListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1971,7 +2172,8 @@ public final class SearchStoryListContext: StoryListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
storyItems.append(State.Item(
|
||||
id: StoryId(peerId: peer.peerId, id: mappedItem.id),
|
||||
|
|
@ -1988,7 +2190,7 @@ public final class SearchStoryListContext: StoryListContext {
|
|||
}
|
||||
}
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] storyItems, totalCount, nextOffset in
|
||||
guard let `self` = self else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -2120,7 +2322,8 @@ public final class SearchStoryListContext: StoryListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) },
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
),
|
||||
peer: currentItem.peer
|
||||
)
|
||||
|
|
@ -2181,7 +2384,8 @@ public final class SearchStoryListContext: StoryListContext {
|
|||
isMy: item.storyItem.isMy,
|
||||
myReaction: reaction,
|
||||
forwardInfo: item.storyItem.forwardInfo,
|
||||
author: item.storyItem.author
|
||||
author: item.storyItem.author,
|
||||
folderIds: item.storyItem.folderIds
|
||||
),
|
||||
peer: item.peer
|
||||
)
|
||||
|
|
@ -2310,7 +2514,8 @@ public final class PeerExpiringStoryListContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
items.append(.item(mappedItem))
|
||||
}
|
||||
|
|
@ -2756,7 +2961,8 @@ public final class BotPreviewStoryListContext: StoryListContext {
|
|||
isMy: false,
|
||||
myReaction: nil,
|
||||
forwardInfo: nil,
|
||||
author: nil
|
||||
author: nil,
|
||||
folderIds: nil
|
||||
),
|
||||
peer: nil
|
||||
))
|
||||
|
|
@ -2805,7 +3011,8 @@ public final class BotPreviewStoryListContext: StoryListContext {
|
|||
isMy: false,
|
||||
myReaction: nil,
|
||||
forwardInfo: nil,
|
||||
author: nil
|
||||
author: nil,
|
||||
folderIds: nil
|
||||
),
|
||||
peer: nil
|
||||
))
|
||||
|
|
@ -2917,7 +3124,8 @@ public final class BotPreviewStoryListContext: StoryListContext {
|
|||
isMy: false,
|
||||
myReaction: nil,
|
||||
forwardInfo: nil,
|
||||
author: nil
|
||||
author: nil,
|
||||
folderIds: nil
|
||||
),
|
||||
peer: nil
|
||||
))
|
||||
|
|
@ -2994,7 +3202,8 @@ public final class BotPreviewStoryListContext: StoryListContext {
|
|||
isMy: false,
|
||||
myReaction: nil,
|
||||
forwardInfo: nil,
|
||||
author: nil
|
||||
author: nil,
|
||||
folderIds: nil
|
||||
),
|
||||
peer: nil
|
||||
))
|
||||
|
|
@ -3057,7 +3266,8 @@ public final class BotPreviewStoryListContext: StoryListContext {
|
|||
isMy: false,
|
||||
myReaction: nil,
|
||||
forwardInfo: nil,
|
||||
author: nil
|
||||
author: nil,
|
||||
folderIds: nil
|
||||
),
|
||||
peer: nil
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1353,7 +1353,8 @@ public extension TelegramEngine {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo,
|
||||
authorId: item.authorId
|
||||
authorId: item.authorId,
|
||||
folderIds: item.folderIds
|
||||
))
|
||||
if let entry = CodableEntry(updatedItem) {
|
||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||
|
|
@ -1367,8 +1368,8 @@ public extension TelegramEngine {
|
|||
}
|
||||
}
|
||||
|
||||
public func uploadStory(target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64, forwardInfo: Stories.PendingForwardInfo?, uploadInfo: StoryUploadInfo? = nil) -> Signal<Int32, NoError> {
|
||||
return _internal_uploadStory(account: self.account, target: target, media: media, mediaAreas: mediaAreas, text: text, entities: entities, pin: pin, privacy: privacy, isForwardingDisabled: isForwardingDisabled, period: period, randomId: randomId, forwardInfo: forwardInfo, uploadInfo: uploadInfo)
|
||||
public func uploadStory(target: Stories.PendingTarget, media: EngineStoryInputMedia, mediaAreas: [MediaArea], text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, isForwardingDisabled: Bool, period: Int, randomId: Int64, forwardInfo: Stories.PendingForwardInfo?, folders: [Int64], uploadInfo: StoryUploadInfo? = nil) -> Signal<Int32, NoError> {
|
||||
return _internal_uploadStory(account: self.account, target: target, media: media, mediaAreas: mediaAreas, text: text, entities: entities, pin: pin, privacy: privacy, isForwardingDisabled: isForwardingDisabled, period: period, randomId: randomId, forwardInfo: forwardInfo, folders: folders, uploadInfo: uploadInfo)
|
||||
}
|
||||
|
||||
public func allStoriesUploadEvents() -> Signal<(Int32, Int32), NoError> {
|
||||
|
|
|
|||
|
|
@ -18,11 +18,12 @@ public struct FoundPeer: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
public enum TelegramSearchPeersScope {
|
||||
public enum TelegramSearchPeersScope: Equatable {
|
||||
case everywhere
|
||||
case channels
|
||||
case groups
|
||||
case privateChats
|
||||
case globalPosts(allowPaidStars: Int?)
|
||||
}
|
||||
|
||||
public func _internal_searchPeers(accountPeerId: PeerId, postbox: Postbox, network: Network, query: String, scope: TelegramSearchPeersScope) -> Signal<([FoundPeer], [FoundPeer]), NoError> {
|
||||
|
|
@ -138,6 +139,9 @@ public func _internal_searchPeers(accountPeerId: PeerId, postbox: Postbox, netwo
|
|||
return false
|
||||
}
|
||||
}
|
||||
case .globalPosts:
|
||||
renderedMyPeers = []
|
||||
renderedPeers = []
|
||||
}
|
||||
|
||||
return (renderedMyPeers, renderedPeers)
|
||||
|
|
|
|||
|
|
@ -510,6 +510,8 @@ public final class ButtonComponent: Component {
|
|||
animateIn = true
|
||||
contentView.isUserInteractionEnabled = false
|
||||
self.addSubview(contentView)
|
||||
|
||||
contentItem.view.parentState = state
|
||||
}
|
||||
let contentFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - contentSize.width) * 0.5), y: floorToScreenPixels((availableSize.height - contentSize.height) * 0.5)), size: contentSize)
|
||||
|
||||
|
|
|
|||
|
|
@ -1456,6 +1456,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||
break
|
||||
case .shareStory:
|
||||
break
|
||||
case .storyFolder:
|
||||
break
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public struct MediaEditorResultPrivacy: Codable, Equatable {
|
|||
case timeout
|
||||
case disableForwarding
|
||||
case archive
|
||||
case folderIds
|
||||
}
|
||||
|
||||
public let sendAsPeerId: EnginePeer.Id?
|
||||
|
|
@ -22,19 +23,22 @@ public struct MediaEditorResultPrivacy: Codable, Equatable {
|
|||
public let timeout: Int
|
||||
public let isForwardingDisabled: Bool
|
||||
public let pin: Bool
|
||||
public let folderIds: [Int64]
|
||||
|
||||
public init(
|
||||
sendAsPeerId: EnginePeer.Id?,
|
||||
privacy: EngineStoryPrivacy,
|
||||
timeout: Int,
|
||||
isForwardingDisabled: Bool,
|
||||
pin: Bool
|
||||
pin: Bool,
|
||||
folderIds: [Int64]
|
||||
) {
|
||||
self.sendAsPeerId = sendAsPeerId
|
||||
self.privacy = privacy
|
||||
self.timeout = timeout
|
||||
self.isForwardingDisabled = isForwardingDisabled
|
||||
self.pin = pin
|
||||
self.folderIds = folderIds
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
|
|
@ -45,6 +49,7 @@ public struct MediaEditorResultPrivacy: Codable, Equatable {
|
|||
self.timeout = Int(try container.decode(Int32.self, forKey: .timeout))
|
||||
self.isForwardingDisabled = try container.decodeIfPresent(Bool.self, forKey: .disableForwarding) ?? false
|
||||
self.pin = try container.decode(Bool.self, forKey: .archive)
|
||||
self.folderIds = try container.decodeIfPresent([Int64].self, forKey: .folderIds) ?? []
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
|
|
@ -55,6 +60,7 @@ public struct MediaEditorResultPrivacy: Codable, Equatable {
|
|||
try container.encode(Int32(self.timeout), forKey: .timeout)
|
||||
try container.encode(self.isForwardingDisabled, forKey: .disableForwarding)
|
||||
try container.encode(self.pin, forKey: .archive)
|
||||
try container.encode(self.folderIds, forKey: .folderIds)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2915,7 +2915,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []),
|
||||
timeout: 86400,
|
||||
isForwardingDisabled: false,
|
||||
pin: true
|
||||
pin: true,
|
||||
folderIds: []
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -6641,7 +6642,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
self.mediaAreas = []
|
||||
self.caption = NSAttributedString()
|
||||
self.coverTimestamp = nil
|
||||
self.options = MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false)
|
||||
self.options = MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false, folderIds: [])
|
||||
self.stickers = []
|
||||
self.randomId = 0
|
||||
}
|
||||
|
|
@ -6651,7 +6652,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
mediaAreas: [MediaArea] = [],
|
||||
caption: NSAttributedString = NSAttributedString(),
|
||||
coverTimestamp: Double? = nil,
|
||||
options: MediaEditorResultPrivacy = MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false),
|
||||
options: MediaEditorResultPrivacy = MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false, folderIds: []),
|
||||
stickers: [TelegramMediaFile] = [],
|
||||
randomId: Int64 = 0
|
||||
) {
|
||||
|
|
@ -6760,7 +6761,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
privacy: initialPrivacy,
|
||||
timeout: 86400,
|
||||
isForwardingDisabled: false,
|
||||
pin: false
|
||||
pin: false,
|
||||
folderIds: []
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -6771,9 +6773,9 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
).start(next: { [weak self] state, peer in
|
||||
if let self, var privacy = state?.privacy {
|
||||
if case let .user(user) = peer, !user.isPremium && privacy.timeout != 86400 {
|
||||
privacy = MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: privacy.privacy, timeout: 86400, isForwardingDisabled: privacy.isForwardingDisabled, pin: privacy.pin)
|
||||
privacy = MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: privacy.privacy, timeout: 86400, isForwardingDisabled: privacy.isForwardingDisabled, pin: privacy.pin, folderIds: privacy.folderIds)
|
||||
} else {
|
||||
privacy = MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: privacy.privacy, timeout: privacy.timeout, isForwardingDisabled: privacy.isForwardingDisabled, pin: privacy.pin)
|
||||
privacy = MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: privacy.privacy, timeout: privacy.timeout, isForwardingDisabled: privacy.isForwardingDisabled, pin: privacy.pin, folderIds: privacy.folderIds)
|
||||
}
|
||||
self.state.privacy = privacy
|
||||
}
|
||||
|
|
@ -6980,7 +6982,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
mentions: mentions,
|
||||
coverImage: coverImage,
|
||||
stateContext: stateContext,
|
||||
completion: { [weak self] sendAsPeerId, privacy, allowScreenshots, pin, _, completed in
|
||||
completion: { [weak self] sendAsPeerId, privacy, allowScreenshots, pin, _, folders, completed in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -6989,13 +6991,14 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
privacy: privacy,
|
||||
timeout: timeout,
|
||||
isForwardingDisabled: !allowScreenshots,
|
||||
pin: pin
|
||||
pin: pin,
|
||||
folderIds: folders
|
||||
)
|
||||
if completed {
|
||||
completion()
|
||||
}
|
||||
},
|
||||
editCategory: { [weak self] privacy, allowScreenshots, pin in
|
||||
editCategory: { [weak self] privacy, allowScreenshots, pin, folders in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -7008,11 +7011,12 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
privacy: privacy,
|
||||
timeout: timeout,
|
||||
isForwardingDisabled: !allowScreenshots,
|
||||
pin: pin
|
||||
pin: pin,
|
||||
folderIds: folders
|
||||
), completion: completion)
|
||||
})
|
||||
},
|
||||
editBlockedPeers: { [weak self] privacy, allowScreenshots, pin in
|
||||
editBlockedPeers: { [weak self] privacy, allowScreenshots, pin, folders in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -7025,7 +7029,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
privacy: privacy,
|
||||
timeout: timeout,
|
||||
isForwardingDisabled: !allowScreenshots,
|
||||
pin: pin
|
||||
pin: pin,
|
||||
folderIds: folders
|
||||
), completion: completion)
|
||||
})
|
||||
},
|
||||
|
|
@ -7078,7 +7083,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
allowScreenshots: !isForwardingDisabled,
|
||||
pin: pin,
|
||||
stateContext: stateContext,
|
||||
completion: { [weak self] _, result, isForwardingDisabled, pin, peers, completed in
|
||||
completion: { [weak self] _, result, isForwardingDisabled, pin, peers, folders, completed in
|
||||
guard let self, completed else {
|
||||
return
|
||||
}
|
||||
|
|
@ -7093,8 +7098,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
completion(result)
|
||||
}
|
||||
},
|
||||
editCategory: { _, _, _ in },
|
||||
editBlockedPeers: { _, _, _ in }
|
||||
editCategory: { _, _, _, _ in },
|
||||
editBlockedPeers: { _, _, _, _ in }
|
||||
)
|
||||
controller.customModalStyleOverlayTransitionFactorUpdated = { [weak self, weak controller] transition in
|
||||
if let self, let controller {
|
||||
|
|
@ -7175,7 +7180,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
privacy: self.state.privacy.privacy,
|
||||
timeout: timeout ?? 86400,
|
||||
isForwardingDisabled: self.state.privacy.isForwardingDisabled,
|
||||
pin: self.state.privacy.pin
|
||||
pin: self.state.privacy.pin,
|
||||
folderIds: self.state.privacy.folderIds
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -7672,7 +7678,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
mediaAreas: [],
|
||||
caption: NSAttributedString(),
|
||||
coverTimestamp: nil,
|
||||
options: MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false),
|
||||
options: MediaEditorResultPrivacy(sendAsPeerId: nil, privacy: EngineStoryPrivacy(base: .everyone, additionallyIncludePeers: []), timeout: 0, isForwardingDisabled: false, pin: false, folderIds: []),
|
||||
stickers: [],
|
||||
randomId: 0
|
||||
)], { [weak self] finished in
|
||||
|
|
|
|||
|
|
@ -152,8 +152,17 @@ public final class PeerInfoRatingComponent: Component {
|
|||
let baseHeight: CGFloat = 20.0
|
||||
let innerInset: CGFloat = 2.0
|
||||
|
||||
let compactLabelSize = self.compactLabel.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.compactLabel, font: Font.medium(11.0), textColor: .black))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
|
||||
let expandedSize = CGSize(width: 174.0, height: baseHeight)
|
||||
let collapsedSize = CGSize(width: baseHeight, height: baseHeight)
|
||||
let collapsedSize = CGSize(width: max(baseHeight, compactLabelSize.width + 6.0 * 2.0), height: baseHeight)
|
||||
|
||||
if self.backgroundView.image == nil {
|
||||
self.backgroundView.image = generateStretchableFilledCircleImage(diameter: baseHeight, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
|
|
@ -189,19 +198,11 @@ public final class PeerInfoRatingComponent: Component {
|
|||
self.foregroundClippedView.backgroundColor = component.foregroundColor
|
||||
transition.setFrame(view: self.foregroundClippedShapeView, frame: foregroundFrame)
|
||||
|
||||
let compactLabelSize = self.compactLabel.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.compactLabel, font: Font.medium(11.0), textColor: .black))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
if let compactLabelView = self.compactLabel.view {
|
||||
if compactLabelView.superview == nil {
|
||||
self.foregroundMaskView.addSubview(compactLabelView)
|
||||
}
|
||||
compactLabelView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((baseHeight - innerInset * 2.0 - compactLabelSize.width) * 0.5), y: floorToScreenPixels((baseHeight - innerInset * 2.0 - compactLabelSize.height) * 0.5) + UIScreenPixel), size: compactLabelSize)
|
||||
compactLabelView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((collapsedSize.width - innerInset * 2.0 - compactLabelSize.width) * 0.5), y: floorToScreenPixels((baseHeight - innerInset * 2.0 - compactLabelSize.height) * 0.5) + UIScreenPixel), size: compactLabelSize)
|
||||
alphaTransition.setAlpha(view: compactLabelView, alpha: component.isExpanded ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
|
|
@ -284,50 +285,57 @@ public final class PeerInfoRatingComponent: Component {
|
|||
})
|
||||
}
|
||||
|
||||
let tooltipController: TooltipScreen
|
||||
if let current = self.tooltipController {
|
||||
tooltipController = current
|
||||
if !component.tooltipLabel.isEmpty {
|
||||
let tooltipController: TooltipScreen
|
||||
if let current = self.tooltipController {
|
||||
tooltipController = current
|
||||
} else {
|
||||
tooltipController = TooltipScreen(
|
||||
context: component.context,
|
||||
account: component.context.account,
|
||||
sharedContext: component.context.sharedContext,
|
||||
text: .attributedString(text: NSAttributedString(string: component.tooltipLabel, font: Font.semibold(11.0), textColor: .white)),
|
||||
style: .customBlur(component.tooltipBackgroundColor, -4.0),
|
||||
arrowStyle: .small,
|
||||
location: .point(CGRect(origin: CGPoint(x: 100.0, y: 100.0), size: CGSize()), .bottom),
|
||||
displayDuration: .infinite,
|
||||
isShimmering: true,
|
||||
cornerRadius: 10.0,
|
||||
shouldDismissOnTouch: { _, _ in
|
||||
return .ignore
|
||||
}
|
||||
)
|
||||
self.tooltipController = tooltipController
|
||||
|
||||
tooltipController.containerLayoutUpdated(ContainerViewLayout(
|
||||
size: CGSize(width: 200.0, height: 200.0),
|
||||
metrics: LayoutMetrics(),
|
||||
deviceMetrics: DeviceMetrics.iPhoneXSMax,
|
||||
intrinsicInsets: UIEdgeInsets(),
|
||||
safeInsets: UIEdgeInsets(),
|
||||
additionalInsets: UIEdgeInsets(),
|
||||
statusBarHeight: nil,
|
||||
inputHeight: nil,
|
||||
inputHeightIsInteractivellyChanging: false,
|
||||
inVoiceOver: false
|
||||
), transition: .immediate)
|
||||
|
||||
self.layer.addSublayer(tooltipController.view.layer)
|
||||
tooltipController.viewWillAppear(false)
|
||||
tooltipController.viewDidAppear(false)
|
||||
tooltipController.setIgnoreAppearanceMethodInvocations(true)
|
||||
tooltipController.view.isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
transition.setFrame(view: tooltipController.view, frame: CGRect(origin: CGPoint(), size: CGSize(width: 200.0, height: 200.0)).offsetBy(dx: -200.0 * 0.5 + foregroundFrame.width + 2.0, dy: -200.0 * 0.5))
|
||||
alphaTransition.setAlpha(view: tooltipController.view, alpha: component.isExpanded ? 1.0 : 0.0)
|
||||
} else {
|
||||
tooltipController = TooltipScreen(
|
||||
context: component.context,
|
||||
account: component.context.account,
|
||||
sharedContext: component.context.sharedContext,
|
||||
text: .attributedString(text: NSAttributedString(string: component.tooltipLabel, font: Font.semibold(11.0), textColor: .white)),
|
||||
style: .customBlur(component.tooltipBackgroundColor, -4.0),
|
||||
arrowStyle: .small,
|
||||
location: .point(CGRect(origin: CGPoint(x: 100.0, y: 100.0), size: CGSize()), .bottom),
|
||||
displayDuration: .infinite,
|
||||
isShimmering: true,
|
||||
cornerRadius: 10.0,
|
||||
shouldDismissOnTouch: { _, _ in
|
||||
return .ignore
|
||||
}
|
||||
)
|
||||
self.tooltipController = tooltipController
|
||||
|
||||
tooltipController.containerLayoutUpdated(ContainerViewLayout(
|
||||
size: CGSize(width: 200.0, height: 200.0),
|
||||
metrics: LayoutMetrics(),
|
||||
deviceMetrics: DeviceMetrics.iPhoneXSMax,
|
||||
intrinsicInsets: UIEdgeInsets(),
|
||||
safeInsets: UIEdgeInsets(),
|
||||
additionalInsets: UIEdgeInsets(),
|
||||
statusBarHeight: nil,
|
||||
inputHeight: nil,
|
||||
inputHeightIsInteractivellyChanging: false,
|
||||
inVoiceOver: false
|
||||
), transition: .immediate)
|
||||
|
||||
self.layer.addSublayer(tooltipController.view.layer)
|
||||
tooltipController.viewWillAppear(false)
|
||||
tooltipController.viewDidAppear(false)
|
||||
tooltipController.setIgnoreAppearanceMethodInvocations(true)
|
||||
tooltipController.view.isUserInteractionEnabled = false
|
||||
if let tooltipController = self.tooltipController {
|
||||
self.tooltipController = nil
|
||||
tooltipController.view.layer.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
|
||||
transition.setFrame(view: tooltipController.view, frame: CGRect(origin: CGPoint(), size: CGSize(width: 200.0, height: 200.0)).offsetBy(dx: -200.0 * 0.5 + foregroundFrame.width + 2.0, dy: -200.0 * 0.5))
|
||||
alphaTransition.setAlpha(view: tooltipController.view, alpha: component.isExpanded ? 1.0 : 0.0)
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1917,7 +1917,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||
let apparentBackgroundHeight = (1.0 - transitionFraction) * backgroundHeight + transitionFraction * transitionSourceHeight
|
||||
|
||||
var subtitleRatingSize: CGSize?
|
||||
if !"".isEmpty, let cachedData = cachedData as? CachedUserData, let starRating = cachedData.starRating {
|
||||
if let cachedData = cachedData as? CachedUserData, let starRating = cachedData.starRating {
|
||||
let subtitleRating: ComponentView<Empty>
|
||||
var subtitleRatingTransition = ComponentTransition(transition)
|
||||
if let current = self.subtitleRating {
|
||||
|
|
@ -1978,7 +1978,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||
|
||||
if self.subtitleRatingIsExpanded, let controller = self.controller, let presentationData = self.presentationData, !self.didDisplayRatingTooltip {
|
||||
self.didDisplayRatingTooltip = true
|
||||
controller.presentInGlobalOverlay(UndoOverlayController(
|
||||
controller.present(UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .info(
|
||||
title: nil,
|
||||
|
|
@ -1987,10 +1987,21 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||
customUndoText: "Learn More"
|
||||
),
|
||||
position: .top,
|
||||
action: { _ in
|
||||
action: { [weak self] action in
|
||||
guard let self else {
|
||||
return true
|
||||
}
|
||||
|
||||
if case .undo = action {
|
||||
var infoUrl = "https://telegram.org/blog/telegram-stars"
|
||||
if let data = self.context.currentAppConfiguration.with({ $0 }).data, let value = data["stars_rating_learnmore_url"] as? String {
|
||||
infoUrl = value
|
||||
}
|
||||
self.context.sharedContext.applicationBindings.openUrl(infoUrl)
|
||||
}
|
||||
return true
|
||||
}
|
||||
))
|
||||
), in: .current)
|
||||
}
|
||||
}
|
||||
)),
|
||||
|
|
|
|||
|
|
@ -527,6 +527,7 @@ private final class PeerInfoPendingPane {
|
|||
chatLocation: ChatLocation,
|
||||
chatLocationContextHolder: Atomic<ChatLocationContextHolder?>,
|
||||
sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?,
|
||||
initialStoryFolderId: Int64?,
|
||||
key: PeerInfoPaneKey,
|
||||
hasBecomeReady: @escaping (PeerInfoPaneKey) -> Void,
|
||||
parentController: ViewController?,
|
||||
|
|
@ -609,7 +610,7 @@ private final class PeerInfoPendingPane {
|
|||
listContext = data.storyListContext
|
||||
}
|
||||
|
||||
let visualPaneNode = PeerInfoStoryPaneNode(context: context, scope: scope, captureProtected: captureProtected, isProfileEmbedded: true, canManageStories: canManage, navigationController: chatControllerInteraction.navigationController, listContext: listContext)
|
||||
let visualPaneNode = PeerInfoStoryPaneNode(context: context, scope: scope, captureProtected: captureProtected, isProfileEmbedded: true, canManageStories: canManage, navigationController: chatControllerInteraction.navigationController, listContext: listContext, initialStoryFolderId: initialStoryFolderId)
|
||||
paneNode = visualPaneNode
|
||||
visualPaneNode.openCurrentDate = {
|
||||
openMediaCalendar()
|
||||
|
|
@ -728,6 +729,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
private var currentPanes: [PeerInfoPaneKey: PeerInfoPaneWrapper] = [:]
|
||||
private var pendingPanes: [PeerInfoPaneKey: PeerInfoPendingPane] = [:]
|
||||
private var shouldFadeIn = false
|
||||
private var initialStoryFolderId: Int64?
|
||||
|
||||
private var transitionFraction: CGFloat = 0.0
|
||||
|
||||
|
|
@ -753,7 +755,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
|
||||
private let initialPaneKey: PeerInfoPaneKey?
|
||||
|
||||
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, chatLocation: ChatLocation, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, isMediaOnly: Bool, initialPaneKey: PeerInfoPaneKey?) {
|
||||
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, chatLocation: ChatLocation, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, isMediaOnly: Bool, initialPaneKey: PeerInfoPaneKey?, initialStoryFolderId: Int64?) {
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.peerId = peerId
|
||||
|
|
@ -762,6 +764,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
self.sharedMediaFromForumTopic = sharedMediaFromForumTopic
|
||||
self.isMediaOnly = isMediaOnly
|
||||
self.initialPaneKey = initialPaneKey
|
||||
self.initialStoryFolderId = initialStoryFolderId
|
||||
|
||||
self.additionalBackgroundNode = ASDisplayNode()
|
||||
|
||||
|
|
@ -1088,7 +1091,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
}
|
||||
}
|
||||
}
|
||||
if let pendingSwitchToPaneKey = self.pendingSwitchToPaneKey {
|
||||
if let pendingSwitchToPaneKey = self.pendingSwitchToPaneKey, availablePanes.contains(pendingSwitchToPaneKey) {
|
||||
if self.currentPanes[pendingSwitchToPaneKey] == nil && self.pendingPanes[pendingSwitchToPaneKey] == nil {
|
||||
if !requiredPendingKeys.contains(pendingSwitchToPaneKey) {
|
||||
requiredPendingKeys.append(pendingSwitchToPaneKey)
|
||||
|
|
@ -1099,6 +1102,13 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
for key in requiredPendingKeys {
|
||||
if self.pendingPanes[key] == nil, let data {
|
||||
var leftScope = false
|
||||
var initialStoryFolderId: Int64?
|
||||
if case .stories = key {
|
||||
if let initialStoryFolderIdValue = self.initialStoryFolderId {
|
||||
self.initialStoryFolderId = nil
|
||||
initialStoryFolderId = initialStoryFolderIdValue
|
||||
}
|
||||
}
|
||||
let pane = PeerInfoPendingPane(
|
||||
context: self.context,
|
||||
updatedPresentationData: self.updatedPresentationData,
|
||||
|
|
@ -1117,6 +1127,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
chatLocation: self.chatLocation,
|
||||
chatLocationContextHolder: self.chatLocationContextHolder,
|
||||
sharedMediaFromForumTopic: self.sharedMediaFromForumTopic,
|
||||
initialStoryFolderId: initialStoryFolderId,
|
||||
key: key,
|
||||
hasBecomeReady: { [weak self] key in
|
||||
let apply: () -> Void = {
|
||||
|
|
@ -1352,7 +1363,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
|
||||
var removeKeys: [PeerInfoPaneKey] = []
|
||||
for (key, paneNode) in self.pendingPanes {
|
||||
if !availablePanes.contains(key) {
|
||||
if !availablePanes.contains(key) && self.pendingSwitchToPaneKey != key {
|
||||
removeKeys.append(key)
|
||||
paneNode.pane.node.removeFromSupernode()
|
||||
}
|
||||
|
|
@ -1363,7 +1374,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
removeKeys.removeAll()
|
||||
|
||||
for (key, paneNode) in self.currentPanes {
|
||||
if !availablePanes.contains(key) {
|
||||
if !availablePanes.contains(key) && self.pendingSwitchToPaneKey != key {
|
||||
removeKeys.append(key)
|
||||
paneNode.node.removeFromSupernode()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2907,6 +2907,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
private let callMessages: [Message]
|
||||
private let chatLocation: ChatLocation
|
||||
private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
|
||||
private let switchToStoryFolder: Int64?
|
||||
private let sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?
|
||||
|
||||
let isSettings: Bool
|
||||
|
|
@ -3031,7 +3032,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
}
|
||||
private var didSetReady = false
|
||||
|
||||
init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, isMyProfile: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, profileGiftsContext: ProfileGiftsContext?, starsContext: StarsContext?, tonContext: StarsContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, initialPaneKey: PeerInfoPaneKey?, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?) {
|
||||
init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, isMyProfile: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, profileGiftsContext: ProfileGiftsContext?, starsContext: StarsContext?, tonContext: StarsContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, switchToStoryFolder: Int64?, initialPaneKey: PeerInfoPaneKey?, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
|
|
@ -3047,6 +3048,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
self.chatLocationContextHolder = chatLocationContextHolder
|
||||
self.isMediaOnly = context.account.peerId == peerId && !isSettings && !isMyProfile
|
||||
self.initialExpandPanes = initialPaneKey != nil
|
||||
self.switchToStoryFolder = switchToStoryFolder
|
||||
self.sharedMediaFromForumTopic = sharedMediaFromForumTopic
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
|
|
@ -3058,7 +3060,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
forumTopicThreadId = message.threadId
|
||||
}
|
||||
self.headerNode = PeerInfoHeaderNode(context: context, controller: controller, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isMediaOnly: self.isMediaOnly, isSettings: isSettings, isMyProfile: isMyProfile, forumTopicThreadId: forumTopicThreadId, chatLocation: self.chatLocation)
|
||||
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, sharedMediaFromForumTopic: sharedMediaFromForumTopic, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly, initialPaneKey: initialPaneKey)
|
||||
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, sharedMediaFromForumTopic: sharedMediaFromForumTopic, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly, initialPaneKey: initialPaneKey, initialStoryFolderId: switchToStoryFolder)
|
||||
|
||||
super.init()
|
||||
|
||||
|
|
@ -5393,7 +5395,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .spring)
|
||||
|
||||
self.headerNode.updateIsAvatarExpanded(false, transition: transition)
|
||||
self.updateNavigationExpansionPresentation(isExpanded: false, animated: true)
|
||||
self.updateNavigationExpansionPresentation(isExpanded: false, animated: animated)
|
||||
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: transition, additive: true)
|
||||
|
|
@ -5404,7 +5406,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
let contentOffset = self.scrollNode.view.contentOffset
|
||||
let paneAreaExpansionFinalPoint: CGFloat = self.paneContainerNode.frame.minY - navigationHeight
|
||||
if contentOffset.y < paneAreaExpansionFinalPoint - CGFloat.ulpOfOne {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
|
||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .spring) : .immediate
|
||||
|
||||
self.ignoreScrolling = true
|
||||
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: paneAreaExpansionFinalPoint), size: self.scrollNode.bounds.size))
|
||||
|
|
@ -11712,7 +11714,23 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
}
|
||||
})))
|
||||
|
||||
/*if pane.canReorder() {
|
||||
if let _ = pane.currentStoryFolder {
|
||||
items.append(.action(ContextMenuActionItem(text: "Share", icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak pane] _, a in
|
||||
if ignoreNextActions {
|
||||
return
|
||||
}
|
||||
ignoreNextActions = true
|
||||
a(.default)
|
||||
|
||||
if let pane {
|
||||
pane.shareCurrentFolder()
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
if pane.canReorder() {
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.BotPreviews_MenuReorder, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak pane] _, a in
|
||||
|
|
@ -11744,7 +11762,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
pane.presentDeleteCurrentStoryFolder()
|
||||
}
|
||||
})))
|
||||
}*/
|
||||
}
|
||||
|
||||
if let language = pane.currentBotPreviewLanguage {
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.BotPreviews_MenuDeleteLanguage(language.name).string, textColor: .destructive, icon: { theme in
|
||||
|
|
@ -13084,6 +13102,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
private let switchToRecommendedChannels: Bool
|
||||
private let switchToGifts: Bool
|
||||
private let switchToGroupsInCommon: Bool
|
||||
private let switchToStoryFolder: Int64?
|
||||
private let sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?
|
||||
let chatLocation: ChatLocation
|
||||
private let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
|
||||
|
|
@ -13159,7 +13178,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
sharedMediaFromForumTopic: (EnginePeer.Id, Int64)? = nil,
|
||||
switchToRecommendedChannels: Bool = false,
|
||||
switchToGifts: Bool = false,
|
||||
switchToGroupsInCommon: Bool = false
|
||||
switchToGroupsInCommon: Bool = false,
|
||||
switchToStoryFolder: Int64? = nil
|
||||
) {
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
|
|
@ -13177,6 +13197,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
self.switchToRecommendedChannels = switchToRecommendedChannels
|
||||
self.switchToGifts = switchToGifts
|
||||
self.switchToGroupsInCommon = switchToGroupsInCommon
|
||||
self.switchToStoryFolder = switchToStoryFolder
|
||||
self.sharedMediaFromForumTopic = sharedMediaFromForumTopic
|
||||
|
||||
if let forumTopicThread = forumTopicThread {
|
||||
|
|
@ -13542,8 +13563,10 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
initialPaneKey = .gifts
|
||||
} else if self.switchToGroupsInCommon {
|
||||
initialPaneKey = .groupsInCommon
|
||||
} else if self.switchToStoryFolder != nil {
|
||||
initialPaneKey = .stories
|
||||
}
|
||||
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, isMyProfile: self.isMyProfile, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, profileGiftsContext: self.profileGiftsContext, starsContext: self.starsContext, tonContext: self.tonContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, initialPaneKey: initialPaneKey, sharedMediaFromForumTopic: self.sharedMediaFromForumTopic)
|
||||
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, isMyProfile: self.isMyProfile, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, profileGiftsContext: self.profileGiftsContext, starsContext: self.starsContext, tonContext: self.tonContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, switchToStoryFolder: self.switchToStoryFolder, initialPaneKey: initialPaneKey, sharedMediaFromForumTopic: self.sharedMediaFromForumTopic)
|
||||
self.controllerNode.accountsAndPeers.set(self.accountsAndPeers.get() |> map { $0.1 })
|
||||
self.controllerNode.activeSessionsContextAndCount.set(self.activeSessionsContextAndCount.get())
|
||||
self.cachedDataPromise.set(self.controllerNode.cachedDataPromise.get())
|
||||
|
|
|
|||
|
|
@ -488,7 +488,8 @@ final class PeerInfoStoryGridScreenComponent: Component {
|
|||
}
|
||||
return self.environment?.controller()?.navigationController as? NavigationController
|
||||
},
|
||||
listContext: nil
|
||||
listContext: nil,
|
||||
initialStoryFolderId: nil
|
||||
)
|
||||
paneNode.isEmptyUpdated = { [weak self] _ in
|
||||
guard let self else {
|
||||
|
|
|
|||
|
|
@ -117,7 +117,8 @@ final class StorySearchGridScreenComponent: Component {
|
|||
}
|
||||
return self.environment?.controller()?.navigationController as? NavigationController
|
||||
},
|
||||
listContext: component.listContext
|
||||
listContext: component.listContext,
|
||||
initialStoryFolderId: nil
|
||||
)
|
||||
paneNode.parentController = environment.controller()
|
||||
paneNode.isEmptyUpdated = { [weak self] _ in
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -44,9 +44,9 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
let categoryItems: [CategoryItem]
|
||||
let optionItems: [OptionItem]
|
||||
let coverItem: CoverItem?
|
||||
let completion: (EnginePeer.Id?, EngineStoryPrivacy, Bool, Bool, [EnginePeer], Bool) -> Void
|
||||
let editCategory: (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||
let editBlockedPeers: (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||
let completion: (EnginePeer.Id?, EngineStoryPrivacy, Bool, Bool, [EnginePeer], [Int64], Bool) -> Void
|
||||
let editCategory: (EngineStoryPrivacy, Bool, Bool, [Int64]) -> Void
|
||||
let editBlockedPeers: (EngineStoryPrivacy, Bool, Bool, [Int64]) -> Void
|
||||
let editCover: () -> Void
|
||||
let peerCompletion: (EnginePeer.Id) -> Void
|
||||
|
||||
|
|
@ -62,9 +62,9 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
categoryItems: [CategoryItem],
|
||||
optionItems: [OptionItem],
|
||||
coverItem: CoverItem?,
|
||||
completion: @escaping (EnginePeer.Id?, EngineStoryPrivacy, Bool, Bool, [EnginePeer], Bool) -> Void,
|
||||
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void,
|
||||
editBlockedPeers: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void,
|
||||
completion: @escaping (EnginePeer.Id?, EngineStoryPrivacy, Bool, Bool, [EnginePeer], [Int64], Bool) -> Void,
|
||||
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool, [Int64]) -> Void,
|
||||
editBlockedPeers: @escaping (EngineStoryPrivacy, Bool, Bool, [Int64]) -> Void,
|
||||
editCover: @escaping () -> Void,
|
||||
peerCompletion: @escaping (EnginePeer.Id) -> Void
|
||||
) {
|
||||
|
|
@ -833,9 +833,9 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
context: component.context,
|
||||
initialPrivacy: EngineStoryPrivacy(base: .nobody, additionallyIncludePeers: []),
|
||||
stateContext: stateContext,
|
||||
completion: { _, _, _, _, _, _ in },
|
||||
editCategory: { _, _, _ in },
|
||||
editBlockedPeers: { _, _, _ in },
|
||||
completion: { _, _, _, _, _, _, _ in },
|
||||
editCategory: { _, _, _, _ in },
|
||||
editBlockedPeers: { _, _, _, _ in },
|
||||
peerCompletion: { [weak self] peerId in
|
||||
guard let self else {
|
||||
return
|
||||
|
|
@ -896,7 +896,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "New Album", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Folder"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { [weak self] c, f in
|
||||
items.append(.action(ContextMenuActionItem(text: "New Album", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddFolder"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { [weak self] c, f in
|
||||
guard let self else {
|
||||
f(.default)
|
||||
return
|
||||
|
|
@ -1005,12 +1005,17 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
return
|
||||
}
|
||||
if let value, !value.isEmpty {
|
||||
let id = PeerStoryListContext.addFolderExternal(account: component.context.account, peerId: component.context.account.peerId, title: value)
|
||||
self.shareToFolders.append(StoryListContext.State.Folder(
|
||||
id: id,
|
||||
title: value
|
||||
))
|
||||
self.state?.updated(transition: .immediate)
|
||||
let _ = PeerStoryListContext.addFolderExternal(account: component.context.account, peerId: component.context.account.peerId, title: value, completion: { [weak self] id in
|
||||
guard let self, let id else {
|
||||
return
|
||||
}
|
||||
|
||||
self.shareToFolders.append(StoryListContext.State.Folder(
|
||||
id: id,
|
||||
title: value
|
||||
))
|
||||
self.state?.updated(transition: .immediate)
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -1416,7 +1421,8 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
component.editCategory(
|
||||
EngineStoryPrivacy(base: .nobody, additionallyIncludePeers: []),
|
||||
self.selectedOptions.contains(.screenshot),
|
||||
self.selectedOptions.contains(.pin)
|
||||
self.selectedOptions.contains(.pin),
|
||||
self.shareToFolders.map(\.id)
|
||||
)
|
||||
controller.dismissAllTooltips()
|
||||
controller.dismiss()
|
||||
|
|
@ -1424,7 +1430,8 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
component.editCategory(
|
||||
EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []),
|
||||
self.selectedOptions.contains(.screenshot),
|
||||
self.selectedOptions.contains(.pin)
|
||||
self.selectedOptions.contains(.pin),
|
||||
self.shareToFolders.map(\.id)
|
||||
)
|
||||
controller.dismissAllTooltips()
|
||||
controller.dismiss()
|
||||
|
|
@ -1452,7 +1459,8 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
component.editCategory(
|
||||
EngineStoryPrivacy(base: base, additionallyIncludePeers: selectedPeers),
|
||||
self.selectedOptions.contains(.screenshot),
|
||||
self.selectedOptions.contains(.pin)
|
||||
self.selectedOptions.contains(.pin),
|
||||
self.shareToFolders.map(\.id)
|
||||
)
|
||||
controller.dismissAllTooltips()
|
||||
controller.dismiss()
|
||||
|
|
@ -1540,7 +1548,8 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
component.editBlockedPeers(
|
||||
EngineStoryPrivacy(base: base, additionallyIncludePeers: self.selectedPeers),
|
||||
self.selectedOptions.contains(.screenshot),
|
||||
self.selectedOptions.contains(.pin)
|
||||
self.selectedOptions.contains(.pin),
|
||||
self.shareToFolders.map(\.id)
|
||||
)
|
||||
controller.dismissAllTooltips()
|
||||
controller.dismiss()
|
||||
|
|
@ -2262,6 +2271,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
self.selectedOptions.contains(.screenshot),
|
||||
self.selectedOptions.contains(.pin),
|
||||
self.component?.stateContext.stateValue?.peers.filter { self.selectedPeers.contains($0.id) } ?? [],
|
||||
self.shareToFolders.map(\.id),
|
||||
false
|
||||
)
|
||||
controller.requestDismiss()
|
||||
|
|
@ -3014,6 +3024,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
self.selectedOptions.contains(.screenshot),
|
||||
self.selectedOptions.contains(.pin),
|
||||
peers.values.compactMap { $0 },
|
||||
self.shareToFolders.map(\.id),
|
||||
true
|
||||
)
|
||||
|
||||
|
|
@ -3265,9 +3276,9 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
|||
mentions: [String] = [],
|
||||
coverImage: UIImage? = nil,
|
||||
stateContext: StateContext,
|
||||
completion: @escaping (EnginePeer.Id?, EngineStoryPrivacy, Bool, Bool, [EnginePeer], Bool) -> Void,
|
||||
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void = { _, _, _ in },
|
||||
editBlockedPeers: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void = { _, _, _ in },
|
||||
completion: @escaping (EnginePeer.Id?, EngineStoryPrivacy, Bool, Bool, [EnginePeer], [Int64], Bool) -> Void,
|
||||
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool, [Int64]) -> Void = { _, _, _, _ in },
|
||||
editBlockedPeers: @escaping (EngineStoryPrivacy, Bool, Bool, [Int64]) -> Void = { _, _, _, _ in },
|
||||
editCover: @escaping () -> Void = { },
|
||||
peerCompletion: @escaping (EnginePeer.Id) -> Void = { _ in }
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -318,7 +318,8 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: forwardInfo,
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
}
|
||||
var totalCount = peerStoryItemsView.items.count
|
||||
|
|
@ -355,7 +356,8 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||
isMy: true,
|
||||
myReaction: nil,
|
||||
forwardInfo: pendingForwardsInfo[item.randomId],
|
||||
author: nil
|
||||
author: nil,
|
||||
folderIds: item.folders
|
||||
))
|
||||
totalCount += 1
|
||||
}
|
||||
|
|
@ -1354,7 +1356,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||
isMy: itemValue.isMy,
|
||||
myReaction: itemValue.myReaction,
|
||||
forwardInfo: forwardInfo,
|
||||
author: itemValue.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) }
|
||||
author: itemValue.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) },
|
||||
folderIds: itemValue.folderIds
|
||||
)
|
||||
|
||||
let mainItem = StoryContentItem(
|
||||
|
|
@ -2292,7 +2295,8 @@ private func getCachedStory(storyId: StoryId, transaction: Transaction) -> Engin
|
|||
isMy: item.isMy,
|
||||
myReaction: item.myReaction,
|
||||
forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) },
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) }
|
||||
author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) },
|
||||
folderIds: item.folderIds
|
||||
)
|
||||
} else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -1695,13 +1695,13 @@ private final class StoryContainerScreenComponent: Component {
|
|||
|
||||
if let content = component.content as? PeerStoryListContentContextImpl {
|
||||
if let listSource = content.listContext as? PeerStoryListContext {
|
||||
listSource.addFolder(title: title, completion: { [weak listSource] id in
|
||||
let _ = listSource.addFolder(title: title, items: [], completion: { [weak listSource] id in
|
||||
Queue.mainQueue().async {
|
||||
guard let listSource else {
|
||||
guard let listSource, let id else {
|
||||
return
|
||||
}
|
||||
if !items.isEmpty {
|
||||
listSource.addToFolder(id: id, items: items)
|
||||
let _ = listSource.addToFolder(id: id, items: items)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -1717,7 +1717,7 @@ private final class StoryContainerScreenComponent: Component {
|
|||
}
|
||||
if let content = component.content as? PeerStoryListContentContextImpl {
|
||||
if let listSource = content.listContext as? PeerStoryListContext {
|
||||
listSource.addToFolder(id: folderId, items: [slice.item.storyItem])
|
||||
let _ = listSource.addToFolder(id: folderId, items: [slice.item.storyItem])
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5090,7 +5090,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
context: context,
|
||||
initialPrivacy: privacy,
|
||||
stateContext: stateContext,
|
||||
completion: { [weak self] _, privacy, _, _, _, completed in
|
||||
completion: { [weak self] _, privacy, _, _, _, _, completed in
|
||||
guard let self, let component = self.component, completed else {
|
||||
return
|
||||
}
|
||||
|
|
@ -5107,7 +5107,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
self.rewindCurrentItem()
|
||||
self.updateIsProgressPaused()
|
||||
},
|
||||
editCategory: { [weak self] privacy, _, _ in
|
||||
editCategory: { [weak self] privacy, _, _, _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -5118,7 +5118,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
self.openItemPrivacySettings(updatedPrivacy: privacy)
|
||||
})
|
||||
},
|
||||
editBlockedPeers: { [weak self] privacy, _, _ in
|
||||
editBlockedPeers: { [weak self] privacy, _, _, _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -5171,7 +5171,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
context: context,
|
||||
initialPrivacy: privacy,
|
||||
stateContext: stateContext,
|
||||
completion: { [weak self] _, result, _, _, peers, completed in
|
||||
completion: { [weak self] _, result, _, _, peers, _, completed in
|
||||
guard completed else {
|
||||
return
|
||||
}
|
||||
|
|
@ -5188,8 +5188,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
completion(result)
|
||||
}
|
||||
},
|
||||
editCategory: { _, _, _ in },
|
||||
editBlockedPeers: { _, _, _ in }
|
||||
editCategory: { _, _, _, _ in },
|
||||
editBlockedPeers: { _, _, _, _ in }
|
||||
)
|
||||
controller.dismissed = { [weak self] in
|
||||
if let self {
|
||||
|
|
@ -6110,7 +6110,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
var items: [ContextMenuItem] = []
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Add to Album", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Folder"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
|
||||
items.append(.action(ContextMenuActionItem(text: "Add to Album", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddToFolder"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
|
||||
guard let self, let c else {
|
||||
f(.default)
|
||||
return
|
||||
|
|
@ -6131,7 +6131,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
})))
|
||||
items.append(.separator)
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "New Album", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Folder"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { [weak self] c, f in
|
||||
items.append(.action(ContextMenuActionItem(text: "New Album", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddFolder"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { [weak self] c, f in
|
||||
guard let self else {
|
||||
f(.default)
|
||||
return
|
||||
|
|
|
|||
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AddFolder.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AddFolder.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "addfolder_24.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AddFolder.imageset/addfolder_24.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AddFolder.imageset/addfolder_24.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AddToFolder.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AddToFolder.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "folder1_24.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AddToFolder.imageset/folder1_24.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AddToFolder.imageset/folder1_24.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -115,7 +115,7 @@ extension ChatControllerImpl {
|
|||
let textItems: [AnimatedTextComponent.Item] = [
|
||||
AnimatedTextComponent.Item(id: 0, content: .text(text))
|
||||
]
|
||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .starsSent(context: self.context, title: title, text: textItems), elevatedLayout: false, position: .top, action: { [weak self] action in
|
||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .starsSent(context: self.context, title: title, text: textItems, hasUndo: true), elevatedLayout: false, position: .top, action: { [weak self] action in
|
||||
guard let self else {
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -535,9 +535,9 @@ extension ChatControllerImpl {
|
|||
|
||||
self.currentSendStarsUndoMessageId = messageId
|
||||
if let current = self.currentSendStarsUndoController {
|
||||
current.content = .starsSent(context: self.context, title: title, text: textItems)
|
||||
current.content = .starsSent(context: self.context, title: title, text: textItems, hasUndo: true)
|
||||
} else {
|
||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .starsSent(context: self.context, title: title, text: textItems), elevatedLayout: false, position: .top, action: { [weak self] action in
|
||||
let controller = UndoOverlayController(presentationData: self.presentationData, content: .starsSent(context: self.context, title: title, text: textItems, hasUndo: true), elevatedLayout: false, position: .top, action: { [weak self] action in
|
||||
guard let self else {
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1498,5 +1498,26 @@ func openResolvedUrlImpl(
|
|||
} else {
|
||||
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.BusinessLink_ErrorExpired, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
}
|
||||
case let .storyFolder(peerId, id):
|
||||
Task { @MainActor [weak navigationController] in
|
||||
guard let peer = await context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
|
||||
).get() else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let controller = context.sharedContext.makePeerInfoController(
|
||||
context: context,
|
||||
updatedPresentationData: updatedPresentationData,
|
||||
peer: peer._asPeer(),
|
||||
mode: .storyAlbum(id: id),
|
||||
avatarInitiallyExpanded: false,
|
||||
fromChat: false,
|
||||
requestsContext: nil
|
||||
) else {
|
||||
return
|
||||
}
|
||||
navigationController?.pushViewController(controller)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3964,6 +3964,7 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
|
|||
var isMyProfile = false
|
||||
var switchToGifts = false
|
||||
var switchToGroupsInCommon = false
|
||||
var switchToStoryFolder: Int64?
|
||||
|
||||
switch mode {
|
||||
case let .nearbyPeer(distance):
|
||||
|
|
@ -3989,10 +3990,12 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
|
|||
switchToGroupsInCommon = true
|
||||
case let .monoforum(peerId):
|
||||
sharedMediaFromForumTopic = (peerId, peer.id.toInt64())
|
||||
case let .storyAlbum(id):
|
||||
switchToStoryFolder = id
|
||||
default:
|
||||
break
|
||||
}
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, reactionSourceMessageId: reactionSourceMessageId, callMessages: callMessages, isMyProfile: isMyProfile, hintGroupInCommon: hintGroupInCommon, forumTopicThread: forumTopicThread, sharedMediaFromForumTopic: sharedMediaFromForumTopic, switchToGifts: switchToGifts, switchToGroupsInCommon: switchToGroupsInCommon)
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, reactionSourceMessageId: reactionSourceMessageId, callMessages: callMessages, isMyProfile: isMyProfile, hintGroupInCommon: hintGroupInCommon, forumTopicThread: forumTopicThread, sharedMediaFromForumTopic: sharedMediaFromForumTopic, switchToGifts: switchToGifts, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder)
|
||||
} else if peer is TelegramSecretChat {
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -487,7 +487,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||
externalState.isPeerArchived = channel.storiesHidden ?? false
|
||||
}
|
||||
|
||||
self.proceedWithStoryUpload(target: target, results: results, existingMedia: nil, forwardInfo: nil, externalState: externalState, commit: commit)
|
||||
self.proceedWithStoryUpload(target: target, results: results, existingMedia: nil, forwardInfo: nil, externalState: externalState, commit: commit)
|
||||
|
||||
dismissCameraImpl?()
|
||||
})
|
||||
|
|
@ -565,6 +565,8 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||
case .botPreview:
|
||||
targetPeerId = nil
|
||||
}
|
||||
|
||||
let folders: [Int64] = results.first?.options.folderIds ?? []
|
||||
|
||||
if let rootTabController = self.rootTabController {
|
||||
if let index = rootTabController.controllers.firstIndex(where: { $0 is ChatListController}) {
|
||||
|
|
@ -736,6 +738,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||
period: result.options.timeout,
|
||||
randomId: result.randomId,
|
||||
forwardInfo: forwardInfo,
|
||||
folders: folders,
|
||||
uploadInfo: results.count > 1 ? StoryUploadInfo(groupingId: groupingId, index: index, total: Int32(results.count)) : nil
|
||||
)
|
||||
|> deliverOnMainQueue).startStandalone(next: { stableId in
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public enum UndoOverlayContent {
|
|||
case copy(text: String)
|
||||
case mediaSaved(text: String)
|
||||
case paymentSent(currencyValue: String, itemTitle: String)
|
||||
case starsSent(context: AccountContext, title: String, text: [AnimatedTextComponent.Item])
|
||||
case starsSent(context: AccountContext, title: String, text: [AnimatedTextComponent.Item], hasUndo: Bool)
|
||||
case inviteRequestSent(title: String, text: String)
|
||||
case image(image: UIImage, title: String?, text: String, round: Bool, undoText: String?)
|
||||
case notificationSoundAdded(title: String, text: String, action: (() -> Void)?)
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||
self.textNode.attributedText = string
|
||||
displayUndo = false
|
||||
self.originalRemainingSeconds = 5
|
||||
case let .starsSent(_, title, textItems):
|
||||
case let .starsSent(_, title, textItems, hasUndo):
|
||||
self.avatarNode = nil
|
||||
self.iconNode = nil
|
||||
self.iconCheckNode = nil
|
||||
|
|
@ -478,11 +478,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||
|
||||
self.animatedTextItems = textItems
|
||||
|
||||
displayUndo = true
|
||||
self.originalRemainingSeconds = 4.9
|
||||
displayUndo = hasUndo
|
||||
self.originalRemainingSeconds = hasUndo ? 4.9 : 3.0
|
||||
isUserInteractionEnabled = true
|
||||
|
||||
self.statusNode = RadialStatusNode(backgroundNodeColor: .clear)
|
||||
if hasUndo {
|
||||
self.statusNode = RadialStatusNode(backgroundNodeColor: .clear)
|
||||
}
|
||||
case let .messagesUnpinned(title, text, undo, isHidden):
|
||||
self.avatarNode = nil
|
||||
self.iconNode = nil
|
||||
|
|
@ -1562,8 +1564,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||
switch content {
|
||||
case .removedChat:
|
||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||
case .starsSent:
|
||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||
case let .starsSent(_, _, _, hasUndo):
|
||||
if hasUndo {
|
||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||
}
|
||||
|
||||
if self.textNode.tapAttributeAction != nil || displayUndo {
|
||||
self.isUserInteractionEnabled = true
|
||||
|
|
@ -1835,7 +1839,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
|
||||
}
|
||||
self.textNode.attributedText = attributedText
|
||||
case let .starsSent(_, title, textItems):
|
||||
case let .starsSent(_, title, textItems, _):
|
||||
self.animatedTextItems = textItems
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ public enum ParsedInternalPeerUrlParameter {
|
|||
case text(String)
|
||||
case profile
|
||||
case referrer(String)
|
||||
case storyFolder(Int64)
|
||||
}
|
||||
|
||||
public enum ParsedInternalUrl {
|
||||
|
|
@ -345,6 +346,8 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, context: Accou
|
|||
}
|
||||
} else if queryItem.name == "ref", let referrer = queryItem.value {
|
||||
return .peer(.name(peerName), .referrer(referrer))
|
||||
} else if queryItem.name == "stories", let value = queryItem.value, let folderId = Int64(value) {
|
||||
return .peer(.name(peerName), .storyFolder(folderId))
|
||||
}
|
||||
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
||||
return .peer(.name(peerName), .voiceChat(nil))
|
||||
|
|
@ -401,6 +404,8 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, context: Accou
|
|||
return .peer(.name(peerName), .appStart("", queryItem.value, mode))
|
||||
} else if queryItem.name == "ref", let referrer = queryItem.value {
|
||||
return .peer(.name(peerName), .referrer(referrer))
|
||||
} else if queryItem.name == "stories", let value = queryItem.value, let folderId = Int64(value) {
|
||||
return .peer(.name(peerName), .storyFolder(folderId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -939,6 +944,8 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
|||
)
|
||||
case .referrer:
|
||||
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))
|
||||
case let .storyFolder(folderId):
|
||||
return .single(.result(.storyFolder(peerId: peer.id, id: folderId)))
|
||||
}
|
||||
} else {
|
||||
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"app": "11.13.4",
|
||||
"xcode": "16.2",
|
||||
"bazel": "8.2.1:22ff65b05869f6160e5157b1b425a14a62085d71d8baef571f462b8fe5a703a3",
|
||||
"bazel": "8.3.1:0cac3a67dc5429c68272dc6944104952e9e4cf84b29d126a5ff3fbaa59045217",
|
||||
"macos": "15"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue