Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Telegram/Watch/Bridge/TGBridgeStickersSignals.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@

+ (NSURL *)stickerPacksURL;

+ (void)prefetchRecentStickersWithLimit:(NSUInteger)limit;

@end
38 changes: 26 additions & 12 deletions Telegram/Watch/Bridge/TGBridgeStickersSignals.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#import "TGBridgeStickerPack.h"
#import "TGBridgeClient.h"
#import "TGBridgeMediaSignals.h"
#import "TGWatchCommon.h"

@implementation TGBridgeStickersSignals

Expand All @@ -16,18 +18,17 @@ + (SSignal *)cachedRecentStickers

+ (SSignal *)recentStickersWithLimit:(NSUInteger)limit
{
return [[TGBridgeClient instance] requestSignalWithSubscription:[[TGBridgeRecentStickersSubscription alloc] initWithLimit:limit]];
// return [[self cachedRecentStickers] mapToSignal:^SSignal *(NSArray *stickers) {
// SSignal *remote = [[TGBridgeClient instance] requestSignalWithSubscription:[[TGBridgeRecentStickersSubscription alloc] initWithLimit:limit]];
// remote = [remote onNext:^(NSArray *stickers) {
// cachedStickers = stickers;
// }];
// if (stickers != nil) {
// return [[SSignal single:stickers] then:remote];
// } else {
// return remote;
// }
// }];
return [[self cachedRecentStickers] mapToSignal:^SSignal *(NSArray *stickers) {
SSignal *remote = [[TGBridgeClient instance] requestSignalWithSubscription:[[TGBridgeRecentStickersSubscription alloc] initWithLimit:limit]];
remote = [remote onNext:^(NSArray *stickers) {
cachedStickers = stickers;
}];
if (stickers != nil) {
return [[SSignal single:stickers] then:remote];
} else {
return remote;
}
}];
}

+ (SSignal *)stickerPacks
Expand All @@ -47,4 +48,17 @@ + (NSURL *)stickerPacksURL
return stickerPacksURL;
}

+ (void)prefetchRecentStickersWithLimit:(NSUInteger)limit
{
[[[self recentStickersWithLimit:limit] deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *stickers)
{
for (TGBridgeDocumentMediaAttachment *sticker in stickers)
{
CGSize stickerSize = TGWatchStickerSizeForScreen(TGWatchScreenType());
NSString *imageUrl = [NSString stringWithFormat:@"sticker_%lld_%dx%d_0", sticker.documentId, (int)stickerSize.width, (int)stickerSize.height];
[[[TGBridgeMediaSignals stickerWithDocumentId:sticker.documentId packId:sticker.stickerPackId accessHash:sticker.stickerPackAccessHash type:TGMediaStickerImageTypeNormal] deliverOn:[SQueue mainQueue]] startWithNext:^(id next) {}];
}
}];
}

@end
1 change: 1 addition & 0 deletions Telegram/Watch/Extension/TGStickersController.m
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ - (void)reloadData
updateInteface(initial, currentStickerModels, true);
}
}]];
[TGBridgeStickersSignals prefetchRecentStickersWithLimit:24];
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ struct PickerSticker: Identifiable, Equatable, Hashable {
let emoji: String
let width: Int
let height: Int

var renderFileId: Int? {
switch render {
case .raster(let id, _): return id
case .lottie(let id): return id
case .none: return nil
}
}
}

/// How to produce a static grid-tile image for a sticker.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ final class StickerPickerStore {
private let logger = Logger(subsystem: "org.telegram.TelegramWatch", category: "stickerpicker")
private var files: [Int: File] = [:]
private var trackedFileIds: Set<Int> = []
private var pinnedFileIds: Set<Int> = []
private var setStickers: [Int64: [PickerSticker]] = [:]

init(loader: StickerPickerLoader) {
Expand Down Expand Up @@ -52,6 +53,7 @@ final class StickerPickerStore {
}

func cancelFileDownload(fileId: Int) {
guard !pinnedFileIds.contains(fileId) else { return }
trackedFileIds.remove(fileId)
logger.info("cancelFileDownload fileId=\(fileId, privacy: .public)")
Task { [logger, loader] in
Expand All @@ -63,6 +65,33 @@ final class StickerPickerStore {
}
}

/// Pre-downloads favorite and recent stickers with high priority so they are
/// cached on disk before the user scrolls to them. Pinned file IDs are
/// protected from cancellation by `cancelFileDownload`.
func prefetchStickers() {
let all = favorites + recents
var renderIds = Set<Int>()
for sticker in all {
if let id = sticker.renderFileId {
renderIds.insert(id)
}
}
guard !renderIds.isEmpty else { return }
let newIds = renderIds.subtracting(trackedFileIds)
pinnedFileIds.formUnion(newIds)
trackedFileIds.formUnion(newIds)
logger.info("prefetchStickers: pinning \(newIds.count, privacy: .public) sticker render files")
for id in newIds {
Task { [logger, loader] in
do {
_ = try await loader.downloadFile(fileId: id, priority: 4)
} catch {
logger.warning("prefetch fileId=\(id, privacy: .public) failed: \(error.localizedDescription, privacy: .public)")
}
}
}
}

/// Fetches favorites, recents, and installed sets concurrently. Each source
/// is independent — one failing source still renders the others. `.failed`
/// only when all three fail. Re-runs on every sheet open (keeps recents/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct StickerPickerView: View {
.task {
client.setActiveStickerPicker(store)
await store.load()
store.prefetchStickers()
}
.onDisappear { client.setActiveStickerPicker(nil) }
}
Expand Down