forked from mirrors/homebox
feat: encode search into url (#131)
* route query helper * encode search parameters into url
This commit is contained in:
parent
b6b2a2d889
commit
b7aacb3cde
2 changed files with 135 additions and 12 deletions
60
frontend/composables/use-route-params.ts
Normal file
60
frontend/composables/use-route-params.ts
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/* eslint no-redeclare: 0 */
|
||||||
|
import { WritableComputedRef } from "vue";
|
||||||
|
|
||||||
|
export function useRouteQuery(q: string, def: string[]): WritableComputedRef<string[]>;
|
||||||
|
export function useRouteQuery(q: string, def: string): WritableComputedRef<string>;
|
||||||
|
export function useRouteQuery(q: string, def: boolean): WritableComputedRef<boolean>;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export function useRouteQuery(q: string, def: any): WritableComputedRef<any> {
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
switch (typeof def) {
|
||||||
|
case "string":
|
||||||
|
if (route.query[q] === undefined) {
|
||||||
|
router.push({ query: { ...route.query, [q]: def } });
|
||||||
|
}
|
||||||
|
|
||||||
|
return computed({
|
||||||
|
get: () => {
|
||||||
|
const qv = route.query[q];
|
||||||
|
if (Array.isArray(qv)) {
|
||||||
|
return qv[0];
|
||||||
|
}
|
||||||
|
return qv;
|
||||||
|
},
|
||||||
|
set: v => {
|
||||||
|
const query = { ...route.query, [q]: v };
|
||||||
|
router.push({ query });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
case "object": // array
|
||||||
|
return computed({
|
||||||
|
get: () => {
|
||||||
|
const qv = route.query[q];
|
||||||
|
if (Array.isArray(qv)) {
|
||||||
|
return qv;
|
||||||
|
}
|
||||||
|
return [qv];
|
||||||
|
},
|
||||||
|
set: v => {
|
||||||
|
const query = { ...route.query, [q]: v };
|
||||||
|
router.push({ query });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
case "boolean":
|
||||||
|
return computed({
|
||||||
|
get: () => {
|
||||||
|
const qv = route.query[q];
|
||||||
|
if (Array.isArray(qv)) {
|
||||||
|
return qv[0] === "true";
|
||||||
|
}
|
||||||
|
return qv === "true";
|
||||||
|
},
|
||||||
|
set: v => {
|
||||||
|
const query = { ...route.query, [q]: `${v}` };
|
||||||
|
router.push({ query });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ItemSummary } from "~~/lib/api/types/data-contracts";
|
import { watchPostEffect } from "vue";
|
||||||
|
import { ItemSummary, LabelSummary, LocationOutCount } from "~~/lib/api/types/data-contracts";
|
||||||
import { useLabelStore } from "~~/stores/labels";
|
import { useLabelStore } from "~~/stores/labels";
|
||||||
import { useLocationStore } from "~~/stores/locations";
|
import { useLocationStore } from "~~/stores/locations";
|
||||||
|
|
||||||
|
@ -11,13 +12,21 @@
|
||||||
title: "Homebox | Home",
|
title: "Homebox | Home",
|
||||||
});
|
});
|
||||||
|
|
||||||
const api = useUserApi();
|
const searchLocked = ref(false);
|
||||||
|
|
||||||
const query = ref("");
|
const api = useUserApi();
|
||||||
const loading = useMinLoader(2000);
|
const loading = useMinLoader(2000);
|
||||||
const results = ref<ItemSummary[]>([]);
|
const results = ref<ItemSummary[]>([]);
|
||||||
|
|
||||||
|
const query = useRouteQuery("q", "");
|
||||||
|
const advanced = useRouteQuery("advanced", false);
|
||||||
|
const includeArchived = useRouteQuery("archived", false);
|
||||||
|
|
||||||
async function search() {
|
async function search() {
|
||||||
|
if (searchLocked.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
const locations = selectedLocations.value.map(l => l.id);
|
const locations = selectedLocations.value.map(l => l.id);
|
||||||
|
@ -38,8 +47,38 @@
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
const route = useRoute();
|
||||||
search();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const queryParamsInitialized = ref(false);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// Wait until locations and labels are loaded
|
||||||
|
let maxRetry = 10;
|
||||||
|
while (!labels.value || !locations.value) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
if (maxRetry-- < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
searchLocked.value = true;
|
||||||
|
const qLoc = route.query.loc as string[];
|
||||||
|
if (qLoc) {
|
||||||
|
selectedLocations.value = locations.value.filter(l => qLoc.includes(l.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
const qLab = route.query.lab as string[];
|
||||||
|
if (qLab) {
|
||||||
|
selectedLabels.value = labels.value.filter(l => qLab.includes(l.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
queryParamsInitialized.value = true;
|
||||||
|
searchLocked.value = false;
|
||||||
|
|
||||||
|
// trigger search if no changes
|
||||||
|
if (!qLab && !qLoc) {
|
||||||
|
search();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const locationsStore = useLocationStore();
|
const locationsStore = useLocationStore();
|
||||||
|
@ -48,10 +87,36 @@
|
||||||
const labelStore = useLabelStore();
|
const labelStore = useLabelStore();
|
||||||
const labels = computed(() => labelStore.labels);
|
const labels = computed(() => labelStore.labels);
|
||||||
|
|
||||||
const advanced = ref(false);
|
const selectedLocations = ref<LocationOutCount[]>([]);
|
||||||
const selectedLocations = ref([]);
|
const selectedLabels = ref<LabelSummary[]>([]);
|
||||||
const selectedLabels = ref([]);
|
|
||||||
const includeArchived = ref(false);
|
watchPostEffect(() => {
|
||||||
|
if (!queryParamsInitialized.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const labelIds = selectedLabels.value.map(l => l.id);
|
||||||
|
router.push({
|
||||||
|
query: {
|
||||||
|
...router.currentRoute.value.query,
|
||||||
|
lab: labelIds,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
watchPostEffect(() => {
|
||||||
|
if (!queryParamsInitialized.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const locIds = selectedLocations.value.map(l => l.id);
|
||||||
|
router.push({
|
||||||
|
query: {
|
||||||
|
...router.currentRoute.value.query,
|
||||||
|
loc: locIds,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (!advanced.value) {
|
if (!advanced.value) {
|
||||||
|
@ -60,9 +125,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watchDebounced(query, search, { debounce: 250, maxWait: 1000 });
|
watchDebounced([selectedLocations, selectedLabels, query], search, { debounce: 250, maxWait: 1000 });
|
||||||
watchDebounced(selectedLocations, search, { debounce: 250, maxWait: 1000 });
|
|
||||||
watchDebounced(selectedLabels, search, { debounce: 250, maxWait: 1000 });
|
|
||||||
watch(includeArchived, search);
|
watch(includeArchived, search);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue