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

View File

@ -2304,6 +2304,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
self.chatListDisplayNode.clearHighlightAnimated(true) self.chatListDisplayNode.clearHighlightAnimated(true)
if let fullScreenEffectView = self.fullScreenEffectView {
self.fullScreenEffectView = nil
fullScreenEffectView.removeFromSuperview()
}
} }
func requestUpdateHeaderContent(transition: ContainedViewLayoutTransition) { func requestUpdateHeaderContent(transition: ContainedViewLayoutTransition) {
@ -2501,25 +2506,37 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
searchContentNode.updateListVisibleContentOffset(.known(0.0)) searchContentNode.updateListVisibleContentOffset(.known(0.0))
self.chatListDisplayNode.scrollToTop() self.chatListDisplayNode.scrollToTop()
} }
}
#if DEBUG && false
var fullScreenEffectView: RippleEffectView? public func animateStoryUploadRipple() {
if let current = self.fullScreenEffectView { if let componentView = self.headerContentView.view as? ChatListHeaderComponent.View {
fullScreenEffectView = current if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) {
self.view.window?.addSubview(current) let localRect = transitionView.convert(transitionView.bounds, to: self.view)
current.sourceView = self.view self.animateRipple(centerLocation: localRect.center)
} else {
if let value = RippleEffectView(test: false) {
fullScreenEffectView = value
self.fullScreenEffectView = value
self.view.window?.addSubview(value)
value.sourceView = self.view
} }
} }
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) { 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[1087454222] = { return Api.StickerSetCovered.parse_stickerSetFullCovered($0) }
dict[872932635] = { return Api.StickerSetCovered.parse_stickerSetMultiCovered($0) } dict[872932635] = { return Api.StickerSetCovered.parse_stickerSetMultiCovered($0) }
dict[2008112412] = { return Api.StickerSetCovered.parse_stickerSetNoCovered($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[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[1964978502] = { return Api.TextWithEntities.parse_textWithEntities($0) }
dict[-1609668650] = { return Api.Theme.parse_theme($0) } dict[-1609668650] = { return Api.Theme.parse_theme($0) }
dict[-94849324] = { return Api.ThemeSettings.parse_themeSettings($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[276907596] = { return Api.storage.FileType.parse_fileWebp($0) }
dict[1214632796] = { return Api.stories.AllStories.parse_allStories($0) } dict[1214632796] = { return Api.stories.AllStories.parse_allStories($0) }
dict[1340440049] = { return Api.stories.Stories.parse_stories($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[-79726676] = { return Api.stories.StoryViewsList.parse_storyViewsList($0) }
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) } dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) } dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) }
@ -1712,6 +1714,8 @@ public extension Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.StoryView: case let _1 as Api.StoryView:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.StoryViews:
_1.serialize(buffer, boxed)
case let _1 as Api.TextWithEntities: case let _1 as Api.TextWithEntities:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.Theme: case let _1 as Api.Theme:
@ -2020,6 +2024,8 @@ public extension Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.stories.Stories: case let _1 as Api.stories.Stories:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.stories.StoryViews:
_1.serialize(buffer, boxed)
case let _1 as Api.stories.StoryViewsList: case let _1 as Api.stories.StoryViewsList:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.updates.ChannelDifference: case let _1 as Api.updates.ChannelDifference:

View File

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

View File

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

View File

@ -307,6 +307,7 @@ private enum MediaReferenceRevalidationKey: Hashable {
case attachBot(peer: PeerReference) case attachBot(peer: PeerReference)
case notificationSoundList case notificationSoundList
case customEmoji(fileId: Int64) case customEmoji(fileId: Int64)
case story(peer: PeerReference, id: Int32)
} }
private final class MediaReferenceRevalidationItemContext { 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> { func notificationSoundList(postbox: Postbox, network: Network, background: Bool) -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> {
return self.genericItem(key: .notificationSoundList, background: background, request: { next, error in return self.genericItem(key: .notificationSoundList, background: background, request: { next, error in
return (requestNotificationSoundList(network: network, hash: 0) return (requestNotificationSoundList(network: network, hash: 0)
@ -682,7 +708,7 @@ struct RevalidatedMediaResource {
let updatedReference: MediaResourceReference? 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 var updatedReference = info.reference
if case let .media(media, resource) = updatedReference { if case let .media(media, resource) = updatedReference {
if case let .message(messageReference, mediaValue) = media { if case let .message(messageReference, mediaValue) = media {
@ -805,6 +831,14 @@ func revalidateMediaResourceReference(postbox: Postbox, network: Network, revali
} }
return .fail(.generic) 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): case let .standalone(media):
if let file = media as? TelegramMediaFile { if let file = media as? TelegramMediaFile {
for attribute in file.attributes { for attribute in file.attributes {

View File

@ -473,6 +473,7 @@ private final class MultipartFetchManager {
var completeSize: Int64? var completeSize: Int64?
var completeSizeReported = false var completeSizeReported = false
let accountPeerId: PeerId
let postbox: Postbox let postbox: Postbox
let network: Network let network: Network
let networkStatsContext: NetworkStatsContext? let networkStatsContext: NetworkStatsContext?
@ -505,7 +506,7 @@ private final class MultipartFetchManager {
private var fetchSpeedRecords: [FetchSpeedRecord] = [] private var fetchSpeedRecords: [FetchSpeedRecord] = []
private var totalFetchedByteCount: Int = 0 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.resource = resource
self.parameters = parameters self.parameters = parameters
self.consumerId = Int64.random(in: Int64.min ... Int64.max) 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.state = MultipartDownloadState(encryptionKey: encryptionKey, decryptedSize: decryptedSize)
self.accountPeerId = accountPeerId
self.postbox = postbox self.postbox = postbox
self.network = network self.network = network
self.networkStatsContext = networkStatsContext self.networkStatsContext = networkStatsContext
@ -836,7 +838,7 @@ private final class MultipartFetchManager {
strongSelf.fetchingParts.removeAll() strongSelf.fetchingParts.removeAll()
if let info = strongSelf.parameters?.info as? TelegramCloudMediaResourceFetchInfo, let revalidationContext = strongSelf.revalidationContext { 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 |> deliverOn(strongSelf.queue)).start(next: { validationResult in
if let strongSelf = self { if let strongSelf = self {
strongSelf.revalidatingMediaReference = false 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( return multipartFetch(
accountPeerId: accountPeerId,
postbox: postbox, postbox: postbox,
network: network, network: network,
mediaReferenceRevalidationContext: nil, mediaReferenceRevalidationContext: nil,
@ -921,6 +924,7 @@ public func resourceFetchInfo(resource: TelegramMediaResource) -> MediaResourceF
} }
private func multipartFetchV1( private func multipartFetchV1(
accountPeerId: PeerId,
postbox: Postbox, postbox: Postbox,
network: Network, network: Network,
mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?,
@ -992,7 +996,7 @@ private func multipartFetchV1(
subscriber.putNext(.reset) 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)) subscriber.putNext(.dataPart(resourceOffset: dataOffset, data: data, range: 0 ..< Int64(data.count), complete: false))
}, reportCompleteSize: { size in }, reportCompleteSize: { size in
subscriber.putNext(.resourceSizeUpdated(size)) subscriber.putNext(.resourceSizeUpdated(size))
@ -1013,6 +1017,7 @@ private func multipartFetchV1(
} }
func multipartFetch( func multipartFetch(
accountPeerId: PeerId,
postbox: Postbox, postbox: Postbox,
network: Network, network: Network,
mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext?,
@ -1029,6 +1034,7 @@ func multipartFetch(
) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> { ) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
if network.useExperimentalFeatures, let _ = resource as? TelegramCloudMediaResource, !(resource is SecretFileMediaResource) { if network.useExperimentalFeatures, let _ = resource as? TelegramCloudMediaResource, !(resource is SecretFileMediaResource) {
return multipartFetchV2( return multipartFetchV2(
accountPeerId: accountPeerId,
postbox: postbox, postbox: postbox,
network: network, network: network,
mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, mediaReferenceRevalidationContext: mediaReferenceRevalidationContext,
@ -1044,6 +1050,7 @@ func multipartFetch(
) )
} }
return multipartFetchV1( return multipartFetchV1(
accountPeerId: accountPeerId,
postbox: postbox, postbox: postbox,
network: network, network: network,
mediaReferenceRevalidationContext: mediaReferenceRevalidationContext, 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 { 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(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) 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 contextResult: OutgoingChatContextResultMessageAttribute?
var autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute? var autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?
var autoclearMessageAttribute: AutoclearTimeoutMessageAttribute? 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) return .immediate(.content(PendingMessageUploadedContentAndReuploadInfo(content: .forward(forwardInfo), reuploadInfo: nil, cacheReferenceKey: nil)), .text)
} else if let contextResult = contextResult { } else if let contextResult = contextResult {
return .immediate(.content(PendingMessageUploadedContentAndReuploadInfo(content: .chatContextResult(contextResult), reuploadInfo: nil, cacheReferenceKey: nil)), .text) 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) return .signal(mediaResult, .media)
} else { } else {
return .signal(.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .text(text), reuploadInfo: nil, cacheReferenceKey: nil))), .text) 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 let image = media as? TelegramMediaImage, let largest = largestImageRepresentation(image.representations) {
if peerId.namespace == Namespaces.Peer.SecretChat, let resource = largest.resource as? SecretFileMediaResource { 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))) 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 { } else {
mediaReference = .savedGif(media: file) 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 |> mapError { _ -> PendingMessageUploadError in
return .generic return .generic
} }

View File

@ -75,7 +75,7 @@ private final class PendingUpdateMessageManagerImpl {
self.contexts[messageId] = context self.contexts[messageId] = context
let queue = self.queue 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 |> deliverOn(self.queue)).start(next: { [weak self, weak context] value in
queue.async { queue.async {
guard let strongSelf = self, let initialContext = context else { 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> { 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> { 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(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) 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 |> `catch` { error -> Signal<RequestEditMessageResult, RequestEditMessageInternalError> in
if case .invalidReference = error { 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 { } else {
return .fail(error) 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> let uploadedMedia: Signal<PendingMessageUploadedContentResult?, NoError>
switch media { switch media {
case .keep: case .keep:
@ -59,7 +59,7 @@ private func requestEditMessageInternal(postbox: Postbox, network: Network, stat
case let .update(media): case let .update(media):
let generateUploadSignal: (Bool) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? = { forceReupload in let generateUploadSignal: (Bool) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? = { forceReupload in
let augmentedMedia = augmentMediaWithReference(media) 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) { if let uploadSignal = generateUploadSignal(forceReupload) {
uploadedMedia = .single(.progress(0.027)) uploadedMedia = .single(.progress(0.027))

View File

@ -4333,62 +4333,10 @@ func replayFinalState(
case .userStories(let userId, let stories), .userStoriesSlice(_, let userId, let stories): case .userStories(let userId, let stories), .userStoriesSlice(_, let userId, let stories):
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
for storyItem in stories { for storyItem in stories {
switch storyItem { if let parsedItem = _internal_parseApiStoryItem(transaction: transaction, peerId: peerId, apiStory: storyItem) {
case let .storyItemDeleted(id): storyUpdates.append(InternalStoryUpdate.added(peerId: peerId, item: parsedItem))
storyUpdates.append(InternalStoryUpdate.deleted(id)) } else {
case let .storyItem(flags, id, date, _, _, media, privacy, recentViewers, viewCount): storyUpdates.append(InternalStoryUpdate.deleted(peerId: peerId, id: storyItem.id))
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
)))
}
} }
} }
} }

View File

@ -7,6 +7,7 @@ import TelegramApi
final class AccountTaskManager { final class AccountTaskManager {
private final class Impl { private final class Impl {
private let queue: Queue private let queue: Queue
private let accountPeerId: PeerId
private let stateManager: AccountStateManager private let stateManager: AccountStateManager
private let accountManager: AccountManager<TelegramAccountManagerTypes> private let accountManager: AccountManager<TelegramAccountManagerTypes>
private let networkArguments: NetworkInitializationArguments private let networkArguments: NetworkInitializationArguments
@ -22,9 +23,10 @@ final class AccountTaskManager {
private var isUpdating: Bool = false 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) { networkArguments: NetworkInitializationArguments, viewTracker: AccountViewTracker, mediaReferenceRevalidationContext: MediaReferenceRevalidationContext, isMainApp: Bool, testingEnvironment: Bool) {
self.queue = queue self.queue = queue
self.accountPeerId = accountPeerId
self.stateManager = stateManager self.stateManager = stateManager
self.accountManager = accountManager self.accountManager = accountManager
self.networkArguments = networkArguments 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: .masks).start())
tasks.add(managedSynchronizeInstalledStickerPacksOperations(postbox: self.stateManager.postbox, network: self.stateManager.network, stateManager: self.stateManager, namespace: .emoji).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(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(managedSynchronizeRecentlyUsedMediaOperations(accountPeerId: self.accountPeerId, 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(managedSynchronizeSavedGifsOperations(accountPeerId: self.accountPeerId, 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(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(_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(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()) 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 let queue = Account.sharedQueue
self.queue = queue self.queue = queue
self.impl = QueueLocalObject(queue: queue, generate: { 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> { 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> { 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> { 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 } |> 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 return Signal { _ in
let tag: PeerOperationLogTag let tag: PeerOperationLogTag
switch category { 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 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 entry = entry {
if let operation = entry.contents as? SynchronizeRecentlyUsedMediaOperation { 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 { } else {
assertionFailure() assertionFailure()
} }
@ -120,7 +120,7 @@ private enum SaveRecentlyUsedMediaError {
case invalidReference 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 { switch operation.content {
case let .add(id, accessHash, fileReference): case let .add(id, accessHash, fileReference):
guard let fileReference = fileReference else { guard let fileReference = fileReference else {
@ -150,7 +150,7 @@ private func synchronizeRecentlyUsedMedia(transaction: Transaction, postbox: Pos
case .generic: case .generic:
return .fail(.generic) return .fail(.generic)
case .invalidReference: 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 |> mapError { _ -> SaveRecentlyUsedMediaError in
return .generic 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 return postbox.transaction { transaction -> Signal<Void, NoError> in
var result: PeerMergedOperationLogEntry? var result: PeerMergedOperationLogEntry?
transaction.operationLogUpdateEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, { entry in transaction.operationLogUpdateEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, { entry in
@ -65,7 +65,7 @@ private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOpera
} |> switchToLatest } |> 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 return Signal { _ in
let tag: PeerOperationLogTag = OperationLogTags.SynchronizeSavedGifs let tag: PeerOperationLogTag = OperationLogTags.SynchronizeSavedGifs
@ -81,10 +81,10 @@ func managedSynchronizeSavedGifsOperations(postbox: Postbox, network: Network, r
} }
for (entry, disposable) in beginOperations { 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 entry = entry {
if let operation = entry.contents as? SynchronizeSavedGifsOperation { 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 { } else {
assertionFailure() assertionFailure()
} }
@ -116,7 +116,7 @@ private enum SaveGifError {
case invalidReference 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 { switch operation.content {
case let .add(id, accessHash, fileReference): case let .add(id, accessHash, fileReference):
guard let fileReference = fileReference else { guard let fileReference = fileReference else {
@ -146,7 +146,7 @@ private func synchronizeSavedGifs(transaction: Transaction, postbox: Postbox, ne
case .generic: case .generic:
return .fail(.generic) return .fail(.generic)
case .invalidReference: 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 |> mapError { _ -> SaveGifError in
return .generic return .generic
} }

View File

@ -65,7 +65,7 @@ private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOpera
} |> switchToLatest } |> 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 return Signal { _ in
let tag: PeerOperationLogTag = OperationLogTags.SynchronizeSavedStickers 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 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 entry = entry {
if let operation = entry.contents as? SynchronizeSavedStickersOperation { 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 { } else {
assertionFailure() assertionFailure()
} }
@ -116,7 +116,7 @@ private enum SaveStickerError {
case invalidReference 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 { switch operation.content {
case let .add(id, accessHash, fileReference): case let .add(id, accessHash, fileReference):
guard let fileReference = fileReference else { guard let fileReference = fileReference else {
@ -146,7 +146,7 @@ private func synchronizeSavedStickers(transaction: Transaction, postbox: Postbox
case .generic: case .generic:
return .fail(.generic) return .fail(.generic)
case .invalidReference: 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 |> mapError { _ -> SaveStickerError in
return .generic return .generic
} }

View File

@ -403,7 +403,7 @@ public final class PendingMessageManager {
return lhs.1.index < rhs.1.index return lhs.1.index < rhs.1.index
}) { }) {
if case let .collectingInfo(message) = messageContext.state { 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 messageContext.contentType = contentToUpload.type
switch contentToUpload { switch contentToUpload {
case let .immediate(result, type): case let .immediate(result, type):

View File

@ -251,6 +251,7 @@ public enum AnyMediaReference: Equatable {
case avatarList(peer: PeerReference, media: Media) case avatarList(peer: PeerReference, media: Media)
case attachBot(peer: PeerReference, media: Media) case attachBot(peer: PeerReference, media: Media)
case customEmoji(media: Media) case customEmoji(media: Media)
case story(peer: PeerReference, id: Int32, media: Media)
public static func ==(lhs: AnyMediaReference, rhs: AnyMediaReference) -> Bool { public static func ==(lhs: AnyMediaReference, rhs: AnyMediaReference) -> Bool {
switch lhs { switch lhs {
@ -302,6 +303,12 @@ public enum AnyMediaReference: Equatable {
} else { } else {
return false 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 return nil
case .customEmoji: case .customEmoji:
return nil return nil
case .story:
return nil
} }
} }
@ -360,6 +369,10 @@ public enum AnyMediaReference: Equatable {
if let media = media as? T { if let media = media as? T {
return .customEmoji(media: media) 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 return nil
} }
@ -382,6 +395,8 @@ public enum AnyMediaReference: Equatable {
return media return media
case let .customEmoji(media): case let .customEmoji(media):
return media return media
case let .story(_, _, media):
return media
} }
} }
@ -462,6 +477,7 @@ public enum MediaReference<T: Media> {
case avatarList case avatarList
case attachBot case attachBot
case customEmoji case customEmoji
case story
} }
case standalone(media: T) case standalone(media: T)
@ -472,6 +488,7 @@ public enum MediaReference<T: Media> {
case avatarList(peer: PeerReference, media: T) case avatarList(peer: PeerReference, media: T)
case attachBot(peer: PeerReference, media: T) case attachBot(peer: PeerReference, media: T)
case customEmoji(media: T) case customEmoji(media: T)
case story(peer: PeerReference, id: Int32, media: T)
public init?(decoder: PostboxDecoder) { public init?(decoder: PostboxDecoder) {
guard let caseIdValue = decoder.decodeOptionalInt32ForKey("_r"), let caseId = CodingCase(rawValue: caseIdValue) else { guard let caseIdValue = decoder.decodeOptionalInt32ForKey("_r"), let caseId = CodingCase(rawValue: caseIdValue) else {
@ -523,6 +540,13 @@ public enum MediaReference<T: Media> {
return nil return nil
} }
self = .customEmoji(media: media) 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): case let .customEmoji(media):
encoder.encodeInt32(CodingCase.customEmoji.rawValue, forKey: "_r") encoder.encodeInt32(CodingCase.customEmoji.rawValue, forKey: "_r")
encoder.encodeObject(media, forKey: "m") 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) return .attachBot(peer: peer, media: media)
case let .customEmoji(media): case let .customEmoji(media):
return .customEmoji(media: 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 return media
case let .customEmoji(media): case let .customEmoji(media):
return 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 class TextEntitiesMessageAttribute: MessageAttribute, Equatable {
public let entities: [MessageTextEntity] 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 { switch media {
case let .image(dimensions, data): case let .image(dimensions, data):
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) 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, partialReference: nil,
flags: [] flags: []
) )
originalMedia = imageMedia
let contentToUpload = messageContentToUpload( contentToUpload = messageContentToUpload(
accountPeerId: account.peerId,
network: account.network, network: account.network,
postbox: account.postbox, postbox: account.postbox,
auxiliaryMethods: account.auxiliaryMethods, auxiliaryMethods: account.auxiliaryMethods,
@ -54,94 +59,6 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, priva
text: "", text: "",
media: [imageMedia] 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): case let .video(dimensions, duration, resource):
let fileMedia = TelegramMediaFile( let fileMedia = TelegramMediaFile(
fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: MediaId.Id.random(in: MediaId.Id.min ... MediaId.Id.max)), 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) TelegramMediaFileAttribute.Video(duration: duration, size: dimensions, flags: .supportsStreaming)
] ]
) )
originalMedia = fileMedia
let contentToUpload = messageContentToUpload( contentToUpload = messageContentToUpload(
accountPeerId: account.peerId,
network: account.network, network: account.network,
postbox: account.postbox, postbox: account.postbox,
auxiliaryMethods: account.auxiliaryMethods, auxiliaryMethods: account.auxiliaryMethods,
@ -172,94 +91,124 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, priva
text: "", text: "",
media: [fileMedia] media: [fileMedia]
) )
let contentSignal: Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> }
switch contentToUpload {
case let .immediate(result, _):
contentSignal = .single(result)
case let .signal(signal, _):
contentSignal = signal
}
return contentSignal let contentSignal: Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>
|> map(Optional.init) switch contentToUpload {
|> `catch` { _ -> Signal<PendingMessageUploadedContentResult?, NoError> in case let .immediate(result, _):
return .single(nil) contentSignal = .single(result)
} case let .signal(signal, _):
|> mapToSignal { result -> Signal<Never, NoError> in contentSignal = signal
return account.postbox.transaction { transaction -> Signal<Never, NoError> in }
var privacyRules: [Api.InputPrivacyRule]
switch privacy.base { return contentSignal
case .everyone: |> map(Optional.init)
privacyRules = [.inputPrivacyValueAllowAll] |> `catch` { _ -> Signal<PendingMessageUploadedContentResult?, NoError> in
case .contacts: return .single(nil)
privacyRules = [.inputPrivacyValueAllowContacts] }
case .closeFriends: |> mapToSignal { result -> Signal<Never, NoError> in
privacyRules = [.inputPrivacyValueAllowCloseFriends] return account.postbox.transaction { transaction -> Signal<Never, NoError> in
} var privacyRules: [Api.InputPrivacyRule]
var privacyUsers: [Api.InputUser] = [] switch privacy.base {
var privacyChats: [Int64] = [] case .everyone:
for peerId in privacy.additionallyIncludePeers { privacyRules = [.inputPrivacyValueAllowAll]
if let peer = transaction.getPeer(peerId) { case .contacts:
if let _ = peer as? TelegramUser { privacyRules = [.inputPrivacyValueAllowContacts]
if let inputUser = apiInputUser(peer) { case .closeFriends:
privacyUsers.append(inputUser) privacyRules = [.inputPrivacyValueAllowCloseFriends]
} }
} else if peer is TelegramGroup || peer is TelegramChannel { var privacyUsers: [Api.InputUser] = []
privacyChats.append(peer.id.id._internalGetInt64Value()) 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 !privacyUsers.isEmpty {
} privacyRules.append(.inputPrivacyValueAllowUsers(users: privacyUsers))
if !privacyChats.isEmpty { }
privacyRules.append(.inputPrivacyValueAllowChatParticipants(chats: privacyChats)) if !privacyChats.isEmpty {
} privacyRules.append(.inputPrivacyValueAllowChatParticipants(chats: privacyChats))
}
switch result {
case let .content(content): switch result {
switch content.content { case let .content(content):
case let .media(inputMedia, _): switch content.content {
return account.network.request(Api.functions.stories.sendStory(flags: 0, media: inputMedia, caption: nil, entities: nil, privacyRules: privacyRules)) case let .media(inputMedia, _):
|> map(Optional.init) var flags: Int32 = 0
|> `catch` { _ -> Signal<Api.Updates?, NoError> in var apiCaption: String?
return .single(nil) 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 { return account.network.request(Api.functions.stories.sendStory(
if case let .updateStories(stories) = update { flags: flags,
switch stories { media: inputMedia,
case .userStories(let userId, let apiStories), .userStoriesSlice(_, let userId, let apiStories): caption: apiCaption,
if PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) == account.peerId, apiStories.count == 1 { entities: apiEntities,
switch apiStories[0] { privacyRules: privacyRules
case let .storyItem(_, _, _, _, _, media, _, _, _): ))
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, account.peerId) |> map(Optional.init)
if let parsedMedia = parsedMedia { |> `catch` { _ -> Signal<Api.Updates?, NoError> in
applyMediaResourceChanges(from: fileMedia, to: parsedMedia, postbox: account.postbox, force: true) return .single(nil)
} }
default: |> mapToSignal { updates -> Signal<Never, NoError> in
break 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() return .complete()
} }
default: default:
return .complete() return .complete()
} }
default:
return .complete()
} }
|> switchToLatest
} }
|> switchToLatest
} }
} }
@ -291,3 +240,217 @@ func _internal_markStoryAsSeen(account: Account, peerId: PeerId, id: Int32) -> S
|> ignoreValues |> 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 import SwiftSignalKit
enum InternalStoryUpdate { enum InternalStoryUpdate {
case deleted(Int32) case deleted(peerId: PeerId, id: Int32)
case added(peerId: PeerId, item: StoryListContext.Item) case added(peerId: PeerId, item: StoryListContext.Item)
case read(peerId: PeerId, maxId: Int32) case read(peerId: PeerId, maxId: Int32)
} }
@ -15,20 +15,32 @@ public final class StoryListContext {
case peer(EnginePeer.Id) 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 final class Item: Equatable {
public let id: Int32 public let id: Int32
public let timestamp: Int32 public let timestamp: Int32
public let media: EngineMedia public let media: EngineMedia
public let seenCount: Int public let text: String
public let seenPeers: [EnginePeer] public let entities: [MessageTextEntity]
public let views: Views?
public let privacy: EngineStoryPrivacy? 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.id = id
self.timestamp = timestamp self.timestamp = timestamp
self.media = media self.media = media
self.seenCount = seenCount self.text = text
self.seenPeers = seenPeers self.entities = entities
self.views = views
self.privacy = privacy self.privacy = privacy
} }
@ -42,10 +54,13 @@ public final class StoryListContext {
if lhs.media != rhs.media { if lhs.media != rhs.media {
return false return false
} }
if lhs.seenCount != rhs.seenCount { if lhs.text != rhs.text {
return false return false
} }
if lhs.seenPeers != rhs.seenPeers { if lhs.entities != rhs.entities {
return false
}
if lhs.views != rhs.views {
return false return false
} }
if lhs.privacy != rhs.privacy { if lhs.privacy != rhs.privacy {
@ -207,18 +222,20 @@ public final class StoryListContext {
for update in updates { for update in updates {
switch update { switch update {
case let .deleted(id): case let .deleted(peerId, id):
for i in 0 ..< itemSets.count { for i in 0 ..< itemSets.count {
if let index = itemSets[i].items.firstIndex(where: { $0.id == id }) { if itemSets[i].peerId == peerId {
var items = itemSets[i].items if let index = itemSets[i].items.firstIndex(where: { $0.id == id }) {
items.remove(at: index) var items = itemSets[i].items
itemSets[i] = PeerItemSet( items.remove(at: index)
peerId: itemSets[i].peerId, itemSets[i] = PeerItemSet(
peer: itemSets[i].peer, peerId: itemSets[i].peerId,
maxReadId: itemSets[i].maxReadId, peer: itemSets[i].peer,
items: items, maxReadId: itemSets[i].maxReadId,
totalCount: items.count items: items,
) totalCount: items.count
)
}
} }
} }
case let .added(peerId, item): case let .added(peerId, item):
@ -232,18 +249,7 @@ public final class StoryListContext {
items.remove(at: index) items.remove(at: index)
} }
if peerId == self.account.peerId { items.append(item)
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.sort(by: { lhsItem, rhsItem in items.sort(by: { lhsItem, rhsItem in
if lhsItem.timestamp != rhsItem.timestamp { if lhsItem.timestamp != rhsItem.timestamp {
@ -367,68 +373,13 @@ public final class StoryListContext {
let peerId = id let peerId = id
for apiStory in apiStories { for apiStory in apiStories {
switch apiStory { if let item = _internal_parseApiStoryItem(transaction: transaction, peerId: peerId, apiStory: apiStory) {
case let .storyItem(flags, id, date, _, _, media, privacy, recentViewers, viewCount): if !parsedItemSets.isEmpty && parsedItemSets[parsedItemSets.count - 1].peerId == peerId {
let _ = flags parsedItemSets[parsedItemSets.count - 1].items.append(item)
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId) parsedItemSets[parsedItemSets.count - 1].totalCount = parsedItemSets[parsedItemSets.count - 1].items.count
if let parsedMedia = parsedMedia { } else {
var seenPeers: [EnginePeer] = [] parsedItemSets.append(StoryListContext.PeerItemSet(peerId: peerId, peer: transaction.getPeer(peerId).flatMap(EnginePeer.init), maxReadId: 0, items: [item], totalCount: 1))
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))
}
} }
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() let uploadContext = UploadContext()
self.uploadContexts.append(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 |> deliverOn(self.queue)).start(next: { _ in
}, completed: { [weak self, weak uploadContext] in }, completed: { [weak self, weak uploadContext] in
guard let `self` = self, let uploadContext = uploadContext else { 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)) let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(apiUserId))
for apiStory in apiStories { for apiStory in apiStories {
switch apiStory { if let item = _internal_parseApiStoryItem(transaction: transaction, peerId: peerId, apiStory: apiStory) {
case let .storyItem(flags, id, date, _, _, media, privacy, recentViewers, viewCount): if !parsedItemSets.isEmpty && parsedItemSets[parsedItemSets.count - 1].peerId == peerId {
let _ = flags parsedItemSets[parsedItemSets.count - 1].items.append(item)
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId) } else {
if let parsedMedia = parsedMedia { parsedItemSets.append(StoryListContext.PeerItemSet(
var seenPeers: [EnginePeer] = [] peerId: peerId,
if let recentViewers = recentViewers { peer: transaction.getPeer(peerId).flatMap(EnginePeer.init),
for id in recentViewers { maxReadId: 0,
if let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id))) { items: [item],
seenPeers.append(EnginePeer(peer)) totalCount: apiTotalCount.flatMap(Int.init)
} ))
}
}
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)
))
}
} }
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 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)) return StoryListContext(account: self.account, scope: .peer(id))
} }
public func uploadStory(media: EngineStoryInputMedia, privacy: EngineStoryPrivacy) -> Signal<Never, NoError> { public func uploadStory(media: EngineStoryInputMedia, text: String, entities: [MessageTextEntity], privacy: EngineStoryPrivacy) -> Signal<Never, NoError> {
return _internal_uploadStory(account: self.account, media: media, privacy: privacy) return _internal_uploadStory(account: self.account, media: media, text: text, entities: entities, privacy: privacy)
} }
public func deleteStory(id: Int32) -> Signal<Never, NoError> { 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> { public func markStoryAsSeen(peerId: EnginePeer.Id, id: Int32) -> Signal<Never, NoError> {
return _internal_markStoryAsSeen(account: self.account, peerId: peerId, id: id) 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 { guard let resource = file.media.resource as? CloudDocumentMediaResource else {
return .fail(.generic) 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)) 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 |> `catch` { error -> Signal<Api.account.SavedRingtone, MTRpcError> in
if error.errorDescription == "FILE_REFERENCE_EXPIRED" { 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 |> mapError { _ -> MTRpcError in
return MTRpcError(errorCode: 500, errorDescription: "Internal") 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 |> mapToSignal { datacenterId -> Signal<EngineMediaResource.Fetch.Result, EngineMediaResource.Fetch.Error> in
let resource = AlbumCoverResource(datacenterId: Int(datacenterId), file: file, title: title, performer: performer, isThumbnail: isThumbnail) 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, tag: nil,
info: TelegramCloudMediaResourceFetchInfo( info: TelegramCloudMediaResourceFetchInfo(
reference: MediaResourceReference.standalone(resource: resource), reference: MediaResourceReference.standalone(resource: resource),

View File

@ -75,9 +75,10 @@ func _internal_stickerPacksAttachedToMedia(account: Account, media: AnyMediaRefe
} else { } else {
return .single([]) return .single([])
} }
let accountPeerId = account.peerId
return account.network.request(Api.functions.messages.getAttachedStickers(media: inputMedia)) return account.network.request(Api.functions.messages.getAttachedStickers(media: inputMedia))
|> `catch` { _ -> Signal<[Api.StickerSetCovered], MTRpcError> in |> `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 |> mapError { _ -> MTRpcError in
return MTRpcError(errorCode: 500, errorDescription: "Internal") 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)); 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 maxDimension = max(dimensions.x, dimensions.y);
float currentDistance = time * maxDimension; float currentDistance = time * maxDimension;
float waveWidth = 100.0f * 3.0f; float waveWidth = 100.0f * contentScale;
float d = distance(center, position); float d = distance(center, position);
@ -50,7 +50,8 @@ vertex RasterizerData rippleVertex
device const uint2 &center [[buffer(0)]], device const uint2 &center [[buffer(0)]],
device const uint2 &gridResolution [[buffer(1)]], device const uint2 &gridResolution [[buffer(1)]],
device const uint2 &resolution [[buffer(2)]], 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 triangleIndex = vid / 6;
uint vertexIndex = vid % 6; uint vertexIndex = vid % 6;
@ -69,7 +70,7 @@ vertex RasterizerData rippleVertex
); );
float2 texCoord = float2(position.x, 1.0 - position.y); 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; zPosition *= 0.5f;
float leftEdgeDistance = abs(position.x); float leftEdgeDistance = abs(position.x);
@ -84,9 +85,6 @@ vertex RasterizerData rippleVertex
zPosition *= edgeDistance; zPosition *= edgeDistance;
zPosition *= max(0.0, min(1.0, linearDecay(time, 0.7))); 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 camPosition = float3(0.0, 0.0f, 1.0f);
float3 camTarget = float3(0.0, 0.0, 0.0); float3 camTarget = float3(0.0, 0.0, 0.0);
@ -147,6 +145,9 @@ fragment half4 rippleFragment(
float4 rgb = float4(texture.sample(textureSampler, texCoord)); float4 rgb = float4(texture.sample(textureSampler, texCoord));
float4 out = float4(rgb.xyz, 1.0); 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); out.a = 1.0 - step(in.visibilityFraction, 0.5);

View File

@ -4,6 +4,9 @@ import MetalKit
import simd import simd
public final class RippleEffectView: MTKView { public final class RippleEffectView: MTKView {
private let centerLocation: CGPoint
private let completion: () -> Void
private let textureLoader: MTKTextureLoader private let textureLoader: MTKTextureLoader
private let commandQueue: MTLCommandQueue private let commandQueue: MTLCommandQueue
private let drawPassthroughPipelineState: MTLRenderPipelineState private let drawPassthroughPipelineState: MTLRenderPipelineState
@ -11,7 +14,7 @@ public final class RippleEffectView: MTKView {
private var viewportDimensions = CGSize(width: 1, height: 1) private var viewportDimensions = CGSize(width: 1, height: 1)
private var time: Float = 0.0 private var startTime: Double?
private var lastUpdateTimestamp: 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) let mainBundle = Bundle(for: RippleEffectView.self)
guard let path = mainBundle.path(forResource: "FullScreenEffectViewBundle", ofType: "bundle") else { guard let path = mainBundle.path(forResource: "FullScreenEffectViewBundle", ofType: "bundle") else {
@ -135,12 +141,21 @@ public final class RippleEffectView: MTKView {
} }
private func redraw(drawable: MTLDrawable) { private func redraw(drawable: MTLDrawable) {
if let lastUpdateTimestamp = self.lastUpdateTimestamp { /*if let lastUpdateTimestamp = self.lastUpdateTimestamp {
if lastUpdateTimestamp + 1.0 < CACurrentMediaTime() { if lastUpdateTimestamp + 1.0 < CACurrentMediaTime() {
self.updateImageFromSourceView() self.updateImageFromSourceView()
} }
} else { } else {
self.updateImageFromSourceView() 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 { guard let commandBuffer = self.commandQueue.makeCommandBuffer() else {
@ -160,20 +175,20 @@ public final class RippleEffectView: MTKView {
renderEncoder.setRenderPipelineState(self.drawPassthroughPipelineState) renderEncoder.setRenderPipelineState(self.drawPassthroughPipelineState)
let gridSize = 1000 let gridSize = 1000
var time = self.time.truncatingRemainder(dividingBy: 0.7) var time: Float = Float(min(relativeTime, 0.7))
//time = 0.6
self.time += (1.0 / 60.0) * 0.1
var gridResolution = simd_uint2(UInt32(gridSize), UInt32(gridSize)) var gridResolution = simd_uint2(UInt32(gridSize), UInt32(gridSize))
var resolution = simd_uint2(UInt32(viewportDimensions.width), UInt32(viewportDimensions.height)) 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 { if let texture = self.texture {
var contentScale: Float = Float(self.contentScaleFactor)
renderEncoder.setVertexBytes(&center, length: MemoryLayout<simd_uint2>.size, index: 0) renderEncoder.setVertexBytes(&center, length: MemoryLayout<simd_uint2>.size, index: 0)
renderEncoder.setVertexBytes(&gridResolution, length: MemoryLayout<simd_uint2>.size, index: 1) renderEncoder.setVertexBytes(&gridResolution, length: MemoryLayout<simd_uint2>.size, index: 1)
renderEncoder.setVertexBytes(&resolution, length: MemoryLayout<simd_uint2>.size, index: 2) renderEncoder.setVertexBytes(&resolution, length: MemoryLayout<simd_uint2>.size, index: 2)
renderEncoder.setVertexBytes(&time, length: MemoryLayout<Float>.size, index: 3) renderEncoder.setVertexBytes(&time, length: MemoryLayout<Float>.size, index: 3)
renderEncoder.setVertexBytes(&contentScale, length: MemoryLayout<Float>.size, index: 4)
renderEncoder.setFragmentTexture(texture, index: 0) renderEncoder.setFragmentTexture(texture, index: 0)
@ -184,5 +199,11 @@ public final class RippleEffectView: MTKView {
commandBuffer.present(drawable) commandBuffer.present(drawable)
commandBuffer.commit() 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 { for itemSet in state.itemSets {
var items: [StoryContentItem] = [] var items: [StoryContentItem] = []
guard let peer = itemSet.peer else {
continue
}
let peerId = itemSet.peerId let peerId = itemSet.peerId
for item in itemSet.items { for item in itemSet.items {
@ -24,7 +27,7 @@ public enum StoryChatContent {
position: items.count, position: items.count,
component: AnyComponent(StoryItemContentComponent( component: AnyComponent(StoryItemContentComponent(
context: context, context: context,
peerId: peerId, peer: peer,
item: item item: item
)), )),
centerInfoComponent: AnyComponent(StoryAuthorInfoComponent( centerInfoComponent: AnyComponent(StoryAuthorInfoComponent(

View File

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

View File

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

View File

@ -372,18 +372,12 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
return return
} }
if let chatListController = self.chatListController as? ChatListControllerImpl, let storyListContext = chatListController.storyListContext { 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() 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) let resource = VideoLibraryMediaResource(localIdentifier: asset.localIdentifier, conversion: VideoLibraryMediaResourceConversion.passthrough)
if let chatListController = self.chatListController as? ChatListControllerImpl, let storyListContext = chatListController.storyListContext { 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() 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: default:
selectionController?.dismiss() selectionController?.dismiss()
} }