Home telegram - revalidateMediaResouceReferences
Post
Cancel

telegram - revalidateMediaResouceReferences

MediaReferenceRevalidationContext

class MediaReferenceRevalidationContext

1
2
private let queue: Queue
private let impl: QueueLocalObject<MediaReferenceRevalidationContextImpl>

enum MediaReferenceRevalidationKey

1
2
3
4
5
6
case message(message: MessageReference)
case webPage(webPage: WebpageReference)
case stickerPack(stickerPack: StickerPackReference)
case savedGifs
case peer(peer: PeerReference)
case wallpapers

class MediaReferenceRevalidationItemContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
let subscribers = Bag<(Any) -> Void>()
let disposable: Disposable
func addSubscriber(_ f: @escaping (Any) -> Void) -> Int {
    return self.subscribers.add(f)
}

func removeSubscriber(_ index: Int) {
    self.subscribers.remove(index)
}

// message
func message(postbox: Postbox, network: Network, background: Bool, message: MessageReference) -> Signal<Message, RevalidateMediaReferenceError> {
    return self.genericItem(key: .message(message: message), background: background, request: { next, error in
        let source: Signal<FetchMessageHistoryHoleSource, NoError>
        if background {
            source = network.background()
            |> map(FetchMessageHistoryHoleSource.download)
        } else {
            source = .single(.network(network))
        }
        let signal = source
        |> mapToSignal { source -> Signal<Message?, NoError> in
            return fetchRemoteMessage(postbox: postbox, source: source, message: message)
        }
        return signal.start(next: { value in
            if let value = value {
                next(value)
            } else {
                error(.generic)
            }
        }, error: { _ in
            error(.generic)
        })
    }) |> mapToSignal { next -> Signal<Message, RevalidateMediaReferenceError> in
        if let next = next as? Message {
            return .single(next)
        } else {
            return .fail(.generic)
        }
    }
}

// stickerPack
func stickerPack(postbox: Postbox, network: Network, background: Bool, stickerPack: StickerPackReference) -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), RevalidateMediaReferenceError> {
    return self.genericItem(key: .stickerPack(stickerPack: stickerPack), background: background, request: { next, error in
        return (updatedRemoteStickerPack(postbox: postbox, network: network, reference: stickerPack)
        |> mapError { _ -> RevalidateMediaReferenceError in
            return .generic
        }).start(next: { value in
            if let value = value {
                next(value)
            } else {
                error(.generic)
            }
        }, error: { _ in
            error(.generic)
        })
    }) |> mapToSignal { next -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), RevalidateMediaReferenceError> in
        if let next = next as? (StickerPackCollectionInfo, [ItemCollectionItem]) {
            return .single(next)
        } else {
            return .fail(.generic)
        }
    }
}

// webPage
func webPage(postbox: Postbox, network: Network, background: Bool, webPage: WebpageReference) -> Signal<TelegramMediaWebpage, RevalidateMediaReferenceError> {
    return self.genericItem(key: .webPage(webPage: webPage), background: background, request: { next, error in
        return (updatedRemoteWebpage(postbox: postbox, network: network, webPage: webPage)
        |> mapError { _ -> RevalidateMediaReferenceError in
            return .generic
        }).start(next: { value in
            if let value = value {
                next(value)
            } else {
                error(.generic)
            }
        }, error: { _ in
            error(.generic)
        })
    }) |> mapToSignal { next -> Signal<TelegramMediaWebpage, RevalidateMediaReferenceError> in
        if let next = next as? TelegramMediaWebpage {
            return .single(next)
        } else {
            return .fail(.generic)
        }
    }
}

// savedGifs
func savedGifs(postbox: Postbox, network: Network, background: Bool) -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> {
    return self.genericItem(key: .savedGifs, background: background, request: { next, error in
        let loadRecentGifs: Signal<[TelegramMediaFile], NoError> = postbox.transaction { transaction -> [TelegramMediaFile] in
            return transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudRecentGifs).compactMap({ item -> TelegramMediaFile? in
                if let contents = item.contents as? RecentMediaItem, let file = contents.media as? TelegramMediaFile {
                    return file
                }
                return nil
            })
        }
        return (managedRecentGifs(postbox: postbox, network: network, forceFetch: true)
        |> mapToSignal { _ -> Signal<[TelegramMediaFile], NoError> in
            return .complete()
        }
        |> then(loadRecentGifs)
        |> introduceError(RevalidateMediaReferenceError.self)).start(next: { value in
            next(value)
        }, error: { _ in
            error(.generic)
        })
    }) |> mapToSignal { next -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> in
        if let next = next as? [TelegramMediaFile] {
            return .single(next)
        } else {
            return .fail(.generic)
        }
    }
}

