Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F9582626
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/collection.ts b/src/collection.ts
index 513e09a..d0b21c2 100644
--- a/src/collection.ts
+++ b/src/collection.ts
@@ -1,172 +1,181 @@
import { useState, useEffect } from "./react-api/api";
import axios from "axios";
import EventEmitter from "eventemitter3";
import {
SealiousItem,
SealiousCollectionResponse,
CollectionParams,
ComplexFilterParam,
} from "./types";
import { CollectionItem } from "./item";
import url from "./url";
const DEFAULT_ITEMS_PER_PAGE = 10;
export function useCollection<
ItemFields,
ItemClass extends CollectionItem<ItemFields>,
TCollection extends CollectionClient<ItemFields, ItemClass>
>(
CollectionClass: CollectionClass<ItemFields, ItemClass, TCollection>,
params: CollectionParams<ItemFields>
-): [TCollection, ItemClass[], CollectionParams<ItemFields>] {
+): [TCollection, ItemClass[], CollectionParams<ItemFields>, 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];
+ return [collectionClient, items, collectionClient.params, version];
}
export type CollectionClass<
ItemFields,
Y extends CollectionItem<ItemFields>,
TCollection extends CollectionClient<ItemFields, Y>
> = new (params: CollectionParams<ItemFields>) => TCollection;
export default class CollectionClient<
ItemFields,
ItemClass extends CollectionItem<ItemFields>
> extends EventEmitter {
items: ItemClass[];
collection_name: string;
item_constructor: new (
id: string,
body: ItemFields & SealiousItem,
attachments?: { [id: string]: SealiousItem }
) => ItemClass;
params: CollectionParams<ItemFields>;
needs_saving: boolean = false;
has_next_page: boolean | null = null;
+ loading: boolean = false;
constructor(params: CollectionParams<ItemFields>) {
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 ItemFields>(
field_name: Field,
value: ComplexFilterParam<ItemFields[Field]> | ItemFields[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,
};
}
}
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 & ItemFields> };
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: ItemFields) {
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");
}
}
diff --git a/src/users.ts b/src/users.ts
index d3ad11a..00375a4 100644
--- a/src/users.ts
+++ b/src/users.ts
@@ -1,84 +1,84 @@
import axios from "axios";
import Collection, { useCollection } from "./collection";
import { useItem, CollectionItem } from "./item";
import {
SingleResourceParams,
SealiousUserRoleItem,
SealiousCollectionResponse,
SealiousItem,
} from "./types";
import url from "./url";
export function useUsers(
params: SingleResourceParams<typeof UserFields>
-): [Users, User[], SingleResourceParams<typeof UserFields>] {
+): [Users, User[], SingleResourceParams<typeof UserFields>, number] {
return useCollection<typeof UserFields, User, Users>(Users, params);
}
export function useUser(
id: string
): [boolean, typeof UserFields | undefined, User] {
return useItem<typeof UserFields, User>(User, id);
}
export const UserFields = {
username: "",
prefered_hours_per_day: 0,
email: "",
roles: [] as Array<string>,
};
export class User extends CollectionItem<typeof UserFields> {
getInfo() {
return {
collection_name: "users",
fields: UserFields,
writable_fields: [
"username",
"prefered_hours_per_day",
"email",
] as (keyof typeof UserFields)[],
fields_with_attachments: ["roles"] as (keyof typeof UserFields)[],
};
}
getRoles(): string[] {
const roles = this.temp_data.roles
.map((role_id: string) => this.getAttachment(role_id))
.map((role: SealiousUserRoleItem) => role.role);
return roles;
}
hasRole(role: string) {
return this.getRoles().includes(role);
}
async setRole(role: string) {
const {
data: { items: current_roles },
} = (await axios.get(
url(`/api/v1/collections/user-roles`, {
filter: { user: this.id },
})
)) as {
data: SealiousCollectionResponse<typeof UserFields & SealiousItem>;
};
for (const role_entry of current_roles) {
await axios.delete(
`/api/v1/collections/user-roles/${role_entry.id}`
);
}
await axios.post("/api/v1/collections/user-roles", {
role: role,
user: this.id,
});
await this.refresh();
}
}
export class Users extends Collection<typeof UserFields, User> {
collection_name = "users";
item_constructor = User;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Oct 11, 07:20 (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
983962
Default Alt Text
(7 KB)
Attached To
Mode
rCOS Cosealious
Attached
Detach File
Event Timeline
Log In to Comment