DodoTransfer/app/src/components/MyUploadsModal.vue
2026-11-05 10:28:10 +09:00

77 lines
3.0 KiB
Vue

<script setup>
import { ref, reactive, onMounted } from 'vue';
import Modal from './Modal.vue';
import Icon from './Icon.vue';
import { t } from '../lib/i18n.js';
import { fmtDate, fmtSize, remain } from '../lib/format.js';
import { readMine, removeMine } from '../lib/mine.js';
import { authHeaders } from '../lib/api.js';
import { copyText, toast } from '../lib/toast.js';
const emit = defineEmits(['close']);
const items = ref(readMine());
// sid -> 'ok' | 'gone' | 'expired'
const status = reactive({});
onMounted(async () => {
const now = Date.now();
await Promise.all(items.value.map(async item => {
if (item.expiresAt && item.expiresAt <= now) {
status[item.sid] = 'expired';
return;
}
// used up one-time files and files removed by the admin are gone on the server:
// a missing bucket is 404, a password protected one answers 401
try {
const res = await fetch(`${ encodeURIComponent(item.sid) }.json`, { headers: authHeaders(), cache: 'no-store' });
status[item.sid] = res.status === 404 ? 'gone' : 'ok';
} catch (e) {
status[item.sid] = 'ok';
}
}));
});
function summary(item) {
const first = item.names[0] || '';
return item.names.length > 1 ? `${ first } +${ item.names.length - 1 }` : first;
}
function remove(item) {
removeMine(item.sid);
items.value = items.value.filter(i => i.sid !== item.sid);
toast(t('removeFromList'));
}
</script>
<template>
<Modal :title="t('myUploads')" @close="emit('close')">
<p v-if="!items.length" class="muted">{{ t('myUploadsEmpty') }}</p>
<div v-for="item in items" :key="item.sid" class="my-item">
<div class="rm">
<div class="rn">{{ summary(item) }}</div>
<div class="rs">{{ fmtDate(item.createdAt) }} · {{ fmtSize(item.size) }} · <span class="mono">{{ item.sid }}</span></div>
</div>
<span v-if="status[item.sid] === 'expired'" class="tag bad">{{ t('statusExpired') }}</span>
<span v-else-if="status[item.sid] === 'gone'" class="tag warn">{{ t('statusGone') }}</span>
<span v-else-if="status[item.sid] === 'ok'" class="tag ok">
{{ item.expiresAt ? remain(item.expiresAt) : t('oneTimeDownload') }}
</span>
<div class="acts">
<template v-if="status[item.sid] === 'ok'">
<button class="btn sm ic-only" :title="t('copyLink')" :aria-label="t('copyLink')" @click="copyText(item.link, t('linkCopied'))">
<Icon name="copy" :size="16" />
</button>
<a class="btn sm ic-only" :href="item.link" :title="t('openLink')" :aria-label="t('openLink')"><Icon name="link" :size="16" /></a>
</template>
<button class="btn sm ic-only ghost" :title="t('removeFromList')" :aria-label="t('removeFromList')" @click="remove(item)">
<Icon name="x" :size="16" />
</button>
</div>
</div>
<template #footer>
<div class="note"><Icon name="info" :size="15" /><span>{{ t('myUploadsNote') }}</span></div>
</template>
</Modal>
</template>