Skip to content
Open
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
7 changes: 6 additions & 1 deletion src/composables/useEdgeStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const selectedFiles = ref([])
const isDownloading = ref(false)
const showDragAndDrop = ref(false)
const folderPath = ref('')
const isFilesLoading = ref(false)
const hadFilesBeforeLoading = ref(false)

const processProgress = computed(() => {
if (operationType.value === 'upload') {
Expand Down Expand Up @@ -195,6 +197,7 @@ export const useEdgeStorage = () => {
if (successCount) {
filesTableNeedRefresh.value = true
showDragAndDrop.value = false
hadFilesBeforeLoading.value = true
handleToast(
failureCount > 0 ? 'warn' : 'success',
failureCount > 0 ? 'Upload Partially Completed' : 'Upload Successful',
Expand Down Expand Up @@ -425,6 +428,8 @@ export const useEdgeStorage = () => {
selectedFiles,
isDownloading,
showDragAndDrop,
folderPath
folderPath,
isFilesLoading,
hadFilesBeforeLoading
}
}
17 changes: 9 additions & 8 deletions src/templates/list-table-block/folder-list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,9 @@
if (rowData.isSkeletonRow || rowData.isFolder || rowData.isParentNav || rowData.isNewFolder)
return

const isSelected = selectedItems.value.includes(rowData)
const isSelected = selectedItems.value.some((item) => item.id === rowData.id)
if (isSelected) {
selectedItems.value = selectedItems.value.filter((item) => item !== rowData)
selectedItems.value = selectedItems.value.filter((item) => item.id !== rowData.id)
} else {
selectedItems.value = [...selectedItems.value, rowData]
}
Expand All @@ -358,13 +358,12 @@
const selectableRows = filterData.value.filter(
(row) => !row.isFolder && !row.isParentNav && !row.isNewFolder && !row.isSkeletonRow
)
return (
selectableRows.length > 0 && selectableRows.every((row) => selectedItems.value.includes(row))
)
const selectedIds = selectedItems.value.map((item) => item.id)
return selectableRows.length > 0 && selectableRows.every((row) => selectedIds.includes(row.id))
})

const stateClassRules = (row) => {
if (selectedItems.value.find((item) => item.id === row.id)) {
if (selectedItems.value.some((item) => item.id === row.id)) {
return 'bg-[var(--table-body-row-hover-bg)] bg-altered'
}
return ''
Expand Down Expand Up @@ -704,8 +703,10 @@
height="1.5rem"
/>
<Checkbox
v-else-if="selectedItems.includes(rowData) && !rowData.isNewFolder"
:model-value="selectedItems.includes(rowData)"
v-else-if="
selectedItems.some((item) => item.id === rowData.id) && !rowData.isNewFolder
"
:model-value="selectedItems.some((item) => item.id === rowData.id)"
@update:model-value="toggleRowSelection(rowData)"
@click.stop="toggleRowSelection(rowData)"
binary
Expand Down
84 changes: 56 additions & 28 deletions src/views/EdgeStorage/ListView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@
<ProgressCard />

<DragAndDrop
v-if="showDragAndDrop"
v-if="!shouldShowTable"
:selectedBucket="selectedBucket"
:isLoading="isFilesLoading"
@reload="handleRefresh"
/>
<div
v-else
v-else-if="shouldShowTable"
class="flex flex-col gap-3 items-center border-1 border-transparent justify-center w-full"
:class="{ 'border-dashed border-[#f3652b] rounded-md pb-4': isDragOver }"
>
Expand Down Expand Up @@ -193,7 +194,9 @@
isDownloading,
showDragAndDrop,
folderPath,
isProcessing
isProcessing,
isFilesLoading,
hadFilesBeforeLoading
} = useEdgeStorage()
const { isGreaterThanMD } = useResize()
const { openDeleteDialog } = useDeleteDialog()
Expand Down Expand Up @@ -255,8 +258,21 @@
return selectedBucket.value && (!selectedBucket.value.files || filesTableNeedRefresh.value)
})

/**
* Determines whether to show the table or DragAndDrop component.
* During loading: uses the previous state (hadFilesBeforeLoading) to avoid flicker
* After loading: uses the actual data state (showDragAndDrop)
*/
const shouldShowTable = computed(() => {
if (isFilesLoading.value) {
return hadFilesBeforeLoading.value
}
return !showDragAndDrop.value
})

const selectBucket = (bucket) => {
showDragAndDrop.value = false
hadFilesBeforeLoading.value = false
selectedBucket.value = bucket
if (!route.query?.folderPath) {
folderPath.value = ''
Expand All @@ -267,6 +283,7 @@
}

const handleBreadcrumbClick = (item) => {
selectedFiles.value = []
if (item.index === 0) {
folderPath.value = ''
} else {
Expand Down Expand Up @@ -327,6 +344,7 @@
if (item.isParentNav) {
goBackToBucket()
} else if (item.isFolder) {
selectedFiles.value = []
folderPath.value += item.name
router.replace({ query: folderPath.value ? { folderPath: folderPath.value } : {} })
filesTableNeedRefresh.value = true
Expand All @@ -336,6 +354,7 @@
}

const goBackToBucket = async () => {
selectedFiles.value = []
const pathSegments = folderPath.value.split('/').filter((segment) => segment !== '')
pathSegments.pop()
folderPath.value = pathSegments.length > 0 ? pathSegments.join('/') + '/' : ''
Expand Down Expand Up @@ -371,32 +390,41 @@

const listEdgeStorageBucketFiles = async () => {
if (needFetchToAPI.value) {
const { files, continuation_token } = await edgeStorageService.listEdgeStorageBucketFiles(
selectedBucket.value.name,
false,
folderPath.value,
selectedBucket.value?.continuation_token
)
if (selectedBucket.value) {
selectedBucket.value.continuation_token = continuation_token || null
const filterFiles = files.map((file) => ({
...file,
name: file.name.replace(folderPath.value, '')
}))
if (folderPath.value && !isPaginationLoading.value) {
filterFiles.unshift({
id: '..',
name: '..',
isParentNav: true,
isFolder: true
})
}
if (isPaginationLoading.value) {
selectedBucket.value.files = [...selectedBucket.value.files, ...filterFiles]
} else {
selectedBucket.value.files = filterFiles
if (!isPaginationLoading.value) {
// Remember if bucket had files before loading to prevent UI flicker
hadFilesBeforeLoading.value = (selectedBucket.value?.files?.length ?? 0) > 0
isFilesLoading.value = true
}
try {
const { files, continuation_token } = await edgeStorageService.listEdgeStorageBucketFiles(
selectedBucket.value.name,
false,
folderPath.value,
selectedBucket.value?.continuation_token
)
if (selectedBucket.value) {
selectedBucket.value.continuation_token = continuation_token || null
const filterFiles = files.map((file) => ({
...file,
name: file.name.replace(folderPath.value, '')
}))
if (folderPath.value && !isPaginationLoading.value) {
filterFiles.unshift({
id: '..',
name: '..',
isParentNav: true,
isFolder: true
})
}
if (isPaginationLoading.value) {
selectedBucket.value.files = [...selectedBucket.value.files, ...filterFiles]
} else {
selectedBucket.value.files = filterFiles
}
filesTableNeedRefresh.value = false
}
filesTableNeedRefresh.value = false
} finally {
isFilesLoading.value = false
}
}

Expand Down
93 changes: 64 additions & 29 deletions src/views/EdgeStorage/components/DragAndDrop.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,82 @@
<div
class="flex flex-col items-center gap-5 justify-center w-full mx-auto text-center py-16 border border-solid surface-border rounded-lg transition-colors"
>
<div
class="rounded-full border surface-border flex items-center justify-center w-[90px] h-[90px] mb-1"
>
<i class="pi pi-cloud-upload text-4xl text-color-primary"></i>
</div>

<div class="flex flex-col gap-4">
<h3 class="text-lg font-medium text-color-primary">
Drag files here to add them to your bucket
</h3>

<div class="flex justify-center">
<SplitButton
size="small"
label="Add to files"
@click="openFileSelector('files')"
:model="uploadMenuItems"
primary
class="whitespace-nowrap"
:disabled="isProcessing"
:menuButtonProps="{
class: 'rounded-l-none',
style: { color: 'var(--primary-text-color) !important' }
}"
:pt="{
root: { class: 'h-[2rem]' }
}"
<!-- Loading State -->
<template v-if="isLoading">
<Skeleton
shape="circle"
width="90px"
height="90px"
class="mb-1"
/>
<div class="flex flex-col gap-4 items-center">
<Skeleton
width="280px"
height="1.5rem"
/>
<Skeleton
width="100px"
height="2rem"
/>
</div>
<Skeleton
width="250px"
height="1rem"
/>
</template>

<!-- Empty State -->
<template v-else>
<div
class="rounded-full border surface-border flex items-center justify-center w-[90px] h-[90px] mb-1"
>
<i class="pi pi-cloud-upload text-4xl text-color-primary"></i>
</div>

<div class="flex flex-col gap-4">
<h3 class="text-lg font-medium text-color-primary">
Drag files here to add them to your bucket
</h3>

<div class="flex justify-center">
<SplitButton
size="small"
label="Add to files"
@click="openFileSelector('files')"
:model="uploadMenuItems"
primary
class="whitespace-nowrap"
:disabled="isProcessing"
:menuButtonProps="{
class: 'rounded-l-none',
style: { color: 'var(--primary-text-color) !important' }
}"
:pt="{
root: { class: 'h-[2rem]' }
}"
/>
</div>
</div>
</div>

<p class="text-sm text-color-secondary">Files larger than 300 MB cannot be uploaded.</p>
<p class="text-sm text-color-secondary">Files larger than 300 MB cannot be uploaded.</p>
</template>
</div>
</div>
</template>

<script setup>
import { onMounted, onUnmounted } from 'vue'
import SplitButton from 'primevue/splitbutton'
import Skeleton from 'primevue/skeleton'
import { useEdgeStorage } from '@/composables/useEdgeStorage'

defineProps({
isLoading: {
type: Boolean,
default: false
}
})

const { uploadFiles, isProcessing } = useEdgeStorage()
const emit = defineEmits(['reload'])

Expand Down
Loading