4ce4486b26
新增 IndexedDB 与最近文件 L1 缓存、云保存/打开链路;首页支持下载与清空本地缓存;修复清空 storage 后语言初始化报错;优化 design2 背景加载与侧栏标签样式;完善会员激活/支付弹层样式;后端补充文件缩略图字段与 thumb 接口。 Co-authored-by: Cursor <cursoragent@cursor.com>
218 lines
6.8 KiB
JavaScript
218 lines
6.8 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
var RECENT_KEY = 'soondesign_recent';
|
|
var MIGRATED_KEY = 'soondesign_recent_migrated';
|
|
var HISTORY_KEY = 'soondesign_history';
|
|
var MAX_ITEMS = 20;
|
|
|
|
function nowTimeStr() {
|
|
var d = new Date();
|
|
function pad(n) { return n < 10 ? '0' + n : String(n); }
|
|
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' +
|
|
pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
|
|
}
|
|
|
|
function readRaw() {
|
|
try {
|
|
var raw = localStorage.getItem(RECENT_KEY);
|
|
if (!raw) return { version: 1, items: [] };
|
|
var j = JSON.parse(raw);
|
|
if (!j || !Array.isArray(j.items)) return { version: 1, items: [] };
|
|
return j;
|
|
} catch (e) {
|
|
return { version: 1, items: [] };
|
|
}
|
|
}
|
|
|
|
function writeRaw(data) {
|
|
try {
|
|
localStorage.setItem(RECENT_KEY, JSON.stringify(data));
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function soonRecentKindFromKey(key) {
|
|
if (!key) return 'local';
|
|
if (key.indexOf('soondesign_file:') === 0) return 'cloud';
|
|
if (key.indexOf('soondesign_template:') === 0) return 'template';
|
|
return 'local';
|
|
}
|
|
|
|
function parseFileId(key) {
|
|
var m = String(key).match(/^soondesign_file:(\d+)/);
|
|
return m ? parseInt(m[1], 10) : null;
|
|
}
|
|
|
|
function soonRecentList() {
|
|
return readRaw().items.slice();
|
|
}
|
|
|
|
function soonRecentRemove(key) {
|
|
if (!key) return;
|
|
var data = readRaw();
|
|
data.items = data.items.filter(function (it) { return it.key !== key; });
|
|
writeRaw(data);
|
|
}
|
|
|
|
function soonRecentUpsert(item) {
|
|
if (!item || !item.key) return;
|
|
var key = item.key;
|
|
var kind = item.kind || soonRecentKindFromKey(key);
|
|
var type = item.type != null ? item.type : 1;
|
|
var name = item.name || '';
|
|
if (!name && typeof window.soonDisplayFileName === 'function') {
|
|
name = window.soonDisplayFileName(key);
|
|
}
|
|
var entry = {
|
|
key: key,
|
|
name: name || 'design.soon',
|
|
type: type,
|
|
kind: kind,
|
|
fileId: item.fileId != null ? item.fileId : parseFileId(key),
|
|
thumbRef: item.thumbRef || key,
|
|
time: item.time || nowTimeStr()
|
|
};
|
|
var data = readRaw();
|
|
data.items = data.items.filter(function (it) { return it.key !== key; });
|
|
data.items.unshift(entry);
|
|
if (data.items.length > MAX_ITEMS) data.items = data.items.slice(0, MAX_ITEMS);
|
|
writeRaw(data);
|
|
}
|
|
|
|
function soonRecentUpsertFromOpen(fileKey, json) {
|
|
if (!fileKey) return;
|
|
var type = 1;
|
|
if (json) {
|
|
type = json.soonType ? json.soonType : (json.backBlackPic ? 2 : 1);
|
|
}
|
|
var name = '';
|
|
if (typeof window.soonDisplayFileName === 'function') name = window.soonDisplayFileName(fileKey);
|
|
var tplMeta = window._soonTemplateMeta;
|
|
if (tplMeta && tplMeta.name && fileKey.indexOf('soondesign_template:') === 0) {
|
|
name = tplMeta.name;
|
|
if (tplMeta.type) type = tplMeta.type;
|
|
}
|
|
var fileMeta = window._soonFileMeta;
|
|
if (fileMeta && fileMeta.name && fileKey.indexOf('soondesign_file:') === 0) {
|
|
name = fileMeta.name;
|
|
}
|
|
soonRecentUpsert({
|
|
key: fileKey,
|
|
name: name,
|
|
type: type,
|
|
kind: soonRecentKindFromKey(fileKey),
|
|
fileId: parseFileId(fileKey),
|
|
thumbRef: fileKey,
|
|
time: nowTimeStr(),
|
|
updated_at: (tplMeta && tplMeta.updated_at) ? tplMeta.updated_at : undefined
|
|
});
|
|
}
|
|
|
|
function soonRecentMigrateFromHistory() {
|
|
try {
|
|
if (localStorage.getItem(MIGRATED_KEY) === '1') return;
|
|
} catch (e) { return; }
|
|
var histRaw;
|
|
try {
|
|
histRaw = localStorage.getItem(HISTORY_KEY);
|
|
} catch (e) { return; }
|
|
if (!histRaw) {
|
|
try { localStorage.setItem(MIGRATED_KEY, '1'); } catch (e2) { /* ignore */ }
|
|
return;
|
|
}
|
|
var hist;
|
|
try {
|
|
hist = JSON.parse(histRaw);
|
|
} catch (e) {
|
|
try { localStorage.setItem(MIGRATED_KEY, '1'); } catch (e2) { /* ignore */ }
|
|
return;
|
|
}
|
|
var list = (hist && Array.isArray(hist.history)) ? hist.history : [];
|
|
list.forEach(function (h) {
|
|
if (!h || !h.path) return;
|
|
soonRecentUpsert({
|
|
key: h.path,
|
|
name: typeof window.soonDisplayFileName === 'function' ? window.soonDisplayFileName(h.path) : h.path,
|
|
type: h.type || 1,
|
|
kind: soonRecentKindFromKey(h.path),
|
|
fileId: parseFileId(h.path),
|
|
thumbRef: h.path,
|
|
time: h.time || nowTimeStr()
|
|
});
|
|
});
|
|
try { localStorage.setItem(MIGRATED_KEY, '1'); } catch (e) { /* ignore */ }
|
|
}
|
|
|
|
function soonRecentEnrichFromCloud(items, cloudItems) {
|
|
if (!items || !items.length || !cloudItems || !cloudItems.length) return items;
|
|
var byId = {};
|
|
cloudItems.forEach(function (c) {
|
|
if (c && c.id != null) byId[c.id] = c;
|
|
});
|
|
return items.map(function (it) {
|
|
if (!it.fileId || !byId[it.fileId]) return it;
|
|
var c = byId[it.fileId];
|
|
var copy = Object.assign({}, it);
|
|
if (c.name) copy.name = c.name;
|
|
var curKey = it.filePath || it.key;
|
|
if (c.version != null && typeof window.soonMakeFileKey === 'function') {
|
|
var newKey = window.soonMakeFileKey(c.id, c.version);
|
|
if (newKey !== curKey) {
|
|
soonRecentRemove(curKey);
|
|
copy.filePath = newKey;
|
|
copy.key = newKey;
|
|
copy.thumbRef = newKey;
|
|
soonRecentUpsert({
|
|
key: newKey,
|
|
name: copy.name,
|
|
type: copy.type,
|
|
kind: soonRecentKindFromKey(newKey),
|
|
fileId: c.id,
|
|
thumbRef: newKey
|
|
});
|
|
}
|
|
}
|
|
return copy;
|
|
});
|
|
}
|
|
|
|
function soonRecentOnCloudSave(res, prevKey, type) {
|
|
if (!res || !res.fileKey) return;
|
|
var dropKeys = {};
|
|
if (prevKey && prevKey !== res.fileKey) dropKeys[prevKey] = 1;
|
|
if (res.fileId != null) {
|
|
readRaw().items.forEach(function (it) {
|
|
if (it.fileId === res.fileId && it.key !== res.fileKey) dropKeys[it.key] = 1;
|
|
});
|
|
}
|
|
Object.keys(dropKeys).forEach(function (k) {
|
|
soonRecentRemove(k);
|
|
if (typeof window.soonLocalRemove === 'function') window.soonLocalRemove(k);
|
|
});
|
|
var name = res.name || '';
|
|
if (!name && typeof window.soonDisplayFileName === 'function') {
|
|
name = window.soonDisplayFileName(res.fileKey);
|
|
}
|
|
soonRecentUpsert({
|
|
key: res.fileKey,
|
|
name: name || 'design.soon',
|
|
type: type != null ? type : 1,
|
|
kind: soonRecentKindFromKey(res.fileKey),
|
|
fileId: res.fileId,
|
|
thumbRef: res.fileKey
|
|
});
|
|
}
|
|
|
|
window.soonRecentList = soonRecentList;
|
|
window.soonRecentRemove = soonRecentRemove;
|
|
window.soonRecentUpsert = soonRecentUpsert;
|
|
window.soonRecentUpsertFromOpen = soonRecentUpsertFromOpen;
|
|
window.soonRecentMigrateFromHistory = soonRecentMigrateFromHistory;
|
|
window.soonRecentKindFromKey = soonRecentKindFromKey;
|
|
window.soonRecentEnrichFromCloud = soonRecentEnrichFromCloud;
|
|
window.soonRecentOnCloudSave = soonRecentOnCloudSave;
|
|
})();
|