Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 31 additions & 62 deletions src/classes/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { decodeTime } from "ulid";
import type { ServerCollection } from "../collections/ServerCollection.js";
import { hydrate } from "../hydration/index.js";
import type { ServerFlags } from "../hydration/server.js";
import { HydratedServerMember } from "../hydration/serverMember.js";
import { HydratedUser } from "../hydration/user.js";
import {
bitwiseAndEq,
calculatePermission,
Expand Down Expand Up @@ -675,79 +677,46 @@ export class Server {

#synced: undefined | "partial" | "full";

async syncMembers(
excludeOffline?: boolean,
excludeOfflineUserCap?: number,
): Promise<void> {
async syncMembers(excludeOffline?: boolean): Promise<void> {
if (this.#synced && (this.#synced === "full" || excludeOffline)) return;

const data = await this.#collection.client.api.get(
`/servers/${this.id as ""}/members`,
{ exclude_offline: excludeOffline },
);

batch(() => {
if (excludeOffline && excludeOfflineUserCap) {
// quick fix to cap users
let count = 0;

for (
let i = 0;
i < data.users.length && count < excludeOfflineUserCap;
i++
) {
const user = data.users[i];
if (user.online && data.members[i].roles?.length) {
this.#collection.client.users.getOrCreate(user._id, user);
this.#collection.client.serverMembers.getOrCreate(
data.members[i]._id,
data.members[i],
);

count++;
}
}
const newUsers: HydratedUser[] = [];
const newServerMembers: HydratedServerMember[] = [];

for (
let i = 0;
i < data.users.length && count < excludeOfflineUserCap;
i++
) {
const user = data.users[i];
if (user.online && !data.members[i].roles?.length) {
this.#collection.client.users.getOrCreate(user._id, user);
this.#collection.client.serverMembers.getOrCreate(
data.members[i]._id,
data.members[i],
);

count++;
}
}
// end quick fix
} else if (excludeOffline) {
for (let i = 0; i < data.users.length; i++) {
const user = data.users[i];
if (user.online) {
this.#collection.client.users.getOrCreate(user._id, user);
this.#collection.client.serverMembers.getOrCreate(
data.members[i]._id,
data.members[i],
);
}
for (let i = 0; i < data.users.length; i++) {
const user = data.users[i];
if (!excludeOffline || user.online) {
const newUser = this.#collection.client.users.hydrateIfNotHas(
user._id,
user,
);
if (newUser) {
newUsers.push(newUser);
}
} else {
for (let i = 0; i < data.users.length; i++) {
this.#collection.client.users.getOrCreate(
data.users[i]._id,
data.users[i],
);
this.#collection.client.serverMembers.getOrCreate(
data.members[i]._id,
data.members[i],
);
const newMember = this.#collection.client.serverMembers.hydrateIfNotHas(
data.members[i]._id,
data.members[i],
);
if (newMember) {
newServerMembers.push(newMember);
}
}
}

batch(() => {
for (const newUser of newUsers) {
this.#collection.client.users.addHydratedUser(newUser);
}
for (const newServerMember of newServerMembers) {
this.#collection.client.serverMembers.addHydratedServerMember(
newServerMember,
);
}
});
}

Expand Down
23 changes: 23 additions & 0 deletions src/collections/Collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,29 @@ export abstract class StoreCollection<T, V> extends Collection<T> {
this.#objects.set(id, instance);
}

/**
* Hydrate a new instance of an object. This function does not add the object to the collection.
* @param id Id
* @param type Type
* @param instance Instance
* @param context Context
* @param data Data
*/
protected hydrate(type: keyof Hydrators, context: unknown, data: object): V {
return this.#storage.hydrateOnly(type, context, data);
}

/**
* Add a previously hydrated object to the collection
* @param id Id
* @param instance Instance
* @param hydrated Hydrated
*/
protected add(id: string, instance: T, hydrated: V) {
this.#storage.add(id, hydrated);
this.#objects.set(id, instance);
}

/**
* Check whether an object is partially defined
* @param id Id
Expand Down
32 changes: 32 additions & 0 deletions src/collections/ServerMemberCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,36 @@ export class ServerMemberCollection extends ClassCollection<
return instance;
}
}

/**
* Hydrate a new server member if it is not in the collection yet. This
* function does not add the server member to the store, make sure you call
* {@link addHydratedServerMember} afterwards. This function is particularly
* useful when adding many server members asynchronously. See
* Server.syncMembers for an example of this in use.
* @param id The ID of the server member
* @param data The API object for a server member
* @returns The HydratedServerMember, or undefined if the user is in the collection
*/
hydrateIfNotHas(
id: MemberCompositeKey,
data: Member,
): HydratedServerMember | undefined {
if (this.hasByKey(id) && !this.isPartialByKey(id)) {
return;
} else {
return this.hydrate("serverMember", this.client, data);
}
}

/**
* Add a pre-hydrated server member to this collection.
* @param user A hydrated server member
* @returns The server member instance
*/
addHydratedServerMember(member: HydratedServerMember): ServerMember {
const instance = new ServerMember(this, member.id);
this.add(member.id.server + member.id.user, instance, member);
return instance;
}
}
29 changes: 28 additions & 1 deletion src/collections/UserCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export class UserCollection extends ClassCollection<User, HydratedUser> {
* Get or create
* @param id Id
* @param data Data
* @param isNew Whether this object is new
*/
getOrCreate(id: string, data: APIUser): User {
if (this.has(id) && !this.isPartial(id)) {
Expand Down Expand Up @@ -70,4 +69,32 @@ export class UserCollection extends ClassCollection<User, HydratedUser> {
return instance;
}
}

/**
* Hydrate a new user if it is not in the collection yet. This function does
* not add the user to the store, make sure you call {@link addHydratedUser}
* afterwards. This function is particularly useful when adding many users
* asynchronously. See Server.syncMembers for an example of this in use.
* @param id The ID of the user
* @param data The API object for a user
* @returns The HydratedUser, or undefined if the user is in the collection
*/
hydrateIfNotHas(id: string, data: APIUser): HydratedUser | undefined {
if (this.has(id) && !this.isPartial(id)) {
return;
} else {
return this.hydrate("user", this.client, data);
}
}

/**
* Add a pre-hydrated user to this collection.
* @param user A hydrated user
* @returns The user instance
*/
addHydratedUser(user: HydratedUser): User {
const instance = new User(this, user.id);
this.add(user.id, instance, user);
return instance;
}
}
22 changes: 22 additions & 0 deletions src/storage/ObjectStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,26 @@ export class ObjectStorage<T> {
this.set(id, hydrate(type, data as never, context, true) as T);
}
}

/**
* Hydrate some data without putting it into storage. Call {@link add}
* afterwards to add the hydrated value into storage.
* @param type Hydration type
* @param context Context
* @param data Input Data
* @returns The hydrated type
*/
hydrateOnly(type: keyof Hydrators, context: unknown, data: object): T {
data = { partial: false, ...data };
return hydrate(type, data as never, context, true) as T;
}

/**
* Add a previously hydrated object to the storage
* @param id ID
* @param toAdd Hydrated object
*/
add(id: string, toAdd: T) {
this.set(id, toAdd);
}
}
Loading