stackchain-dashboard/frontend/search-preview.js
timmy 43163ed926
All checks were successful
CI / lint (pull_request) Successful in 1m41s
CI / build-release (pull_request) Successful in 5s
CI / release-candidate (pull_request) Has been skipped
feat: share resumable mobile search previews (Closes #787)
2026-08-14 01:04:20 +00:00

72 lines
2.6 KiB
JavaScript

(function (root, factory) {
const createSearchPreview = factory();
if (typeof module === 'object' && module.exports) module.exports = createSearchPreview;
if (root) root.createSearchPreview = createSearchPreview;
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
return function createSearchPreview({ fetchJson, mutate, share, onState }) {
let generation = 0;
let current = null;
let mutationRequest = null;
let shareRequest = null;
function run(action, pending, success, detail) {
if (mutationRequest) return mutationRequest;
onState({ status: pending, item: current, detail });
mutationRequest = mutate(detail, action).then(result => {
onState({ status: success, item: current, detail, result });
return result;
}).catch(error => {
onState({ status: 'ready', item: current, detail, error });
throw error;
}).finally(() => { mutationRequest = null; });
return mutationRequest;
}
return {
open(item) {
generation += 1;
const requestGeneration = generation;
current = { ...item };
onState({ status: 'loading', item: current });
return fetchJson(current).then(detail => {
if (requestGeneration === generation) {
current = { ...current, ...detail };
onState({ status: 'ready', item: current, detail });
}
return detail;
}).catch(error => {
if (requestGeneration === generation) {
onState({ status: 'error', item: current, error });
}
throw error;
});
},
close() {
generation += 1;
current = null;
onState({ status: 'closed' });
},
share(url) {
if (shareRequest) return shareRequest;
if (!current || typeof share !== 'function') return Promise.reject(new Error('Sharing is unavailable.'));
const detail = current;
onState({ status:'sharing', item:current, detail });
shareRequest = share(url).then(result => {
onState({ status:result, item:current, detail });
return result;
}).catch(error => {
onState({ status:error?.name === 'AbortError' ? 'share-canceled' : 'share-error', item:current, detail, error });
throw error;
}).finally(() => { shareRequest = null; });
return shareRequest;
},
claim(detail) {
return run('claim', 'claiming', 'claimed', detail);
},
reopen(detail) {
return run('reopen', 'reopening', 'reopened', detail);
},
};
};
});