mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Reflect Content Version in URL / history (#21624)
* Reflect Content Version in URL / history * Add changeset * Update lockfile * Update .changeset/stupid-oranges-listen.md
This commit is contained in:
5
.changeset/stupid-oranges-listen.md
Normal file
5
.changeset/stupid-oranges-listen.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@directus/app': minor
|
||||
---
|
||||
|
||||
Integrated the Content Version in the browser URL / history to enable browser navigation and page refresh
|
||||
@@ -98,6 +98,7 @@
|
||||
"@vitejs/plugin-vue": "5.0.4",
|
||||
"@vue/test-utils": "2.4.4",
|
||||
"@vueuse/core": "10.7.2",
|
||||
"@vueuse/router": "10.9.0",
|
||||
"apexcharts": "3.46.0",
|
||||
"axios": "1.6.7",
|
||||
"base-64": "1.0.0",
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { ref, unref, type Ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { isEqual } from 'lodash';
|
||||
import { MaybeRef, Ref, ref, unref } from 'vue';
|
||||
import { LocationQuery, useRoute } from 'vue-router';
|
||||
import { useNavigationGuard } from './use-navigation-guard';
|
||||
|
||||
type EditsGuardOptions = {
|
||||
ignorePrefix?: string | Ref<string>;
|
||||
ignorePrefix?: MaybeRef<string>;
|
||||
compareQuery?: MaybeRef<string[]>;
|
||||
};
|
||||
|
||||
export function useEditsGuard(hasEdits: Ref<boolean>, opts?: EditsGuardOptions) {
|
||||
@@ -11,9 +13,12 @@ export function useEditsGuard(hasEdits: Ref<boolean>, opts?: EditsGuardOptions)
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
useNavigationGuard(hasEdits, (to) => {
|
||||
const { path } = useRoute();
|
||||
const { path, query } = useRoute();
|
||||
|
||||
if (hasEdits.value && !isSubpath(path, to.path) && !isIgnoredPath(unref(opts?.ignorePrefix), to.path)) {
|
||||
if (
|
||||
hasEdits.value &&
|
||||
((!isSubpath(path, to.path) && !isIgnoredPath(to.path)) || hasChangedQuery(query, to.query))
|
||||
) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
@@ -23,16 +28,31 @@ export function useEditsGuard(hasEdits: Ref<boolean>, opts?: EditsGuardOptions)
|
||||
});
|
||||
|
||||
return { confirmLeave, leaveTo };
|
||||
}
|
||||
|
||||
function isSubpath(currentPath: string, newPath: string) {
|
||||
return (
|
||||
currentPath === newPath || (newPath.startsWith(currentPath) && newPath.substring(currentPath.length).includes('/'))
|
||||
);
|
||||
}
|
||||
function isSubpath(currentPath: string, newPath: string) {
|
||||
return (
|
||||
currentPath === newPath ||
|
||||
(newPath.startsWith(currentPath) && newPath.substring(currentPath.length).includes('/'))
|
||||
);
|
||||
}
|
||||
|
||||
function isIgnoredPath(ignorePrefix: string | undefined, newPath: string) {
|
||||
if (!ignorePrefix) return false;
|
||||
function isIgnoredPath(newPath: string) {
|
||||
const ignorePrefix = unref(opts?.ignorePrefix);
|
||||
|
||||
return newPath.startsWith(ignorePrefix);
|
||||
if (!ignorePrefix) return false;
|
||||
|
||||
return newPath.startsWith(ignorePrefix);
|
||||
}
|
||||
|
||||
function hasChangedQuery(currentQuery: LocationQuery, newQuery: LocationQuery) {
|
||||
const compareQuery = unref(opts?.compareQuery);
|
||||
|
||||
if (!compareQuery) return false;
|
||||
|
||||
for (const query of compareQuery) {
|
||||
if (!isEqual(currentQuery[query], newQuery[query])) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,45 @@
|
||||
import api from '@/api';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
import { ContentVersion, Filter, Query } from '@directus/types';
|
||||
import { useRouteQuery } from '@vueuse/router';
|
||||
import { Ref, computed, ref, unref, watch } from 'vue';
|
||||
import { useCollectionPermissions } from './use-permissions';
|
||||
|
||||
export function useVersions(collection: Ref<string>, isSingleton: Ref<boolean>, primaryKey: Ref<string | null>) {
|
||||
const { readAllowed: readVersionsAllowed } = useCollectionPermissions('directus_versions');
|
||||
|
||||
const currentVersion = ref<ContentVersion | null>(null);
|
||||
const versions = ref<ContentVersion[] | null>(null);
|
||||
const loading = ref(false);
|
||||
const saveVersionLoading = ref(false);
|
||||
|
||||
const { readAllowed: readVersionsAllowed } = useCollectionPermissions('directus_versions');
|
||||
|
||||
const queryVersion = useRouteQuery<string | null>('version', null, {
|
||||
transform: (value) => (Array.isArray(value) ? value[0] : value),
|
||||
mode: 'push',
|
||||
});
|
||||
|
||||
watch(
|
||||
[queryVersion, versions],
|
||||
([newQueryVersion, newVersions]) => {
|
||||
if (!newVersions) return;
|
||||
|
||||
let version;
|
||||
|
||||
if (queryVersion) {
|
||||
version = newVersions.find((version) => version.key === newQueryVersion);
|
||||
}
|
||||
|
||||
if (version?.key === currentVersion.value?.key) return;
|
||||
|
||||
currentVersion.value = version ?? null;
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(currentVersion, (newCurrentVersion) => {
|
||||
queryVersion.value = newCurrentVersion?.key ?? null;
|
||||
});
|
||||
|
||||
const query = computed<Query>(() => {
|
||||
if (!currentVersion.value) return {};
|
||||
|
||||
@@ -22,7 +50,7 @@ export function useVersions(collection: Ref<string>, isSingleton: Ref<boolean>,
|
||||
|
||||
watch(
|
||||
[collection, isSingleton, primaryKey],
|
||||
([newCollection, _newIsSingleton, _newPrimaryKey], [oldCollection, _oldIsSingleton, _oldPrimaryKey]) => {
|
||||
([newCollection], [oldCollection]) => {
|
||||
if (newCollection !== oldCollection) currentVersion.value = null;
|
||||
getVersions();
|
||||
},
|
||||
|
||||
@@ -89,7 +89,7 @@ const {
|
||||
|
||||
const { templateData } = useTemplateData(collectionInfo, primaryKey);
|
||||
|
||||
const { confirmLeave, leaveTo } = useEditsGuard(hasEdits);
|
||||
const { confirmLeave, leaveTo } = useEditsGuard(hasEdits, { compareQuery: ['version'] });
|
||||
const confirmDelete = ref(false);
|
||||
const confirmArchive = ref(false);
|
||||
|
||||
|
||||
29
pnpm-lock.yaml
generated
29
pnpm-lock.yaml
generated
@@ -721,6 +721,9 @@ importers:
|
||||
'@vueuse/core':
|
||||
specifier: 10.7.2
|
||||
version: 10.7.2(vue@3.4.19)
|
||||
'@vueuse/router':
|
||||
specifier: 10.9.0
|
||||
version: 10.9.0(vue-router@4.3.0)(vue@3.4.19)
|
||||
apexcharts:
|
||||
specifier: 3.46.0
|
||||
version: 3.46.0
|
||||
@@ -961,7 +964,7 @@ importers:
|
||||
version: 5.3.3
|
||||
vite:
|
||||
specifier: 5.1.1
|
||||
version: 5.1.1(sass@1.71.1)
|
||||
version: 5.1.1(@types/node@18.19.17)
|
||||
vite-plugin-dts:
|
||||
specifier: 3.7.3
|
||||
version: 3.7.3(rollup@4.10.0)(typescript@5.3.3)(vite@5.1.1)
|
||||
@@ -1890,7 +1893,7 @@ importers:
|
||||
version: 5.3.3
|
||||
vite:
|
||||
specifier: 5.1.1
|
||||
version: 5.1.1(sass@1.71.1)
|
||||
version: 5.1.1(@types/node@18.19.17)
|
||||
vite-plugin-dts:
|
||||
specifier: 3.7.3
|
||||
version: 3.7.3(rollup@4.10.0)(typescript@5.3.3)(vite@5.1.1)
|
||||
@@ -8761,6 +8764,19 @@ packages:
|
||||
/@vueuse/metadata@10.7.2:
|
||||
resolution: {integrity: sha512-kCWPb4J2KGrwLtn1eJwaJD742u1k5h6v/St5wFe8Quih90+k2a0JP8BS4Zp34XUuJqS2AxFYMb1wjUL8HfhWsQ==}
|
||||
|
||||
/@vueuse/router@10.9.0(vue-router@4.3.0)(vue@3.4.19):
|
||||
resolution: {integrity: sha512-MOmrCMQlRuPS4PExE1hy8T0XbZUXaNbEuh7CAG5mC8kdvdgANQMkdvJ7vIEOP27n5mXK/4YjvXJOZSsur4E0QQ==}
|
||||
peerDependencies:
|
||||
vue-router: '>=4.0.0-rc.1'
|
||||
dependencies:
|
||||
'@vueuse/shared': 10.9.0(vue@3.4.19)
|
||||
vue-demi: 0.14.7(vue@3.4.19)
|
||||
vue-router: 4.3.0(vue@3.4.19)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
dev: true
|
||||
|
||||
/@vueuse/shared@10.7.0(vue@3.3.8):
|
||||
resolution: {integrity: sha512-kc00uV6CiaTdc3i1CDC4a3lBxzaBE9AgYNtFN87B5OOscqeWElj/uza8qVDmk7/U8JbqoONLbtqiLJ5LGRuqlw==}
|
||||
dependencies:
|
||||
@@ -8787,6 +8803,15 @@ packages:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
/@vueuse/shared@10.9.0(vue@3.4.19):
|
||||
resolution: {integrity: sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==}
|
||||
dependencies:
|
||||
vue-demi: 0.14.7(vue@3.4.19)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
dev: true
|
||||
|
||||
/@xmldom/xmldom@0.8.10:
|
||||
resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
Reference in New Issue
Block a user