Organize uploader storage and restore transfer readiness

This commit is contained in:
2026-10-14 19:15:46 +09:00
parent 69e7449ba7
commit c3573b605c
24 changed files with 966 additions and 449 deletions

View File

@@ -1,7 +1,7 @@
import { readDeviceInfo } from './deviceInfo';
import { PASSWORD_MIN_LENGTH } from '@hop/shared';
import { createRefreshQueue } from './refreshQueue';
import { ref } from 'vue';
import { ref, watch } from 'vue';
import { io } from 'socket.io-client';
export const account = ref(null),
registrationOpen = ref(false),
@@ -27,6 +27,7 @@ export const device = ref(null),
error = ref('');
export const pendingPeers = ref([]);
export const transferCooldown = ref({ until: 0, targetDeviceId: null });
export const transferConnecting = ref(false);
let pendingTimer;
let deviceInfo = {};
export const nextCursor = ref(null);
@@ -46,6 +47,46 @@ export function reserveLocationAcceptance(requestId) {
}
let socket;
let reauthorizing;
export async function waitForConnection() {
if (socket?.connected) return;
if (!socket) throw Error('서버 연결을 확인해 주세요.');
await new Promise((resolve, reject) => {
const stop = watch(connected, (ready) => {
if (!ready) return;
clearTimeout(timer);
stop();
resolve();
});
const timer = setTimeout(() => {
stop();
reject(Error('서버에 다시 연결하지 못했어요. 잠시 후 다시 시도해 주세요.'));
}, 5000);
socket.connect();
});
}
export async function requestTransfer(path, input) {
const key = crypto.randomUUID(),
owner = device.value?.id;
try {
transferConnecting.value = !socket?.connected;
await waitForConnection();
const deadline = Date.now() + 5000;
while (true) {
if (device.value?.id !== owner)
throw Error('기기 세션이 변경되었습니다. 다시 선택해 주세요.');
try {
return await api(path, 'POST', input, key);
} catch (error) {
// Only a known, still-authorized peer in its reconnect grace period is retried.
if (error.code !== 'TARGET_RECONNECTING' || Date.now() >= deadline) throw error;
transferConnecting.value = true;
await new Promise((resolve) => setTimeout(resolve, 500));
}
}
} finally {
transferConnecting.value = false;
}
}
const drafts = new Map();
const completing = new Set();
export async function api(path, method = 'GET', body, key, signal) {
@@ -222,17 +263,12 @@ export const refresh = createRefreshQueue(async () => {
});
export async function send(targetDeviceId, kind, values) {
const content = kind === 'text' ? values.text : values.urls.join('\n');
const { requestId } = await api(
'/transfer-requests',
'POST',
{
targetDeviceId,
kind,
byteSize: new TextEncoder().encode(content).length,
count: kind === 'text' ? 1 : values.urls.length,
},
crypto.randomUUID(),
);
const { requestId } = await requestTransfer('/transfer-requests', {
targetDeviceId,
kind,
byteSize: new TextEncoder().encode(content).length,
count: kind === 'text' ? 1 : values.urls.length,
});
drafts.set(requestId, { ...values, key: crypto.randomUUID() });
await refresh();
return requestId;
@@ -317,7 +353,7 @@ export async function sendFiles(targetDeviceId, files) {
type: f.type || 'application/octet-stream',
})),
};
const { requestId } = await api('/transfer-requests', 'POST', input, crypto.randomUUID());
const { requestId } = await requestTransfer('/transfer-requests', input);
fileDrafts.set(requestId, files);
await refresh();
return requestId;