Page MenuHomeSealhub

collection.ts
No OneTemporary

collection.ts

import { useState, useEffect } from "./react-api/api";
import axios from "axios";
import EventEmitter from "eventemitter3";
import {
SealiousItem,
SealiousCollectionResponse,
CollectionParams,
ComplexFilterParam,
} from "./types";
import { CollectionItem, ExtractItemFields } from "./item";
import url from "./url";
const DEFAULT_ITEMS_PER_PAGE = 10;
export type ExtractCollectionItemClass<
C extends CollectionClient<any>
> = InstanceType<C["item_constructor"]>;
export type ExtractCollectionFields<
C extends CollectionClient<any>
> = ExtractItemFields<ExtractCollectionItemClass<C>>;
export function useCollection<
TCollection extends CollectionClient<any>,
Fields = ExtractCollectionFields<TCollection>,
ItemClass = ExtractCollectionItemClass<TCollection>
>(
CollectionClass: CollectionClass<TCollection>,
params: Fields
): [TCollection, ItemClass[], CollectionParams<Fields>, number] {
const [collectionClient] = useState<TCollection>(
() => new CollectionClass(params)
);
const [version, setVersion] = useState(0);
const [items, setItems] = useState<ItemClass[]>([]);
useEffect(() => {
const callback = () =>
collectionClient
.refresh()
.then(() => setItems(collectionClient.items));
collectionClient.on("change", callback);
collectionClient.on("state-change", () => {
setVersion((v) => v + 1);
});
callback();
return () => {
collectionClient.removeListener("change", callback);
};
}, []);
return [collectionClient, items, collectionClient.params, version];
}
export type CollectionClass<TCollection extends CollectionClient<any>> = new (
params: CollectionParams<
ExtractItemFields<ExtractCollectionItemClass<TCollection>>
>
) => TCollection;
export default class CollectionClient<
ItemClass extends CollectionItem<any>
> extends EventEmitter {
items: ItemClass[];
collection_name: string;
item_constructor: new (
id: string,
body: ExtractItemFields<ItemClass> & SealiousItem,
attachments?: { [id: string]: SealiousItem }
) => ItemClass;
params: CollectionParams<ExtractItemFields<ItemClass>>;
needs_saving: boolean = false;
has_next_page: boolean | null = null;
loading: boolean = false;
constructor(params: CollectionParams<ExtractItemFields<ItemClass>>) {
super();
this.params = params;
this.save = this.save.bind(this);
this.setPage = this.setPage.bind(this);
this.getPage = this.getPage.bind(this);
this.nextPage = this.nextPage.bind(this);
this.prevPage = this.prevPage.bind(this);
}
setFilter<Field extends keyof ExtractItemFields<ItemClass>>(
field_name: Field,
value:
| ComplexFilterParam<ExtractItemFields<ItemClass>[Field]>
| ExtractItemFields<ItemClass>[Field],
replace: boolean = false
) {
if (!this.params.filter) {
this.params.filter = {};
}
if (replace) {
this.params.filter[field_name] = value;
} else {
if (typeof value == "object") {
this.params.filter[field_name] = {
...this.params.filter[field_name],
...value,
};
} else {
this.params.filter = {
...this.params.filter,
[field_name]: value,
};
}
}
if (this.isPaginated()) {
this.setPage(1);
}
this.emit("change");
}
isPaginated(): boolean {
return typeof this.params.pagination == "object";
}
setPage(page: number) {
if (!this.isPaginated()) {
throw new Error("This client does not use pagination");
}
this.params.pagination = {
items: this?.params?.pagination?.items || DEFAULT_ITEMS_PER_PAGE,
page: page,
};
this.emit("change");
}
getPage(): number {
if (!this.isPaginated()) {
throw new Error("This client does not use pagination");
}
return this.params?.pagination?.page || 1;
}
nextPage() {
if (this.has_next_page) {
this.setPage(this.getPage() + 1);
}
}
prevPage() {
if (this.getPage() > 0) {
this.setPage(this.getPage() - 1);
}
}
async refresh(): Promise<void> {
this.loading = true;
this.emit("state-change");
const response = (await axios.get(
url(`/api/v1/collections/${this.collection_name}`, this.params)
)) as {
data: SealiousCollectionResponse<
SealiousItem & ExtractItemFields<ItemClass>
>;
};
this.needs_saving = false;
const attachments = response.data.attachments;
if (this.isPaginated()) {
this.has_next_page =
response.data.items.length === this.params?.pagination?.items;
}
this.items = response.data.items.map((item) => {
const entry = new this.item_constructor(item.id, item, attachments);
entry.on("change", () => this.emit("change"));
entry.on("temp-change", () => (this.needs_saving = true));
return entry;
});
this.loading = false;
this.emit("state-change");
}
async create(body: ExtractItemFields<ItemClass>) {
const response = await axios.post(
`/api/v1/collections/${this.collection_name}`,
body
);
this.emit("change");
return response.data.items;
}
async save() {
const promises = [];
for (const item of this.items) {
if (item.needs_saving) {
promises.push(item.save());
}
}
await Promise.all(promises);
this.needs_saving = false;
this.emit("change");
}
}

File Metadata

Mime Type
text/x-java
Expires
Sat, Nov 23, 04:12 (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
547830
Default Alt Text
collection.ts (4 KB)

Event Timeline