Skip to content

Commit 2ac7bd0

Browse files
committed
refactor: Add ability to hydrate outside batch()
Signed-off-by: Jacob Schlecht <dadadah@echoha.us>
1 parent d9a7d91 commit 2ac7bd0

5 files changed

Lines changed: 125 additions & 63 deletions

File tree

src/classes/Server.ts

Lines changed: 31 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import { decodeTime } from "ulid";
2222
import type { ServerCollection } from "../collections/ServerCollection.js";
2323
import { hydrate } from "../hydration/index.js";
2424
import type { ServerFlags } from "../hydration/server.js";
25+
import { HydratedServerMember } from "../hydration/serverMember.js";
26+
import { HydratedUser } from "../hydration/user.js";
2527
import {
2628
bitwiseAndEq,
2729
calculatePermission,
@@ -675,79 +677,46 @@ export class Server {
675677

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

678-
async syncMembers(
679-
excludeOffline?: boolean,
680-
excludeOfflineUserCap?: number,
681-
): Promise<void> {
680+
async syncMembers(excludeOffline?: boolean): Promise<void> {
682681
if (this.#synced && (this.#synced === "full" || excludeOffline)) return;
683682

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

689-
batch(() => {
690-
if (excludeOffline && excludeOfflineUserCap) {
691-
// quick fix to cap users
692-
let count = 0;
693-
694-
for (
695-
let i = 0;
696-
i < data.users.length && count < excludeOfflineUserCap;
697-
i++
698-
) {
699-
const user = data.users[i];
700-
if (user.online && data.members[i].roles?.length) {
701-
this.#collection.client.users.getOrCreate(user._id, user);
702-
this.#collection.client.serverMembers.getOrCreate(
703-
data.members[i]._id,
704-
data.members[i],
705-
);
706-
707-
count++;
708-
}
709-
}
688+
const newUsers: HydratedUser[] = [];
689+
const newServerMembers: HydratedServerMember[] = [];
710690

711-
for (
712-
let i = 0;
713-
i < data.users.length && count < excludeOfflineUserCap;
714-
i++
715-
) {
716-
const user = data.users[i];
717-
if (user.online && !data.members[i].roles?.length) {
718-
this.#collection.client.users.getOrCreate(user._id, user);
719-
this.#collection.client.serverMembers.getOrCreate(
720-
data.members[i]._id,
721-
data.members[i],
722-
);
723-
724-
count++;
725-
}
726-
}
727-
// end quick fix
728-
} else if (excludeOffline) {
729-
for (let i = 0; i < data.users.length; i++) {
730-
const user = data.users[i];
731-
if (user.online) {
732-
this.#collection.client.users.getOrCreate(user._id, user);
733-
this.#collection.client.serverMembers.getOrCreate(
734-
data.members[i]._id,
735-
data.members[i],
736-
);
737-
}
691+
for (let i = 0; i < data.users.length; i++) {
692+
const user = data.users[i];
693+
if (!excludeOffline || user.online) {
694+
const newUser = this.#collection.client.users.hydrateIfNotHas(
695+
user._id,
696+
user,
697+
);
698+
if (newUser) {
699+
newUsers.push(newUser);
738700
}
739-
} else {
740-
for (let i = 0; i < data.users.length; i++) {
741-
this.#collection.client.users.getOrCreate(
742-
data.users[i]._id,
743-
data.users[i],
744-
);
745-
this.#collection.client.serverMembers.getOrCreate(
746-
data.members[i]._id,
747-
data.members[i],
748-
);
701+
const newMember = this.#collection.client.serverMembers.hydrateIfNotHas(
702+
data.members[i]._id,
703+
data.members[i],
704+
);
705+
if (newMember) {
706+
newServerMembers.push(newMember);
749707
}
750708
}
709+
}
710+
711+
batch(() => {
712+
for (const newUser of newUsers) {
713+
this.#collection.client.users.addHydratedUser(newUser);
714+
}
715+
for (const newServerMember of newServerMembers) {
716+
this.#collection.client.serverMembers.addHydratedServerMember(
717+
newServerMember,
718+
);
719+
}
751720
});
752721
}
753722

src/collections/Collection.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,23 @@ export abstract class StoreCollection<T, V> extends Collection<T> {
175175
this.#objects.set(id, instance);
176176
}
177177

178+
/**
179+
* Hydrate a new instance of an object. This function does not add the object to the collection.
180+
* @param id Id
181+
* @param type Type
182+
* @param instance Instance
183+
* @param context Context
184+
* @param data Data
185+
*/
186+
protected hydrate(type: keyof Hydrators, context: unknown, data: object): V {
187+
return this.#storage.hydrateOnly(type, context, data);
188+
}
189+
190+
protected add(id: string, instance: T, hydrated: V) {
191+
this.#storage.add(id, hydrated);
192+
this.#objects.set(id, instance);
193+
}
194+
178195
/**
179196
* Check whether an object is partially defined
180197
* @param id Id

src/collections/ServerMemberCollection.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,36 @@ export class ServerMemberCollection extends ClassCollection<
9797
return instance;
9898
}
9999
}
100+
101+
/**
102+
* Hydrate a new server member if it is not in the collection yet. This
103+
* function does not add the server member to the store, make sure you call
104+
* ServerMemberCollection.addHydratedServerMember afterwards. This function
105+
* is particularly useful when adding many server members asynchronously. See
106+
* Server.syncMembers for an example of this in use.
107+
* @param id The ID of the server member
108+
* @param data The API object for a server member
109+
* @returns The HydratedServerMember, or undefined if the user is in the collection
110+
*/
111+
hydrateIfNotHas(
112+
id: MemberCompositeKey,
113+
data: Member,
114+
): HydratedServerMember | undefined {
115+
if (this.hasByKey(id) && !this.isPartialByKey(id)) {
116+
return;
117+
} else {
118+
return this.hydrate("serverMember", this.client, data);
119+
}
120+
}
121+
122+
/**
123+
* Add a pre-hydrated server member to this collection.
124+
* @param user A hydrated server member
125+
* @returns The server member instance
126+
*/
127+
addHydratedServerMember(member: HydratedServerMember): ServerMember {
128+
const instance = new ServerMember(this, member.id);
129+
this.add(member.id.server + member.id.user, instance, member);
130+
return instance;
131+
}
100132
}

