Skip to content

Commit 7351ab5

Browse files
authored
Merge pull request #7219 from LibreSign/backport/7218/stable32
[stable32] chore: migrate to Vue3
2 parents 936d705 + e98a24a commit 7351ab5

8 files changed

Lines changed: 1050 additions & 421 deletions

File tree

src/components/FileStatusList.vue

Lines changed: 118 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -42,127 +42,143 @@
4242
</div>
4343
</template>
4444

45-
<script>
45+
<script setup lang="ts">
4646
import { mdiFilePdfBox } from '@mdi/js'
4747
import { formatFileSize } from '@nextcloud/files'
4848
import { n, t } from '@nextcloud/l10n'
4949
import Moment from '@nextcloud/moment'
5050
import axios from '@nextcloud/axios'
5151
import { generateOcsUrl } from '@nextcloud/router'
52+
import { onBeforeUnmount, onMounted, ref } from 'vue'
5253
5354
import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent'
5455
import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
5556
56-
import { FILE_STATUS } from '../../constants.js'
57-
import { getStatusLabel, getStatusIcon } from '../../utils/fileStatus.js'
57+
import { FILE_STATUS } from '../constants.js'
58+
import { getStatusLabel, getStatusIcon } from '../utils/fileStatus.js'
5859
59-
export default {
60+
defineOptions({
6061
name: 'FileStatusList',
61-
components: {
62-
NcEmptyContent,
63-
NcIconSvgWrapper,
64-
},
65-
props: {
66-
fileIds: {
67-
type: Array,
68-
default: () => [],
69-
},
70-
updateInterval: {
71-
type: Number,
72-
default: 2000, // 2 seconds
73-
},
74-
},
75-
emits: ['file-signed', 'files-updated'],
76-
setup() {
77-
return {
78-
t,
79-
n,
80-
formatFileSize,
81-
getStatusIcon,
82-
getStatusLabel,
83-
mdiFilePdfBox,
84-
}
85-
},
86-
data() {
87-
return {
88-
files: [],
89-
isLoading: false,
90-
updatePollingInterval: null,
91-
previouslySignedIds: [],
92-
}
93-
},
94-
mounted() {
95-
this.loadFiles()
96-
this.startUpdatePolling()
97-
},
98-
beforeUnmount() {
99-
this.stopUpdatePolling()
100-
},
101-
methods: {
102-
async loadFiles() {
103-
if (this.isLoading || this.fileIds.length === 0) {
104-
return
105-
}
62+
})
63+
64+
type FileEntry = {
65+
id: number
66+
uuid?: string
67+
name: string
68+
status?: string | number
69+
size?: number
70+
signedAt?: string
71+
}
10672
107-
this.isLoading = true
108-
try {
109-
const { data } = await axios.get(
110-
generateOcsUrl('/apps/libresign/api/v1/file/list'),
111-
{ timeout: 10000 }
112-
)
73+
const props = withDefaults(defineProps<{
74+
fileIds?: number[]
75+
updateInterval?: number
76+
}>(), {
77+
fileIds: () => [],
78+
updateInterval: 2000,
79+
})
80+
81+
const emit = defineEmits<{
82+
(event: 'file-signed', file: FileEntry): void
83+
(event: 'files-updated', files: FileEntry[]): void
84+
}>()
85+
86+
const files = ref<FileEntry[]>([])
87+
const isLoading = ref(false)
88+
const updatePollingInterval = ref<ReturnType<typeof setInterval> | null>(null)
89+
const previouslySignedIds = ref<number[]>([])
90+
91+
async function loadFiles() {
92+
if (isLoading.value || props.fileIds.length === 0) {
93+
return
94+
}
11395
114-
const fileList = data.ocs?.data?.data ?? []
115-
const fileMap = new Map(fileList.map(f => [f.id, f]))
96+
isLoading.value = true
97+
try {
98+
const { data } = await axios.get(
99+
generateOcsUrl('/apps/libresign/api/v1/file/list'),
100+
{ timeout: 10000 },
101+
)
116102
117-
this.files = this.fileIds
118-
.map(id => fileMap.get(id))
119-
.filter(Boolean)
103+
const fileList = data.ocs?.data?.data ?? []
104+
const fileMap = new Map(fileList.map((file: FileEntry) => [file.id, file]))
120105
121-
this.$emit('files-updated', this.files)
106+
files.value = props.fileIds
107+
.map(id => fileMap.get(id))
108+
.filter((file): file is FileEntry => Boolean(file))
122109
123-
const signedFile = this.files.find(f => f.status === FILE_STATUS.SIGNED)
124-
if (signedFile && !this.previouslySignedIds?.includes(signedFile.id)) {
125-
this.$emit('file-signed', signedFile)
126-
}
110+
emit('files-updated', files.value)
127111
128-
this.previouslySignedIds = this.files
129-
.filter(f => f.status === FILE_STATUS.SIGNED)
130-
.map(f => f.id)
131-
} catch (error) {
132-
console.error('[libresign][FileStatusList] Error loading files:', error)
133-
} finally {
134-
this.isLoading = false
135-
}
136-
},
137-
startUpdatePolling() {
138-
this.updatePollingInterval = setInterval(() => {
139-
this.loadFiles()
140-
}, this.updateInterval)
141-
},
142-
stopUpdatePolling() {
143-
if (this.updatePollingInterval) {
144-
clearInterval(this.updatePollingInterval)
145-
this.updatePollingInterval = null
146-
}
147-
},
148-
getStatusClass(status) {
149-
const statusMap = {
150-
[FILE_STATUS.NOT_LIBRESIGN_FILE]: 'not-libresign',
151-
[FILE_STATUS.DRAFT]: 'draft',
152-
[FILE_STATUS.ABLE_TO_SIGN]: 'ready',
153-
[FILE_STATUS.PARTIAL_SIGNED]: 'partial',
154-
[FILE_STATUS.SIGNED]: 'signed',
155-
[FILE_STATUS.DELETED]: 'deleted',
156-
[FILE_STATUS.SIGNING_IN_PROGRESS]: 'signing',
157-
}
158-
return statusMap[status] || 'unknown'
159-
},
160-
formatDate(date) {
161-
if (!date) return ''
162-
return Moment(date).calendar()
163-
},
164-
},
112+
const signedFile = files.value.find(file => file.status === FILE_STATUS.SIGNED)
113+
if (signedFile && !previouslySignedIds.value.includes(signedFile.id)) {
114+
emit('file-signed', signedFile)
115+
}
116+
117+
previouslySignedIds.value = files.value
118+
.filter(file => file.status === FILE_STATUS.SIGNED)
119+
.map(file => file.id)
120+
} catch (error) {
121+
console.error('[libresign][FileStatusList] Error loading files:', error)
122+
} finally {
123+
isLoading.value = false
124+
}
165125
}
126+
127+
function startUpdatePolling() {
128+
updatePollingInterval.value = setInterval(() => {
129+
loadFiles()
130+
}, props.updateInterval)
131+
}
132+
133+
function stopUpdatePolling() {
134+
if (updatePollingInterval.value) {
135+
clearInterval(updatePollingInterval.value)
136+
updatePollingInterval.value = null
137+
}
138+
}
139+
140+
function getStatusClass(status: string | number | undefined) {
141+
const statusMap: Record<string | number, string> = {
142+
[FILE_STATUS.NOT_LIBRESIGN_FILE]: 'not-libresign',
143+
[FILE_STATUS.DRAFT]: 'draft',
144+
[FILE_STATUS.ABLE_TO_SIGN]: 'ready',
145+
[FILE_STATUS.PARTIAL_SIGNED]: 'partial',
146+
[FILE_STATUS.SIGNED]: 'signed',
147+
[FILE_STATUS.DELETED]: 'deleted',
148+
[FILE_STATUS.SIGNING_IN_PROGRESS]: 'signing',
149+
}
150+
return status === undefined ? 'unknown' : statusMap[status] || 'unknown'
151+
}
152+
153+
function formatDate(date: string | undefined) {
154+
if (!date) {
155+
return ''
156+
}
157+
return Moment(date).calendar()
158+
}
159+
160+
onMounted(() => {
161+
loadFiles()
162+
startUpdatePolling()
163+
})
164+
165+
onBeforeUnmount(() => {
166+
stopUpdatePolling()
167+
})
168+
169+
defineExpose({
170+
files,
171+
isLoading,
172+
updatePollingInterval,
173+
previouslySignedIds,
174+
loadFiles,
175+
startUpdatePolling,
176+
stopUpdatePolling,
177+
getStatusClass,
178+
formatDate,
179+
getStatusIcon,
180+
getStatusLabel,
181+
})
166182
</script>
167183

168184
<style lang="scss" scoped>

src/tests/components/File/File.spec.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ import { mount } from '@vue/test-utils'
88

99
import File from '../../../components/File/File.vue'
1010

11+
type FileEntry = {
12+
id: number
13+
nodeId?: number
14+
name: string
15+
status: number
16+
statusText: string
17+
}
18+
1119
const filesStoreMock = {
1220
selectedFileId: 7,
1321
files: {
@@ -18,7 +26,7 @@ const filesStoreMock = {
1826
status: 3,
1927
statusText: 'Signed',
2028
},
21-
},
29+
} as Record<number, FileEntry>,
2230
selectFile: vi.fn(),
2331
}
2432

@@ -118,4 +126,4 @@ describe('File.vue', () => {
118126
expect(wrapper.vm.statusToClass(3)).toBe('signed')
119127
expect(wrapper.vm.statusToClass(999)).toBe('')
120128
})
121-
})
129+
})

0 commit comments

Comments
 (0)