mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-25 03:49:11 +00:00
feat(api): add get-picker-data; refactor fully away from old gRPC datatypes
This commit is contained in:
parent
823760dc2f
commit
0b384bfe5c
22 changed files with 359 additions and 151 deletions
|
@ -63,7 +63,7 @@ const server = http.createServer((req, res) => {
|
||||||
}
|
}
|
||||||
res.statusCode = response.status;
|
res.statusCode = response.status;
|
||||||
loggedStatus = String(response.status);
|
loggedStatus = String(response.status);
|
||||||
Object.entries(response.headers).forEach(([k, v]) => res.setHeader(k, v));
|
response.headers.forEach((value, key) => res.setHeader(key, value));
|
||||||
res.end(response.body);
|
res.end(response.body);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
78
src/backend-worker/handlers/get-picker-data.ts
Normal file
78
src/backend-worker/handlers/get-picker-data.ts
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import {
|
||||||
|
DiscordUser,
|
||||||
|
GuildSlug,
|
||||||
|
PresentableGuild,
|
||||||
|
SessionData,
|
||||||
|
} from 'roleypoly/common/types';
|
||||||
|
import { respond, withSession } from '../utils/api-tools';
|
||||||
|
import { getGuild, getGuildData, getGuildMemberRoles } from '../utils/guild';
|
||||||
|
|
||||||
|
const fail = respond(
|
||||||
|
{
|
||||||
|
error: 'guild not found',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 404,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const GetPickerData = withSession(
|
||||||
|
(session?: SessionData) => async (request: Request): Promise<Response> => {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const [, , guildID] = url.pathname.split('/');
|
||||||
|
|
||||||
|
if (!guildID) {
|
||||||
|
return respond(
|
||||||
|
{
|
||||||
|
error: 'missing guild id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id: userID } = session?.user as DiscordUser;
|
||||||
|
const guilds = session?.guilds as GuildSlug[];
|
||||||
|
|
||||||
|
// Save a Discord API request by checking if this user is a member by session first
|
||||||
|
const checkGuild = guilds.find((guild) => guild.id === guildID);
|
||||||
|
if (!checkGuild) {
|
||||||
|
return fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
const guild = await getGuild(guildID, {
|
||||||
|
skipCachePull: url.searchParams.has('__no_cache'),
|
||||||
|
});
|
||||||
|
if (!guild) {
|
||||||
|
return fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberRolesP = getGuildMemberRoles({
|
||||||
|
serverID: guildID,
|
||||||
|
userID,
|
||||||
|
});
|
||||||
|
|
||||||
|
const guildDataP = getGuildData(guildID);
|
||||||
|
|
||||||
|
const [guildData, memberRoles] = await Promise.all([guildDataP, memberRolesP]);
|
||||||
|
if (!memberRoles) {
|
||||||
|
return fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
const presentableGuild: PresentableGuild = {
|
||||||
|
id: guildID,
|
||||||
|
guild: checkGuild,
|
||||||
|
roles: guild.roles,
|
||||||
|
member: {
|
||||||
|
roles: memberRoles,
|
||||||
|
},
|
||||||
|
data: guildData,
|
||||||
|
};
|
||||||
|
|
||||||
|
return respond(presentableGuild);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mustAuthenticate: true,
|
||||||
|
}
|
||||||
|
);
|
|
@ -120,11 +120,15 @@ const getGuilds = async (accessToken: string) => {
|
||||||
'Bearer'
|
'Bearer'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!guilds) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const guildSlugs = guilds.map<GuildSlug>((guild) => ({
|
const guildSlugs = guilds.map<GuildSlug>((guild) => ({
|
||||||
id: guild.id,
|
id: guild.id,
|
||||||
name: guild.name,
|
name: guild.name,
|
||||||
icon: guild.icon,
|
icon: guild.icon,
|
||||||
permissionLevel: parsePermissions(guild.permissions, guild.owner),
|
permissionLevel: parsePermissions(BigInt(guild.permissions), guild.owner),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return guildSlugs;
|
return guildSlugs;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { BotJoin } from './handlers/bot-join';
|
import { BotJoin } from './handlers/bot-join';
|
||||||
|
import { GetPickerData } from './handlers/get-picker-data';
|
||||||
import { GetSession } from './handlers/get-session';
|
import { GetSession } from './handlers/get-session';
|
||||||
import { GetSlug } from './handlers/get-slug';
|
import { GetSlug } from './handlers/get-slug';
|
||||||
import { LoginBounce } from './handlers/login-bounce';
|
import { LoginBounce } from './handlers/login-bounce';
|
||||||
|
@ -16,6 +17,7 @@ router.add('GET', 'login-bounce', LoginBounce);
|
||||||
router.add('GET', 'login-callback', LoginCallback);
|
router.add('GET', 'login-callback', LoginCallback);
|
||||||
router.add('GET', 'get-session', GetSession);
|
router.add('GET', 'get-session', GetSession);
|
||||||
router.add('GET', 'get-slug', GetSlug);
|
router.add('GET', 'get-slug', GetSlug);
|
||||||
|
router.add('GET', 'get-picker-data', GetPickerData);
|
||||||
router.add('GET', 'x-headers', (request) => {
|
router.add('GET', 'x-headers', (request) => {
|
||||||
const headers: { [x: string]: string } = {};
|
const headers: { [x: string]: string } = {};
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ export const resolveFailures = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parsePermissions = (
|
export const parsePermissions = (
|
||||||
permissions: number,
|
permissions: bigint,
|
||||||
owner: boolean = false
|
owner: boolean = false
|
||||||
): UserGuildPermissions => {
|
): UserGuildPermissions => {
|
||||||
if (owner || evaluatePermission(permissions, Permissions.ADMINISTRATOR)) {
|
if (owner || evaluatePermission(permissions, Permissions.ADMINISTRATOR)) {
|
||||||
|
@ -56,6 +56,9 @@ export const getSessionID = (request: Request): { type: string; id: string } | n
|
||||||
return { type, id };
|
return { type, id };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const userAgent =
|
||||||
|
'DiscordBot (https://github.com/roleypoly/roleypoly, git-main) (+https://roleypoly.com)';
|
||||||
|
|
||||||
export const discordFetch = async <T>(
|
export const discordFetch = async <T>(
|
||||||
url: string,
|
url: string,
|
||||||
auth: string,
|
auth: string,
|
||||||
|
@ -64,8 +67,7 @@ export const discordFetch = async <T>(
|
||||||
const response = await fetch('https://discord.com/api/v8' + url, {
|
const response = await fetch('https://discord.com/api/v8' + url, {
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `${authType} ${auth}`,
|
authorization: `${authType} ${auth}`,
|
||||||
'user-agent':
|
'user-agent': userAgent,
|
||||||
'DiscordBot (https://github.com/roleypoly/roleypoly, git-main) (+https://roleypoly.com)',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -81,12 +83,17 @@ export const cacheLayer = <Identity, Data>(
|
||||||
keyFactory: (identity: Identity) => string,
|
keyFactory: (identity: Identity) => string,
|
||||||
missHandler: (identity: Identity) => Promise<Data | null>,
|
missHandler: (identity: Identity) => Promise<Data | null>,
|
||||||
ttlSeconds?: number
|
ttlSeconds?: number
|
||||||
) => async (identity: Identity): Promise<Data | null> => {
|
) => async (
|
||||||
|
identity: Identity,
|
||||||
|
options: { skipCachePull?: boolean } = {}
|
||||||
|
): Promise<Data | null> => {
|
||||||
const key = keyFactory(identity);
|
const key = keyFactory(identity);
|
||||||
|
|
||||||
const value = await kv.get<Data>(key);
|
if (!options.skipCachePull) {
|
||||||
if (value) {
|
const value = await kv.get<Data>(key);
|
||||||
return value;
|
if (value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fallbackValue = await missHandler(identity);
|
const fallbackValue = await missHandler(identity);
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
import { Guild, Member, Role, RoleSafety } from 'roleypoly/common/types';
|
import {
|
||||||
|
Features,
|
||||||
|
Guild,
|
||||||
|
GuildData as GuildDataT,
|
||||||
|
OwnRoleInfo,
|
||||||
|
Role,
|
||||||
|
RoleSafety,
|
||||||
|
} from 'roleypoly/common/types';
|
||||||
|
import { evaluatePermission, permissions } from 'roleypoly/common/utils/hasPermission';
|
||||||
import { cacheLayer, discordFetch } from './api-tools';
|
import { cacheLayer, discordFetch } from './api-tools';
|
||||||
import { botToken } from './config';
|
import { botClientID, botToken } from './config';
|
||||||
import { Guilds } from './kv';
|
import { GuildData, Guilds } from './kv';
|
||||||
|
|
||||||
type APIGuild = {
|
type APIGuild = {
|
||||||
// Only relevant stuff
|
// Only relevant stuff
|
||||||
|
@ -30,24 +38,112 @@ export const getGuild = cacheLayer(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const botMemberRoles =
|
||||||
|
(await getGuildMemberRoles({
|
||||||
|
serverID: id,
|
||||||
|
userID: botClientID,
|
||||||
|
})) || [];
|
||||||
|
|
||||||
|
const highestRolePosition = botMemberRoles.reduce<number>((highest, roleID) => {
|
||||||
|
const role = guildRaw.roles.find((guildRole) => guildRole.id === roleID);
|
||||||
|
if (!role) {
|
||||||
|
return highest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If highest is a bigger number, it stays the highest.
|
||||||
|
if (highest > role.position) {
|
||||||
|
return highest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return role.position;
|
||||||
|
}, guildRaw.roles.length - 1);
|
||||||
|
|
||||||
|
const roles = guildRaw.roles.map<Role>((role) => ({
|
||||||
|
id: role.id,
|
||||||
|
name: role.name,
|
||||||
|
color: role.color,
|
||||||
|
managed: role.managed,
|
||||||
|
position: role.position,
|
||||||
|
permissions: role.permissions,
|
||||||
|
safety: calculateRoleSafety(role, highestRolePosition),
|
||||||
|
}));
|
||||||
|
|
||||||
// Filters the raw guild data into data we actually want
|
// Filters the raw guild data into data we actually want
|
||||||
const guild: Guild = {
|
const guild: Guild & OwnRoleInfo = {
|
||||||
id: guildRaw.id,
|
id: guildRaw.id,
|
||||||
name: guildRaw.name,
|
name: guildRaw.name,
|
||||||
icon: guildRaw.icon,
|
icon: guildRaw.icon,
|
||||||
roles: guildRaw.roles.map<Role>((role) => ({
|
roles,
|
||||||
...role,
|
highestRolePosition,
|
||||||
safety: RoleSafety.SAFE, // TODO: calculate safety
|
|
||||||
})),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return guild;
|
return guild;
|
||||||
}
|
},
|
||||||
|
60 * 60 * 2 // 2 hour TTL
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getGuildMember = async (
|
type GuildMemberIdentity = {
|
||||||
serverID: string,
|
serverID: string;
|
||||||
userID: string
|
userID: string;
|
||||||
): Promise<Member> => {
|
};
|
||||||
return {} as any;
|
|
||||||
|
type APIMember = {
|
||||||
|
// Only relevant stuff, again.
|
||||||
|
roles: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGuildMemberRoles = cacheLayer<GuildMemberIdentity, Role['id'][]>(
|
||||||
|
Guilds,
|
||||||
|
({ serverID, userID }) => `guilds/${serverID}/members/${userID}`,
|
||||||
|
async ({ serverID, userID }) => {
|
||||||
|
const discordMember = await discordFetch<APIMember>(
|
||||||
|
`/guilds/${serverID}/members/${userID}`,
|
||||||
|
botToken,
|
||||||
|
'Bot'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!discordMember) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return discordMember.roles;
|
||||||
|
},
|
||||||
|
60 * 5 // 5 minute TTL
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getGuildData = async (id: string): Promise<GuildDataT> => {
|
||||||
|
const guildData = await GuildData.get<GuildDataT>(id);
|
||||||
|
|
||||||
|
if (!guildData) {
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
message: '',
|
||||||
|
categories: [],
|
||||||
|
features: Features.None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return guildData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateRoleSafety = (role: Role | APIRole, highestBotRolePosition: number) => {
|
||||||
|
let safety = RoleSafety.Safe;
|
||||||
|
|
||||||
|
if (role.managed) {
|
||||||
|
safety |= RoleSafety.ManagedRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role.position > highestBotRolePosition) {
|
||||||
|
safety |= RoleSafety.HigherThanBot;
|
||||||
|
}
|
||||||
|
|
||||||
|
const permBigInt = BigInt(role.permissions);
|
||||||
|
if (
|
||||||
|
evaluatePermission(permBigInt, permissions.ADMINISTRATOR) ||
|
||||||
|
evaluatePermission(permBigInt, permissions.MANAGE_ROLES)
|
||||||
|
) {
|
||||||
|
safety |= RoleSafety.DangerousPermissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return safety;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
export enum CategoryType {
|
export enum CategoryType {
|
||||||
SINGLE = 0,
|
Single = 0,
|
||||||
MULTI,
|
Multi,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Category = {
|
export type Category = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
rolesList: string[];
|
roles: string[];
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
type: CategoryType;
|
type: CategoryType;
|
||||||
position: number;
|
position: number;
|
||||||
|
|
|
@ -9,34 +9,34 @@ export type Guild = {
|
||||||
roles: Role[];
|
roles: Role[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GuildRoles = {
|
export enum Features {
|
||||||
id: string;
|
None,
|
||||||
rolesList: Role[];
|
Preview = None,
|
||||||
};
|
}
|
||||||
|
|
||||||
export type GuildData = {
|
export type GuildData = {
|
||||||
id: string;
|
id: string;
|
||||||
message: string;
|
message: string;
|
||||||
categoriesList: Category[];
|
categories: Category[];
|
||||||
entitlementsList: string[];
|
features: Features;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PresentableGuild = {
|
export type PresentableGuild = {
|
||||||
id: string;
|
id: string;
|
||||||
guild: Guild;
|
guild: GuildSlug;
|
||||||
member: Member;
|
member: Member;
|
||||||
data: GuildData;
|
data: GuildData;
|
||||||
roles: GuildRoles;
|
roles: Role[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GuildEnumeration = {
|
export type GuildEnumeration = {
|
||||||
guildsList: PresentableGuild[];
|
guilds: PresentableGuild[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum UserGuildPermissions {
|
export enum UserGuildPermissions {
|
||||||
User,
|
User,
|
||||||
Manager,
|
Manager = 1 << 1,
|
||||||
Admin,
|
Admin = 1 << 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GuildSlug = {
|
export type GuildSlug = {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
export enum RoleSafety {
|
export enum RoleSafety {
|
||||||
SAFE = 0,
|
Safe = 0,
|
||||||
HIGHERTHANBOT,
|
HigherThanBot = 1 << 1,
|
||||||
DANGEROUSPERMISSIONS,
|
DangerousPermissions = 1 << 2,
|
||||||
|
ManagedRole = 1 << 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Role = {
|
export type Role = {
|
||||||
|
@ -14,3 +15,7 @@ export type Role = {
|
||||||
/** Permissions is should be used as a BigInt, NOT a number. */
|
/** Permissions is should be used as a BigInt, NOT a number. */
|
||||||
permissions: string;
|
permissions: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type OwnRoleInfo = {
|
||||||
|
highestRolePosition: number;
|
||||||
|
};
|
||||||
|
|
|
@ -7,10 +7,10 @@ export type DiscordUser = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Member = {
|
export type Member = {
|
||||||
guildid: string;
|
guildid?: string;
|
||||||
rolesList: string[];
|
roles: string[];
|
||||||
nick: string;
|
nick?: string;
|
||||||
user: DiscordUser;
|
user?: DiscordUser;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RoleypolyUser = {
|
export type RoleypolyUser = {
|
||||||
|
|
|
@ -2,10 +2,10 @@ import {
|
||||||
Category,
|
Category,
|
||||||
CategoryType,
|
CategoryType,
|
||||||
DiscordUser,
|
DiscordUser,
|
||||||
|
Features,
|
||||||
Guild,
|
Guild,
|
||||||
GuildData,
|
GuildData,
|
||||||
GuildEnumeration,
|
GuildEnumeration,
|
||||||
GuildRoles,
|
|
||||||
GuildSlug,
|
GuildSlug,
|
||||||
Member,
|
Member,
|
||||||
Role,
|
Role,
|
||||||
|
@ -21,7 +21,7 @@ export const roleCategory: Role[] = [
|
||||||
color: 0xffc0cb,
|
color: 0xffc0cb,
|
||||||
position: 1,
|
position: 1,
|
||||||
managed: false,
|
managed: false,
|
||||||
safety: RoleSafety.SAFE,
|
safety: RoleSafety.Safe,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'bbb',
|
id: 'bbb',
|
||||||
|
@ -30,7 +30,7 @@ export const roleCategory: Role[] = [
|
||||||
color: 0xc0ebff,
|
color: 0xc0ebff,
|
||||||
position: 2,
|
position: 2,
|
||||||
managed: false,
|
managed: false,
|
||||||
safety: RoleSafety.SAFE,
|
safety: RoleSafety.Safe,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ccc',
|
id: 'ccc',
|
||||||
|
@ -39,7 +39,7 @@ export const roleCategory: Role[] = [
|
||||||
color: 0xc0ffd5,
|
color: 0xc0ffd5,
|
||||||
position: 3,
|
position: 3,
|
||||||
managed: false,
|
managed: false,
|
||||||
safety: RoleSafety.SAFE,
|
safety: RoleSafety.Safe,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ddd',
|
id: 'ddd',
|
||||||
|
@ -48,7 +48,7 @@ export const roleCategory: Role[] = [
|
||||||
color: 0xff0000,
|
color: 0xff0000,
|
||||||
position: 4,
|
position: 4,
|
||||||
managed: false,
|
managed: false,
|
||||||
safety: RoleSafety.SAFE,
|
safety: RoleSafety.Safe,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'eee',
|
id: 'eee',
|
||||||
|
@ -57,7 +57,7 @@ export const roleCategory: Role[] = [
|
||||||
color: 0x000000,
|
color: 0x000000,
|
||||||
position: 5,
|
position: 5,
|
||||||
managed: false,
|
managed: false,
|
||||||
safety: RoleSafety.SAFE,
|
safety: RoleSafety.Safe,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'fff',
|
id: 'fff',
|
||||||
|
@ -66,7 +66,7 @@ export const roleCategory: Role[] = [
|
||||||
color: 0x1,
|
color: 0x1,
|
||||||
position: 6,
|
position: 6,
|
||||||
managed: false,
|
managed: false,
|
||||||
safety: RoleSafety.SAFE,
|
safety: RoleSafety.Safe,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'unsafe1',
|
id: 'unsafe1',
|
||||||
|
@ -75,7 +75,7 @@ export const roleCategory: Role[] = [
|
||||||
color: 0xff0088,
|
color: 0xff0088,
|
||||||
position: 7,
|
position: 7,
|
||||||
managed: false,
|
managed: false,
|
||||||
safety: RoleSafety.HIGHERTHANBOT,
|
safety: RoleSafety.HigherThanBot,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'unsafe2',
|
id: 'unsafe2',
|
||||||
|
@ -84,16 +84,16 @@ export const roleCategory: Role[] = [
|
||||||
color: 0x00ff88,
|
color: 0x00ff88,
|
||||||
position: 8,
|
position: 8,
|
||||||
managed: false,
|
managed: false,
|
||||||
safety: RoleSafety.DANGEROUSPERMISSIONS,
|
safety: RoleSafety.DangerousPermissions,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const mockCategory: Category = {
|
export const mockCategory: Category = {
|
||||||
id: 'aaa',
|
id: 'aaa',
|
||||||
name: 'Mock',
|
name: 'Mock',
|
||||||
rolesList: roleCategory.map((x) => x.id),
|
roles: roleCategory.map((x) => x.id),
|
||||||
hidden: false,
|
hidden: false,
|
||||||
type: CategoryType.MULTI,
|
type: CategoryType.Multi,
|
||||||
position: 0,
|
position: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ export const roleCategory2: Role[] = [
|
||||||
color: 0xff0000,
|
color: 0xff0000,
|
||||||
position: 9,
|
position: 9,
|
||||||
managed: false,
|
managed: false,
|
||||||
safety: RoleSafety.SAFE,
|
safety: RoleSafety.Safe,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'eee2',
|
id: 'eee2',
|
||||||
|
@ -114,24 +114,19 @@ export const roleCategory2: Role[] = [
|
||||||
color: 0x00ff00,
|
color: 0x00ff00,
|
||||||
position: 10,
|
position: 10,
|
||||||
managed: false,
|
managed: false,
|
||||||
safety: RoleSafety.SAFE,
|
safety: RoleSafety.Safe,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const mockCategorySingle: Category = {
|
export const mockCategorySingle: Category = {
|
||||||
id: 'bbb',
|
id: 'bbb',
|
||||||
name: 'Mock Single 岡野',
|
name: 'Mock Single 岡野',
|
||||||
rolesList: roleCategory2.map((x) => x.id),
|
roles: roleCategory2.map((x) => x.id),
|
||||||
hidden: false,
|
hidden: false,
|
||||||
type: CategoryType.SINGLE,
|
type: CategoryType.Single,
|
||||||
position: 0,
|
position: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const guildRoles: GuildRoles = {
|
|
||||||
id: 'aaa',
|
|
||||||
rolesList: [...roleCategory, ...roleCategory2],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const roleWikiData = {
|
export const roleWikiData = {
|
||||||
aaa: 'Typically used by feminine-identifying people',
|
aaa: 'Typically used by feminine-identifying people',
|
||||||
bbb: 'Typically used by masculine-identifying people',
|
bbb: 'Typically used by masculine-identifying people',
|
||||||
|
@ -145,33 +140,38 @@ export const guild: Guild = {
|
||||||
roles: [],
|
roles: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const guildMap: { [x: string]: Guild } = {
|
export const guildMap: { [x: string]: GuildSlug } = {
|
||||||
'emoji megaporium': guild,
|
'emoji megaporium': {
|
||||||
|
name: guild.name,
|
||||||
|
id: guild.id,
|
||||||
|
permissionLevel: 0,
|
||||||
|
icon: guild.icon,
|
||||||
|
},
|
||||||
Roleypoly: {
|
Roleypoly: {
|
||||||
name: 'Roleypoly',
|
name: 'Roleypoly',
|
||||||
id: '203493697696956418',
|
id: '203493697696956418',
|
||||||
|
permissionLevel: 0,
|
||||||
icon: 'ff08d36f5aee1ff48f8377b65d031ab0',
|
icon: 'ff08d36f5aee1ff48f8377b65d031ab0',
|
||||||
roles: [],
|
|
||||||
},
|
},
|
||||||
'chamber of secrets': {
|
'chamber of secrets': {
|
||||||
name: 'chamber of secrets',
|
name: 'chamber of secrets',
|
||||||
id: 'aaa',
|
id: 'aaa',
|
||||||
|
permissionLevel: 0,
|
||||||
icon: '',
|
icon: '',
|
||||||
roles: [],
|
|
||||||
},
|
},
|
||||||
Eclipse: {
|
Eclipse: {
|
||||||
name: 'Eclipse',
|
name: 'Eclipse',
|
||||||
id: '408821059161423873',
|
id: '408821059161423873',
|
||||||
|
permissionLevel: 0,
|
||||||
icon: '49dfdd8b2456e2977e80a8b577b19c0d',
|
icon: '49dfdd8b2456e2977e80a8b577b19c0d',
|
||||||
roles: [],
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const guildData: GuildData = {
|
export const guildData: GuildData = {
|
||||||
id: 'aaa',
|
id: 'aaa',
|
||||||
message: 'henlo worl!!',
|
message: 'henlo worl!!',
|
||||||
categoriesList: [mockCategory, mockCategorySingle],
|
categories: [mockCategory, mockCategorySingle],
|
||||||
entitlementsList: [],
|
features: Features.None,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const user: DiscordUser = {
|
export const user: DiscordUser = {
|
||||||
|
@ -184,7 +184,7 @@ export const user: DiscordUser = {
|
||||||
|
|
||||||
export const member: Member = {
|
export const member: Member = {
|
||||||
guildid: 'aaa',
|
guildid: 'aaa',
|
||||||
rolesList: ['aaa', 'eee', 'unsafe2', 'ddd2'],
|
roles: ['aaa', 'eee', 'unsafe2', 'ddd2'],
|
||||||
nick: 'okano cat',
|
nick: 'okano cat',
|
||||||
user: user,
|
user: user,
|
||||||
};
|
};
|
||||||
|
@ -194,42 +194,42 @@ export const rpUser: RoleypolyUser = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const guildEnum: GuildEnumeration = {
|
export const guildEnum: GuildEnumeration = {
|
||||||
guildsList: [
|
guilds: [
|
||||||
{
|
{
|
||||||
id: 'aaa',
|
id: 'aaa',
|
||||||
guild: guildMap['emoji megaporium'],
|
guild: guildMap['emoji megaporium'],
|
||||||
member,
|
member,
|
||||||
data: guildData,
|
data: guildData,
|
||||||
roles: guildRoles,
|
roles: [...roleCategory, ...roleCategory2],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'bbb',
|
id: 'bbb',
|
||||||
guild: guildMap['Roleypoly'],
|
guild: guildMap['Roleypoly'],
|
||||||
member: {
|
member: {
|
||||||
...member,
|
...member,
|
||||||
rolesList: ['unsafe2'],
|
roles: ['unsafe2'],
|
||||||
},
|
},
|
||||||
data: guildData,
|
data: guildData,
|
||||||
roles: guildRoles,
|
roles: [...roleCategory, ...roleCategory2],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ccc',
|
id: 'ccc',
|
||||||
guild: guildMap['chamber of secrets'],
|
guild: guildMap['chamber of secrets'],
|
||||||
member,
|
member,
|
||||||
data: guildData,
|
data: guildData,
|
||||||
roles: guildRoles,
|
roles: [...roleCategory, ...roleCategory2],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ddd',
|
id: 'ddd',
|
||||||
guild: guildMap['Eclipse'],
|
guild: guildMap['Eclipse'],
|
||||||
member,
|
member,
|
||||||
data: guildData,
|
data: guildData,
|
||||||
roles: guildRoles,
|
roles: [...roleCategory, ...roleCategory2],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mastheadSlugs: GuildSlug[] = guildEnum.guildsList.map<GuildSlug>(
|
export const mastheadSlugs: GuildSlug[] = guildEnum.guilds.map<GuildSlug>(
|
||||||
(guild, idx) => ({
|
(guild, idx) => ({
|
||||||
id: guild.guild.id,
|
id: guild.guild.id,
|
||||||
name: guild.guild.name,
|
name: guild.guild.name,
|
||||||
|
|
|
@ -1,20 +1,29 @@
|
||||||
import { Role } from 'roleypoly/common/types';
|
import { Role } from 'roleypoly/common/types';
|
||||||
import { guildRoles } from 'roleypoly/common/types/storyData';
|
import { roleCategory } from 'roleypoly/common/types/storyData';
|
||||||
import { hasPermission, hasPermissionOrAdmin, permissions } from './hasPermission';
|
import { hasPermission, hasPermissionOrAdmin } from './hasPermission';
|
||||||
|
|
||||||
|
export const permissions = {
|
||||||
|
KICK_MEMBERS: BigInt(0x2),
|
||||||
|
BAN_MEMBERS: BigInt(0x4),
|
||||||
|
ADMINISTRATOR: BigInt(0x8),
|
||||||
|
SPEAK: BigInt(0x200000),
|
||||||
|
CHANGE_NICKNAME: BigInt(0x4000000),
|
||||||
|
MANAGE_ROLES: BigInt(0x10000000),
|
||||||
|
};
|
||||||
|
|
||||||
const roles: Role[] = [
|
const roles: Role[] = [
|
||||||
{
|
{
|
||||||
...guildRoles.rolesList[0],
|
...roleCategory[0],
|
||||||
permissions: String(permissions.ADMINISTRATOR),
|
permissions: String(permissions.ADMINISTRATOR),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...guildRoles.rolesList[0],
|
...roleCategory[0],
|
||||||
permissions: String(
|
permissions: String(
|
||||||
permissions.SPEAK | permissions.BAN_MEMBERS | permissions.CHANGE_NICKNAME
|
permissions.SPEAK | permissions.BAN_MEMBERS | permissions.CHANGE_NICKNAME
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...guildRoles.rolesList[0],
|
...roleCategory[0],
|
||||||
permissions: String(permissions.BAN_MEMBERS),
|
permissions: String(permissions.BAN_MEMBERS),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -19,35 +19,37 @@ export const hasPermissionOrAdmin = (roles: Role[], permission: bigint): boolean
|
||||||
hasPermission(roles, permission | permissions.ADMINISTRATOR);
|
hasPermission(roles, permission | permissions.ADMINISTRATOR);
|
||||||
|
|
||||||
export const permissions = {
|
export const permissions = {
|
||||||
CREATE_INSTANT_INVITE: BigInt(0x1),
|
// IMPORTANT: Only uncomment what's actually used. All are left for convenience.
|
||||||
KICK_MEMBERS: BigInt(0x2),
|
|
||||||
BAN_MEMBERS: BigInt(0x4),
|
// CREATE_INSTANT_INVITE: BigInt(0x1),
|
||||||
|
// KICK_MEMBERS: BigInt(0x2),
|
||||||
|
// BAN_MEMBERS: BigInt(0x4),
|
||||||
ADMINISTRATOR: BigInt(0x8),
|
ADMINISTRATOR: BigInt(0x8),
|
||||||
MANAGE_CHANNELS: BigInt(0x10),
|
// MANAGE_CHANNELS: BigInt(0x10),
|
||||||
MANAGE_GUILD: BigInt(0x20),
|
// MANAGE_GUILD: BigInt(0x20),
|
||||||
ADD_REACTIONS: BigInt(0x40),
|
// ADD_REACTIONS: BigInt(0x40),
|
||||||
VIEW_AUDIT_LOG: BigInt(0x80),
|
// VIEW_AUDIT_LOG: BigInt(0x80),
|
||||||
VIEW_CHANNEL: BigInt(0x400),
|
// VIEW_CHANNEL: BigInt(0x400),
|
||||||
SEND_MESSAGES: BigInt(0x800),
|
// SEND_MESSAGES: BigInt(0x800),
|
||||||
SEND_TTS_MESSAGES: BigInt(0x1000),
|
// SEND_TTS_MESSAGES: BigInt(0x1000),
|
||||||
MANAGE_MESSAGES: BigInt(0x2000),
|
// MANAGE_MESSAGES: BigInt(0x2000),
|
||||||
EMBED_LINKS: BigInt(0x4000),
|
// EMBED_LINKS: BigInt(0x4000),
|
||||||
ATTACH_FILES: BigInt(0x8000),
|
// ATTACH_FILES: BigInt(0x8000),
|
||||||
READ_MESSAGE_HISTORY: BigInt(0x10000),
|
// READ_MESSAGE_HISTORY: BigInt(0x10000),
|
||||||
MENTION_EVERYONE: BigInt(0x20000),
|
// MENTION_EVERYONE: BigInt(0x20000),
|
||||||
USE_EXTERNAL_EMOJIS: BigInt(0x40000),
|
// USE_EXTERNAL_EMOJIS: BigInt(0x40000),
|
||||||
VIEW_GUILD_INSIGHTS: BigInt(0x80000),
|
// VIEW_GUILD_INSIGHTS: BigInt(0x80000),
|
||||||
CONNECT: BigInt(0x100000),
|
// CONNECT: BigInt(0x100000),
|
||||||
SPEAK: BigInt(0x200000),
|
// SPEAK: BigInt(0x200000),
|
||||||
MUTE_MEMBERS: BigInt(0x400000),
|
// MUTE_MEMBERS: BigInt(0x400000),
|
||||||
DEAFEN_MEMBERS: BigInt(0x800000),
|
// DEAFEN_MEMBERS: BigInt(0x800000),
|
||||||
MOVE_MEMBERS: BigInt(0x1000000),
|
// MOVE_MEMBERS: BigInt(0x1000000),
|
||||||
USE_VAD: BigInt(0x2000000),
|
// USE_VAD: BigInt(0x2000000),
|
||||||
PRIORITY_SPEAKER: BigInt(0x100),
|
// PRIORITY_SPEAKER: BigInt(0x100),
|
||||||
STREAM: BigInt(0x200),
|
// STREAM: BigInt(0x200),
|
||||||
CHANGE_NICKNAME: BigInt(0x4000000),
|
// CHANGE_NICKNAME: BigInt(0x4000000),
|
||||||
MANAGE_NICKNAMES: BigInt(0x8000000),
|
// MANAGE_NICKNAMES: BigInt(0x8000000),
|
||||||
MANAGE_ROLES: BigInt(0x10000000),
|
MANAGE_ROLES: BigInt(0x10000000),
|
||||||
MANAGE_WEBHOOKS: BigInt(0x20000000),
|
// MANAGE_WEBHOOKS: BigInt(0x20000000),
|
||||||
MANAGE_EMOJIS: BigInt(0x40000000),
|
// MANAGE_EMOJIS: BigInt(0x40000000),
|
||||||
};
|
};
|
||||||
|
|
|
@ -65,9 +65,9 @@ export const Role = (props: Props) => {
|
||||||
|
|
||||||
const disabledReason = (role: RPCRole) => {
|
const disabledReason = (role: RPCRole) => {
|
||||||
switch (role.safety) {
|
switch (role.safety) {
|
||||||
case RoleSafety.HIGHERTHANBOT:
|
case RoleSafety.HigherThanBot:
|
||||||
return `This role is above Roleypoly's own role.`;
|
return `This role is above Roleypoly's own role.`;
|
||||||
case RoleSafety.DANGEROUSPERMISSIONS:
|
case RoleSafety.DangerousPermissions:
|
||||||
const rolePermissions = BigInt(role.permissions);
|
const rolePermissions = BigInt(role.permissions);
|
||||||
let permissionHits: string[] = [];
|
let permissionHits: string[] = [];
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const typeEnumToSwitch = (typeData: CategoryType) => {
|
const typeEnumToSwitch = (typeData: CategoryType) => {
|
||||||
if (typeData === CategoryType.SINGLE) {
|
if (typeData === CategoryType.Single) {
|
||||||
return 'Single';
|
return 'Single';
|
||||||
} else {
|
} else {
|
||||||
return 'Multiple';
|
return 'Multiple';
|
||||||
|
@ -28,9 +28,9 @@ const typeEnumToSwitch = (typeData: CategoryType) => {
|
||||||
|
|
||||||
const switchToTypeEnum = (typeData: 'Single' | 'Multiple') => {
|
const switchToTypeEnum = (typeData: 'Single' | 'Multiple') => {
|
||||||
if (typeData === 'Single') {
|
if (typeData === 'Single') {
|
||||||
return CategoryType.SINGLE;
|
return CategoryType.Single;
|
||||||
} else {
|
} else {
|
||||||
return CategoryType.MULTI;
|
return CategoryType.Multi;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,14 +53,14 @@ export const EditorCategory = (props: Props) => {
|
||||||
updateSearchTerm('');
|
updateSearchTerm('');
|
||||||
props.onChange({
|
props.onChange({
|
||||||
...props.category,
|
...props.category,
|
||||||
rolesList: [...props.category.rolesList, role.id],
|
roles: [...props.category.roles, role.id],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRoleDeselect = (role: RoleType) => () => {
|
const handleRoleDeselect = (role: RoleType) => () => {
|
||||||
props.onChange({
|
props.onChange({
|
||||||
...props.category,
|
...props.category,
|
||||||
rolesList: props.category.rolesList.filter((x) => x !== role.id),
|
roles: props.category.roles.filter((x) => x !== role.id),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ export const EditorCategory = (props: Props) => {
|
||||||
onChange={(x) => updateSearchTerm(x.target.value)}
|
onChange={(x) => updateSearchTerm(x.target.value)}
|
||||||
/>
|
/>
|
||||||
<RoleContainer>
|
<RoleContainer>
|
||||||
{props.category.rolesList.map((id) => {
|
{props.category.roles.map((id) => {
|
||||||
const role = props.guildRoles.find((x) => x.id === id);
|
const role = props.guildRoles.find((x) => x.id === id);
|
||||||
if (!role) {
|
if (!role) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|
|
@ -56,7 +56,7 @@ export const PickerCategory = (props: CategoryProps) => (
|
||||||
role={role}
|
role={role}
|
||||||
selected={props.selectedRoles.includes(role.id)}
|
selected={props.selectedRoles.includes(role.id)}
|
||||||
onClick={props.onChange(role)}
|
onClick={props.onChange(role)}
|
||||||
disabled={role.safety !== RoleSafety.SAFE}
|
disabled={role.safety !== RoleSafety.Safe}
|
||||||
tooltipId={props.category.id}
|
tooltipId={props.category.id}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
|
@ -17,12 +17,12 @@ export const EditorShell = (props: Props) => (
|
||||||
|
|
||||||
const RolesTab = (props: Props) => (
|
const RolesTab = (props: Props) => (
|
||||||
<div>
|
<div>
|
||||||
{props.guild.data.categoriesList.map((category, idx) => (
|
{props.guild.data.categories.map((category, idx) => (
|
||||||
<CategoryContainer key={idx}>
|
<CategoryContainer key={idx}>
|
||||||
<EditorCategory
|
<EditorCategory
|
||||||
category={category}
|
category={category}
|
||||||
uncategorizedRoles={[]}
|
uncategorizedRoles={[]}
|
||||||
guildRoles={props.guild.roles.rolesList}
|
guildRoles={props.guild.roles}
|
||||||
onChange={(x) => console.log(x)}
|
onChange={(x) => console.log(x)}
|
||||||
/>
|
/>
|
||||||
</CategoryContainer>
|
</CategoryContainer>
|
||||||
|
|
|
@ -8,9 +8,10 @@ import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
guild,
|
guild,
|
||||||
guildData,
|
guildData,
|
||||||
guildRoles,
|
|
||||||
member,
|
member,
|
||||||
mockCategorySingle,
|
mockCategorySingle,
|
||||||
|
roleCategory,
|
||||||
|
roleCategory2,
|
||||||
} from 'roleypoly/common/types/storyData';
|
} from 'roleypoly/common/types/storyData';
|
||||||
import { Role } from 'roleypoly/design-system/atoms/role';
|
import { Role } from 'roleypoly/design-system/atoms/role';
|
||||||
import { PickerCategory } from 'roleypoly/design-system/molecules/picker-category';
|
import { PickerCategory } from 'roleypoly/design-system/molecules/picker-category';
|
||||||
|
@ -19,9 +20,9 @@ import { RolePicker, RolePickerProps } from './RolePicker';
|
||||||
|
|
||||||
it('unselects the rest of a category in single mode', () => {
|
it('unselects the rest of a category in single mode', () => {
|
||||||
const props: RolePickerProps = {
|
const props: RolePickerProps = {
|
||||||
guildData: { ...guildData, categoriesList: [mockCategorySingle] },
|
guildData: { ...guildData, categories: [mockCategorySingle] },
|
||||||
member: { ...member, rolesList: [] },
|
member: { ...member, roles: [] },
|
||||||
roles: guildRoles,
|
roles: [...roleCategory, ...roleCategory2],
|
||||||
guild: guild,
|
guild: guild,
|
||||||
onSubmit: jest.fn(),
|
onSubmit: jest.fn(),
|
||||||
editable: false,
|
editable: false,
|
||||||
|
@ -34,10 +35,10 @@ it('unselects the rest of a category in single mode', () => {
|
||||||
roles.first().props().onClick?.(true);
|
roles.first().props().onClick?.(true);
|
||||||
|
|
||||||
view.find(ResetSubmit).props().onSubmit();
|
view.find(ResetSubmit).props().onSubmit();
|
||||||
expect(props.onSubmit).toBeCalledWith([mockCategorySingle.rolesList[0]]);
|
expect(props.onSubmit).toBeCalledWith([mockCategorySingle.roles[0]]);
|
||||||
|
|
||||||
roles.last().props().onClick?.(true);
|
roles.at(1).props().onClick?.(true);
|
||||||
|
|
||||||
view.find(ResetSubmit).props().onSubmit();
|
view.find(ResetSubmit).props().onSubmit();
|
||||||
expect(props.onSubmit).toBeCalledWith([mockCategorySingle.rolesList[1]]);
|
expect(props.onSubmit).toBeCalledWith([mockCategorySingle.roles[1]]);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { guild, guildData, guildRoles, member } from 'roleypoly/common/types/storyData';
|
import {
|
||||||
|
guild,
|
||||||
|
guildData,
|
||||||
|
member,
|
||||||
|
roleCategory,
|
||||||
|
roleCategory2,
|
||||||
|
} from 'roleypoly/common/types/storyData';
|
||||||
import { RolePicker, RolePickerProps } from './RolePicker';
|
import { RolePicker, RolePickerProps } from './RolePicker';
|
||||||
|
|
||||||
const props: Partial<RolePickerProps> = {
|
const props: Partial<RolePickerProps> = {
|
||||||
guildData: guildData,
|
guildData: guildData,
|
||||||
member: member,
|
member: member,
|
||||||
guild: guild,
|
guild: guild,
|
||||||
roles: guildRoles,
|
roles: [...roleCategory, ...roleCategory2],
|
||||||
editable: false,
|
editable: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {
|
||||||
CategoryType,
|
CategoryType,
|
||||||
Guild,
|
Guild,
|
||||||
GuildData,
|
GuildData,
|
||||||
GuildRoles,
|
|
||||||
Member,
|
Member,
|
||||||
Role,
|
Role,
|
||||||
} from 'roleypoly/common/types';
|
} from 'roleypoly/common/types';
|
||||||
|
@ -27,7 +26,7 @@ export type RolePickerProps = {
|
||||||
guild: Guild;
|
guild: Guild;
|
||||||
guildData: GuildData;
|
guildData: GuildData;
|
||||||
member: Member;
|
member: Member;
|
||||||
roles: GuildRoles;
|
roles: Role[];
|
||||||
onSubmit: (selectedRoles: string[]) => void;
|
onSubmit: (selectedRoles: string[]) => void;
|
||||||
editable: boolean;
|
editable: boolean;
|
||||||
};
|
};
|
||||||
|
@ -43,15 +42,15 @@ const arrayMatches = (a: any[], b: any[]) => {
|
||||||
|
|
||||||
export const RolePicker = (props: RolePickerProps) => {
|
export const RolePicker = (props: RolePickerProps) => {
|
||||||
const [selectedRoles, updateSelectedRoles] = React.useState<string[]>(
|
const [selectedRoles, updateSelectedRoles] = React.useState<string[]>(
|
||||||
props.member.rolesList
|
props.member.roles
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChange = (category: Category) => (role: Role) => (newState: boolean) => {
|
const handleChange = (category: Category) => (role: Role) => (newState: boolean) => {
|
||||||
if (category.type === CategoryType.SINGLE) {
|
if (category.type === CategoryType.Single) {
|
||||||
updateSelectedRoles(
|
updateSelectedRoles(
|
||||||
newState === true
|
newState === true
|
||||||
? [
|
? [
|
||||||
...selectedRoles.filter((x) => !category.rolesList.includes(x)),
|
...selectedRoles.filter((x) => !category.roles.includes(x)),
|
||||||
role.id,
|
role.id,
|
||||||
]
|
]
|
||||||
: selectedRoles.filter((x) => x !== role.id)
|
: selectedRoles.filter((x) => x !== role.id)
|
||||||
|
@ -79,31 +78,29 @@ export const RolePicker = (props: RolePickerProps) => {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{props.guildData.categoriesList.length !== 0 ? (
|
{props.guildData.categories.length !== 0 ? (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
{props.guildData.categoriesList.map((category, idx) => (
|
{props.guildData.categories.map((category, idx) => (
|
||||||
<CategoryContainer key={idx}>
|
<CategoryContainer key={idx}>
|
||||||
<PickerCategory
|
<PickerCategory
|
||||||
key={idx}
|
key={idx}
|
||||||
category={category}
|
category={category}
|
||||||
title={category.name}
|
title={category.name}
|
||||||
selectedRoles={selectedRoles.filter((roleId) =>
|
selectedRoles={selectedRoles.filter((roleId) =>
|
||||||
category.rolesList.includes(roleId)
|
category.roles.includes(roleId)
|
||||||
)}
|
)}
|
||||||
roles={
|
roles={
|
||||||
category.rolesList
|
category.roles
|
||||||
.map((role) =>
|
.map((role) =>
|
||||||
props.roles.rolesList.find(
|
props.roles.find((r) => r.id === role)
|
||||||
(r) => r.id === role
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.filter((r) => r !== undefined) as Role[]
|
.filter((r) => r !== undefined) as Role[]
|
||||||
}
|
}
|
||||||
onChange={handleChange(category)}
|
onChange={handleChange(category)}
|
||||||
wikiMode={false}
|
wikiMode={false}
|
||||||
type={
|
type={
|
||||||
category.type === CategoryType.SINGLE
|
category.type === CategoryType.Single
|
||||||
? 'single'
|
? 'single'
|
||||||
: 'multi'
|
: 'multi'
|
||||||
}
|
}
|
||||||
|
@ -112,12 +109,12 @@ export const RolePicker = (props: RolePickerProps) => {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<FaderOpacity
|
<FaderOpacity
|
||||||
isVisible={!arrayMatches(selectedRoles, props.member.rolesList)}
|
isVisible={!arrayMatches(selectedRoles, props.member.roles)}
|
||||||
>
|
>
|
||||||
<ResetSubmit
|
<ResetSubmit
|
||||||
onSubmit={() => props.onSubmit(selectedRoles)}
|
onSubmit={() => props.onSubmit(selectedRoles)}
|
||||||
onReset={() => {
|
onReset={() => {
|
||||||
updateSelectedRoles(props.member.rolesList);
|
updateSelectedRoles(props.member.roles);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FaderOpacity>
|
</FaderOpacity>
|
||||||
|
|
|
@ -3,9 +3,10 @@ import {
|
||||||
guild,
|
guild,
|
||||||
guildData,
|
guildData,
|
||||||
guildEnum,
|
guildEnum,
|
||||||
guildRoles,
|
|
||||||
mastheadSlugs,
|
mastheadSlugs,
|
||||||
member,
|
member,
|
||||||
|
roleCategory,
|
||||||
|
roleCategory2,
|
||||||
user,
|
user,
|
||||||
} from 'roleypoly/common/types/storyData';
|
} from 'roleypoly/common/types/storyData';
|
||||||
import { RolePickerTemplate, RolePickerTemplateProps } from './RolePicker';
|
import { RolePickerTemplate, RolePickerTemplateProps } from './RolePicker';
|
||||||
|
@ -19,7 +20,7 @@ const props: RolePickerTemplateProps = {
|
||||||
member: member,
|
member: member,
|
||||||
guild: guild,
|
guild: guild,
|
||||||
guilds: mastheadSlugs,
|
guilds: mastheadSlugs,
|
||||||
roles: guildRoles,
|
roles: [...roleCategory, ...roleCategory2],
|
||||||
editable: false,
|
editable: false,
|
||||||
user: user,
|
user: user,
|
||||||
guildEnumeration: guildEnum,
|
guildEnumeration: guildEnum,
|
||||||
|
|
Loading…
Add table
Reference in a new issue