added deck progress
This commit is contained in:
+86
-55
@@ -145,10 +145,11 @@
|
||||
const deckPreloadStatus = {
|
||||
activeDeckId: DEFAULT_DECK_ID,
|
||||
selectedDeckPhase: "idle",
|
||||
backgroundPhase: "idle",
|
||||
selectedDeckLoadedCount: 0,
|
||||
selectedDeckTotalCount: 0,
|
||||
selectedDeckPercent: 0,
|
||||
warmedDeckIds: []
|
||||
};
|
||||
let backgroundDeckWarmupPromise = null;
|
||||
let activeDeckId = DEFAULT_DECK_ID;
|
||||
|
||||
function getApiBaseUrl() {
|
||||
@@ -288,7 +289,9 @@
|
||||
const snapshot = {
|
||||
activeDeckId: deckPreloadStatus.activeDeckId,
|
||||
selectedDeckPhase: deckPreloadStatus.selectedDeckPhase,
|
||||
backgroundPhase: deckPreloadStatus.backgroundPhase,
|
||||
selectedDeckLoadedCount: deckPreloadStatus.selectedDeckLoadedCount,
|
||||
selectedDeckTotalCount: deckPreloadStatus.selectedDeckTotalCount,
|
||||
selectedDeckPercent: deckPreloadStatus.selectedDeckPercent,
|
||||
warmedDeckIds: [...deckPreloadStatus.warmedDeckIds],
|
||||
warmedDeckCount: deckPreloadStatus.warmedDeckIds.length,
|
||||
totalDeckCount: Object.keys(getDeckManifestSources()).length
|
||||
@@ -314,8 +317,25 @@
|
||||
deckPreloadStatus.selectedDeckPhase = String(partialStatus.selectedDeckPhase || "idle");
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(partialStatus, "backgroundPhase")) {
|
||||
deckPreloadStatus.backgroundPhase = String(partialStatus.backgroundPhase || "idle");
|
||||
if (Object.prototype.hasOwnProperty.call(partialStatus, "selectedDeckLoadedCount")) {
|
||||
const nextLoadedCount = Number(partialStatus.selectedDeckLoadedCount);
|
||||
deckPreloadStatus.selectedDeckLoadedCount = Number.isFinite(nextLoadedCount) && nextLoadedCount >= 0
|
||||
? nextLoadedCount
|
||||
: 0;
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(partialStatus, "selectedDeckTotalCount")) {
|
||||
const nextTotalCount = Number(partialStatus.selectedDeckTotalCount);
|
||||
deckPreloadStatus.selectedDeckTotalCount = Number.isFinite(nextTotalCount) && nextTotalCount >= 0
|
||||
? nextTotalCount
|
||||
: 0;
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(partialStatus, "selectedDeckPercent")) {
|
||||
const nextPercent = Number(partialStatus.selectedDeckPercent);
|
||||
deckPreloadStatus.selectedDeckPercent = Number.isFinite(nextPercent)
|
||||
? Math.max(0, Math.min(100, nextPercent))
|
||||
: 0;
|
||||
}
|
||||
|
||||
if (Array.isArray(partialStatus.warmedDeckIds)) {
|
||||
@@ -577,11 +597,12 @@
|
||||
imagePreloadCache.clear();
|
||||
loadedImageCache.clear();
|
||||
deckImagePreloadCache.clear();
|
||||
backgroundDeckWarmupPromise = null;
|
||||
setDeckPreloadStatus({
|
||||
activeDeckId,
|
||||
selectedDeckPhase: "idle",
|
||||
backgroundPhase: "idle",
|
||||
selectedDeckLoadedCount: 0,
|
||||
selectedDeckTotalCount: 0,
|
||||
selectedDeckPercent: 0,
|
||||
warmedDeckIds: []
|
||||
});
|
||||
setActiveDeck(activeDeckId);
|
||||
@@ -1012,7 +1033,7 @@
|
||||
return Boolean(cachedImage?.complete && cachedImage?.naturalWidth);
|
||||
}
|
||||
|
||||
async function preloadImageUrls(urls, concurrency = 6) {
|
||||
async function preloadImageUrls(urls, concurrency = 6, onProgress = null) {
|
||||
const queue = Array.from(new Set(Array.isArray(urls) ? urls.map((entry) => String(entry || "").trim()).filter(Boolean) : []));
|
||||
if (queue.length === 0) {
|
||||
return [];
|
||||
@@ -1021,12 +1042,29 @@
|
||||
const limit = Math.max(1, Number.isInteger(concurrency) ? concurrency : 6);
|
||||
const results = [];
|
||||
let cursor = 0;
|
||||
let completedCount = 0;
|
||||
|
||||
function reportProgress(lastUrl) {
|
||||
completedCount += 1;
|
||||
if (typeof onProgress === "function") {
|
||||
onProgress({
|
||||
completedCount,
|
||||
totalCount: queue.length,
|
||||
lastUrl: String(lastUrl || "").trim()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function consumeQueue() {
|
||||
while (cursor < queue.length) {
|
||||
const currentIndex = cursor;
|
||||
cursor += 1;
|
||||
results[currentIndex] = await preloadImageUrl(queue[currentIndex]);
|
||||
const currentUrl = queue[currentIndex];
|
||||
try {
|
||||
results[currentIndex] = await preloadImageUrl(currentUrl);
|
||||
} finally {
|
||||
reportProgress(currentUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1078,6 +1116,8 @@
|
||||
|
||||
function preloadDeckImages(deckId, options = {}) {
|
||||
const normalizedDeckId = normalizeDeckId(deckId);
|
||||
const preloadUrls = buildDeckImagePreloadUrls(normalizedDeckId, options);
|
||||
const totalCount = preloadUrls.length;
|
||||
const cacheKey = JSON.stringify({
|
||||
deckId: normalizedDeckId,
|
||||
includeFull: options.includeFull !== false,
|
||||
@@ -1086,26 +1126,49 @@
|
||||
});
|
||||
|
||||
if (!options.force && deckImagePreloadCache.has(cacheKey)) {
|
||||
if (deckPreloadStatus.warmedDeckIds.includes(normalizedDeckId)) {
|
||||
setDeckPreloadStatus({
|
||||
activeDeckId: normalizedDeckId,
|
||||
selectedDeckPhase: "ready",
|
||||
selectedDeckLoadedCount: totalCount,
|
||||
selectedDeckTotalCount: totalCount,
|
||||
selectedDeckPercent: totalCount > 0 ? 100 : 0
|
||||
});
|
||||
}
|
||||
return deckImagePreloadCache.get(cacheKey);
|
||||
}
|
||||
|
||||
if (!options.background) {
|
||||
setDeckPreloadStatus({
|
||||
activeDeckId: normalizedDeckId,
|
||||
selectedDeckPhase: "loading"
|
||||
selectedDeckPhase: "loading",
|
||||
selectedDeckLoadedCount: 0,
|
||||
selectedDeckTotalCount: totalCount,
|
||||
selectedDeckPercent: 0
|
||||
});
|
||||
}
|
||||
|
||||
const preloadPromise = preloadImageUrls(buildDeckImagePreloadUrls(normalizedDeckId, options), options.concurrency)
|
||||
const preloadPromise = preloadImageUrls(preloadUrls, options.concurrency, ({ completedCount, totalCount: progressTotalCount }) => {
|
||||
if (!options.background) {
|
||||
setDeckPreloadStatus({
|
||||
activeDeckId: normalizedDeckId,
|
||||
selectedDeckPhase: "loading",
|
||||
selectedDeckLoadedCount: completedCount,
|
||||
selectedDeckTotalCount: progressTotalCount,
|
||||
selectedDeckPercent: progressTotalCount > 0 ? Math.round((completedCount / progressTotalCount) * 100) : 0
|
||||
});
|
||||
}
|
||||
})
|
||||
.then((result) => {
|
||||
markDeckAsWarmed(normalizedDeckId);
|
||||
if (!options.background) {
|
||||
setDeckPreloadStatus({
|
||||
activeDeckId: normalizedDeckId,
|
||||
selectedDeckPhase: "ready"
|
||||
selectedDeckPhase: "ready",
|
||||
selectedDeckLoadedCount: totalCount,
|
||||
selectedDeckTotalCount: totalCount,
|
||||
selectedDeckPercent: totalCount > 0 ? 100 : 0
|
||||
});
|
||||
} else {
|
||||
emitDeckPreloadStatus();
|
||||
}
|
||||
return result;
|
||||
})
|
||||
@@ -1113,7 +1176,8 @@
|
||||
if (!options.background) {
|
||||
setDeckPreloadStatus({
|
||||
activeDeckId: normalizedDeckId,
|
||||
selectedDeckPhase: "error"
|
||||
selectedDeckPhase: "error",
|
||||
selectedDeckTotalCount: totalCount
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
@@ -1141,44 +1205,6 @@
|
||||
return deferPreload(() => preloadDeckImages(deckId, options));
|
||||
}
|
||||
|
||||
function scheduleBackgroundDeckWarmup(deckId, options = {}) {
|
||||
if (backgroundDeckWarmupPromise) {
|
||||
return backgroundDeckWarmupPromise;
|
||||
}
|
||||
|
||||
const normalizedDeckId = normalizeDeckId(deckId);
|
||||
const warmupOptions = {
|
||||
...options,
|
||||
startDeckId: normalizedDeckId,
|
||||
background: true,
|
||||
concurrency: Number.isInteger(options.concurrency) ? options.concurrency : 2
|
||||
};
|
||||
|
||||
setDeckPreloadStatus({
|
||||
activeDeckId: normalizedDeckId,
|
||||
backgroundPhase: "loading"
|
||||
});
|
||||
|
||||
backgroundDeckWarmupPromise = scheduleDeckImagePreload(normalizedDeckId, warmupOptions)
|
||||
.then(() => deferPreload(() => preloadAllDeckImages(warmupOptions)))
|
||||
.then((result) => {
|
||||
setDeckPreloadStatus({
|
||||
activeDeckId: normalizedDeckId,
|
||||
backgroundPhase: "ready"
|
||||
});
|
||||
return result;
|
||||
})
|
||||
.catch(() => {
|
||||
setDeckPreloadStatus({
|
||||
activeDeckId: normalizedDeckId,
|
||||
backgroundPhase: "error"
|
||||
});
|
||||
return [];
|
||||
});
|
||||
|
||||
return backgroundDeckWarmupPromise;
|
||||
}
|
||||
|
||||
function resolveDisplayNameWithDeck(deckId, cardName, trumpNumber) {
|
||||
const manifest = getDeckManifest(deckId);
|
||||
const fallbackName = String(cardName || "").trim();
|
||||
@@ -1263,12 +1289,17 @@
|
||||
function setActiveDeck(deckId) {
|
||||
activeDeckId = normalizeDeckId(deckId);
|
||||
getDeckManifest(activeDeckId);
|
||||
const preloadUrls = buildDeckImagePreloadUrls(activeDeckId);
|
||||
const totalCount = preloadUrls.length;
|
||||
const isWarmed = deckPreloadStatus.warmedDeckIds.includes(activeDeckId);
|
||||
setDeckPreloadStatus({
|
||||
activeDeckId,
|
||||
selectedDeckPhase: "idle"
|
||||
selectedDeckPhase: isWarmed ? "ready" : "idle",
|
||||
selectedDeckLoadedCount: isWarmed ? totalCount : 0,
|
||||
selectedDeckTotalCount: totalCount,
|
||||
selectedDeckPercent: isWarmed && totalCount > 0 ? 100 : 0
|
||||
});
|
||||
scheduleDeckImagePreload(activeDeckId);
|
||||
scheduleBackgroundDeckWarmup(activeDeckId);
|
||||
return activeDeckId;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user