Browse Source

Merge pull request #5046 from nocodb/fix/4992-attachment

fix: drop url availability check with fetch
pull/4956/merge
աɨռɢӄաօռɢ 2 years ago committed by GitHub
parent
commit
5c73cb0395
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      packages/nc-gui/components/cell/attachment/Carousel.vue
  2. 25
      packages/nc-gui/components/cell/attachment/Image.vue
  3. 16
      packages/nc-gui/components/cell/attachment/Modal.vue
  4. 33
      packages/nc-gui/components/cell/attachment/index.vue
  5. 33
      packages/nc-gui/components/cell/attachment/utils.ts
  6. 20
      packages/nc-gui/components/smartsheet/Gallery.vue
  7. 20
      packages/nc-gui/components/smartsheet/Kanban.vue
  8. 40
      packages/nc-gui/composables/useAttachment.ts
  9. 62
      packages/nc-gui/composables/useKanbanViewStore.ts
  10. 60
      packages/nc-gui/composables/useViewData.ts
  11. 1
      packages/nc-gui/utils/index.ts
  12. 714
      packages/nc-gui/utils/mimeTypeUtils.ts
  13. 22
      tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts
  14. 1
      tests/playwright/tests/columnAttachments.spec.ts

28
packages/nc-gui/components/cell/attachment/Carousel.vue

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onKeyDown } from '@vueuse/core' import { onKeyDown } from '@vueuse/core'
import { useAttachmentCell } from './utils' import { useAttachmentCell } from './utils'
import { computed, isImage, onClickOutside, ref } from '#imports' import { computed, isImage, onClickOutside, ref, useAttachment } from '#imports'
const { selectedImage, visibleItems, downloadFile } = useAttachmentCell()! const { selectedImage, visibleItems, downloadFile } = useAttachmentCell()!
@ -9,6 +9,8 @@ const carouselRef = ref()
const imageItems = computed(() => visibleItems.value.filter((item) => isImage(item.title, item.mimetype))) const imageItems = computed(() => visibleItems.value.filter((item) => isImage(item.title, item.mimetype)))
const { getPossibleAttachmentSrc } = useAttachment()
/** navigate to previous image on button click */ /** navigate to previous image on button click */
onKeyDown( onKeyDown(
(e) => ['Left', 'ArrowLeft', 'A'].includes(e.key), (e) => ['Left', 'ArrowLeft', 'A'].includes(e.key),
@ -81,22 +83,16 @@ onClickOutside(carouselRef, () => {
</template> </template>
<template #customPaging="props"> <template #customPaging="props">
<a> <div class="cursor-pointer h-full nc-attachment-img-wrapper">
<LazyNuxtImg <LazyCellAttachmentImage
quality="90" class="!block m-auto h-full w-full"
placeholder
class="!block"
:alt="imageItems[props.i].title || `#${props.i}`" :alt="imageItems[props.i].title || `#${props.i}`"
:src="imageItems[props.i].url || imageItems[props.i].data" :srcs="getPossibleAttachmentSrc(imageItems[props.i])"
/> />
</a> </div>
</template> </template>
<div v-for="(item, idx) of imageItems" :key="idx">
<div v-for="item of imageItems" :key="item.url"> <LazyCellAttachmentImage :srcs="getPossibleAttachmentSrc(item)" class="max-w-70vw max-h-70vh" />
<div
:style="{ backgroundImage: `url('${item.url || item.data}')` }"
class="min-w-70vw min-h-70vh w-full h-full bg-contain bg-center bg-no-repeat"
/>
</div> </div>
</a-carousel> </a-carousel>
</div> </div>
@ -146,4 +142,8 @@ onClickOutside(carouselRef, () => {
.ant-carousel :deep(.custom-slick-arrow:hover) { .ant-carousel :deep(.custom-slick-arrow:hover) {
opacity: 0.5; opacity: 0.5;
} }
.nc-attachment-img-wrapper {
width: fit-content !important;
}
</style> </style>

25
packages/nc-gui/components/cell/attachment/Image.vue

@ -0,0 +1,25 @@
<script setup lang="ts">
interface Props {
srcs: string[]
alt?: string
}
const props = defineProps<Props>()
const index = ref(0)
const onError = () => index.value++
</script>
<template>
<LazyNuxtImg
v-if="index < props.srcs.length"
class="m-auto"
:src="props.srcs[index]"
:alt="props?.alt || ''"
placeholder
quality="75"
@error="onError"
/>
<MdiFileImageBox v-else />
</template>

16
packages/nc-gui/components/cell/attachment/Modal.vue

@ -2,7 +2,7 @@
import { onKeyDown } from '@vueuse/core' import { onKeyDown } from '@vueuse/core'
import { useAttachmentCell } from './utils' import { useAttachmentCell } from './utils'
import { useSortable } from './sort' import { useSortable } from './sort'
import { isImage, openLink, ref, useDropZone, useUIPermission, watch } from '#imports' import { isImage, ref, useAttachment, useDropZone, useUIPermission, watch } from '#imports'
const { isUIAllowed } = useUIPermission() const { isUIAllowed } = useUIPermission()
@ -38,6 +38,8 @@ const { isOverDropZone } = useDropZone(dropZoneRef, onDrop)
const { isSharedForm } = useSmartsheetStoreOrThrow() const { isSharedForm } = useSmartsheetStoreOrThrow()
const { getPossibleAttachmentSrc, openAttachment } = useAttachment()
onKeyDown('Escape', () => { onKeyDown('Escape', () => {
modalVisible.value = false modalVisible.value = false
isOverDropZone.value = false isOverDropZone.value = false
@ -159,12 +161,12 @@ function onRemoveFileClick(title: any, i: number) {
<div <div
:class="[dragging ? 'cursor-move' : 'cursor-pointer']" :class="[dragging ? 'cursor-move' : 'cursor-pointer']"
class="nc-attachment h-full w-full flex items-center justify-center" class="nc-attachment h-full w-full flex items-center justify-center overflow-hidden"
> >
<div <LazyCellAttachmentImage
v-if="isImage(item.title, item.mimetype)" v-if="isImage(item.title, item.mimetype)"
:style="{ backgroundImage: `url('${item.url || item.data}')` }" :srcs="getPossibleAttachmentSrc(item)"
class="w-full h-full bg-contain bg-center bg-no-repeat" class="max-w-full max-h-full m-auto justify-center"
@click.stop="onClick(item)" @click.stop="onClick(item)"
/> />
@ -173,10 +175,10 @@ function onRemoveFileClick(title: any, i: number) {
v-else-if="item.icon" v-else-if="item.icon"
height="150" height="150"
width="150" width="150"
@click.stop="openLink(item.url || item.data)" @click.stop="openAttachment(item)"
/> />
<IcOutlineInsertDriveFile v-else height="150" width="150" @click.stop="openLink(item.url || item.data)" /> <IcOutlineInsertDriveFile v-else height="150" width="150" @click.stop="openAttachment(item)" />
</div> </div>
</a-card> </a-card>

33
packages/nc-gui/components/cell/attachment/index.vue

@ -10,8 +10,8 @@ import {
inject, inject,
isImage, isImage,
nextTick, nextTick,
openLink,
ref, ref,
useAttachment,
useDropZone, useDropZone,
useSelectedCellKeyupListener, useSelectedCellKeyupListener,
useSmartsheetRowStoreOrThrow, useSmartsheetRowStoreOrThrow,
@ -46,6 +46,8 @@ const currentCellRef = ref<Element | undefined>(dropZoneInjection.value)
const { cellRefs, isSharedForm } = useSmartsheetStoreOrThrow()! const { cellRefs, isSharedForm } = useSmartsheetStoreOrThrow()!
const { getPossibleAttachmentSrc, openAttachment } = useAttachment()
const { const {
isPublic, isPublic,
isForm, isForm,
@ -60,7 +62,6 @@ const {
selectedImage, selectedImage,
isReadonly, isReadonly,
storedFiles, storedFiles,
getAttachmentUrl,
} = useProvideAttachmentCell(updateModelValue) } = useProvideAttachmentCell(updateModelValue)
watch( watch(
@ -101,16 +102,7 @@ watch(
async (nextModel) => { async (nextModel) => {
if (nextModel) { if (nextModel) {
try { try {
let nextAttachments = ((typeof nextModel === 'string' ? JSON.parse(nextModel) : nextModel) || []).filter(Boolean) const nextAttachments = ((typeof nextModel === 'string' ? JSON.parse(nextModel) : nextModel) || []).filter(Boolean)
// reconstruct the url
// See /packages/nocodb/src/lib/version-upgrader/ncAttachmentUpgrader.ts for the details
nextAttachments = await Promise.all(
nextAttachments.map(async (attachment: any) => ({
...attachment,
url: await getAttachmentUrl(attachment),
})),
)
if (isPublic.value && isForm.value) { if (isPublic.value && isForm.value) {
storedFiles.value = nextAttachments storedFiles.value = nextAttachments
@ -229,21 +221,12 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e) => {
<template #title> <template #title>
<div class="text-center w-full">{{ item.title }}</div> <div class="text-center w-full">{{ item.title }}</div>
</template> </template>
<div v-if="isImage(item.title, item.mimetype ?? item.type)">
<template v-if="isImage(item.title, item.mimetype ?? item.type) && (item.url || item.data)">
<div class="nc-attachment flex items-center justify-center" @click.stop="selectedImage = item"> <div class="nc-attachment flex items-center justify-center" @click.stop="selectedImage = item">
<LazyNuxtImg <LazyCellAttachmentImage :alt="item.title || `#${i}`" :srcs="getPossibleAttachmentSrc(item)" />
quality="75"
placeholder
fit="cover"
:alt="item.title || `#${i}`"
:src="item.url || item.data"
class="max-w-full max-h-full"
/>
</div> </div>
</template> </div>
<div v-else class="nc-attachment flex items-center justify-center" @click="openAttachment(item)">
<div v-else class="nc-attachment flex items-center justify-center" @click="openLink(item.url || item.data)">
<component :is="FileIcon(item.icon)" v-if="item.icon" /> <component :is="FileIcon(item.icon)" v-if="item.icon" />
<IcOutlineInsertDriveFile v-else /> <IcOutlineInsertDriveFile v-else />

33
packages/nc-gui/components/cell/attachment/utils.ts

@ -14,6 +14,7 @@ import {
message, message,
ref, ref,
useApi, useApi,
useAttachment,
useFileDialog, useFileDialog,
useI18n, useI18n,
useInjectionState, useInjectionState,
@ -60,6 +61,8 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
const { t } = useI18n() const { t } = useI18n()
const { getAttachmentSrc } = useAttachment()
const defaultAttachmentMeta = { const defaultAttachmentMeta = {
...(appInfo.value.ee && { ...(appInfo.value.ee && {
// Maximum Number of Attachments per cell // Maximum Number of Attachments per cell
@ -226,31 +229,12 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
/** download a file */ /** download a file */
async function downloadFile(item: AttachmentType) { async function downloadFile(item: AttachmentType) {
;(await import('file-saver')).saveAs(item.url || item.data, item.title) const src = await getAttachmentSrc(item)
} if (src) {
;(await import('file-saver')).saveAs(src, item.title)
/** construct the attachment url } else {
* See /packages/nocodb/src/lib/version-upgrader/ncAttachmentUpgrader.ts for the details message.error('Failed to download file')
* */
async function getAttachmentUrl(item: AttachmentType) {
const path = item?.path
// if path doesn't exist, use `item.url`
if (path) {
// try ${appInfo.value.ncSiteUrl}/${item.path} first
const url = `${appInfo.value.ncSiteUrl}/${item.path}`
try {
const res = await fetch(url)
if (res.ok) {
// use `url` if it is accessible
return Promise.resolve(url)
}
} catch {
// for some cases, `url` is not accessible as expected
// do nothing here
}
} }
// if it fails, use the original url
return Promise.resolve(item.url)
} }
const FileIcon = (icon: string) => { const FileIcon = (icon: string) => {
@ -294,7 +278,6 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
storedFiles, storedFiles,
bulkDownloadFiles, bulkDownloadFiles,
defaultAttachmentMeta, defaultAttachmentMeta,
getAttachmentUrl,
} }
}, },
'useAttachmentCell', 'useAttachmentCell',

20
packages/nc-gui/components/smartsheet/Gallery.vue

@ -18,11 +18,13 @@ import {
createEventHook, createEventHook,
extractPkFromRow, extractPkFromRow,
inject, inject,
isImage,
isLTAR, isLTAR,
nextTick, nextTick,
onMounted, onMounted,
provide, provide,
ref, ref,
useAttachment,
useViewData, useViewData,
} from '#imports' } from '#imports'
import type { Row as RowType } from '~/lib' import type { Row as RowType } from '~/lib'
@ -64,6 +66,8 @@ const route = useRoute()
const router = useRouter() const router = useRouter()
const { getPossibleAttachmentSrc } = useAttachment()
const fieldsWithoutCover = computed(() => fields.value.filter((f) => f.id !== galleryData.value?.fk_cover_image_col_id)) const fieldsWithoutCover = computed(() => fields.value.filter((f) => f.id !== galleryData.value?.fk_cover_image_col_id))
const coverImageColumn: any = $( const coverImageColumn: any = $(
@ -199,14 +203,14 @@ watch(view, async (nextView) => {
<div style="z-index: 1"></div> <div style="z-index: 1"></div>
</template> </template>
<LazyNuxtImg <template v-for="(attachment, index) in attachments(record)">
v-for="(attachment, index) in attachments(record)" <LazyCellAttachmentImage
:key="`carousel-${record.row.id}-${index}`" v-if="isImage(attachment.title, attachment.mimetype ?? attachment.type)"
quality="90" :key="`carousel-${record.row.id}-${index}`"
placeholder class="h-52 object-contain"
class="h-52 object-contain" :srcs="getPossibleAttachmentSrc(attachment)"
:src="attachment.url" />
/> </template>
</a-carousel> </a-carousel>
<MdiFileImageBox v-else class="w-full h-48 my-4 text-cool-gray-200" /> <MdiFileImageBox v-else class="w-full h-48 my-4 text-cool-gray-200" />

20
packages/nc-gui/components/smartsheet/Kanban.vue

@ -13,10 +13,12 @@ import {
MetaInj, MetaInj,
OpenNewRecordFormHookInj, OpenNewRecordFormHookInj,
inject, inject,
isImage,
isLTAR, isLTAR,
onBeforeMount, onBeforeMount,
onBeforeUnmount, onBeforeUnmount,
provide, provide,
useAttachment,
useKanbanViewStoreOrThrow, useKanbanViewStoreOrThrow,
} from '#imports' } from '#imports'
import type { Row as RowType } from '~/lib' import type { Row as RowType } from '~/lib'
@ -55,6 +57,8 @@ const route = useRoute()
const router = useRouter() const router = useRouter()
const { getPossibleAttachmentSrc } = useAttachment()
const { const {
loadKanbanData, loadKanbanData,
loadMoreKanbanData, loadMoreKanbanData,
@ -456,14 +460,14 @@ watch(view, async (nextView) => {
<div style="z-index: 1"></div> <div style="z-index: 1"></div>
</template> </template>
<LazyNuxtImg <template v-for="(attachment, index) in attachments(record)">
v-for="(attachment, index) in attachments(record)" <LazyCellAttachmentImage
:key="`carousel-${record.row.id}-${index}`" v-if="isImage(attachment.title, attachment.mimetype ?? attachment.type)"
quality="90" :key="`carousel-${record.row.id}-${index}`"
placeholder class="h-52 object-cover"
class="h-52 object-cover" :srcs="getPossibleAttachmentSrc(attachment)"
:src="attachment.url" />
/> </template>
</a-carousel> </a-carousel>
<MdiFileImageBox v-else class="w-full h-48 my-4 text-cool-gray-200" /> <MdiFileImageBox v-else class="w-full h-48 my-4 text-cool-gray-200" />

40
packages/nc-gui/composables/useAttachment.ts

@ -0,0 +1,40 @@
import { mimeTypes, openLink, useGlobal } from '#imports'
const useAttachment = () => {
const { appInfo } = useGlobal()
const getPossibleAttachmentSrc = (item: Record<string, any>) => {
const res: string[] = []
if (item?.path) res.push(`${appInfo.value.ncSiteUrl}/${item.path}`)
if (item?.url) res.push(item.url)
return res
}
const getAttachmentSrc = async (item: Record<string, any>) => {
if (item?.data) {
return item.data
}
const sources = getPossibleAttachmentSrc(item)
const mimeType = mimeTypes[item?.mimetype?.split('/')?.pop() || 'txt']
for (const source of sources) {
// test if the source is accessible or not
const res = await fetch(source)
if (res.ok && res.headers.get('Content-Type') === mimeType) {
return source
}
}
return null
}
const openAttachment = async (item: Record<string, any>) => {
openLink(await getAttachmentSrc(item))
}
return {
getAttachmentSrc,
getPossibleAttachmentSrc,
openAttachment,
}
}
export default useAttachment

62
packages/nc-gui/composables/useKanbanViewStore.ts

@ -1,15 +1,5 @@
import type { ComputedRef, Ref } from 'vue' import type { ComputedRef, Ref } from 'vue'
import type { import type { Api, ColumnType, KanbanType, SelectOptionType, SelectOptionsType, TableType, ViewType } from 'nocodb-sdk'
Api,
AttachmentType,
ColumnType,
KanbanType,
SelectOptionType,
SelectOptionsType,
TableType,
ViewType,
} from 'nocodb-sdk'
import { UITypes } from 'nocodb-sdk'
import type { Row } from '~/lib' import type { Row } from '~/lib'
import { import {
IsPublicInj, IsPublicInj,
@ -25,7 +15,6 @@ import {
ref, ref,
useApi, useApi,
useFieldQuery, useFieldQuery,
useGlobal,
useI18n, useI18n,
useInjectionState, useInjectionState,
useNuxtApp, useNuxtApp,
@ -55,8 +44,6 @@ const [useProvideKanbanViewStore, useKanbanViewStore] = useInjectionState(
const { $e, $api } = useNuxtApp() const { $e, $api } = useNuxtApp()
const { appInfo } = useGlobal()
const { sorts, nestedFilters } = useSmartsheetStoreOrThrow() const { sorts, nestedFilters } = useSmartsheetStoreOrThrow()
const { sharedView, fetchSharedViewData, fetchSharedViewGroupedData } = useSharedView() const { sharedView, fetchSharedViewData, fetchSharedViewGroupedData } = useSharedView()
@ -91,10 +78,6 @@ const [useProvideKanbanViewStore, useKanbanViewStore] = useInjectionState(
return where return where
}) })
const attachmentColumns = computed(() =>
(meta.value?.columns as ColumnType[])?.filter((c) => c.uidt === UITypes.Attachment).map((c) => c.title),
)
provide(SharedViewPasswordInj, password) provide(SharedViewPasswordInj, password)
// kanban view meta data // kanban view meta data
@ -144,27 +127,6 @@ const [useProvideKanbanViewStore, useKanbanViewStore] = useInjectionState(
rowMeta: {}, rowMeta: {},
})) }))
async function getAttachmentUrl(item: AttachmentType) {
const path = item?.path
// if path doesn't exist, use `item.url`
if (path) {
// try ${appInfo.value.ncSiteUrl}/${item.path} first
const url = `${appInfo.value.ncSiteUrl}/${item.path}`
try {
const res = await fetch(url)
if (res.ok) {
// use `url` if it is accessible
return Promise.resolve(url)
}
} catch {
// for some cases, `url` is not accessible as expected
// do nothing here
}
}
// if it fails, use the original url
return Promise.resolve(item.url)
}
async function loadKanbanData() { async function loadKanbanData() {
if ((!project?.value?.id || !meta.value?.id || !viewMeta?.value?.id || !groupingFieldColumn?.value?.id) && !isPublic.value) if ((!project?.value?.id || !meta.value?.id || !viewMeta?.value?.id || !groupingFieldColumn?.value?.id) && !isPublic.value)
return return
@ -193,28 +155,8 @@ const [useProvideKanbanViewStore, useKanbanViewStore] = useInjectionState(
} }
for (const data of groupData) { for (const data of groupData) {
const records = []
const key = data.key const key = data.key
// TODO: optimize formattedData.value.set(key, formatData(data.value.list))
// reconstruct the url
// See /packages/nocodb/src/lib/version-upgrader/ncAttachmentUpgrader.ts for the details
for (const record of data.value.list) {
for (const attachmentColumn of attachmentColumns.value) {
// attachment column can be hidden
if (!record[attachmentColumn!]) continue
const oldAttachment = JSON.parse(record[attachmentColumn!])
const newAttachment = []
for (const attachmentObj of oldAttachment) {
newAttachment.push({
...attachmentObj,
url: await getAttachmentUrl(attachmentObj),
})
}
record[attachmentColumn!] = newAttachment
}
records.push(record)
}
formattedData.value.set(key, formatData(records))
countByStack.value.set(key, data.value.pageInfo.totalRows || 0) countByStack.value.set(key, data.value.pageInfo.totalRows || 0)
} }
} }

60
packages/nc-gui/composables/useViewData.ts

@ -1,15 +1,5 @@
import { UITypes, ViewTypes } from 'nocodb-sdk' import { UITypes, ViewTypes } from 'nocodb-sdk'
import type { import type { Api, ColumnType, FormColumnType, FormType, GalleryType, PaginatedType, TableType, ViewType } from 'nocodb-sdk'
Api,
AttachmentType,
ColumnType,
FormColumnType,
FormType,
GalleryType,
PaginatedType,
TableType,
ViewType,
} from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue' import type { ComputedRef, Ref } from 'vue'
import { import {
IsPublicInj, IsPublicInj,
@ -90,10 +80,6 @@ export function useViewData(
const { isUIAllowed } = useUIPermission() const { isUIAllowed } = useUIPermission()
const attachmentColumns = computed(() =>
(meta.value?.columns as ColumnType[])?.filter((c) => c.uidt === UITypes.Attachment).map((c) => c.title),
)
const routeQuery = $computed(() => route.query as Record<string, string>) const routeQuery = $computed(() => route.query as Record<string, string>)
const paginationData = computed({ const paginationData = computed({
@ -200,28 +186,6 @@ export function useViewData(
} }
} }
// TODO: refactor
async function getAttachmentUrl(item: AttachmentType) {
const path = item?.path
// if path doesn't exist, use `item.url`
if (path) {
// try ${appInfo.value.ncSiteUrl}/${item.path} first
const url = `${appInfo.ncSiteUrl}/${item.path}`
try {
const res = await fetch(url)
if (res.ok) {
// use `url` if it is accessible
return Promise.resolve(url)
}
} catch {
// for some cases, `url` is not accessible as expected
// do nothing here
}
}
// if it fails, use the original url
return Promise.resolve(item.url)
}
async function loadData(params: Parameters<Api<any>['dbViewRow']['list']>[4] = {}) { async function loadData(params: Parameters<Api<any>['dbViewRow']['list']>[4] = {}) {
if ((!project?.value?.id || !meta.value?.id || !viewMeta.value?.id) && !isPublic.value) return if ((!project?.value?.id || !meta.value?.id || !viewMeta.value?.id) && !isPublic.value) return
const response = !isPublic.value const response = !isPublic.value
@ -233,27 +197,7 @@ export function useViewData(
where: where?.value, where: where?.value,
}) })
: await fetchSharedViewData({ sortsArr: sorts.value, filtersArr: nestedFilters.value }) : await fetchSharedViewData({ sortsArr: sorts.value, filtersArr: nestedFilters.value })
// reconstruct the url formattedData.value = formatData(response.list)
// See /packages/nocodb/src/lib/version-upgrader/ncAttachmentUpgrader.ts for the details
const records = []
for (const record of response.list) {
for (const attachmentColumn of attachmentColumns.value) {
// attachment column can be hidden
if (!record[attachmentColumn!]) continue
const oldAttachment =
typeof record[attachmentColumn!] === 'string' ? JSON.parse(record[attachmentColumn!]) : record[attachmentColumn!]
const newAttachment = []
for (const attachmentObj of oldAttachment) {
newAttachment.push({
...attachmentObj,
url: await getAttachmentUrl(attachmentObj),
})
}
record[attachmentColumn!] = newAttachment
}
records.push(record)
}
formattedData.value = formatData(records)
paginationData.value = response.pageInfo paginationData.value = response.pageInfo
// to cater the case like when querying with a non-zero offset // to cater the case like when querying with a non-zero offset

1
packages/nc-gui/utils/index.ts

@ -20,3 +20,4 @@ export * from './userUtils'
export * from './stringUtils' export * from './stringUtils'
export * from './memStorage' export * from './memStorage'
export * from './browserUtils' export * from './browserUtils'
export * from './mimeTypeUtils'

714
packages/nc-gui/utils/mimeTypeUtils.ts

@ -0,0 +1,714 @@
const mimeTypes: Record<string, string> = {
'123': 'application/vnd.lotus-1-2-3',
'x3d': 'application/vnd.hzn-3d-crossword',
'3gp': 'video/3gpp',
'3g2': 'video/3gpp2',
'mseq': 'application/vnd.mseq',
'pwn': 'application/vnd.3m.post-it-notes',
'plb': 'application/vnd.3gpp.pic-bw-large',
'psb': 'application/vnd.3gpp.pic-bw-small',
'pvb': 'application/vnd.3gpp.pic-bw-var',
'tcap': 'application/vnd.3gpp2.tcap',
'7z': 'application/x-7z-compressed',
'abw': 'application/x-abiword',
'ace': 'application/x-ace-compressed',
'acc': 'application/vnd.americandynamics.acc',
'acu': 'application/vnd.acucobol',
'atc': 'application/vnd.acucorp',
'adp': 'audio/adpcm',
'aab': 'application/x-authorware-bin',
'aam': 'application/x-authorware-map',
'aas': 'application/x-authorware-seg',
'air': 'application/vnd.adobe.air-application-installer-package+zip',
'swf': 'application/x-shockwave-flash',
'fxp': 'application/vnd.adobe.fxp',
'pdf': 'application/pdf',
'ppd': 'application/vnd.cups-ppd',
'dir': 'application/x-director',
'xdp': 'application/vnd.adobe.xdp+xml',
'xfdf': 'application/vnd.adobe.xfdf',
'aac': 'audio/x-aac',
'ahead': 'application/vnd.ahead.space',
'azf': 'application/vnd.airzip.filesecure.azf',
'azs': 'application/vnd.airzip.filesecure.azs',
'azw': 'application/vnd.amazon.ebook',
'ami': 'application/vnd.amiga.ami',
'/A': 'application/andrew-inset',
'apk': 'application/vnd.android.package-archive',
'cii': 'application/vnd.anser-web-certificate-issue-initiation',
'fti': 'application/vnd.anser-web-funds-transfer-initiation',
'atx': 'application/vnd.antix.game-component',
'dmg': 'application/x-apple-diskimage',
'mpkg': 'application/vnd.apple.installer+xml',
'aw': 'application/applixware',
'les': 'application/vnd.hhe.lesson-player',
'swi': 'application/vnd.aristanetworks.swi',
's': 'text/x-asm',
'atomcat': 'application/atomcat+xml',
'atomsvc': 'application/atomsvc+xml',
'atom, .xml': 'application/atom+xml',
'ac': 'application/pkix-attr-cert',
'aif': 'audio/x-aiff',
'avi': 'video/x-msvideo',
'aep': 'application/vnd.audiograph',
'dxf': 'image/vnd.dxf',
'dwf': 'model/vnd.dwf',
'par': 'text/plain-bas',
'bcpio': 'application/x-bcpio',
'bin': 'application/octet-stream',
'bmp': 'image/bmp',
'torrent': 'application/x-bittorrent',
'cod': 'application/vnd.rim.cod',
'mpm': 'application/vnd.blueice.multipass',
'bmi': 'application/vnd.bmi',
'sh': 'application/x-sh',
'btif': 'image/prs.btif',
'rep': 'application/vnd.businessobjects',
'bz': 'application/x-bzip',
'bz2': 'application/x-bzip2',
'csh': 'application/x-csh',
'c': 'text/x-c',
'cdxml': 'application/vnd.chemdraw+xml',
'css': 'text/css',
'cdx': 'chemical/x-cdx',
'cml': 'chemical/x-cml',
'csml': 'chemical/x-csml',
'cdbcmsg': 'application/vnd.contact.cmsg',
'cla': 'application/vnd.claymore',
'c4g': 'application/vnd.clonk.c4group',
'sub': 'image/vnd.dvb.subtitle',
'cdmia': 'application/cdmi-capability',
'cdmic': 'application/cdmi-container',
'cdmid': 'application/cdmi-domain',
'cdmio': 'application/cdmi-object',
'cdmiq': 'application/cdmi-queue',
'c11amc': 'application/vnd.cluetrust.cartomobile-config',
'c11amz': 'application/vnd.cluetrust.cartomobile-config-pkg',
'ras': 'image/x-cmu-raster',
'dae': 'model/vnd.collada+xml',
'csv': 'text/csv',
'cpt': 'application/mac-compactpro',
'wmlc': 'application/vnd.wap.wmlc',
'cgm': 'image/cgm',
'ice': 'x-conference/x-cooltalk',
'cmx': 'image/x-cmx',
'xar': 'application/vnd.xara',
'cmc': 'application/vnd.cosmocaller',
'cpio': 'application/x-cpio',
'clkx': 'application/vnd.crick.clicker',
'clkk': 'application/vnd.crick.clicker.keyboard',
'clkp': 'application/vnd.crick.clicker.palette',
'clkt': 'application/vnd.crick.clicker.template',
'clkw': 'application/vnd.crick.clicker.wordbank',
'wbs': 'application/vnd.criticaltools.wbs+xml',
'cryptonote': 'application/vnd.rig.cryptonote',
'cif': 'chemical/x-cif',
'cmdf': 'chemical/x-cmdf',
'cu': 'application/cu-seeme',
'cww': 'application/prs.cww',
'curl': 'text/vnd.curl',
'dcurl': 'text/vnd.curl.dcurl',
'mcurl': 'text/vnd.curl.mcurl',
'scurl': 'text/vnd.curl.scurl',
'car': 'application/vnd.curl.car',
'pcurl': 'application/vnd.curl.pcurl',
'cmp': 'application/vnd.yellowriver-custom-menu',
'dssc': 'application/dssc+der',
'xdssc': 'application/dssc+xml',
'deb': 'application/x-debian-package',
'uva': 'audio/vnd.dece.audio',
'uvi': 'image/vnd.dece.graphic',
'uvh': 'video/vnd.dece.hd',
'uvm': 'video/vnd.dece.mobile',
'uvu': 'video/vnd.uvvu.mp4',
'uvp': 'video/vnd.dece.pd',
'uvs': 'video/vnd.dece.sd',
'uvv': 'video/vnd.dece.video',
'dvi': 'application/x-dvi',
'seed': 'application/vnd.fdsn.seed',
'dtb': 'application/x-dtbook+xml',
'res': 'application/x-dtbresource+xml',
'ait': 'application/vnd.dvb.ait',
'svc': 'application/vnd.dvb.service',
'eol': 'audio/vnd.digital-winds',
'djvu': 'image/vnd.djvu',
'dtd': 'application/xml-dtd',
'mlp': 'application/vnd.dolby.mlp',
'wad': 'application/x-doom',
'dpg': 'application/vnd.dpgraph',
'dra': 'audio/vnd.dra',
'dfac': 'application/vnd.dreamfactory',
'dts': 'audio/vnd.dts',
'dtshd': 'audio/vnd.dts.hd',
'dwg': 'image/vnd.dwg',
'geo': 'application/vnd.dynageo',
'es': 'application/ecmascript',
'mag': 'application/vnd.ecowin.chart',
'mmr': 'image/vnd.fujixerox.edmics-mmr',
'rlc': 'image/vnd.fujixerox.edmics-rlc',
'exi': 'application/exi',
'mgz': 'application/vnd.proteus.magazine',
'epub': 'application/epub+zip',
'eml': 'message/rfc822',
'nml': 'application/vnd.enliven',
'xpr': 'application/vnd.is-xpr',
'xif': 'image/vnd.xiff',
'xfdl': 'application/vnd.xfdl',
'emma': 'application/emma+xml',
'ez2': 'application/vnd.ezpix-album',
'ez3': 'application/vnd.ezpix-package',
'fst': 'image/vnd.fst',
'fvt': 'video/vnd.fvt',
'fbs': 'image/vnd.fastbidsheet',
'fe_launch': 'application/vnd.denovo.fcselayout-link',
'f4v': 'video/x-f4v',
'flv': 'video/x-flv',
'fpx': 'image/vnd.fpx',
'npx': 'image/vnd.net-fpx',
'flx': 'text/vnd.fmi.flexstor',
'fli': 'video/x-fli',
'ftc': 'application/vnd.fluxtime.clip',
'fdf': 'application/vnd.fdf',
'f': 'text/x-fortran',
'mif': 'application/vnd.mif',
'fm': 'application/vnd.framemaker',
'fh': 'image/x-freehand',
'fsc': 'application/vnd.fsc.weblaunch',
'fnc': 'application/vnd.frogans.fnc',
'ltf': 'application/vnd.frogans.ltf',
'ddd': 'application/vnd.fujixerox.ddd',
'xdw': 'application/vnd.fujixerox.docuworks',
'xbd': 'application/vnd.fujixerox.docuworks.binder',
'oas': 'application/vnd.fujitsu.oasys',
'oa2': 'application/vnd.fujitsu.oasys2',
'oa3': 'application/vnd.fujitsu.oasys3',
'fg5': 'application/vnd.fujitsu.oasysgp',
'bh2': 'application/vnd.fujitsu.oasysprs',
'spl': 'application/x-futuresplash',
'fzs': 'application/vnd.fuzzysheet',
'g3': 'image/g3fax',
'gmx': 'application/vnd.gmx',
'gtw': 'model/vnd.gtw',
'txd': 'application/vnd.genomatix.tuxedo',
'ggb': 'application/vnd.geogebra.file',
'ggt': 'application/vnd.geogebra.tool',
'gdl': 'model/vnd.gdl',
'gex': 'application/vnd.geometry-explorer',
'gxt': 'application/vnd.geonext',
'g2w': 'application/vnd.geoplan',
'g3w': 'application/vnd.geospace',
'gsf': 'application/x-font-ghostscript',
'bdf': 'application/x-font-bdf',
'gtar': 'application/x-gtar',
'texinfo': 'application/x-texinfo',
'gnumeric': 'application/x-gnumeric',
'kml': 'application/vnd.google-earth.kml+xml',
'kmz': 'application/vnd.google-earth.kmz',
'gpx': 'application/gpx+xml',
'gqf': 'application/vnd.grafeq',
'gif': 'image/gif',
'gv': 'text/vnd.graphviz',
'gac': 'application/vnd.groove-account',
'ghf': 'application/vnd.groove-help',
'gim': 'application/vnd.groove-identity-message',
'grv': 'application/vnd.groove-injector',
'gtm': 'application/vnd.groove-tool-message',
'tpl': 'application/vnd.groove-tool-template',
'vcg': 'application/vnd.groove-vcard',
'h261': 'video/h261',
'h263': 'video/h263',
'h264': 'video/h264',
'hpid': 'application/vnd.hp-hpid',
'hps': 'application/vnd.hp-hps',
'hdf': 'application/x-hdf',
'rip': 'audio/vnd.rip',
'hbci': 'application/vnd.hbci',
'jlt': 'application/vnd.hp-jlyt',
'pcl': 'application/vnd.hp-pcl',
'hpgl': 'application/vnd.hp-hpgl',
'hvs': 'application/vnd.yamaha.hv-script',
'hvd': 'application/vnd.yamaha.hv-dic',
'hvp': 'application/vnd.yamaha.hv-voice',
'sfd-hdstx': 'application/vnd.hydrostatix.sof-data',
'stk': 'application/hyperstudio',
'hal': 'application/vnd.hal+xml',
'html': 'text/html',
'irm': 'application/vnd.ibm.rights-management',
'sc': 'application/vnd.ibm.secure-container',
'ics': 'text/calendar',
'icc': 'application/vnd.iccprofile',
'ico': 'image/x-icon',
'igl': 'application/vnd.igloader',
'ief': 'image/ief',
'ivp': 'application/vnd.immervision-ivp',
'ivu': 'application/vnd.immervision-ivu',
'rif': 'application/reginfo+xml',
'3dml': 'text/vnd.in3d.3dml',
'spot': 'text/vnd.in3d.spot',
'igs': 'model/iges',
'i2g': 'application/vnd.intergeo',
'cdy': 'application/vnd.cinderella',
'xpw': 'application/vnd.intercon.formnet',
'fcs': 'application/vnd.isac.fcs',
'ipfix': 'application/ipfix',
'cer': 'application/pkix-cert',
'pki': 'application/pkixcmp',
'crl': 'application/pkix-crl',
'pkipath': 'application/pkix-pkipath',
'igm': 'application/vnd.insors.igm',
'rcprofile': 'application/vnd.ipunplugged.rcprofile',
'irp': 'application/vnd.irepository.package+xml',
'jad': 'text/vnd.sun.j2me.app-descriptor',
'jar': 'application/java-archive',
'class': 'application/java-vm',
'jnlp': 'application/x-java-jnlp-file',
'ser': 'application/java-serialized-object',
'java': 'text/x-java-source,java',
'js': 'application/javascript',
'json': 'application/json',
'joda': 'application/vnd.joost.joda-archive',
'jpm': 'video/jpm',
'jpeg': 'image/x-citrix-jpeg',
'jpg': 'image/x-citrix-jpeg',
'pjpeg': 'image/pjpeg',
'jpgv': 'video/jpeg',
'ktz': 'application/vnd.kahootz',
'mmd': 'application/vnd.chipnuts.karaoke-mmd',
'karbon': 'application/vnd.kde.karbon',
'chrt': 'application/vnd.kde.kchart',
'kfo': 'application/vnd.kde.kformula',
'flw': 'application/vnd.kde.kivio',
'kon': 'application/vnd.kde.kontour',
'kpr': 'application/vnd.kde.kpresenter',
'ksp': 'application/vnd.kde.kspread',
'kwd': 'application/vnd.kde.kword',
'htke': 'application/vnd.kenameaapp',
'kia': 'application/vnd.kidspiration',
'kne': 'application/vnd.kinar',
'sse': 'application/vnd.kodak-descriptor',
'lasxml': 'application/vnd.las.las+xml',
'latex': 'application/x-latex',
'lbd': 'application/vnd.llamagraphics.life-balance.desktop',
'lbe': 'application/vnd.llamagraphics.life-balance.exchange+xml',
'jam': 'application/vnd.jam',
'apr': 'application/vnd.lotus-approach',
'pre': 'application/vnd.lotus-freelance',
'nsf': 'application/vnd.lotus-notes',
'org': 'application/vnd.lotus-organizer',
'scm': 'application/vnd.lotus-screencam',
'lwp': 'application/vnd.lotus-wordpro',
'lvp': 'audio/vnd.lucent.voice',
'm3u': 'audio/x-mpegurl',
'm4v': 'video/x-m4v',
'hqx': 'application/mac-binhex40',
'portpkg': 'application/vnd.macports.portpkg',
'mgp': 'application/vnd.osgeo.mapguide.package',
'mrc': 'application/marc',
'mrcx': 'application/marcxml+xml',
'mxf': 'application/mxf',
'nbp': 'application/vnd.wolfram.player',
'ma': 'application/mathematica',
'mathml': 'application/mathml+xml',
'mbox': 'application/mbox',
'mc1': 'application/vnd.medcalcdata',
'mscml': 'application/mediaservercontrol+xml',
'cdkey': 'application/vnd.mediastation.cdkey',
'mwf': 'application/vnd.mfer',
'mfm': 'application/vnd.mfmp',
'msh': 'model/mesh',
'mads': 'application/mads+xml',
'mets': 'application/mets+xml',
'mods': 'application/mods+xml',
'meta4': 'application/metalink4+xml',
'mcd': 'application/vnd.mcd',
'flo': 'application/vnd.micrografx.flo',
'igx': 'application/vnd.micrografx.igx',
'es3': 'application/vnd.eszigno3+xml',
'mdb': 'application/x-msaccess',
'asf': 'video/x-ms-asf',
'exe': 'application/x-msdownload',
'cil': 'application/vnd.ms-artgalry',
'cab': 'application/vnd.ms-cab-compressed',
'ims': 'application/vnd.ms-ims',
'application': 'application/x-ms-application',
'clp': 'application/x-msclip',
'mdi': 'image/vnd.ms-modi',
'eot': 'application/vnd.ms-fontobject',
'xls': 'application/vnd.ms-excel',
'xlam': 'application/vnd.ms-excel.addin.macroenabled.12',
'xlsb': 'application/vnd.ms-excel.sheet.binary.macroenabled.12',
'xltm': 'application/vnd.ms-excel.template.macroenabled.12',
'xlsm': 'application/vnd.ms-excel.sheet.macroenabled.12',
'chm': 'application/vnd.ms-htmlhelp',
'crd': 'application/x-mscardfile',
'lrm': 'application/vnd.ms-lrm',
'mvb': 'application/x-msmediaview',
'mny': 'application/x-msmoney',
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx': 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'ppsx': 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'potx': 'application/vnd.openxmlformats-officedocument.presentationml.template',
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'obd': 'application/x-msbinder',
'thmx': 'application/vnd.ms-officetheme',
'onetoc': 'application/onenote',
'pya': 'audio/vnd.ms-playready.media.pya',
'pyv': 'video/vnd.ms-playready.media.pyv',
'ppt': 'application/vnd.ms-powerpoint',
'ppam': 'application/vnd.ms-powerpoint.addin.macroenabled.12',
'sldm': 'application/vnd.ms-powerpoint.slide.macroenabled.12',
'pptm': 'application/vnd.ms-powerpoint.presentation.macroenabled.12',
'ppsm': 'application/vnd.ms-powerpoint.slideshow.macroenabled.12',
'potm': 'application/vnd.ms-powerpoint.template.macroenabled.12',
'mpp': 'application/vnd.ms-project',
'pub': 'application/x-mspublisher',
'scd': 'application/x-msschedule',
'xap': 'application/x-silverlight-app',
'stl': 'application/vnd.ms-pki.stl',
'cat': 'application/vnd.ms-pki.seccat',
'vsd': 'application/vnd.visio',
'vsdx': 'application/vnd.visio2013',
'wm': 'video/x-ms-wm',
'wma': 'audio/x-ms-wma',
'wax': 'audio/x-ms-wax',
'wmx': 'video/x-ms-wmx',
'wmd': 'application/x-ms-wmd',
'wpl': 'application/vnd.ms-wpl',
'wmz': 'application/x-ms-wmz',
'wmv': 'video/x-ms-wmv',
'wvx': 'video/x-ms-wvx',
'wmf': 'application/x-msmetafile',
'trm': 'application/x-msterminal',
'doc': 'application/msword',
'docm': 'application/vnd.ms-word.document.macroenabled.12',
'dotm': 'application/vnd.ms-word.template.macroenabled.12',
'wri': 'application/x-mswrite',
'wps': 'application/vnd.ms-works',
'xbap': 'application/x-ms-xbap',
'xps': 'application/vnd.ms-xpsdocument',
'mid': 'audio/midi',
'mpy': 'application/vnd.ibm.minipay',
'afp': 'application/vnd.ibm.modcap',
'rms': 'application/vnd.jcp.javame.midlet-rms',
'tmo': 'application/vnd.tmobile-livetv',
'prc': 'application/x-mobipocket-ebook',
'mbk': 'application/vnd.mobius.mbk',
'dis': 'application/vnd.mobius.dis',
'plc': 'application/vnd.mobius.plc',
'mqy': 'application/vnd.mobius.mqy',
'msl': 'application/vnd.mobius.msl',
'txf': 'application/vnd.mobius.txf',
'daf': 'application/vnd.mobius.daf',
'fly': 'text/vnd.fly',
'mpc': 'application/vnd.mophun.certificate',
'mpn': 'application/vnd.mophun.application',
'mj2': 'video/mj2',
'mpga': 'audio/mpeg',
'mxu': 'video/vnd.mpegurl',
'mpeg': 'video/mpeg',
'm21': 'application/mp21',
'mp4a': 'audio/mp4',
'mp4': 'application/mp4',
'm3u8': 'application/vnd.apple.mpegurl',
'mus': 'application/vnd.musician',
'msty': 'application/vnd.muvee.style',
'mxml': 'application/xv+xml',
'ngdat': 'application/vnd.nokia.n-gage.data',
'n-gage': 'application/vnd.nokia.n-gage.symbian.install',
'ncx': 'application/x-dtbncx+xml',
'nc': 'application/x-netcdf',
'nlu': 'application/vnd.neurolanguage.nlu',
'dna': 'application/vnd.dna',
'nnd': 'application/vnd.noblenet-directory',
'nns': 'application/vnd.noblenet-sealer',
'nnw': 'application/vnd.noblenet-web',
'rpst': 'application/vnd.nokia.radio-preset',
'rpss': 'application/vnd.nokia.radio-presets',
'n3': 'text/n3',
'edm': 'application/vnd.novadigm.edm',
'edx': 'application/vnd.novadigm.edx',
'ext': 'application/vnd.novadigm.ext',
'gph': 'application/vnd.flographit',
'ecelp4800': 'audio/vnd.nuera.ecelp4800',
'ecelp7470': 'audio/vnd.nuera.ecelp7470',
'ecelp9600': 'audio/vnd.nuera.ecelp9600',
'oda': 'application/oda',
'ogx': 'application/ogg',
'oga': 'audio/ogg',
'ogv': 'video/ogg',
'dd2': 'application/vnd.oma.dd2+xml',
'oth': 'application/vnd.oasis.opendocument.text-web',
'opf': 'application/oebps-package+xml',
'qbo': 'application/vnd.intu.qbo',
'oxt': 'application/vnd.openofficeorg.extension',
'osf': 'application/vnd.yamaha.openscoreformat',
'weba': 'audio/webm',
'webm': 'video/webm',
'odc': 'application/vnd.oasis.opendocument.chart',
'otc': 'application/vnd.oasis.opendocument.chart-template',
'odb': 'application/vnd.oasis.opendocument.database',
'odf': 'application/vnd.oasis.opendocument.formula',
'odft': 'application/vnd.oasis.opendocument.formula-template',
'odg': 'application/vnd.oasis.opendocument.graphics',
'otg': 'application/vnd.oasis.opendocument.graphics-template',
'odi': 'application/vnd.oasis.opendocument.image',
'oti': 'application/vnd.oasis.opendocument.image-template',
'odp': 'application/vnd.oasis.opendocument.presentation',
'otp': 'application/vnd.oasis.opendocument.presentation-template',
'ods': 'application/vnd.oasis.opendocument.spreadsheet',
'ots': 'application/vnd.oasis.opendocument.spreadsheet-template',
'odt': 'application/vnd.oasis.opendocument.text',
'odm': 'application/vnd.oasis.opendocument.text-master',
'ott': 'application/vnd.oasis.opendocument.text-template',
'ktx': 'image/ktx',
'sxc': 'application/vnd.sun.xml.calc',
'stc': 'application/vnd.sun.xml.calc.template',
'sxd': 'application/vnd.sun.xml.draw',
'std': 'application/vnd.sun.xml.draw.template',
'sxi': 'application/vnd.sun.xml.impress',
'sti': 'application/vnd.sun.xml.impress.template',
'sxm': 'application/vnd.sun.xml.math',
'sxw': 'application/vnd.sun.xml.writer',
'sxg': 'application/vnd.sun.xml.writer.global',
'stw': 'application/vnd.sun.xml.writer.template',
'otf': 'application/x-font-otf',
'osfpvg': 'application/vnd.yamaha.openscoreformat.osfpvg+xml',
'dp': 'application/vnd.osgi.dp',
'pdb': 'application/vnd.palm',
'p': 'text/x-pascal',
'paw': 'application/vnd.pawaafile',
'pclxl': 'application/vnd.hp-pclxl',
'efif': 'application/vnd.picsel',
'pcx': 'image/x-pcx',
'psd': 'image/vnd.adobe.photoshop',
'prf': 'application/pics-rules',
'pic': 'image/x-pict',
'chat': 'application/x-chat',
'p10': 'application/pkcs10',
'p12': 'application/x-pkcs12',
'p7m': 'application/pkcs7-mime',
'p7s': 'application/pkcs7-signature',
'p7r': 'application/x-pkcs7-certreqresp',
'p7b': 'application/x-pkcs7-certificates',
'p8': 'application/pkcs8',
'plf': 'application/vnd.pocketlearn',
'pnm': 'image/x-portable-anymap',
'pbm': 'image/x-portable-bitmap',
'pcf': 'application/x-font-pcf',
'pfr': 'application/font-tdpfr',
'pgn': 'application/x-chess-pgn',
'pgm': 'image/x-portable-graymap',
'png': 'image/x-png',
'ppm': 'image/x-portable-pixmap',
'pskcxml': 'application/pskc+xml',
'pml': 'application/vnd.ctc-posml',
'ai': 'application/postscript',
'pfa': 'application/x-font-type1',
'pbd': 'application/vnd.powerbuilder6',
'pgp': 'application/pgp-signature',
'box': 'application/vnd.previewsystems.box',
'ptid': 'application/vnd.pvi.ptid1',
'pls': 'application/pls+xml',
'str': 'application/vnd.pg.format',
'ei6': 'application/vnd.pg.osasli',
'dsc': 'text/prs.lines.tag',
'psf': 'application/x-font-linux-psf',
'qps': 'application/vnd.publishare-delta-tree',
'wg': 'application/vnd.pmi.widget',
'qxd': 'application/vnd.quark.quarkxpress',
'esf': 'application/vnd.epson.esf',
'msf': 'application/vnd.epson.msf',
'ssf': 'application/vnd.epson.ssf',
'qam': 'application/vnd.epson.quickanime',
'qfx': 'application/vnd.intu.qfx',
'qt': 'video/quicktime',
'rar': 'application/x-rar-compressed',
'ram': 'audio/x-pn-realaudio',
'rmp': 'audio/x-pn-realaudio-plugin',
'rsd': 'application/rsd+xml',
'rm': 'application/vnd.rn-realmedia',
'bed': 'application/vnd.realvnc.bed',
'mxl': 'application/vnd.recordare.musicxml',
'musicxml': 'application/vnd.recordare.musicxml+xml',
'rnc': 'application/relax-ng-compact-syntax',
'rdz': 'application/vnd.data-vision.rdz',
'rdf': 'application/rdf+xml',
'rp9': 'application/vnd.cloanto.rp9',
'jisp': 'application/vnd.jisp',
'rtf': 'application/rtf',
'rtx': 'text/richtext',
'link66': 'application/vnd.route66.link66+xml',
'rss, .xml': 'application/rss+xml',
'shf': 'application/shf+xml',
'st': 'application/vnd.sailingtracker.track',
'svg': 'image/svg+xml',
'sus': 'application/vnd.sus-calendar',
'sru': 'application/sru+xml',
'setpay': 'application/set-payment-initiation',
'setreg': 'application/set-registration-initiation',
'sema': 'application/vnd.sema',
'semd': 'application/vnd.semd',
'semf': 'application/vnd.semf',
'see': 'application/vnd.seemail',
'snf': 'application/x-font-snf',
'spq': 'application/scvp-vp-request',
'spp': 'application/scvp-vp-response',
'scq': 'application/scvp-cv-request',
'scs': 'application/scvp-cv-response',
'sdp': 'application/sdp',
'etx': 'text/x-setext',
'movie': 'video/x-sgi-movie',
'ifm': 'application/vnd.shana.informed.formdata',
'itp': 'application/vnd.shana.informed.formtemplate',
'iif': 'application/vnd.shana.informed.interchange',
'ipk': 'application/vnd.shana.informed.package',
'tfi': 'application/thraud+xml',
'shar': 'application/x-shar',
'rgb': 'image/x-rgb',
'slt': 'application/vnd.epson.salt',
'aso': 'application/vnd.accpac.simply.aso',
'imp': 'application/vnd.accpac.simply.imp',
'twd': 'application/vnd.simtech-mindmapper',
'csp': 'application/vnd.commonspace',
'saf': 'application/vnd.yamaha.smaf-audio',
'mmf': 'application/vnd.smaf',
'spf': 'application/vnd.yamaha.smaf-phrase',
'teacher': 'application/vnd.smart.teacher',
'svd': 'application/vnd.svd',
'rq': 'application/sparql-query',
'srx': 'application/sparql-results+xml',
'gram': 'application/srgs',
'grxml': 'application/srgs+xml',
'ssml': 'application/ssml+xml',
'skp': 'application/vnd.koan',
'sgml': 'text/sgml',
'sdc': 'application/vnd.stardivision.calc',
'sda': 'application/vnd.stardivision.draw',
'sdd': 'application/vnd.stardivision.impress',
'smf': 'application/vnd.stardivision.math',
'sdw': 'application/vnd.stardivision.writer',
'sgl': 'application/vnd.stardivision.writer-global',
'sm': 'application/vnd.stepmania.stepchart',
'sit': 'application/x-stuffit',
'sitx': 'application/x-stuffitx',
'sdkm': 'application/vnd.solent.sdkm+xml',
'xo': 'application/vnd.olpc-sugar',
'au': 'audio/basic',
'wqd': 'application/vnd.wqd',
'sis': 'application/vnd.symbian.install',
'smi': 'application/smil+xml',
'xsm': 'application/vnd.syncml+xml',
'bdm': 'application/vnd.syncml.dm+wbxml',
'xdm': 'application/vnd.syncml.dm+xml',
'sv4cpio': 'application/x-sv4cpio',
'sv4crc': 'application/x-sv4crc',
'sbml': 'application/sbml+xml',
'tsv': 'text/tab-separated-values',
'tiff': 'image/tiff',
'tao': 'application/vnd.tao.intent-module-archive',
'tar': 'application/x-tar',
'tcl': 'application/x-tcl',
'tex': 'application/x-tex',
'tfm': 'application/x-tex-tfm',
'tei': 'application/tei+xml',
'txt': 'text/plain',
'dxp': 'application/vnd.spotfire.dxp',
'sfs': 'application/vnd.spotfire.sfs',
'tsd': 'application/timestamped-data',
'tpt': 'application/vnd.trid.tpt',
'mxs': 'application/vnd.triscape.mxs',
't': 'text/troff',
'tra': 'application/vnd.trueapp',
'ttf': 'application/x-font-ttf',
'ttl': 'text/turtle',
'umj': 'application/vnd.umajin',
'uoml': 'application/vnd.uoml+xml',
'unityweb': 'application/vnd.unity',
'ufd': 'application/vnd.ufdl',
'uri': 'text/uri-list',
'utz': 'application/vnd.uiq.theme',
'ustar': 'application/x-ustar',
'uu': 'text/x-uuencode',
'vcs': 'text/x-vcalendar',
'vcf': 'text/x-vcard',
'vcd': 'application/x-cdlink',
'vsf': 'application/vnd.vsf',
'wrl': 'model/vrml',
'vcx': 'application/vnd.vcx',
'mts': 'model/vnd.mts',
'vtu': 'model/vnd.vtu',
'vis': 'application/vnd.visionary',
'viv': 'video/vnd.vivo',
'ccxml': 'application/ccxml+xml,',
'vxml': 'application/voicexml+xml',
'src': 'application/x-wais-source',
'wbxml': 'application/vnd.wap.wbxml',
'wbmp': 'image/vnd.wap.wbmp',
'wav': 'audio/x-wav',
'davmount': 'application/davmount+xml',
'woff': 'application/x-font-woff',
'wspolicy': 'application/wspolicy+xml',
'webp': 'image/webp',
'wtb': 'application/vnd.webturbo',
'wgt': 'application/widget',
'hlp': 'application/winhlp',
'wml': 'text/vnd.wap.wml',
'wmls': 'text/vnd.wap.wmlscript',
'wmlsc': 'application/vnd.wap.wmlscriptc',
'wpd': 'application/vnd.wordperfect',
'stf': 'application/vnd.wt.stf',
'wsdl': 'application/wsdl+xml',
'xbm': 'image/x-xbitmap',
'xpm': 'image/x-xpixmap',
'xwd': 'image/x-xwindowdump',
'der': 'application/x-x509-ca-cert',
'fig': 'application/x-xfig',
'xhtml': 'application/xhtml+xml',
'xml': 'application/xml',
'xdf': 'application/xcap-diff+xml',
'xenc': 'application/xenc+xml',
'xer': 'application/patch-ops-error+xml',
'rl': 'application/resource-lists+xml',
'rs': 'application/rls-services+xml',
'rld': 'application/resource-lists-diff+xml',
'xslt': 'application/xslt+xml',
'xop': 'application/xop+xml',
'xpi': 'application/x-xpinstall',
'xspf': 'application/xspf+xml',
'xul': 'application/vnd.mozilla.xul+xml',
'xyz': 'chemical/x-xyz',
'yaml': 'text/yaml',
'yang': 'application/yang',
'yin': 'application/yin+xml',
'zir': 'application/vnd.zul',
'zip': 'application/zip',
'zmm': 'application/vnd.handheld-entertainment+xml',
'zaz': 'application/vnd.zzazz.deck+xml',
}
const mimeIcons = {
pdf: 'mdi-pdf-box',
docx: 'mdi-file-word-outline',
dotx: 'mdi-file-word-outline',
odt: 'mdi-file-word-outline',
odm: 'mdi-file-word-outline',
ott: 'mdi-file-word-outline',
ppt: 'mdi-file-powerpoint-box',
pptx: 'mdi-file-powerpoint-box',
sldx: 'mdi-file-powerpoint-box',
ppsx: 'mdi-file-powerpoint-box',
potx: 'mdi-file-powerpoint-box',
xls: 'mdi-file-excel-outline',
xlam: 'mdi-file-excel-outline',
xlsb: 'mdi-file-excel-outline',
xltm: 'mdi-file-excel-outline',
xlsm: 'mdi-file-excel-outline',
}
export { mimeTypes, mimeIcons }

22
tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts

@ -48,15 +48,21 @@ export class AttachmentCellPageObject extends BasePage {
} }
async verifyFileCount({ index, columnHeader, count }: { index: number; columnHeader: string; count: number }) { async verifyFileCount({ index, columnHeader, count }: { index: number; columnHeader: string; count: number }) {
const attachments = await this.get({ index, columnHeader }).locator( // retry below logic for 5 times, with 1 second delay
'.nc-cell > .nc-attachment-cell > .flex > .nc-attachment' let retryCount = 0;
); while (retryCount < 5) {
const attachments = await this.get({ index, columnHeader }).locator('.nc-attachment');
console.log(await attachments.count());
if ((await attachments.count()) === count) {
break;
}
retryCount++;
await this.rootPage.waitForTimeout(1000);
console.log(await attachments.count()); if (retryCount === 5) {
expect(await attachments.count()).toBe(count); expect(await attachments.count()).toBe(count);
}
// attachments should be of count 'count' }
// await expect(await attachments.count()).toBe(count);
} }
async expandModalClose() { async expandModalClose() {

1
tests/playwright/tests/columnAttachments.spec.ts

@ -138,7 +138,6 @@ test.describe('Attachment column', () => {
columnHeader: 'testAttach', columnHeader: 'testAttach',
filePath: twoFileArray, filePath: twoFileArray,
}); });
await dashboard.rootPage.waitForTimeout(2000);
await dashboard.grid.cell.attachment.verifyFileCount({ await dashboard.grid.cell.attachment.verifyFileCount({
index: 1, index: 1,
columnHeader: 'testAttach', columnHeader: 'testAttach',

Loading…
Cancel
Save