feat: preserve mobile shares through sign-in (#313)
CI / lint (pull_request) Successful in 29s
CI / build-frontend (pull_request) Successful in 4s

This commit is contained in:
timmy committed 2026-08-08 15:18:30 +00:00
1 parent c0b42b71cf
commit 8ba4d48549
6 files changed
+226 -9

No files matched your search

+22 -2
View File
@@ -2,15 +2,33 @@
if (typeof module === 'object' && module.exports) module.exports = factory;
else root.createLoginController = factory;
}(typeof self !== 'undefined' ? self : this, function createLoginController(options) {
function validShareContinuation(value) {
if (typeof value !== 'string' || !value.startsWith('./?') || value.includes('#')) return './';
const limits = { title: 200, text: 8000, url: 2048 };
const params = new URLSearchParams(value.slice(3));
const entries = Array.from(params.entries());
if (!entries.length) return './';
const invalid = entries.some(([name, content]) => (
!Object.prototype.hasOwnProperty.call(limits, name)
|| !content
|| content.length > limits[name]
));
if (invalid) return './';
return value;
}
const form = options.form;
const status = options.status;
const button = options.button;
const fetchImpl = options.fetchImpl;
const location = options.location;
const continuation = validShareContinuation(options.continuation);
const setIntervalImpl = options.setIntervalImpl || setInterval;
const clearIntervalImpl = options.clearIntervalImpl || clearInterval;
let timer = null;
if (continuation !== './') status.textContent = 'Sign in to continue your shared capture.';
function showReason(reason) {
if (reason !== 'session-expired') return;
status.textContent = 'Your session expired. Private drafts remain on this device. Sign in to continue.';
@@ -49,7 +67,7 @@
}
form.reset();
if (response.ok) {
location.replace('./');
location.replace(continuation);
return;
}
if (response.status === 429) {
@@ -66,14 +84,16 @@ if (typeof document !== 'undefined') {
const form = document.getElementById('sign-in');
const status = document.getElementById('status');
const button = document.getElementById('submit-sign-in');
const loginParams = new URLSearchParams(window.location.search);
const controller = createLoginController({
form,
status,
button,
fetchImpl: fetch.bind(window),
location: window.location,
continuation: loginParams.get('continue'),
});
controller.showReason(new URLSearchParams(window.location.search).get('reason'));
controller.showReason(loginParams.get('reason'));
form.addEventListener('submit', event => {
event.preventDefault();
const accessToken = new FormData(form).get('access_token');
+4 -2
View File
@@ -1,6 +1,6 @@
const BASE = new URL('./', self.location.href).pathname;
importScripts(BASE + 'static/background-issue-sync.js');
const CACHE = 'stackchain-dashboard-shell-v31';
const CACHE = 'stackchain-dashboard-shell-v32';
const OUTAGE_STATUSES = new Set([500, 502, 503, 504]);
const SHELL = [
BASE,
@@ -163,7 +163,9 @@ self.addEventListener('fetch', event => {
event.respondWith(
fetch(request).then(async response => {
const cache = await caches.open(CACHE);
if (response.ok) {
const responseUrl = new URL(response.url || request.url);
const isDashboardShell = responseUrl.origin === self.location.origin && responseUrl.pathname === BASE;
if (response.ok && !response.redirected && isDashboardShell) {
await cache.put(BASE, response.clone());
} else if (OUTAGE_STATUSES.has(response.status)) {
const cached = await cache.match(BASE);