// peer
func peer(postbox: Postbox, network: Network, background: Bool, peer: PeerReference) -> Signal<Peer, RevalidateMediaReferenceError> {
    return self.genericItem(key: .peer(peer: peer), background: background, request: { next, error in
        return (updatedRemotePeer(postbox: postbox, network: network, peer: peer)
        |> mapError { _ -> RevalidateMediaReferenceError in
            return .generic
        }).start(next: { value in
            next(value)
        }, error: { _ in
            error(.generic)
        })
    }) |> mapToSignal { next -> Signal<Peer, RevalidateMediaReferenceError> in
        if let next = next as? Peer {
            return .single(next)
        } else {
            return .fail(.generic)
        }
    }
}

// wallpapers
func wallpapers(postbox: Postbox, network: Network, background: Bool) -> Signal<[TelegramWallpaper], RevalidateMediaReferenceError> {
    return self.genericItem(key: .wallpapers, background: background, request: { next, error in
        return (telegramWallpapers(postbox: postbox, network: network, forceUpdate: true)
        |> last
        |> mapError { _ -> RevalidateMediaReferenceError in
            return .generic
        }).start(next: { value in
            if let value = value {
                next(value)
            } else {
                error(.generic)
            }
        }, error: { _ in
            error(.generic)
        })
    }) |> mapToSignal { next -> Signal<[TelegramWallpaper], RevalidateMediaReferenceError> in
        if let next = next as? [TelegramWallpaper] {
            return .single(next)
        } else {
            return .fail(.generic)
        }
    }
}

class MediaReferenceRevalidationContextImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
let queue: Queue
var itemContexts: [MediaReferenceRevalidationKeyAndPlacement: MediaReferenceRevalidationItemContext] = [:]

func genericItem(key: MediaReferenceRevalidationKey, background: Bool, request: @escaping (@escaping (Any) -> Void, @escaping (RevalidateMediaReferenceError) -> Void) -> Disposable, _ f: @escaping (Any) -> Void) -> Disposable {
    let queue = self.queue

    let itemKey = MediaReferenceRevalidationKeyAndPlacement(key: key, background: background)

    let context: MediaReferenceRevalidationItemContext
    if let current = self.itemContexts[itemKey] {
        context = current
    } else {
        let disposable = MetaDisposable()
        context = MediaReferenceRevalidationItemContext(disposable: disposable)
        self.itemContexts[itemKey] = context
        disposable.set(request({ [weak self] result in
            queue.async {
                guard let strongSelf = self else {
                    return
                }
                if let current = strongSelf.itemContexts[itemKey], current === context {
                    strongSelf.itemContexts.removeValue(forKey: itemKey)
                    for subscriber in current.subscribers.copyItems() {
                        subscriber(result)
                    }
                }
            }
        }, { _ in
        }))
    }

    let index = context.addSubscriber(f)

    return ActionDisposable { [weak self, weak context] in
        queue.async {
            guard let strongSelf = self else {
                return
            }
            if let current = strongSelf.itemContexts[itemKey], current === context {
                current.removeSubscriber(index)
                if current.isEmpty {
                    current.disposable.dispose()
                    strongSelf.itemContexts.removeValue(forKey: itemKey)
                }
            }
        }
    }
}

RevalidatedMediaResource

struct RevalidatedMediaResource

1
2
let updatedResource: TelegramMediaResource
let updatedReference: MediaResourceReference?

