diff --git a/src/core/file.ts b/src/core/file.ts index e089c46b..ac1f8240 100644 --- a/src/core/file.ts +++ b/src/core/file.ts @@ -1,3 +1,39 @@ +export class KnownFileType { + private static readonly allInstancesByExtension: Record = {}; + + public static readonly CSV = new KnownFileType('csv', 'text/csv'); + public static readonly TSV = new KnownFileType('tsv', 'text/tab-separated-values'); + public static readonly MARKDOWN = new KnownFileType('md', 'text/markdown'); + + public readonly extension: string; + public readonly contentType: string; + + private constructor(extension: string, contentType: string) { + this.extension = extension; + this.contentType = contentType; + + KnownFileType.allInstancesByExtension[extension] = this; + } + + public formatFileName(fileName: string): string { + if (fileName.endsWith(`.${this.extension}`)) { + return fileName; + } + + return `${fileName}.${this.extension}`; + } + + public createBlob(content: string): Blob { + return new Blob([content], { + type: this.contentType, + }); + } + + public static parse(extension: string): KnownFileType | undefined { + return KnownFileType.allInstancesByExtension[extension]; + } +} + export interface ImportFileTypeAndExtensions { readonly type: string; readonly extensions?: string; diff --git a/src/views/desktop/statistics/transaction/dialogs/ExportDialog.vue b/src/views/desktop/statistics/transaction/dialogs/ExportDialog.vue index 93e16b16..356b0d34 100644 --- a/src/views/desktop/statistics/transaction/dialogs/ExportDialog.vue +++ b/src/views/desktop/statistics/transaction/dialogs/ExportDialog.vue @@ -12,17 +12,17 @@ + @click="fileFormat = KnownFileType.CSV.extension"> + @click="fileFormat = KnownFileType.TSV.extension"> + @click="fileFormat = KnownFileType.MARKDOWN.extension"> @@ -91,6 +91,7 @@ import { useI18n } from '@/locales/helpers.ts'; import { useUserStore } from '@/stores/user.ts'; +import { KnownFileType } from '@/core/file.ts'; import { replaceAll } from '@/lib/common.ts'; import { copyTextToClipboard, startDownloadFile } from '@/lib/ui/common.ts'; @@ -115,9 +116,21 @@ const snackbar = useTemplateRef('snackbar'); const showState = ref(false); const headers = ref([]); const data = ref([]); -const fileFormat = ref('csv'); +const fileFormat = ref(KnownFileType.CSV.extension); const showRawData = ref(false); +const fileName = computed(() => { + const nickname = userStore.currentUserNickname; + + if (nickname) { + return tt('dataExport.exportStatisticsFileName', { + nickname: nickname + }); + } + + return tt('dataExport.defaultExportStatisticsFileName'); +}); + const dataTableHeaders = computed(() => { return headers.value.map((header, index) => ({ key: index.toString(), @@ -143,10 +156,10 @@ const dataTableItems = computed(() => { const exportedData = computed(() => { let ret = ''; - if (fileFormat.value === 'csv' || fileFormat.value === 'tsv') { + if (fileFormat.value === KnownFileType.CSV.extension || fileFormat.value === KnownFileType.TSV.extension) { let separator = ','; - if (fileFormat.value === 'tsv') { + if (fileFormat.value === KnownFileType.TSV.extension) { separator = '\t'; } @@ -158,7 +171,7 @@ const exportedData = computed(() => { ret += '\n'; ret += row.map(item => replaceAll(item, separator, ' ')).join(separator); } - } else if (fileFormat.value === 'md') { + } else if (fileFormat.value === KnownFileType.MARKDOWN.extension) { ret += '| ' + headers.value.map(item => replaceAll(item, '|', ' ')).join(' | ') + ' |'; ret += '\n'; ret += '| ' + headers.value.map(() => '---').join(' | ') + ' |'; @@ -173,22 +186,10 @@ const exportedData = computed(() => { return ret; }); -function getExportFileName(fileExtension: string): string { - const nickname = userStore.currentUserNickname; - - if (nickname) { - return tt('dataExport.exportStatisticsFileName', { - nickname: nickname - }) + '.' + fileExtension; - } - - return tt('dataExport.defaultExportStatisticsFileName') + '.' + fileExtension; -} - function open(options: { headers: string[], data: string[][] }): void { headers.value = options.headers || []; data.value = options.data || []; - fileFormat.value = 'csv'; + fileFormat.value = KnownFileType.CSV.extension; showRawData.value = false; showState.value = true; } @@ -199,15 +200,13 @@ function copy(): void { } function save(): void { - let contentType = 'text/csv'; + let fileType = KnownFileType.parse(fileFormat.value); - if (fileFormat.value === 'tsv') { - contentType = 'text/tab-separated-values'; - } else if (fileFormat.value === 'md') { - contentType = 'text/markdown'; + if (!fileType) { + fileType = KnownFileType.CSV; } - startDownloadFile(getExportFileName(fileFormat.value), new Blob([exportedData.value], { type: contentType })); + startDownloadFile(fileType.formatFileName(fileName.value), fileType.createBlob(exportedData.value)); } function cancel(): void {