src/collections/UserCollection.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export class UserCollection extends ClassCollection<User, HydratedUser> {
4242
* Get or create
4343
* @param id Id
4444
* @param data Data
45-
* @param isNew Whether this object is new
4645
*/
4746
getOrCreate(id: string, data: APIUser): User {
4847
if (this.has(id) && !this.isPartial(id)) {
@@ -70,4 +69,33 @@ export class UserCollection extends ClassCollection<User, HydratedUser> {
7069
return instance;
7170
}
7271
}
72+
73+
/**
74+
* Hydrate a new user if it is not in the collection yet. This function does
75+
* not add the user to the store, make sure you call
76+
* UserCollection.addHydratedUser afterwards. This function is particularly
77+
* useful when adding many users asynchronously. See Server.syncMembers for
78+
* an example of this in use.
79+
* @param id The ID of the user
80+
* @param data The API object for a user
81+
* @returns The HydratedUser, or undefined if the user is in the collection
82+
*/
83+
hydrateIfNotHas(id: string, data: APIUser): HydratedUser | undefined {
84+
if (this.has(id) && !this.isPartial(id)) {
85+
return;
86+
} else {
87+
return this.hydrate("user", this.client, data);
88+
}
89+
}
90+
91+
/**
92+
* Add a pre-hydrated user to this collection.
93+
* @param user A hydrated user
94+
* @returns The user instance
95+
*/
96+
addHydratedUser(user: HydratedUser): User {
97+
const instance = new User(this, user.id);
98+
this.add(user.id, instance, user);
99+
return instance;
100+
}
73101
}

src/storage/ObjectStorage.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,20 @@ export class ObjectStorage<T> {
4747
this.set(id, hydrate(type, data as never, context, true) as T);
4848
}
4949
}
50+
51+
/**
52+
* Hydrate some data without putting it into storage
53+
* @param type Hydration type
54+
* @param context Context
55+
* @param data Input Data
56+
* @returns The hydrated type
57+
*/
58+
hydrateOnly(type: keyof Hydrators, context: unknown, data: object): T {
59+
data = { partial: false, ...data };
60+
return hydrate(type, data as never, context, true) as T;
61+
}
62+
63+
add(id: string, toAdd: T) {
64+
this.set(id, toAdd);
65+
}
5066
}

0 commit comments

Comments
 (0)