revalidateMediaResourceReference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
func revalidateMediaResourceReference(postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo, resource: MediaResource) -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> {
    var updatedReference = info.reference
    if case let .media(media, resource) = updatedReference {
        if case let .message(_, mediaValue) = media {
            if let file = mediaValue as? TelegramMediaFile {
                if let partialReference = file.partialReference {
                    updatedReference = partialReference.mediaReference(media.media).resourceReference(resource)
                }
                if file.isSticker {
                    var stickerPackReference: StickerPackReference?
                    for attribute in file.attributes {
                        if case let .Sticker(sticker) = attribute {
                            if let packReference = sticker.packReference {
                                stickerPackReference = packReference
                            }
                        }
                    }
                    if let stickerPackReference = stickerPackReference {
                        updatedReference = .media(media: .stickerPack(stickerPack: stickerPackReference, media: mediaValue), resource: resource)
                    }
                }
            } else if let image = mediaValue as? TelegramMediaImage {
                if let partialReference = image.partialReference {
                    updatedReference = partialReference.mediaReference(media.media).resourceReference(resource)
                }
            }
        }
    }
    
    switch updatedReference {
        case let .media(media, _):
            switch media {
                case let .message(message, previousMedia):
                    return revalidationContext.message(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, message: message)
                    |> mapToSignal { message -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> in
                        for media in message.media {
                            if let updatedResource = findUpdatedMediaResource(media: media, previousMedia: previousMedia, resource: resource) {
                                return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil))
                            }
                        }
                        return .fail(.generic)
                    }
                case let .stickerPack(stickerPack, media):
                    return revalidationContext.stickerPack(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, stickerPack: stickerPack)
                    |> mapToSignal { result -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> in
                        for item in result.1 {
                            if let item = item as? StickerPackItem {
                                if media.id != nil && item.file.id == media.id {
                                    if let updatedResource = findUpdatedMediaResource(media: item.file, previousMedia: media, resource: resource) {
                                        return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil))
                                    }
                                }
                            }
                        }
                        return .fail(.generic)
                    }
                case let .webPage(webPage, previousMedia):
                    return revalidationContext.webPage(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, webPage: webPage)
                    |> mapToSignal { result -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> in
                        if let updatedResource = findUpdatedMediaResource(media: result, previousMedia: previousMedia, resource: resource) {
                            return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil))
                        }
                        return .fail(.generic)
                    }
                case let .savedGif(media):
                    return revalidationContext.savedGifs(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation)
                    |> mapToSignal { result -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> in
                        for file in result {
                            if media.id != nil && file.id == media.id {
                                if let updatedResource = findUpdatedMediaResource(media: file, previousMedia: media, resource: resource) {
                                    return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil))
                                }
                            }
                        }
                        return .fail(.generic)
                    }
                case let .standalone(media):
                    if let file = media as? TelegramMediaFile {
                        for attribute in file.attributes {
                            if case let .Sticker(sticker) = attribute, let stickerPack = sticker.packReference {
                                return revalidationContext.stickerPack(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, stickerPack: stickerPack)
                                |> mapToSignal { result -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> in
                                    for item in result.1 {
                                        if let item = item as? StickerPackItem {
                                            if media.id != nil && item.file.id == media.id {
                                                if let updatedResource = findUpdatedMediaResource(media: item.file, previousMedia: media,  resource: resource) {
                                                    return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil))
                                                }
                                            }
                                        }
                                    }
                                    return .fail(.generic)
                                }
                            }
                        }
                    }
                    return .fail(.generic)
            }
        case let .avatar(peer, _):
            return revalidationContext.peer(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, peer: peer)
            |> mapToSignal { updatedPeer -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> in
                for representation in updatedPeer.profileImageRepresentations {
                    if representation.resource.id.isEqual(to: resource.id) {
                        return .single(RevalidatedMediaResource(updatedResource: representation.resource, updatedReference: nil))
                    }
                }
                if let legacyResource = resource as? CloudFileMediaResource {
                    for representation in updatedPeer.profileImageRepresentations {
                        if let updatedResource = representation.resource as? CloudPeerPhotoSizeMediaResource {
                            if updatedResource.localId == legacyResource.localId && updatedResource.volumeId == legacyResource.volumeId {
                                return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil))
                            }
                        }
                    }
                }
                return .fail(.generic)
            }
        case let .messageAuthorAvatar(message, _):
            return revalidationContext.message(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, message: message)
            |> mapToSignal { updatedMessage -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> in
                guard let author = updatedMessage.author, let authorReference = PeerReference(author) else {
                    return .fail(.generic)
                }
                for representation in author.profileImageRepresentations {
                    if representation.resource.id.isEqual(to: resource.id) {
                        return .single(RevalidatedMediaResource(updatedResource: representation.resource, updatedReference: .avatar(peer: authorReference, resource: representation.resource)))
                    }
                }
                if let legacyResource = resource as? CloudFileMediaResource {
                    for representation in author.profileImageRepresentations {
                        if let updatedResource = representation.resource as? CloudPeerPhotoSizeMediaResource {
                            if updatedResource.localId == legacyResource.localId && updatedResource.volumeId == legacyResource.volumeId {
                                return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: .avatar(peer: authorReference, resource: updatedResource)))
                            }
                        }
                    }
                }
                return .fail(.generic)
            }
        case .wallpaper:
            return revalidationContext.wallpapers(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation)
            |> mapToSignal { wallpapers -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> in
                for wallpaper in wallpapers {
                    switch wallpaper {
                        case let .image(representations, _):
                            for representation in representations {
                                if representation.resource.id.isEqual(to: resource.id) {
                                    return .single(RevalidatedMediaResource(updatedResource: representation.resource, updatedReference: nil))
                                }
                            }
                        case let .file(_, _, _, _, _, _, _, file, _):
                            if let updatedResource = findUpdatedMediaResource(media: file, previousMedia: nil, resource: resource) {
                                return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil))
                            }
                        default:
                            break
                    }
                }
                return .fail(.generic)
            }
        case let .stickerPackThumbnail(packReference, resource):
            return revalidationContext.stickerPack(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, stickerPack: packReference)
            |> mapToSignal { result -> Signal<RevalidatedMediaResource, RevalidateMediaReferenceError> in
                if let thumbnail = result.0.thumbnail {
                    if thumbnail.resource.id.isEqual(to: resource.id) {
                        return .single(RevalidatedMediaResource(updatedResource: thumbnail.resource, updatedReference: nil))
                    }
                    if let legacyResource = resource as? CloudFileMediaResource {
                        if let updatedResource = thumbnail.resource as? CloudStickerPackThumbnailMediaResource {
                            if updatedResource.localId == legacyResource.localId && updatedResource.volumeId == legacyResource.volumeId {
                                return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil))
                            }
                        }
                    }
                }
                return .fail(.generic)
            }
        case .standalone:
            return .fail(.generic)
    }
}
This post is licensed under CC BY 4.0 by the author.