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">
|
||||
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 { useLocationStore } from "~~/stores/locations";
|
||||
|
||||
|
@ -11,13 +12,21 @@
|
|||
title: "Homebox | Home",
|
||||
});
|
||||
|
||||
const api = useUserApi();
|
||||
const searchLocked = ref(false);
|
||||
|
||||
const query = ref("");
|
||||
const api = useUserApi();
|
||||
const loading = useMinLoader(2000);
|
||||
const results = ref<ItemSummary[]>([]);
|
||||
|
||||
const query = useRouteQuery("q", "");
|
||||
const advanced = useRouteQuery("advanced", false);
|
||||
const includeArchived = useRouteQuery("archived", false);
|
||||
|
||||
async function search() {
|
||||
if (searchLocked.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
|
||||
const locations = selectedLocations.value.map(l => l.id);
|
||||
|
@ -38,8 +47,38 @@
|
|||
loading.value = false;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
const route = useRoute();
|
||||
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();
|
||||
|
@ -48,10 +87,36 @@
|
|||
const labelStore = useLabelStore();
|
||||
const labels = computed(() => labelStore.labels);
|
||||
|
||||
const advanced = ref(false);
|
||||
const selectedLocations = ref([]);
|
||||
const selectedLabels = ref([]);
|
||||
const includeArchived = ref(false);
|
||||
const selectedLocations = ref<LocationOutCount[]>([]);
|
||||
const selectedLabels = ref<LabelSummary[]>([]);
|
||||
|
||||
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(() => {
|
||||
if (!advanced.value) {
|
||||
|
@ -60,9 +125,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
watchDebounced(query, search, { debounce: 250, maxWait: 1000 });
|
||||
watchDebounced(selectedLocations, search, { debounce: 250, maxWait: 1000 });
|
||||
watchDebounced(selectedLabels, search, { debounce: 250, maxWait: 1000 });
|
||||
watchDebounced([selectedLocations, selectedLabels, query], search, { debounce: 250, maxWait: 1000 });
|
||||
watch(includeArchived, search);
|
||||
</script>
|
||||
|
||||
|
|
Loading…
Reference in a new issue