mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
303 lines
8.5 KiB
303 lines
8.5 KiB
import type { Api, ColumnType, PaginatedType, TableType, ViewType } from 'nocodb-sdk' |
|
import type { ComputedRef, Ref } from 'vue' |
|
import type { EventHook } from '@vueuse/core' |
|
import { NavigateDir, type Row } from '#imports' |
|
|
|
const formatData = (list: Record<string, any>[], pageInfo: PaginatedType) => |
|
list.map((row, index) => ({ |
|
row: { ...row }, |
|
oldRow: { ...row }, |
|
rowMeta: { |
|
// Calculate the rowIndex based on the offset and the index of the row |
|
rowIndex: (pageInfo.page - 1) * pageInfo.pageSize + index, |
|
}, |
|
})) |
|
|
|
export function useGridViewData( |
|
_meta: Ref<TableType | undefined> | ComputedRef<TableType | undefined>, |
|
viewMeta: Ref<ViewType | undefined> | ComputedRef<(ViewType & { id: string }) | undefined>, |
|
where?: ComputedRef<string | undefined>, |
|
reloadVisibleDataHook?: EventHook<void>, |
|
) { |
|
const tablesStore = useTablesStore() |
|
const { activeTableId, activeTable } = storeToRefs(tablesStore) |
|
|
|
const meta = computed(() => _meta.value || activeTable.value) |
|
|
|
const metaId = computed(() => _meta.value?.id || activeTableId.value) |
|
|
|
const { t } = useI18n() |
|
|
|
const optimisedQuery = useState('optimisedQuery', () => true) |
|
|
|
const router = useRouter() |
|
|
|
const route = router.currentRoute |
|
|
|
const { appInfo, gridViewPageSize } = useGlobal() |
|
|
|
const appInfoDefaultLimit = gridViewPageSize.value || appInfo.value.defaultLimit || 25 |
|
|
|
const _paginationData = ref<PaginatedType>({ page: 1, pageSize: appInfoDefaultLimit }) |
|
|
|
const excludePageInfo = ref(false) |
|
|
|
const isPublic = inject(IsPublicInj, ref(false)) |
|
|
|
const { base } = storeToRefs(useBase()) |
|
|
|
const { fetchSharedViewData, paginationData: sharedPaginationData } = useSharedView() |
|
|
|
const { $api } = useNuxtApp() |
|
|
|
const { sorts, nestedFilters } = useSmartsheetStoreOrThrow() |
|
|
|
const { isUIAllowed } = useRoles() |
|
|
|
const routeQuery = computed(() => route.value.query as Record<string, string>) |
|
|
|
const paginationData = computed({ |
|
get: () => (isPublic.value ? sharedPaginationData.value : _paginationData.value), |
|
set: (value) => { |
|
if (isPublic.value) { |
|
sharedPaginationData.value = value |
|
} else { |
|
_paginationData.value = value |
|
} |
|
}, |
|
}) |
|
|
|
const { |
|
insertRow, |
|
updateRowProperty, |
|
addEmptyRow, |
|
deleteRow, |
|
deleteRowById, |
|
deleteSelectedRows, |
|
deleteRangeOfRows, |
|
updateOrSaveRow, |
|
cachedRows, |
|
clearCache, |
|
totalRows, |
|
bulkUpdateRows, |
|
bulkUpdateView, |
|
removeRowIfNew, |
|
syncCount, |
|
selectedRows, |
|
chunkStates, |
|
isRowSortRequiredRows, |
|
clearInvalidRows, |
|
applySorting, |
|
} = useInfiniteData({ |
|
meta, |
|
viewMeta, |
|
callbacks: { |
|
loadData, |
|
syncVisibleData, |
|
}, |
|
where, |
|
}) |
|
|
|
function syncVisibleData() { |
|
reloadVisibleDataHook?.trigger() |
|
} |
|
|
|
function getExpandedRowIndex(): number { |
|
const rowId = routeQuery.value.rowId |
|
if (!rowId) return -1 |
|
|
|
for (const [_index, row] of cachedRows.value.entries()) { |
|
if (extractPkFromRow(row.row, meta.value?.columns as ColumnType[]) === rowId) { |
|
return row.rowMeta.rowIndex! |
|
} |
|
} |
|
return -1 |
|
} |
|
|
|
const isLastRow = computed(() => { |
|
const expandedRowIndex = getExpandedRowIndex() |
|
if (expandedRowIndex === -1) return false |
|
|
|
return expandedRowIndex === totalRows.value - 1 |
|
}) |
|
|
|
const isFirstRow = computed(() => { |
|
const expandedRowIndex = getExpandedRowIndex() |
|
if (expandedRowIndex === -1) return false |
|
|
|
return expandedRowIndex === 0 |
|
}) |
|
|
|
const queryParams = computed(() => ({ |
|
offset: ((paginationData.value.page ?? 0) - 1) * (paginationData.value.pageSize ?? appInfoDefaultLimit), |
|
limit: paginationData.value.pageSize ?? appInfoDefaultLimit, |
|
where: where?.value ?? '', |
|
})) |
|
|
|
async function loadAggCommentsCount(formattedData: Array<Row>) { |
|
if (!isUIAllowed('commentCount') || isPublic.value) return |
|
|
|
const ids = formattedData |
|
.filter(({ rowMeta: { new: isNew } }) => !isNew) |
|
.map(({ row }) => extractPkFromRow(row, meta?.value?.columns as ColumnType[])) |
|
.filter(Boolean) |
|
|
|
if (!ids.length) return |
|
|
|
try { |
|
const aggCommentCount = await $api.utils.commentCount({ |
|
ids, |
|
fk_model_id: metaId.value as string, |
|
}) |
|
|
|
formattedData.forEach((row) => { |
|
const cachedRow = Array.from(cachedRows.value.values()).find( |
|
(cachedRow) => cachedRow.rowMeta.rowIndex === row.rowMeta.rowIndex, |
|
) |
|
if (!cachedRow) return |
|
|
|
const id = extractPkFromRow(row.row, meta.value?.columns as ColumnType[]) |
|
const count = aggCommentCount?.find((c: Record<string, any>) => c.row_id === id)?.count || 0 |
|
cachedRow.rowMeta.commentCount = +count |
|
}) |
|
} catch (e) { |
|
console.error('Failed to load aggregate comment count:', e) |
|
} |
|
} |
|
|
|
async function loadData( |
|
params: Parameters<Api<any>['dbViewRow']['list']>[4] & { |
|
limit?: number |
|
offset?: number |
|
} = {}, |
|
): Promise<Row[] | undefined> { |
|
if ((!base?.value?.id || !metaId.value || !viewMeta.value?.id) && !isPublic.value) return |
|
|
|
try { |
|
const response = !isPublic.value |
|
? await $api.dbViewRow.list('noco', base.value.id!, metaId.value!, viewMeta.value!.id!, { |
|
...queryParams.value, |
|
...params, |
|
...(isUIAllowed('sortSync') ? {} : { sortArrJson: JSON.stringify(sorts.value) }), |
|
...(isUIAllowed('filterSync') ? {} : { filterArrJson: JSON.stringify(nestedFilters.value) }), |
|
where: where?.value, |
|
...(excludePageInfo.value ? { excludeCount: 'true' } : {}), |
|
} as any) |
|
: await fetchSharedViewData( |
|
{ |
|
sortsArr: sorts.value, |
|
filtersArr: nestedFilters.value, |
|
where: where?.value, |
|
offset: params.offset, |
|
limit: params.limit, |
|
}, |
|
{ |
|
isInfiniteScroll: true, |
|
}, |
|
) |
|
|
|
const data = formatData(response.list, response.pageInfo) |
|
|
|
if (response.pageInfo.totalRows) { |
|
totalRows.value = response.pageInfo.totalRows |
|
} |
|
|
|
loadAggCommentsCount(data) |
|
|
|
return data |
|
} catch (error: any) { |
|
if (error?.response?.data.error === 'INVALID_OFFSET_VALUE') { |
|
return [] |
|
} |
|
if (error?.response?.data?.error === 'FORMULA_ERROR') { |
|
await tablesStore.reloadTableMeta(metaId.value as string) |
|
return loadData(params) |
|
} |
|
|
|
console.error(error) |
|
message.error(await extractSdkResponseErrorMsg(error)) |
|
} |
|
} |
|
|
|
const navigateToSiblingRow = async (dir: NavigateDir) => { |
|
const expandedRowIndex = getExpandedRowIndex() |
|
if (expandedRowIndex === -1) return |
|
|
|
const sortedIndices = Array.from(cachedRows.value.keys()).sort((a, b) => a - b) |
|
let siblingIndex = sortedIndices.findIndex((index) => index === expandedRowIndex) + (dir === NavigateDir.NEXT ? 1 : -1) |
|
|
|
// Skip unsaved rows |
|
while ( |
|
siblingIndex >= 0 && |
|
siblingIndex < sortedIndices.length && |
|
cachedRows.value.get(sortedIndices[siblingIndex])?.rowMeta?.new |
|
) { |
|
siblingIndex += dir === NavigateDir.NEXT ? 1 : -1 |
|
} |
|
|
|
// Check if we've gone out of bounds |
|
if (siblingIndex < 0 || siblingIndex >= totalRows.value) { |
|
return message.info(t('msg.info.noMoreRecords')) |
|
} |
|
|
|
// If the sibling row is not in cachedRows, load more data |
|
if (siblingIndex >= sortedIndices.length) { |
|
await loadData({ |
|
offset: sortedIndices[sortedIndices.length - 1] + 1, |
|
limit: 10, |
|
}) |
|
sortedIndices.push( |
|
...Array.from(cachedRows.value.keys()) |
|
.filter((key) => !sortedIndices.includes(key)) |
|
.sort((a, b) => a - b), |
|
) |
|
} |
|
|
|
// Extract the row id of the sibling row |
|
const siblingRow = cachedRows.value.get(sortedIndices[siblingIndex]) |
|
if (siblingRow) { |
|
const rowId = extractPkFromRow(siblingRow.row, meta.value?.columns as ColumnType[]) |
|
if (rowId) { |
|
await router.push({ |
|
query: { |
|
...routeQuery.value, |
|
rowId, |
|
}, |
|
}) |
|
} |
|
} |
|
} |
|
|
|
return { |
|
cachedRows, |
|
loadData, |
|
paginationData, |
|
queryParams, |
|
insertRow, |
|
updateRowProperty, |
|
addEmptyRow, |
|
deleteRow, |
|
deleteRowById, |
|
deleteSelectedRows, |
|
deleteRangeOfRows, |
|
updateOrSaveRow, |
|
bulkUpdateRows, |
|
bulkUpdateView, |
|
loadAggCommentsCount, |
|
syncCount, |
|
removeRowIfNew, |
|
navigateToSiblingRow, |
|
getExpandedRowIndex, |
|
optimisedQuery, |
|
isLastRow, |
|
isFirstRow, |
|
clearCache, |
|
totalRows, |
|
selectedRows, |
|
syncVisibleData, |
|
chunkStates, |
|
clearInvalidRows, |
|
applySorting, |
|
isRowSortRequiredRows, |
|
} |
|
}
|
|
|