mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-06-15 09:09:10 +00:00
big overhaul (#474)
* miniflare init * feat(api): add tests * chore: more tests, almost 100% * add sessions/state spec * add majority of routes and datapaths, start on interactions * nevermind, no interactions * nevermind x2, tweetnacl is bad but SubtleCrypto has what we need apparently * simplify interactions verify * add brute force interactions tests * every primary path API route is refactored! * automatically import from legacy, or die trying. * check that we only fetch legacy once, ever * remove old-src, same some historic pieces * remove interactions & worker-utils package, update misc/types * update some packages we don't need specific pinning for anymore * update web references to API routes since they all changed * fix all linting issues, upgrade most packages * fix tests, divorce enzyme where-ever possible * update web, fix integration issues * pre-build api * fix tests * move api pretest to api package.json instead of CI * remove interactions from terraform, fix deploy side configs * update to tf 1.1.4 * prevent double writes to worker in GCS, port to newer GCP auth workflow * fix api.tf var refs, upgrade node action * change to curl-based script upload for worker script due to terraform provider limitations * oh no, cloudflare freaked out :(
This commit is contained in:
parent
b644a38aa7
commit
3291f9aacc
183 changed files with 9853 additions and 9924 deletions
|
@ -15,6 +15,10 @@ module.exports = {
|
|||
: [match.loader.include];
|
||||
match.loader.include = [...include, ...includePaths];
|
||||
}
|
||||
|
||||
webpackConfig.resolve.fallback = {
|
||||
crypto: false,
|
||||
};
|
||||
return webpackConfig;
|
||||
},
|
||||
},
|
||||
|
|
11
packages/web/jest.config.js
Normal file
11
packages/web/jest.config.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
module.exports = {
|
||||
name: 'web',
|
||||
preset: 'ts-jest/presets/js-with-ts',
|
||||
testEnvironment: 'jsdom',
|
||||
setupFilesAfterEnv: ['../../hack/jestSetup.ts'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '../../tsconfig.test.json',
|
||||
},
|
||||
},
|
||||
};
|
|
@ -5,35 +5,37 @@
|
|||
"scripts": {
|
||||
"build": "cross-env BUILD_PATH=../../dist craco build",
|
||||
"start": "cross-env PORT=6601 craco start",
|
||||
"test": "craco test"
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@reach/router": "^1.3.4",
|
||||
"@roleypoly/design-system": "*",
|
||||
"@roleypoly/misc-utils": "*",
|
||||
"memoize-one": "^5.2.1",
|
||||
"memoize-one": "^6.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-scripts": "4.0.3",
|
||||
"typescript": "^4.3.5",
|
||||
"web-vitals": "^2.1.0"
|
||||
"react-scripts": "5.0.0",
|
||||
"typescript": "^4.5.5",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@craco/craco": "^6.2.0",
|
||||
"@craco/craco": "^7.0.0-alpha.0",
|
||||
"@roleypoly/types": "*",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@testing-library/user-event": "^13.1.9",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/node": "^16.0.1",
|
||||
"@types/react": "^17.0.14",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/react-helmet": "^6.1.2",
|
||||
"babel-loader": "8.1.0",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/jest": "^27.4.0",
|
||||
"@types/node": "^17.0.13",
|
||||
"@types/reach__router": "^1.3.10",
|
||||
"@types/react": "^17.0.38",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/react-helmet": "^6.1.5",
|
||||
"babel-loader": "8.2.3",
|
||||
"cross-env": "7.0.3",
|
||||
"ts-loader": "^8.3.0",
|
||||
"webpack": "4.44.2"
|
||||
"jest": "^27.4.7",
|
||||
"ts-loader": "^9.2.6",
|
||||
"webpack": "5.67.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
|
|
@ -8,7 +8,7 @@ export const getDefaultApiUrl = memoizeOne.default((host: string) => {
|
|||
/^stage\.roleypoly\.com$/.test(host)
|
||||
) {
|
||||
return 'https://api-stage.roleypoly.com';
|
||||
} else if (/\blocalhost|127\.0\.0\.1\b/.test(host)) {
|
||||
} else if (/\blocalhost|127\.0\.0\.1|172.21.92\b/.test(host)) {
|
||||
return 'http://localhost:6609';
|
||||
} else {
|
||||
return 'https://api-prod.roleypoly.com';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { GuildSlug, PresentableGuild } from '@roleypoly/types';
|
||||
import React from 'react';
|
||||
import { useApiContext } from '../api/ApiContext';
|
||||
import { useAuthedFetch } from '../session/AuthedFetchContext';
|
||||
import { useSessionContext } from '../session/SessionContext';
|
||||
|
||||
const CACHE_HOLD_TIME = 2 * 60 * 1000; // 2 minutes
|
||||
|
@ -29,7 +30,8 @@ export const GuildContext = React.createContext<GuildContextT>({
|
|||
export const useGuildContext = () => React.useContext(GuildContext);
|
||||
|
||||
export const GuildProvider = (props: { children: React.ReactNode }) => {
|
||||
const { session, authedFetch } = useSessionContext();
|
||||
const { session } = useSessionContext();
|
||||
const { authedFetch } = useAuthedFetch();
|
||||
const { fetch } = useApiContext();
|
||||
|
||||
const guildContextValue: GuildContextT = {
|
||||
|
@ -52,7 +54,7 @@ export const GuildProvider = (props: { children: React.ReactNode }) => {
|
|||
}
|
||||
}
|
||||
|
||||
const response = await fetch(`/get-slug/${id}`);
|
||||
const response = await fetch(`/guilds/${id}/slug`);
|
||||
if (response.status !== 200) {
|
||||
return null;
|
||||
}
|
||||
|
@ -83,7 +85,7 @@ export const GuildProvider = (props: { children: React.ReactNode }) => {
|
|||
}
|
||||
|
||||
const skipCache = uncached ? '?__no_cache' : '';
|
||||
const response = await authedFetch(`/get-picker-data/${id}${skipCache}`);
|
||||
const response = await authedFetch(`/guilds/${id}${skipCache}`);
|
||||
const guild: PresentableGuild = await response.json();
|
||||
|
||||
if (response.status !== 200) {
|
||||
|
|
34
packages/web/src/contexts/session/AuthedFetchContext.tsx
Normal file
34
packages/web/src/contexts/session/AuthedFetchContext.tsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
import React from 'react';
|
||||
import { useApiContext } from '../api/ApiContext';
|
||||
import { useSessionContext } from './SessionContext';
|
||||
|
||||
type AuthedFetchContextT = {
|
||||
authedFetch: (url: string, options?: RequestInit) => Promise<Response>;
|
||||
};
|
||||
|
||||
export const AuthedFetchContext = React.createContext<AuthedFetchContextT>({
|
||||
authedFetch: () => Promise.reject(new Error('AuthedFetchContext not initialized')),
|
||||
});
|
||||
|
||||
export const useAuthedFetch = () => React.useContext(AuthedFetchContext);
|
||||
|
||||
export const AuthedFetchProvider = (props: { children: React.ReactNode }) => {
|
||||
const { fetch } = useApiContext();
|
||||
const { sessionID } = useSessionContext();
|
||||
|
||||
const authedFetch = (url: string, options?: RequestInit) => {
|
||||
return fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
...options?.headers,
|
||||
Authorization: sessionID ? `Bearer ${sessionID}` : undefined,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthedFetchContext.Provider value={{ authedFetch }}>
|
||||
{props.children}
|
||||
</AuthedFetchContext.Provider>
|
||||
);
|
||||
};
|
|
@ -2,12 +2,6 @@ import { SessionData } from '@roleypoly/types';
|
|||
import * as React from 'react';
|
||||
import { useApiContext } from '../api/ApiContext';
|
||||
|
||||
enum SessionState {
|
||||
NoAuth,
|
||||
HalfAuth,
|
||||
FullAuth,
|
||||
}
|
||||
|
||||
type SavedSession = {
|
||||
sessionID: SessionData['sessionID'];
|
||||
|
||||
|
@ -18,10 +12,8 @@ type SavedSession = {
|
|||
};
|
||||
|
||||
type SessionContextT = {
|
||||
setupSession: (sessionID: string) => void;
|
||||
authedFetch: (url: string, opts?: RequestInit) => Promise<Response>;
|
||||
setupSession: (sessionID: string | null) => void;
|
||||
isAuthenticated: boolean;
|
||||
sessionState: SessionState;
|
||||
sessionID?: SessionData['sessionID'];
|
||||
session: {
|
||||
user?: SessionData['user'];
|
||||
|
@ -30,148 +22,125 @@ type SessionContextT = {
|
|||
};
|
||||
|
||||
const SessionContext = React.createContext<SessionContextT>({
|
||||
sessionState: SessionState.NoAuth,
|
||||
sessionID: undefined,
|
||||
isAuthenticated: false,
|
||||
session: {
|
||||
user: undefined,
|
||||
guilds: undefined,
|
||||
},
|
||||
isAuthenticated: false,
|
||||
setupSession: () => {},
|
||||
authedFetch: async () => {
|
||||
return new Response();
|
||||
},
|
||||
});
|
||||
|
||||
export const useSessionContext = () => React.useContext(SessionContext);
|
||||
|
||||
export const SessionContextProvider = (props: { children: React.ReactNode }) => {
|
||||
const { fetch } = useApiContext();
|
||||
const [sessionID, setSessionID] =
|
||||
React.useState<SessionContextT['sessionID']>(undefined);
|
||||
const [sessionState, setSessionState] = React.useState<SessionState>(
|
||||
SessionState.NoAuth
|
||||
);
|
||||
const [session, setSession] = React.useState<SessionContextT['session']>({
|
||||
user: undefined,
|
||||
guilds: undefined,
|
||||
const [locked, setLock] = React.useState(false);
|
||||
const [session, setSession] = React.useState<SessionContextT>({
|
||||
sessionID: undefined,
|
||||
session: {
|
||||
user: undefined,
|
||||
guilds: undefined,
|
||||
},
|
||||
isAuthenticated: false,
|
||||
setupSession: (key: string | null) => {
|
||||
if (key) {
|
||||
saveSessionKey(key);
|
||||
setSession({
|
||||
...session,
|
||||
sessionID: key,
|
||||
});
|
||||
} else {
|
||||
deleteSessionKey();
|
||||
deleteSessionData();
|
||||
setSession({
|
||||
...session,
|
||||
sessionID: undefined,
|
||||
isAuthenticated: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
const [lock, setLock] = React.useState(false);
|
||||
|
||||
// Possible flows:
|
||||
/*
|
||||
if no key, check if key available in LS
|
||||
|
||||
No session:
|
||||
no key
|
||||
isAuth = false
|
||||
|
||||
Half session:
|
||||
have key
|
||||
lock = true
|
||||
isAuth = false
|
||||
fetch cached in SS _OR_ syncSession()
|
||||
lock = false
|
||||
|
||||
Full session
|
||||
have session
|
||||
isAuth = true
|
||||
*/
|
||||
|
||||
const sessionContextValue: SessionContextT = {
|
||||
sessionID,
|
||||
session,
|
||||
sessionState,
|
||||
isAuthenticated: sessionState === SessionState.FullAuth,
|
||||
setupSession: async (newID: string) => {
|
||||
setSessionID(newID);
|
||||
setSessionState(SessionState.HalfAuth);
|
||||
saveSessionKey(newID);
|
||||
},
|
||||
authedFetch: async (url: string, init?: RequestInit): Promise<Response> => {
|
||||
if (sessionID) {
|
||||
init = {
|
||||
...init,
|
||||
headers: {
|
||||
...init?.headers,
|
||||
authorization: `Bearer ${sessionID}`,
|
||||
},
|
||||
};
|
||||
React.useEffect(() => {
|
||||
const fetchSession = async (sessionID: string): Promise<ServerSession | null> => {
|
||||
const sessionResponse = await fetch('/auth/session', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${sessionID}`,
|
||||
},
|
||||
});
|
||||
if (sessionResponse.status !== 200) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fetch(url, init);
|
||||
},
|
||||
};
|
||||
const { guilds, user }: ServerSession = await sessionResponse.json();
|
||||
return {
|
||||
sessionID,
|
||||
guilds,
|
||||
user,
|
||||
};
|
||||
};
|
||||
|
||||
const { setupSession, authedFetch } = sessionContextValue;
|
||||
|
||||
// Local storage sync on NoAuth
|
||||
React.useEffect(() => {
|
||||
if (!sessionID) {
|
||||
const storedKey = getSessionKey();
|
||||
if (!storedKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
setupSession(storedKey);
|
||||
}
|
||||
}, [sessionID, setupSession]);
|
||||
|
||||
// Sync session data on HalfAuth
|
||||
React.useEffect(() => {
|
||||
if (lock) {
|
||||
console.warn('hit syncSession lock');
|
||||
if (locked) {
|
||||
console.warn('Session locked, skipping update');
|
||||
return;
|
||||
}
|
||||
if (!session.sessionID) {
|
||||
const sessionKey = getSessionKey();
|
||||
if (sessionKey) {
|
||||
session.setupSession(sessionKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (sessionState === SessionState.HalfAuth) {
|
||||
setLock(true);
|
||||
|
||||
// Use cached session
|
||||
const storedData = getSessionData();
|
||||
if (storedData && storedData?.sessionID === sessionID) {
|
||||
setSession(storedData.session);
|
||||
setSessionState(SessionState.FullAuth);
|
||||
setLock(false);
|
||||
return;
|
||||
if (session.sessionID && !session.session.user) {
|
||||
// Lets see if session is in session storage...
|
||||
const sessionData = getSessionData();
|
||||
if (sessionData) {
|
||||
setSession({
|
||||
...session,
|
||||
isAuthenticated: true,
|
||||
session: {
|
||||
user: sessionData.session.user,
|
||||
guilds: sessionData.session.guilds,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// If no cached session, let's grab it from server
|
||||
const syncSession = async () => {
|
||||
try {
|
||||
const serverSession = await fetchSession(authedFetch);
|
||||
if (!serverSession) {
|
||||
// Not found, lets reset.
|
||||
deleteSessionKey();
|
||||
setSessionID(undefined);
|
||||
setSessionState(SessionState.NoAuth);
|
||||
// If not, lets fetch it from the server
|
||||
setLock(true);
|
||||
fetchSession(session.sessionID)
|
||||
.then((sessionData) => {
|
||||
if (sessionData) {
|
||||
setSession({
|
||||
...session,
|
||||
isAuthenticated: true,
|
||||
session: {
|
||||
user: sessionData.user,
|
||||
guilds: sessionData.guilds,
|
||||
},
|
||||
});
|
||||
saveSessionData({
|
||||
sessionID: session.sessionID!,
|
||||
session: {
|
||||
user: sessionData.user,
|
||||
guilds: sessionData.guilds,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
session.setupSession(null);
|
||||
setLock(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const newSession = {
|
||||
user: serverSession.user,
|
||||
guilds: serverSession.guilds,
|
||||
};
|
||||
|
||||
saveSessionData({ sessionID: sessionID || '', session: newSession });
|
||||
setSession(newSession);
|
||||
setSessionState(SessionState.FullAuth);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
session.setupSession(null);
|
||||
setLock(false);
|
||||
} catch (e) {
|
||||
console.error('syncSession failed', e);
|
||||
setLock(false);
|
||||
}
|
||||
};
|
||||
|
||||
syncSession();
|
||||
});
|
||||
}
|
||||
}, [sessionState, sessionID, authedFetch, lock]);
|
||||
}, [session, locked, setLock, fetch]);
|
||||
|
||||
return (
|
||||
<SessionContext.Provider value={sessionContextValue}>
|
||||
{props.children}
|
||||
</SessionContext.Provider>
|
||||
<SessionContext.Provider value={session}>{props.children}</SessionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -179,24 +148,10 @@ const saveSessionKey = (key: string) => localStorage.setItem('rp_session_key', k
|
|||
const deleteSessionKey = () => localStorage.removeItem('rp_session_key');
|
||||
const getSessionKey = () => localStorage.getItem('rp_session_key');
|
||||
|
||||
type ServerSession = Omit<SessionData, 'tokens'>;
|
||||
const fetchSession = async (
|
||||
authedFetch: SessionContextT['authedFetch']
|
||||
): Promise<ServerSession | null> => {
|
||||
const sessionResponse = await authedFetch('/get-session');
|
||||
if (sessionResponse.status !== 200) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { sessionID, guilds, user }: ServerSession = await sessionResponse.json();
|
||||
return {
|
||||
sessionID,
|
||||
guilds,
|
||||
user,
|
||||
};
|
||||
};
|
||||
type ServerSession = Omit<Omit<SessionData, 'tokens'>, 'flags'>;
|
||||
|
||||
const saveSessionData = (data: SavedSession) =>
|
||||
sessionStorage.setItem('rp_session_data', JSON.stringify(data));
|
||||
const getSessionData = (): SavedSession | null =>
|
||||
JSON.parse(sessionStorage.getItem('rp_session_data') || 'null');
|
||||
const deleteSessionData = () => localStorage.removeItem('rp_session_data');
|
||||
|
|
|
@ -6,6 +6,7 @@ import { ApiContextProvider } from './contexts/api/ApiContext';
|
|||
import { AppShellPropsProvider } from './contexts/app-shell/AppShellContext';
|
||||
import { GuildProvider } from './contexts/guild/GuildContext';
|
||||
import { RecentGuildsProvider } from './contexts/recent-guilds/RecentGuildsContext';
|
||||
import { AuthedFetchProvider } from './contexts/session/AuthedFetchContext';
|
||||
import { SessionContextProvider } from './contexts/session/SessionContext';
|
||||
|
||||
const ProviderProvider = (props: {
|
||||
|
@ -24,6 +25,7 @@ ReactDOM.render(
|
|||
providerChain={[
|
||||
ApiContextProvider,
|
||||
SessionContextProvider,
|
||||
AuthedFetchProvider,
|
||||
RecentGuildsProvider,
|
||||
AppShellPropsProvider,
|
||||
BreakpointsProvider,
|
||||
|
|
|
@ -15,13 +15,13 @@ const Login = (props: { path: string }) => {
|
|||
// If ?r is in query, then let's render the slug page
|
||||
// If not, redirect.
|
||||
const [guildSlug, setGuildSlug] = React.useState<GuildSlug | null>(null);
|
||||
const [oauthLink, setOauthLink] = React.useState(`${apiUrl}/login-bounce`);
|
||||
const [oauthLink, setOauthLink] = React.useState(`${apiUrl}/auth/bounce`);
|
||||
|
||||
React.useEffect(() => {
|
||||
const url = new URL(window.location.href);
|
||||
const callbackHost = new URL('/', url);
|
||||
const redirectServerID = url.searchParams.get('r');
|
||||
const redirectUrl = `${apiUrl}/login-bounce?cbh=${callbackHost.href}`;
|
||||
const redirectUrl = `${apiUrl}/auth/bounce?cbh=${callbackHost.href}`;
|
||||
if (!redirectServerID) {
|
||||
if (isAuthenticated) {
|
||||
redirectTo('/servers');
|
||||
|
|
|
@ -10,6 +10,7 @@ import * as React from 'react';
|
|||
import { useAppShellProps } from '../contexts/app-shell/AppShellContext';
|
||||
import { useGuildContext } from '../contexts/guild/GuildContext';
|
||||
import { useRecentGuilds } from '../contexts/recent-guilds/RecentGuildsContext';
|
||||
import { useAuthedFetch } from '../contexts/session/AuthedFetchContext';
|
||||
import { useSessionContext } from '../contexts/session/SessionContext';
|
||||
import { Title } from '../utils/metaTitle';
|
||||
|
||||
|
@ -20,10 +21,11 @@ type EditorProps = {
|
|||
|
||||
const Editor = (props: EditorProps) => {
|
||||
const { serverID } = props;
|
||||
const { session, authedFetch, isAuthenticated } = useSessionContext();
|
||||
const { session, isAuthenticated } = useSessionContext();
|
||||
const { authedFetch } = useAuthedFetch();
|
||||
const { pushRecentGuild } = useRecentGuilds();
|
||||
const appShellProps = useAppShellProps();
|
||||
const { getFullGuild } = useGuildContext();
|
||||
const { getFullGuild, uncacheGuild } = useGuildContext();
|
||||
|
||||
const [guild, setGuild] = React.useState<PresentableGuild | null | false>(null);
|
||||
const [pending, setPending] = React.useState(false);
|
||||
|
@ -89,13 +91,14 @@ const Editor = (props: EditorProps) => {
|
|||
categories: guild.data.categories,
|
||||
};
|
||||
|
||||
const response = await authedFetch(`/update-guild/${serverID}`, {
|
||||
const response = await authedFetch(`/guilds/${serverID}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(updatePayload),
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
setGuild(guild);
|
||||
uncacheGuild(serverID);
|
||||
navigate(`/s/${props.serverID}`);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,12 @@ import React from 'react';
|
|||
import { useAppShellProps } from '../../contexts/app-shell/AppShellContext';
|
||||
import { useGuildContext } from '../../contexts/guild/GuildContext';
|
||||
import { useRecentGuilds } from '../../contexts/recent-guilds/RecentGuildsContext';
|
||||
import { useAuthedFetch } from '../../contexts/session/AuthedFetchContext';
|
||||
import { useSessionContext } from '../../contexts/session/SessionContext';
|
||||
|
||||
const AccessControlPage = (props: { serverID: string; path: string }) => {
|
||||
const { session, isAuthenticated, authedFetch } = useSessionContext();
|
||||
const { session, isAuthenticated } = useSessionContext();
|
||||
const { authedFetch } = useAuthedFetch();
|
||||
const { pushRecentGuild } = useRecentGuilds();
|
||||
const { getFullGuild, uncacheGuild } = useGuildContext();
|
||||
const appShellProps = useAppShellProps();
|
||||
|
|
|
@ -10,7 +10,7 @@ const BotJoin = (props: { serverID: string; path: string }) => {
|
|||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
window.location.href = `${apiUrl}/bot-join${params}`;
|
||||
window.location.href = `${apiUrl}/auth/bot${params}`;
|
||||
}, [apiUrl, params]);
|
||||
|
||||
return <GenericLoadingTemplate />;
|
||||
|
|
|
@ -7,6 +7,7 @@ import * as React from 'react';
|
|||
import { useAppShellProps } from '../contexts/app-shell/AppShellContext';
|
||||
import { useGuildContext } from '../contexts/guild/GuildContext';
|
||||
import { useRecentGuilds } from '../contexts/recent-guilds/RecentGuildsContext';
|
||||
import { useAuthedFetch } from '../contexts/session/AuthedFetchContext';
|
||||
import { useSessionContext } from '../contexts/session/SessionContext';
|
||||
import { Title } from '../utils/metaTitle';
|
||||
import { makeRoleTransactions } from '../utils/roleTransactions';
|
||||
|
@ -17,7 +18,8 @@ type PickerProps = {
|
|||
};
|
||||
|
||||
const Picker = (props: PickerProps) => {
|
||||
const { session, authedFetch, isAuthenticated } = useSessionContext();
|
||||
const { session, isAuthenticated } = useSessionContext();
|
||||
const { authedFetch } = useAuthedFetch();
|
||||
const { pushRecentGuild } = useRecentGuilds();
|
||||
const appShellProps = useAppShellProps();
|
||||
const { getFullGuild, uncacheGuild } = useGuildContext();
|
||||
|
@ -65,7 +67,7 @@ const Picker = (props: PickerProps) => {
|
|||
const guildSlug = session.guilds.find((guild) => guild.id === props.serverID);
|
||||
|
||||
if (!guildSlug) {
|
||||
console.error({ error: 'guold not in session, 404' });
|
||||
console.error({ error: 'guild not in session, 404' });
|
||||
return <Redirect to="/error/404" replace />;
|
||||
}
|
||||
|
||||
|
@ -94,8 +96,8 @@ const Picker = (props: PickerProps) => {
|
|||
};
|
||||
|
||||
uncacheGuild(props.serverID);
|
||||
const response = await authedFetch(`/update-roles/${props.serverID}`, {
|
||||
method: 'PATCH',
|
||||
const response = await authedFetch(`/guilds/${props.serverID}/roles`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(updatePayload),
|
||||
});
|
||||
if (response.status === 200) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue