mirror of
https://github.com/MetaFam/TheGame.git
synced 2026-04-24 03:00:09 -04:00
Merge branch 'develop'
This commit is contained in:
175
packages/web/pages/join/guild/[guildname].tsx
Normal file
175
packages/web/pages/join/guild/[guildname].tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
import { Flex, LoadingState, MetaHeading, useToast } from '@metafam/ds';
|
||||
import { FlexContainer } from 'components/Container';
|
||||
import { EditGuildFormInputs, GuildForm } from 'components/Guild/GuildForm';
|
||||
import {
|
||||
GuildInfoInput,
|
||||
GuildType_ActionEnum,
|
||||
LinkType_Enum,
|
||||
useAddGuildLinkMutation,
|
||||
useGetGuildQuery,
|
||||
useUpdateGuildMutation,
|
||||
} from 'graphql/autogen/types';
|
||||
import { useWeb3 } from 'lib/hooks';
|
||||
import { useRouter } from 'next/router';
|
||||
import Page404 from 'pages/404';
|
||||
import React, { lazy,useCallback } from 'react';
|
||||
import { errorHandler } from 'utils/errorHandler';
|
||||
|
||||
const PageContainer = lazy(() => import('components/Container'));
|
||||
|
||||
const SetupGuild: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const guildNameRouter = router.query.guildname as string;
|
||||
const toast = useToast();
|
||||
|
||||
const [, addLink] = useAddGuildLinkMutation();
|
||||
const [updateGuildState, updateGuild] = useUpdateGuildMutation();
|
||||
const [res] = useGetGuildQuery({ variables: { guildname: guildNameRouter } });
|
||||
const guild = res.data?.guild[0];
|
||||
const { w3storage } = useWeb3();
|
||||
const onSubmit = useCallback(
|
||||
async (editGuildFormInputs: EditGuildFormInputs) => {
|
||||
if (!guild) return;
|
||||
|
||||
const {
|
||||
type,
|
||||
discordAdminRoles: adminRoles,
|
||||
discordMembershipRoles: membershipRoles,
|
||||
logoFile,
|
||||
logoURL,
|
||||
daos,
|
||||
websiteURL,
|
||||
githubURL,
|
||||
twitterURL,
|
||||
description,
|
||||
guildname,
|
||||
name,
|
||||
discordInviteURL,
|
||||
joinURL,
|
||||
} = editGuildFormInputs;
|
||||
|
||||
let newLogoURL = logoURL;
|
||||
|
||||
if (logoFile?.[0]) {
|
||||
try {
|
||||
const ipfsHash = await w3storage?.uploadFile(logoFile[0]);
|
||||
newLogoURL = `ipfs://${ipfsHash}`;
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: 'Error Saving Logo',
|
||||
description: (error as Error).message,
|
||||
status: 'warning',
|
||||
isClosable: true,
|
||||
duration: 8000,
|
||||
});
|
||||
errorHandler(error as Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const twitterGuildLink = {
|
||||
guildId: guild.id,
|
||||
name: 'Find Us On Twitter',
|
||||
url: twitterURL || '',
|
||||
type: 'TWITTER' as LinkType_Enum,
|
||||
};
|
||||
await addLink(twitterGuildLink);
|
||||
|
||||
const discordGuildLink = {
|
||||
guildId: guild.id,
|
||||
name: 'Join Us On Discord',
|
||||
url: discordInviteURL || '',
|
||||
type: 'DISCORD' as LinkType_Enum,
|
||||
};
|
||||
await addLink(discordGuildLink);
|
||||
|
||||
const githubGuildLink = {
|
||||
guildId: guild.id,
|
||||
name: 'Find Us On Github',
|
||||
url: githubURL || '',
|
||||
type: 'GITHUB' as LinkType_Enum,
|
||||
};
|
||||
await addLink(githubGuildLink);
|
||||
|
||||
const payload: GuildInfoInput = {
|
||||
guildname,
|
||||
name,
|
||||
description,
|
||||
joinURL,
|
||||
daos,
|
||||
websiteURL,
|
||||
discordAdminRoles: adminRoles.map((o) => o.value),
|
||||
discordMembershipRoles: membershipRoles.map((o) => o.value),
|
||||
type: type as unknown as GuildType_ActionEnum,
|
||||
uuid: guild.id,
|
||||
logoURL: newLogoURL,
|
||||
};
|
||||
|
||||
const response = await updateGuild({ guildInfo: payload });
|
||||
|
||||
const saveGuildResponse = response.data?.saveGuildInformation;
|
||||
if (saveGuildResponse?.success) {
|
||||
toast({
|
||||
title: 'Guild information submitted',
|
||||
description: 'Thanks! Your guild will go live shortly 🚀',
|
||||
status: 'success',
|
||||
isClosable: true,
|
||||
duration: 5000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
router.push('/dashboard');
|
||||
}, 5000);
|
||||
} else {
|
||||
toast({
|
||||
title: 'Error while saving guild information',
|
||||
description:
|
||||
response.error?.message ||
|
||||
saveGuildResponse?.error ||
|
||||
'unknown error',
|
||||
status: 'error',
|
||||
isClosable: true,
|
||||
duration: 10000,
|
||||
});
|
||||
}
|
||||
},
|
||||
[guild, router, toast, updateGuild, addLink, w3storage],
|
||||
);
|
||||
|
||||
if (res.fetching || res.data == null) {
|
||||
return <LoadingState />;
|
||||
}
|
||||
|
||||
if (res.data.guild.length === 0 || guild == null) {
|
||||
return <Page404 />;
|
||||
}
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<FlexContainer flex="1" justify="start" mt={5}>
|
||||
<MetaHeading textAlign="center" mb={10} size="md">
|
||||
Add guild information
|
||||
</MetaHeading>
|
||||
<Flex
|
||||
direction="column"
|
||||
bg="whiteAlpha.200"
|
||||
backdropFilter="blur(7px)"
|
||||
rounded="lg"
|
||||
p="6"
|
||||
my="6"
|
||||
w="max-content"
|
||||
align="stretch"
|
||||
justify="space-between"
|
||||
>
|
||||
<GuildForm
|
||||
workingGuild={guild}
|
||||
onSubmit={onSubmit}
|
||||
success={!!updateGuildState.data?.saveGuildInformation?.success}
|
||||
submitting={updateGuildState.fetching}
|
||||
/>
|
||||
</Flex>
|
||||
</FlexContainer>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default SetupGuild;
|
||||
70
packages/web/pages/join/guild/auth.tsx
Normal file
70
packages/web/pages/join/guild/auth.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Box, Link } from '@metafam/ds';
|
||||
import { useAuthenticateDiscordGuildMutation } from 'graphql/autogen/types';
|
||||
import { get, remove } from 'lib/store';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { lazy,useEffect, useState } from 'react';
|
||||
|
||||
import { discordAuthStateGuidKey } from './start';
|
||||
|
||||
const PageContainer = lazy(() => import('components/Container'));
|
||||
|
||||
const GuildSetupAuthCallback: React.FC = () => {
|
||||
const router = useRouter();
|
||||
|
||||
const [authGuildRes, authGuild] = useAuthenticateDiscordGuildMutation();
|
||||
const [error, setError] = useState<string>('');
|
||||
const [fetching, setFetching] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
// when auth request is denied, we get `error=access_denied` and `error_description` and `state` parameters
|
||||
const { code, error_description: discordErrorDetail, state } = router.query;
|
||||
|
||||
const localState = get(discordAuthStateGuidKey);
|
||||
|
||||
if (discordErrorDetail != null) {
|
||||
setError(discordErrorDetail as string);
|
||||
return;
|
||||
}
|
||||
|
||||
const submitAuthCode = async () => {
|
||||
const { data, error: mutationError } = await authGuild({
|
||||
code: code as string,
|
||||
});
|
||||
const response = data?.authenticateDiscordGuild;
|
||||
if (mutationError || response?.success === false) {
|
||||
setError(mutationError?.message || 'An unexpected error occurred.');
|
||||
} else if (response?.guildname != null) {
|
||||
// clean up guid
|
||||
remove(discordAuthStateGuidKey);
|
||||
router.push(`/join/guild/${response?.guildname}`);
|
||||
}
|
||||
};
|
||||
if (!fetching && code) {
|
||||
if (localState == null || localState !== state) {
|
||||
setError('State did not match.');
|
||||
return;
|
||||
}
|
||||
setFetching(true);
|
||||
submitAuthCode();
|
||||
}
|
||||
}, [router, authGuild, error, fetching]);
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
{error && (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
Could not load your guild information from Discord: {error}
|
||||
<Box mt={2}>
|
||||
<Link href="/join/guild" color="blue.400">
|
||||
Try again
|
||||
</Link>
|
||||
</Box>
|
||||
</div>
|
||||
)}
|
||||
{authGuildRes.fetching &&
|
||||
'Loading your guild information from Discord...'}
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default GuildSetupAuthCallback;
|
||||
101
packages/web/pages/join/guild/start.tsx
Normal file
101
packages/web/pages/join/guild/start.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
ListItem,
|
||||
MetaButton,
|
||||
MetaHeading,
|
||||
Text,
|
||||
UnorderedList,
|
||||
} from '@metafam/ds';
|
||||
import { Constants, generateUUID } from '@metafam/utils';
|
||||
import { CONFIG } from 'config';
|
||||
import { useUser } from 'lib/hooks';
|
||||
import { get, set } from 'lib/store';
|
||||
import React, { lazy,useEffect, useState } from 'react';
|
||||
|
||||
const discordOAuthCallbackURL = `${CONFIG.publicURL}/${Constants.DISCORD_OAUTH_CALLBACK_PATH}`;
|
||||
export const discordAuthStateGuidKey = 'metagame-add-guild';
|
||||
|
||||
const PageContainer = lazy(() => import('components/Container'));
|
||||
|
||||
const GuildSetupAuthCallback: React.FC = () => {
|
||||
const { user } = useUser();
|
||||
|
||||
const [stateGuid, setStateGuid] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
let guid = get(discordAuthStateGuidKey);
|
||||
if (guid == null) {
|
||||
guid = generateUUID();
|
||||
set(discordAuthStateGuidKey, guid);
|
||||
}
|
||||
setStateGuid(guid);
|
||||
}, [setStateGuid]);
|
||||
|
||||
const discordAuthParams = new URLSearchParams({
|
||||
response_type: 'code',
|
||||
client_id: Constants.DISCORD_BOT_CLIENT_ID,
|
||||
// This will be passed-back and verified after the Discord auth redirect
|
||||
state: stateGuid as string,
|
||||
permissions: Constants.JOIN_GUILD_DISCORD_BOT_PERMISSIONS,
|
||||
redirect_uri: encodeURI(discordOAuthCallbackURL),
|
||||
scope: Constants.JOIN_GUILD_DISCORD_OAUTH_SCOPES,
|
||||
});
|
||||
|
||||
const discordAuthURL = `${
|
||||
CONFIG.discordAPIBaseUrl
|
||||
}/oauth2/authorize?${discordAuthParams.toString()}`;
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<MetaHeading>Join as a Guild</MetaHeading>
|
||||
<Box maxW="xl" mt={8}>
|
||||
{stateGuid?.length && user ? (
|
||||
<>
|
||||
<Text>
|
||||
Clicking the link below will redirect to a Discord page asking for
|
||||
your permission to collect this information about your guild from
|
||||
your guild's Discord server:
|
||||
<UnorderedList pt={2}>
|
||||
<ListItem fontSize="small">
|
||||
Read messages / history. <em>Optional</em>, but this allows us
|
||||
to display announcements from your Discord announcements
|
||||
channel(s) on your MyMeta guild's page.
|
||||
</ListItem>
|
||||
</UnorderedList>
|
||||
</Text>
|
||||
<Text fontStyle="italic" mt={3}>
|
||||
Wait, why Discord?
|
||||
</Text>
|
||||
<Text mt={2}>
|
||||
Well, turns out that (at this moment anyway) there is no
|
||||
standardized source of truth for determining who is a "member" of
|
||||
a guild. We built an integration with Discord because just about
|
||||
every guild has a Discord server. Most servers use roles to give
|
||||
certain community members additional privileges, which is often a
|
||||
good approximation for "membership". So, by linking your Discord
|
||||
server and telling us what roles determine what, we can determine
|
||||
which MyMeta users are members of your guild!
|
||||
</Text>
|
||||
<MetaButton
|
||||
size="lg"
|
||||
maxW="15rem"
|
||||
mt={6}
|
||||
as="a"
|
||||
href={discordAuthURL}
|
||||
>
|
||||
Join
|
||||
</MetaButton>
|
||||
</>
|
||||
) : (
|
||||
<Flex fontStyle="italic" mt={4}>
|
||||
Please log in or create a player profile by pressing the "Connect"
|
||||
button to start the guild setup process.
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default GuildSetupAuthCallback;
|
||||
Reference in New Issue
Block a user