Skip to content

Commit

Permalink
Implement items provider for server-side pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
davelopez committed May 16, 2024
1 parent 7efbd99 commit 2c6a152
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 7 deletions.
59 changes: 58 additions & 1 deletion client/src/components/FilesDialog/FilesDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@ import {
} from "@/api/remoteFiles";
import { UrlTracker } from "@/components/DataDialog/utilities";
import { fileSourcePluginToItem, isSubPath } from "@/components/FilesDialog/utilities";
import { SELECTION_STATES, type SelectionItem } from "@/components/SelectionDialog/selectionTypes";
import {
ItemsProvider,
ItemsProviderContext,
SELECTION_STATES,
type SelectionItem,
} from "@/components/SelectionDialog/selectionTypes";
import { useConfig } from "@/composables/config";
import { useFileSources } from "@/composables/fileSources";
import { errorMessageAsString } from "@/utils/simple-error";
import { Model } from "./model";
import SelectionDialog from "@/components/SelectionDialog/SelectionDialog.vue";
const filesSources = useFileSources();
interface FilesDialogProps {
/** Callback function to be called passing the results when selection is complete */
callback?: (files: any) => void;
Expand Down Expand Up @@ -55,6 +63,7 @@ const selectedDirectories = ref<SelectionItem[]>([]);
const errorMessage = ref<string>();
const filter = ref();
const items = ref<SelectionItem[]>([]);
const itemsProvider = ref<ItemsProvider>();
const modalShow = ref(true);
const optionsShow = ref(false);
const undoShow = ref(false);
Expand Down Expand Up @@ -232,6 +241,7 @@ function load(record?: SelectionItem) {
optionsShow.value = false;
undoShow.value = !urlTracker.value.atRoot();
if (urlTracker.value.atRoot() || errorMessage.value) {
itemsProvider.value = undefined;
errorMessage.value = undefined;
getFileSources(props.filterOptions)
.then((results) => {
Expand Down Expand Up @@ -260,6 +270,11 @@ function load(record?: SelectionItem) {
showDetails.value = false;
return;
}
if (shouldUseItemsProvider()) {
itemsProvider.value = provideItems;
}
browseRemoteFiles(currentDirectory.value?.url, false, props.requireWritable)
.then((result) => {
items.value = filterByMode(result.entries).map(entryToRecord);
Expand All @@ -275,6 +290,47 @@ function load(record?: SelectionItem) {
}
}
/**
* Check if the current file source supports server-side pagination.
* If it does, we will use the items provider to fetch items.
*/
function shouldUseItemsProvider(): boolean {
const fileSource = filesSources.getFileSourceById(currentDirectory.value?.id!);
const supportsPagination = fileSource?.supports?.pagination;
return Boolean(supportsPagination);
}
/**
* Fetches items from the server using server-side pagination and filtering.
**/
async function provideItems(ctx: ItemsProviderContext): Promise<SelectionItem[]> {
isBusy.value = true;
try {
if (!currentDirectory.value) {
return [];
}
const limit = ctx.perPage;
const offset = (ctx.currentPage - 1) * ctx.perPage;
const query = ctx.filter;
const response = await browseRemoteFiles(
currentDirectory.value?.url,
false,
props.requireWritable,
limit,
offset,
query
);
const result = response.entries.map(entryToRecord);
totalItems.value = response.totalMatches;
return result;
} catch (error) {
errorMessage.value = errorMessageAsString(error);
return [];
} finally {
isBusy.value = false;
}
}
function filterByMode(results: RemoteEntry[]): RemoteEntry[] {
if (!fileMode.value) {
// In directory mode, only show directories
Expand Down Expand Up @@ -350,6 +406,7 @@ onMounted(() => {
:fields="fields"
:is-busy="isBusy"
:items="items"
:items-provider="itemsProvider"
:total-items="totalItems"
:modal-show="modalShow"
:modal-static="modalStatic"
Expand Down
16 changes: 10 additions & 6 deletions client/src/components/SelectionDialog/SelectionDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BAlert, BButton, BLink, BModal, BPagination, BSpinner, BTable } from "bootstrap-vue";
import { computed, ref, watch } from "vue";
import { SELECTION_STATES } from "@/components/SelectionDialog/selectionTypes";
import { ItemsProvider, SELECTION_STATES } from "@/components/SelectionDialog/selectionTypes";
import { type FieldEntry, type SelectionItem } from "./selectionTypes";
Expand All @@ -22,10 +22,11 @@ interface Props {
disableOk?: boolean;
errorMessage?: string;
fileMode?: boolean;
fields?: Array<FieldEntry>;
fields?: FieldEntry[];
isBusy?: boolean;
isEncoded?: boolean;
items?: Array<SelectionItem>;
items?: SelectionItem[];
itemsProvider?: ItemsProvider;
totalItems?: number;
leafIcon?: string;
modalShow?: boolean;
Expand All @@ -46,6 +47,7 @@ const props = withDefaults(defineProps<Props>(), {
isBusy: false,
isEncoded: false,
items: () => [],
itemsProvider: undefined,
totalItems: 0,
leafIcon: "fa fa-file-o",
modalShow: true,
Expand All @@ -69,7 +71,7 @@ const emit = defineEmits<{
const filter = ref("");
const currentPage = ref(1);
const perPage = ref(100);
const perPage = ref(25);
const fieldDetails = computed(() => {
const fields = props.fields.slice().map((x) => {
Expand Down Expand Up @@ -98,7 +100,9 @@ function selectionIcon(variant: string) {
/** Resets pagination when a filter/search word is entered **/
function filtered(items: Array<SelectionItem>) {
currentPage.value = 1;
if (props.itemsProvider === undefined) {
currentPage.value = 1;
}
}
/** Format time stamp */
Expand Down Expand Up @@ -148,7 +152,7 @@ watch(
primary-key="id"
:busy="isBusy"
:current-page="currentPage"
:items="items"
:items="itemsProvider ?? items"
:fields="fieldDetails"
:filter="filter"
:per-page="perPage"
Expand Down
11 changes: 11 additions & 0 deletions client/src/components/SelectionDialog/selectionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,14 @@ export interface SelectionItem {
isLeaf: boolean;
url: string;
}

export interface ItemsProviderContext {
apiUrl?: string;
currentPage: number;
perPage: number;
filter?: string;
sortBy?: string;
sortDesc?: boolean;
}

export type ItemsProvider = (ctx: ItemsProviderContext) => Promise<SelectionItem[]>;

0 comments on commit 2c6a152

Please sign in to comment.