mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-06-17 09:59:10 +00:00
Refactor node packages to yarn workspaces & ditch next.js for CRA. (#161)
* chore: restructure project into yarn workspaces, remove next * fix tests, remove webapp from terraform * remove more ui deployment bits * remove pages, fix FUNDING.yml * remove isomorphism * remove next providers * fix linting issues * feat: start basis of new web ui system on CRA * chore: move types to @roleypoly/types package * chore: move src/common/utils to @roleypoly/misc-utils * chore: remove roleypoly/ path remappers * chore: renmove vercel config * chore: re-add worker-types to api package * chore: fix type linting scope for api * fix(web): craco should include all of packages dir * fix(ci): change api webpack path for wrangler * chore: remove GAR actions from CI * chore: update codeql job * chore: test better github dar matcher in lint-staged
This commit is contained in:
parent
49e308507e
commit
2ff6588030
328 changed files with 16624 additions and 3525 deletions
|
@ -1,13 +0,0 @@
|
|||
export enum CategoryType {
|
||||
Single = 0,
|
||||
Multi,
|
||||
}
|
||||
|
||||
export type Category = {
|
||||
id: string;
|
||||
name: string;
|
||||
roles: string[];
|
||||
hidden: boolean;
|
||||
type: CategoryType;
|
||||
position: number;
|
||||
};
|
|
@ -1,47 +0,0 @@
|
|||
import { Category } from './Category';
|
||||
import { Role } from './Role';
|
||||
import { Member } from './User';
|
||||
|
||||
export type Guild = {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
roles: Role[];
|
||||
};
|
||||
|
||||
export enum Features {
|
||||
None,
|
||||
Preview = None,
|
||||
}
|
||||
|
||||
export type GuildData = {
|
||||
id: string;
|
||||
message: string;
|
||||
categories: Category[];
|
||||
features: Features;
|
||||
};
|
||||
|
||||
export type PresentableGuild = {
|
||||
id: string;
|
||||
guild: GuildSlug;
|
||||
member: Member;
|
||||
data: GuildData;
|
||||
roles: Role[];
|
||||
};
|
||||
|
||||
export type GuildEnumeration = {
|
||||
guilds: PresentableGuild[];
|
||||
};
|
||||
|
||||
export enum UserGuildPermissions {
|
||||
User,
|
||||
Manager = 1 << 1,
|
||||
Admin = 1 << 2,
|
||||
}
|
||||
|
||||
export type GuildSlug = {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
permissionLevel: UserGuildPermissions;
|
||||
};
|
|
@ -1,37 +0,0 @@
|
|||
export enum RoleSafety {
|
||||
Safe = 0,
|
||||
HigherThanBot = 1 << 1,
|
||||
DangerousPermissions = 1 << 2,
|
||||
ManagedRole = 1 << 3,
|
||||
}
|
||||
|
||||
export type Role = {
|
||||
id: string;
|
||||
name: string;
|
||||
color: number;
|
||||
managed: boolean;
|
||||
position: number;
|
||||
safety: RoleSafety;
|
||||
/** Permissions is should be used as a BigInt, NOT a number. */
|
||||
permissions: string;
|
||||
};
|
||||
|
||||
export type OwnRoleInfo = {
|
||||
highestRolePosition: number;
|
||||
};
|
||||
|
||||
export enum TransactionType {
|
||||
None = 0,
|
||||
Remove = 1 << 1,
|
||||
Add = 1 << 2,
|
||||
}
|
||||
|
||||
export type RoleTransaction = {
|
||||
id: string;
|
||||
action: TransactionType;
|
||||
};
|
||||
|
||||
export type RoleUpdate = {
|
||||
knownState: Role['id'][];
|
||||
transactions: RoleTransaction[];
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
import { GuildSlug } from './Guild';
|
||||
import { DiscordUser } from './User';
|
||||
|
||||
export type AuthTokenResponse = {
|
||||
access_token: string;
|
||||
token_type: 'Bearer';
|
||||
expires_in: number;
|
||||
refresh_token: string;
|
||||
scope: string;
|
||||
};
|
||||
|
||||
export type SessionData = {
|
||||
/** sessionID is a KSUID */
|
||||
sessionID: string;
|
||||
tokens: AuthTokenResponse;
|
||||
user: DiscordUser;
|
||||
guilds: GuildSlug[];
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
export type DiscordUser = {
|
||||
id: string;
|
||||
username: string;
|
||||
discriminator: string;
|
||||
avatar: string;
|
||||
bot: boolean;
|
||||
};
|
||||
|
||||
export type Member = {
|
||||
guildid?: string;
|
||||
roles: string[];
|
||||
nick?: string;
|
||||
user?: DiscordUser;
|
||||
};
|
||||
|
||||
export type RoleypolyUser = {
|
||||
discorduser: DiscordUser;
|
||||
};
|
|
@ -1,58 +0,0 @@
|
|||
import { Role } from '.';
|
||||
|
||||
export const demoData: Role[] = [
|
||||
{
|
||||
id: '557812805546541066',
|
||||
name: 'a cute role ♡',
|
||||
color: 0xd19494,
|
||||
permissions: '0',
|
||||
safety: 0,
|
||||
managed: false,
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
id: '557812901717737472',
|
||||
name: 'a vanity role ♡',
|
||||
color: 0xd1d194,
|
||||
permissions: '0',
|
||||
safety: 0,
|
||||
managed: false,
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
id: '557812915386843170',
|
||||
name: 'a brave role ♡',
|
||||
color: 0x94d194,
|
||||
permissions: '0',
|
||||
safety: 0,
|
||||
managed: false,
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
id: '557824893241131029',
|
||||
name: 'a proud role ♡',
|
||||
color: 0x94d1d1,
|
||||
permissions: '0',
|
||||
safety: 0,
|
||||
managed: false,
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
id: '557824994269200384',
|
||||
name: 'a wonderful role ♡',
|
||||
color: 0x9494d1,
|
||||
permissions: '0',
|
||||
safety: 0,
|
||||
managed: false,
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
id: '557825026406088717',
|
||||
name: 'a 日本語 role ♡',
|
||||
color: 0xd194d1,
|
||||
permissions: '0',
|
||||
safety: 0,
|
||||
managed: false,
|
||||
position: 0,
|
||||
},
|
||||
];
|
|
@ -1,5 +0,0 @@
|
|||
export * from './Category';
|
||||
export * from './Guild';
|
||||
export * from './Role';
|
||||
export * from './Session';
|
||||
export * from './User';
|
|
@ -1,241 +0,0 @@
|
|||
import {
|
||||
Category,
|
||||
CategoryType,
|
||||
DiscordUser,
|
||||
Features,
|
||||
Guild,
|
||||
GuildData,
|
||||
GuildEnumeration,
|
||||
GuildSlug,
|
||||
Member,
|
||||
Role,
|
||||
RoleSafety,
|
||||
RoleypolyUser,
|
||||
} from '.';
|
||||
|
||||
export const roleCategory: Role[] = [
|
||||
{
|
||||
id: 'aaa',
|
||||
permissions: '0',
|
||||
name: 'She/Her',
|
||||
color: 0xffc0cb,
|
||||
position: 1,
|
||||
managed: false,
|
||||
safety: RoleSafety.Safe,
|
||||
},
|
||||
{
|
||||
id: 'bbb',
|
||||
permissions: '0',
|
||||
name: 'He/Him',
|
||||
color: 0xc0ebff,
|
||||
position: 2,
|
||||
managed: false,
|
||||
safety: RoleSafety.Safe,
|
||||
},
|
||||
{
|
||||
id: 'ccc',
|
||||
permissions: '0',
|
||||
name: 'They/Them',
|
||||
color: 0xc0ffd5,
|
||||
position: 3,
|
||||
managed: false,
|
||||
safety: RoleSafety.Safe,
|
||||
},
|
||||
{
|
||||
id: 'ddd',
|
||||
permissions: '0',
|
||||
name: 'Reee',
|
||||
color: 0xff0000,
|
||||
position: 4,
|
||||
managed: false,
|
||||
safety: RoleSafety.Safe,
|
||||
},
|
||||
{
|
||||
id: 'eee',
|
||||
permissions: '0',
|
||||
name: 'black but actually bravely default',
|
||||
color: 0x000000,
|
||||
position: 5,
|
||||
managed: false,
|
||||
safety: RoleSafety.Safe,
|
||||
},
|
||||
{
|
||||
id: 'fff',
|
||||
permissions: '0',
|
||||
name: 'b̻͌̆̽ͣ̃ͭ̊l͚̥͙̔ͨ̊aͥć͕k͎̟͍͕ͥ̋ͯ̓̈̉̋i͛̄̔͂̚̚҉̳͈͔̖̼̮ṣ̤̗̝͊̌͆h͈̭̰͔̥̯ͅ',
|
||||
color: 0x1,
|
||||
position: 6,
|
||||
managed: false,
|
||||
safety: RoleSafety.Safe,
|
||||
},
|
||||
{
|
||||
id: 'unsafe1',
|
||||
permissions: '0',
|
||||
name: 'too high',
|
||||
color: 0xff0088,
|
||||
position: 7,
|
||||
managed: false,
|
||||
safety: RoleSafety.HigherThanBot,
|
||||
},
|
||||
{
|
||||
id: 'unsafe2',
|
||||
permissions: String(0x00000008 | 0x10000000),
|
||||
name: 'too strong',
|
||||
color: 0x00ff88,
|
||||
position: 8,
|
||||
managed: false,
|
||||
safety: RoleSafety.DangerousPermissions,
|
||||
},
|
||||
];
|
||||
|
||||
export const mockCategory: Category = {
|
||||
id: 'aaa',
|
||||
name: 'Mock',
|
||||
roles: roleCategory.map((x) => x.id),
|
||||
hidden: false,
|
||||
type: CategoryType.Multi,
|
||||
position: 0,
|
||||
};
|
||||
|
||||
export const roleCategory2: Role[] = [
|
||||
{
|
||||
id: 'ddd2',
|
||||
permissions: '0',
|
||||
name: 'red',
|
||||
color: 0xff0000,
|
||||
position: 9,
|
||||
managed: false,
|
||||
safety: RoleSafety.Safe,
|
||||
},
|
||||
{
|
||||
id: 'eee2',
|
||||
permissions: '0',
|
||||
name: 'green',
|
||||
color: 0x00ff00,
|
||||
position: 10,
|
||||
managed: false,
|
||||
safety: RoleSafety.Safe,
|
||||
},
|
||||
];
|
||||
|
||||
export const mockCategorySingle: Category = {
|
||||
id: 'bbb',
|
||||
name: 'Mock Single 岡野',
|
||||
roles: roleCategory2.map((x) => x.id),
|
||||
hidden: false,
|
||||
type: CategoryType.Single,
|
||||
position: 0,
|
||||
};
|
||||
|
||||
export const roleWikiData = {
|
||||
aaa: 'Typically used by feminine-identifying people',
|
||||
bbb: 'Typically used by masculine-identifying people',
|
||||
ccc: 'Typically used to refer to all people as a singular neutral.',
|
||||
};
|
||||
|
||||
export const guild: Guild = {
|
||||
name: 'emoji megaporium',
|
||||
id: '421896162539470888',
|
||||
icon: '3372fd895ed913b55616c5e49cd50e60',
|
||||
roles: [],
|
||||
};
|
||||
|
||||
export const roleypolyGuild: GuildSlug = {
|
||||
name: 'Roleypoly',
|
||||
id: '386659935687147521',
|
||||
permissionLevel: 0,
|
||||
icon: 'ffee638c73ff9c972554f64ca34d67ee',
|
||||
};
|
||||
|
||||
export const guildMap: { [x: string]: GuildSlug } = {
|
||||
'emoji megaporium': {
|
||||
name: guild.name,
|
||||
id: guild.id,
|
||||
permissionLevel: 0,
|
||||
icon: guild.icon,
|
||||
},
|
||||
Roleypoly: roleypolyGuild,
|
||||
'chamber of secrets': {
|
||||
name: 'chamber of secrets',
|
||||
id: 'aaa',
|
||||
permissionLevel: 0,
|
||||
icon: '',
|
||||
},
|
||||
Eclipse: {
|
||||
name: 'Eclipse',
|
||||
id: '408821059161423873',
|
||||
permissionLevel: 0,
|
||||
icon: '49dfdd8b2456e2977e80a8b577b19c0d',
|
||||
},
|
||||
};
|
||||
|
||||
export const guildData: GuildData = {
|
||||
id: 'aaa',
|
||||
message: 'henlo worl!!',
|
||||
categories: [mockCategory, mockCategorySingle],
|
||||
features: Features.None,
|
||||
};
|
||||
|
||||
export const user: DiscordUser = {
|
||||
id: '62601275618889728',
|
||||
username: 'okano',
|
||||
discriminator: '0001',
|
||||
avatar: 'ca2028bab0fe30e1af4392f3fa3576e2',
|
||||
bot: false,
|
||||
};
|
||||
|
||||
export const member: Member = {
|
||||
guildid: 'aaa',
|
||||
roles: ['aaa', 'eee', 'unsafe2', 'ddd2'],
|
||||
nick: 'okano cat',
|
||||
user: user,
|
||||
};
|
||||
|
||||
export const rpUser: RoleypolyUser = {
|
||||
discorduser: user,
|
||||
};
|
||||
|
||||
export const guildEnum: GuildEnumeration = {
|
||||
guilds: [
|
||||
{
|
||||
id: 'aaa',
|
||||
guild: guildMap['emoji megaporium'],
|
||||
member,
|
||||
data: guildData,
|
||||
roles: [...roleCategory, ...roleCategory2],
|
||||
},
|
||||
{
|
||||
id: 'bbb',
|
||||
guild: guildMap['Roleypoly'],
|
||||
member: {
|
||||
...member,
|
||||
roles: ['unsafe2'],
|
||||
},
|
||||
data: guildData,
|
||||
roles: [...roleCategory, ...roleCategory2],
|
||||
},
|
||||
{
|
||||
id: 'ccc',
|
||||
guild: guildMap['chamber of secrets'],
|
||||
member,
|
||||
data: guildData,
|
||||
roles: [...roleCategory, ...roleCategory2],
|
||||
},
|
||||
{
|
||||
id: 'ddd',
|
||||
guild: guildMap['Eclipse'],
|
||||
member,
|
||||
data: guildData,
|
||||
roles: [...roleCategory, ...roleCategory2],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const mastheadSlugs: GuildSlug[] = guildEnum.guilds.map<GuildSlug>(
|
||||
(guild, idx) => ({
|
||||
id: guild.guild.id,
|
||||
name: guild.guild.name,
|
||||
icon: guild.guild.icon,
|
||||
permissionLevel: 1 << idx % 3,
|
||||
})
|
||||
);
|
|
@ -1,9 +0,0 @@
|
|||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
import { ReactifyNewlines } from './ReactifyNewlines';
|
||||
|
||||
it('renders a correct number of divs per newlines', () => {
|
||||
const view = shallow(<ReactifyNewlines>{`1\n2\n3`}</ReactifyNewlines>);
|
||||
|
||||
expect(view.find('div').length).toBe(3);
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
import * as React from 'react';
|
||||
|
||||
export const ReactifyNewlines = (props: { children: string }) => {
|
||||
const textArray = props.children.split('\n');
|
||||
return (
|
||||
<>
|
||||
{textArray.map((part, idx) => (
|
||||
<div key={`rifynl${idx}`}>{part || <> </>}</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,19 +0,0 @@
|
|||
import * as React from 'react';
|
||||
|
||||
export enum FeatureFlag {
|
||||
AllowListsBlockLists = 'AllowListsBlockLists',
|
||||
}
|
||||
|
||||
export class FeatureFlagProvider {
|
||||
activeFlags: FeatureFlag[] = [];
|
||||
|
||||
constructor(flags: FeatureFlag[] = []) {
|
||||
this.activeFlags = flags;
|
||||
}
|
||||
|
||||
has(flag: FeatureFlag) {
|
||||
return this.activeFlags.includes(flag);
|
||||
}
|
||||
}
|
||||
|
||||
export const FeatureFlagsContext = React.createContext(new FeatureFlagProvider());
|
|
@ -1 +0,0 @@
|
|||
export * from './FeatureFlags';
|
|
@ -1,12 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { FeatureFlag, FeatureFlagProvider, FeatureFlagsContext } from './FeatureFlags';
|
||||
|
||||
export const FeatureFlagDecorator = (flags: FeatureFlag[]) => (
|
||||
storyFn: () => React.ReactNode
|
||||
) => {
|
||||
return (
|
||||
<FeatureFlagsContext.Provider value={new FeatureFlagProvider(flags)}>
|
||||
{storyFn()}
|
||||
</FeatureFlagsContext.Provider>
|
||||
);
|
||||
};
|
|
@ -1,47 +0,0 @@
|
|||
import { Role } from 'roleypoly/common/types';
|
||||
import { roleCategory } from 'roleypoly/common/types/storyData';
|
||||
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[] = [
|
||||
{
|
||||
...roleCategory[0],
|
||||
permissions: String(permissions.ADMINISTRATOR),
|
||||
},
|
||||
{
|
||||
...roleCategory[0],
|
||||
permissions: String(
|
||||
permissions.SPEAK | permissions.BAN_MEMBERS | permissions.CHANGE_NICKNAME
|
||||
),
|
||||
},
|
||||
{
|
||||
...roleCategory[0],
|
||||
permissions: String(permissions.BAN_MEMBERS),
|
||||
},
|
||||
];
|
||||
|
||||
it('finds a permission within a list of roles', () => {
|
||||
const result = hasPermission(roles, permissions.CHANGE_NICKNAME);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('finds admin within a list of roles', () => {
|
||||
const result = hasPermissionOrAdmin(roles, permissions.BAN_MEMBERS);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('does not find a permission within a list of roles without one', () => {
|
||||
const result = hasPermission(roles, permissions.KICK_MEMBERS);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
|
@ -1,55 +0,0 @@
|
|||
import { Role } from '../types';
|
||||
|
||||
export const evaluatePermission = <T extends number | bigint>(
|
||||
haystack: T,
|
||||
needle: T
|
||||
): boolean => {
|
||||
return (haystack & needle) === needle;
|
||||
};
|
||||
|
||||
export const hasPermission = (roles: Role[], permission: bigint): boolean => {
|
||||
const aggregateRoles = roles.reduce(
|
||||
(acc, role) => acc | BigInt(role.permissions),
|
||||
BigInt(0)
|
||||
);
|
||||
return evaluatePermission(aggregateRoles, permission);
|
||||
};
|
||||
|
||||
export const hasPermissionOrAdmin = (roles: Role[], permission: bigint): boolean =>
|
||||
hasPermission(roles, permission | permissions.ADMINISTRATOR);
|
||||
|
||||
export const permissions = {
|
||||
// IMPORTANT: Only uncomment what's actually used. All are left for convenience.
|
||||
|
||||
// CREATE_INSTANT_INVITE: BigInt(0x1),
|
||||
// KICK_MEMBERS: BigInt(0x2),
|
||||
// BAN_MEMBERS: BigInt(0x4),
|
||||
ADMINISTRATOR: BigInt(0x8),
|
||||
// MANAGE_CHANNELS: BigInt(0x10),
|
||||
// MANAGE_GUILD: BigInt(0x20),
|
||||
// ADD_REACTIONS: BigInt(0x40),
|
||||
// VIEW_AUDIT_LOG: BigInt(0x80),
|
||||
// VIEW_CHANNEL: BigInt(0x400),
|
||||
// SEND_MESSAGES: BigInt(0x800),
|
||||
// SEND_TTS_MESSAGES: BigInt(0x1000),
|
||||
// MANAGE_MESSAGES: BigInt(0x2000),
|
||||
// EMBED_LINKS: BigInt(0x4000),
|
||||
// ATTACH_FILES: BigInt(0x8000),
|
||||
// READ_MESSAGE_HISTORY: BigInt(0x10000),
|
||||
// MENTION_EVERYONE: BigInt(0x20000),
|
||||
// USE_EXTERNAL_EMOJIS: BigInt(0x40000),
|
||||
// VIEW_GUILD_INSIGHTS: BigInt(0x80000),
|
||||
// CONNECT: BigInt(0x100000),
|
||||
// SPEAK: BigInt(0x200000),
|
||||
// MUTE_MEMBERS: BigInt(0x400000),
|
||||
// DEAFEN_MEMBERS: BigInt(0x800000),
|
||||
// MOVE_MEMBERS: BigInt(0x1000000),
|
||||
// USE_VAD: BigInt(0x2000000),
|
||||
// PRIORITY_SPEAKER: BigInt(0x100),
|
||||
// STREAM: BigInt(0x200),
|
||||
// CHANGE_NICKNAME: BigInt(0x4000000),
|
||||
// MANAGE_NICKNAMES: BigInt(0x8000000),
|
||||
MANAGE_ROLES: BigInt(0x10000000),
|
||||
// MANAGE_WEBHOOKS: BigInt(0x20000000),
|
||||
// MANAGE_EMOJIS: BigInt(0x40000000),
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
export const isBrowser = () => typeof window !== 'undefined';
|
|
@ -1,53 +0,0 @@
|
|||
import { NextPageContext } from 'next';
|
||||
import getConfig from 'next/config';
|
||||
import nookies from 'nookies';
|
||||
import useSWR from 'swr';
|
||||
|
||||
export const getPublicURI = (context?: NextPageContext) => {
|
||||
if (context?.req) {
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
return publicRuntimeConfig.apiPublicURI;
|
||||
} else {
|
||||
return typeof localStorage !== 'undefined' && localStorage.getItem('api_uri');
|
||||
}
|
||||
};
|
||||
|
||||
export const getSessionKey = (context?: NextPageContext) => {
|
||||
if (context?.req) {
|
||||
return nookies.get(context)['rp_session_key'];
|
||||
} else {
|
||||
return (
|
||||
typeof sessionStorage !== 'undefined' && sessionStorage.getItem('session_key')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const apiFetch = async <T>(
|
||||
path: string,
|
||||
init?: RequestInit,
|
||||
context?: NextPageContext
|
||||
): Promise<T | null> => {
|
||||
const sessionKey = getSessionKey(context);
|
||||
|
||||
const authorizedInit: RequestInit = {
|
||||
...(init || {}),
|
||||
headers: {
|
||||
...(init?.headers || {}),
|
||||
authorization: sessionKey ? `Bearer ${sessionKey}` : '',
|
||||
},
|
||||
};
|
||||
|
||||
const response = await fetch(`${getPublicURI(context)}${path}`, authorizedInit);
|
||||
|
||||
if (response.status >= 400) {
|
||||
const reason = (await response.json())['error'];
|
||||
throw new Error(`Fetch failed: ${reason}`);
|
||||
}
|
||||
|
||||
return response.json() as Promise<T>;
|
||||
};
|
||||
|
||||
export const swrFetch = <T>(path: string, context?: NextPageContext) =>
|
||||
useSWR<T>(path, (url: string): Promise<any> => apiFetch<T>(url, undefined, context), {
|
||||
revalidateOnFocus: false,
|
||||
});
|
|
@ -1,48 +0,0 @@
|
|||
import { sortBy } from './sortBy';
|
||||
|
||||
it('sorts an array of objects by its key', () => {
|
||||
const output = sortBy(
|
||||
[
|
||||
{
|
||||
name: 'bbb',
|
||||
},
|
||||
{
|
||||
name: 'aaa',
|
||||
},
|
||||
{
|
||||
name: 'ddd',
|
||||
},
|
||||
{
|
||||
name: 'ccc',
|
||||
},
|
||||
],
|
||||
'name'
|
||||
);
|
||||
|
||||
expect(output.map((v) => v.name)).toEqual(['aaa', 'bbb', 'ccc', 'ddd']);
|
||||
});
|
||||
|
||||
it('sorts an array of objects by its key with a predicate', () => {
|
||||
const output = sortBy(
|
||||
[
|
||||
{
|
||||
name: 'cc',
|
||||
},
|
||||
{
|
||||
name: 'bbb',
|
||||
},
|
||||
{
|
||||
name: 'aaaa',
|
||||
},
|
||||
{
|
||||
name: 'd',
|
||||
},
|
||||
],
|
||||
'name',
|
||||
(a, b) => {
|
||||
return a.length > b.length ? 1 : -1;
|
||||
}
|
||||
);
|
||||
|
||||
expect(output.map((v) => v.name)).toEqual(['d', 'cc', 'bbb', 'aaaa']);
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
export const sortBy = <T, Key extends keyof T>(
|
||||
array: T[],
|
||||
key: Key,
|
||||
predicate?: (a: T[typeof key], b: T[typeof key]) => number
|
||||
) => {
|
||||
return array.sort((a, b) => {
|
||||
if (predicate) {
|
||||
return predicate(a[key], b[key]);
|
||||
}
|
||||
|
||||
if (a[key] === b[key]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a[key] > b[key]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
});
|
||||
};
|
|
@ -1,11 +0,0 @@
|
|||
import * as React from 'react';
|
||||
|
||||
export type ContextShimProps<T> = {
|
||||
context: React.Context<T>;
|
||||
children: (data: T) => any;
|
||||
};
|
||||
|
||||
export function ContextShim<T>(props: ContextShimProps<T>) {
|
||||
const context = React.useContext(props.context);
|
||||
return <>{props.children(context)}</>;
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
export * as testHelpers from './contextTestHelpers';
|
||||
export * from './withContext';
|
|
@ -1,10 +0,0 @@
|
|||
import * as React from 'react';
|
||||
|
||||
export const withContext = <T, K extends T>(
|
||||
Context: React.Context<T>,
|
||||
Component: React.ComponentType<K>
|
||||
): React.FunctionComponent<K> => (props) => (
|
||||
<Context.Consumer>
|
||||
{(context) => <Component {...props} {...context} />}
|
||||
</Context.Consumer>
|
||||
);
|
Loading…
Add table
Add a link
Reference in a new issue