From ae7d1f90d69c6c480fcb154f65cdcb8a87afa01a Mon Sep 17 00:00:00 2001 From: Noah Stuertz Date: Tue, 14 Nov 2023 10:29:20 +0100 Subject: [PATCH 1/5] add persistent query and paging --- src/router/index.ts | 6 ++++++ src/views/search/SearchView.vue | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/router/index.ts b/src/router/index.ts index 6f3543f..684131f 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -23,6 +23,12 @@ const router = createRouter({ name: "search", component: () => import("../views/search/SearchView.vue"), }, + { + path: "/search/:query", + name: "search-query", + component: () => import("../views/search/SearchView.vue"), + props: true, + }, { path: "/imprint", name: "imprint", diff --git a/src/views/search/SearchView.vue b/src/views/search/SearchView.vue index b0bb790..338e89b 100644 --- a/src/views/search/SearchView.vue +++ b/src/views/search/SearchView.vue @@ -35,15 +35,29 @@ import { import ExportProgress from "./ExportProgress.vue"; import { downloadFullTsv, type ProgressEvent } from "./ExportTsv"; import ResultTable from "./ResultTable.vue"; +import { useRoute } from "vue-router"; +import router from "@/router"; const pageState = usePageState(); const searchState = usePageState(); const entries: Ref = ref([]); const api = useApi(); +const route = useRoute(); + const pagination: Ref = ref(empty()); const query: Ref = ref({ op: "and", value: [] }); const ordering: Ref = ref([{ field: "id", ord: "asc" }]); const searchinfo: Ref = ref({ fields: [] }); +let startingOffset: number = 0; + +if (route.params.query) { + const importedData = importQuery(route.params.query as string); + pagination.value.limit = importedData.pagination; + query.value = importedData.query; + ordering.value = importedData.ordering; + startingOffset = importedData.offset; +} + function init() { pageState.value.setState(State.Loading); api @@ -52,6 +66,11 @@ function init() { searchinfo.value = r; pageState.value.setState(State.Main); }) + .then(() => { + if (route.params.query) { + search(startingOffset); + } + }) .catch((err) => pageState.value.setError(err)); } @@ -158,6 +177,8 @@ const fieldNames: Record = { function search(offset = 0) { searchState.value.setState(State.Loading); resetTsvExport(); + const b64 = exportQuery(offset); + router.push({ name: "search-query", params: { query: b64 } }); api .search({ query: unref(query), @@ -174,6 +195,21 @@ function search(offset = 0) { .catch((err) => pageState.value.setError(err)); } +function exportQuery(offset: number): string { + const data = { + query: query.value, + ordering: ordering.value, + pagination: pagination.value.limit, + offset: offset, + }; + + return btoa(JSON.stringify(data)); +} + +function importQuery(base64: string) { + return JSON.parse(atob(base64)); +} + const positionInResults: Ref = computed(() => toPosition(pagination.value), ); From 2e88a083fa24c2ff1765be80ba19cc47592f4d81 Mon Sep 17 00:00:00 2001 From: Noah Stuertz Date: Mon, 20 Nov 2023 09:06:39 +0100 Subject: [PATCH 2/5] add firefox back and forward buttons functionality --- src/router/index.ts | 11 +++-- src/views/search/SearchView.vue | 76 ++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/src/router/index.ts b/src/router/index.ts index 684131f..fe64713 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -22,12 +22,11 @@ const router = createRouter({ path: "/search", name: "search", component: () => import("../views/search/SearchView.vue"), - }, - { - path: "/search/:query", - name: "search-query", - component: () => import("../views/search/SearchView.vue"), - props: true, + props: (route) => ({ + offset: route.query.offset, + limit: route.query.limit, + query: route.query.query, + }), }, { path: "/imprint", diff --git a/src/views/search/SearchView.vue b/src/views/search/SearchView.vue index 338e89b..82c3be9 100644 --- a/src/views/search/SearchView.vue +++ b/src/views/search/SearchView.vue @@ -31,32 +31,22 @@ import { ref, unref, type Ref, + watch, } from "vue"; import ExportProgress from "./ExportProgress.vue"; import { downloadFullTsv, type ProgressEvent } from "./ExportTsv"; import ResultTable from "./ResultTable.vue"; -import { useRoute } from "vue-router"; import router from "@/router"; +import { useRoute } from "vue-router"; const pageState = usePageState(); const searchState = usePageState(); const entries: Ref = ref([]); const api = useApi(); -const route = useRoute(); - const pagination: Ref = ref(empty()); const query: Ref = ref({ op: "and", value: [] }); const ordering: Ref = ref([{ field: "id", ord: "asc" }]); const searchinfo: Ref = ref({ fields: [] }); -let startingOffset: number = 0; - -if (route.params.query) { - const importedData = importQuery(route.params.query as string); - pagination.value.limit = importedData.pagination; - query.value = importedData.query; - ordering.value = importedData.ordering; - startingOffset = importedData.offset; -} function init() { pageState.value.setState(State.Loading); @@ -66,14 +56,40 @@ function init() { searchinfo.value = r; pageState.value.setState(State.Main); }) - .then(() => { - if (route.params.query) { - search(startingOffset); - } - }) + .then(populateVariables) + .then(() => search()) .catch((err) => pageState.value.setError(err)); } +const route = useRoute(); + +function encodeQuery(): string { + return btoa(JSON.stringify({ query: query.value, ordering: ordering.value })); +} + +function decodeQuery(encodedQuery: string): { + query: CompoundQuery; + ordering: SortOption[]; +} { + return JSON.parse(atob(encodedQuery)); +} + +function populateVariables() { + if (route.query.query) { + const decodedQuery = decodeQuery(route.query.query as string); + query.value = decodedQuery.query; + ordering.value = decodedQuery.ordering; + } +} + +watch( + () => route.query, + () => { + populateVariables(); + search(); + }, +); + function searchinfo2querybuilderrules(f: SearchInfoField): Rule { const config = fieldNames[f.field]; const field = config ? config.label : f.field; @@ -177,8 +193,15 @@ const fieldNames: Record = { function search(offset = 0) { searchState.value.setState(State.Loading); resetTsvExport(); - const b64 = exportQuery(offset); - router.push({ name: "search-query", params: { query: b64 } }); + const encodedQuery = encodeQuery(); + router.push({ + name: "search", + query: { + offset: pagination.value.offset, + limit: pagination.value.limit, + query: encodedQuery, + }, + }); api .search({ query: unref(query), @@ -195,21 +218,6 @@ function search(offset = 0) { .catch((err) => pageState.value.setError(err)); } -function exportQuery(offset: number): string { - const data = { - query: query.value, - ordering: ordering.value, - pagination: pagination.value.limit, - offset: offset, - }; - - return btoa(JSON.stringify(data)); -} - -function importQuery(base64: string) { - return JSON.parse(atob(base64)); -} - const positionInResults: Ref = computed(() => toPosition(pagination.value), ); From 8fca73f0d86af54d040c7a5633ab6ddfa700d6c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noah=20St=C3=BCrtz?= Date: Tue, 21 Nov 2023 12:07:20 +0100 Subject: [PATCH 3/5] remove router for useRouter import --- src/views/search/SearchView.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/views/search/SearchView.vue b/src/views/search/SearchView.vue index 82c3be9..799e47c 100644 --- a/src/views/search/SearchView.vue +++ b/src/views/search/SearchView.vue @@ -36,13 +36,15 @@ import { import ExportProgress from "./ExportProgress.vue"; import { downloadFullTsv, type ProgressEvent } from "./ExportTsv"; import ResultTable from "./ResultTable.vue"; -import router from "@/router"; -import { useRoute } from "vue-router"; +import { useRoute, useRouter } from "vue-router"; const pageState = usePageState(); const searchState = usePageState(); const entries: Ref = ref([]); const api = useApi(); +const route = useRoute(); +const router = useRouter(); + const pagination: Ref = ref(empty()); const query: Ref = ref({ op: "and", value: [] }); const ordering: Ref = ref([{ field: "id", ord: "asc" }]); @@ -61,7 +63,6 @@ function init() { .catch((err) => pageState.value.setError(err)); } -const route = useRoute(); function encodeQuery(): string { return btoa(JSON.stringify({ query: query.value, ordering: ordering.value })); From 289e98ed65ee2b22da4b81124ca2bf6a90870355 Mon Sep 17 00:00:00 2001 From: Noah Stuertz Date: Thu, 23 Nov 2023 10:01:10 +0100 Subject: [PATCH 4/5] fix search being called multiple times by conditionally calling it --- src/views/search/SearchView.vue | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/views/search/SearchView.vue b/src/views/search/SearchView.vue index 799e47c..5248962 100644 --- a/src/views/search/SearchView.vue +++ b/src/views/search/SearchView.vue @@ -59,11 +59,9 @@ function init() { pageState.value.setState(State.Main); }) .then(populateVariables) - .then(() => search()) .catch((err) => pageState.value.setError(err)); } - function encodeQuery(): string { return btoa(JSON.stringify({ query: query.value, ordering: ordering.value })); } @@ -80,6 +78,7 @@ function populateVariables() { const decodedQuery = decodeQuery(route.query.query as string); query.value = decodedQuery.query; ordering.value = decodedQuery.ordering; + search(); } } @@ -87,7 +86,6 @@ watch( () => route.query, () => { populateVariables(); - search(); }, ); From 25b996b8bae5d0aa1c675968accdd58a704e39ce Mon Sep 17 00:00:00 2001 From: Noah Stuertz Date: Thu, 23 Nov 2023 12:08:02 +0100 Subject: [PATCH 5/5] add consistency when changing pages --- src/views/search/SearchView.vue | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/views/search/SearchView.vue b/src/views/search/SearchView.vue index 5248962..81f2a38 100644 --- a/src/views/search/SearchView.vue +++ b/src/views/search/SearchView.vue @@ -78,7 +78,9 @@ function populateVariables() { const decodedQuery = decodeQuery(route.query.query as string); query.value = decodedQuery.query; ordering.value = decodedQuery.ordering; - search(); + pagination.value.offset = Number.parseInt(route.query.offset as string); + pagination.value.limit = Number.parseInt(route.query.limit as string); + search(pagination.value.offset); } } @@ -89,6 +91,17 @@ watch( }, ); +function updateQuery(offset = 0) { + router.push({ + name: "search", + query: { + offset: offset, + limit: pagination.value.limit, + query: encodeQuery(), + }, + }); +} + function searchinfo2querybuilderrules(f: SearchInfoField): Rule { const config = fieldNames[f.field]; const field = config ? config.label : f.field; @@ -192,15 +205,6 @@ const fieldNames: Record = { function search(offset = 0) { searchState.value.setState(State.Loading); resetTsvExport(); - const encodedQuery = encodeQuery(); - router.push({ - name: "search", - query: { - offset: pagination.value.offset, - limit: pagination.value.limit, - query: encodedQuery, - }, - }); api .search({ query: unref(query), @@ -213,6 +217,7 @@ function search(offset = 0) { searchState.value.setState(State.Main); if (r.offset) pagination.value.offset = r.offset; pagination.value.total = r.total; + updateQuery(r.offset); }) .catch((err) => pageState.value.setError(err)); } @@ -337,7 +342,7 @@ onBeforeUnmount(() => { v-if="pagination.total > 0" class="mt-3" :value="pagination" - @update:offset="search" + @update:offset="updateQuery" />