Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
Seroxdesign committed Mar 13, 2024
2 parents 4fa95e3 + eca2be7 commit 613ffda
Show file tree
Hide file tree
Showing 3 changed files with 346 additions and 0 deletions.
175 changes: 175 additions & 0 deletions packages/web/pages/join/guild/[guildname].tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 70 additions & 0 deletions packages/web/pages/join/guild/auth.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 101 additions & 0 deletions packages/web/pages/join/guild/start.tsx
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 613ffda

Please sign in to comment.