Organize uploader storage and restore transfer readiness
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user