-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
346 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |