[WIP] Paginate History Store

Trying an approach with v-infinite-scroll in selector modal
This commit is contained in:
Ahmed Awan
2023-06-16 11:07:43 -04:00
committed by Dannon Baker
parent 6616eebe5c
commit b7e980e71b
6 changed files with 176 additions and 79 deletions
@@ -114,11 +114,10 @@ export default {
},
},
created() {
this.loadHistories();
this.load();
},
methods: {
...mapActions(useHistoryStore, ["loadHistories", "applyFilters"]),
...mapActions(useHistoryStore, ["applyFilters"]),
load(concat = false) {
this.loading = true;
getDatasets({
@@ -22,8 +22,13 @@ import { useRouter } from "vue-router/composables";
import { useHistoryStore } from "@/stores/historyStore";
import { library } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { faColumns, faSignInAlt } from "@fortawesome/free-solid-svg-icons";
import { faColumns, faSignInAlt, faCaretRight } from "@fortawesome/free-solid-svg-icons";
import { faListAlt } from "@fortawesome/free-regular-svg-icons";
import Vue from "vue";
//@ts-ignore missing typedefs
import infiniteScroll from "vue-infinite-scroll";
Vue.use(infiniteScroll);
type AdditionalOptions = "set-current" | "multi" | "center";
@@ -40,10 +45,13 @@ const emit = defineEmits<{
(e: "selectHistories", histories: HistorySummary[]): void;
}>();
library.add(faColumns, faSignInAlt, faListAlt);
// @ts-ignore bad library types
library.add(faColumns, faSignInAlt, faListAlt, faCaretRight);
const filter = ref("");
const currentPage = ref(1);
const busy = ref(false);
const allLoaded = ref(false);
const modal: Ref<BModal | null> = ref(null);
// reactive proxy for props.histories, as the prop is not
@@ -75,8 +83,8 @@ watch(
}
);
const paginated = computed(() =>
filtered.value.slice((currentPage.value - 1) * props.perPage, currentPage.value * props.perPage)
const paginated = computed(
() => filtered.value //.slice((currentPage.value - 1) * props.perPage, currentPage.value * props.perPage)
);
const totalRows = computed(() => filtered.value.length);
@@ -121,11 +129,24 @@ function openInMulti(history: HistorySummary) {
historyStore.pinHistory(history.id);
modal.value?.hide();
}
async function loadMore() {
if (!allLoaded.value) {
busy.value = true;
await historyStore.loadHistories();
busy.value = false;
await historyStore.getTotalHistoryCount.then((count) => {
if (count <= paginated.value.length) {
allLoaded.value = true;
}
});
}
}
</script>
<template>
<div>
<b-modal ref="modal" v-bind="$attrs" content-class="history-selector-modal" v-on="$listeners">
<b-modal ref="modal" v-bind="$attrs" scrollable content-class="history-selector-modal" v-on="$listeners">
<template v-slot:modal-title>
<Heading h2 inline size="sm">{{ localize(title) }}</Heading>
</template>
@@ -134,86 +155,96 @@ function openInMulti(history: HistorySummary) {
<b-form-input v-model="filter" type="search" debounce="400" :placeholder="localize('Search Filter')" />
</b-form-group>
<b-list-group>
<b-list-group-item
v-for="history in paginated"
:key="history.id"
:data-pk="history.id"
button
:class="{ current: history.id === currentHistoryId }"
:active="selectedHistories.includes(history)"
@click="() => historyClicked(history)">
<div class="d-flex justify-content-between align-items-center">
<Heading h3 inline bold size="text">
{{ history.name }}
<i v-if="history.id === currentHistoryId">(Current)</i>
</Heading>
<div v-infinite-scroll="loadMore">
<b-list-group>
<b-list-group-item
v-for="history in paginated"
:key="history.id"
:data-pk="history.id"
button
:class="{ current: history.id === currentHistoryId }"
:active="selectedHistories.includes(history)"
@click="() => historyClicked(history)">
<div class="d-flex justify-content-between align-items-center">
<Heading h3 inline bold size="text">
{{ history.name }}
<i v-if="history.id === currentHistoryId">(Current)</i>
</Heading>
<div class="d-flex align-items-center flex-gapx-1">
<b-badge v-b-tooltip pill :title="localize('Amount of items in history')">
{{ history.count }} {{ localize("items") }}
</b-badge>
<b-badge v-b-tooltip pill :title="localize('Last edited')">
<UtcDate :date="history.update_time" mode="elapsed" />
</b-badge>
<div class="d-flex align-items-center flex-gapx-1">
<b-badge v-b-tooltip pill :title="localize('Amount of items in history')">
{{ history.count }} {{ localize("items") }}
</b-badge>
<b-badge v-b-tooltip pill :title="localize('Last edited')">
<UtcDate :date="history.update_time" mode="elapsed" />
</b-badge>
</div>
</div>
</div>
<p v-if="history.annotation" class="my-1">{{ history.annotation }}</p>
<p v-if="history.annotation" class="my-1">{{ history.annotation }}</p>
<StatelessTags
v-if="history.tags.length > 0"
class="my-1"
:value="history.tags"
:disabled="true"
:max-visible-tags="10" />
<StatelessTags
v-if="history.tags.length > 0"
class="my-1"
:value="history.tags"
:disabled="true"
:max-visible-tags="10" />
<div
v-if="props.additionalOptions.length > 0"
class="d-flex justify-content-end align-items-center mt-1">
<b-button-group>
<b-button
v-if="props.additionalOptions.includes('set-current')"
v-b-tooltip
:title="localize('Set as current history')"
variant="link"
class="p-0 px-1"
@click.stop="() => setCurrentHistory(history)">
<FontAwesomeIcon icon="fa-sign-in-alt" />
</b-button>
<div
v-if="props.additionalOptions.length > 0"
class="d-flex justify-content-end align-items-center mt-1">
<b-button-group>
<b-button
v-if="props.additionalOptions.includes('set-current')"
v-b-tooltip
:title="localize('Set as current history')"
variant="link"
class="p-0 px-1"
@click.stop="() => setCurrentHistory(history)">
<FontAwesomeIcon icon="fa-sign-in-alt" />
</b-button>
<b-button
v-if="props.additionalOptions.includes('multi')"
v-b-tooltip
:title="localize('Open in multi-view')"
variant="link"
class="p-0 px-1"
@click.stop="() => openInMulti(history)">
<FontAwesomeIcon icon="fa-columns" />
</b-button>
<b-button
v-if="props.additionalOptions.includes('multi')"
v-b-tooltip
:title="localize('Open in multi-view')"
variant="link"
class="p-0 px-1"
@click.stop="() => openInMulti(history)">
<FontAwesomeIcon icon="fa-columns" />
</b-button>
<b-button
v-if="props.additionalOptions.includes('center')"
v-b-tooltip
:title="localize('Open in center panel')"
variant="link"
class="p-0 px-1"
@click.stop="() => setCenterPanelHistory(history)">
<FontAwesomeIcon icon="far fa-list-alt" />
</b-button>
</b-button-group>
</div>
</b-list-group-item>
</b-list-group>
<b-button
v-if="props.additionalOptions.includes('center')"
v-b-tooltip
:title="localize('Open in center panel')"
variant="link"
class="p-0 px-1"
@click.stop="() => setCenterPanelHistory(history)">
<FontAwesomeIcon icon="far fa-list-alt" />
</b-button>
</b-button-group>
</div>
</b-list-group-item>
</b-list-group>
</div>
<b-pagination
<!-- <b-pagination
v-if="totalRows > props.perPage"
v-model="currentPage"
class="mt-4"
:total-rows="totalRows"
:per-page="props.perPage"></b-pagination>
:per-page="props.perPage"></b-pagination> -->
<template v-slot:modal-footer>
<b-button
v-b-tooltip
:title="localize('Load More Histories')"
:disabled="busy || allLoaded"
variant="primary"
@click="loadMore">
<FontAwesomeIcon icon="fa-solid fa-caret-right" />
</b-button>
<b-button
v-if="multiple"
v-localize
+25 -4
View File
@@ -9,6 +9,7 @@ import {
deleteHistoryById,
getCurrentHistoryFromServer,
getHistoryByIdFromServer,
getHistoryCount,
getHistoryList,
secureHistoryOnServer,
setCurrentHistoryOnServer,
@@ -18,6 +19,7 @@ import { useUserLocalStorage } from "@/composables/userLocalStorage";
export type HistorySummary = components["schemas"]["HistorySummary"];
const PAGINATION_LENGTH = 7;
const isLoadingHistory = new Set();
export const useHistoryStore = defineStore("historyStore", () => {
@@ -31,6 +33,15 @@ export const useHistoryStore = defineStore("historyStore", () => {
return Object.values(storedHistories.value).sort(sortByObjectProp("name"));
});
const historiesOffset = computed(() => {
return histories.value.length;
});
const getTotalHistoryCount = computed(async () => {
const count = await getHistoryCount();
return count;
});
const getFirstHistoryId = computed(() => {
return histories.value[0]?.id ?? null;
});
@@ -39,7 +50,6 @@ export const useHistoryStore = defineStore("historyStore", () => {
if (storedCurrentHistoryId.value !== null) {
return storedHistories.value[storedCurrentHistoryId.value];
}
return null;
});
const currentHistoryId = computed(() => {
@@ -58,15 +68,19 @@ export const useHistoryStore = defineStore("historyStore", () => {
}
});
/** Returns history from storedHistories, will load history if not in store */
const getHistoryById = computed(() => {
return (historyId: string) => {
if (!storedHistories.value[historyId]) {
loadHistoryById(historyId);
}
return storedHistories.value[historyId] ?? null;
};
});
const getHistoryNameById = computed(() => {
return (historyId: string) => {
const history = storedHistories.value[historyId];
const history = getHistoryById.value(historyId);
if (history) {
return history.name;
} else {
@@ -173,10 +187,15 @@ export const useHistoryStore = defineStore("historyStore", () => {
selectHistory(history as HistorySummary);
}
async function loadHistories() {
async function loadHistories(paginate: boolean = true) {
if (!historiesLoading.value) {
if (paginate && historiesOffset.value >= (await getTotalHistoryCount.value)) {
return;
}
setHistoriesLoading(true);
await getHistoryList()
const limit = paginate ? PAGINATION_LENGTH : null;
const offset = paginate ? historiesOffset.value : 0;
await getHistoryList(offset, limit)
.then((histories) => setHistories(histories))
.catch((error) => console.warn(error))
.finally(() => {
@@ -215,6 +234,7 @@ export const useHistoryStore = defineStore("historyStore", () => {
pinnedHistories,
getHistoryById,
getHistoryNameById,
getTotalHistoryCount,
setCurrentHistory,
setCurrentHistoryId,
setFilterText,
@@ -233,5 +253,6 @@ export const useHistoryStore = defineStore("historyStore", () => {
secureHistory,
updateHistory,
historiesLoading,
historiesOffset,
};
});
+20 -2
View File
@@ -108,11 +108,29 @@ export async function setCurrentHistoryOnServer(historyId) {
/**
* Get list of histories from server and return them.
* @param {Number} offset
* @param {Number | null} limit
* @return {Promise.<Array>} list of histories
*/
export async function getHistoryList() {
export async function getHistoryList(offset = 0, limit = null) {
const url = "api/histories";
const response = await axios.get(prependPath(url), { params: stdHistoryParams });
const paginatedParams = {
view: "summary",
offset: offset,
limit: limit,
order: "update_time",
};
const response = await axios.get(prependPath(url), { params: paginatedParams });
return doResponse(response);
}
/**
* Get number of histories from server and return them.
* @return {Promise.<Number>} number of histories
*/
export async function getHistoryCount() {
const url = "api/histories/count";
const response = await axios.get(prependPath(url));
return doResponse(response);
}
@@ -131,6 +131,17 @@ class FastAPIHistories:
trans, serialization_params, filter_query_params, deleted_only=deleted, all_histories=all
)
# THIS IS JUST A DUMMY ROUTE FOR WIP testing
@router.get(
"/api/histories/count",
summary="Returns number of histories for the current user.",
)
def count(
self,
trans: ProvidesHistoryContext = DependsOnTrans,
) -> int:
return self.service.count(trans)
@router.get(
"/api/histories/deleted",
summary="Returns deleted histories for the current user.",
@@ -462,6 +462,23 @@ class HistoriesService(ServiceBase, ConsumesModelStores, ServesExportStores):
]
return rval
# THIS IS JUST A DUMMY FN FOR WIP testing
def count(
self,
trans: ProvidesHistoryContext,
):
"""
Returns number of histories for the current user.
"""
# bail early with current history if user is anonymous
current_user = self.user_manager.current_user(trans)
if self.user_manager.is_anonymous(current_user):
current_history = self.manager.get_current(trans)
if not current_history:
return 0
return 1
return len(current_user.active_histories)
def published(
self,
trans: ProvidesHistoryContext,