Browse Source

feat: undo/redo filters (WIP)

Signed-off-by: mertmit <mertmit99@gmail.com>
pull/5332/head
mertmit 2 years ago
parent
commit
a104fcf7fd
  1. 4
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
  2. 152
      packages/nc-gui/composables/useViewFilters.ts

4
packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue

@ -364,7 +364,7 @@ defineExpose({
</div> </div>
<div class="flex gap-2 mb-2 mt-4"> <div class="flex gap-2 mb-2 mt-4">
<a-button class="elevation-0 text-capitalize" type="primary" ghost @click.stop="addFilter"> <a-button class="elevation-0 text-capitalize" type="primary" ghost @click.stop="addFilter()">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<component :is="iconMap.plus" /> <component :is="iconMap.plus" />
<!-- Add Filter --> <!-- Add Filter -->
@ -372,7 +372,7 @@ defineExpose({
</div> </div>
</a-button> </a-button>
<a-button v-if="!webHook" class="text-capitalize !text-gray-500" @click.stop="addFilterGroup"> <a-button v-if="!webHook" class="text-capitalize !text-gray-500" @click.stop="addFilterGroup()">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<!-- Add Filter Group --> <!-- Add Filter Group -->
<component :is="iconMap.plus" /> <component :is="iconMap.plus" />

152
packages/nc-gui/composables/useViewFilters.ts

@ -20,7 +20,7 @@ import {
watch, watch,
} from '#imports' } from '#imports'
import { TabMetaInj } from '~/context' import { TabMetaInj } from '~/context'
import type { Filter, TabItem } from '~/lib' import type { Filter, TabItem, UndoRedoAction } from '~/lib'
export function useViewFilters( export function useViewFilters(
view: Ref<ViewType | undefined>, view: Ref<ViewType | undefined>,
@ -46,6 +46,8 @@ export function useViewFilters(
const { metas } = useMetas() const { metas } = useMetas()
const { addUndo, clone } = useUndoRedo()
const _filters = ref<Filter[]>([]) const _filters = ref<Filter[]>([])
const nestedMode = computed(() => isPublic.value || !isUIAllowed('filterSync') || !isUIAllowed('filterChildrenRead')) const nestedMode = computed(() => isPublic.value || !isUIAllowed('filterSync') || !isUIAllowed('filterChildrenRead'))
@ -107,6 +109,19 @@ export function useViewFilters(
}, {}) }, {})
}) })
const lastFilters = ref<Filter[]>([])
watchOnce(filters, (filters: Filter[]) => {
lastFilters.value = clone(filters)
})
// get delta between two objects and return the changed fields (value is from b)
const getFieldDelta = (a: any, b: any) => {
return Object.entries(b)
.filter(([key, val]) => a[key] !== val && key in a)
.reduce((a, [key, v]) => ({ ...a, [key]: v }), {})
}
const isComparisonOpAllowed = ( const isComparisonOpAllowed = (
filter: FilterType, filter: FilterType,
compOp: { compOp: {
@ -230,47 +245,47 @@ export function useViewFilters(
} }
} }
const deleteFilter = async (filter: Filter, i: number) => { const saveOrUpdate = async (filter: Filter, i: number, force = false, undo = false) => {
// if shared or sync permission not allowed simply remove it from array if (!view.value) return
if (nestedMode.value) {
filters.value.splice(i, 1) if (!undo) {
filters.value = [...filters.value] const lastFilter = lastFilters.value[i]
reloadData?.() if (lastFilter) {
} else { const delta = clone(getFieldDelta(filter, lastFilter))
if (filter.id) { if (Object.keys(delta).length > 0) {
// if auto-apply disabled mark it as disabled addUndo({
if (!autoApply?.value) { undo: {
filter.status = 'delete' fn: (prop: string, data: any) => {
// if auto-apply enabled invoke delete api and remove from array const f = filters.value[i]
// no splice is required here if (f) {
} else { f[prop as keyof Filter] = data
try { saveOrUpdate(f, i, force, true)
await $api.dbTableFilter.delete(filter.id)
reloadData?.()
filters.value.splice(i, 1)
} catch (e: any) {
console.log(e)
message.error(await extractSdkResponseErrorMsg(e))
} }
},
args: [Object.keys(delta)[0], Object.values(delta)[0]],
},
redo: {
fn: (prop: string, data: any) => {
const f = filters.value[i]
if (f) {
f[prop as keyof Filter] = data
saveOrUpdate(f, i, force, true)
} }
// if not synced yet remove it from array },
} else { args: [Object.keys(delta)[0], filter[Object.keys(delta)[0] as keyof Filter]],
filters.value.splice(i, 1) },
})
} }
$e('a:filter:delete', { length: nonDeletedFilters.value.length })
} }
} }
const saveOrUpdate = async (filter: Filter, i: number, force = false) => {
if (!view.value) return
try { try {
if (nestedMode.value) { if (nestedMode.value) {
filters.value[i] = { ...filter } filters.value[i] = { ...filter }
filters.value = [...filters.value] filters.value = [...filters.value]
} else if (!autoApply?.value && !force) { } else if (!autoApply?.value && !force) {
filter.status = filter.id ? 'update' : 'create' filter.status = filter.id ? 'update' : 'create'
} else if (filter.id) { } else if (filter.id && filter.status !== 'create') {
await $api.dbTableFilter.update(filter.id, { await $api.dbTableFilter.update(filter.id, {
...filter, ...filter,
fk_parent_id: parentId, fk_parent_id: parentId,
@ -290,13 +305,86 @@ export function useViewFilters(
message.error(await extractSdkResponseErrorMsg(e)) message.error(await extractSdkResponseErrorMsg(e))
} }
lastFilters.value = clone(filters.value)
reloadData?.() reloadData?.()
} }
const deleteFilter = async (filter: Filter, i: number, undo = false) => {
if (!undo && !filter.is_group) {
addUndo({
undo: {
fn: async (fl: Filter) => {
fl.status = 'create'
filters.value.splice(i, 0, fl)
await saveOrUpdate(fl, i, false, true)
},
args: [clone(filter)],
},
redo: {
fn: async (index: number) => {
await deleteFilter(filters.value[index], index, true)
},
args: [i],
},
})
}
// if shared or sync permission not allowed simply remove it from array
if (nestedMode.value) {
filters.value.splice(i, 1)
filters.value = [...filters.value]
reloadData?.()
} else {
if (filter.id) {
// if auto-apply disabled mark it as disabled
if (!autoApply?.value) {
filter.status = 'delete'
// if auto-apply enabled invoke delete api and remove from array
// no splice is required here
} else {
try {
await $api.dbTableFilter.delete(filter.id)
reloadData?.()
filters.value.splice(i, 1)
} catch (e: any) {
console.log(e)
message.error(await extractSdkResponseErrorMsg(e))
}
}
// if not synced yet remove it from array
} else {
filters.value.splice(i, 1)
}
$e('a:filter:delete', { length: nonDeletedFilters.value.length })
}
}
const saveOrUpdateDebounced = useDebounceFn(saveOrUpdate, 500) const saveOrUpdateDebounced = useDebounceFn(saveOrUpdate, 500)
const addFilter = () => { const addFilter = async (undo = false) => {
filters.value.push(placeholderFilter()) filters.value.push(placeholderFilter())
if (!undo) {
addUndo({
undo: {
fn: async function undo(this: UndoRedoAction, i: number) {
this.redo.args = [i, clone(filters.value[i])]
await deleteFilter(filters.value[i], i, true)
},
args: [filters.value.length - 1],
},
redo: {
fn: async (i: number, fl: Filter) => {
fl.status = 'create'
filters.value.splice(i, 0, fl)
await saveOrUpdate(fl, i, false, true)
},
args: [],
},
})
}
lastFilters.value = clone(filters.value)
$e('a:filter:add', { length: filters.value.length }) $e('a:filter:add', { length: filters.value.length })
} }
@ -317,6 +405,8 @@ export function useViewFilters(
await saveOrUpdate(filters.value[index], index, true) await saveOrUpdate(filters.value[index], index, true)
lastFilters.value = clone(filters.value)
$e('a:filter:add', { length: filters.value.length, group: true }) $e('a:filter:add', { length: filters.value.length, group: true })
} }

Loading…
Cancel
Save