mirror of
https://github.com/stoatchat/javascript-client-sdk.git
synced 2026-07-19 09:05:59 -04:00
feat: create separate collection classes
This commit is contained in:
+45
-20
@@ -9,23 +9,38 @@ import messageClassFactory from "./classes/Message";
|
||||
import serverClassFactory from "./classes/Server";
|
||||
import serverMemberClassFactory from "./classes/ServerMember";
|
||||
import userClassFactory from "./classes/User";
|
||||
import {
|
||||
ChannelCollection,
|
||||
EmojiCollection,
|
||||
MessageCollection,
|
||||
ServerCollection,
|
||||
ServerMemberCollection,
|
||||
UserCollection,
|
||||
} from "./collections";
|
||||
import { EventClient, createEventClient } from "./events/client";
|
||||
|
||||
// eslint-disable-next-line
|
||||
type Obj<T extends (...args: any) => any> = InstanceType<ReturnType<T>>;
|
||||
type O<T extends (...args: any) => any> = InstanceType<ReturnType<T>>;
|
||||
|
||||
export type Channel = Obj<typeof channelClassFactory>;
|
||||
export type Emoji = Obj<typeof emojiClassFactory>;
|
||||
export type Message = Obj<typeof messageClassFactory>;
|
||||
export type Server = Obj<typeof serverClassFactory>;
|
||||
export type ServerMember = Obj<typeof serverMemberClassFactory>;
|
||||
export type User = Obj<typeof userClassFactory>;
|
||||
export type Channel = O<typeof channelClassFactory>;
|
||||
export type Emoji = O<typeof emojiClassFactory>;
|
||||
export type Message = O<typeof messageClassFactory>;
|
||||
export type Server = O<typeof serverClassFactory>;
|
||||
export type ServerMember = O<typeof serverMemberClassFactory>;
|
||||
export type User = O<typeof userClassFactory>;
|
||||
export type Session = { token: string; user_id: string } | string;
|
||||
|
||||
/**
|
||||
* Revolt.js Client
|
||||
*/
|
||||
export class Client {
|
||||
readonly Channel: ReturnType<typeof channelClassFactory>;
|
||||
readonly Emoji: ReturnType<typeof emojiClassFactory>;
|
||||
readonly Message: ReturnType<typeof messageClassFactory>;
|
||||
readonly User: ReturnType<typeof userClassFactory>;
|
||||
readonly Server: ReturnType<typeof serverClassFactory>;
|
||||
readonly ServerMember: ReturnType<typeof serverMemberClassFactory>;
|
||||
|
||||
readonly channels;
|
||||
readonly emojis;
|
||||
readonly messages;
|
||||
@@ -59,12 +74,22 @@ export class Client {
|
||||
this.ready = ready;
|
||||
this.#setReady = setReady;
|
||||
|
||||
this.channels = channelClassFactory(this);
|
||||
this.emojis = emojiClassFactory(this);
|
||||
this.messages = messageClassFactory(this);
|
||||
this.users = userClassFactory(this);
|
||||
this.servers = serverClassFactory(this);
|
||||
this.serverMembers = serverMemberClassFactory(this);
|
||||
this.channels = new ChannelCollection();
|
||||
this.emojis = new EmojiCollection();
|
||||
this.messages = new MessageCollection();
|
||||
this.users = new UserCollection();
|
||||
this.servers = new ServerCollection();
|
||||
this.serverMembers = new ServerMemberCollection();
|
||||
|
||||
this.Channel = channelClassFactory(this, this.channels as never);
|
||||
this.Emoji = emojiClassFactory(this, this.emojis as never);
|
||||
this.Message = messageClassFactory(this, this.messages as never);
|
||||
this.User = userClassFactory(this, this.users as never);
|
||||
this.Server = serverClassFactory(this, this.servers as never);
|
||||
this.ServerMember = serverMemberClassFactory(
|
||||
this,
|
||||
this.serverMembers as never
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +101,7 @@ export class Client {
|
||||
if (event.type === "Ready") {
|
||||
console.time("load users");
|
||||
for (const user of event.users) {
|
||||
const u = new this.users(user._id, user);
|
||||
const u = new this.User(user._id, user);
|
||||
|
||||
if (u.relationship === "User") {
|
||||
this.user = u;
|
||||
@@ -85,22 +110,22 @@ export class Client {
|
||||
console.timeEnd("load users");
|
||||
console.time("load servers");
|
||||
for (const server of event.servers) {
|
||||
new this.servers(server._id, server);
|
||||
new this.Server(server._id, server);
|
||||
}
|
||||
console.timeEnd("load servers");
|
||||
console.time("load memberships");
|
||||
for (const member of event.members) {
|
||||
new this.serverMembers(member._id, member);
|
||||
new this.ServerMember(member._id, member);
|
||||
}
|
||||
console.timeEnd("load memberships");
|
||||
console.time("load channels");
|
||||
for (const channel of event.channels) {
|
||||
new this.channels(channel._id, channel);
|
||||
new this.Channel(channel._id, channel);
|
||||
}
|
||||
console.timeEnd("load channels");
|
||||
console.time("load emojis");
|
||||
for (const emoji of event.emojis) {
|
||||
new this.emojis(emoji._id, emoji);
|
||||
new this.Emoji(emoji._id, emoji);
|
||||
}
|
||||
console.timeEnd("load emojis");
|
||||
|
||||
@@ -113,11 +138,11 @@ export class Client {
|
||||
"It has the channels:",
|
||||
lounge.channels.map((channel) => channel.name)
|
||||
);
|
||||
console.log(
|
||||
/*console.log(
|
||||
"They joined at:",
|
||||
this.serverMembers.get({ server: lounge.id, user: lounge.owner!.id })
|
||||
?.joinedAt
|
||||
);
|
||||
);*/
|
||||
|
||||
this.#setReady(true);
|
||||
}
|
||||
|
||||
+216
-100
@@ -1,107 +1,60 @@
|
||||
import { ReactiveMap } from "@solid-primitives/map";
|
||||
import type { Channel as ApiChannel } from "revolt-api";
|
||||
import { decodeTime } from "ulid";
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
|
||||
import type {
|
||||
Channel as ApiChannel,
|
||||
DataEditChannel,
|
||||
DataMessageSend,
|
||||
Member,
|
||||
Message,
|
||||
User,
|
||||
} from "revolt-api";
|
||||
import { APIRoutes } from "revolt-api/dist/routes";
|
||||
import { decodeTime, ulid } from "ulid";
|
||||
|
||||
import { Client } from "../Client";
|
||||
import { StoreCollection } from "../collections/Collection";
|
||||
import { hydrate } from "../hydration";
|
||||
import { HydratedChannel } from "../hydration/channel";
|
||||
import { bitwiseAndEq, calculatePermission } from "../permissions/calculator";
|
||||
import { Permission } from "../permissions/definitions";
|
||||
import { ObjectStorage } from "../storage/ObjectStorage";
|
||||
|
||||
export default (client: Client) =>
|
||||
export default (
|
||||
client: Client,
|
||||
collection: StoreCollection<unknown, HydratedChannel>
|
||||
) =>
|
||||
/**
|
||||
* Channel Class
|
||||
*/
|
||||
class Channel {
|
||||
static #storage = new ObjectStorage<HydratedChannel>();
|
||||
static #collection: StoreCollection<
|
||||
InstanceType<typeof this>,
|
||||
HydratedChannel
|
||||
>;
|
||||
static #set: SetStoreFunction<Record<string, HydratedChannel>>;
|
||||
static #get: (id: string) => HydratedChannel;
|
||||
|
||||
static {
|
||||
Channel.#collection = collection as never;
|
||||
Channel.#set = collection.updateUnderlyingObject as never;
|
||||
Channel.#get = collection.getUnderlyingObject as never;
|
||||
|
||||
client.events.on("event", (event) => {
|
||||
switch (event.type) {
|
||||
case "ChannelUpdate": {
|
||||
this.#storage.set(event.id, hydrate("channel", event.data));
|
||||
this.#set(event.id, hydrate("channel", event.data));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// * Object Map Definition
|
||||
static #objects = new ReactiveMap<string, InstanceType<typeof this>>();
|
||||
|
||||
/**
|
||||
* Get an existing object
|
||||
* @param id ID
|
||||
* @returns Object
|
||||
*/
|
||||
static get(id: string): InstanceType<typeof this> | undefined {
|
||||
return this.#objects.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of stored objects
|
||||
* @returns Size
|
||||
*/
|
||||
static size() {
|
||||
return this.#objects.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of keys in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static keys() {
|
||||
return this.#objects.keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of values in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static values() {
|
||||
return this.#objects.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* List of values in the map
|
||||
* @returns List
|
||||
*/
|
||||
static toList() {
|
||||
return [...this.#objects.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of key, value pairs in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static entries() {
|
||||
return this.#objects.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a provided function over each key, value pair in the map
|
||||
* @param cb Callback for each pair
|
||||
* @returns Iterable
|
||||
*/
|
||||
static forEach(
|
||||
cb: (
|
||||
value: InstanceType<typeof this>,
|
||||
key: string,
|
||||
map: ReactiveMap<string, InstanceType<typeof this>>
|
||||
) => void
|
||||
) {
|
||||
return this.#objects.forEach(cb);
|
||||
}
|
||||
// * End Object Map Definition
|
||||
|
||||
/**
|
||||
* Fetch channel by ID
|
||||
* @param id ID
|
||||
* @returns Channel
|
||||
*/
|
||||
static async fetch(id: string): Promise<Channel | undefined> {
|
||||
const channel = Channel.get(id);
|
||||
const channel = Channel.#collection.get(id);
|
||||
if (channel) return channel;
|
||||
|
||||
const data = await client.api.get(`/channels/${id as ""}`);
|
||||
@@ -116,12 +69,16 @@ export default (client: Client) =>
|
||||
*/
|
||||
constructor(id: string, data?: ApiChannel) {
|
||||
this.id = id;
|
||||
Channel.#storage.hydrate(id, "channel", data);
|
||||
Channel.#objects.set(id, this);
|
||||
Channel.#collection.create(id, "channel", this, data);
|
||||
}
|
||||
|
||||
updateSomething() {
|
||||
Channel.#storage.set(this.id, "name", "troling");
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
static new(id: string, data?: ApiChannel) {
|
||||
return client.channels.get(id) ?? new Channel(id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,51 +92,51 @@ export default (client: Client) =>
|
||||
* Channel type
|
||||
*/
|
||||
get type() {
|
||||
return Channel.#storage.get(this.id).channelType;
|
||||
return Channel.#get(this.id).channelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel name
|
||||
*/
|
||||
get name() {
|
||||
return Channel.#storage.get(this.id).name;
|
||||
return Channel.#get(this.id).name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel description
|
||||
*/
|
||||
get description() {
|
||||
return Channel.#storage.get(this.id).description;
|
||||
return Channel.#get(this.id).description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel icon
|
||||
*/
|
||||
get icon() {
|
||||
return Channel.#storage.get(this.id).icon;
|
||||
return Channel.#get(this.id).icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the conversation is active
|
||||
*/
|
||||
get active() {
|
||||
return Channel.#storage.get(this.id).active;
|
||||
return Channel.#get(this.id).active;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Ids of recipients of the group
|
||||
*/
|
||||
get recipientIds() {
|
||||
return Channel.#storage.get(this.id).recipientIds;
|
||||
return Channel.#get(this.id).recipientIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recipients of the group
|
||||
*/
|
||||
get recipients() {
|
||||
return Channel.#storage
|
||||
.get(this.id)
|
||||
.recipientIds.map((id) => client.users.get(id)!);
|
||||
return Channel.#get(this.id).recipientIds.map(
|
||||
(id) => client.users.get(id)!
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,77 +152,77 @@ export default (client: Client) =>
|
||||
* User ID
|
||||
*/
|
||||
get userId() {
|
||||
return Channel.#storage.get(this.id).userId!;
|
||||
return Channel.#get(this.id).userId!;
|
||||
}
|
||||
|
||||
/**
|
||||
* User this channel belongs to
|
||||
*/
|
||||
get user() {
|
||||
return client.users.get(Channel.#storage.get(this.id).userId!);
|
||||
return client.users.get(Channel.#get(this.id).userId!);
|
||||
}
|
||||
|
||||
/**
|
||||
* Owner ID
|
||||
*/
|
||||
get ownerId() {
|
||||
return Channel.#storage.get(this.id).ownerId!;
|
||||
return Channel.#get(this.id).ownerId!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Owner of the group
|
||||
*/
|
||||
get owner() {
|
||||
return client.users.get(Channel.#storage.get(this.id).ownerId!);
|
||||
return client.users.get(Channel.#get(this.id).ownerId!);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server ID
|
||||
*/
|
||||
get serverId() {
|
||||
return Channel.#storage.get(this.id).serverId!;
|
||||
return Channel.#get(this.id).serverId!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Server this channel is in
|
||||
*/
|
||||
get server() {
|
||||
return client.servers.get(Channel.#storage.get(this.id).serverId!);
|
||||
return client.servers.get(Channel.#get(this.id).serverId!);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permissions allowed for users in this group
|
||||
*/
|
||||
get permissions() {
|
||||
return Channel.#storage.get(this.id).permissions;
|
||||
return Channel.#get(this.id).permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default permissions for this server channel
|
||||
*/
|
||||
get defaultPermissions() {
|
||||
return Channel.#storage.get(this.id).defaultPermissions;
|
||||
return Channel.#get(this.id).defaultPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Role permissions for this server channel
|
||||
*/
|
||||
get rolePermissions() {
|
||||
return Channel.#storage.get(this.id).rolePermissions;
|
||||
return Channel.#get(this.id).rolePermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this channel is marked as mature
|
||||
*/
|
||||
get mature() {
|
||||
return Channel.#storage.get(this.id).nsfw;
|
||||
return Channel.#get(this.id).nsfw;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID of the last message sent in this channel
|
||||
*/
|
||||
get lastMessageId() {
|
||||
return Channel.#storage.get(this.id).lastMessageId;
|
||||
return Channel.#get(this.id).lastMessageId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,4 +298,163 @@ export default (client: Client) =>
|
||||
...permission.map((x) => Permission[x])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a channel's members.
|
||||
* @requires `Group`
|
||||
* @returns An array of the channel's members.
|
||||
*/
|
||||
async fetchMembers() {
|
||||
const members = await client.api.get(
|
||||
`/channels/${this.id as ""}/members`
|
||||
);
|
||||
|
||||
return members.map((user) => client.User.new(user._id, user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a channel
|
||||
* @param data Edit data
|
||||
*/
|
||||
async edit(data: DataEditChannel) {
|
||||
await client.api.patch(`/channels/${this.id as ""}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a channel
|
||||
* @param leave_silently Whether to not send a message on leave
|
||||
* @param noRequest Whether to not send a request
|
||||
* @requires `DM`, `Group`, `TextChannel`, `VoiceChannel`
|
||||
*/
|
||||
async delete(leave_silently?: boolean, noRequest?: boolean) {
|
||||
if (!noRequest)
|
||||
await client.api.delete(`/channels/${this.id as ""}`, {
|
||||
leave_silently,
|
||||
});
|
||||
|
||||
if (this.type === "DirectMessage") {
|
||||
Channel.#set(this.id, "active", false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.type === "TextChannel" || this.type === "VoiceChannel") {
|
||||
const server = this.server;
|
||||
if (server) {
|
||||
server.channelIds.delete(this.id);
|
||||
}
|
||||
}
|
||||
|
||||
client.channels.delete(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a user to a group
|
||||
* @param user_id ID of the target user
|
||||
*/
|
||||
async addMember(user_id: string) {
|
||||
return await client.api.put(
|
||||
`/channels/${this.id as ""}/recipients/${user_id as ""}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a user from a group
|
||||
* @param user_id ID of the target user
|
||||
*/
|
||||
async removeMember(user_id: string) {
|
||||
return await client.api.delete(
|
||||
`/channels/${this.id as ""}/recipients/${user_id as ""}`
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Send a message
|
||||
* @param data Either the message as a string or message sending route data
|
||||
* @returns The message
|
||||
*/
|
||||
async sendMessage(
|
||||
data: string | DataMessageSend,
|
||||
idempotencyKey: string = ulid()
|
||||
) {
|
||||
const msg: DataMessageSend =
|
||||
typeof data === "string" ? { content: data } : data;
|
||||
|
||||
const message = await client.api.post(
|
||||
`/channels/${this.id as ""}/messages`,
|
||||
msg,
|
||||
{
|
||||
headers: {
|
||||
"Idempotency-Key": idempotencyKey,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return client.Message.new(message._id, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a message by its ID
|
||||
* @param messageId ID of the target message
|
||||
* @returns The message
|
||||
*/
|
||||
async fetchMessage(messageId: string) {
|
||||
const message = await client.api.get(
|
||||
`/channels/${this.id as ""}/messages/${messageId as ""}`
|
||||
);
|
||||
|
||||
return client.Message.new(message._id, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch multiple messages from a channel
|
||||
* @param params Message fetching route data
|
||||
* @returns The messages
|
||||
*/
|
||||
async fetchMessages(
|
||||
params?: Omit<
|
||||
(APIRoutes & {
|
||||
method: "get";
|
||||
path: "/channels/{target}/messages";
|
||||
})["params"],
|
||||
"include_users"
|
||||
>
|
||||
) {
|
||||
const messages = (await client.api.get(
|
||||
`/channels/${this.id as ""}/messages`,
|
||||
{ ...params }
|
||||
)) as Message[];
|
||||
|
||||
return messages.map((message) =>
|
||||
client.Message.new(message._id, message)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch multiple messages from a channel including the users that sent them
|
||||
* @param params Message fetching route data
|
||||
* @returns Object including messages and users
|
||||
*/
|
||||
async fetchMessagesWithUsers(
|
||||
params?: Omit<
|
||||
(APIRoutes & {
|
||||
method: "get";
|
||||
path: "/channels/{target}/messages";
|
||||
})["params"],
|
||||
"include_users"
|
||||
>
|
||||
) {
|
||||
const data = (await client.api.get(
|
||||
`/channels/${this.id as ""}/messages`,
|
||||
{ ...params, include_users: true }
|
||||
)) as { messages: Message[]; users: User[]; members?: Member[] };
|
||||
|
||||
return {
|
||||
messages: data.messages.map((message) =>
|
||||
client.Message.new(message._id, message)
|
||||
),
|
||||
users: data.users.map((user) => client.User.new(user._id, user)),
|
||||
members: data.members?.map((member) =>
|
||||
client.ServerMember.new(member._id, member)
|
||||
),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
+33
-78
@@ -1,93 +1,40 @@
|
||||
import { ReactiveMap } from "@solid-primitives/map";
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
|
||||
import type { Emoji as ApiEmoji } from "revolt-api";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { Client } from "../Client";
|
||||
import { StoreCollection } from "../collections/Collection";
|
||||
import { HydratedEmoji } from "../hydration/emoji";
|
||||
import { ObjectStorage } from "../storage/ObjectStorage";
|
||||
|
||||
export default (client: Client) =>
|
||||
export default (
|
||||
client: Client,
|
||||
collection: StoreCollection<unknown, unknown>
|
||||
) =>
|
||||
/**
|
||||
* Emoji Class
|
||||
*/
|
||||
class Emoji {
|
||||
static #storage = new ObjectStorage<HydratedEmoji>();
|
||||
static #collection: StoreCollection<
|
||||
InstanceType<typeof this>,
|
||||
HydratedEmoji
|
||||
>;
|
||||
static #set: SetStoreFunction<Record<string, HydratedEmoji>>;
|
||||
static #get: (id: string) => HydratedEmoji;
|
||||
|
||||
// * Object Map Definition
|
||||
static #objects = new ReactiveMap<string, InstanceType<typeof this>>();
|
||||
|
||||
/**
|
||||
* Get an existing object
|
||||
* @param id ID
|
||||
* @returns Object
|
||||
*/
|
||||
static get(id: string): InstanceType<typeof this> | undefined {
|
||||
return this.#objects.get(id);
|
||||
static {
|
||||
Emoji.#collection = collection as never;
|
||||
Emoji.#set = collection.updateUnderlyingObject as never;
|
||||
Emoji.#get = collection.getUnderlyingObject as never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of stored objects
|
||||
* @returns Size
|
||||
*/
|
||||
static size() {
|
||||
return this.#objects.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of keys in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static keys() {
|
||||
return this.#objects.keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of values in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static values() {
|
||||
return this.#objects.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* List of values in the map
|
||||
* @returns List
|
||||
*/
|
||||
static toList() {
|
||||
return [...this.#objects.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of key, value pairs in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static entries() {
|
||||
return this.#objects.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a provided function over each key, value pair in the map
|
||||
* @param cb Callback for each pair
|
||||
* @returns Iterable
|
||||
*/
|
||||
static forEach(
|
||||
cb: (
|
||||
value: InstanceType<typeof this>,
|
||||
key: string,
|
||||
map: ReactiveMap<string, InstanceType<typeof this>>
|
||||
) => void
|
||||
) {
|
||||
return this.#objects.forEach(cb);
|
||||
}
|
||||
// * End Object Map Definition
|
||||
|
||||
/**
|
||||
* Fetch emoji by ID
|
||||
* @param id ID
|
||||
* @returns Emoji
|
||||
*/
|
||||
static async fetch(id: string): Promise<Emoji | undefined> {
|
||||
const emoji = Emoji.get(id);
|
||||
const emoji = Emoji.#collection.get(id);
|
||||
if (emoji) return emoji;
|
||||
|
||||
const data = await client.api.get(`/custom/emoji/${id as ""}`);
|
||||
@@ -102,8 +49,16 @@ export default (client: Client) =>
|
||||
*/
|
||||
constructor(id: string, data?: ApiEmoji) {
|
||||
this.id = id;
|
||||
Emoji.#storage.hydrate(id, "emoji", data);
|
||||
Emoji.#objects.set(id, this);
|
||||
Emoji.#collection.create(id, "emoji", this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
static new(id: string, data?: ApiEmoji) {
|
||||
return client.emojis.get(id) ?? new Emoji(id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,34 +72,34 @@ export default (client: Client) =>
|
||||
* Information about the parent of this emoji
|
||||
*/
|
||||
get parent() {
|
||||
return Emoji.#storage.get(this.id).parent;
|
||||
return Emoji.#get(this.id).parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creator of the emoji
|
||||
*/
|
||||
get creator() {
|
||||
return client.users.get(Emoji.#storage.get(this.id).creatorId);
|
||||
return client.users.get(Emoji.#get(this.id).creatorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
get name() {
|
||||
return Emoji.#storage.get(this.id).name;
|
||||
return Emoji.#get(this.id).name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the emoji is animated
|
||||
*/
|
||||
get animated() {
|
||||
return Emoji.#storage.get(this.id).animated;
|
||||
return Emoji.#get(this.id).animated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the emoji is marked as mature
|
||||
*/
|
||||
get mature() {
|
||||
return Emoji.#storage.get(this.id).nsfw;
|
||||
return Emoji.#get(this.id).nsfw;
|
||||
}
|
||||
};
|
||||
|
||||
+43
-88
@@ -1,86 +1,33 @@
|
||||
import { ReactiveMap } from "@solid-primitives/map";
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
|
||||
import type { Message as ApiMessage } from "revolt-api";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { Client } from "../Client";
|
||||
import { StoreCollection } from "../collections/Collection";
|
||||
import { HydratedMessage } from "../hydration/message";
|
||||
import { ObjectStorage } from "../storage/ObjectStorage";
|
||||
|
||||
export default (client: Client) =>
|
||||
export default (
|
||||
client: Client,
|
||||
collection: StoreCollection<unknown, unknown>
|
||||
) =>
|
||||
/**
|
||||
* Message Class
|
||||
*/
|
||||
class Message {
|
||||
static #storage = new ObjectStorage<HydratedMessage>();
|
||||
static #collection: StoreCollection<
|
||||
InstanceType<typeof this>,
|
||||
HydratedMessage
|
||||
>;
|
||||
static #set: SetStoreFunction<Record<string, HydratedMessage>>;
|
||||
static #get: (id: string) => HydratedMessage;
|
||||
|
||||
// * Object Map Definition
|
||||
static #objects = new ReactiveMap<string, InstanceType<typeof this>>();
|
||||
|
||||
/**
|
||||
* Get an existing object
|
||||
* @param id ID
|
||||
* @returns Object
|
||||
*/
|
||||
static get(id: string): InstanceType<typeof this> | undefined {
|
||||
return this.#objects.get(id);
|
||||
static {
|
||||
Message.#collection = collection as never;
|
||||
Message.#set = collection.updateUnderlyingObject as never;
|
||||
Message.#get = collection.getUnderlyingObject as never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of stored objects
|
||||
* @returns Size
|
||||
*/
|
||||
static size() {
|
||||
return this.#objects.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of keys in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static keys() {
|
||||
return this.#objects.keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of values in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static values() {
|
||||
return this.#objects.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* List of values in the map
|
||||
* @returns List
|
||||
*/
|
||||
static toList() {
|
||||
return [...this.#objects.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of key, value pairs in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static entries() {
|
||||
return this.#objects.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a provided function over each key, value pair in the map
|
||||
* @param cb Callback for each pair
|
||||
* @returns Iterable
|
||||
*/
|
||||
static forEach(
|
||||
cb: (
|
||||
value: InstanceType<typeof this>,
|
||||
key: string,
|
||||
map: ReactiveMap<string, InstanceType<typeof this>>
|
||||
) => void
|
||||
) {
|
||||
return this.#objects.forEach(cb);
|
||||
}
|
||||
// * End Object Map Definition
|
||||
|
||||
/**
|
||||
* Fetch message by ID
|
||||
* @param channelId Channel ID
|
||||
@@ -91,7 +38,7 @@ export default (client: Client) =>
|
||||
channelId: string,
|
||||
messageId: string
|
||||
): Promise<Message | undefined> {
|
||||
const message = Message.get(messageId);
|
||||
const message = Message.#collection.get(messageId);
|
||||
if (message) return message;
|
||||
|
||||
const data = await client.api.get(
|
||||
@@ -108,8 +55,16 @@ export default (client: Client) =>
|
||||
*/
|
||||
constructor(id: string, data?: ApiMessage) {
|
||||
this.id = id;
|
||||
Message.#storage.hydrate(id, "message", data);
|
||||
Message.#objects.set(id, this);
|
||||
Message.#collection.create(id, "message", this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
static new(id: string, data?: ApiMessage) {
|
||||
return client.messages.get(id) ?? new Message(id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,104 +78,104 @@ export default (client: Client) =>
|
||||
* Nonce value
|
||||
*/
|
||||
get nonce() {
|
||||
return Message.#storage.get(this.id).nonce;
|
||||
return Message.#get(this.id).nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Id of channel this message was sent in
|
||||
*/
|
||||
get channelId() {
|
||||
return Message.#storage.get(this.id).channelId;
|
||||
return Message.#get(this.id).channelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel this message was sent in
|
||||
*/
|
||||
get channel() {
|
||||
return client.channels.get(Message.#storage.get(this.id).channelId);
|
||||
return client.channels.get(Message.#get(this.id).channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Id of user this message was sent by
|
||||
*/
|
||||
get authorId() {
|
||||
return Message.#storage.get(this.id).authorId;
|
||||
return Message.#get(this.id).authorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* User this message was sent by
|
||||
*/
|
||||
get author() {
|
||||
return client.users.get(Message.#storage.get(this.id).authorId!);
|
||||
return client.users.get(Message.#get(this.id).authorId!);
|
||||
}
|
||||
|
||||
/**
|
||||
* Content
|
||||
*/
|
||||
get content() {
|
||||
return Message.#storage.get(this.id).content;
|
||||
return Message.#get(this.id).content;
|
||||
}
|
||||
|
||||
/**
|
||||
* System message content
|
||||
*/
|
||||
get systemMessage() {
|
||||
return Message.#storage.get(this.id).systemMessage;
|
||||
return Message.#get(this.id).systemMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attachments
|
||||
*/
|
||||
get attachments() {
|
||||
return Message.#storage.get(this.id).attachments;
|
||||
return Message.#get(this.id).attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time at which this message was edited
|
||||
*/
|
||||
get editedAt() {
|
||||
return Message.#storage.get(this.id).editedAt;
|
||||
return Message.#get(this.id).editedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Embeds
|
||||
*/
|
||||
get embeds() {
|
||||
return Message.#storage.get(this.id).embeds;
|
||||
return Message.#get(this.id).embeds;
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs of users this message mentions
|
||||
*/
|
||||
get mentionIds() {
|
||||
return Message.#storage.get(this.id).mentionIds;
|
||||
return Message.#get(this.id).mentionIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs of messages this message replies to
|
||||
*/
|
||||
get replyIds() {
|
||||
return Message.#storage.get(this.id).replyIds;
|
||||
return Message.#get(this.id).replyIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reactions
|
||||
*/
|
||||
get reactions() {
|
||||
return Message.#storage.get(this.id).reactions;
|
||||
return Message.#get(this.id).reactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactions
|
||||
*/
|
||||
get interactions() {
|
||||
return Message.#storage.get(this.id).interactions;
|
||||
return Message.#get(this.id).interactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Masquerade
|
||||
*/
|
||||
get masquerade() {
|
||||
return Message.#storage.get(this.id).masquerade;
|
||||
return Message.#get(this.id).masquerade;
|
||||
}
|
||||
};
|
||||
|
||||
+54
-91
@@ -1,95 +1,42 @@
|
||||
import { ReactiveMap } from "@solid-primitives/map";
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
|
||||
import type { Server as ApiServer, Category } from "revolt-api";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { Channel, Client } from "../Client";
|
||||
import { StoreCollection } from "../collections/Collection";
|
||||
import { HydratedServer } from "../hydration/server";
|
||||
import { bitwiseAndEq, calculatePermission } from "../permissions/calculator";
|
||||
import { Permission } from "../permissions/definitions";
|
||||
import { ObjectStorage } from "../storage/ObjectStorage";
|
||||
|
||||
export default (client: Client) =>
|
||||
export default (
|
||||
client: Client,
|
||||
collection: StoreCollection<unknown, unknown>
|
||||
) =>
|
||||
/**
|
||||
* Server Class
|
||||
*/
|
||||
class Server {
|
||||
static #storage = new ObjectStorage<HydratedServer>();
|
||||
static #collection: StoreCollection<
|
||||
InstanceType<typeof this>,
|
||||
HydratedServer
|
||||
>;
|
||||
static #set: SetStoreFunction<Record<string, HydratedServer>>;
|
||||
static #get: (id: string) => HydratedServer;
|
||||
|
||||
// * Object Map Definition
|
||||
static #objects = new ReactiveMap<string, InstanceType<typeof this>>();
|
||||
|
||||
/**
|
||||
* Get an existing object
|
||||
* @param id ID
|
||||
* @returns Object
|
||||
*/
|
||||
static get(id: string): InstanceType<typeof this> | undefined {
|
||||
return this.#objects.get(id);
|
||||
static {
|
||||
Server.#collection = collection as never;
|
||||
Server.#set = collection.updateUnderlyingObject as never;
|
||||
Server.#get = collection.getUnderlyingObject as never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of stored objects
|
||||
* @returns Size
|
||||
*/
|
||||
static size() {
|
||||
return this.#objects.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of keys in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static keys() {
|
||||
return this.#objects.keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of values in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static values() {
|
||||
return this.#objects.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* List of values in the map
|
||||
* @returns List
|
||||
*/
|
||||
static toList() {
|
||||
return [...this.#objects.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of key, value pairs in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static entries() {
|
||||
return this.#objects.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a provided function over each key, value pair in the map
|
||||
* @param cb Callback for each pair
|
||||
* @returns Iterable
|
||||
*/
|
||||
static forEach(
|
||||
cb: (
|
||||
value: InstanceType<typeof this>,
|
||||
key: string,
|
||||
map: ReactiveMap<string, InstanceType<typeof this>>
|
||||
) => void
|
||||
) {
|
||||
return this.#objects.forEach(cb);
|
||||
}
|
||||
// * End Object Map Definition
|
||||
|
||||
/**
|
||||
* Fetch server by ID
|
||||
* @param id ID
|
||||
* @returns Server
|
||||
*/
|
||||
static async fetch(id: string): Promise<Server | undefined> {
|
||||
const server = Server.get(id);
|
||||
const server = Server.#collection.get(id);
|
||||
if (server) return server;
|
||||
|
||||
const data = await client.api.get(`/servers/${id as ""}`);
|
||||
@@ -99,13 +46,22 @@ export default (client: Client) =>
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* Construct Server
|
||||
* @param id Server Id
|
||||
* Construct
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
constructor(id: string, data?: ApiServer) {
|
||||
this.id = id;
|
||||
Server.#storage.hydrate(id, "server", data);
|
||||
Server.#objects.set(id, this);
|
||||
Server.#collection.create(id, "server", this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
static new(id: string, data?: ApiServer) {
|
||||
return client.servers.get(id) ?? new Server(id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,49 +75,56 @@ export default (client: Client) =>
|
||||
* Owner's user ID
|
||||
*/
|
||||
get ownerId() {
|
||||
return Server.#storage.get(this.id).ownerId;
|
||||
return Server.#get(this.id).ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Owner
|
||||
*/
|
||||
get owner() {
|
||||
return client.users.get(Server.#storage.get(this.id).ownerId);
|
||||
return client.users.get(Server.#get(this.id).ownerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
get name() {
|
||||
return Server.#storage.get(this.id).name;
|
||||
return Server.#get(this.id).name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
get description() {
|
||||
return Server.#storage.get(this.id).description;
|
||||
return Server.#get(this.id).description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon
|
||||
*/
|
||||
get icon() {
|
||||
return Server.#storage.get(this.id).icon;
|
||||
return Server.#get(this.id).icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Banner
|
||||
*/
|
||||
get banner() {
|
||||
return Server.#storage.get(this.id).banner;
|
||||
return Server.#get(this.id).banner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel IDs
|
||||
*/
|
||||
get channelIds() {
|
||||
return Server.#get(this.id).channelIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channels
|
||||
*/
|
||||
get channels() {
|
||||
return [...Server.#storage.get(this.id).channelIds.values()]
|
||||
return [...Server.#get(this.id).channelIds.values()]
|
||||
.map((id) => client.channels.get(id)!)
|
||||
.filter((x) => x);
|
||||
}
|
||||
@@ -170,56 +133,56 @@ export default (client: Client) =>
|
||||
* Categories
|
||||
*/
|
||||
get categories() {
|
||||
return Server.#storage.get(this.id).categories;
|
||||
return Server.#get(this.id).categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* System message channels
|
||||
*/
|
||||
get systemMessages() {
|
||||
return Server.#storage.get(this.id).systemMessages;
|
||||
return Server.#get(this.id).systemMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Roles
|
||||
*/
|
||||
get roles() {
|
||||
return Server.#storage.get(this.id).roles;
|
||||
return Server.#get(this.id).roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default permissions
|
||||
*/
|
||||
get defaultPermissions() {
|
||||
return Server.#storage.get(this.id).defaultPermissions;
|
||||
return Server.#get(this.id).defaultPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Server flags
|
||||
*/
|
||||
get flags() {
|
||||
return Server.#storage.get(this.id).flags;
|
||||
return Server.#get(this.id).flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether analytics are enabled for this server
|
||||
*/
|
||||
get analytics() {
|
||||
return Server.#storage.get(this.id).analytics;
|
||||
return Server.#get(this.id).analytics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this server is publicly discoverable
|
||||
*/
|
||||
get discoverable() {
|
||||
return Server.#storage.get(this.id).discoverable;
|
||||
return Server.#get(this.id).discoverable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this server is marked as mature
|
||||
*/
|
||||
get mature() {
|
||||
return Server.#storage.get(this.id).nsfw;
|
||||
return Server.#get(this.id).nsfw;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,7 +299,7 @@ export default (client: Client) =>
|
||||
* Own member object for this server
|
||||
*/
|
||||
get member() {
|
||||
return client.serverMembers.get({
|
||||
return client.serverMembers.getByKey({
|
||||
server: this.id,
|
||||
user: client.user!.id,
|
||||
});
|
||||
|
||||
+36
-87
@@ -1,11 +1,12 @@
|
||||
import { ReactiveMap } from "@solid-primitives/map";
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
|
||||
import type { Member as ApiMember, MemberCompositeKey } from "revolt-api";
|
||||
|
||||
import { Channel, Client, Server } from "../Client";
|
||||
import { StoreCollection } from "../collections/Collection";
|
||||
import { HydratedServerMember } from "../hydration/serverMember";
|
||||
import { bitwiseAndEq, calculatePermission } from "../permissions/calculator";
|
||||
import { Permission } from "../permissions/definitions";
|
||||
import { ObjectStorage } from "../storage/ObjectStorage";
|
||||
|
||||
/**
|
||||
* Deterministic conversion of member composite key to string ID
|
||||
@@ -16,88 +17,27 @@ function key(key: MemberCompositeKey) {
|
||||
return key.server + key.user;
|
||||
}
|
||||
|
||||
export default (client: Client) =>
|
||||
export default (
|
||||
client: Client,
|
||||
collection: StoreCollection<unknown, unknown>
|
||||
) =>
|
||||
/**
|
||||
* Server Member Class
|
||||
*/
|
||||
class ServerMember {
|
||||
static #storage = new ObjectStorage<HydratedServerMember>();
|
||||
static #collection: StoreCollection<
|
||||
InstanceType<typeof this>,
|
||||
HydratedServerMember
|
||||
>;
|
||||
static #set: SetStoreFunction<Record<string, HydratedServerMember>>;
|
||||
static #get: (id: string) => HydratedServerMember;
|
||||
|
||||
// * Object Map Definition
|
||||
static #objects = new ReactiveMap<string, InstanceType<typeof this>>();
|
||||
|
||||
/**
|
||||
* Deterministic conversion of member composite key to string ID
|
||||
* @param key Key
|
||||
* @returns String key
|
||||
*/
|
||||
static keyToString = key;
|
||||
|
||||
/**
|
||||
* Get an existing Member
|
||||
* @param id Member ID
|
||||
* @returns Member
|
||||
*/
|
||||
static get(id: MemberCompositeKey): ServerMember | undefined {
|
||||
return ServerMember.#objects.get(key(id));
|
||||
static {
|
||||
ServerMember.#collection = collection as never;
|
||||
ServerMember.#set = collection.updateUnderlyingObject as never;
|
||||
ServerMember.#get = collection.getUnderlyingObject as never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of stored objects
|
||||
* @returns Size
|
||||
*/
|
||||
static size() {
|
||||
return this.#objects.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of keys in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static keys() {
|
||||
return this.#objects.keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of values in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static values() {
|
||||
return this.#objects.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* List of values in the map
|
||||
* @returns List
|
||||
*/
|
||||
static toList() {
|
||||
return [...this.#objects.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of key, value pairs in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static entries() {
|
||||
return this.#objects.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a provided function over each key, value pair in the map
|
||||
* @param cb Callback for each pair
|
||||
* @returns Iterable
|
||||
*/
|
||||
static forEach(
|
||||
cb: (
|
||||
value: InstanceType<typeof this>,
|
||||
key: string,
|
||||
map: ReactiveMap<string, InstanceType<typeof this>>
|
||||
) => void
|
||||
) {
|
||||
return this.#objects.forEach(cb);
|
||||
}
|
||||
// * End Object Map Definition
|
||||
|
||||
/**
|
||||
* Fetch member by ID
|
||||
* @param id Member ID
|
||||
@@ -106,7 +46,7 @@ export default (client: Client) =>
|
||||
static async fetch(
|
||||
id: MemberCompositeKey
|
||||
): Promise<ServerMember | undefined> {
|
||||
const channel = ServerMember.get(id);
|
||||
const channel = ServerMember.#collection.get(key(id));
|
||||
if (channel) return channel;
|
||||
|
||||
const data = await client.api.get(
|
||||
@@ -118,13 +58,22 @@ export default (client: Client) =>
|
||||
readonly id: MemberCompositeKey;
|
||||
|
||||
/**
|
||||
* Construct Member
|
||||
* @param id Member ID
|
||||
* Construct
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
constructor(id: MemberCompositeKey, data?: ApiMember) {
|
||||
ServerMember.#storage.hydrate(key(id), "serverMember", data);
|
||||
ServerMember.#objects.set(key(id), this);
|
||||
this.id = id;
|
||||
ServerMember.#collection.create(key(id), "serverMember", this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
static new(id: MemberCompositeKey, data?: ApiMember) {
|
||||
return client.serverMembers.get(key(id)) ?? new ServerMember(id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,35 +94,35 @@ export default (client: Client) =>
|
||||
* When this user joined the server
|
||||
*/
|
||||
get joinedAt() {
|
||||
return ServerMember.#storage.get(key(this.id)).joinedAt;
|
||||
return ServerMember.#get(key(this.id)).joinedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nickname
|
||||
*/
|
||||
get nickname() {
|
||||
return ServerMember.#storage.get(key(this.id)).nickname;
|
||||
return ServerMember.#get(key(this.id)).nickname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avatar
|
||||
*/
|
||||
get avatar() {
|
||||
return ServerMember.#storage.get(key(this.id)).avatar;
|
||||
return ServerMember.#get(key(this.id)).avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of role IDs
|
||||
*/
|
||||
get roles() {
|
||||
return ServerMember.#storage.get(key(this.id)).roles;
|
||||
return ServerMember.#get(key(this.id)).roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time at which timeout expires
|
||||
*/
|
||||
get timeout() {
|
||||
return ServerMember.#storage.get(key(this.id)).timeout;
|
||||
return ServerMember.#get(key(this.id)).timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+40
-84
@@ -1,94 +1,41 @@
|
||||
import { ReactiveMap } from "@solid-primitives/map";
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
|
||||
import type { User as ApiUser } from "revolt-api";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { Client, FileArgs } from "../Client";
|
||||
import { StoreCollection } from "../collections/Collection";
|
||||
import { HydratedUser } from "../hydration/user";
|
||||
import { U32_MAX, UserPermission } from "../permissions/definitions";
|
||||
import { ObjectStorage } from "../storage/ObjectStorage";
|
||||
|
||||
export default (client: Client) =>
|
||||
export default (
|
||||
client: Client,
|
||||
collection: StoreCollection<unknown, unknown>
|
||||
) =>
|
||||
/**
|
||||
* User Class
|
||||
*/
|
||||
class User {
|
||||
static #storage = new ObjectStorage<HydratedUser>();
|
||||
static #collection: StoreCollection<
|
||||
InstanceType<typeof this>,
|
||||
HydratedUser
|
||||
>;
|
||||
static #set: SetStoreFunction<Record<string, HydratedUser>>;
|
||||
static #get: (id: string) => HydratedUser;
|
||||
|
||||
// * Object Map Definition
|
||||
static #objects = new ReactiveMap<string, InstanceType<typeof this>>();
|
||||
|
||||
/**
|
||||
* Get an existing object
|
||||
* @param id ID
|
||||
* @returns Object
|
||||
*/
|
||||
static get(id: string): InstanceType<typeof this> | undefined {
|
||||
return this.#objects.get(id);
|
||||
static {
|
||||
User.#collection = collection as never;
|
||||
User.#set = collection.updateUnderlyingObject as never;
|
||||
User.#get = collection.getUnderlyingObject as never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of stored objects
|
||||
* @returns Size
|
||||
*/
|
||||
static size() {
|
||||
return this.#objects.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of keys in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static keys() {
|
||||
return this.#objects.keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of values in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static values() {
|
||||
return this.#objects.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* List of values in the map
|
||||
* @returns List
|
||||
*/
|
||||
static toList() {
|
||||
return [...this.#objects.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of key, value pairs in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
static entries() {
|
||||
return this.#objects.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a provided function over each key, value pair in the map
|
||||
* @param cb Callback for each pair
|
||||
* @returns Iterable
|
||||
*/
|
||||
static forEach(
|
||||
cb: (
|
||||
value: InstanceType<typeof this>,
|
||||
key: string,
|
||||
map: ReactiveMap<string, InstanceType<typeof this>>
|
||||
) => void
|
||||
) {
|
||||
return this.#objects.forEach(cb);
|
||||
}
|
||||
// * End Object Map Definition
|
||||
|
||||
/**
|
||||
* Fetch user by ID
|
||||
* @param id ID
|
||||
* @returns User
|
||||
*/
|
||||
static async fetch(id: string): Promise<User | undefined> {
|
||||
const user = this.get(id);
|
||||
const user = this.#collection.get(id);
|
||||
if (user) return user;
|
||||
|
||||
const data = await client.api.get(`/users/${id as ""}`);
|
||||
@@ -98,13 +45,22 @@ export default (client: Client) =>
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* Construct User
|
||||
* @param id User Id
|
||||
* Construct
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
constructor(id: string, data?: ApiUser) {
|
||||
this.id = id;
|
||||
User.#storage.hydrate(id, "user", data);
|
||||
User.#objects.set(id, this);
|
||||
User.#collection.create(id, "user", this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
static new(id: string, data?: ApiUser) {
|
||||
return client.users.get(id) ?? new User(id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,63 +74,63 @@ export default (client: Client) =>
|
||||
* Username
|
||||
*/
|
||||
get username() {
|
||||
return User.#storage.get(this.id).username;
|
||||
return User.#get(this.id).username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avatar
|
||||
*/
|
||||
get avatar() {
|
||||
return User.#storage.get(this.id).avatar;
|
||||
return User.#get(this.id).avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Badges
|
||||
*/
|
||||
get badges() {
|
||||
return User.#storage.get(this.id).badges;
|
||||
return User.#get(this.id).badges;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Status
|
||||
*/
|
||||
get status() {
|
||||
return User.#storage.get(this.id).status;
|
||||
return User.#get(this.id).status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Relationship with user
|
||||
*/
|
||||
get relationship() {
|
||||
return User.#storage.get(this.id).relationship;
|
||||
return User.#get(this.id).relationship;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user is online
|
||||
*/
|
||||
get online() {
|
||||
return User.#storage.get(this.id).online;
|
||||
return User.#get(this.id).online;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user is privileged
|
||||
*/
|
||||
get privileged() {
|
||||
return User.#storage.get(this.id).privileged;
|
||||
return User.#get(this.id).privileged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags
|
||||
*/
|
||||
get flags() {
|
||||
return User.#storage.get(this.id).flags;
|
||||
return User.#get(this.id).flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bot information
|
||||
*/
|
||||
get bot() {
|
||||
return User.#storage.get(this.id).bot;
|
||||
return User.#get(this.id).bot;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
|
||||
import { ReactiveMap } from "@solid-primitives/map";
|
||||
|
||||
import { Hydrators, hydrate } from "../hydration";
|
||||
import { ObjectStorage } from "../storage/ObjectStorage";
|
||||
|
||||
/**
|
||||
* Abstract Collection type
|
||||
*/
|
||||
export abstract class Collection<T> {
|
||||
/**
|
||||
* Get an existing object
|
||||
* @param id Id
|
||||
* @returns Object
|
||||
*/
|
||||
abstract get(id: string): T | undefined;
|
||||
|
||||
/**
|
||||
* Check whether an id exists in the Collection
|
||||
* @param id Id
|
||||
* @returns Whether it exists
|
||||
*/
|
||||
abstract has(id: string): boolean;
|
||||
|
||||
/**
|
||||
* Delete an object
|
||||
* @param id Id
|
||||
*/
|
||||
abstract delete(id: string): void;
|
||||
|
||||
/**
|
||||
* Number of stored objects
|
||||
* @returns Size
|
||||
*/
|
||||
abstract size(): number;
|
||||
|
||||
/**
|
||||
* Iterable of keys in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
abstract keys(): IterableIterator<string>;
|
||||
|
||||
/**
|
||||
* Iterable of values in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
abstract values(): IterableIterator<T>;
|
||||
|
||||
/**
|
||||
* Iterable of key, value pairs in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
abstract entries(): IterableIterator<[string, T]>;
|
||||
|
||||
/**
|
||||
* List of values in the map
|
||||
* @returns List
|
||||
*/
|
||||
toList() {
|
||||
return [...this.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a provided function over each key, value pair in the map
|
||||
* @param cb Callback for each pair
|
||||
*/
|
||||
abstract forEach(
|
||||
cb: (value: T, key: string, map: ReactiveMap<string, T>) => void
|
||||
): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection backed by a Solid.js Store
|
||||
*/
|
||||
export abstract class StoreCollection<T, V> extends Collection<T> {
|
||||
#storage = new ObjectStorage<V>();
|
||||
#objects = new ReactiveMap<string, T>();
|
||||
readonly getUnderlyingObject: (id: string) => V;
|
||||
readonly updateUnderlyingObject: SetStoreFunction<Record<string, V>>;
|
||||
|
||||
/**
|
||||
* Construct store backed collection
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.getUnderlyingObject = this.#storage.get;
|
||||
this.updateUnderlyingObject = this.#storage.set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an existing object
|
||||
* @param id Id
|
||||
* @returns Object
|
||||
*/
|
||||
get(id: string): T | undefined {
|
||||
return this.#objects.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether an id exists in the Collection
|
||||
* @param id Id
|
||||
* @returns Whether it exists
|
||||
*/
|
||||
has(id: string) {
|
||||
return this.#objects.has(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an object
|
||||
* @param id Id
|
||||
*/
|
||||
delete(id: string): void {
|
||||
this.#objects.delete(id);
|
||||
this.updateUnderlyingObject(id, undefined as never);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of an object
|
||||
* @param id Id
|
||||
* @param type Type
|
||||
* @param instance Instance
|
||||
* @param data Data
|
||||
*/
|
||||
create(id: string, type: keyof Hydrators, instance: T, data?: unknown) {
|
||||
this.#storage.hydrate(id, type, data);
|
||||
this.#objects.set(id, instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of stored objects
|
||||
* @returns Size
|
||||
*/
|
||||
size() {
|
||||
return this.#objects.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of keys in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
keys() {
|
||||
return this.#objects.keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of values in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
values() {
|
||||
return this.#objects.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable of key, value pairs in the map
|
||||
* @returns Iterable
|
||||
*/
|
||||
entries() {
|
||||
return this.#objects.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a provided function over each key, value pair in the map
|
||||
* @param cb Callback for each pair
|
||||
* @returns Iterable
|
||||
*/
|
||||
forEach(cb: (value: T, key: string, map: ReactiveMap<string, T>) => void) {
|
||||
return this.#objects.forEach(cb);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { MemberCompositeKey } from "revolt-api";
|
||||
|
||||
import { Channel, Emoji, Message, Server, ServerMember, User } from "..";
|
||||
import { HydratedChannel } from "../hydration/channel";
|
||||
import { HydratedEmoji } from "../hydration/emoji";
|
||||
import { HydratedMessage } from "../hydration/message";
|
||||
import { HydratedServer } from "../hydration/server";
|
||||
import { HydratedServerMember } from "../hydration/serverMember";
|
||||
import { HydratedUser } from "../hydration/user";
|
||||
|
||||
import { StoreCollection } from "./Collection";
|
||||
|
||||
export class ChannelCollection extends StoreCollection<
|
||||
Channel,
|
||||
HydratedChannel
|
||||
> {}
|
||||
export class EmojiCollection extends StoreCollection<Emoji, HydratedEmoji> {}
|
||||
export class MessageCollection extends StoreCollection<
|
||||
Message,
|
||||
HydratedMessage
|
||||
> {}
|
||||
export class ServerCollection extends StoreCollection<Server, HydratedServer> {}
|
||||
export class UserCollection extends StoreCollection<User, HydratedUser> {}
|
||||
export class ServerMemberCollection extends StoreCollection<
|
||||
ServerMember,
|
||||
HydratedServerMember
|
||||
> {
|
||||
getByKey(id: MemberCompositeKey) {
|
||||
return super.get(id.server + id.user);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ export type {
|
||||
ServerMember,
|
||||
User,
|
||||
Session,
|
||||
Message,
|
||||
} from "./Client";
|
||||
|
||||
export * from "./lib/regex";
|
||||
|
||||
@@ -40,14 +40,14 @@ export function calculatePermission(
|
||||
return Permission.GrantAllSafe;
|
||||
}
|
||||
|
||||
if (target instanceof client.servers) {
|
||||
if (target instanceof client.Server) {
|
||||
// 1. Check if owner.
|
||||
if (target.owner === user?.id) {
|
||||
return Permission.GrantAllSafe;
|
||||
} else {
|
||||
// 2. Get ServerMember.
|
||||
const member = options?.member ??
|
||||
client.serverMembers.get({
|
||||
client.serverMembers.getByKey({
|
||||
user: user!.id,
|
||||
server: target.id,
|
||||
}) ?? { roles: null, timeout: null };
|
||||
@@ -113,7 +113,7 @@ export function calculatePermission(
|
||||
} else {
|
||||
// 4. Get ServerMember.
|
||||
const member = options?.member ??
|
||||
client.serverMembers.get({
|
||||
client.serverMembers.getByKey({
|
||||
user: user!.id,
|
||||
server: server.id,
|
||||
}) ?? { roles: null, timeout: null };
|
||||
|
||||
@@ -16,6 +16,7 @@ export class ObjectStorage<T> {
|
||||
const [store, setStore] = createStore({});
|
||||
this.store = store as never;
|
||||
this.set = setStore;
|
||||
this.get = this.get.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user