77 lines
3.0 KiB
Vue
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>
|