refactor progress
This commit is contained in:
@@ -17,13 +17,7 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export type AuthSession = Readonly<{
|
||||
id: string;
|
||||
approx_last_used_at: string | null;
|
||||
client_os: string;
|
||||
client_platform: string;
|
||||
client_location: string | null;
|
||||
}>;
|
||||
import type {AuthSessionLocation, AuthSessionResponse} from '@fluxer/schema/src/domains/auth/AuthSchemas';
|
||||
|
||||
export class AuthSessionRecord {
|
||||
readonly id: string;
|
||||
@@ -31,22 +25,25 @@ export class AuthSessionRecord {
|
||||
readonly clientOs: string;
|
||||
readonly clientPlatform: string;
|
||||
readonly clientLocation: string | null;
|
||||
readonly isCurrent: boolean;
|
||||
private readonly clientInfo: AuthSessionResponse['client_info'] | null;
|
||||
|
||||
constructor(data: AuthSession) {
|
||||
this.id = data.id;
|
||||
constructor(data: AuthSessionResponse) {
|
||||
this.id = data.id_hash;
|
||||
this.approxLastUsedAt = data.approx_last_used_at ? new Date(data.approx_last_used_at) : null;
|
||||
this.clientOs = data.client_os;
|
||||
this.clientPlatform = data.client_platform;
|
||||
this.clientLocation = data.client_location;
|
||||
this.clientInfo = data.client_info ?? null;
|
||||
this.clientOs = this.clientInfo?.os ?? 'Unknown';
|
||||
this.clientPlatform = this.clientInfo?.platform ?? 'Unknown';
|
||||
this.clientLocation = getLocationLabel(this.clientInfo?.location ?? null);
|
||||
this.isCurrent = data.current;
|
||||
}
|
||||
|
||||
toJSON(): AuthSession {
|
||||
toJSON(): AuthSessionResponse {
|
||||
return {
|
||||
id: this.id,
|
||||
id_hash: this.id,
|
||||
approx_last_used_at: this.approxLastUsedAt?.toISOString() ?? null,
|
||||
client_os: this.clientOs,
|
||||
client_platform: this.clientPlatform,
|
||||
client_location: this.clientLocation,
|
||||
client_info: this.clientInfo,
|
||||
current: this.isCurrent,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,3 +51,12 @@ export class AuthSessionRecord {
|
||||
return JSON.stringify(this) === JSON.stringify(other);
|
||||
}
|
||||
}
|
||||
|
||||
function getLocationLabel(location: AuthSessionLocation | null): string | null {
|
||||
if (!location) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parts = [location.city, location.region, location.country].filter(Boolean);
|
||||
return parts.length ? parts.join(', ') : null;
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {type UserPartial, UserRecord} from '~/records/UserRecord';
|
||||
import UserStore from '~/stores/UserStore';
|
||||
|
||||
export type BetaCode = Readonly<{
|
||||
code: string;
|
||||
created_at: string;
|
||||
redeemed_at: string | null;
|
||||
redeemer: UserPartial | null;
|
||||
}>;
|
||||
|
||||
export class BetaCodeRecord {
|
||||
readonly code: string;
|
||||
readonly createdAt: Date;
|
||||
readonly redeemedAt: Date | null;
|
||||
readonly redeemer: UserRecord | null;
|
||||
|
||||
constructor(data: BetaCode) {
|
||||
this.code = data.code;
|
||||
this.createdAt = new Date(data.created_at);
|
||||
this.redeemedAt = data.redeemed_at ? new Date(data.redeemed_at) : null;
|
||||
this.redeemer = data.redeemer ? new UserRecord(data.redeemer) : null;
|
||||
|
||||
if (this.redeemer != null) {
|
||||
UserStore.cacheUsers([this.redeemer.toJSON()]);
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(): BetaCode {
|
||||
return {
|
||||
code: this.code,
|
||||
created_at: this.createdAt.toISOString(),
|
||||
redeemed_at: this.redeemedAt?.toISOString() ?? null,
|
||||
redeemer: this.redeemer ? this.redeemer.toJSON() : null,
|
||||
};
|
||||
}
|
||||
|
||||
equals(other: BetaCodeRecord): boolean {
|
||||
return JSON.stringify(this) === JSON.stringify(other);
|
||||
}
|
||||
}
|
||||
@@ -17,18 +17,13 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {ChannelTypes} from '~/Constants';
|
||||
import type {UserPartial} from '~/records/UserRecord';
|
||||
import UserPinnedDMStore from '~/stores/UserPinnedDMStore';
|
||||
import UserStore from '~/stores/UserStore';
|
||||
import * as SnowflakeUtils from '~/utils/SnowflakeUtils';
|
||||
|
||||
type ChannelOverwrite = Readonly<{
|
||||
id: string;
|
||||
type: number;
|
||||
allow: string;
|
||||
deny: string;
|
||||
}>;
|
||||
import RuntimeConfigStore from '@app/stores/RuntimeConfigStore';
|
||||
import UserPinnedDMStore from '@app/stores/UserPinnedDMStore';
|
||||
import UserStore from '@app/stores/UserStore';
|
||||
import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants';
|
||||
import type {Channel, ChannelOverwrite, DefaultReactionEmoji} from '@fluxer/schema/src/domains/channel/ChannelSchemas';
|
||||
import type {UserPartial} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
||||
import * as SnowflakeUtils from '@fluxer/snowflake/src/SnowflakeUtils';
|
||||
|
||||
export class ChannelOverwriteRecord {
|
||||
readonly id: string;
|
||||
@@ -66,40 +61,12 @@ export class ChannelOverwriteRecord {
|
||||
}
|
||||
}
|
||||
|
||||
export type DefaultReactionEmoji = Readonly<{
|
||||
emoji_id: string | null;
|
||||
emoji_name: string | null;
|
||||
}>;
|
||||
|
||||
export type Channel = Readonly<{
|
||||
id: string;
|
||||
guild_id?: string;
|
||||
name?: string;
|
||||
topic?: string | null;
|
||||
url?: string | null;
|
||||
icon?: string | null;
|
||||
owner_id?: string | null;
|
||||
type: number;
|
||||
position?: number;
|
||||
parent_id?: string | null;
|
||||
bitrate?: number | null;
|
||||
user_limit?: number | null;
|
||||
rtc_region?: string | null;
|
||||
last_message_id?: string | null;
|
||||
last_pin_timestamp?: string | null;
|
||||
permission_overwrites?: ReadonlyArray<ChannelOverwrite>;
|
||||
recipients?: ReadonlyArray<UserPartial>;
|
||||
nsfw?: boolean;
|
||||
rate_limit_per_user?: number;
|
||||
nicks?: Readonly<Record<string, string>>;
|
||||
flags?: number;
|
||||
member_count?: number;
|
||||
message_count?: number;
|
||||
total_message_sent?: number;
|
||||
default_reaction_emoji?: DefaultReactionEmoji | null;
|
||||
}>;
|
||||
interface ChannelRecordOptions {
|
||||
instanceId?: string;
|
||||
}
|
||||
|
||||
export class ChannelRecord {
|
||||
readonly instanceId: string;
|
||||
readonly id: string;
|
||||
readonly guildId?: string;
|
||||
readonly name?: string;
|
||||
@@ -126,7 +93,8 @@ export class ChannelRecord {
|
||||
readonly totalMessageSent?: number;
|
||||
readonly defaultReactionEmoji?: DefaultReactionEmoji | null;
|
||||
|
||||
constructor(channel: Channel) {
|
||||
constructor(channel: Channel, options?: ChannelRecordOptions) {
|
||||
this.instanceId = options?.instanceId ?? RuntimeConfigStore.localInstanceDomain;
|
||||
this.id = channel.id;
|
||||
this.guildId = channel.guild_id;
|
||||
this.name = channel.name;
|
||||
@@ -257,35 +225,38 @@ export class ChannelRecord {
|
||||
newRecipients = this.recipientIds.map((id) => UserStore.getUser(id)!.toJSON());
|
||||
}
|
||||
|
||||
return new ChannelRecord({
|
||||
id: this.id,
|
||||
guild_id: updates.guild_id ?? this.guildId,
|
||||
name: updates.name ?? this.name,
|
||||
topic: updates.topic !== undefined ? updates.topic : this.topic,
|
||||
url: updates.url !== undefined ? updates.url : this.url,
|
||||
icon: updates.icon !== undefined ? updates.icon : this.icon,
|
||||
owner_id: updates.owner_id !== undefined ? updates.owner_id : this.ownerId,
|
||||
type: updates.type ?? this.type,
|
||||
position: updates.position ?? this.position,
|
||||
parent_id: updates.parent_id !== undefined ? updates.parent_id : this.parentId,
|
||||
bitrate: updates.bitrate !== undefined ? updates.bitrate : this.bitrate,
|
||||
user_limit: updates.user_limit !== undefined ? updates.user_limit : this.userLimit,
|
||||
rtc_region: updates.rtc_region !== undefined ? updates.rtc_region : this.rtcRegion,
|
||||
last_message_id: updates.last_message_id !== undefined ? updates.last_message_id : this.lastMessageId,
|
||||
last_pin_timestamp: updates.last_pin_timestamp ?? this.lastPinTimestamp?.toISOString() ?? undefined,
|
||||
permission_overwrites: !this.isPrivate()
|
||||
? (updates.permission_overwrites ?? Object.values(this.permissionOverwrites).map((o) => o.toJSON()))
|
||||
: undefined,
|
||||
recipients: newRecipients.length > 0 ? newRecipients : undefined,
|
||||
nsfw: updates.nsfw ?? this.nsfw,
|
||||
rate_limit_per_user: updates.rate_limit_per_user ?? this.rateLimitPerUser,
|
||||
nicks: updates.nicks ?? this.nicks,
|
||||
flags: updates.flags ?? this.flags,
|
||||
member_count: updates.member_count ?? this.memberCount,
|
||||
message_count: updates.message_count ?? this.messageCount,
|
||||
total_message_sent: updates.total_message_sent ?? this.totalMessageSent,
|
||||
default_reaction_emoji: updates.default_reaction_emoji ?? this.defaultReactionEmoji,
|
||||
});
|
||||
return new ChannelRecord(
|
||||
{
|
||||
id: this.id,
|
||||
guild_id: updates.guild_id ?? this.guildId,
|
||||
name: updates.name ?? this.name,
|
||||
topic: updates.topic !== undefined ? updates.topic : this.topic,
|
||||
url: updates.url !== undefined ? updates.url : this.url,
|
||||
icon: updates.icon !== undefined ? updates.icon : this.icon,
|
||||
owner_id: updates.owner_id !== undefined ? updates.owner_id : this.ownerId,
|
||||
type: updates.type ?? this.type,
|
||||
position: updates.position ?? this.position,
|
||||
parent_id: updates.parent_id !== undefined ? updates.parent_id : this.parentId,
|
||||
bitrate: updates.bitrate !== undefined ? updates.bitrate : this.bitrate,
|
||||
user_limit: updates.user_limit !== undefined ? updates.user_limit : this.userLimit,
|
||||
rtc_region: updates.rtc_region !== undefined ? updates.rtc_region : this.rtcRegion,
|
||||
last_message_id: updates.last_message_id !== undefined ? updates.last_message_id : this.lastMessageId,
|
||||
last_pin_timestamp: updates.last_pin_timestamp ?? this.lastPinTimestamp?.toISOString() ?? undefined,
|
||||
permission_overwrites: !this.isPrivate()
|
||||
? (updates.permission_overwrites ?? Object.values(this.permissionOverwrites).map((o) => o.toJSON()))
|
||||
: undefined,
|
||||
recipients: newRecipients.length > 0 ? newRecipients : undefined,
|
||||
nsfw: updates.nsfw ?? this.nsfw,
|
||||
rate_limit_per_user: updates.rate_limit_per_user ?? this.rateLimitPerUser,
|
||||
nicks: updates.nicks ?? this.nicks,
|
||||
flags: updates.flags ?? this.flags,
|
||||
member_count: updates.member_count ?? this.memberCount,
|
||||
message_count: updates.message_count ?? this.messageCount,
|
||||
total_message_sent: updates.total_message_sent ?? this.totalMessageSent,
|
||||
default_reaction_emoji: updates.default_reaction_emoji ?? this.defaultReactionEmoji,
|
||||
},
|
||||
{instanceId: this.instanceId},
|
||||
);
|
||||
}
|
||||
|
||||
withOverwrite(overwrite: ChannelOverwriteRecord): ChannelRecord {
|
||||
@@ -293,18 +264,22 @@ export class ChannelRecord {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new ChannelRecord({
|
||||
...this.toJSON(),
|
||||
permission_overwrites: Object.values({
|
||||
...this.permissionOverwrites,
|
||||
[overwrite.id]: overwrite,
|
||||
}).map((o) => o.toJSON()),
|
||||
});
|
||||
return new ChannelRecord(
|
||||
{
|
||||
...this.toJSON(),
|
||||
permission_overwrites: Object.values({
|
||||
...this.permissionOverwrites,
|
||||
[overwrite.id]: overwrite,
|
||||
}).map((o) => o.toJSON()),
|
||||
},
|
||||
{instanceId: this.instanceId},
|
||||
);
|
||||
}
|
||||
|
||||
equals(other: ChannelRecord): boolean {
|
||||
if (this === other) return true;
|
||||
|
||||
if (this.instanceId !== other.instanceId) return false;
|
||||
if (this.id !== other.id) return false;
|
||||
if (this.guildId !== other.guildId) return false;
|
||||
if (this.name !== other.name) return false;
|
||||
|
||||
61
fluxer_app/src/records/ConnectionRecord.tsx
Normal file
61
fluxer_app/src/records/ConnectionRecord.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {ConnectionType} from '@fluxer/constants/src/ConnectionConstants';
|
||||
import type {ConnectionResponse} from '@fluxer/schema/src/domains/connection/ConnectionSchemas';
|
||||
|
||||
export class ConnectionRecord {
|
||||
readonly id: string;
|
||||
readonly type: ConnectionType;
|
||||
readonly name: string;
|
||||
readonly verified: boolean;
|
||||
readonly visibilityFlags: number;
|
||||
readonly sortOrder: number;
|
||||
|
||||
constructor(connection: ConnectionResponse) {
|
||||
this.id = connection.id;
|
||||
this.type = connection.type;
|
||||
this.name = connection.name;
|
||||
this.verified = connection.verified;
|
||||
this.visibilityFlags = connection.visibility_flags;
|
||||
this.sortOrder = connection.sort_order;
|
||||
}
|
||||
|
||||
equals(other: ConnectionRecord): boolean {
|
||||
return (
|
||||
this.id === other.id &&
|
||||
this.type === other.type &&
|
||||
this.name === other.name &&
|
||||
this.verified === other.verified &&
|
||||
this.visibilityFlags === other.visibilityFlags &&
|
||||
this.sortOrder === other.sortOrder
|
||||
);
|
||||
}
|
||||
|
||||
toJSON(): ConnectionResponse {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
name: this.name,
|
||||
verified: this.verified,
|
||||
visibility_flags: this.visibilityFlags,
|
||||
sort_order: this.sortOrder,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ export interface DeveloperApplicationBot {
|
||||
bio?: string | null;
|
||||
token?: string;
|
||||
banner?: string | null;
|
||||
flags?: number;
|
||||
}
|
||||
|
||||
export interface DeveloperApplication {
|
||||
@@ -64,6 +65,7 @@ export class DeveloperApplicationRecord implements DeveloperApplication {
|
||||
bio: application.bot.bio ?? null,
|
||||
token: application.bot.token,
|
||||
banner: application.bot.banner ?? null,
|
||||
flags: application.bot.flags,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -89,7 +91,12 @@ export class DeveloperApplicationRecord implements DeveloperApplication {
|
||||
bot_public: this.bot_public,
|
||||
bot_require_code_grant: this.bot_require_code_grant,
|
||||
client_secret: this.client_secret,
|
||||
bot: this.bot ? {...this.bot} : undefined,
|
||||
bot: this.bot
|
||||
? {
|
||||
...this.bot,
|
||||
flags: this.bot.flags,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import * as SnowflakeUtils from '~/utils/SnowflakeUtils';
|
||||
import * as SnowflakeUtils from '@fluxer/snowflake/src/SnowflakeUtils';
|
||||
|
||||
export type FavoriteMeme = Readonly<{
|
||||
id: string;
|
||||
@@ -35,7 +35,8 @@ export type FavoriteMeme = Readonly<{
|
||||
duration: number | null;
|
||||
is_gifv: boolean;
|
||||
url: string;
|
||||
tenor_id: string | null;
|
||||
klipy_slug: string | null;
|
||||
tenor_slug_id: string | null;
|
||||
}>;
|
||||
|
||||
export class FavoriteMemeRecord {
|
||||
@@ -54,7 +55,8 @@ export class FavoriteMemeRecord {
|
||||
readonly duration: number | null;
|
||||
readonly isGifv: boolean;
|
||||
readonly url: string;
|
||||
readonly tenorId: string | null;
|
||||
readonly klipySlug: string | null;
|
||||
readonly tenorSlugId: string | null;
|
||||
|
||||
constructor(meme: FavoriteMeme) {
|
||||
this.id = meme.id;
|
||||
@@ -72,7 +74,8 @@ export class FavoriteMemeRecord {
|
||||
this.duration = meme.duration;
|
||||
this.isGifv = meme.is_gifv;
|
||||
this.url = meme.url;
|
||||
this.tenorId = meme.tenor_id;
|
||||
this.klipySlug = meme.klipy_slug;
|
||||
this.tenorSlugId = meme.tenor_slug_id;
|
||||
}
|
||||
|
||||
get createdAtTimestamp(): number {
|
||||
@@ -120,7 +123,8 @@ export class FavoriteMemeRecord {
|
||||
this.duration === other.duration &&
|
||||
this.isGifv === other.isGifv &&
|
||||
this.url === other.url &&
|
||||
this.tenorId === other.tenorId
|
||||
this.klipySlug === other.klipySlug &&
|
||||
this.tenorSlugId === other.tenorSlugId
|
||||
);
|
||||
}
|
||||
|
||||
@@ -141,7 +145,8 @@ export class FavoriteMemeRecord {
|
||||
duration: this.duration,
|
||||
is_gifv: this.isGifv,
|
||||
url: this.url,
|
||||
tenor_id: this.tenorId,
|
||||
klipy_slug: this.klipySlug,
|
||||
tenor_slug_id: this.tenorSlugId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,19 +17,9 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {UserPartial} from '~/records/UserRecord';
|
||||
import * as AvatarUtils from '~/utils/AvatarUtils';
|
||||
|
||||
export type GuildEmoji = Readonly<{
|
||||
id: string;
|
||||
name: string;
|
||||
animated: boolean;
|
||||
user?: UserPartial;
|
||||
}>;
|
||||
|
||||
export interface GuildEmojiWithUser extends GuildEmoji {
|
||||
user?: UserPartial;
|
||||
}
|
||||
import * as AvatarUtils from '@app/utils/AvatarUtils';
|
||||
import type {GuildEmoji} from '@fluxer/schema/src/domains/guild/GuildEmojiSchemas';
|
||||
import type {UserPartial} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
||||
|
||||
export class GuildEmojiRecord {
|
||||
readonly id: string;
|
||||
|
||||
@@ -17,57 +17,44 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {GuildMemberProfileFlags} from '~/Constants';
|
||||
import type {GuildRoleRecord} from '~/records/GuildRoleRecord';
|
||||
import type {UserPartial} from '~/records/UserRecord';
|
||||
import {UserRecord} from '~/records/UserRecord';
|
||||
import AuthenticationStore from '~/stores/AuthenticationStore';
|
||||
import GuildStore from '~/stores/GuildStore';
|
||||
import UserStore from '~/stores/UserStore';
|
||||
import * as ColorUtils from '~/utils/ColorUtils';
|
||||
import type {GuildRoleRecord} from '@app/records/GuildRoleRecord';
|
||||
import {UserRecord} from '@app/records/UserRecord';
|
||||
import AuthenticationStore from '@app/stores/AuthenticationStore';
|
||||
import GuildStore from '@app/stores/GuildStore';
|
||||
import RuntimeConfigStore from '@app/stores/RuntimeConfigStore';
|
||||
import UserStore from '@app/stores/UserStore';
|
||||
import * as ColorUtils from '@app/utils/ColorUtils';
|
||||
import {GuildMemberProfileFlags} from '@fluxer/constants/src/GuildConstants';
|
||||
import type {GuildMemberData} from '@fluxer/schema/src/domains/guild/GuildMemberSchemas';
|
||||
|
||||
export type GuildMember = Readonly<{
|
||||
user: UserPartial;
|
||||
nick?: string | null;
|
||||
avatar?: string | null;
|
||||
banner?: string | null;
|
||||
accent_color?: string | null;
|
||||
roles: ReadonlyArray<string>;
|
||||
joined_at: string;
|
||||
join_source_type?: number | null;
|
||||
source_invite_code?: string | null;
|
||||
inviter_id?: string | null;
|
||||
mute?: boolean;
|
||||
deaf?: boolean;
|
||||
communication_disabled_until?: string | null;
|
||||
profile_flags?: number | null;
|
||||
}>;
|
||||
interface GuildMemberRecordOptions {
|
||||
instanceId?: string;
|
||||
}
|
||||
|
||||
export class GuildMemberRecord {
|
||||
readonly instanceId: string;
|
||||
readonly guildId: string;
|
||||
readonly user: UserRecord;
|
||||
readonly nick: string | null;
|
||||
readonly avatar: string | null;
|
||||
readonly banner: string | null;
|
||||
readonly accentColor: string | null;
|
||||
readonly accentColor: number | null;
|
||||
readonly roles: ReadonlySet<string>;
|
||||
readonly joinedAt: Date;
|
||||
readonly joinSourceType: number | null;
|
||||
readonly sourceInviteCode: string | null;
|
||||
readonly inviterId: string | null;
|
||||
readonly mute: boolean;
|
||||
readonly deaf: boolean;
|
||||
readonly communicationDisabledUntil: Date | null;
|
||||
readonly profileFlags: number;
|
||||
|
||||
constructor(guildId: string, guildMember: GuildMember) {
|
||||
constructor(guildId: string, guildMember: GuildMemberData, options?: GuildMemberRecordOptions) {
|
||||
this.instanceId = options?.instanceId ?? RuntimeConfigStore.localInstanceDomain;
|
||||
this.guildId = guildId;
|
||||
|
||||
const cachedUser = UserStore.getUser(guildMember.user.id);
|
||||
if (cachedUser) {
|
||||
this.user = cachedUser;
|
||||
} else {
|
||||
this.user = new UserRecord(guildMember.user);
|
||||
this.user = new UserRecord(guildMember.user, {instanceId: this.instanceId});
|
||||
UserStore.cacheUsers([this.user.toJSON()]);
|
||||
}
|
||||
|
||||
@@ -77,9 +64,6 @@ export class GuildMemberRecord {
|
||||
this.accentColor = guildMember.accent_color ?? null;
|
||||
this.roles = new Set(guildMember.roles);
|
||||
this.joinedAt = new Date(guildMember.joined_at);
|
||||
this.joinSourceType = guildMember.join_source_type ?? null;
|
||||
this.sourceInviteCode = guildMember.source_invite_code ?? null;
|
||||
this.inviterId = guildMember.inviter_id ?? null;
|
||||
this.mute = guildMember.mute ?? false;
|
||||
this.deaf = guildMember.deaf ?? false;
|
||||
this.communicationDisabledUntil = guildMember.communication_disabled_until
|
||||
@@ -96,31 +80,36 @@ export class GuildMemberRecord {
|
||||
return (this.profileFlags & GuildMemberProfileFlags.BANNER_UNSET) !== 0;
|
||||
}
|
||||
|
||||
withUpdates(updates: Partial<GuildMember>): GuildMemberRecord {
|
||||
return new GuildMemberRecord(this.guildId, {
|
||||
user: updates.user ?? this.user.toJSON(),
|
||||
nick: updates.nick ?? this.nick,
|
||||
avatar: updates.avatar ?? this.avatar,
|
||||
banner: updates.banner ?? this.banner,
|
||||
accent_color: updates.accent_color ?? this.accentColor,
|
||||
roles: updates.roles ?? Array.from(this.roles),
|
||||
joined_at: updates.joined_at ?? this.joinedAt.toISOString(),
|
||||
join_source_type: updates.join_source_type ?? this.joinSourceType,
|
||||
source_invite_code: updates.source_invite_code ?? this.sourceInviteCode,
|
||||
inviter_id: updates.inviter_id ?? this.inviterId,
|
||||
mute: updates.mute ?? this.mute,
|
||||
deaf: updates.deaf ?? this.deaf,
|
||||
communication_disabled_until:
|
||||
updates.communication_disabled_until ?? this.communicationDisabledUntil?.toISOString() ?? null,
|
||||
profile_flags: updates.profile_flags ?? this.profileFlags,
|
||||
});
|
||||
withUpdates(updates: Partial<GuildMemberData>): GuildMemberRecord {
|
||||
return new GuildMemberRecord(
|
||||
this.guildId,
|
||||
{
|
||||
user: updates.user ?? this.user.toJSON(),
|
||||
nick: updates.nick ?? this.nick,
|
||||
avatar: updates.avatar ?? this.avatar,
|
||||
banner: updates.banner ?? this.banner,
|
||||
accent_color: updates.accent_color ?? this.accentColor,
|
||||
roles: updates.roles ?? Array.from(this.roles),
|
||||
joined_at: updates.joined_at ?? this.joinedAt.toISOString(),
|
||||
mute: updates.mute ?? this.mute,
|
||||
deaf: updates.deaf ?? this.deaf,
|
||||
communication_disabled_until:
|
||||
updates.communication_disabled_until ?? this.communicationDisabledUntil?.toISOString() ?? null,
|
||||
profile_flags: updates.profile_flags ?? this.profileFlags,
|
||||
},
|
||||
{instanceId: this.instanceId},
|
||||
);
|
||||
}
|
||||
|
||||
withRoles(roles: Iterable<string>): GuildMemberRecord {
|
||||
return new GuildMemberRecord(this.guildId, {
|
||||
...this.toJSON(),
|
||||
roles: Array.from(roles),
|
||||
});
|
||||
return new GuildMemberRecord(
|
||||
this.guildId,
|
||||
{
|
||||
...this.toJSON(),
|
||||
roles: Array.from(roles),
|
||||
},
|
||||
{instanceId: this.instanceId},
|
||||
);
|
||||
}
|
||||
|
||||
getSortedRoles(): ReadonlyArray<GuildRoleRecord> {
|
||||
@@ -170,7 +159,7 @@ export class GuildMemberRecord {
|
||||
return this.communicationDisabledUntil.getTime() > Date.now();
|
||||
}
|
||||
|
||||
toJSON(): GuildMember {
|
||||
toJSON(): GuildMemberData {
|
||||
return {
|
||||
user: this.user.toJSON(),
|
||||
nick: this.nick,
|
||||
@@ -179,9 +168,6 @@ export class GuildMemberRecord {
|
||||
accent_color: this.accentColor,
|
||||
roles: Array.from(this.roles),
|
||||
joined_at: this.joinedAt.toISOString(),
|
||||
join_source_type: this.joinSourceType,
|
||||
source_invite_code: this.sourceInviteCode,
|
||||
inviter_id: this.inviterId,
|
||||
mute: this.mute,
|
||||
deaf: this.deaf,
|
||||
communication_disabled_until: this.communicationDisabledUntil?.toISOString() ?? null,
|
||||
|
||||
@@ -17,80 +17,32 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {GuildSplashCardAlignmentValue} from '~/Constants';
|
||||
import {GuildRoleRecord} from '@app/records/GuildRoleRecord';
|
||||
import RuntimeConfigStore from '@app/stores/RuntimeConfigStore';
|
||||
import type {GuildReadyData} from '@app/types/gateway/GatewayGuildTypes';
|
||||
import {LARGE_GUILD_THRESHOLD} from '@fluxer/constants/src/GatewayConstants';
|
||||
import type {GuildSplashCardAlignmentValue} from '@fluxer/constants/src/GuildConstants';
|
||||
import {GuildFeatures, GuildSplashCardAlignment} from '@fluxer/constants/src/GuildConstants';
|
||||
import {
|
||||
GuildFeatures,
|
||||
GuildSplashCardAlignment,
|
||||
LARGE_GUILD_THRESHOLD,
|
||||
MAX_GUILD_EMOJIS_ANIMATED,
|
||||
MAX_GUILD_EMOJIS_ANIMATED_MORE_EMOJI,
|
||||
MAX_GUILD_EMOJIS_STATIC,
|
||||
MAX_GUILD_EMOJIS_STATIC_MORE_EMOJI,
|
||||
MAX_GUILD_STICKERS,
|
||||
MAX_GUILD_STICKERS_MORE_STICKERS,
|
||||
MessageNotifications,
|
||||
} from '~/Constants';
|
||||
import type {Channel} from '~/records/ChannelRecord';
|
||||
import type {GuildEmoji} from '~/records/GuildEmojiRecord';
|
||||
import type {GuildMember} from '~/records/GuildMemberRecord';
|
||||
import type {GuildRole} from '~/records/GuildRoleRecord';
|
||||
import {GuildRoleRecord} from '~/records/GuildRoleRecord';
|
||||
import type {GuildSticker} from '~/records/GuildStickerRecord';
|
||||
import type {Presence} from '~/stores/PresenceStore';
|
||||
import type {VoiceState} from '~/stores/voice/MediaEngineFacade';
|
||||
import * as SnowflakeUtils from '~/utils/SnowflakeUtils';
|
||||
} from '@fluxer/constants/src/LimitConstants';
|
||||
import {MessageNotifications} from '@fluxer/constants/src/NotificationConstants';
|
||||
import type {Guild} from '@fluxer/schema/src/domains/guild/GuildResponseSchemas';
|
||||
import * as SnowflakeUtils from '@fluxer/snowflake/src/SnowflakeUtils';
|
||||
|
||||
export type Guild = Readonly<{
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string | null;
|
||||
banner?: string | null;
|
||||
banner_width?: number | null;
|
||||
banner_height?: number | null;
|
||||
splash?: string | null;
|
||||
splash_width?: number | null;
|
||||
splash_height?: number | null;
|
||||
splash_card_alignment?: GuildSplashCardAlignmentValue;
|
||||
embed_splash?: string | null;
|
||||
embed_splash_width?: number | null;
|
||||
embed_splash_height?: number | null;
|
||||
vanity_url_code: string | null;
|
||||
owner_id: string;
|
||||
system_channel_id: string | null;
|
||||
system_channel_flags?: number;
|
||||
rules_channel_id?: string | null;
|
||||
afk_channel_id?: string | null;
|
||||
afk_timeout?: number;
|
||||
features: ReadonlyArray<string>;
|
||||
verification_level?: number;
|
||||
mfa_level?: number;
|
||||
nsfw_level?: number;
|
||||
explicit_content_filter?: number;
|
||||
default_message_notifications?: number;
|
||||
disabled_operations?: number;
|
||||
joined_at?: string;
|
||||
unavailable?: boolean;
|
||||
member_count?: number;
|
||||
}>;
|
||||
|
||||
export type GuildReadyData = Readonly<{
|
||||
id: string;
|
||||
properties: Omit<Guild, 'roles'>;
|
||||
channels: ReadonlyArray<Channel>;
|
||||
emojis: ReadonlyArray<GuildEmoji>;
|
||||
stickers?: ReadonlyArray<GuildSticker>;
|
||||
members: ReadonlyArray<GuildMember>;
|
||||
member_count: number;
|
||||
presences?: ReadonlyArray<Presence>;
|
||||
voice_states?: ReadonlyArray<VoiceState>;
|
||||
roles: ReadonlyArray<GuildRole>;
|
||||
joined_at: string;
|
||||
unavailable?: boolean;
|
||||
}>;
|
||||
interface GuildRecordOptions {
|
||||
instanceId?: string;
|
||||
}
|
||||
|
||||
type GuildInput = Guild | GuildRecord;
|
||||
|
||||
export class GuildRecord {
|
||||
readonly instanceId: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly icon: string | null;
|
||||
@@ -121,9 +73,12 @@ export class GuildRecord {
|
||||
private readonly _disabledOperations: number;
|
||||
readonly joinedAt: string | null;
|
||||
readonly unavailable: boolean;
|
||||
readonly messageHistoryCutoff: string | null;
|
||||
readonly memberCount: number;
|
||||
|
||||
constructor(guild: GuildInput) {
|
||||
constructor(guild: GuildInput, options?: GuildRecordOptions) {
|
||||
this.instanceId =
|
||||
options?.instanceId ?? (guild instanceof GuildRecord ? guild.instanceId : RuntimeConfigStore.localInstanceDomain);
|
||||
this.id = guild.id;
|
||||
this.name = guild.name;
|
||||
this.icon = guild.icon;
|
||||
@@ -153,6 +108,7 @@ export class GuildRecord {
|
||||
this.defaultMessageNotifications = this.normalizeDefaultMessageNotifications(guild);
|
||||
this._disabledOperations = this.normalizeDisabledOperations(guild);
|
||||
this.joinedAt = this.normalizeJoinedAt(guild);
|
||||
this.messageHistoryCutoff = this.normalizeMessageHistoryCutoff(guild);
|
||||
this.unavailable = guild.unavailable ?? false;
|
||||
this.memberCount = this.normalizeMemberCount(guild);
|
||||
}
|
||||
@@ -274,6 +230,10 @@ export class GuildRecord {
|
||||
return this.normalizeField(guild, 'joined_at', 'joinedAt');
|
||||
}
|
||||
|
||||
private normalizeMessageHistoryCutoff(guild: GuildInput): string | null {
|
||||
return this.normalizeField(guild, 'message_history_cutoff', 'messageHistoryCutoff');
|
||||
}
|
||||
|
||||
private normalizeMemberCount(guild: GuildInput): number {
|
||||
if (this.isGuildInput(guild)) {
|
||||
const value = (guild as Guild).member_count;
|
||||
@@ -290,7 +250,7 @@ export class GuildRecord {
|
||||
return this._disabledOperations;
|
||||
}
|
||||
|
||||
static fromGuildReadyData(guildData: GuildReadyData): GuildRecord {
|
||||
static fromGuildReadyData(guildData: GuildReadyData, instanceId?: string): GuildRecord {
|
||||
const roles = Object.freeze(
|
||||
guildData.roles.reduce<Record<string, GuildRoleRecord>>(
|
||||
(acc, role) => ({
|
||||
@@ -302,12 +262,15 @@ export class GuildRecord {
|
||||
),
|
||||
);
|
||||
|
||||
return new GuildRecord({
|
||||
...guildData.properties,
|
||||
roles,
|
||||
joined_at: guildData.joined_at,
|
||||
unavailable: guildData.unavailable,
|
||||
});
|
||||
return new GuildRecord(
|
||||
{
|
||||
...guildData.properties,
|
||||
roles,
|
||||
joined_at: guildData.joined_at,
|
||||
unavailable: guildData.unavailable,
|
||||
},
|
||||
{instanceId},
|
||||
);
|
||||
}
|
||||
|
||||
toJSON(): Guild & {
|
||||
@@ -341,6 +304,7 @@ export class GuildRecord {
|
||||
explicit_content_filter: this.explicitContentFilter,
|
||||
default_message_notifications: this.defaultMessageNotifications,
|
||||
disabled_operations: this._disabledOperations,
|
||||
message_history_cutoff: this.messageHistoryCutoff,
|
||||
joined_at: this.joinedAt ?? undefined,
|
||||
unavailable: this.unavailable,
|
||||
member_count: this.memberCount,
|
||||
@@ -349,44 +313,54 @@ export class GuildRecord {
|
||||
}
|
||||
|
||||
withUpdates(guild: Partial<Guild>): GuildRecord {
|
||||
return new GuildRecord({
|
||||
...this,
|
||||
name: guild.name ?? this.name,
|
||||
icon: guild.icon ?? this.icon,
|
||||
banner: guild.banner ?? this.banner,
|
||||
bannerWidth: guild.banner_width ?? this.bannerWidth,
|
||||
bannerHeight: guild.banner_height ?? this.bannerHeight,
|
||||
splash: guild.splash ?? this.splash,
|
||||
splashWidth: guild.splash_width ?? this.splashWidth,
|
||||
splashHeight: guild.splash_height ?? this.splashHeight,
|
||||
splashCardAlignment: guild.splash_card_alignment ?? this.splashCardAlignment,
|
||||
embedSplash: guild.embed_splash ?? this.embedSplash,
|
||||
embedSplashWidth: guild.embed_splash_width ?? this.embedSplashWidth,
|
||||
embedSplashHeight: guild.embed_splash_height ?? this.embedSplashHeight,
|
||||
features: guild.features ? new Set(guild.features) : this.features,
|
||||
vanityURLCode: guild.vanity_url_code ?? this.vanityURLCode,
|
||||
ownerId: guild.owner_id ?? this.ownerId,
|
||||
systemChannelId: guild.system_channel_id ?? this.systemChannelId,
|
||||
systemChannelFlags: guild.system_channel_flags ?? this.systemChannelFlags,
|
||||
rulesChannelId: guild.rules_channel_id ?? this.rulesChannelId,
|
||||
afkChannelId: guild.afk_channel_id ?? this.afkChannelId,
|
||||
afkTimeout: guild.afk_timeout ?? this.afkTimeout,
|
||||
verificationLevel: guild.verification_level ?? this.verificationLevel,
|
||||
mfaLevel: guild.mfa_level ?? this.mfaLevel,
|
||||
nsfwLevel: guild.nsfw_level ?? this.nsfwLevel,
|
||||
explicitContentFilter: guild.explicit_content_filter ?? this.explicitContentFilter,
|
||||
defaultMessageNotifications: guild.default_message_notifications ?? this.defaultMessageNotifications,
|
||||
disabledOperations: guild.disabled_operations ?? this.disabledOperations,
|
||||
unavailable: guild.unavailable ?? this.unavailable,
|
||||
memberCount: guild.member_count ?? this.memberCount,
|
||||
});
|
||||
return new GuildRecord(
|
||||
{
|
||||
...this,
|
||||
name: guild.name ?? this.name,
|
||||
icon: guild.icon ?? this.icon,
|
||||
banner: guild.banner ?? this.banner,
|
||||
bannerWidth: guild.banner_width ?? this.bannerWidth,
|
||||
bannerHeight: guild.banner_height ?? this.bannerHeight,
|
||||
splash: guild.splash ?? this.splash,
|
||||
splashWidth: guild.splash_width ?? this.splashWidth,
|
||||
splashHeight: guild.splash_height ?? this.splashHeight,
|
||||
splashCardAlignment: guild.splash_card_alignment ?? this.splashCardAlignment,
|
||||
embedSplash: guild.embed_splash ?? this.embedSplash,
|
||||
embedSplashWidth: guild.embed_splash_width ?? this.embedSplashWidth,
|
||||
embedSplashHeight: guild.embed_splash_height ?? this.embedSplashHeight,
|
||||
features: guild.features ? new Set(guild.features) : this.features,
|
||||
vanityURLCode: guild.vanity_url_code ?? this.vanityURLCode,
|
||||
ownerId: guild.owner_id ?? this.ownerId,
|
||||
systemChannelId: guild.system_channel_id ?? this.systemChannelId,
|
||||
systemChannelFlags: guild.system_channel_flags ?? this.systemChannelFlags,
|
||||
rulesChannelId: guild.rules_channel_id ?? this.rulesChannelId,
|
||||
afkChannelId: guild.afk_channel_id ?? this.afkChannelId,
|
||||
afkTimeout: guild.afk_timeout ?? this.afkTimeout,
|
||||
verificationLevel: guild.verification_level ?? this.verificationLevel,
|
||||
mfaLevel: guild.mfa_level ?? this.mfaLevel,
|
||||
nsfwLevel: guild.nsfw_level ?? this.nsfwLevel,
|
||||
explicitContentFilter: guild.explicit_content_filter ?? this.explicitContentFilter,
|
||||
defaultMessageNotifications: guild.default_message_notifications ?? this.defaultMessageNotifications,
|
||||
disabledOperations: guild.disabled_operations ?? this.disabledOperations,
|
||||
messageHistoryCutoff:
|
||||
guild.message_history_cutoff !== undefined
|
||||
? (guild.message_history_cutoff ?? null)
|
||||
: this.messageHistoryCutoff,
|
||||
unavailable: guild.unavailable ?? this.unavailable,
|
||||
memberCount: guild.member_count ?? this.memberCount,
|
||||
},
|
||||
{instanceId: this.instanceId},
|
||||
);
|
||||
}
|
||||
|
||||
withRoles(roles: Record<string, GuildRoleRecord>): GuildRecord {
|
||||
return new GuildRecord({
|
||||
...this,
|
||||
roles: Object.freeze({...roles}),
|
||||
});
|
||||
return new GuildRecord(
|
||||
{
|
||||
...this,
|
||||
roles: Object.freeze({...roles}),
|
||||
},
|
||||
{instanceId: this.instanceId},
|
||||
);
|
||||
}
|
||||
|
||||
addRole(role: GuildRoleRecord): GuildRecord {
|
||||
@@ -451,7 +425,11 @@ export class GuildRecord {
|
||||
}
|
||||
|
||||
get isLargeGuild(): boolean {
|
||||
return this.features.has(GuildFeatures.LARGE_GUILD_OVERRIDE) || this.memberCount > LARGE_GUILD_THRESHOLD;
|
||||
return (
|
||||
this.features.has(GuildFeatures.LARGE_GUILD_OVERRIDE) ||
|
||||
this.features.has(GuildFeatures.VERY_LARGE_GUILD) ||
|
||||
this.memberCount > LARGE_GUILD_THRESHOLD
|
||||
);
|
||||
}
|
||||
|
||||
get effectiveMessageNotifications(): number {
|
||||
|
||||
@@ -17,18 +17,15 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface GuildRole {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly color: number;
|
||||
readonly position: number;
|
||||
readonly hoist_position?: number | null;
|
||||
readonly permissions: string;
|
||||
readonly hoist: boolean;
|
||||
readonly mentionable: boolean;
|
||||
import RuntimeConfigStore from '@app/stores/RuntimeConfigStore';
|
||||
import type {GuildRole} from '@fluxer/schema/src/domains/guild/GuildRoleSchemas';
|
||||
|
||||
interface GuildRoleRecordOptions {
|
||||
instanceId?: string;
|
||||
}
|
||||
|
||||
export class GuildRoleRecord {
|
||||
readonly instanceId: string;
|
||||
readonly id: string;
|
||||
readonly guildId: string;
|
||||
readonly name: string;
|
||||
@@ -39,7 +36,8 @@ export class GuildRoleRecord {
|
||||
readonly hoist: boolean;
|
||||
readonly mentionable: boolean;
|
||||
|
||||
constructor(guildId: string, guildRole: GuildRole) {
|
||||
constructor(guildId: string, guildRole: GuildRole, options?: GuildRoleRecordOptions) {
|
||||
this.instanceId = options?.instanceId ?? RuntimeConfigStore.localInstanceDomain;
|
||||
this.id = guildRole.id;
|
||||
this.guildId = guildId;
|
||||
this.name = guildRole.name;
|
||||
@@ -56,16 +54,20 @@ export class GuildRoleRecord {
|
||||
}
|
||||
|
||||
withUpdates(updates: Partial<GuildRole>): GuildRoleRecord {
|
||||
return new GuildRoleRecord(this.guildId, {
|
||||
id: this.id,
|
||||
name: updates.name ?? this.name,
|
||||
color: updates.color ?? this.color,
|
||||
position: updates.position ?? this.position,
|
||||
hoist_position: updates.hoist_position !== undefined ? updates.hoist_position : this.hoistPosition,
|
||||
permissions: updates.permissions ?? this.permissions.toString(),
|
||||
hoist: updates.hoist ?? this.hoist,
|
||||
mentionable: updates.mentionable ?? this.mentionable,
|
||||
});
|
||||
return new GuildRoleRecord(
|
||||
this.guildId,
|
||||
{
|
||||
id: this.id,
|
||||
name: updates.name ?? this.name,
|
||||
color: updates.color ?? this.color,
|
||||
position: updates.position ?? this.position,
|
||||
hoist_position: updates.hoist_position !== undefined ? updates.hoist_position : this.hoistPosition,
|
||||
permissions: updates.permissions ?? this.permissions.toString(),
|
||||
hoist: updates.hoist ?? this.hoist,
|
||||
mentionable: updates.mentionable ?? this.mentionable,
|
||||
},
|
||||
{instanceId: this.instanceId},
|
||||
);
|
||||
}
|
||||
|
||||
get isEveryone(): boolean {
|
||||
@@ -74,6 +76,7 @@ export class GuildRoleRecord {
|
||||
|
||||
equals(other: GuildRoleRecord): boolean {
|
||||
return (
|
||||
this.instanceId === other.instanceId &&
|
||||
this.id === other.id &&
|
||||
this.guildId === other.guildId &&
|
||||
this.name === other.name &&
|
||||
|
||||
@@ -17,26 +17,9 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {StickerFormatTypes} from '~/Constants';
|
||||
import type {UserPartial} from '~/records/UserRecord';
|
||||
import * as AvatarUtils from '~/utils/AvatarUtils';
|
||||
|
||||
export type GuildSticker = Readonly<{
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
tags: Array<string>;
|
||||
format_type: number;
|
||||
user?: UserPartial;
|
||||
}>;
|
||||
|
||||
export interface GuildStickerWithUser extends GuildSticker {
|
||||
user?: UserPartial;
|
||||
}
|
||||
|
||||
export function isStickerAnimated(sticker: GuildSticker) {
|
||||
return sticker.format_type === StickerFormatTypes.GIF;
|
||||
}
|
||||
import * as AvatarUtils from '@app/utils/AvatarUtils';
|
||||
import type {GuildSticker} from '@fluxer/schema/src/domains/guild/GuildEmojiSchemas';
|
||||
import type {UserPartial} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
||||
|
||||
export class GuildStickerRecord {
|
||||
readonly id: string;
|
||||
@@ -45,7 +28,7 @@ export class GuildStickerRecord {
|
||||
readonly description: string;
|
||||
readonly tags: ReadonlyArray<string>;
|
||||
readonly url: string;
|
||||
readonly formatType: number;
|
||||
readonly animated: boolean;
|
||||
readonly user?: UserPartial;
|
||||
|
||||
constructor(guildId: string, data: GuildSticker) {
|
||||
@@ -56,24 +39,20 @@ export class GuildStickerRecord {
|
||||
this.tags = Object.freeze([...data.tags]);
|
||||
this.url = AvatarUtils.getStickerURL({
|
||||
id: data.id,
|
||||
animated: isStickerAnimated(data),
|
||||
animated: data.animated,
|
||||
size: 320,
|
||||
});
|
||||
this.formatType = data.format_type;
|
||||
this.animated = data.animated;
|
||||
this.user = data.user;
|
||||
}
|
||||
|
||||
isAnimated() {
|
||||
return isStickerAnimated(this.toJSON());
|
||||
}
|
||||
|
||||
withUpdates(updates: Partial<GuildSticker>): GuildStickerRecord {
|
||||
return new GuildStickerRecord(this.guildId, {
|
||||
id: updates.id ?? this.id,
|
||||
name: updates.name ?? this.name,
|
||||
description: updates.description ?? this.description,
|
||||
tags: updates.tags ?? [...this.tags],
|
||||
format_type: updates.format_type ?? this.formatType,
|
||||
animated: updates.animated ?? this.animated,
|
||||
user: updates.user ?? this.user,
|
||||
});
|
||||
}
|
||||
@@ -85,7 +64,7 @@ export class GuildStickerRecord {
|
||||
this.name === other.name &&
|
||||
this.description === other.description &&
|
||||
JSON.stringify(this.tags) === JSON.stringify(other.tags) &&
|
||||
this.formatType === other.formatType &&
|
||||
this.animated === other.animated &&
|
||||
this.user?.id === other.user?.id
|
||||
);
|
||||
}
|
||||
@@ -96,7 +75,7 @@ export class GuildStickerRecord {
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
tags: [...this.tags],
|
||||
format_type: this.formatType,
|
||||
animated: this.animated,
|
||||
user: this.user,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,193 +17,35 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {MessageFlags, MessageStates, MessageTypes} from '~/Constants';
|
||||
import type {GuildMember} from '~/records/GuildMemberRecord';
|
||||
import type {UserPartial} from '~/records/UserRecord';
|
||||
import {UserRecord} from '~/records/UserRecord';
|
||||
import AuthenticationStore from '~/stores/AuthenticationStore';
|
||||
import ChannelStore from '~/stores/ChannelStore';
|
||||
import GuildMemberStore from '~/stores/GuildMemberStore';
|
||||
import GuildStore from '~/stores/GuildStore';
|
||||
import RelationshipStore from '~/stores/RelationshipStore';
|
||||
import UserStore from '~/stores/UserStore';
|
||||
import type {Invite as InviteType} from '~/types/InviteTypes';
|
||||
import * as GiftCodeUtils from '~/utils/giftCodeUtils';
|
||||
import * as InviteUtils from '~/utils/InviteUtils';
|
||||
import {emojiEquals, type ReactionEmoji} from '~/utils/ReactionUtils';
|
||||
import * as ThemeUtils from '~/utils/ThemeUtils';
|
||||
|
||||
export type Invite = InviteType;
|
||||
|
||||
export interface EmbedAuthor {
|
||||
name: string;
|
||||
url?: string;
|
||||
icon_url?: string;
|
||||
proxy_icon_url?: string;
|
||||
}
|
||||
|
||||
export interface EmbedFooter {
|
||||
text: string;
|
||||
icon_url?: string;
|
||||
proxy_icon_url?: string;
|
||||
}
|
||||
|
||||
export interface EmbedMedia {
|
||||
url: string;
|
||||
proxy_url?: string;
|
||||
content_type?: string;
|
||||
content_hash?: string | null;
|
||||
width?: number;
|
||||
height?: number;
|
||||
placeholder?: string;
|
||||
flags: number;
|
||||
description?: string;
|
||||
duration?: number;
|
||||
nsfw?: boolean;
|
||||
}
|
||||
|
||||
export interface EmbedField {
|
||||
name: string;
|
||||
value: string;
|
||||
inline: boolean;
|
||||
}
|
||||
|
||||
export interface MessageEmbed {
|
||||
id: string;
|
||||
type: string;
|
||||
url?: string;
|
||||
title?: string;
|
||||
color?: number;
|
||||
timestamp?: string;
|
||||
description?: string;
|
||||
author?: EmbedAuthor;
|
||||
image?: EmbedMedia;
|
||||
thumbnail?: EmbedMedia;
|
||||
footer?: EmbedFooter;
|
||||
fields?: ReadonlyArray<EmbedField>;
|
||||
provider?: EmbedAuthor;
|
||||
video?: EmbedMedia;
|
||||
audio?: EmbedMedia;
|
||||
flags?: number;
|
||||
}
|
||||
|
||||
export interface MessageReference {
|
||||
message_id: string;
|
||||
channel_id: string;
|
||||
guild_id?: string;
|
||||
type?: number;
|
||||
}
|
||||
|
||||
export interface MessageReaction {
|
||||
emoji: ReactionEmoji;
|
||||
count: number;
|
||||
me?: true;
|
||||
me_burst?: boolean;
|
||||
count_details?: {
|
||||
burst: number;
|
||||
normal: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface MessageAttachment {
|
||||
id: string;
|
||||
filename: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
caption?: string;
|
||||
content_type?: string;
|
||||
size: number;
|
||||
url: string | null;
|
||||
proxy_url: string | null;
|
||||
width?: number;
|
||||
height?: number;
|
||||
placeholder?: string;
|
||||
placeholder_version?: number;
|
||||
flags: number;
|
||||
duration_secs?: number;
|
||||
duration?: number;
|
||||
waveform?: string;
|
||||
content_hash?: string | null;
|
||||
nsfw?: boolean;
|
||||
expires_at?: string | null;
|
||||
expired?: boolean;
|
||||
}
|
||||
|
||||
export interface MessageCall {
|
||||
participants: Array<string>;
|
||||
ended_timestamp?: string | null;
|
||||
}
|
||||
|
||||
export interface MessageSnapshot {
|
||||
type: number;
|
||||
content: string;
|
||||
embeds?: ReadonlyArray<MessageEmbed>;
|
||||
attachments?: ReadonlyArray<MessageAttachment>;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface MessageStickerItem {
|
||||
id: string;
|
||||
name: string;
|
||||
format_type: number;
|
||||
}
|
||||
|
||||
export interface ChannelMention {
|
||||
id: string;
|
||||
guild_id: string;
|
||||
type: number;
|
||||
name: string;
|
||||
parent_id?: string | null;
|
||||
}
|
||||
|
||||
export interface AllowedMentions {
|
||||
parse?: ReadonlyArray<'roles' | 'users' | 'everyone'>;
|
||||
roles?: ReadonlyArray<string>;
|
||||
users?: ReadonlyArray<string>;
|
||||
replied_user?: boolean;
|
||||
}
|
||||
|
||||
export interface MessageMention extends UserPartial {
|
||||
member?: Omit<GuildMember, 'user'>;
|
||||
}
|
||||
|
||||
export type Message = Readonly<{
|
||||
id: string;
|
||||
channel_id: string;
|
||||
guild_id?: string;
|
||||
author: UserPartial;
|
||||
member?: Omit<GuildMember, 'user'>;
|
||||
webhook_id?: string;
|
||||
application_id?: string;
|
||||
type: number;
|
||||
flags: number;
|
||||
pinned: boolean;
|
||||
tts?: boolean;
|
||||
mention_everyone: boolean;
|
||||
content: string;
|
||||
timestamp: string;
|
||||
edited_timestamp?: string;
|
||||
mentions?: ReadonlyArray<MessageMention>;
|
||||
mention_roles?: ReadonlyArray<string>;
|
||||
mention_channels?: ReadonlyArray<ChannelMention>;
|
||||
embeds?: ReadonlyArray<MessageEmbed>;
|
||||
attachments?: ReadonlyArray<MessageAttachment>;
|
||||
stickers?: ReadonlyArray<MessageStickerItem>;
|
||||
reactions?: ReadonlyArray<MessageReaction>;
|
||||
message_reference?: MessageReference;
|
||||
referenced_message?: Message | null;
|
||||
message_snapshots?: ReadonlyArray<MessageSnapshot>;
|
||||
call?: MessageCall | null;
|
||||
state?: string;
|
||||
nonce?: string;
|
||||
blocked?: boolean;
|
||||
loggingName?: string;
|
||||
_allowedMentions?: AllowedMentions;
|
||||
_favoriteMemeId?: string;
|
||||
}>;
|
||||
import {UserRecord} from '@app/records/UserRecord';
|
||||
import AuthenticationStore from '@app/stores/AuthenticationStore';
|
||||
import ChannelStore from '@app/stores/ChannelStore';
|
||||
import GuildMemberStore from '@app/stores/GuildMemberStore';
|
||||
import GuildStore from '@app/stores/GuildStore';
|
||||
import RelationshipStore from '@app/stores/RelationshipStore';
|
||||
import RuntimeConfigStore from '@app/stores/RuntimeConfigStore';
|
||||
import UserStore from '@app/stores/UserStore';
|
||||
import * as GiftCodeUtils from '@app/utils/GiftCodeUtils';
|
||||
import * as InviteUtils from '@app/utils/InviteUtils';
|
||||
import {emojiEquals} from '@app/utils/ReactionUtils';
|
||||
import * as ThemeUtils from '@app/utils/ThemeUtils';
|
||||
import {MessageFlags, MessageStates, MessageTypes} from '@fluxer/constants/src/ChannelConstants';
|
||||
import type {MessageEmbed} from '@fluxer/schema/src/domains/message/EmbedSchemas';
|
||||
import type {
|
||||
AllowedMentions,
|
||||
ChannelMention,
|
||||
Message,
|
||||
MessageAttachment,
|
||||
MessageCall,
|
||||
MessageReaction,
|
||||
MessageReference,
|
||||
MessageSnapshot,
|
||||
MessageStickerItem,
|
||||
ReactionEmoji,
|
||||
} from '@fluxer/schema/src/domains/message/MessageResponseSchemas';
|
||||
|
||||
interface TransformedMessageCall {
|
||||
participants: Array<string>;
|
||||
participants: ReadonlyArray<string>;
|
||||
endedTimestamp: Date | null;
|
||||
}
|
||||
|
||||
@@ -249,15 +91,16 @@ const getOrCreateEmbedId = (embed: Omit<MessageEmbed, 'id'>): string => {
|
||||
|
||||
interface MessageRecordOptions {
|
||||
skipUserCache?: boolean;
|
||||
instanceId?: string;
|
||||
}
|
||||
|
||||
export class MessageRecord {
|
||||
readonly instanceId: string;
|
||||
readonly id: string;
|
||||
readonly channelId: string;
|
||||
readonly guildId?: string;
|
||||
readonly author: UserRecord;
|
||||
readonly webhookId?: string;
|
||||
readonly applicationId?: string;
|
||||
readonly type: number;
|
||||
readonly flags: number;
|
||||
readonly pinned: boolean;
|
||||
@@ -290,23 +133,31 @@ export class MessageRecord {
|
||||
readonly stickers?: ReadonlyArray<MessageStickerItem>;
|
||||
|
||||
constructor(message: Message, options?: MessageRecordOptions) {
|
||||
this.instanceId = options?.instanceId ?? RuntimeConfigStore.localInstanceDomain;
|
||||
|
||||
const shouldCacheAuthor = !message.webhook_id;
|
||||
if (!options?.skipUserCache) {
|
||||
UserStore.cacheUsers([message.author, ...(message.mentions ?? [])]);
|
||||
const authorsToCache = [...(shouldCacheAuthor ? [message.author] : []), ...(message.mentions ?? [])].filter(
|
||||
Boolean,
|
||||
);
|
||||
if (authorsToCache.length > 0) {
|
||||
UserStore.cacheUsers(authorsToCache);
|
||||
}
|
||||
}
|
||||
|
||||
const isBlocked = RelationshipStore.isBlocked(message.author.id);
|
||||
|
||||
if (message.webhook_id) {
|
||||
this.author = new UserRecord(message.author);
|
||||
this.author = new UserRecord(message.author, {instanceId: this.instanceId});
|
||||
} else {
|
||||
this.author = UserStore.getUser(message.author.id) || new UserRecord(message.author);
|
||||
this.author =
|
||||
UserStore.getUser(message.author.id) || new UserRecord(message.author, {instanceId: this.instanceId});
|
||||
}
|
||||
|
||||
this.id = message.id;
|
||||
this.channelId = message.channel_id;
|
||||
this.guildId = message.guild_id;
|
||||
this.webhookId = message.webhook_id;
|
||||
this.applicationId = message.application_id;
|
||||
this.type = message.type;
|
||||
this.flags = message.flags;
|
||||
this.pinned = message.pinned;
|
||||
@@ -371,6 +222,10 @@ export class MessageRecord {
|
||||
);
|
||||
}
|
||||
|
||||
isClientSystemMessage(): boolean {
|
||||
return this.type === MessageTypes.CLIENT_SYSTEM;
|
||||
}
|
||||
|
||||
isSystemMessage(): boolean {
|
||||
return !this.isUserMessage();
|
||||
}
|
||||
@@ -411,7 +266,6 @@ export class MessageRecord {
|
||||
guild_id: updates.guild_id ?? this.guildId,
|
||||
author: updates.author ?? this.author.toJSON(),
|
||||
webhook_id: updates.webhook_id ?? this.webhookId,
|
||||
application_id: updates.application_id ?? this.applicationId,
|
||||
type: updates.type ?? this.type,
|
||||
flags: updates.flags ?? this.flags,
|
||||
pinned: updates.pinned ?? this.pinned,
|
||||
@@ -435,7 +289,7 @@ export class MessageRecord {
|
||||
blocked: updates.blocked ?? this.blocked,
|
||||
loggingName: updates.loggingName ?? this.loggingName,
|
||||
},
|
||||
{skipUserCache: true},
|
||||
{skipUserCache: true, instanceId: this.instanceId},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -502,6 +356,7 @@ export class MessageRecord {
|
||||
equals(other: MessageRecord): boolean {
|
||||
if (this === other) return true;
|
||||
|
||||
if (this.instanceId !== other.instanceId) return false;
|
||||
if (this.id !== other.id) return false;
|
||||
if (this.channelId !== other.channelId) return false;
|
||||
if (this.guildId !== other.guildId) return false;
|
||||
@@ -514,7 +369,6 @@ export class MessageRecord {
|
||||
if (this.nonce !== other.nonce) return false;
|
||||
if (this.blocked !== other.blocked) return false;
|
||||
if (this.webhookId !== other.webhookId) return false;
|
||||
if (this.applicationId !== other.applicationId) return false;
|
||||
if (this.loggingName !== other.loggingName) return false;
|
||||
|
||||
if (this.timestamp.getTime() !== other.timestamp.getTime()) return false;
|
||||
@@ -574,7 +428,7 @@ export class MessageRecord {
|
||||
for (let i = 0; i < this.stickerItems.length; i++) {
|
||||
const s1 = this.stickerItems[i];
|
||||
const s2 = other.stickerItems[i];
|
||||
if (s1.id !== s2.id || s1.name !== s2.name || s1.format_type !== s2.format_type) {
|
||||
if (s1.id !== s2.id || s1.name !== s2.name || s1.animated !== s2.animated) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -655,7 +509,6 @@ export class MessageRecord {
|
||||
guild_id: this.guildId,
|
||||
author: this.author.toJSON(),
|
||||
webhook_id: this.webhookId,
|
||||
application_id: this.applicationId,
|
||||
type: this.type,
|
||||
flags: this.flags,
|
||||
pinned: this.pinned,
|
||||
|
||||
@@ -17,14 +17,21 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {GuildMember, GuildMemberRecord} from '~/records/GuildMemberRecord';
|
||||
import type {GuildRecord} from '~/records/GuildRecord';
|
||||
import type {UserPartial, UserProfile} from '~/records/UserRecord';
|
||||
import GuildMemberStore from '~/stores/GuildMemberStore';
|
||||
import GuildStore from '~/stores/GuildStore';
|
||||
import UserStore from '~/stores/UserStore';
|
||||
import type {GuildMemberRecord} from '@app/records/GuildMemberRecord';
|
||||
import type {GuildRecord} from '@app/records/GuildRecord';
|
||||
import GuildMemberStore from '@app/stores/GuildMemberStore';
|
||||
import GuildStore from '@app/stores/GuildStore';
|
||||
import UserStore from '@app/stores/UserStore';
|
||||
import type {ConnectionResponse} from '@fluxer/schema/src/domains/connection/ConnectionSchemas';
|
||||
import type {GuildMemberData} from '@fluxer/schema/src/domains/guild/GuildMemberSchemas';
|
||||
import type {UserPartial, UserProfile} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
||||
|
||||
export type ProfileMutualGuild = Readonly<{
|
||||
export interface ProfileMutualGuild {
|
||||
id: string;
|
||||
nick: string | null;
|
||||
}
|
||||
|
||||
export type MiniGuildMember = Readonly<{
|
||||
id: string;
|
||||
nick: string | null;
|
||||
}>;
|
||||
@@ -34,12 +41,13 @@ export type Profile = Readonly<{
|
||||
user_profile: UserProfile;
|
||||
guild_member_profile?: UserProfile | null;
|
||||
timezone_offset: number | null;
|
||||
guild_member?: GuildMember;
|
||||
guild_member?: GuildMemberData;
|
||||
premium_type?: number;
|
||||
premium_since?: string;
|
||||
premium_lifetime_sequence?: number;
|
||||
mutual_friends?: Array<UserPartial>;
|
||||
mutual_guilds?: Array<ProfileMutualGuild>;
|
||||
connected_accounts?: Array<ConnectionResponse>;
|
||||
}>;
|
||||
|
||||
export class ProfileRecord {
|
||||
@@ -53,6 +61,7 @@ export class ProfileRecord {
|
||||
readonly premiumLifetimeSequence: number | null;
|
||||
readonly mutualFriends: ReadonlyArray<UserPartial> | null;
|
||||
readonly mutualGuilds: ReadonlyArray<ProfileMutualGuild> | null;
|
||||
readonly connectedAccounts: ReadonlyArray<ConnectionResponse> | null;
|
||||
|
||||
constructor(profile: Profile, guildId?: string) {
|
||||
this.userId = profile.user.id;
|
||||
@@ -65,6 +74,7 @@ export class ProfileRecord {
|
||||
this.premiumLifetimeSequence = profile.premium_lifetime_sequence ?? null;
|
||||
this.mutualFriends = profile.mutual_friends ? Object.freeze([...profile.mutual_friends]) : null;
|
||||
this.mutualGuilds = profile.mutual_guilds ? Object.freeze([...profile.mutual_guilds]) : null;
|
||||
this.connectedAccounts = profile.connected_accounts ? Object.freeze([...profile.connected_accounts]) : null;
|
||||
}
|
||||
|
||||
withUpdates(updates: Partial<Profile>): ProfileRecord {
|
||||
@@ -85,6 +95,8 @@ export class ProfileRecord {
|
||||
: (this.premiumLifetimeSequence ?? undefined),
|
||||
mutual_friends: updates.mutual_friends ?? (this.mutualFriends ? [...this.mutualFriends] : undefined),
|
||||
mutual_guilds: updates.mutual_guilds ?? (this.mutualGuilds ? [...this.mutualGuilds] : undefined),
|
||||
connected_accounts:
|
||||
updates.connected_accounts ?? (this.connectedAccounts ? [...this.connectedAccounts] : undefined),
|
||||
},
|
||||
this.guildId ?? undefined,
|
||||
);
|
||||
@@ -139,13 +151,14 @@ export class ProfileRecord {
|
||||
this.premiumSince === other.premiumSince &&
|
||||
this.premiumLifetimeSequence === other.premiumLifetimeSequence &&
|
||||
JSON.stringify(this.mutualFriends) === JSON.stringify(other.mutualFriends) &&
|
||||
JSON.stringify(this.mutualGuilds) === JSON.stringify(other.mutualGuilds)
|
||||
JSON.stringify(this.mutualGuilds) === JSON.stringify(other.mutualGuilds) &&
|
||||
JSON.stringify(this.connectedAccounts) === JSON.stringify(other.connectedAccounts)
|
||||
);
|
||||
}
|
||||
|
||||
toJSON(): Profile {
|
||||
return {
|
||||
user: UserStore.getUser(this.userId)!,
|
||||
user: UserStore.getUser(this.userId)!.toJSON(),
|
||||
user_profile: {...this.userProfile},
|
||||
guild_member_profile: this.guildMemberProfile ? {...this.guildMemberProfile} : undefined,
|
||||
timezone_offset: this.timezoneOffset,
|
||||
@@ -154,6 +167,7 @@ export class ProfileRecord {
|
||||
premium_lifetime_sequence: this.premiumLifetimeSequence ?? undefined,
|
||||
mutual_friends: this.mutualFriends ? [...this.mutualFriends] : undefined,
|
||||
mutual_guilds: this.mutualGuilds ? [...this.mutualGuilds] : undefined,
|
||||
connected_accounts: this.connectedAccounts ? [...this.connectedAccounts] : undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {UserPartial, UserRecord} from '~/records/UserRecord';
|
||||
import UserStore from '~/stores/UserStore';
|
||||
import type {UserRecord} from '@app/records/UserRecord';
|
||||
import RuntimeConfigStore from '@app/stores/RuntimeConfigStore';
|
||||
import UserStore from '@app/stores/UserStore';
|
||||
import type {UserPartial} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
||||
|
||||
export type Relationship = Readonly<{
|
||||
id: string;
|
||||
@@ -28,14 +30,20 @@ export type Relationship = Readonly<{
|
||||
nickname?: string | null;
|
||||
}>;
|
||||
|
||||
interface RelationshipRecordOptions {
|
||||
instanceId?: string;
|
||||
}
|
||||
|
||||
export class RelationshipRecord {
|
||||
readonly instanceId: string;
|
||||
readonly id: string;
|
||||
readonly type: number;
|
||||
readonly userId: string;
|
||||
readonly since: Date;
|
||||
readonly nickname: string | null;
|
||||
|
||||
constructor(relationship: Relationship) {
|
||||
constructor(relationship: Relationship, options?: RelationshipRecordOptions) {
|
||||
this.instanceId = options?.instanceId ?? RuntimeConfigStore.localInstanceDomain;
|
||||
if (relationship.user) {
|
||||
UserStore.cacheUsers([relationship.user]);
|
||||
this.userId = relationship.user.id;
|
||||
@@ -60,12 +68,15 @@ export class RelationshipRecord {
|
||||
}
|
||||
: this.user?.toJSON();
|
||||
|
||||
return new RelationshipRecord({
|
||||
id: relationship.id ?? this.id,
|
||||
type: relationship.type ?? this.type,
|
||||
since: relationship.since ?? this.since.toISOString(),
|
||||
nickname: relationship.nickname ?? this.nickname,
|
||||
user: mergedUser,
|
||||
});
|
||||
return new RelationshipRecord(
|
||||
{
|
||||
id: relationship.id ?? this.id,
|
||||
type: relationship.type ?? this.type,
|
||||
since: relationship.since ?? this.since.toISOString(),
|
||||
nickname: relationship.nickname ?? this.nickname,
|
||||
user: mergedUser,
|
||||
},
|
||||
{instanceId: this.instanceId},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {type Message, MessageRecord} from '~/records/MessageRecord';
|
||||
import {MessageRecord} from '@app/records/MessageRecord';
|
||||
import type {Message} from '@fluxer/schema/src/domains/message/MessageResponseSchemas';
|
||||
|
||||
export interface SavedMessageEntryResponse {
|
||||
export interface SavedMessageEntry {
|
||||
id: string;
|
||||
channel_id: string;
|
||||
message_id: string;
|
||||
@@ -42,7 +43,7 @@ export class SavedMessageEntryRecord {
|
||||
readonly status: SavedMessageStatus;
|
||||
readonly message: MessageRecord | null;
|
||||
|
||||
constructor(data: SavedMessageEntryResponse) {
|
||||
constructor(data: SavedMessageEntry) {
|
||||
this.id = data.id;
|
||||
this.channelId = data.channel_id;
|
||||
this.messageId = data.message_id;
|
||||
@@ -50,7 +51,7 @@ export class SavedMessageEntryRecord {
|
||||
this.message = data.message ? new MessageRecord(data.message) : null;
|
||||
}
|
||||
|
||||
static fromResponse(response: SavedMessageEntryResponse): SavedMessageEntryRecord {
|
||||
static fromResponse(response: SavedMessageEntry): SavedMessageEntryRecord {
|
||||
return new SavedMessageEntryRecord(response);
|
||||
}
|
||||
|
||||
@@ -17,97 +17,26 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import DeveloperOptionsStore from '@app/stores/DeveloperOptionsStore';
|
||||
import RuntimeConfigStore from '@app/stores/RuntimeConfigStore';
|
||||
import {getMaxAttachmentFileSize} from '@app/utils/AttachmentUtils';
|
||||
import {LimitResolver} from '@app/utils/limits/LimitResolverAdapter';
|
||||
import type {LimitKey} from '@fluxer/constants/src/LimitConfigMetadata';
|
||||
import {
|
||||
MAX_BIO_LENGTH_NON_PREMIUM,
|
||||
MAX_BIO_LENGTH_PREMIUM,
|
||||
MAX_ATTACHMENTS_PER_MESSAGE,
|
||||
MAX_BIO_LENGTH,
|
||||
MAX_BOOKMARKS_NON_PREMIUM,
|
||||
MAX_BOOKMARKS_PREMIUM,
|
||||
MAX_FAVORITE_MEME_TAGS,
|
||||
MAX_FAVORITE_MEMES_NON_PREMIUM,
|
||||
MAX_FAVORITE_MEMES_PREMIUM,
|
||||
MAX_GROUP_DM_RECIPIENTS,
|
||||
MAX_GUILDS_NON_PREMIUM,
|
||||
MAX_GUILDS_PREMIUM,
|
||||
MAX_MESSAGE_LENGTH_NON_PREMIUM,
|
||||
MAX_MESSAGE_LENGTH_PREMIUM,
|
||||
UserFlags,
|
||||
} from '~/Constants';
|
||||
import DeveloperOptionsStore from '~/stores/DeveloperOptionsStore';
|
||||
import RuntimeConfigStore from '~/stores/RuntimeConfigStore';
|
||||
import {getAttachmentMaxSize} from '~/utils/AttachmentUtils';
|
||||
import * as SnowflakeUtils from '~/utils/SnowflakeUtils';
|
||||
|
||||
export type BackupCode = Readonly<{
|
||||
code: string;
|
||||
consumed: boolean;
|
||||
}>;
|
||||
|
||||
export type UserProfile = Readonly<{
|
||||
bio: string | null;
|
||||
banner: string | null;
|
||||
banner_color?: number | null;
|
||||
pronouns: string | null;
|
||||
accent_color: string | null;
|
||||
}>;
|
||||
|
||||
export type UserPartial = Readonly<{
|
||||
id: string;
|
||||
username: string;
|
||||
discriminator: string;
|
||||
global_name?: string | null;
|
||||
avatar: string | null;
|
||||
avatar_color?: number | null;
|
||||
bot?: boolean;
|
||||
system?: boolean;
|
||||
flags: number;
|
||||
}>;
|
||||
|
||||
export type RequiredAction =
|
||||
| 'REQUIRE_VERIFIED_EMAIL'
|
||||
| 'REQUIRE_REVERIFIED_EMAIL'
|
||||
| 'REQUIRE_VERIFIED_PHONE'
|
||||
| 'REQUIRE_REVERIFIED_PHONE'
|
||||
| 'REQUIRE_VERIFIED_EMAIL_OR_VERIFIED_PHONE'
|
||||
| 'REQUIRE_REVERIFIED_EMAIL_OR_VERIFIED_PHONE'
|
||||
| 'REQUIRE_VERIFIED_EMAIL_OR_REVERIFIED_PHONE'
|
||||
| 'REQUIRE_REVERIFIED_EMAIL_OR_REVERIFIED_PHONE';
|
||||
|
||||
export type UserPrivate = Readonly<
|
||||
UserPartial &
|
||||
UserProfile & {
|
||||
email: string | null;
|
||||
mfa_enabled: boolean;
|
||||
phone: string | null;
|
||||
authenticator_types: Array<number>;
|
||||
verified: boolean;
|
||||
premium_type: number | null;
|
||||
premium_since: string | null;
|
||||
premium_until: string | null;
|
||||
premium_will_cancel: boolean;
|
||||
premium_billing_cycle: string | null;
|
||||
premium_lifetime_sequence: number | null;
|
||||
premium_badge_hidden: boolean;
|
||||
premium_badge_masked: boolean;
|
||||
premium_badge_timestamp_hidden: boolean;
|
||||
premium_badge_sequence_hidden: boolean;
|
||||
premium_purchase_disabled: boolean;
|
||||
premium_enabled_override: boolean;
|
||||
password_last_changed_at: string | null;
|
||||
required_actions: Array<RequiredAction> | null;
|
||||
nsfw_allowed: boolean;
|
||||
pending_manual_verification: boolean;
|
||||
pending_bulk_message_deletion: {
|
||||
scheduled_at: string;
|
||||
channel_count: number;
|
||||
message_count: number;
|
||||
} | null;
|
||||
has_dismissed_premium_onboarding: boolean;
|
||||
has_ever_purchased: boolean;
|
||||
has_unread_gift_inventory: boolean;
|
||||
unread_gift_inventory_count: number;
|
||||
used_mobile_client: boolean;
|
||||
}
|
||||
>;
|
||||
|
||||
export type User = Readonly<UserPartial & Partial<UserPrivate>>;
|
||||
MAX_PRIVATE_CHANNELS_PER_USER,
|
||||
MAX_RELATIONSHIPS,
|
||||
} from '@fluxer/constants/src/LimitConstants';
|
||||
import {PublicUserFlags} from '@fluxer/constants/src/UserConstants';
|
||||
import type {RequiredAction, User, UserPartial, UserPrivate} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
||||
import * as SnowflakeUtils from '@fluxer/snowflake/src/SnowflakeUtils';
|
||||
|
||||
export type PendingBulkMessageDeletion = Readonly<{
|
||||
scheduledAt: Date;
|
||||
@@ -115,7 +44,12 @@ export type PendingBulkMessageDeletion = Readonly<{
|
||||
messageCount: number;
|
||||
}>;
|
||||
|
||||
interface UserRecordOptions {
|
||||
instanceId?: string;
|
||||
}
|
||||
|
||||
export class UserRecord {
|
||||
readonly instanceId: string;
|
||||
readonly id: string;
|
||||
readonly username: string;
|
||||
readonly discriminator: string;
|
||||
@@ -126,14 +60,15 @@ export class UserRecord {
|
||||
readonly system: boolean;
|
||||
readonly flags: number;
|
||||
private readonly _email?: string | null;
|
||||
private readonly _emailBounced?: boolean;
|
||||
readonly bio?: string | null;
|
||||
readonly banner?: string | null;
|
||||
readonly bannerColor?: number | null;
|
||||
readonly pronouns?: string | null;
|
||||
readonly accentColor?: string | null;
|
||||
readonly accentColor?: number | null;
|
||||
readonly mfaEnabled?: boolean;
|
||||
readonly phone?: string | null;
|
||||
readonly authenticatorTypes?: Array<number>;
|
||||
readonly authenticatorTypes?: ReadonlyArray<number>;
|
||||
private readonly _verified?: boolean;
|
||||
private readonly _premiumType?: number | null;
|
||||
private readonly _premiumSince?: Date | null;
|
||||
@@ -149,7 +84,6 @@ export class UserRecord {
|
||||
readonly premiumEnabledOverride?: boolean;
|
||||
readonly passwordLastChangedAt?: Date | null;
|
||||
readonly requiredActions?: Array<RequiredAction> | null;
|
||||
readonly pendingManualVerification?: boolean;
|
||||
readonly pendingBulkMessageDeletion: PendingBulkMessageDeletion | null;
|
||||
private readonly _nsfwAllowed?: boolean;
|
||||
readonly hasDismissedPremiumOnboarding?: boolean;
|
||||
@@ -157,8 +91,10 @@ export class UserRecord {
|
||||
private readonly _hasUnreadGiftInventory?: boolean;
|
||||
private readonly _unreadGiftInventoryCount?: number;
|
||||
private readonly _usedMobileClient?: boolean;
|
||||
private readonly _traits: ReadonlyArray<string>;
|
||||
|
||||
constructor(user: User) {
|
||||
constructor(user: User, options?: UserRecordOptions) {
|
||||
this.instanceId = options?.instanceId ?? RuntimeConfigStore.localInstanceDomain;
|
||||
this.id = user.id;
|
||||
this.username = user.username;
|
||||
this.discriminator = user.discriminator;
|
||||
@@ -170,6 +106,7 @@ export class UserRecord {
|
||||
this.flags = user.flags;
|
||||
|
||||
if ('email' in user) this._email = user.email;
|
||||
if ('email_bounced' in user) this._emailBounced = user.email_bounced;
|
||||
if ('bio' in user) this.bio = user.bio;
|
||||
if ('banner' in user) this.banner = user.banner;
|
||||
if ('banner_color' in user) this.bannerColor = user.banner_color;
|
||||
@@ -195,9 +132,9 @@ export class UserRecord {
|
||||
if ('password_last_changed_at' in user)
|
||||
this.passwordLastChangedAt = user.password_last_changed_at ? new Date(user.password_last_changed_at) : null;
|
||||
if ('required_actions' in user) {
|
||||
this.requiredActions = user.required_actions && user.required_actions.length > 0 ? user.required_actions : null;
|
||||
const actions = user.required_actions;
|
||||
this.requiredActions = actions && actions.length > 0 ? [...actions] : null;
|
||||
}
|
||||
if ('pending_manual_verification' in user) this.pendingManualVerification = user.pending_manual_verification;
|
||||
if ('pending_bulk_message_deletion' in user && user.pending_bulk_message_deletion) {
|
||||
this.pendingBulkMessageDeletion = {
|
||||
scheduledAt: new Date(user.pending_bulk_message_deletion.scheduled_at),
|
||||
@@ -214,6 +151,11 @@ export class UserRecord {
|
||||
if ('has_unread_gift_inventory' in user) this._hasUnreadGiftInventory = user.has_unread_gift_inventory;
|
||||
if ('unread_gift_inventory_count' in user) this._unreadGiftInventoryCount = user.unread_gift_inventory_count;
|
||||
if ('used_mobile_client' in user) this._usedMobileClient = user.used_mobile_client;
|
||||
if ('traits' in user) {
|
||||
this._traits = Object.freeze((user.traits ?? []).slice());
|
||||
} else {
|
||||
this._traits = [];
|
||||
}
|
||||
}
|
||||
|
||||
get email(): string | null | undefined {
|
||||
@@ -223,6 +165,10 @@ export class UserRecord {
|
||||
return this._email;
|
||||
}
|
||||
|
||||
get emailBounced(): boolean | undefined {
|
||||
return this._emailBounced;
|
||||
}
|
||||
|
||||
get verified(): boolean | undefined {
|
||||
const verifiedOverride = DeveloperOptionsStore.emailVerifiedOverride;
|
||||
if (verifiedOverride != null) {
|
||||
@@ -287,12 +233,15 @@ export class UserRecord {
|
||||
return this._usedMobileClient;
|
||||
}
|
||||
|
||||
withUpdates(updates: Partial<User>): UserRecord {
|
||||
withUpdates(updates: Partial<User>, options?: {clearMissingOptionalFields?: boolean}): UserRecord {
|
||||
const clearMissingOptionalFields = options?.clearMissingOptionalFields ?? false;
|
||||
const baseFields: UserPartial = {
|
||||
id: updates.id ?? this.id,
|
||||
username: updates.username ?? this.username,
|
||||
discriminator: updates.discriminator ?? this.discriminator,
|
||||
global_name: 'global_name' in updates ? (updates.global_name as string | null) : this.globalName,
|
||||
avatar: 'avatar' in updates ? (updates.avatar as string | null) : this.avatar,
|
||||
avatar_color: 'avatar_color' in updates ? (updates.avatar_color as number | null) : (this.avatarColor ?? null),
|
||||
bot: updates.bot ?? this.bot,
|
||||
system: updates.system ?? this.system,
|
||||
flags: updates.flags ?? this.flags,
|
||||
@@ -311,6 +260,9 @@ export class UserRecord {
|
||||
|
||||
const privateFields: Partial<UserPrivate> = {
|
||||
...(this._email !== undefined || updates.email !== undefined ? {email: updates.email ?? this._email} : {}),
|
||||
...(this._emailBounced !== undefined || updates.email_bounced !== undefined
|
||||
? {email_bounced: updates.email_bounced ?? this._emailBounced}
|
||||
: {}),
|
||||
...(this.bio !== undefined || 'bio' in updates
|
||||
? {bio: 'bio' in updates && updates.bio !== undefined ? (updates.bio as string | null) : this.bio}
|
||||
: {}),
|
||||
@@ -356,7 +308,7 @@ export class UserRecord {
|
||||
? {
|
||||
accent_color:
|
||||
'accent_color' in updates && updates.accent_color !== undefined
|
||||
? (updates.accent_color as string | null)
|
||||
? (updates.accent_color as number | null)
|
||||
: this.accentColor,
|
||||
}
|
||||
: {}),
|
||||
@@ -414,21 +366,18 @@ export class UserRecord {
|
||||
updates.password_last_changed_at ?? this.passwordLastChangedAt?.toISOString() ?? null,
|
||||
}
|
||||
: {}),
|
||||
...(this.pendingManualVerification !== undefined || updates.pending_manual_verification !== undefined
|
||||
? {
|
||||
pending_manual_verification: updates.pending_manual_verification ?? this.pendingManualVerification,
|
||||
}
|
||||
: {}),
|
||||
pending_bulk_message_deletion: pendingBulkMessageDeletionValue,
|
||||
...(this.requiredActions !== undefined || 'required_actions' in updates
|
||||
? {
|
||||
required_actions:
|
||||
'required_actions' in updates
|
||||
? updates.required_actions && (updates.required_actions as Array<RequiredAction>).length > 0
|
||||
? 'required_actions' in updates
|
||||
? {
|
||||
required_actions:
|
||||
updates.required_actions && (updates.required_actions as Array<RequiredAction>).length > 0
|
||||
? (updates.required_actions as Array<RequiredAction>)
|
||||
: null
|
||||
: this.requiredActions,
|
||||
}
|
||||
: null,
|
||||
}
|
||||
: clearMissingOptionalFields
|
||||
? {}
|
||||
: {required_actions: this.requiredActions}
|
||||
: {}),
|
||||
...(this._nsfwAllowed !== undefined || updates.nsfw_allowed !== undefined
|
||||
? {nsfw_allowed: updates.nsfw_allowed ?? this._nsfwAllowed}
|
||||
@@ -451,12 +400,16 @@ export class UserRecord {
|
||||
...(this._usedMobileClient !== undefined || updates.used_mobile_client !== undefined
|
||||
? {used_mobile_client: updates.used_mobile_client ?? this._usedMobileClient}
|
||||
: {}),
|
||||
traits: updates.traits ?? [...this.traits],
|
||||
};
|
||||
|
||||
return new UserRecord({
|
||||
...baseFields,
|
||||
...privateFields,
|
||||
});
|
||||
return new UserRecord(
|
||||
{
|
||||
...baseFields,
|
||||
...privateFields,
|
||||
},
|
||||
{instanceId: this.instanceId},
|
||||
);
|
||||
}
|
||||
|
||||
get displayName(): string {
|
||||
@@ -471,39 +424,70 @@ export class UserRecord {
|
||||
return new Date(SnowflakeUtils.extractTimestamp(this.id));
|
||||
}
|
||||
|
||||
get traits(): ReadonlyArray<string> {
|
||||
return this._traits;
|
||||
}
|
||||
|
||||
isPremium(): boolean {
|
||||
if (RuntimeConfigStore.isSelfHosted()) {
|
||||
return true;
|
||||
}
|
||||
return this.premiumType != null && this.premiumType > 0;
|
||||
}
|
||||
|
||||
get maxGuilds(): number {
|
||||
return this.isPremium() ? MAX_GUILDS_PREMIUM : MAX_GUILDS_NON_PREMIUM;
|
||||
return this.resolveRuntimeLimit('max_guilds', MAX_GUILDS_NON_PREMIUM);
|
||||
}
|
||||
|
||||
get maxMessageLength(): number {
|
||||
return this.isPremium() ? MAX_MESSAGE_LENGTH_PREMIUM : MAX_MESSAGE_LENGTH_NON_PREMIUM;
|
||||
return this.resolveRuntimeLimit('max_message_length', MAX_MESSAGE_LENGTH_NON_PREMIUM);
|
||||
}
|
||||
|
||||
get maxAttachmentSize(): number {
|
||||
return getAttachmentMaxSize(this.isPremium());
|
||||
get maxAttachmentFileSize(): number {
|
||||
return this.resolveRuntimeLimit('max_attachment_file_size', getMaxAttachmentFileSize());
|
||||
}
|
||||
|
||||
get maxAttachmentsPerMessage(): number {
|
||||
return this.resolveRuntimeLimit('max_attachments_per_message', MAX_ATTACHMENTS_PER_MESSAGE);
|
||||
}
|
||||
|
||||
get maxBioLength(): number {
|
||||
return this.isPremium() ? MAX_BIO_LENGTH_PREMIUM : MAX_BIO_LENGTH_NON_PREMIUM;
|
||||
return this.resolveRuntimeLimit('max_bio_length', MAX_BIO_LENGTH);
|
||||
}
|
||||
|
||||
get maxBookmarks(): number {
|
||||
return this.isPremium() ? MAX_BOOKMARKS_PREMIUM : MAX_BOOKMARKS_NON_PREMIUM;
|
||||
return this.resolveRuntimeLimit('max_bookmarks', MAX_BOOKMARKS_NON_PREMIUM);
|
||||
}
|
||||
|
||||
get maxFavoriteMemes(): number {
|
||||
return this.isPremium() ? MAX_FAVORITE_MEMES_PREMIUM : MAX_FAVORITE_MEMES_NON_PREMIUM;
|
||||
return this.resolveRuntimeLimit('max_favorite_memes', MAX_FAVORITE_MEMES_NON_PREMIUM);
|
||||
}
|
||||
|
||||
get maxFavoriteMemeTags(): number {
|
||||
return this.resolveRuntimeLimit('max_favorite_meme_tags', MAX_FAVORITE_MEME_TAGS);
|
||||
}
|
||||
|
||||
get maxGroupDmRecipients(): number {
|
||||
return this.resolveRuntimeLimit('max_group_dm_recipients', MAX_GROUP_DM_RECIPIENTS);
|
||||
}
|
||||
|
||||
get maxPrivateChannels(): number {
|
||||
return this.resolveRuntimeLimit('max_private_channels_per_user', MAX_PRIVATE_CHANNELS_PER_USER);
|
||||
}
|
||||
|
||||
get maxRelationships(): number {
|
||||
return this.resolveRuntimeLimit('max_relationships', MAX_RELATIONSHIPS);
|
||||
}
|
||||
|
||||
private resolveRuntimeLimit(key: LimitKey, fallback: number): number {
|
||||
return LimitResolver.resolve({
|
||||
key,
|
||||
fallback,
|
||||
context: {
|
||||
traits: this.traits,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
isStaff(): boolean {
|
||||
return (this.flags & UserFlags.STAFF) !== 0;
|
||||
return (this.flags & PublicUserFlags.STAFF) !== 0;
|
||||
}
|
||||
|
||||
isClaimed(): boolean {
|
||||
@@ -512,6 +496,7 @@ export class UserRecord {
|
||||
|
||||
equals(other: UserRecord): boolean {
|
||||
return (
|
||||
this.instanceId === other.instanceId &&
|
||||
this.id === other.id &&
|
||||
this.username === other.username &&
|
||||
this.discriminator === other.discriminator &&
|
||||
@@ -521,6 +506,7 @@ export class UserRecord {
|
||||
this.system === other.system &&
|
||||
this.flags === other.flags &&
|
||||
this._email === other._email &&
|
||||
this._emailBounced === other._emailBounced &&
|
||||
this.bio === other.bio &&
|
||||
this.banner === other.banner &&
|
||||
this.bannerColor === other.bannerColor &&
|
||||
@@ -543,7 +529,6 @@ export class UserRecord {
|
||||
this.premiumEnabledOverride === other.premiumEnabledOverride &&
|
||||
this.passwordLastChangedAt?.getTime() === other.passwordLastChangedAt?.getTime() &&
|
||||
JSON.stringify(this.requiredActions) === JSON.stringify(other.requiredActions) &&
|
||||
this.pendingManualVerification === other.pendingManualVerification &&
|
||||
((this.pendingBulkMessageDeletion === null && other.pendingBulkMessageDeletion === null) ||
|
||||
(this.pendingBulkMessageDeletion != null &&
|
||||
other.pendingBulkMessageDeletion != null &&
|
||||
@@ -555,7 +540,8 @@ export class UserRecord {
|
||||
this.hasDismissedPremiumOnboarding === other.hasDismissedPremiumOnboarding &&
|
||||
this._hasUnreadGiftInventory === other._hasUnreadGiftInventory &&
|
||||
this._unreadGiftInventoryCount === other._unreadGiftInventoryCount &&
|
||||
this._usedMobileClient === other._usedMobileClient
|
||||
this._usedMobileClient === other._usedMobileClient &&
|
||||
JSON.stringify(this.traits) === JSON.stringify(other.traits)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -572,6 +558,7 @@ export class UserRecord {
|
||||
discriminator: this.discriminator,
|
||||
global_name: this.globalName,
|
||||
avatar: this.avatar,
|
||||
avatar_color: this.avatarColor ?? null,
|
||||
bot: this.bot,
|
||||
system: this.system,
|
||||
flags: this.flags,
|
||||
@@ -579,6 +566,7 @@ export class UserRecord {
|
||||
|
||||
const privateFields: Partial<UserPrivate> = {
|
||||
...(this._email !== undefined ? {email: this._email} : {}),
|
||||
...(this._emailBounced !== undefined ? {email_bounced: this._emailBounced} : {}),
|
||||
...(this.bio !== undefined ? {bio: this.bio} : {}),
|
||||
...(this.banner !== undefined ? {banner: this.banner} : {}),
|
||||
...(this.avatarColor !== undefined ? {avatar_color: this.avatarColor} : {}),
|
||||
@@ -609,9 +597,6 @@ export class UserRecord {
|
||||
? {password_last_changed_at: normalizeDate(this.passwordLastChangedAt)}
|
||||
: {}),
|
||||
...(this.requiredActions !== undefined ? {required_actions: this.requiredActions} : {}),
|
||||
...(this.pendingManualVerification !== undefined
|
||||
? {pending_manual_verification: this.pendingManualVerification}
|
||||
: {}),
|
||||
...(this.pendingBulkMessageDeletion !== undefined
|
||||
? {
|
||||
pending_bulk_message_deletion: this.pendingBulkMessageDeletion
|
||||
@@ -632,6 +617,7 @@ export class UserRecord {
|
||||
? {unread_gift_inventory_count: this._unreadGiftInventoryCount}
|
||||
: {}),
|
||||
...(this._usedMobileClient !== undefined ? {used_mobile_client: this._usedMobileClient} : {}),
|
||||
traits: [...this.traits],
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -17,20 +17,12 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {UserPartial, UserRecord} from '~/records/UserRecord';
|
||||
import UserStore from '~/stores/UserStore';
|
||||
import * as SnowflakeUtils from '~/utils/SnowflakeUtils';
|
||||
import {webhookUrl} from '~/utils/UrlUtils';
|
||||
|
||||
export type Webhook = Readonly<{
|
||||
id: string;
|
||||
guild_id: string;
|
||||
channel_id: string;
|
||||
user: UserPartial;
|
||||
name: string;
|
||||
avatar: string | null;
|
||||
token: string;
|
||||
}>;
|
||||
import type {UserRecord} from '@app/records/UserRecord';
|
||||
import UserStore from '@app/stores/UserStore';
|
||||
import {webhookUrl} from '@app/utils/UrlUtils';
|
||||
import type {UserPartial} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
||||
import type {Webhook} from '@fluxer/schema/src/domains/webhook/WebhookSchemas';
|
||||
import * as SnowflakeUtils from '@fluxer/snowflake/src/SnowflakeUtils';
|
||||
|
||||
export class WebhookRecord {
|
||||
readonly id: string;
|
||||
|
||||
Reference in New Issue
Block a user