Update API

This commit is contained in:
Ali 2023-05-15 18:43:14 +04:00
parent a4175c44ca
commit 8477cf454c
34 changed files with 797 additions and 520 deletions

View File

@ -1214,6 +1214,7 @@ private final class NotificationServiceHandler {
let collectedData = Atomic<DataValue>(value: DataValue())
return standaloneMultipartFetch(
accountPeerId: stateManager.accountPeerId,
postbox: stateManager.postbox,
network: stateManager.network,
resource: resource,
@ -1304,6 +1305,7 @@ private final class NotificationServiceHandler {
fetchNotificationSoundSignal = Signal { subscriber in
let collectedData = Atomic<Data>(value: Data())
return standaloneMultipartFetch(
accountPeerId: stateManager.accountPeerId,
postbox: stateManager.postbox,
network: stateManager.network,
resource: resource,

View File

@ -2304,6 +2304,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
self.chatListDisplayNode.clearHighlightAnimated(true)
if let fullScreenEffectView = self.fullScreenEffectView {
self.fullScreenEffectView = nil
fullScreenEffectView.removeFromSuperview()
}
}
func requestUpdateHeaderContent(transition: ContainedViewLayoutTransition) {
@ -2501,25 +2506,37 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
searchContentNode.updateListVisibleContentOffset(.known(0.0))
self.chatListDisplayNode.scrollToTop()
}
#if DEBUG && false
var fullScreenEffectView: RippleEffectView?
if let current = self.fullScreenEffectView {
fullScreenEffectView = current
self.view.window?.addSubview(current)
current.sourceView = self.view
} else {
if let value = RippleEffectView(test: false) {
fullScreenEffectView = value
self.fullScreenEffectView = value
self.view.window?.addSubview(value)
value.sourceView = self.view
}
public func animateStoryUploadRipple() {
if let componentView = self.headerContentView.view as? ChatListHeaderComponent.View {
if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) {
let localRect = transitionView.convert(transitionView.bounds, to: self.view)
self.animateRipple(centerLocation: localRect.center)
}
}
if let fullScreenEffectView {
fullScreenEffectView.frame = CGRect(origin: CGPoint(), size: layout.size)
}
public func animateRipple(centerLocation: CGPoint) {
if let fullScreenEffectView = self.fullScreenEffectView {
self.fullScreenEffectView = nil
fullScreenEffectView.removeFromSuperview()
}
if let value = RippleEffectView(centerLocation: centerLocation, completion: { [weak self] in
guard let self else {
return
}
if let fullScreenEffectView = self.fullScreenEffectView {
self.fullScreenEffectView = nil
fullScreenEffectView.removeFromSuperview()
}
}) {
self.fullScreenEffectView = value
value.sourceView = self.view
self.view.addSubview(value)
value.frame = CGRect(origin: CGPoint(), size: self.view.bounds.size)
}
#endif
}
private func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {

View File

@ -786,9 +786,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1087454222] = { return Api.StickerSetCovered.parse_stickerSetFullCovered($0) }
dict[872932635] = { return Api.StickerSetCovered.parse_stickerSetMultiCovered($0) }
dict[2008112412] = { return Api.StickerSetCovered.parse_stickerSetNoCovered($0) }
dict[-1526488475] = { return Api.StoryItem.parse_storyItem($0) }
dict[-1882351956] = { return Api.StoryItem.parse_storyItem($0) }
dict[1374088783] = { return Api.StoryItem.parse_storyItemDeleted($0) }
dict[90474706] = { return Api.StoryView.parse_storyView($0) }
dict[-1491424062] = { return Api.StoryView.parse_storyView($0) }
dict[1368082392] = { return Api.StoryViews.parse_storyViews($0) }
dict[1964978502] = { return Api.TextWithEntities.parse_textWithEntities($0) }
dict[-1609668650] = { return Api.Theme.parse_theme($0) }
dict[-94849324] = { return Api.ThemeSettings.parse_themeSettings($0) }
@ -1156,6 +1157,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[276907596] = { return Api.storage.FileType.parse_fileWebp($0) }
dict[1214632796] = { return Api.stories.AllStories.parse_allStories($0) }
dict[1340440049] = { return Api.stories.Stories.parse_stories($0) }
dict[-560009955] = { return Api.stories.StoryViews.parse_storyViews($0) }
dict[-79726676] = { return Api.stories.StoryViewsList.parse_storyViewsList($0) }
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) }
@ -1712,6 +1714,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.StoryView:
_1.serialize(buffer, boxed)
case let _1 as Api.StoryViews:
_1.serialize(buffer, boxed)
case let _1 as Api.TextWithEntities:
_1.serialize(buffer, boxed)
case let _1 as Api.Theme:
@ -2020,6 +2024,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.stories.Stories:
_1.serialize(buffer, boxed)
case let _1 as Api.stories.StoryViews:
_1.serialize(buffer, boxed)
case let _1 as Api.stories.StoryViewsList:
_1.serialize(buffer, boxed)
case let _1 as Api.updates.ChannelDifference:

View File

@ -328,14 +328,14 @@ public extension Api {
}
public extension Api {
indirect enum StoryItem: TypeConstructorDescription {
case storyItem(flags: Int32, id: Int32, date: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, privacy: [Api.PrivacyRule]?, recentViewers: [Int64]?, viewsCount: Int32?)
case storyItem(flags: Int32, id: Int32, date: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?)
case storyItemDeleted(id: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .storyItem(let flags, let id, let date, let caption, let entities, let media, let privacy, let recentViewers, let viewsCount):
case .storyItem(let flags, let id, let date, let caption, let entities, let media, let privacy, let views):
if boxed {
buffer.appendInt32(-1526488475)
buffer.appendInt32(-1882351956)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
@ -352,12 +352,7 @@ public extension Api {
for item in privacy! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(recentViewers!.count))
for item in recentViewers! {
serializeInt64(item, buffer: buffer, boxed: false)
}}
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(viewsCount!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {views!.serialize(buffer, true)}
break
case .storyItemDeleted(let id):
if boxed {
@ -370,8 +365,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .storyItem(let flags, let id, let date, let caption, let entities, let media, let privacy, let recentViewers, let viewsCount):
return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("privacy", privacy as Any), ("recentViewers", recentViewers as Any), ("viewsCount", viewsCount as Any)])
case .storyItem(let flags, let id, let date, let caption, let entities, let media, let privacy, let views):
return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("privacy", privacy as Any), ("views", views as Any)])
case .storyItemDeleted(let id):
return ("storyItemDeleted", [("id", id as Any)])
}
@ -398,12 +393,10 @@ public extension Api {
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self)
} }
var _8: [Int64]?
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
_8 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
var _8: Api.StoryViews?
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.StoryViews
} }
var _9: Int32?
if Int(_1!) & Int(1 << 3) != 0 {_9 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -412,9 +405,8 @@ public extension Api {
let _c6 = _6 != nil
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, caption: _4, entities: _5, media: _6!, privacy: _7, recentViewers: _8, viewsCount: _9)
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, caption: _4, entities: _5, media: _6!, privacy: _7, views: _8)
}
else {
return nil
@ -436,16 +428,16 @@ public extension Api {
}
public extension Api {
enum StoryView: TypeConstructorDescription {
case storyView(userId: Int64, date: Int64)
case storyView(userId: Int64, date: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .storyView(let userId, let date):
if boxed {
buffer.appendInt32(90474706)
buffer.appendInt32(-1491424062)
}
serializeInt64(userId, buffer: buffer, boxed: false)
serializeInt64(date, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
break
}
}
@ -460,8 +452,8 @@ public extension Api {
public static func parse_storyView(_ reader: BufferReader) -> StoryView? {
var _1: Int64?
_1 = reader.readInt64()
var _2: Int64?
_2 = reader.readInt64()
var _2: Int32?
_2 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
@ -474,6 +466,52 @@ public extension Api {
}
}
public extension Api {
enum StoryViews: TypeConstructorDescription {
case storyViews(recentViewers: [Int64], viewsCount: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .storyViews(let recentViewers, let viewsCount):
if boxed {
buffer.appendInt32(1368082392)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(recentViewers.count))
for item in recentViewers {
serializeInt64(item, buffer: buffer, boxed: false)
}
serializeInt32(viewsCount, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .storyViews(let recentViewers, let viewsCount):
return ("storyViews", [("recentViewers", recentViewers as Any), ("viewsCount", viewsCount as Any)])
}
}
public static func parse_storyViews(_ reader: BufferReader) -> StoryViews? {
var _1: [Int64]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
}
var _2: Int32?
_2 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.StoryViews.storyViews(recentViewers: _1!, viewsCount: _2!)
}
else {
return nil
}
}
}
}
public extension Api {
enum TextWithEntities: TypeConstructorDescription {
case textWithEntities(text: String, entities: [Api.MessageEntity])

View File

@ -474,6 +474,58 @@ public extension Api.stories {
}
}
public extension Api.stories {
enum StoryViews: TypeConstructorDescription {
case storyViews(views: [Api.StoryViews], users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .storyViews(let views, let users):
if boxed {
buffer.appendInt32(-560009955)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(views.count))
for item in views {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .storyViews(let views, let users):
return ("storyViews", [("views", views as Any), ("users", users as Any)])
}
}
public static func parse_storyViews(_ reader: BufferReader) -> StoryViews? {
var _1: [Api.StoryViews]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryViews.self)
}
var _2: [Api.User]?
if let _ = reader.readInt32() {
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.stories.StoryViews.storyViews(views: _1!, users: _2!)
}
else {
return nil
}
}
}
}
public extension Api.stories {
enum StoryViewsList: TypeConstructorDescription {
case storyViewsList(count: Int32, views: [Api.StoryView], users: [Api.User])

View File

@ -8508,14 +8508,53 @@ public extension Api.functions.stories {
}
}
public extension Api.functions.stories {
static func getStoryViews(id: Int32, offsetDate: Int32, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.StoryViewsList>) {
static func getStoriesByID(userId: Api.InputUser, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.Stories>) {
let buffer = Buffer()
buffer.appendInt32(-2075944968)
buffer.appendInt32(1779814214)
userId.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(id.count))
for item in id {
serializeInt32(item, buffer: buffer, boxed: false)
}
return (FunctionDescription(name: "stories.getStoriesByID", parameters: [("userId", String(describing: userId)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.Stories? in
let reader = BufferReader(buffer)
var result: Api.stories.Stories?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.stories.Stories
}
return result
})
}
}
public extension Api.functions.stories {
static func getStoriesViews(id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.StoryViews>) {
let buffer = Buffer()
buffer.appendInt32(-1703553370)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(id.count))
for item in id {
serializeInt32(item, buffer: buffer, boxed: false)
}
return (FunctionDescription(name: "stories.getStoriesViews", parameters: [("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.StoryViews? in
let reader = BufferReader(buffer)
var result: Api.stories.StoryViews?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.stories.StoryViews
}
return result
})
}
}
public extension Api.functions.stories {
static func getStoryViewsList(id: Int32, offsetDate: Int32, offsetId: Int64, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.StoryViewsList>) {
let buffer = Buffer()
buffer.appendInt32(1262182039)
serializeInt32(id, buffer: buffer, boxed: false)
serializeInt32(offsetDate, buffer: buffer, boxed: false)
serializeInt32(offsetId, buffer: buffer, boxed: false)
serializeInt64(offsetId, buffer: buffer, boxed: false)
serializeInt32(limit, buffer: buffer, boxed: false)
return (FunctionDescription(name: "stories.getStoryViews", parameters: [("id", String(describing: id)), ("offsetDate", String(describing: offsetDate)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.StoryViewsList? in
return (FunctionDescription(name: "stories.getStoryViewsList", parameters: [("id", String(describing: id)), ("offsetDate", String(describing: offsetDate)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.StoryViewsList? in
let reader = BufferReader(buffer)
var result: Api.stories.StoryViewsList?
if let signature = reader.readInt32() {

View File

@ -191,6 +191,7 @@ private final class FetchImpl {
private final class Impl {
private let queue: Queue
private let accountPeerId: PeerId
private let postbox: Postbox
private let network: Network
private let mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?
@ -220,6 +221,7 @@ private final class FetchImpl {
init(
queue: Queue,
accountPeerId: PeerId,
postbox: Postbox,
network: Network,
mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?,
@ -237,6 +239,7 @@ private final class FetchImpl {
) {
self.queue = queue
self.accountPeerId = accountPeerId
self.postbox = postbox
self.network = network
self.mediaReferenceRevalidationContext = mediaReferenceRevalidationContext
@ -471,6 +474,7 @@ private final class FetchImpl {
let fetchLocation = state.fetchLocation
state.disposable = (revalidateMediaResourceReference(
accountPeerId: self.accountPeerId,
postbox: self.postbox,
network: self.network,
revalidationContext: mediaReferenceRevalidationContext,
@ -716,6 +720,7 @@ private final class FetchImpl {
init(
accountPeerId: PeerId,
postbox: Postbox,
network: Network,
mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?,
@ -736,6 +741,7 @@ private final class FetchImpl {
self.impl = QueueLocalObject(queue: queue, generate: {
return Impl(
queue: queue,
accountPeerId: accountPeerId,
postbox: postbox,
network: network,
mediaReferenceRevalidationContext: mediaReferenceRevalidationContext,
@ -756,6 +762,7 @@ private final class FetchImpl {
}
func multipartFetchV2(
accountPeerId: PeerId,
postbox: Postbox,
network: Network,
mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?,
@ -771,6 +778,7 @@ func multipartFetchV2(
) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
return Signal { subscriber in
let impl = FetchImpl(
accountPeerId: accountPeerId,
postbox: postbox,
network: network,
mediaReferenceRevalidationContext: mediaReferenceRevalidationContext,

View File

@ -307,6 +307,7 @@ private enum MediaReferenceRevalidationKey: Hashable {
case attachBot(peer: PeerReference)
case notificationSoundList
case customEmoji(fileId: Int64)
case story(peer: PeerReference, id: Int32)
}
private final class MediaReferenceRevalidationItemContext {
@ -658,6 +659,31 @@ final class MediaReferenceRevalidationContext {
}
}
func story(accountPeerId: PeerId, postbox: Postbox, network: Network, background: Bool, peer: PeerReference, id: Int32) -> Signal<StoryListContext.Item, RevalidateMediaReferenceError> {
return self.genericItem(key: .story(peer: peer, id: id), background: background, request: { next, error in
return (_internal_getStoryById(accountPeerId: accountPeerId, postbox: postbox, network: network, peer: peer, id: id)
|> castError(RevalidateMediaReferenceError.self)
|> mapToSignal { result -> Signal<StoryListContext.Item, RevalidateMediaReferenceError> in
if let result = result {
return .single(result)
} else {
return .fail(.generic)
}
}).start(next: { value in
next(value)
}, error: { _ in
error(.generic)
})
})
|> mapToSignal { next -> Signal<StoryListContext.Item, RevalidateMediaReferenceError> in
if let next = next as? StoryListContext.Item {
return .single(next)
} else {
return .fail(.generic)
}
}
}
func notificationSoundList(postbox: Postbox, network: Network, background: Bool) -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> {
return self.genericItem(key: .notificationSoundList, background: background, request: { next, error in
return (requestNotificationSoundList(network: network, hash: 0)
@ -682,7 +708,7 @@ struct RevalidatedMediaResource {
let updatedReference: MediaResourceReference?
}
func revalidateMediaResourceReference(postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo, resource: MediaResource) -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> {
func revalidateMediaResourceReference(accountPeerId: PeerId, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo, resource: MediaResource) -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> {
var updatedReference = info.reference
if case let .media(media, resource) = updatedReference {
if case let .message(messageReference, mediaValue) = media {
@ -805,6 +831,14 @@ func revalidateMediaResourceReference(postbox: Postbox, network: Network, revali
}
return .fail(.generic)
}
case let .story(peer, id, _):
return revalidationContext.story(accountPeerId: accountPeerId, postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, peer: peer, id: id)
|> mapToSignal { storyItem -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> in
if let updatedResource = findUpdatedMediaResource(media: storyItem.media._asMedia(), previousMedia: nil, resource: resource) {
return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil))
}
return .fail(.generic)
}
case let .standalone(media):
if let file = media as? TelegramMediaFile {
for attribute in file.attributes {

View File

@ -473,6 +473,7 @@ private final class MultipartFetchManager {
var completeSize: Int64?
var completeSizeReported = false
let accountPeerId: PeerId
let postbox: Postbox
let network: Network
let networkStatsContext: NetworkStatsContext?
@ -505,7 +506,7 @@ private final class MultipartFetchManager {
private var fetchSpeedRecords: [FetchSpeedRecord] = []
private var totalFetchedByteCount: Int = 0
init(resource: TelegramMediaResource, parameters: MediaResourceFetchParameters?, size: Int64?, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, encryptionKey: SecretFileEncryptionKey?, decryptedSize: Int64?, location: MultipartFetchMasterLocation, postbox: Postbox, network: Network, networkStatsContext: NetworkStatsContext?, revalidationContext: MediaReferenceRevalidationContext?, partReady: @escaping (Int64, Data) -> Void, reportCompleteSize: @escaping (Int64) -> Void, finishWithError: @escaping (MediaResourceDataFetchError) -> Void, useMainConnection: Bool) {
init(resource: TelegramMediaResource, parameters: MediaResourceFetchParameters?, size: Int64?, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, encryptionKey: SecretFileEncryptionKey?, decryptedSize: Int64?, location: MultipartFetchMasterLocation, accountPeerId: PeerId, postbox: Postbox, network: Network, networkStatsContext: NetworkStatsContext?, revalidationContext: MediaReferenceRevalidationContext?, partReady: @escaping (Int64, Data) -> Void, reportCompleteSize: @escaping (Int64) -> Void, finishWithError: @escaping (MediaResourceDataFetchError) -> Void, useMainConnection: Bool) {
self.resource = resource
self.parameters = parameters
self.consumerId = Int64.random(in: Int64.min ... Int64.max)
@ -555,6 +556,7 @@ private final class MultipartFetchManager {
}
self.state = MultipartDownloadState(encryptionKey: encryptionKey, decryptedSize: decryptedSize)
self.accountPeerId = accountPeerId
self.postbox = postbox
self.network = network
self.networkStatsContext = networkStatsContext
@ -836,7 +838,7 @@ private final class MultipartFetchManager {
strongSelf.fetchingParts.removeAll()
if let info = strongSelf.parameters?.info as? TelegramCloudMediaResourceFetchInfo, let revalidationContext = strongSelf.revalidationContext {
strongSelf.revalidateMediaReferenceDisposable.set((revalidateMediaResourceReference(postbox: strongSelf.postbox, network: strongSelf.network, revalidationContext: revalidationContext, info: info, resource: strongSelf.resource)
strongSelf.revalidateMediaReferenceDisposable.set((revalidateMediaResourceReference(accountPeerId: strongSelf.accountPeerId, postbox: strongSelf.postbox, network: strongSelf.network, revalidationContext: revalidationContext, info: info, resource: strongSelf.resource)
|> deliverOn(strongSelf.queue)).start(next: { validationResult in
if let strongSelf = self {
strongSelf.revalidatingMediaReference = false
@ -897,8 +899,9 @@ private final class MultipartFetchManager {
}
}
public func standaloneMultipartFetch(postbox: Postbox, network: Network, resource: TelegramMediaResource, datacenterId: Int, size: Int64?, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?, encryptionKey: SecretFileEncryptionKey? = nil, decryptedSize: Int32? = nil, continueInBackground: Bool = false, useMainConnection: Bool = false) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
public func standaloneMultipartFetch(accountPeerId: PeerId, postbox: Postbox, network: Network, resource: TelegramMediaResource, datacenterId: Int, size: Int64?, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?, encryptionKey: SecretFileEncryptionKey? = nil, decryptedSize: Int32? = nil, continueInBackground: Bool = false, useMainConnection: Bool = false) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
return multipartFetch(
accountPeerId: accountPeerId,
postbox: postbox,
network: network,
mediaReferenceRevalidationContext: nil,
@ -921,6 +924,7 @@ public func resourceFetchInfo(resource: TelegramMediaResource) -> MediaResourceF
}
private func multipartFetchV1(
accountPeerId: PeerId,
postbox: Postbox,
network: Network,
mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?,
@ -992,7 +996,7 @@ private func multipartFetchV1(
subscriber.putNext(.reset)
}
let manager = MultipartFetchManager(resource: resource, parameters: parameters, size: size, intervals: intervals, encryptionKey: encryptionKey, decryptedSize: decryptedSize, location: location, postbox: postbox, network: network, networkStatsContext: networkStatsContext, revalidationContext: mediaReferenceRevalidationContext, partReady: { dataOffset, data in
let manager = MultipartFetchManager(resource: resource, parameters: parameters, size: size, intervals: intervals, encryptionKey: encryptionKey, decryptedSize: decryptedSize, location: location, accountPeerId: accountPeerId, postbox: postbox, network: network, networkStatsContext: networkStatsContext, revalidationContext: mediaReferenceRevalidationContext, partReady: { dataOffset, data in
subscriber.putNext(.dataPart(resourceOffset: dataOffset, data: data, range: 0 ..< Int64(data.count), complete: false))
}, reportCompleteSize: { size in
subscriber.putNext(.resourceSizeUpdated(size))
@ -1013,6 +1017,7 @@ private func multipartFetchV1(
}
func multipartFetch(
accountPeerId: PeerId,
postbox: Postbox,
network: Network,
mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?,
@ -1029,6 +1034,7 @@ func multipartFetch(
) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
if network.useExperimentalFeatures, let _ = resource as? TelegramCloudMediaResource, !(resource is SecretFileMediaResource) {
return multipartFetchV2(
accountPeerId: accountPeerId,
postbox: postbox,
network: network,
mediaReferenceRevalidationContext: mediaReferenceRevalidationContext,
@ -1044,6 +1050,7 @@ func multipartFetch(
)
}
return multipartFetchV1(
accountPeerId: accountPeerId,
postbox: postbox,
network: network,
mediaReferenceRevalidationContext: mediaReferenceRevalidationContext,

View File

@ -52,11 +52,11 @@ enum MessageContentToUpload {
}
}
func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, message: Message) -> MessageContentToUpload {
return messageContentToUpload(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, peerId: message.id.peerId, messageId: message.id, attributes: message.attributes, text: message.text, media: message.media)
func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, message: Message) -> MessageContentToUpload {
return messageContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, peerId: message.id.peerId, messageId: message.id, attributes: message.attributes, text: message.text, media: message.media)
}
func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, peerId: PeerId, messageId: MessageId?, attributes: [MessageAttribute], text: String, media: [Media]) -> MessageContentToUpload {
func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, peerId: PeerId, messageId: MessageId?, attributes: [MessageAttribute], text: String, media: [Media]) -> MessageContentToUpload {
var contextResult: OutgoingChatContextResultMessageAttribute?
var autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?
var autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?
@ -87,14 +87,14 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods
return .immediate(.content(PendingMessageUploadedContentAndReuploadInfo(content: .forward(forwardInfo), reuploadInfo: nil, cacheReferenceKey: nil)), .text)
} else if let contextResult = contextResult {
return .immediate(.content(PendingMessageUploadedContentAndReuploadInfo(content: .chatContextResult(contextResult), reuploadInfo: nil, cacheReferenceKey: nil)), .text)
} else if let media = media.first, let mediaResult = mediaContentToUpload(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, peerId: peerId, media: media, text: text, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute, messageId: messageId, attributes: attributes) {
} else if let media = media.first, let mediaResult = mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: revalidationContext, forceReupload: forceReupload, isGrouped: isGrouped, peerId: peerId, media: media, text: text, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute, messageId: messageId, attributes: attributes) {
return .signal(mediaResult, .media)
} else {
return .signal(.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .text(text), reuploadInfo: nil, cacheReferenceKey: nil))), .text)
}
}
func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, peerId: PeerId, media: Media, text: String, autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?, autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute]) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? {
func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, peerId: PeerId, media: Media, text: String, autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?, autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute]) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? {
if let image = media as? TelegramMediaImage, let largest = largestImageRepresentation(image.representations) {
if peerId.namespace == Namespaces.Peer.SecretChat, let resource = largest.resource as? SecretFileMediaResource {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(.inputEncryptedFile(id: resource.fileId, accessHash: resource.accessHash), resource.decryptedSize, resource.key), reuploadInfo: nil, cacheReferenceKey: nil)))
@ -123,7 +123,7 @@ func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods:
} else {
mediaReference = .savedGif(media: file)
}
return revalidateMediaResourceReference(postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: mediaReference.resourceReference(file.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: resource)
return revalidateMediaResourceReference(accountPeerId: accountPeerId, postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: mediaReference.resourceReference(file.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: resource)
|> mapError { _ -> PendingMessageUploadError in
return .generic
}

View File

@ -75,7 +75,7 @@ private final class PendingUpdateMessageManagerImpl {
self.contexts[messageId] = context
let queue = self.queue
disposable.set((requestEditMessage(postbox: self.postbox, network: self.network, stateManager: self.stateManager, transformOutgoingMessageMedia: self.transformOutgoingMessageMedia, messageMediaPreuploadManager: self.messageMediaPreuploadManager, mediaReferenceRevalidationContext: self.mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: nil)
disposable.set((requestEditMessage(accountPeerId: self.stateManager.accountPeerId, postbox: self.postbox, network: self.network, stateManager: self.stateManager, transformOutgoingMessageMedia: self.transformOutgoingMessageMedia, messageMediaPreuploadManager: self.messageMediaPreuploadManager, mediaReferenceRevalidationContext: self.mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: nil)
|> deliverOn(self.queue)).start(next: { [weak self, weak context] value in
queue.async {
guard let strongSelf = self, let initialContext = context else {

View File

@ -28,14 +28,14 @@ public enum RequestEditMessageError {
}
func _internal_requestEditMessage(account: Account, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: Media], disableUrlPreview: Bool = false, scheduleTime: Int32? = nil) -> Signal<RequestEditMessageResult, RequestEditMessageError> {
return requestEditMessage(postbox: account.postbox, network: account.network, stateManager: account.stateManager, transformOutgoingMessageMedia: account.transformOutgoingMessageMedia, messageMediaPreuploadManager: account.messageMediaPreuploadManager, mediaReferenceRevalidationContext: account.mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime)
return requestEditMessage(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, stateManager: account.stateManager, transformOutgoingMessageMedia: account.transformOutgoingMessageMedia, messageMediaPreuploadManager: account.messageMediaPreuploadManager, mediaReferenceRevalidationContext: account.mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime)
}
func requestEditMessage(postbox: Postbox, network: Network, stateManager: AccountStateManager, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: Media], disableUrlPreview: Bool, scheduleTime: Int32?) -> Signal<RequestEditMessageResult, RequestEditMessageError> {
return requestEditMessageInternal(postbox: postbox, network: network, stateManager: stateManager, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime, forceReupload: false)
func requestEditMessage(accountPeerId: PeerId, postbox: Postbox, network: Network, stateManager: AccountStateManager, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: Media], disableUrlPreview: Bool, scheduleTime: Int32?) -> Signal<RequestEditMessageResult, RequestEditMessageError> {
return requestEditMessageInternal(accountPeerId: accountPeerId, postbox: postbox, network: network, stateManager: stateManager, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime, forceReupload: false)
|> `catch` { error -> Signal<RequestEditMessageResult, RequestEditMessageInternalError> in
if case .invalidReference = error {
return requestEditMessageInternal(postbox: postbox, network: network, stateManager: stateManager, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime, forceReupload: true)
return requestEditMessageInternal(accountPeerId: accountPeerId, postbox: postbox, network: network, stateManager: stateManager, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, messageId: messageId, text: text, media: media, entities: entities, inlineStickers: inlineStickers, disableUrlPreview: disableUrlPreview, scheduleTime: scheduleTime, forceReupload: true)
} else {
return .fail(error)
}
@ -50,7 +50,7 @@ func requestEditMessage(postbox: Postbox, network: Network, stateManager: Accoun
}
}
private func requestEditMessageInternal(postbox: Postbox, network: Network, stateManager: AccountStateManager, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: Media], disableUrlPreview: Bool, scheduleTime: Int32?, forceReupload: Bool) -> Signal<RequestEditMessageResult, RequestEditMessageInternalError> {
private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox, network: Network, stateManager: AccountStateManager, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, messageId: MessageId, text: String, media: RequestEditMessageMedia, entities: TextEntitiesMessageAttribute?, inlineStickers: [MediaId: Media], disableUrlPreview: Bool, scheduleTime: Int32?, forceReupload: Bool) -> Signal<RequestEditMessageResult, RequestEditMessageInternalError> {
let uploadedMedia: Signal<PendingMessageUploadedContentResult?, NoError>
switch media {
case .keep:
@ -59,7 +59,7 @@ private func requestEditMessageInternal(postbox: Postbox, network: Network, stat
case let .update(media):
let generateUploadSignal: (Bool) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? = { forceReupload in
let augmentedMedia = augmentMediaWithReference(media)
return mediaContentToUpload(network: network, postbox: postbox, auxiliaryMethods: stateManager.auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: mediaReferenceRevalidationContext, forceReupload: forceReupload, isGrouped: false, peerId: messageId.peerId, media: augmentedMedia, text: "", autoremoveMessageAttribute: nil, autoclearMessageAttribute: nil, messageId: nil, attributes: [])
return mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: stateManager.auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: mediaReferenceRevalidationContext, forceReupload: forceReupload, isGrouped: false, peerId: messageId.peerId, media: augmentedMedia, text: "", autoremoveMessageAttribute: nil, autoclearMessageAttribute: nil, messageId: nil, attributes: [])
}
if let uploadSignal = generateUploadSignal(forceReupload) {
uploadedMedia = .single(.progress(0.027))

View File

@ -4333,62 +4333,10 @@ func replayFinalState(
case .userStories(let userId, let stories), .userStoriesSlice(_, let userId, let stories):
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
for storyItem in stories {
switch storyItem {
case let .storyItemDeleted(id):
storyUpdates.append(InternalStoryUpdate.deleted(id))
case let .storyItem(flags, id, date, _, _, media, privacy, recentViewers, viewCount):
let _ = flags
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let parsedMedia = parsedMedia {
var seenPeers: [EnginePeer] = []
if let recentViewers = recentViewers {
for id in recentViewers {
if let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id))) {
seenPeers.append(EnginePeer(peer))
}
}
}
var parsedPrivacy: EngineStoryPrivacy?
if let privacy = privacy {
var base: EngineStoryPrivacy.Base = .everyone
var additionalPeerIds: [EnginePeer.Id] = []
for rule in privacy {
switch rule {
case .privacyValueAllowAll:
base = .everyone
case .privacyValueAllowContacts:
base = .contacts
case .privacyValueAllowCloseFriends:
base = .closeFriends
case let .privacyValueAllowUsers(users):
for id in users {
additionalPeerIds.append(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(id)))
}
case let .privacyValueAllowChatParticipants(chats):
for id in chats {
if let peer = transaction.getPeer(EnginePeer.Id(namespace: Namespaces.Peer.CloudGroup, id: EnginePeer.Id.Id._internalFromInt64Value(id))) {
additionalPeerIds.append(peer.id)
} else if let peer = transaction.getPeer(EnginePeer.Id(namespace: Namespaces.Peer.CloudChannel, id: EnginePeer.Id.Id._internalFromInt64Value(id))) {
additionalPeerIds.append(peer.id)
}
}
default:
break
}
}
parsedPrivacy = EngineStoryPrivacy(base: base, additionallyIncludePeers: additionalPeerIds)
}
storyUpdates.append(InternalStoryUpdate.added(peerId: peerId, item: StoryListContext.Item(
id: id,
timestamp: date,
media: EngineMedia(parsedMedia),
seenCount: viewCount.flatMap(Int.init) ?? 0,
seenPeers: seenPeers,
privacy: parsedPrivacy
)))
}
if let parsedItem = _internal_parseApiStoryItem(transaction: transaction, peerId: peerId, apiStory: storyItem) {
storyUpdates.append(InternalStoryUpdate.added(peerId: peerId, item: parsedItem))
} else {
storyUpdates.append(InternalStoryUpdate.deleted(peerId: peerId, id: storyItem.id))
}
}
}

View File

@ -7,6 +7,7 @@ import TelegramApi
final class AccountTaskManager {
private final class Impl {
private let queue: Queue
private let accountPeerId: PeerId
private let stateManager: AccountStateManager
private let accountManager: AccountManager<TelegramAccountManagerTypes>
private let networkArguments: NetworkInitializationArguments
@ -22,9 +23,10 @@ final class AccountTaskManager {
private var isUpdating: Bool = false
init(queue: Queue, stateManager: AccountStateManager, accountManager: AccountManager<TelegramAccountManagerTypes>,
init(queue: Queue, accountPeerId: PeerId, stateManager: AccountStateManager, accountManager: AccountManager<TelegramAccountManagerTypes>,
networkArguments: NetworkInitializationArguments, viewTracker: AccountViewTracker, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, isMainApp: Bool, testingEnvironment: Bool) {
self.queue = queue
self.accountPeerId = accountPeerId
self.stateManager = stateManager
self.accountManager = accountManager
self.networkArguments = networkArguments
@ -73,9 +75,9 @@ final class AccountTaskManager {
tasks.add(managedSynchronizeInstalledStickerPacksOperations(postbox: self.stateManager.postbox, network: self.stateManager.network, stateManager: self.stateManager, namespace: .masks).start())
tasks.add(managedSynchronizeInstalledStickerPacksOperations(postbox: self.stateManager.postbox, network: self.stateManager.network, stateManager: self.stateManager, namespace: .emoji).start())
tasks.add(managedSynchronizeMarkFeaturedStickerPacksAsSeenOperations(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
tasks.add(managedSynchronizeRecentlyUsedMediaOperations(postbox: self.stateManager.postbox, network: self.stateManager.network, category: .stickers, revalidationContext: self.mediaReferenceRevalidationContext).start())
tasks.add(managedSynchronizeSavedGifsOperations(postbox: self.stateManager.postbox, network: self.stateManager.network, revalidationContext: self.mediaReferenceRevalidationContext).start())
tasks.add(managedSynchronizeSavedStickersOperations(postbox: self.stateManager.postbox, network: self.stateManager.network, revalidationContext: self.mediaReferenceRevalidationContext).start())
tasks.add(managedSynchronizeRecentlyUsedMediaOperations(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network, category: .stickers, revalidationContext: self.mediaReferenceRevalidationContext).start())
tasks.add(managedSynchronizeSavedGifsOperations(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network, revalidationContext: self.mediaReferenceRevalidationContext).start())
tasks.add(managedSynchronizeSavedStickersOperations(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network, revalidationContext: self.mediaReferenceRevalidationContext).start())
tasks.add(_internal_managedRecentlyUsedInlineBots(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start())
tasks.add(managedSynchronizeConsumeMessageContentOperations(postbox: self.stateManager.postbox, network: self.stateManager.network, stateManager: self.stateManager).start())
tasks.add(managedConsumePersonalMessagesActions(postbox: self.stateManager.postbox, network: self.stateManager.network, stateManager: self.stateManager).start())
@ -146,7 +148,7 @@ final class AccountTaskManager {
let queue = Account.sharedQueue
self.queue = queue
self.impl = QueueLocalObject(queue: queue, generate: {
return Impl(queue: queue, stateManager: stateManager, accountManager: accountManager, networkArguments: networkArguments, viewTracker: viewTracker, mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, isMainApp: isMainApp, testingEnvironment: testingEnvironment)
return Impl(queue: queue, accountPeerId: stateManager.accountPeerId, stateManager: stateManager, accountManager: accountManager, networkArguments: networkArguments, viewTracker: viewTracker, mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, isMainApp: isMainApp, testingEnvironment: testingEnvironment)
})
}
}

View File

@ -23,7 +23,7 @@ private final class MediaResourceDataCopyFile : MediaResourceDataFetchCopyLocalI
}
public func fetchCloudMediaLocation(account: Account, resource: TelegramMediaResource, datacenterId: Int, size: Int64?, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
return multipartFetch(postbox: account.postbox, network: account.network, mediaReferenceRevalidationContext: account.mediaReferenceRevalidationContext, networkStatsContext: account.networkStatsContext, resource: resource, datacenterId: datacenterId, size: size, intervals: intervals, parameters: parameters)
return multipartFetch(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, mediaReferenceRevalidationContext: account.mediaReferenceRevalidationContext, networkStatsContext: account.networkStatsContext, resource: resource, datacenterId: datacenterId, size: size, intervals: intervals, parameters: parameters)
}
private func fetchLocalFileResource(path: String, move: Bool) -> Signal<MediaResourceDataFetchResult, NoError> {

View File

@ -5,5 +5,5 @@ import MtProtoKit
func fetchSecretFileResource(account: Account, resource: SecretFileMediaResource, intervals: Signal<[(Range<Int64>, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
return multipartFetch(postbox: account.postbox, network: account.network, mediaReferenceRevalidationContext: account.mediaReferenceRevalidationContext, networkStatsContext: account.networkStatsContext, resource: resource, datacenterId: resource.datacenterId, size: resource.size, intervals: intervals, parameters: parameters, encryptionKey: resource.key, decryptedSize: resource.decryptedSize)
return multipartFetch(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, mediaReferenceRevalidationContext: account.mediaReferenceRevalidationContext, networkStatsContext: account.networkStatsContext, resource: resource, datacenterId: resource.datacenterId, size: resource.size, intervals: intervals, parameters: parameters, encryptionKey: resource.key, decryptedSize: resource.decryptedSize)
}

View File

@ -65,7 +65,7 @@ private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOpera
} |> switchToLatest
}
func managedSynchronizeRecentlyUsedMediaOperations(postbox: Postbox, network: Network, category: RecentlyUsedMediaCategory, revalidationContext: MediaReferenceRevalidationContext) -> Signal<Void, NoError> {
func managedSynchronizeRecentlyUsedMediaOperations(accountPeerId: PeerId, postbox: Postbox, network: Network, category: RecentlyUsedMediaCategory, revalidationContext: MediaReferenceRevalidationContext) -> Signal<Void, NoError> {
return Signal { _ in
let tag: PeerOperationLogTag
switch category {
@ -88,7 +88,7 @@ func managedSynchronizeRecentlyUsedMediaOperations(postbox: Postbox, network: Ne
let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Void, NoError> in
if let entry = entry {
if let operation = entry.contents as? SynchronizeRecentlyUsedMediaOperation {
return synchronizeRecentlyUsedMedia(transaction: transaction, postbox: postbox, network: network, revalidationContext: revalidationContext, operation: operation)
return synchronizeRecentlyUsedMedia(transaction: transaction, accountPeerId: accountPeerId, postbox: postbox, network: network, revalidationContext: revalidationContext, operation: operation)
} else {
assertionFailure()
}
@ -120,7 +120,7 @@ private enum SaveRecentlyUsedMediaError {
case invalidReference
}
private func synchronizeRecentlyUsedMedia(transaction: Transaction, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, operation: SynchronizeRecentlyUsedMediaOperation) -> Signal<Void, NoError> {
private func synchronizeRecentlyUsedMedia(transaction: Transaction, accountPeerId: PeerId, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, operation: SynchronizeRecentlyUsedMediaOperation) -> Signal<Void, NoError> {
switch operation.content {
case let .add(id, accessHash, fileReference):
guard let fileReference = fileReference else {
@ -150,7 +150,7 @@ private func synchronizeRecentlyUsedMedia(transaction: Transaction, postbox: Pos
case .generic:
return .fail(.generic)
case .invalidReference:
return revalidateMediaResourceReference(postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: fileReference.resourceReference(fileReference.media.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: fileReference.media.resource)
return revalidateMediaResourceReference(accountPeerId: accountPeerId, postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: fileReference.resourceReference(fileReference.media.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: fileReference.media.resource)
|> mapError { _ -> SaveRecentlyUsedMediaError in
return .generic
}

View File

@ -49,7 +49,7 @@ private final class ManagedSynchronizeSavedGifsOperationsHelper {
}
}
private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOperationLogTag, tagLocalIndex: Int32, _ f: @escaping (Transaction, PeerMergedOperationLogEntry?) -> Signal<Void, NoError>) -> Signal<Void, NoError> {
private func withTakenOperation(accountPeerId: PeerId, postbox: Postbox, peerId: PeerId, tag: PeerOperationLogTag, tagLocalIndex: Int32, _ f: @escaping (Transaction, PeerMergedOperationLogEntry?) -> Signal<Void, NoError>) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Signal<Void, NoError> in
var result: PeerMergedOperationLogEntry?
transaction.operationLogUpdateEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, { entry in
@ -65,7 +65,7 @@ private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOpera
} |> switchToLatest
}
func managedSynchronizeSavedGifsOperations(postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext) -> Signal<Void, NoError> {
func managedSynchronizeSavedGifsOperations(accountPeerId: PeerId, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext) -> Signal<Void, NoError> {
return Signal { _ in
let tag: PeerOperationLogTag = OperationLogTags.SynchronizeSavedGifs
@ -81,10 +81,10 @@ func managedSynchronizeSavedGifsOperations(postbox: Postbox, network: Network, r
}
for (entry, disposable) in beginOperations {
let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Void, NoError> in
let signal = withTakenOperation(accountPeerId: accountPeerId, postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Void, NoError> in
if let entry = entry {
if let operation = entry.contents as? SynchronizeSavedGifsOperation {
return synchronizeSavedGifs(transaction: transaction, postbox: postbox, network: network, revalidationContext: revalidationContext, operation: operation)
return synchronizeSavedGifs(transaction: transaction, accountPeerId: accountPeerId, postbox: postbox, network: network, revalidationContext: revalidationContext, operation: operation)
} else {
assertionFailure()
}
@ -116,7 +116,7 @@ private enum SaveGifError {
case invalidReference
}
private func synchronizeSavedGifs(transaction: Transaction, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, operation: SynchronizeSavedGifsOperation) -> Signal<Void, NoError> {
private func synchronizeSavedGifs(transaction: Transaction, accountPeerId: PeerId, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, operation: SynchronizeSavedGifsOperation) -> Signal<Void, NoError> {
switch operation.content {
case let .add(id, accessHash, fileReference):
guard let fileReference = fileReference else {
@ -146,7 +146,7 @@ private func synchronizeSavedGifs(transaction: Transaction, postbox: Postbox, ne
case .generic:
return .fail(.generic)
case .invalidReference:
return revalidateMediaResourceReference(postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: fileReference.resourceReference(fileReference.media.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: fileReference.media.resource)
return revalidateMediaResourceReference(accountPeerId: accountPeerId, postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: fileReference.resourceReference(fileReference.media.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: fileReference.media.resource)
|> mapError { _ -> SaveGifError in
return .generic
}

View File

@ -65,7 +65,7 @@ private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOpera
} |> switchToLatest
}
func managedSynchronizeSavedStickersOperations(postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext) -> Signal<Void, NoError> {
func managedSynchronizeSavedStickersOperations(accountPeerId: PeerId, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext) -> Signal<Void, NoError> {
return Signal { _ in
let tag: PeerOperationLogTag = OperationLogTags.SynchronizeSavedStickers
@ -84,7 +84,7 @@ func managedSynchronizeSavedStickersOperations(postbox: Postbox, network: Networ
let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Void, NoError> in
if let entry = entry {
if let operation = entry.contents as? SynchronizeSavedStickersOperation {
return synchronizeSavedStickers(transaction: transaction, postbox: postbox, network: network, revalidationContext: revalidationContext, operation: operation)
return synchronizeSavedStickers(transaction: transaction, accountPeerId: accountPeerId, postbox: postbox, network: network, revalidationContext: revalidationContext, operation: operation)
} else {
assertionFailure()
}
@ -116,7 +116,7 @@ private enum SaveStickerError {
case invalidReference
}
private func synchronizeSavedStickers(transaction: Transaction, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, operation: SynchronizeSavedStickersOperation) -> Signal<Void, NoError> {
private func synchronizeSavedStickers(transaction: Transaction, accountPeerId: PeerId, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, operation: SynchronizeSavedStickersOperation) -> Signal<Void, NoError> {
switch operation.content {
case let .add(id, accessHash, fileReference):
guard let fileReference = fileReference else {
@ -146,7 +146,7 @@ private func synchronizeSavedStickers(transaction: Transaction, postbox: Postbox
case .generic:
return .fail(.generic)
case .invalidReference:
return revalidateMediaResourceReference(postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: fileReference.resourceReference(fileReference.media.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: fileReference.media.resource)
return revalidateMediaResourceReference(accountPeerId: accountPeerId, postbox: postbox, network: network, revalidationContext: revalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: fileReference.resourceReference(fileReference.media.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: fileReference.media.resource)
|> mapError { _ -> SaveStickerError in
return .generic
}

View File

@ -403,7 +403,7 @@ public final class PendingMessageManager {
return lhs.1.index < rhs.1.index
}) {
if case let .collectingInfo(message) = messageContext.state {
let contentToUpload = messageContentToUpload(network: strongSelf.network, postbox: strongSelf.postbox, auxiliaryMethods: strongSelf.auxiliaryMethods, transformOutgoingMessageMedia: strongSelf.transformOutgoingMessageMedia, messageMediaPreuploadManager: strongSelf.messageMediaPreuploadManager, revalidationContext: strongSelf.revalidationContext, forceReupload: messageContext.forcedReuploadOnce, isGrouped: message.groupingKey != nil, message: message)
let contentToUpload = messageContentToUpload(accountPeerId: strongSelf.accountPeerId, network: strongSelf.network, postbox: strongSelf.postbox, auxiliaryMethods: strongSelf.auxiliaryMethods, transformOutgoingMessageMedia: strongSelf.transformOutgoingMessageMedia, messageMediaPreuploadManager: strongSelf.messageMediaPreuploadManager, revalidationContext: strongSelf.revalidationContext, forceReupload: messageContext.forcedReuploadOnce, isGrouped: message.groupingKey != nil, message: message)
messageContext.contentType = contentToUpload.type
switch contentToUpload {
case let .immediate(result, type):

View File

@ -251,6 +251,7 @@ public enum AnyMediaReference: Equatable {
case avatarList(peer: PeerReference, media: Media)
case attachBot(peer: PeerReference, media: Media)
case customEmoji(media: Media)
case story(peer: PeerReference, id: Int32, media: Media)
public static func ==(lhs: AnyMediaReference, rhs: AnyMediaReference) -> Bool {
switch lhs {
@ -302,6 +303,12 @@ public enum AnyMediaReference: Equatable {
} else {
return false
}
case let .story(lhsPeer, lhsId, lhsMedia):
if case let .story(rhsPeer, rhsId, rhsMedia) = rhs, lhsPeer == rhsPeer, lhsId == rhsId, lhsMedia.isEqual(to: rhsMedia) {
return true
} else {
return false
}
}
}
@ -323,6 +330,8 @@ public enum AnyMediaReference: Equatable {
return nil
case .customEmoji:
return nil
case .story:
return nil
}
}
@ -360,6 +369,10 @@ public enum AnyMediaReference: Equatable {
if let media = media as? T {
return .customEmoji(media: media)
}
case let .story(peer, id, media):
if let media = media as? T {
return .story(peer: peer, id: id, media: media)
}
}
return nil
}
@ -382,6 +395,8 @@ public enum AnyMediaReference: Equatable {
return media
case let .customEmoji(media):
return media
case let .story(_, _, media):
return media
}
}
@ -462,6 +477,7 @@ public enum MediaReference<T: Media> {
case avatarList
case attachBot
case customEmoji
case story
}
case standalone(media: T)
@ -472,6 +488,7 @@ public enum MediaReference<T: Media> {
case avatarList(peer: PeerReference, media: T)
case attachBot(peer: PeerReference, media: T)
case customEmoji(media: T)
case story(peer: PeerReference, id: Int32, media: T)
public init?(decoder: PostboxDecoder) {
guard let caseIdValue = decoder.decodeOptionalInt32ForKey("_r"), let caseId = CodingCase(rawValue: caseIdValue) else {
@ -523,6 +540,13 @@ public enum MediaReference<T: Media> {
return nil
}
self = .customEmoji(media: media)
case .story:
let peer = decoder.decodeObjectForKey("pr", decoder: { PeerReference(decoder: $0) }) as! PeerReference
guard let media = decoder.decodeObjectForKey("m") as? T else {
return nil
}
let id = decoder.decodeInt32ForKey("sid", orElse: 0)
self = .story(peer: peer, id: id, media: media)
}
}
@ -557,6 +581,11 @@ public enum MediaReference<T: Media> {
case let .customEmoji(media):
encoder.encodeInt32(CodingCase.customEmoji.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m")
case let .story(peer, id, media):
encoder.encodeInt32(CodingCase.story.rawValue, forKey: "_r")
encoder.encodeObject(peer, forKey: "pr")
encoder.encodeInt32(id, forKey: "sid")
encoder.encodeObject(media, forKey: "m")
}
}
@ -578,6 +607,8 @@ public enum MediaReference<T: Media> {
return .attachBot(peer: peer, media: media)
case let .customEmoji(media):
return .customEmoji(media: media)
case let .story(peer, id, media):
return .story(peer: peer, id: id, media: media)
}
}
@ -603,6 +634,8 @@ public enum MediaReference<T: Media> {
return media
case let .customEmoji(media):
return media
case let .story(_, _, media):
return media
}
}

View File

@ -255,6 +255,17 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
}
}
extension MessageTextEntity {
var associatedPeerIds: [PeerId] {
switch self.type {
case let .TextMention(peerId):
return [peerId]
default:
return []
}
}
}
public class TextEntitiesMessageAttribute: MessageAttribute, Equatable {
public let entities: [MessageTextEntity]

View File

@ -24,7 +24,10 @@ public struct EngineStoryPrivacy: Equatable {
}
}
func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, privacy: EngineStoryPrivacy) -> Signal<Never, NoError> {
func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, text: String, entities: [MessageTextEntity], privacy: EngineStoryPrivacy) -> Signal<Never, NoError> {
let originalMedia: Media
let contentToUpload: MessageContentToUpload
switch media {
case let .image(dimensions, data):
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
@ -38,8 +41,10 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, priva
partialReference: nil,
flags: []
)
originalMedia = imageMedia
let contentToUpload = messageContentToUpload(
contentToUpload = messageContentToUpload(
accountPeerId: account.peerId,
network: account.network,
postbox: account.postbox,
auxiliaryMethods: account.auxiliaryMethods,
@ -54,94 +59,6 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, priva
text: "",
media: [imageMedia]
)
let contentSignal: Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>
switch contentToUpload {
case let .immediate(result, _):
contentSignal = .single(result)
case let .signal(signal, _):
contentSignal = signal
}
return contentSignal
|> map(Optional.init)
|> `catch` { _ -> Signal<PendingMessageUploadedContentResult?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<Never, NoError> in
return account.postbox.transaction { transaction -> Signal<Never, NoError> in
var privacyRules: [Api.InputPrivacyRule]
switch privacy.base {
case .everyone:
privacyRules = [.inputPrivacyValueAllowAll]
case .contacts:
privacyRules = [.inputPrivacyValueAllowContacts]
case .closeFriends:
privacyRules = [.inputPrivacyValueAllowCloseFriends]
}
var privacyUsers: [Api.InputUser] = []
var privacyChats: [Int64] = []
for peerId in privacy.additionallyIncludePeers {
if let peer = transaction.getPeer(peerId) {
if let _ = peer as? TelegramUser {
if let inputUser = apiInputUser(peer) {
privacyUsers.append(inputUser)
}
} else if peer is TelegramGroup || peer is TelegramChannel {
privacyChats.append(peer.id.id._internalGetInt64Value())
}
}
}
if !privacyUsers.isEmpty {
privacyRules.append(.inputPrivacyValueAllowUsers(users: privacyUsers))
}
if !privacyChats.isEmpty {
privacyRules.append(.inputPrivacyValueAllowChatParticipants(chats: privacyChats))
}
switch result {
case let .content(content):
switch content.content {
case let .media(inputMedia, _):
return account.network.request(Api.functions.stories.sendStory(flags: 0, media: inputMedia, caption: nil, entities: nil, privacyRules: privacyRules))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
return .single(nil)
}
|> mapToSignal { updates -> Signal<Never, NoError> in
if let updates = updates {
for update in updates.allUpdates {
if case let .updateStories(stories) = update {
switch stories {
case .userStories(let userId, let apiStories), .userStoriesSlice(_, let userId, let apiStories):
if PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) == account.peerId, apiStories.count == 1 {
switch apiStories[0] {
case let .storyItem(_, _, _, _, _, media, _, _, _):
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, account.peerId)
if let parsedMedia = parsedMedia {
applyMediaResourceChanges(from: imageMedia, to: parsedMedia, postbox: account.postbox, force: false)
}
default:
break
}
}
}
}
}
account.stateManager.addUpdates(updates)
}
return .complete()
}
default:
return .complete()
}
default:
return .complete()
}
}
|> switchToLatest
}
case let .video(dimensions, duration, resource):
let fileMedia = TelegramMediaFile(
fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: MediaId.Id.random(in: MediaId.Id.min ... MediaId.Id.max)),
@ -156,8 +73,10 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, priva
TelegramMediaFileAttribute.Video(duration: duration, size: dimensions, flags: .supportsStreaming)
]
)
originalMedia = fileMedia
let contentToUpload = messageContentToUpload(
contentToUpload = messageContentToUpload(
accountPeerId: account.peerId,
network: account.network,
postbox: account.postbox,
auxiliaryMethods: account.auxiliaryMethods,
@ -172,94 +91,124 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, priva
text: "",
media: [fileMedia]
)
let contentSignal: Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>
switch contentToUpload {
case let .immediate(result, _):
contentSignal = .single(result)
case let .signal(signal, _):
contentSignal = signal
}
}
return contentSignal
|> map(Optional.init)
|> `catch` { _ -> Signal<PendingMessageUploadedContentResult?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<Never, NoError> in
return account.postbox.transaction { transaction -> Signal<Never, NoError> in
var privacyRules: [Api.InputPrivacyRule]
switch privacy.base {
case .everyone:
privacyRules = [.inputPrivacyValueAllowAll]
case .contacts:
privacyRules = [.inputPrivacyValueAllowContacts]
case .closeFriends:
privacyRules = [.inputPrivacyValueAllowCloseFriends]
}
var privacyUsers: [Api.InputUser] = []
var privacyChats: [Int64] = []
for peerId in privacy.additionallyIncludePeers {
if let peer = transaction.getPeer(peerId) {
if let _ = peer as? TelegramUser {
if let inputUser = apiInputUser(peer) {
privacyUsers.append(inputUser)
}
} else if peer is TelegramGroup || peer is TelegramChannel {
privacyChats.append(peer.id.id._internalGetInt64Value())
let contentSignal: Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>
switch contentToUpload {
case let .immediate(result, _):
contentSignal = .single(result)
case let .signal(signal, _):
contentSignal = signal
}
return contentSignal
|> map(Optional.init)
|> `catch` { _ -> Signal<PendingMessageUploadedContentResult?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<Never, NoError> in
return account.postbox.transaction { transaction -> Signal<Never, NoError> in
var privacyRules: [Api.InputPrivacyRule]
switch privacy.base {
case .everyone:
privacyRules = [.inputPrivacyValueAllowAll]
case .contacts:
privacyRules = [.inputPrivacyValueAllowContacts]
case .closeFriends:
privacyRules = [.inputPrivacyValueAllowCloseFriends]
}
var privacyUsers: [Api.InputUser] = []
var privacyChats: [Int64] = []
for peerId in privacy.additionallyIncludePeers {
if let peer = transaction.getPeer(peerId) {
if let _ = peer as? TelegramUser {
if let inputUser = apiInputUser(peer) {
privacyUsers.append(inputUser)
}
} else if peer is TelegramGroup || peer is TelegramChannel {
privacyChats.append(peer.id.id._internalGetInt64Value())
}
}
if !privacyUsers.isEmpty {
privacyRules.append(.inputPrivacyValueAllowUsers(users: privacyUsers))
}
if !privacyChats.isEmpty {
privacyRules.append(.inputPrivacyValueAllowChatParticipants(chats: privacyChats))
}
switch result {
case let .content(content):
switch content.content {
case let .media(inputMedia, _):
return account.network.request(Api.functions.stories.sendStory(flags: 0, media: inputMedia, caption: nil, entities: nil, privacyRules: privacyRules))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
return .single(nil)
}
if !privacyUsers.isEmpty {
privacyRules.append(.inputPrivacyValueAllowUsers(users: privacyUsers))
}
if !privacyChats.isEmpty {
privacyRules.append(.inputPrivacyValueAllowChatParticipants(chats: privacyChats))
}
switch result {
case let .content(content):
switch content.content {
case let .media(inputMedia, _):
var flags: Int32 = 0
var apiCaption: String?
var apiEntities: [Api.MessageEntity]?
if !text.isEmpty {
flags |= 1 << 0
apiCaption = text
if !entities.isEmpty {
flags |= 1 << 1
var associatedPeers: [PeerId: Peer] = [:]
for entity in entities {
for entityPeerId in entity.associatedPeerIds {
if let peer = transaction.getPeer(entityPeerId) {
associatedPeers[peer.id] = peer
}
}
}
apiEntities = apiEntitiesFromMessageTextEntities(entities, associatedPeers: SimpleDictionary(associatedPeers))
}
|> mapToSignal { updates -> Signal<Never, NoError> in
if let updates = updates {
for update in updates.allUpdates {
if case let .updateStories(stories) = update {
switch stories {
case .userStories(let userId, let apiStories), .userStoriesSlice(_, let userId, let apiStories):
if PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) == account.peerId, apiStories.count == 1 {
switch apiStories[0] {
case let .storyItem(_, _, _, _, _, media, _, _, _):
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, account.peerId)
if let parsedMedia = parsedMedia {
applyMediaResourceChanges(from: fileMedia, to: parsedMedia, postbox: account.postbox, force: true)
}
default:
break
}
return account.network.request(Api.functions.stories.sendStory(
flags: flags,
media: inputMedia,
caption: apiCaption,
entities: apiEntities,
privacyRules: privacyRules
))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
return .single(nil)
}
|> mapToSignal { updates -> Signal<Never, NoError> in
if let updates = updates {
for update in updates.allUpdates {
if case let .updateStories(stories) = update {
switch stories {
case .userStories(let userId, let apiStories), .userStoriesSlice(_, let userId, let apiStories):
if PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) == account.peerId, apiStories.count == 1 {
switch apiStories[0] {
case let .storyItem(_, _, _, _, _, media, _, _):
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, account.peerId)
if let parsedMedia = parsedMedia {
applyMediaResourceChanges(from: originalMedia, to: parsedMedia, postbox: account.postbox, force: false)
}
default:
break
}
}
}
}
account.stateManager.addUpdates(updates)
}
return .complete()
account.stateManager.addUpdates(updates)
}
default:
return .complete()
}
default:
return .complete()
}
default:
return .complete()
}
|> switchToLatest
}
|> switchToLatest
}
}
@ -291,3 +240,217 @@ func _internal_markStoryAsSeen(account: Account, peerId: PeerId, id: Int32) -> S
|> ignoreValues
}
}
extension Api.StoryItem {
var id: Int32 {
switch self {
case let .storyItem(_, id, _, _, _, _, _, _):
return id
case let .storyItemDeleted(id):
return id
}
}
}
func _internal_parseApiStoryItem(transaction: Transaction, peerId: PeerId, apiStory: Api.StoryItem) -> StoryListContext.Item? {
switch apiStory {
case let .storyItem(flags, id, date, caption, entities, media, privacy, views):
let _ = flags
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let parsedMedia = parsedMedia {
var parsedPrivacy: EngineStoryPrivacy?
if let privacy = privacy {
var base: EngineStoryPrivacy.Base = .everyone
var additionalPeerIds: [EnginePeer.Id] = []
for rule in privacy {
switch rule {
case .privacyValueAllowAll:
base = .everyone
case .privacyValueAllowContacts:
base = .contacts
case .privacyValueAllowCloseFriends:
base = .closeFriends
case let .privacyValueAllowUsers(users):
for id in users {
additionalPeerIds.append(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(id)))
}
case let .privacyValueAllowChatParticipants(chats):
for id in chats {
if let peer = transaction.getPeer(EnginePeer.Id(namespace: Namespaces.Peer.CloudGroup, id: EnginePeer.Id.Id._internalFromInt64Value(id))) {
additionalPeerIds.append(peer.id)
} else if let peer = transaction.getPeer(EnginePeer.Id(namespace: Namespaces.Peer.CloudChannel, id: EnginePeer.Id.Id._internalFromInt64Value(id))) {
additionalPeerIds.append(peer.id)
}
}
default:
break
}
}
parsedPrivacy = EngineStoryPrivacy(base: base, additionallyIncludePeers: additionalPeerIds)
}
let item = StoryListContext.Item(
id: id,
timestamp: date,
media: EngineMedia(parsedMedia),
text: caption ?? "",
entities: entities.flatMap { entities in return messageTextEntitiesFromApiEntities(entities) } ?? [],
views: views.flatMap { _internal_parseApiStoryViews(transaction: transaction, views: $0) },
privacy: parsedPrivacy
)
return item
} else {
return nil
}
case .storyItemDeleted:
return nil
}
}
func _internal_parseApiStoryViews(transaction: Transaction, views: Api.StoryViews) -> StoryListContext.Views {
switch views {
case let .storyViews(recentViewers, viewsCount):
return StoryListContext.Views(seenCount: Int(viewsCount), seenPeers: recentViewers.compactMap { id -> EnginePeer? in
return transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id))).flatMap(EnginePeer.init)
})
}
}
func _internal_getStoryById(accountPeerId: PeerId, postbox: Postbox, network: Network, peer: PeerReference, id: Int32) -> Signal<StoryListContext.Item?, NoError> {
guard let inputUser = peer.inputUser else {
return .single(nil)
}
return network.request(Api.functions.stories.getStoriesByID(userId: inputUser, id: [id]))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.stories.Stories?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<StoryListContext.Item?, NoError> in
guard let result else {
return .single(nil)
}
return postbox.transaction { transaction -> StoryListContext.Item? in
switch result {
case let .stories(_, stories, users):
var peers: [Peer] = []
var peerPresences: [PeerId: Api.User] = [:]
for user in users {
let telegramUser = TelegramUser(user: user)
peers.append(telegramUser)
peerPresences[telegramUser.id] = user
}
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated
})
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences)
return stories.first.flatMap { _internal_parseApiStoryItem(transaction: transaction, peerId: peer.id, apiStory: $0) }
}
}
}
}
public final class StoryViewList {
public final class Item {
public let peer: EnginePeer
public let timestamp: Int32
public init(peer: EnginePeer, timestamp: Int32) {
self.peer = peer
self.timestamp = timestamp
}
}
public let items: [Item]
public let totalCount: Int
public init(items: [Item], totalCount: Int) {
self.items = items
self.totalCount = totalCount
}
}
func _internal_getStoryViewList(account: Account, id: Int32, offsetTimestamp: Int32?, offsetPeerId: PeerId?, limit: Int) -> Signal<StoryViewList?, NoError> {
return account.network.request(Api.functions.stories.getStoryViewsList(id: id, offsetDate: offsetTimestamp ?? 0, offsetId: offsetPeerId?.id._internalGetInt64Value() ?? 0, limit: Int32(limit)))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.stories.StoryViewsList?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<StoryViewList?, NoError> in
guard let result else {
return .single(nil)
}
return account.postbox.transaction { transaction -> StoryViewList? in
switch result {
case let .storyViewsList(count, views, users):
var peers: [Peer] = []
var peerPresences: [PeerId: Api.User] = [:]
for user in users {
let telegramUser = TelegramUser(user: user)
peers.append(telegramUser)
peerPresences[telegramUser.id] = user
}
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated
})
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
var items: [StoryViewList.Item] = []
for view in views {
switch view {
case let .storyView(userId, date):
if let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))) {
items.append(StoryViewList.Item(peer: EnginePeer(peer), timestamp: date))
}
}
}
return StoryViewList(items: items, totalCount: Int(count))
}
}
}
}
func _internal_getStoryViews(account: Account, ids: [Int32]) -> Signal<[Int32: StoryListContext.Views], NoError> {
return account.network.request(Api.functions.stories.getStoriesViews(id: ids))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.stories.StoryViews?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<[Int32: StoryListContext.Views], NoError> in
guard let result else {
return .single([:])
}
return account.postbox.transaction { transaction -> [Int32: StoryListContext.Views] in
var parsedViews: [Int32: StoryListContext.Views] = [:]
switch result {
case let .storyViews(views, users):
var peers: [Peer] = []
var peerPresences: [PeerId: Api.User] = [:]
for user in users {
let telegramUser = TelegramUser(user: user)
peers.append(telegramUser)
peerPresences[telegramUser.id] = user
}
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated
})
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
for i in 0 ..< views.count {
if i < ids.count {
parsedViews[ids[i]] = _internal_parseApiStoryViews(transaction: transaction, views: views[i])
}
}
}
return parsedViews
}
}
}

View File

@ -4,7 +4,7 @@ import TelegramApi
import SwiftSignalKit
enum InternalStoryUpdate {
case deleted(Int32)
case deleted(peerId: PeerId, id: Int32)
case added(peerId: PeerId, item: StoryListContext.Item)
case read(peerId: PeerId, maxId: Int32)
}
@ -15,20 +15,32 @@ public final class StoryListContext {
case peer(EnginePeer.Id)
}
public struct Views: Equatable {
public var seenCount: Int
public var seenPeers: [EnginePeer]
public init(seenCount: Int, seenPeers: [EnginePeer]) {
self.seenCount = seenCount
self.seenPeers = seenPeers
}
}
public final class Item: Equatable {
public let id: Int32
public let timestamp: Int32
public let media: EngineMedia
public let seenCount: Int
public let seenPeers: [EnginePeer]
public let text: String
public let entities: [MessageTextEntity]
public let views: Views?
public let privacy: EngineStoryPrivacy?
public init(id: Int32, timestamp: Int32, media: EngineMedia, seenCount: Int, seenPeers: [EnginePeer], privacy: EngineStoryPrivacy?) {
public init(id: Int32, timestamp: Int32, media: EngineMedia, text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?) {
self.id = id
self.timestamp = timestamp
self.media = media
self.seenCount = seenCount
self.seenPeers = seenPeers
self.text = text
self.entities = entities
self.views = views
self.privacy = privacy
}
@ -42,10 +54,13 @@ public final class StoryListContext {
if lhs.media != rhs.media {
return false
}
if lhs.seenCount != rhs.seenCount {
if lhs.text != rhs.text {
return false
}
if lhs.seenPeers != rhs.seenPeers {
if lhs.entities != rhs.entities {
return false
}
if lhs.views != rhs.views {
return false
}
if lhs.privacy != rhs.privacy {
@ -207,18 +222,20 @@ public final class StoryListContext {
for update in updates {
switch update {
case let .deleted(id):
case let .deleted(peerId, id):
for i in 0 ..< itemSets.count {
if let index = itemSets[i].items.firstIndex(where: { $0.id == id }) {
var items = itemSets[i].items
items.remove(at: index)
itemSets[i] = PeerItemSet(
peerId: itemSets[i].peerId,
peer: itemSets[i].peer,
maxReadId: itemSets[i].maxReadId,
items: items,
totalCount: items.count
)
if itemSets[i].peerId == peerId {
if let index = itemSets[i].items.firstIndex(where: { $0.id == id }) {
var items = itemSets[i].items
items.remove(at: index)
itemSets[i] = PeerItemSet(
peerId: itemSets[i].peerId,
peer: itemSets[i].peer,
maxReadId: itemSets[i].maxReadId,
items: items,
totalCount: items.count
)
}
}
}
case let .added(peerId, item):
@ -232,18 +249,7 @@ public final class StoryListContext {
items.remove(at: index)
}
if peerId == self.account.peerId {
items.append(Item(
id: item.id,
timestamp: item.timestamp,
media: item.media,
seenCount: item.seenCount,
seenPeers: item.seenPeers,
privacy: item.privacy
))
} else {
items.append(item)
}
items.append(item)
items.sort(by: { lhsItem, rhsItem in
if lhsItem.timestamp != rhsItem.timestamp {
@ -367,68 +373,13 @@ public final class StoryListContext {
let peerId = id
for apiStory in apiStories {
switch apiStory {
case let .storyItem(flags, id, date, _, _, media, privacy, recentViewers, viewCount):
let _ = flags
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let parsedMedia = parsedMedia {
var seenPeers: [EnginePeer] = []
if let recentViewers = recentViewers {
for id in recentViewers {
if let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id))) {
seenPeers.append(EnginePeer(peer))
}
}
}
var parsedPrivacy: EngineStoryPrivacy?
if let privacy = privacy {
var base: EngineStoryPrivacy.Base = .everyone
var additionalPeerIds: [EnginePeer.Id] = []
for rule in privacy {
switch rule {
case .privacyValueAllowAll:
base = .everyone
case .privacyValueAllowContacts:
base = .contacts
case .privacyValueAllowCloseFriends:
base = .closeFriends
case let .privacyValueAllowUsers(users):
for id in users {
additionalPeerIds.append(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(id)))
}
case let .privacyValueAllowChatParticipants(chats):
for id in chats {
if let peer = transaction.getPeer(EnginePeer.Id(namespace: Namespaces.Peer.CloudGroup, id: EnginePeer.Id.Id._internalFromInt64Value(id))) {
additionalPeerIds.append(peer.id)
} else if let peer = transaction.getPeer(EnginePeer.Id(namespace: Namespaces.Peer.CloudChannel, id: EnginePeer.Id.Id._internalFromInt64Value(id))) {
additionalPeerIds.append(peer.id)
}
}
default:
break
}
}
parsedPrivacy = EngineStoryPrivacy(base: base, additionallyIncludePeers: additionalPeerIds)
}
let item = StoryListContext.Item(
id: id,
timestamp: date,
media: EngineMedia(parsedMedia),
seenCount: viewCount.flatMap(Int.init) ?? 0,
seenPeers: seenPeers,
privacy: parsedPrivacy
)
if !parsedItemSets.isEmpty && parsedItemSets[parsedItemSets.count - 1].peerId == peerId {
parsedItemSets[parsedItemSets.count - 1].items.append(item)
parsedItemSets[parsedItemSets.count - 1].totalCount = parsedItemSets[parsedItemSets.count - 1].items.count
} else {
parsedItemSets.append(StoryListContext.PeerItemSet(peerId: peerId, peer: transaction.getPeer(peerId).flatMap(EnginePeer.init), maxReadId: 0, items: [item], totalCount: 1))
}
if let item = _internal_parseApiStoryItem(transaction: transaction, peerId: peerId, apiStory: apiStory) {
if !parsedItemSets.isEmpty && parsedItemSets[parsedItemSets.count - 1].peerId == peerId {
parsedItemSets[parsedItemSets.count - 1].items.append(item)
parsedItemSets[parsedItemSets.count - 1].totalCount = parsedItemSets[parsedItemSets.count - 1].items.count
} else {
parsedItemSets.append(StoryListContext.PeerItemSet(peerId: peerId, peer: transaction.getPeer(peerId).flatMap(EnginePeer.init), maxReadId: 0, items: [item], totalCount: 1))
}
case .storyItemDeleted:
break
}
}
@ -450,10 +401,10 @@ public final class StoryListContext {
}
}
func upload(media: EngineStoryInputMedia, privacy: EngineStoryPrivacy) {
func upload(media: EngineStoryInputMedia, text: String, entities: [MessageTextEntity], privacy: EngineStoryPrivacy) {
let uploadContext = UploadContext()
self.uploadContexts.append(uploadContext)
uploadContext.disposable.set((_internal_uploadStory(account: self.account, media: media, privacy: privacy)
uploadContext.disposable.set((_internal_uploadStory(account: self.account, media: media, text: text, entities: entities, privacy: privacy)
|> deliverOn(self.queue)).start(next: { _ in
}, completed: { [weak self, weak uploadContext] in
guard let `self` = self, let uploadContext = uploadContext else {
@ -530,73 +481,18 @@ public final class StoryListContext {
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(apiUserId))
for apiStory in apiStories {
switch apiStory {
case let .storyItem(flags, id, date, _, _, media, privacy, recentViewers, viewCount):
let _ = flags
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let parsedMedia = parsedMedia {
var seenPeers: [EnginePeer] = []
if let recentViewers = recentViewers {
for id in recentViewers {
if let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id))) {
seenPeers.append(EnginePeer(peer))
}
}
}
var parsedPrivacy: EngineStoryPrivacy?
if let privacy = privacy {
var base: EngineStoryPrivacy.Base = .everyone
var additionalPeerIds: [EnginePeer.Id] = []
for rule in privacy {
switch rule {
case .privacyValueAllowAll:
base = .everyone
case .privacyValueAllowContacts:
base = .contacts
case .privacyValueAllowCloseFriends:
base = .closeFriends
case let .privacyValueAllowUsers(users):
for id in users {
additionalPeerIds.append(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(id)))
}
case let .privacyValueAllowChatParticipants(chats):
for id in chats {
if let peer = transaction.getPeer(EnginePeer.Id(namespace: Namespaces.Peer.CloudGroup, id: EnginePeer.Id.Id._internalFromInt64Value(id))) {
additionalPeerIds.append(peer.id)
} else if let peer = transaction.getPeer(EnginePeer.Id(namespace: Namespaces.Peer.CloudChannel, id: EnginePeer.Id.Id._internalFromInt64Value(id))) {
additionalPeerIds.append(peer.id)
}
}
default:
break
}
}
parsedPrivacy = EngineStoryPrivacy(base: base, additionallyIncludePeers: additionalPeerIds)
}
let item = StoryListContext.Item(
id: id,
timestamp: date,
media: EngineMedia(parsedMedia),
seenCount: viewCount.flatMap(Int.init) ?? 0,
seenPeers: seenPeers,
privacy: parsedPrivacy
)
if !parsedItemSets.isEmpty && parsedItemSets[parsedItemSets.count - 1].peerId == peerId {
parsedItemSets[parsedItemSets.count - 1].items.append(item)
} else {
parsedItemSets.append(StoryListContext.PeerItemSet(
peerId: peerId,
peer: transaction.getPeer(peerId).flatMap(EnginePeer.init),
maxReadId: 0,
items: [item],
totalCount: apiTotalCount.flatMap(Int.init)
))
}
if let item = _internal_parseApiStoryItem(transaction: transaction, peerId: peerId, apiStory: apiStory) {
if !parsedItemSets.isEmpty && parsedItemSets[parsedItemSets.count - 1].peerId == peerId {
parsedItemSets[parsedItemSets.count - 1].items.append(item)
} else {
parsedItemSets.append(StoryListContext.PeerItemSet(
peerId: peerId,
peer: transaction.getPeer(peerId).flatMap(EnginePeer.init),
maxReadId: 0,
items: [item],
totalCount: apiTotalCount.flatMap(Int.init)
))
}
case .storyItemDeleted:
break
}
}
}
@ -721,9 +617,9 @@ public final class StoryListContext {
}
}
public func upload(media: EngineStoryInputMedia, privacy: EngineStoryPrivacy) {
public func upload(media: EngineStoryInputMedia, text: String, entities: [MessageTextEntity], privacy: EngineStoryPrivacy) {
self.impl.with { impl in
impl.upload(media: media, privacy: privacy)
impl.upload(media: media, text: text, entities: entities, privacy: privacy)
}
}
}

View File

@ -577,8 +577,8 @@ public extension TelegramEngine {
return StoryListContext(account: self.account, scope: .peer(id))
}
public func uploadStory(media: EngineStoryInputMedia, privacy: EngineStoryPrivacy) -> Signal<Never, NoError> {
return _internal_uploadStory(account: self.account, media: media, privacy: privacy)
public func uploadStory(media: EngineStoryInputMedia, text: String, entities: [MessageTextEntity], privacy: EngineStoryPrivacy) -> Signal<Never, NoError> {
return _internal_uploadStory(account: self.account, media: media, text: text, entities: entities, privacy: privacy)
}
public func deleteStory(id: Int32) -> Signal<Never, NoError> {
@ -588,5 +588,9 @@ public extension TelegramEngine {
public func markStoryAsSeen(peerId: EnginePeer.Id, id: Int32) -> Signal<Never, NoError> {
return _internal_markStoryAsSeen(account: self.account, peerId: peerId, id: id)
}
public func getStoryViewList(account: Account, id: Int32, offsetTimestamp: Int32?, offsetPeerId: PeerId?, limit: Int) -> Signal<StoryViewList?, NoError> {
return _internal_getStoryViewList(account: account, id: id, offsetTimestamp: offsetTimestamp, offsetPeerId: offsetPeerId, limit: limit)
}
}
}

View File

@ -268,10 +268,11 @@ func _internal_saveNotificationSound(account: Account, file: FileMediaReference,
guard let resource = file.media.resource as? CloudDocumentMediaResource else {
return .fail(.generic)
}
let accountPeerId = account.peerId
return account.network.request(Api.functions.account.saveRingtone(id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)), unsave: unsave ? .boolTrue : .boolFalse))
|> `catch` { error -> Signal<Api.account.SavedRingtone, MTRpcError> in
if error.errorDescription == "FILE_REFERENCE_EXPIRED" {
return revalidateMediaResourceReference(postbox: account.postbox, network: account.network, revalidationContext: account.mediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: file.abstract.resourceReference(file.media.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: file.media.resource)
return revalidateMediaResourceReference(accountPeerId: accountPeerId, postbox: account.postbox, network: account.network, revalidationContext: account.mediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: file.abstract.resourceReference(file.media.resource), preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: file.media.resource)
|> mapError { _ -> MTRpcError in
return MTRpcError(errorCode: 500, errorDescription: "Internal")
}

View File

@ -353,7 +353,7 @@ public extension TelegramEngine {
|> mapToSignal { datacenterId -> Signal<EngineMediaResource.Fetch.Result, EngineMediaResource.Fetch.Error> in
let resource = AlbumCoverResource(datacenterId: Int(datacenterId), file: file, title: title, performer: performer, isThumbnail: isThumbnail)
return multipartFetch(postbox: self.account.postbox, network: self.account.network, mediaReferenceRevalidationContext: self.account.mediaReferenceRevalidationContext, networkStatsContext: self.account.networkStatsContext, resource: resource, datacenterId: Int(datacenterId), size: nil, intervals: .single([(0 ..< Int64.max, .default)]), parameters: MediaResourceFetchParameters(
return multipartFetch(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, mediaReferenceRevalidationContext: self.account.mediaReferenceRevalidationContext, networkStatsContext: self.account.networkStatsContext, resource: resource, datacenterId: Int(datacenterId), size: nil, intervals: .single([(0 ..< Int64.max, .default)]), parameters: MediaResourceFetchParameters(
tag: nil,
info: TelegramCloudMediaResourceFetchInfo(
reference: MediaResourceReference.standalone(resource: resource),

View File

@ -75,9 +75,10 @@ func _internal_stickerPacksAttachedToMedia(account: Account, media: AnyMediaRefe
} else {
return .single([])
}
let accountPeerId = account.peerId
return account.network.request(Api.functions.messages.getAttachedStickers(media: inputMedia))
|> `catch` { _ -> Signal<[Api.StickerSetCovered], MTRpcError> in
return revalidateMediaResourceReference(postbox: account.postbox, network: account.network, revalidationContext: account.mediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: resourceReference, preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: resourceReference.resource)
return revalidateMediaResourceReference(accountPeerId: accountPeerId, postbox: account.postbox, network: account.network, revalidationContext: account.mediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: resourceReference, preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: resourceReference.resource)
|> mapError { _ -> MTRpcError in
return MTRpcError(errorCode: 500, errorDescription: "Internal")
}

View File

@ -25,11 +25,11 @@ float doubleStep(float value, float lowerBound, float upperBound) {
return step(lowerBound, value) * (1.0 - step(upperBound, value));
}
float fieldFunction(float2 center, float2 position, float2 dimensions, float time) {
float fieldFunction(float2 center, float contentScale, float2 position, float2 dimensions, float time) {
float maxDimension = max(dimensions.x, dimensions.y);
float currentDistance = time * maxDimension;
float waveWidth = 100.0f * 3.0f;
float waveWidth = 100.0f * contentScale;
float d = distance(center, position);
@ -50,7 +50,8 @@ vertex RasterizerData rippleVertex
device const uint2 &center [[buffer(0)]],
device const uint2 &gridResolution [[buffer(1)]],
device const uint2 &resolution [[buffer(2)]],
device const float &time [[buffer(3)]]
device const float &time [[buffer(3)]],
device const float &contentScale [[buffer(4)]]
) {
uint triangleIndex = vid / 6;
uint vertexIndex = vid % 6;
@ -69,7 +70,7 @@ vertex RasterizerData rippleVertex
);
float2 texCoord = float2(position.x, 1.0 - position.y);
float zPosition = fieldFunction(float2(center), float2(position.x * dimensions.x, (1.0 - position.y) * dimensions.y), dimensions, time);
float zPosition = fieldFunction(float2(center), contentScale, float2(position.x * dimensions.x, (1.0 - position.y) * dimensions.y), dimensions, time);
zPosition *= 0.5f;
float leftEdgeDistance = abs(position.x);
@ -84,9 +85,6 @@ vertex RasterizerData rippleVertex
zPosition *= edgeDistance;
zPosition *= max(0.0, min(1.0, linearDecay(time, 0.7)));
if (zPosition <= 0.1) {
//zPosition = 0.0;
}
float3 camPosition = float3(0.0, 0.0f, 1.0f);
float3 camTarget = float3(0.0, 0.0, 0.0);
@ -147,6 +145,9 @@ fragment half4 rippleFragment(
float4 rgb = float4(texture.sample(textureSampler, texCoord));
float4 out = float4(rgb.xyz, 1.0);
/*out.r = 0.0;
out.g = 0.0;
out.b = 1.0;*/
out.a = 1.0 - step(in.visibilityFraction, 0.5);

View File

@ -4,6 +4,9 @@ import MetalKit
import simd
public final class RippleEffectView: MTKView {
private let centerLocation: CGPoint
private let completion: () -> Void
private let textureLoader: MTKTextureLoader
private let commandQueue: MTLCommandQueue
private let drawPassthroughPipelineState: MTLRenderPipelineState
@ -11,7 +14,7 @@ public final class RippleEffectView: MTKView {
private var viewportDimensions = CGSize(width: 1, height: 1)
private var time: Float = 0.0
private var startTime: Double?
private var lastUpdateTimestamp: Double?
@ -21,7 +24,10 @@ public final class RippleEffectView: MTKView {
}
}
public init?(test: Bool) {
public init?(centerLocation: CGPoint, completion: @escaping () -> Void) {
self.centerLocation = centerLocation
self.completion = completion
let mainBundle = Bundle(for: RippleEffectView.self)
guard let path = mainBundle.path(forResource: "FullScreenEffectViewBundle", ofType: "bundle") else {
@ -135,12 +141,21 @@ public final class RippleEffectView: MTKView {
}
private func redraw(drawable: MTLDrawable) {
if let lastUpdateTimestamp = self.lastUpdateTimestamp {
/*if let lastUpdateTimestamp = self.lastUpdateTimestamp {
if lastUpdateTimestamp + 1.0 < CACurrentMediaTime() {
self.updateImageFromSourceView()
}
} else {
self.updateImageFromSourceView()
}*/
let relativeTime: Double
let timestamp = CACurrentMediaTime()
if let startTime = self.startTime {
relativeTime = timestamp - startTime
} else {
self.startTime = timestamp
relativeTime = 0.0
}
guard let commandBuffer = self.commandQueue.makeCommandBuffer() else {
@ -160,20 +175,20 @@ public final class RippleEffectView: MTKView {
renderEncoder.setRenderPipelineState(self.drawPassthroughPipelineState)
let gridSize = 1000
var time = self.time.truncatingRemainder(dividingBy: 0.7)
//time = 0.6
self.time += (1.0 / 60.0) * 0.1
var time: Float = Float(min(relativeTime, 0.7))
var gridResolution = simd_uint2(UInt32(gridSize), UInt32(gridSize))
var resolution = simd_uint2(UInt32(viewportDimensions.width), UInt32(viewportDimensions.height))
var center = simd_uint2(200, 200);
var center = simd_uint2(UInt32(self.centerLocation.x * self.contentScaleFactor), UInt32(self.centerLocation.y * self.contentScaleFactor));
if let texture = self.texture {
var contentScale: Float = Float(self.contentScaleFactor)
renderEncoder.setVertexBytes(&center, length: MemoryLayout<simd_uint2>.size, index: 0)
renderEncoder.setVertexBytes(&gridResolution, length: MemoryLayout<simd_uint2>.size, index: 1)
renderEncoder.setVertexBytes(&resolution, length: MemoryLayout<simd_uint2>.size, index: 2)
renderEncoder.setVertexBytes(&time, length: MemoryLayout<Float>.size, index: 3)
renderEncoder.setVertexBytes(&contentScale, length: MemoryLayout<Float>.size, index: 4)
renderEncoder.setFragmentTexture(texture, index: 0)
@ -184,5 +199,11 @@ public final class RippleEffectView: MTKView {
commandBuffer.present(drawable)
commandBuffer.commit()
if relativeTime >= 0.7 {
//self.startTime = nil
self.isPaused = true
self.completion()
}
}
}

View File

@ -16,6 +16,9 @@ public enum StoryChatContent {
for itemSet in state.itemSets {
var items: [StoryContentItem] = []
guard let peer = itemSet.peer else {
continue
}
let peerId = itemSet.peerId
for item in itemSet.items {
@ -24,7 +27,7 @@ public enum StoryChatContent {
position: items.count,
component: AnyComponent(StoryItemContentComponent(
context: context,
peerId: peerId,
peer: peer,
item: item
)),
centerInfoComponent: AnyComponent(StoryAuthorInfoComponent(

View File

@ -16,12 +16,12 @@ final class StoryItemContentComponent: Component {
typealias EnvironmentType = StoryContentItem.Environment
let context: AccountContext
let peerId: EnginePeer.Id
let peer: EnginePeer
let item: StoryListContext.Item
init(context: AccountContext, peerId: EnginePeer.Id, item: StoryListContext.Item) {
init(context: AccountContext, peer: EnginePeer, item: StoryListContext.Item) {
self.context = context
self.peerId = peerId
self.peer = peer
self.item = item
}
@ -29,7 +29,7 @@ final class StoryItemContentComponent: Component {
if lhs.context !== rhs.context {
return false
}
if lhs.peerId != rhs.peerId {
if lhs.peer != rhs.peer {
return false
}
if lhs.item != rhs.item {
@ -144,7 +144,7 @@ final class StoryItemContentComponent: Component {
return
}
if case let .file(file) = currentMessageMedia {
if case let .file(file) = currentMessageMedia, let peerReference = PeerReference(component.peer._asPeer()) {
if self.videoNode == nil {
let videoNode = UniversalVideoNode(
postbox: component.context.account.postbox,
@ -154,7 +154,7 @@ final class StoryItemContentComponent: Component {
content: NativeVideoContent(
id: .message(0, file.fileId),
userLocation: .other,
fileReference: .standalone(media: file),
fileReference: .story(peer: peerReference, id: component.item.id, media: file),
imageReference: nil,
loopVideo: true,
enableSound: true,
@ -224,7 +224,7 @@ final class StoryItemContentComponent: Component {
if !self.markedAsSeen {
self.markedAsSeen = true
if let component = self.component {
let _ = component.context.engine.messages.markStoryAsSeen(peerId: component.peerId, id: component.item.id).start()
let _ = component.context.engine.messages.markStoryAsSeen(peerId: component.peer.id, id: component.item.id).start()
}
}
@ -293,7 +293,7 @@ final class StoryItemContentComponent: Component {
if !self.markedAsSeen {
self.markedAsSeen = true
if let component = self.component {
let _ = component.context.engine.messages.markStoryAsSeen(peerId: component.peerId, id: component.item.id).start()
let _ = component.context.engine.messages.markStoryAsSeen(peerId: component.peer.id, id: component.item.id).start()
}
}
}
@ -308,6 +308,8 @@ final class StoryItemContentComponent: Component {
self.state = state
self.environment = environment[StoryContentItem.Environment.self].value
let peerReference = PeerReference(component.peer._asPeer())
var messageMedia: EngineMedia?
switch component.item.media {
case let .image(image):
@ -324,7 +326,7 @@ final class StoryItemContentComponent: Component {
reloadMedia = true
}
if reloadMedia, let messageMedia {
if reloadMedia, let messageMedia, let peerReference {
var signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
var fetchSignal: Signal<Never, NoError>?
switch messageMedia {
@ -332,7 +334,7 @@ final class StoryItemContentComponent: Component {
signal = chatMessagePhoto(
postbox: component.context.account.postbox,
userLocation: .other,
photoReference: .standalone(media: image),
photoReference: .story(peer: peerReference, id: component.item.id, media: image),
synchronousLoad: true,
highQuality: true
)
@ -341,7 +343,7 @@ final class StoryItemContentComponent: Component {
mediaBox: component.context.account.postbox.mediaBox,
userLocation: .other,
userContentType: .image,
reference: ImageMediaReference.standalone(media: image).resourceReference(representation.resource)
reference: ImageMediaReference.story(peer: peerReference, id: component.item.id, media: image).resourceReference(representation.resource)
)
|> ignoreValues
|> `catch` { _ -> Signal<Never, NoError> in
@ -354,14 +356,14 @@ final class StoryItemContentComponent: Component {
signal = chatMessageVideo(
postbox: component.context.account.postbox,
userLocation: .other,
videoReference: .standalone(media: file),
videoReference: .story(peer: peerReference, id: component.item.id, media: file),
synchronousLoad: true
)
fetchSignal = fetchedMediaResource(
mediaBox: component.context.account.postbox.mediaBox,
userLocation: .other,
userContentType: .image,
reference: FileMediaReference.standalone(media: file).resourceReference(file.resource)
reference: FileMediaReference.story(peer: peerReference, id: component.item.id, media: file).resourceReference(file.resource)
)
|> ignoreValues
|> `catch` { _ -> Signal<Never, NoError> in

View File

@ -73,7 +73,7 @@ public final class StoryFooterPanelComponent: Component {
let avatarSpacing: CGFloat = 18.0
var peers: [EnginePeer] = []
if let seenPeers = component.storyItem?.seenPeers {
if let seenPeers = component.storyItem?.views?.seenPeers {
peers = Array(seenPeers.prefix(3))
}
let avatarsContent = self.avatarsContext.update(peers: peers, animated: false)
@ -86,11 +86,11 @@ public final class StoryFooterPanelComponent: Component {
}
let viewsText: String
if let storyItem = component.storyItem, storyItem.seenCount != 0 {
if storyItem.seenCount == 1 {
if let views = component.storyItem?.views, views.seenCount != 0 {
if views.seenCount == 1 {
viewsText = "1 view"
} else {
viewsText = "\(storyItem.seenCount) views"
viewsText = "\(views.seenCount) views"
}
} else {
viewsText = "No views yet"

View File

@ -372,18 +372,12 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
return
}
if let chatListController = self.chatListController as? ChatListControllerImpl, let storyListContext = chatListController.storyListContext {
storyListContext.upload(media: .image(dimensions: PixelDimensions(image.size), data: data), privacy: privacy)
storyListContext.upload(media: .image(dimensions: PixelDimensions(image.size), data: data), text: "", entities: [], privacy: privacy)
Queue.mainQueue().after(0.3, { [weak chatListController] in
chatListController?.animateStoryUploadRipple()
})
}
selectionController?.dismiss()
/*let _ = (context.engine.messages.uploadStory(media: )
|> deliverOnMainQueue).start(completed: {
guard let self else {
return
}
let _ = self
selectionController?.dismiss()
})*/
}
}
})
@ -391,18 +385,12 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
let resource = VideoLibraryMediaResource(localIdentifier: asset.localIdentifier, conversion: VideoLibraryMediaResourceConversion.passthrough)
if let chatListController = self.chatListController as? ChatListControllerImpl, let storyListContext = chatListController.storyListContext {
storyListContext.upload(media: .video(dimensions: PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight)), duration: Int(asset.duration), resource: resource), privacy: privacy)
storyListContext.upload(media: .video(dimensions: PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight)), duration: Int(asset.duration), resource: resource), text: "", entities: [], privacy: privacy)
Queue.mainQueue().after(0.3, { [weak chatListController] in
chatListController?.animateStoryUploadRipple()
})
}
selectionController?.dismiss()
/*let _ = (context.engine.messages.uploadStory(media: .video(dimensions: PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight)), duration: Int(asset.duration), resource: resource), privacy: privacy)
|> deliverOnMainQueue).start(completed: { [weak self] in
guard let self else {
return
}
let _ = self
selectionController?.dismiss()
})*/
default:
selectionController?.dismiss()
}