fix: Load relationships on search page load (#6295)

This commit is contained in:
Tom Moor
2023-12-16 10:36:19 -05:00
committed by GitHub
parent ab7515b0e1
commit 7df0f63ce6
4 changed files with 45 additions and 29 deletions

View File

@@ -68,6 +68,10 @@ const DocumentBreadcrumb: React.FC<Props> = ({
? collections.get(document.collectionId)
: undefined;
React.useEffect(() => {
void document.loadRelations();
}, [document]);
let collectionNode: MenuInternalLink | undefined;
if (collection) {

View File

@@ -3,6 +3,7 @@ import { set, observable, action } from "mobx";
import type Store from "~/stores/base/Store";
import Logger from "~/utils/Logger";
import { getFieldsForModel } from "../decorators/Field";
import { getRelationsForModelClass } from "../decorators/Relation";
export default abstract class Model {
static modelName: string;
@@ -16,6 +17,7 @@ export default abstract class Model {
@observable
isNew: boolean;
@observable
createdAt: string;
@observable
@@ -29,6 +31,36 @@ export default abstract class Model {
this.isNew = !this.id;
}
/**
* Ensures all the defined relations for the model are in memory
*
* @returns A promise that resolves when loading is complete.
*/
async loadRelations() {
const relations = getRelationsForModelClass(
this.constructor as typeof Model
);
if (!relations) {
return;
}
for (const properties of relations.values()) {
const store = this.store.rootStore.getStoreForModelName(
properties.relationClassResolver().modelName
);
if ("fetch" in store) {
await store.fetch(this[properties.idKey]);
}
}
}
/**
* Persists the model to the server API
*
* @param params Specific fields to save, if not provided the model will be serialized
* @param options Options to pass to the store
* @returns A promise that resolves with the updated model
*/
save = async (
params?: Record<string, any>,
options?: Record<string, string | boolean | number | undefined>
@@ -93,7 +125,7 @@ export default abstract class Model {
* Returns a plain object representation of fields on the model for
* persistence to the server API
*
* @returns {Record<string, any>}
* @returns A plain object representation of the model
*/
toAPI = (): Record<string, any> => {
const fields = getFieldsForModel(this);
@@ -104,7 +136,7 @@ export default abstract class Model {
* Returns a plain object representation of all the properties on the model
* overrides the native toJSON method to avoid attempting to serialize store
*
* @returns {Record<string, any>}
* @returns A plain object representation of the model
*/
toJSON() {
const output: Partial<typeof this> = {};

View File

@@ -49,6 +49,9 @@ export const getInverseRelationsForModelClass = (targetClass: typeof Model) => {
return inverseRelations;
};
export const getRelationsForModelClass = (targetClass: typeof Model) =>
relations.get(targetClass.modelName);
/**
* A decorator that records this key as a relation field on the model.
* Properties decorated with @Relation will merge and read their data from

View File

@@ -11,7 +11,6 @@ import {
} from "@shared/types";
import Collection from "~/models/Collection";
import { client } from "~/utils/ApiClient";
import { AuthorizationError, NotFoundError } from "~/utils/errors";
import RootStore from "./RootStore";
import Store from "./base/Store";
@@ -164,32 +163,10 @@ export default class CollectionsStore extends Store<Collection> {
}
@action
async fetch(
id: string,
options: Record<string, any> = {}
): Promise<Collection> {
const item = this.get(id) || this.getByUrl(id);
if (item && !options.force) {
return item;
}
this.isFetching = true;
try {
const res = await client.post(`/collections.info`, {
id,
});
invariant(res?.data, "Collection not available");
this.addPolicies(res.policies);
return this.add(res.data);
} catch (err) {
if (err instanceof AuthorizationError || err instanceof NotFoundError) {
this.remove(id);
}
throw err;
} finally {
this.isFetching = false;
}
async fetch(id: string, options?: { force: boolean }): Promise<Collection> {
const model = await super.fetch(id, options);
await model.fetchDocuments(options);
return model;
}
@computed