Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 53 additions & 7 deletions src/components/filelist.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
@mouseup="uploadFilesUp"
@touchstart="e => e.preventDefault() || uploadFilesDown()"
@touchend="e => e.preventDefault() || uploadFilesUp()"
style="transition: all 0.2s ease; border-radius: 8px;"
class="mx-1"
>
<v-badge
v-if="uploadlist.filter(e => !e.uploaded && !e.aborted && !e.fail).length"
Expand All @@ -48,6 +50,8 @@
item-props
width="min(480px, calc(100vw - 24px))"
max-height="540"
style="border-radius: 8px; background: rgba(var(--v-theme-surface), 0.95); backdrop-filter: blur(10px);"
class="elevation-4"
>
<v-list-item v-for="e, i in uploadlist" v-ripple>
<template v-slot:prepend>
Expand Down Expand Up @@ -94,6 +98,20 @@
</v-list-item>
</v-list>
</v-menu>
<v-tooltip
:text="t('titleRefresh')"
>
<template v-slot:activator="{ props }">
<v-btn
v-bind="props"
variant="text"
icon="$mdiRefresh"
@click="refreshFileList"
style="transition: all 0.2s ease; border-radius: 8px;"
class="mx-1"
></v-btn>
</template>
</v-tooltip>
<v-tooltip
v-if="filelist.allow_upload"
:text="t('titleCreateFolder')"
Expand Down Expand Up @@ -137,7 +155,7 @@
</v-tooltip>
</Teleport>

<v-card class="my-4">
<v-card class="my-4 elevation-2" style="border-radius: 12px;">
<div class="d-flex flex-column flex-sm-row align-sm-center">
<v-breadcrumbs :items="breadcrumb" class="flex-grow-1 overflow-x-auto py-2 py-sm-4">
<template v-slot:divider>
Expand Down Expand Up @@ -182,6 +200,8 @@
:label="t('headerSearch')"
:append-inner-icon="search ? '$mdiCloseCircle' : '$mdiMagnify'"
@click:append-inner="search = ''"
style="border-radius: 8px;"
class="elevation-1"
></v-text-field>
</div>
</div>
Expand All @@ -190,9 +210,9 @@
:loading="filelistSkeleton"
type="table-tbody"
>
<v-table class="w-100" :class="{'overflow-x-auto': display.xs.value}" style="font-size:1rem">
<v-table class="w-100" :class="{'overflow-x-auto': display.xs.value}" style="font-size:1rem; border-radius: 8px;">
<thead>
<tr style="color:rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity))">
<tr style="color:rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity)); background: rgba(var(--v-theme-primary), 0.05);">
<th class="w-100">
<span
style="cursor:pointer"
Expand Down Expand Up @@ -266,6 +286,9 @@
<tr
v-for="p in filelistPathsSorted.slice((filelistPage - 1) * filelistPageSize, filelistPage * filelistPageSize)"
v-ripple
style="transition: background-color 0.2s ease;"
@mouseenter="$event.target.style.backgroundColor = 'rgba(var(--v-theme-primary), 0.04)'"
@mouseleave="$event.target.style.backgroundColor = 'transparent'"
>
<td>
<div
Expand Down Expand Up @@ -477,8 +500,8 @@
</v-skeleton-loader>
</v-card>

<v-card v-if="readmeItem" class="my-4">
<v-card-title class="d-flex align-center text-subtitle-1">
<v-card v-if="readmeItem" class="my-4 elevation-2" style="border-radius: 12px;">
<v-card-title class="d-flex align-center text-subtitle-1" style="background: linear-gradient(135deg, rgba(var(--v-theme-primary), 0.1), rgba(var(--v-theme-secondary), 0.05)); border-radius: 12px 12px 0 0;">
<v-icon icon="$mdiBookOpenVariant" size="small" class="mr-2"></v-icon>
<span class="flex-grow-1">{{ readmeItem.filename }}</span>
<v-btn
Expand Down Expand Up @@ -517,6 +540,8 @@
@mouseup="filelistPageUp"
@touchstart="e => e.preventDefault() || filelistPageDown()"
@touchend="e => e.preventDefault() || filelistPageUp()"
style="border-radius: 8px;"
elevation="1"
></v-pagination>

<v-dialog
Expand Down Expand Up @@ -1058,6 +1083,11 @@ const updateFilelist = async () => {
};
const updateFilelistResetPage = () => updateFilelist().then(() => filelistPage.value = 1);

const refreshFileList = async () => {
await updateFilelist();
$toast.success(t('toastRefreshSuccess'));
};

const breadcrumb = computed(() => {
const r = [{title: '/', href: pathPrefix}];
let h = pathPrefix;
Expand Down Expand Up @@ -1231,7 +1261,15 @@ const uploadFilesClick = async () => {
return new Uploader(
cp + encodeURIComponent(file.name),
file,
() => currentPath.value === cp && updateFilelist(),
() => {
if (currentPath.value === cp) {
updateFilelist();
$toast.success(t('toastUploadSuccess', [file.name]));
}
},
() => {
$toast.error(t('toastUploadError', [file.name]));
}
);
})
.forEach(e => {
Expand Down Expand Up @@ -1277,7 +1315,15 @@ document.body.addEventListener('drop', async e => {
return new Uploader(
cp + encodeURIComponent(path),
file,
() => currentPath.value === cp && updateFilelist(),
() => {
if (currentPath.value === cp) {
updateFilelist();
$toast.success(t('toastUploadSuccess', [file.name]));
}
},
() => {
$toast.error(t('toastUploadError', [file.name]));
}
);
})
.forEach(e => {
Expand Down
12 changes: 12 additions & 0 deletions src/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ messages.en = {
titleUploadFile: 'Upload files',
titleCreateFolder: 'Create folder',
titleDownloadArchive: 'Download root folder as a .zip file',
titleRefresh: 'Refresh file list',
titleLogin: 'Login',
titleLogout: 'Logout ({0})',
titlePageSkip: 'Go to page',
Expand All @@ -34,6 +35,9 @@ messages.en = {
toastMoveFile: 'File moved.',
toastUploadDisabled: 'File uploading is disabled.',
toastCreateFolder: 'New folder created.',
toastRefreshSuccess: 'File list refreshed successfully.',
toastUploadSuccess: 'File {0} uploaded successfully.',
toastUploadError: 'Failed to upload file {0}.',
toastFailedLoadAudioMetadata: 'Failed to read audio metadata: {0}',
dialogDeleteConfirm: 'Are you sure to delete {0}?',
dialogMoveLabel: 'Path',
Expand Down Expand Up @@ -72,6 +76,7 @@ messages['zh-CN'] = {
titleUploadFile: '上传文件',
titleCreateFolder: '新建文件夹',
titleDownloadArchive: '下载当前目录为 ZIP 压缩包',
titleRefresh: '刷新文件列表',
titleLogin: '登录',
titleLogout: '注销({0})',
titlePageSkip: '翻页',
Expand All @@ -81,6 +86,9 @@ messages['zh-CN'] = {
toastMoveFile: '已移动文件',
toastUploadDisabled: '文件上传已禁用',
toastCreateFolder: '已新建文件夹',
toastRefreshSuccess: '文件列表刷新成功',
toastUploadSuccess: '文件 {0} 上传成功',
toastUploadError: '文件 {0} 上传失败',
toastFailedLoadAudioMetadata: '加载音频元数据失败:{0}',
dialogDeleteConfirm: '确定要删除 {0} 吗?',
dialogMoveLabel: '路径',
Expand Down Expand Up @@ -114,6 +122,7 @@ messages['zh-HK'] = {
titleUploadFile: '上傳文件',
titleCreateFolder: '新建文件夾',
titleDownloadArchive: '下載當前目錄為 ZIP 壓縮包',
titleRefresh: '刷新文件列表',
titleLogin: '登入',
titleLogout: '登出({0})',
titlePageSkip: '翻頁',
Expand All @@ -122,6 +131,9 @@ messages['zh-HK'] = {
toastMoveFile: '已移動文件',
toastUploadDisabled: '文件上傳已禁用',
toastCreateFolder: '已新建文件夾',
toastRefreshSuccess: '文件列表刷新成功',
toastUploadSuccess: '文件 {0} 上傳成功',
toastUploadError: '文件 {0} 上傳失敗',
toastFailedLoadAudioMetadata: '加載音頻元數據失敗:{0}',
dialogDeleteConfirm: '確定要刪除 {0} 嗎?',
dialogMoveLabel: '路徑',
Expand Down
2 changes: 2 additions & 0 deletions src/uploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class Uploader {
if (xhr.readyState !== XMLHttpRequest.DONE) return;
if (xhr.status >= 400) {
this.fail = xhr.statusText;
this.onError();
semaphore.release();
} else if (xhr.status >= 200 && xhr.status < 300) {
this.uploaded = true;
this.onSuccess();
Expand Down
2 changes: 2 additions & 0 deletions src/vuetify.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import {
mdiFormatTextWrappingWrap,
mdiUpload,
mdiFolderPlus,
mdiRefresh,
mdiMusic,
mdiPlay,
mdiPause,
Expand Down Expand Up @@ -154,6 +155,7 @@ export default createVuetify({
mdiFormatTextWrappingWrap,
mdiUpload,
mdiFolderPlus,
mdiRefresh,
mdiMusic,
mdiPlay,
mdiPause,
Expand Down