From 80f35039620d2b2f51b82ab848e581df4ff86a80 Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Fri, 29 Sep 2023 11:55:09 -0400 Subject: [PATCH 001/122] unified community page start --- packages/web/pages/community/index.tsx | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 packages/web/pages/community/index.tsx diff --git a/packages/web/pages/community/index.tsx b/packages/web/pages/community/index.tsx new file mode 100644 index 0000000000..e3932535ef --- /dev/null +++ b/packages/web/pages/community/index.tsx @@ -0,0 +1,3 @@ +import React from 'react'; + +export const UnifiedCommunityPage: React.FC = () =>
UnifiedCommunit
; From 39bdcbafacefaad6d7b4c710521dba85d9250a83 Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Mon, 2 Oct 2023 10:39:30 -0400 Subject: [PATCH 002/122] community tabs, tablist and body --- packages/web/pages/community/index.tsx | 107 +++++++++++++++++- packages/web/pages/join/guild/[guildname].tsx | 2 +- packages/web/pages/join/guild/index.tsx | 9 -- packages/web/pages/players.tsx | 2 +- 4 files changed, 107 insertions(+), 13 deletions(-) diff --git a/packages/web/pages/community/index.tsx b/packages/web/pages/community/index.tsx index e3932535ef..6f529c7831 100644 --- a/packages/web/pages/community/index.tsx +++ b/packages/web/pages/community/index.tsx @@ -1,3 +1,106 @@ -import React from 'react'; +import { + Box, + Button, + Flex, + HStack, + MetaHeading, + Tab, + TabList, + TabPanel, + TabPanels, + Tabs, + VStack, +} from '@metafam/ds'; +import { PageContainer } from 'components/Container'; +import { getPatrons, getPSeedPrice } from 'graphql/getPatrons'; +import { getGuilds } from 'graphql/queries/guild'; +import { InferGetStaticPropsType } from 'next'; +import GuildsPage from 'pages/guilds'; +import PatronsPage from 'pages/patrons'; +import Players from 'pages/players'; +import React, { useEffect, useState } from 'react'; -export const UnifiedCommunityPage: React.FC = () =>
UnifiedCommunit
; +type Props = InferGetStaticPropsType; + +export const getStaticProps = async () => { + const patronsLimit = 150; + const patrons = await getPatrons(patronsLimit); + const pSeedPrice = await getPSeedPrice().catch((error) => { + console.error('Error fetching pSeed price', error); + return null; + }); + return { + props: { + guilds: await getGuilds(), + patrons, + pSeedPrice, + }, + revalidate: 1, + }; +}; + +const UnifiedCommunityPage: React.FC = ({ + guilds, + patrons, + pSeedPrice, +}) => ( + + + Community + + + + + Players + + + Guilds + + + Patrons + + {/* Elders */} + + + + + + + + + + + + {/* + Elders + */} + + + + + +); +export default UnifiedCommunityPage; diff --git a/packages/web/pages/join/guild/[guildname].tsx b/packages/web/pages/join/guild/[guildname].tsx index cc3dcc7a44..fae529b69d 100644 --- a/packages/web/pages/join/guild/[guildname].tsx +++ b/packages/web/pages/join/guild/[guildname].tsx @@ -114,7 +114,7 @@ const SetupGuild: React.FC = () => { rounded="lg" p="6" my="6" - w="100%" + w="max-content" align="stretch" justify="space-between" > diff --git a/packages/web/pages/join/guild/index.tsx b/packages/web/pages/join/guild/index.tsx index af77f5d1f3..6362750c62 100644 --- a/packages/web/pages/join/guild/index.tsx +++ b/packages/web/pages/join/guild/index.tsx @@ -113,15 +113,6 @@ export const GuildJoinLanding: React.FC = ({ guilds }) => { {/* Section: Tiers and Perks */} - - {/* Section: Other guilds include... - NOTES - - Was supposed to be limited to 6 guilds with a LOAD MORE button :( - - Is actually limited to first 6 guilds with VIEW ALL GUILDS button that links to https://metagame.wtf/guilds - - This section is not imported as a component because getStaticProps is used to get the list of guilds and it's not possible - to use getStaticProps in a component that is imported to a static page - */} - ; export const getStaticProps = async (): Promise<{ - props: { urqlState: SSRData }; + props: { urqlState?: SSRData }; revalidate: 1; }> => { const [ssrClient, ssrCache] = getSsrClient(); From c2fd25c014c60860601c5b97d113dd0bc6fe2bb5 Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Mon, 2 Oct 2023 10:42:58 -0400 Subject: [PATCH 003/122] community tab fix --- packages/web/utils/menuLinks.ts | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/packages/web/utils/menuLinks.ts b/packages/web/utils/menuLinks.ts index a2194f18a7..41563ed4d8 100644 --- a/packages/web/utils/menuLinks.ts +++ b/packages/web/utils/menuLinks.ts @@ -40,30 +40,8 @@ export const MenuSectionLinks: MenuLinkSet[] = [ }, { label: 'community', - type: 'menu', - menuItems: [ - { - title: 'Players', - explainerText: - 'Find players of MetaGame; their NFTs, their skills & whatever else they put on there', - url: '/players', - icon: 'players', - }, - { - title: 'Patrons', - explainerText: - 'Check the patrons of MetaGame; the ones supporting MetaGame by buying Seeds', - url: '/patrons', - icon: 'patrons', - }, - { - title: 'Guilds', - explainerText: - 'Discover the guilds of MetaGame; groups of players set around more specific goals', - url: '/guilds', - icon: 'guilds', - }, - ], + type: 'internal-link', + url: '/community', }, { label: 'learn', From d08c59abf5524598ed289fbb45348bd681ea1c28 Mon Sep 17 00:00:00 2001 From: nitegeist Date: Tue, 10 Oct 2023 20:14:36 -0500 Subject: [PATCH 004/122] Make tabs width dynamic --- packages/web/pages/community/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/pages/community/index.tsx b/packages/web/pages/community/index.tsx index 6f529c7831..3757e2c282 100644 --- a/packages/web/pages/community/index.tsx +++ b/packages/web/pages/community/index.tsx @@ -51,7 +51,7 @@ const UnifiedCommunityPage: React.FC = ({ From 50ba9973ecef9d94958108008ab02e646dac7b2c Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Tue, 10 Oct 2023 11:28:20 -0400 Subject: [PATCH 005/122] fix dashboard regression --- packages/web/pages/dashboard.tsx | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/web/pages/dashboard.tsx b/packages/web/pages/dashboard.tsx index 3b3e82e927..2132228499 100644 --- a/packages/web/pages/dashboard.tsx +++ b/packages/web/pages/dashboard.tsx @@ -1,5 +1,3 @@ -import { Maybe } from '@metafam/utils'; -import { ConnectedPage } from 'components/ConnectedPage'; import { PageContainer } from 'components/Container'; import { ALL_BOXES, @@ -11,19 +9,13 @@ import { Player, useUpdatePlayerDashboardLayoutMutation as useUpdateLayout, } from 'graphql/autogen/types'; +import { useUser } from 'lib/hooks'; import React, { useCallback, useMemo } from 'react'; import { DisplayOutput, LayoutData } from 'utils/boxTypes'; -const ConnectedDashboardPage: React.FC = () => ( - -); - -export default ConnectedDashboardPage; - -type Props = { player: Maybe }; - -export const DashboardPage: React.FC = ({ player }) => { +const DashboardPage: React.FC = () => { const [{ fetching: persisting }, saveLayoutData] = useUpdateLayout(); + const { user: player } = useUser(); const savedLayoutData = useMemo( () => player?.dashboardLayout @@ -46,14 +38,12 @@ export const DashboardPage: React.FC = ({ player }) => { [saveLayoutData, player], ); - if (!player) return null; - return ( = ({ player }) => { ); }; + +export default DashboardPage; From d66f9f4c33d383b12d755a98f1b65018dcf0363f Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Mon, 23 Oct 2023 08:37:38 -0400 Subject: [PATCH 006/122] make community page dynamic, reduce repitition --- packages/web/components/Landing/WhoAreWe.tsx | 9 +- packages/web/pages/community/index.tsx | 146 +++++++++++-------- 2 files changed, 88 insertions(+), 67 deletions(-) diff --git a/packages/web/components/Landing/WhoAreWe.tsx b/packages/web/components/Landing/WhoAreWe.tsx index 54273e509a..0b7dd7b88c 100644 --- a/packages/web/components/Landing/WhoAreWe.tsx +++ b/packages/web/components/Landing/WhoAreWe.tsx @@ -81,6 +81,7 @@ export const WhoAreWe: React.FC< const { players } = usePlayerFilter(); const topPlayers = useMemo(() => players.slice(0, 7), [players]); + const topPatrons = useMemo(() => patrons.slice(0, 7), [patrons]); return ( 01Players (Builders) {topPlayers && ( - + )} 02Patrons (Funders) - {patrons && } + {topPatrons && } 03Elders (Advisors) - {patrons && } + {players && } 04MetaAlliance (Member projects) - {guilds && } + {guilds && } diff --git a/packages/web/pages/community/index.tsx b/packages/web/pages/community/index.tsx index 3757e2c282..cf39ae950f 100644 --- a/packages/web/pages/community/index.tsx +++ b/packages/web/pages/community/index.tsx @@ -1,8 +1,5 @@ import { Box, - Button, - Flex, - HStack, MetaHeading, Tab, TabList, @@ -19,16 +16,19 @@ import GuildsPage from 'pages/guilds'; import PatronsPage from 'pages/patrons'; import Players from 'pages/players'; import React, { useEffect, useState } from 'react'; +import { useRouter } from 'next/router'; type Props = InferGetStaticPropsType; export const getStaticProps = async () => { const patronsLimit = 150; const patrons = await getPatrons(patronsLimit); + const pSeedPrice = await getPSeedPrice().catch((error) => { console.error('Error fetching pSeed price', error); return null; }); + return { props: { guilds: await getGuilds(), @@ -43,64 +43,84 @@ const UnifiedCommunityPage: React.FC = ({ guilds, patrons, pSeedPrice, -}) => ( - - - Community - - - - - Players - - - Guilds - - - Patrons - - {/* Elders */} - - - - - - - - - - - - {/* - Elders - */} - - - - - -); +}) => { + + const router = useRouter(); + const [activeTab, setActiveTab] = useState<{ link: string, index: number }>({ link: 'Players', index: 0 }); + + const communityTabs = [ + { link: 'Players', component: }, + { link: 'Guilds', component: }, + { link: 'Patrons', component: } + ] + + useEffect(() => { + const { query } = router; + if (query.tab) { + const index = communityTabs.findIndex(tab => tab.link === query.tab); + setActiveTab({ link: query.tab as string, index }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [router.query]); + + const handleTabChange = (index: number) => { + const tab = communityTabs[index]; + setActiveTab({ link: tab.link, index }); + router.push(`/community/?tab=${tab.link}`, undefined, { shallow: true }); + }; + + return ( + + + Community + + + + <> + { + communityTabs.map(({ link }) => { + return( + + {link} + + ) + }) + } + + + + + { + communityTabs.map(({ link, component }) => { + return ( + + {component} + + ) + }) + } + + + + + + + ) + +}; + export default UnifiedCommunityPage; From d125f0bab20b612ab930dd5494ce1f05579889a4 Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Mon, 23 Oct 2023 09:34:35 -0400 Subject: [PATCH 007/122] fix imports, fix bugs --- packages/web/pages/community/index.tsx | 53 +++++++++++--------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/packages/web/pages/community/index.tsx b/packages/web/pages/community/index.tsx index cf39ae950f..a4b8f47708 100644 --- a/packages/web/pages/community/index.tsx +++ b/packages/web/pages/community/index.tsx @@ -12,11 +12,12 @@ import { PageContainer } from 'components/Container'; import { getPatrons, getPSeedPrice } from 'graphql/getPatrons'; import { getGuilds } from 'graphql/queries/guild'; import { InferGetStaticPropsType } from 'next'; +import { useRouter } from 'next/router'; import GuildsPage from 'pages/guilds'; import PatronsPage from 'pages/patrons'; import Players from 'pages/players'; import React, { useEffect, useState } from 'react'; -import { useRouter } from 'next/router'; + type Props = InferGetStaticPropsType; @@ -83,37 +84,29 @@ const UnifiedCommunityPage: React.FC = ({ onChange={handleTabChange} > - <> - { - communityTabs.map(({ link }) => { - return( - - {link} - - ) - }) - } - + { + communityTabs.map(({ link }) => + + {link} + + ) + } - - { - communityTabs.map(({ link, component }) => { - return ( - - {component} - - ) - }) - } - + { + communityTabs.map(({ link, component }) => + + {component} + + ) + } From fd3f5941fcb2260f675b04e3eb752c7cfd66175f Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Tue, 17 Oct 2023 08:27:48 -0400 Subject: [PATCH 008/122] make underline on selected page, change custom block CTA, remove forum. align news --- .../Dashboard/LatestContentTabs/Read.tsx | 11 +- .../components/MegaMenu/DesktopNavLinks.tsx | 244 ++++++++++-------- .../components/MegaMenu/MegaMenuHeader.tsx | 18 +- .../web/components/Section/AddBoxSection.tsx | 26 +- packages/web/utils/menuLinks.ts | 5 - 5 files changed, 172 insertions(+), 132 deletions(-) diff --git a/packages/web/components/Dashboard/LatestContentTabs/Read.tsx b/packages/web/components/Dashboard/LatestContentTabs/Read.tsx index cf7ad9268b..2b538bae20 100644 --- a/packages/web/components/Dashboard/LatestContentTabs/Read.tsx +++ b/packages/web/components/Dashboard/LatestContentTabs/Read.tsx @@ -48,8 +48,17 @@ export const Read: React.FC = () => { w="100%" h="100%" overflow="hidden" + justifyContent={'left'} > - + = (props) => ( - - {MenuSectionLinks.map((section: MenuLinkSet) => ( - - {({ isOpen }) => ( - <> - {section.type === 'menu' ? ( - <> - - ) : ( - - ) - } - > - {section.label} - {section.type === 'menu' && ( - - - - )} - - {section.type === 'menu' && isOpen && ( - - )} - {section?.menuItems?.length && ( - 4 - ? 'repeat(2, 1fr)' - : 'repeat(1, 1fr)' - } - gridGap={2} - w={section.menuItems.length > 4 ? '60rem' : '30rem'} - p={4} - boxShadow="dark-lg" - bg="linear-gradient(180deg, rgba(42, 31, 71, 0.9) 6.18%, rgba(17, 3, 32, 0.86) 140%)" - borderRadius="md" - border={0} - > - {section.menuItems.map((item: MenuLinkItem) => ( - - ))} - - )} - - ) : ( - <> - +export const DesktopNavLinks: React.FC = (props) => { + const router = useRouter(); + return ( + + {MenuSectionLinks.map((section: MenuLinkSet) => ( + + {({ isOpen }) => ( + <> + {section.type === 'menu' ? ( + <> : undefined} + rightIcon={ + isOpen ? ( + + ) : ( + + ) + } > {section.label} {section.type === 'menu' && ( @@ -149,12 +82,101 @@ export const DesktopNavLinks: React.FC = (props) => ( )} - - - )} - - )} - - ))} - -); + {section.type === 'menu' && isOpen && ( + + )} + {section?.menuItems?.length && ( + 4 + ? 'repeat(2, 1fr)' + : 'repeat(1, 1fr)' + } + gridGap={2} + w={section.menuItems.length > 4 ? '60rem' : '30rem'} + p={4} + boxShadow="dark-lg" + bg="linear-gradient(180deg, rgba(42, 31, 71, 0.9) 6.18%, rgba(17, 3, 32, 0.86) 140%)" + borderRadius="md" + border={0} + > + {section.menuItems.map((item: MenuLinkItem) => ( + + ))} + + )} + + ) : ( + <> + + + ) : undefined + } // #79F8FB + > + {section.label} + {section.type === 'menu' && ( + + + + )} + + + + )} + + )} + + ))} + + ); +}; diff --git a/packages/web/components/MegaMenu/MegaMenuHeader.tsx b/packages/web/components/MegaMenu/MegaMenuHeader.tsx index d915853981..87a59cb653 100644 --- a/packages/web/components/MegaMenu/MegaMenuHeader.tsx +++ b/packages/web/components/MegaMenu/MegaMenuHeader.tsx @@ -8,6 +8,7 @@ import { Flex, FlexProps, HamburgerIcon, + HStack, Input, InputGroup, InputLeftElement, @@ -429,6 +430,7 @@ export const MegaMenuHeader: React.FC = () => { backdropFilter="blur(10px)" px={4} py={1.5} + pb="0" h={20} justify="space-between" w="100%" @@ -459,7 +461,7 @@ export const MegaMenuHeader: React.FC = () => { { top="auto" bottom="auto" /> - - - + + + + ( )} + + + Not seeing what you need? + + Consider Building a new block! + + + @@ -190,15 +203,6 @@ export const AddBoxSection = React.forwardRef( Close - - Create a Custom Block - - diff --git a/packages/web/utils/menuLinks.ts b/packages/web/utils/menuLinks.ts index a2194f18a7..d4b688b8f4 100644 --- a/packages/web/utils/menuLinks.ts +++ b/packages/web/utils/menuLinks.ts @@ -106,9 +106,4 @@ export const MenuSectionLinks: MenuLinkSet[] = [ type: 'external-link', url: 'https://discord.gg/XGNFdUaxCH', }, - { - label: 'forum', - type: 'external-link', - url: 'https://forum.metagame.wtf/', - }, ]; From b420d81bbe8fd3a472f6f8bd675d91a9f28263c0 Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Wed, 11 Oct 2023 09:59:02 -0400 Subject: [PATCH 009/122] onboarding screen full page --- packages/web/components/Landing/Onboard.tsx | 2 +- .../web/components/Landing/OnboardingGame/index.tsx | 4 +--- .../web/components/MegaMenu/DesktopPlayerStats.tsx | 10 +++++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/web/components/Landing/Onboard.tsx b/packages/web/components/Landing/Onboard.tsx index 2e1c7884ef..56ca260530 100644 --- a/packages/web/components/Landing/Onboard.tsx +++ b/packages/web/components/Landing/Onboard.tsx @@ -13,7 +13,7 @@ export const Onboard: React.FC = () => { { top="25vh" right={0} width={1} - height="50vh" pointerEvents="none" zIndex={0} /> @@ -447,14 +446,13 @@ export const OnboardingGame: React.FC = (): JSX.Element => { <> = ({ player }) => { const linkURL = usePlayerURL(player); return ( - + @@ -55,7 +55,7 @@ export const DesktopPlayerStats: React.FC = ({ player }) => { borderRadius="md" border={0} px={2} - position="relative" + position="absolute" > = ({ player }) => { color: 'white', borderRadius: 'md', }, - bg: 'transparent' + bg: 'transparent', }} > Profile Wizard @@ -117,7 +117,7 @@ export const DesktopPlayerStats: React.FC = ({ player }) => { color: 'white', borderRadius: 'md', }, - bg: 'transparent' + bg: 'transparent', }} > @@ -132,7 +132,7 @@ export const DesktopPlayerStats: React.FC = ({ player }) => { color: 'white', borderRadius: 'md', }, - bg: 'transparent' + bg: 'transparent', }} > From 463095bd6efd8ec05fd71b9f4c5571befb89b5d5 Mon Sep 17 00:00:00 2001 From: Sero <69639595+Seroxdesign@users.noreply.github.com> Date: Mon, 23 Oct 2023 06:10:51 -0400 Subject: [PATCH 010/122] Update index.tsx --- packages/web/components/Landing/OnboardingGame/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/web/components/Landing/OnboardingGame/index.tsx b/packages/web/components/Landing/OnboardingGame/index.tsx index ca1bb4ddde..28bc0495dd 100644 --- a/packages/web/components/Landing/OnboardingGame/index.tsx +++ b/packages/web/components/Landing/OnboardingGame/index.tsx @@ -452,7 +452,6 @@ export const OnboardingGame: React.FC = (): JSX.Element => { textShadow={`0 0 10px var(--chakra-colors-landing500)`} maxW={{ base: 'full', md: '4xl' }} height="100vh" - border="2px solid red" overflowY="auto" width={{ base: '90%', xl: '100%' }} pl={{ base: 0, md: 10, xl: 0 }} From 802c0b3fad523bcfb17149bc8183441e152618a7 Mon Sep 17 00:00:00 2001 From: Sero <69639595+Seroxdesign@users.noreply.github.com> Date: Mon, 23 Oct 2023 06:11:23 -0400 Subject: [PATCH 011/122] Update index.tsx --- packages/web/components/Landing/OnboardingGame/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/components/Landing/OnboardingGame/index.tsx b/packages/web/components/Landing/OnboardingGame/index.tsx index 28bc0495dd..280512e58e 100644 --- a/packages/web/components/Landing/OnboardingGame/index.tsx +++ b/packages/web/components/Landing/OnboardingGame/index.tsx @@ -451,7 +451,7 @@ export const OnboardingGame: React.FC = (): JSX.Element => { color="var(--chakra-colors-landing550)" textShadow={`0 0 10px var(--chakra-colors-landing500)`} maxW={{ base: 'full', md: '4xl' }} - height="100vh" + height="100%" overflowY="auto" width={{ base: '90%', xl: '100%' }} pl={{ base: 0, md: 10, xl: 0 }} From d27be6fa0a3f57aa5434f62b2ecb259aac3a4fa7 Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Fri, 29 Sep 2023 11:55:09 -0400 Subject: [PATCH 012/122] unified community page start --- packages/web/pages/community/index.tsx | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 packages/web/pages/community/index.tsx diff --git a/packages/web/pages/community/index.tsx b/packages/web/pages/community/index.tsx new file mode 100644 index 0000000000..e3932535ef --- /dev/null +++ b/packages/web/pages/community/index.tsx @@ -0,0 +1,3 @@ +import React from 'react'; + +export const UnifiedCommunityPage: React.FC = () =>
UnifiedCommunit
; From ccc555930bb895b9712dfe5bcb27d312fd2a551c Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Mon, 2 Oct 2023 10:39:30 -0400 Subject: [PATCH 013/122] community tabs, tablist and body --- packages/web/pages/community/index.tsx | 107 +++++++++++++++++- packages/web/pages/join/guild/[guildname].tsx | 2 +- packages/web/pages/join/guild/index.tsx | 9 -- packages/web/pages/players.tsx | 2 +- 4 files changed, 107 insertions(+), 13 deletions(-) diff --git a/packages/web/pages/community/index.tsx b/packages/web/pages/community/index.tsx index e3932535ef..6f529c7831 100644 --- a/packages/web/pages/community/index.tsx +++ b/packages/web/pages/community/index.tsx @@ -1,3 +1,106 @@ -import React from 'react'; +import { + Box, + Button, + Flex, + HStack, + MetaHeading, + Tab, + TabList, + TabPanel, + TabPanels, + Tabs, + VStack, +} from '@metafam/ds'; +import { PageContainer } from 'components/Container'; +import { getPatrons, getPSeedPrice } from 'graphql/getPatrons'; +import { getGuilds } from 'graphql/queries/guild'; +import { InferGetStaticPropsType } from 'next'; +import GuildsPage from 'pages/guilds'; +import PatronsPage from 'pages/patrons'; +import Players from 'pages/players'; +import React, { useEffect, useState } from 'react'; -export const UnifiedCommunityPage: React.FC = () =>
UnifiedCommunit
; +type Props = InferGetStaticPropsType; + +export const getStaticProps = async () => { + const patronsLimit = 150; + const patrons = await getPatrons(patronsLimit); + const pSeedPrice = await getPSeedPrice().catch((error) => { + console.error('Error fetching pSeed price', error); + return null; + }); + return { + props: { + guilds: await getGuilds(), + patrons, + pSeedPrice, + }, + revalidate: 1, + }; +}; + +const UnifiedCommunityPage: React.FC = ({ + guilds, + patrons, + pSeedPrice, +}) => ( + + + Community + + + + + Players + + + Guilds + + + Patrons + + {/* Elders */} + + + + + + + + + + + + {/* + Elders + */} + + + + + +); +export default UnifiedCommunityPage; diff --git a/packages/web/pages/join/guild/[guildname].tsx b/packages/web/pages/join/guild/[guildname].tsx index 28c81f3f55..c8065af723 100644 --- a/packages/web/pages/join/guild/[guildname].tsx +++ b/packages/web/pages/join/guild/[guildname].tsx @@ -154,7 +154,7 @@ const SetupGuild: React.FC = () => { rounded="lg" p="6" my="6" - w="100%" + w="max-content" align="stretch" justify="space-between" > diff --git a/packages/web/pages/join/guild/index.tsx b/packages/web/pages/join/guild/index.tsx index af77f5d1f3..6362750c62 100644 --- a/packages/web/pages/join/guild/index.tsx +++ b/packages/web/pages/join/guild/index.tsx @@ -113,15 +113,6 @@ export const GuildJoinLanding: React.FC = ({ guilds }) => { {/* Section: Tiers and Perks */} - - {/* Section: Other guilds include... - NOTES - - Was supposed to be limited to 6 guilds with a LOAD MORE button :( - - Is actually limited to first 6 guilds with VIEW ALL GUILDS button that links to https://metagame.wtf/guilds - - This section is not imported as a component because getStaticProps is used to get the list of guilds and it's not possible - to use getStaticProps in a component that is imported to a static page - */} - ; export const getStaticProps = async (): Promise<{ - props: { urqlState: SSRData }; + props: { urqlState?: SSRData }; revalidate: 1; }> => { const [ssrClient, ssrCache] = getSsrClient(); From aa42ea3a791283f6dbc2c8de8f71124c68e5c8aa Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Mon, 2 Oct 2023 10:42:58 -0400 Subject: [PATCH 014/122] community tab fix --- packages/web/utils/menuLinks.ts | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/packages/web/utils/menuLinks.ts b/packages/web/utils/menuLinks.ts index d4b688b8f4..2d422a64ec 100644 --- a/packages/web/utils/menuLinks.ts +++ b/packages/web/utils/menuLinks.ts @@ -40,30 +40,8 @@ export const MenuSectionLinks: MenuLinkSet[] = [ }, { label: 'community', - type: 'menu', - menuItems: [ - { - title: 'Players', - explainerText: - 'Find players of MetaGame; their NFTs, their skills & whatever else they put on there', - url: '/players', - icon: 'players', - }, - { - title: 'Patrons', - explainerText: - 'Check the patrons of MetaGame; the ones supporting MetaGame by buying Seeds', - url: '/patrons', - icon: 'patrons', - }, - { - title: 'Guilds', - explainerText: - 'Discover the guilds of MetaGame; groups of players set around more specific goals', - url: '/guilds', - icon: 'guilds', - }, - ], + type: 'internal-link', + url: '/community', }, { label: 'learn', From ce37975b7f81eaf1aba7adde8cea4ceb3c701ab0 Mon Sep 17 00:00:00 2001 From: nitegeist Date: Tue, 10 Oct 2023 20:14:36 -0500 Subject: [PATCH 015/122] Make tabs width dynamic --- packages/web/pages/community/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/pages/community/index.tsx b/packages/web/pages/community/index.tsx index 6f529c7831..3757e2c282 100644 --- a/packages/web/pages/community/index.tsx +++ b/packages/web/pages/community/index.tsx @@ -51,7 +51,7 @@ const UnifiedCommunityPage: React.FC = ({ From c001ec3d706ad7ef126c65efc86fa93b24bb4871 Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Mon, 23 Oct 2023 08:37:38 -0400 Subject: [PATCH 016/122] make community page dynamic, reduce repitition --- packages/web/components/Landing/WhoAreWe.tsx | 9 +- packages/web/pages/community/index.tsx | 146 +++++++++++-------- 2 files changed, 88 insertions(+), 67 deletions(-) diff --git a/packages/web/components/Landing/WhoAreWe.tsx b/packages/web/components/Landing/WhoAreWe.tsx index 54273e509a..0b7dd7b88c 100644 --- a/packages/web/components/Landing/WhoAreWe.tsx +++ b/packages/web/components/Landing/WhoAreWe.tsx @@ -81,6 +81,7 @@ export const WhoAreWe: React.FC< const { players } = usePlayerFilter(); const topPlayers = useMemo(() => players.slice(0, 7), [players]); + const topPatrons = useMemo(() => patrons.slice(0, 7), [patrons]); return ( 01Players (Builders) {topPlayers && ( - + )} 02Patrons (Funders) - {patrons && } + {topPatrons && } 03Elders (Advisors) - {patrons && } + {players && } 04MetaAlliance (Member projects) - {guilds && } + {guilds && } diff --git a/packages/web/pages/community/index.tsx b/packages/web/pages/community/index.tsx index 3757e2c282..cf39ae950f 100644 --- a/packages/web/pages/community/index.tsx +++ b/packages/web/pages/community/index.tsx @@ -1,8 +1,5 @@ import { Box, - Button, - Flex, - HStack, MetaHeading, Tab, TabList, @@ -19,16 +16,19 @@ import GuildsPage from 'pages/guilds'; import PatronsPage from 'pages/patrons'; import Players from 'pages/players'; import React, { useEffect, useState } from 'react'; +import { useRouter } from 'next/router'; type Props = InferGetStaticPropsType; export const getStaticProps = async () => { const patronsLimit = 150; const patrons = await getPatrons(patronsLimit); + const pSeedPrice = await getPSeedPrice().catch((error) => { console.error('Error fetching pSeed price', error); return null; }); + return { props: { guilds: await getGuilds(), @@ -43,64 +43,84 @@ const UnifiedCommunityPage: React.FC = ({ guilds, patrons, pSeedPrice, -}) => ( - - - Community - - - - - Players - - - Guilds - - - Patrons - - {/* Elders */} - - - - - - - - - - - - {/* - Elders - */} - - - - - -); +}) => { + + const router = useRouter(); + const [activeTab, setActiveTab] = useState<{ link: string, index: number }>({ link: 'Players', index: 0 }); + + const communityTabs = [ + { link: 'Players', component: }, + { link: 'Guilds', component: }, + { link: 'Patrons', component: } + ] + + useEffect(() => { + const { query } = router; + if (query.tab) { + const index = communityTabs.findIndex(tab => tab.link === query.tab); + setActiveTab({ link: query.tab as string, index }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [router.query]); + + const handleTabChange = (index: number) => { + const tab = communityTabs[index]; + setActiveTab({ link: tab.link, index }); + router.push(`/community/?tab=${tab.link}`, undefined, { shallow: true }); + }; + + return ( + + + Community + + + + <> + { + communityTabs.map(({ link }) => { + return( + + {link} + + ) + }) + } + + + + + { + communityTabs.map(({ link, component }) => { + return ( + + {component} + + ) + }) + } + + + + + + + ) + +}; + export default UnifiedCommunityPage; From 5b74dfa39ba87ebbe7aefa30e5ad71c1ec03caea Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Mon, 23 Oct 2023 09:34:35 -0400 Subject: [PATCH 017/122] fix imports, fix bugs --- packages/web/pages/community/index.tsx | 53 +++++++++++--------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/packages/web/pages/community/index.tsx b/packages/web/pages/community/index.tsx index cf39ae950f..a4b8f47708 100644 --- a/packages/web/pages/community/index.tsx +++ b/packages/web/pages/community/index.tsx @@ -12,11 +12,12 @@ import { PageContainer } from 'components/Container'; import { getPatrons, getPSeedPrice } from 'graphql/getPatrons'; import { getGuilds } from 'graphql/queries/guild'; import { InferGetStaticPropsType } from 'next'; +import { useRouter } from 'next/router'; import GuildsPage from 'pages/guilds'; import PatronsPage from 'pages/patrons'; import Players from 'pages/players'; import React, { useEffect, useState } from 'react'; -import { useRouter } from 'next/router'; + type Props = InferGetStaticPropsType; @@ -83,37 +84,29 @@ const UnifiedCommunityPage: React.FC = ({ onChange={handleTabChange} > - <> - { - communityTabs.map(({ link }) => { - return( - - {link} - - ) - }) - } - + { + communityTabs.map(({ link }) => + + {link} + + ) + } - - { - communityTabs.map(({ link, component }) => { - return ( - - {component} - - ) - }) - } - + { + communityTabs.map(({ link, component }) => + + {component} + + ) + } From d1b4f3f8ba97b5bbdde4c2c85e9e6e8e7e78d266 Mon Sep 17 00:00:00 2001 From: Seroxdesign Date: Tue, 24 Oct 2023 09:55:03 -0400 Subject: [PATCH 018/122] questchain fix --- .../Dashboard/LatestContentTabs/Watch.tsx | 34 +++++++++++-------- .../web/components/QuestChain/UploadProof.tsx | 7 ++-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/web/components/Dashboard/LatestContentTabs/Watch.tsx b/packages/web/components/Dashboard/LatestContentTabs/Watch.tsx index 51134b7f18..778366d518 100644 --- a/packages/web/components/Dashboard/LatestContentTabs/Watch.tsx +++ b/packages/web/components/Dashboard/LatestContentTabs/Watch.tsx @@ -23,20 +23,26 @@ export const Watch: React.FC = () => { useEffect(() => { const abortController = new AbortController(); const load = async () => { - const res = await fetch(URL, { - signal: abortController.signal, - }).then((r) => r.json()); - const videosList = res.items - ? res.items.map((video: GoogleApiYouTubePlaylistItemResource) => ({ - id: video.id, - videoId: video.snippet.resourceId.videoId, - title: video.snippet.title, - })) - : []; - setNextPageToken(res.nextPageToken); - setVideos(videosList); - setCount(res.pageInfo ? res.pageInfo.totalResults : null); - setLoading(false); + try { + const res: any = await fetch(URL, { + signal: abortController.signal, + }).then((r) => r.json()); + const videosList = res.items + ? res.items.map((video: GoogleApiYouTubePlaylistItemResource) => ({ + id: video.id, + videoId: video.snippet.resourceId.videoId, + title: video.snippet.title, + })) + : []; + setNextPageToken(res.nextPageToken); + setVideos(videosList); + setCount(res.pageInfo ? res.pageInfo.totalResults : null); + setLoading(false); + } catch (err) { + if ((err as Error).name !== 'AbortError') { + throw err; + } + } }; load(); diff --git a/packages/web/components/QuestChain/UploadProof.tsx b/packages/web/components/QuestChain/UploadProof.tsx index a7b1db3829..757897147c 100644 --- a/packages/web/components/QuestChain/UploadProof.tsx +++ b/packages/web/components/QuestChain/UploadProof.tsx @@ -121,12 +121,8 @@ export const UploadProof: React.FC<{ questChain.version, provider.getSigner(), ); - const tx = await (questChain.version === '1' - ? (contract as contracts.V1.QuestChain).submitProofs( - [questId], - [details], - ) + ? (contract as contracts.V1.QuestChain).submitProofs([0], [details]) : (contract as contracts.V0.QuestChain).submitProof(questId, details)); addToast({ description: 'Transaction submitted. Waiting for 1 block confirmation', @@ -156,6 +152,7 @@ export const UploadProof: React.FC<{ duration: 2000, isClosable: true, }); + console.error(error); errorHandler(error as Error); } From 0523bc13476a68596d9518f8986a8f2cddc51456 Mon Sep 17 00:00:00 2001 From: luxumbra Date: Thu, 28 Sep 2023 00:39:11 +0100 Subject: [PATCH 019/122] Commit wip --- packages/web/pages/pathsandplaybooks.tsx | 60 ++++++++++++++++++++++++ packages/web/utils/menuLinks.ts | 53 +++++++++++---------- 2 files changed, 89 insertions(+), 24 deletions(-) create mode 100644 packages/web/pages/pathsandplaybooks.tsx diff --git a/packages/web/pages/pathsandplaybooks.tsx b/packages/web/pages/pathsandplaybooks.tsx new file mode 100644 index 0000000000..d744e59a5f --- /dev/null +++ b/packages/web/pages/pathsandplaybooks.tsx @@ -0,0 +1,60 @@ +import { Box, Center, Link, Text, VStack } from '@metafam/ds'; +import { PageContainer } from 'components/Container'; +import { HeadComponent } from 'components/Seo'; +import { QuestStatus_Enum } from 'graphql/autogen/types'; +import { getQuests } from 'graphql/getQuests'; +import { InferGetStaticPropsType } from 'next'; +import React from 'react'; + +type Props = InferGetStaticPropsType; + +export const getStaticProps = async () => ({ + props: { + quests: await getQuests({status: QuestStatus_Enum.Open}), + // playbooks: await getPlaybooks() + }, + revalidate: 1, +}); + +const PathsAndPlaybooksPage: React.FC = ({ quests }) => { + console.log({quests}); + + return ( + + + + {/* VStack is used to make a consistent gap between the Join CTA and the Guilds list */} + +
+ + Paths and playbooks. + +
+ + + {/* {quests && quests.length > 0 && ( + + quests.map((quest) => { + return ( + {quest.title} + ) + + }) + )} */} + +
+
+ ); +} + +export default PathsAndPlaybooksPage; diff --git a/packages/web/utils/menuLinks.ts b/packages/web/utils/menuLinks.ts index 2d422a64ec..7befce4b1a 100644 --- a/packages/web/utils/menuLinks.ts +++ b/packages/web/utils/menuLinks.ts @@ -43,31 +43,36 @@ export const MenuSectionLinks: MenuLinkSet[] = [ type: 'internal-link', url: '/community', }, + // { + // label: 'learn', + // type: 'menu', + // menuItems: [ + // { + // title: 'Roles', + // explainerText: + // 'Find about all roles in MetaGame, see which ones are open & how to play them', + // // url: 'https://meta-game.notion.site/Internal-Roles-Guilds-bec3a0437f684322b650dbb7aca616e8', + // url: '/roles', + // icon: 'roles', + // }, + // { + // title: 'Playbooks', + // explainerText: descriptions.playbooks, + // url: '/learn/playbooks', + // icon: 'playbooks', + // }, + // { + // title: 'The Great Houses', + // explainerText: descriptions.thegreathouses, + // url: '/learn/thegreathouses', + // icon: 'thegreathouses', + // }, + // ], + // }, { - label: 'learn', - type: 'menu', - menuItems: [ - { - title: 'Roles', - explainerText: - 'Find about all roles in MetaGame, see which ones are open & how to play them', - // url: 'https://meta-game.notion.site/Internal-Roles-Guilds-bec3a0437f684322b650dbb7aca616e8', - url: '/roles', - icon: 'roles', - }, - { - title: 'Playbooks', - explainerText: descriptions.playbooks, - url: '/learn/playbooks', - icon: 'playbooks', - }, - { - title: 'The Great Houses', - explainerText: descriptions.thegreathouses, - url: '/learn/thegreathouses', - icon: 'thegreathouses', - }, - ], + label: 'paths & playbooks', + type: 'internal-link', + url: '/paths-and-playbooks' }, { label: 'quests', From f3be2f958d5124e51a166f34176b1132f5b772b5 Mon Sep 17 00:00:00 2001 From: luxumbra Date: Tue, 3 Oct 2023 01:38:01 +0100 Subject: [PATCH 020/122] Fixes "Merge role paths, playbooks & great houses into a single page" - Just need to add numbers or data source for `seedsEarned` --- packages/design-system/src/MetaTile.tsx | 191 +++++++++++++++------ packages/web/pages/paths-and-playbooks.tsx | 180 +++++++++++++++++++ packages/web/pages/pathsandplaybooks.tsx | 60 ------- packages/web/utils/questChains.ts | 174 +++++++++++++++++-- 4 files changed, 485 insertions(+), 120 deletions(-) create mode 100644 packages/web/pages/paths-and-playbooks.tsx delete mode 100644 packages/web/pages/pathsandplaybooks.tsx diff --git a/packages/design-system/src/MetaTile.tsx b/packages/design-system/src/MetaTile.tsx index 9d0580d901..894ab6b9da 100644 --- a/packages/design-system/src/MetaTile.tsx +++ b/packages/design-system/src/MetaTile.tsx @@ -1,5 +1,12 @@ -import { Flex, FlexProps, Image, StackProps, VStack } from '@chakra-ui/react'; -import { Maybe } from '@metafam/utils'; +import { + Box, + Flex, + FlexProps, + Image, + StackProps, + VStack, +} from '@chakra-ui/react'; +import { Maybe, Values } from '@metafam/utils'; import React, { PropsWithChildren, useEffect, useRef } from 'react'; import VanillaTilt from 'vanilla-tilt'; @@ -96,60 +103,144 @@ export const MetaTile = React.forwardRef< export const MetaTilePlaybook = React.forwardRef< HTMLDivElement, - FlexProps & MetaTileProps & { image: string } ->(({ noTilt = false, maxTilt = 6, children, image, ...props }, fwdRef) => { - const tilt = useRef>(null); + FlexProps & MetaTileProps & { image: string; isPath?: boolean } +>( + ( + { noTilt = false, maxTilt = 6, children, image, isPath = false, ...props }, + fwdRef, + ) => { + const tilt = useRef>(null); - useEffect(() => { - if (!noTilt && tilt.current) { - VanillaTilt.init(tilt.current); - } - }, [noTilt]); + useEffect(() => { + if (!noTilt && tilt.current) { + VanillaTilt.init(tilt.current); + } + }, [noTilt]); - return ( - { - tilt.current = elem; - if (typeof fwdRef === 'function') { - fwdRef(elem); - } else if (fwdRef) { - // eslint-disable-next-line no-param-reassign - fwdRef.current = elem; - } - }} - > - + return ( { + tilt.current = elem; + if (typeof fwdRef === 'function') { + fwdRef(elem); + } else if (fwdRef) { + // eslint-disable-next-line no-param-reassign + fwdRef.current = elem; + } + }} > - {children} + + + {children} + {isPath ? : null} + + {isPath ? : null} - - ); -}); + ); + }, +); + +type MetaPathCosmeticOptions = 'edges' | 'overlay'; +interface MetaPathCosmeticsProps { + type: MetaPathCosmeticOptions; +} + +/** + * `MetaPathCosmetics` - The cosmetic elements of the MetaTilePlaybook component when used in paths & playbooks + * @param type 'edges | overlay' - The type of cosmetic to render + * @returns + */ +export const MetaPathCosmetics: React.FC = ({ + type, +}) => { + if (type === 'edges') { + return ( +
{Object.entries(QuestChainsCategoriesDetails).map((category, i) => { - const { name: categoryName, title: categoryTitle } = category[1]; + const { + name: categoryName, + title: categoryTitle, + description, + } = category[1]; const categoryItems = Object.entries( QuestChainPathsAndPlaybooksDetails, ).filter(([name, { category: cat }]) => cat === categoryName); @@ -91,12 +96,11 @@ const PathsAndPlaybooksPage: React.FC = () => { > {categoryTitle} - {/* Description wasn't in the design but it is an option */} - {/* {description ? ( - + {description ? ( + {description} - ) : null} */} + ) : null} {categoryItems.length > 0 ? ( Date: Fri, 13 Oct 2023 15:18:08 +0100 Subject: [PATCH 031/122] rename to Academy. - also removed overlay on cards, replaced with text shadow --- packages/design-system/src/MetaTile.tsx | 2 +- .../{paths-and-playbooks.tsx => academy.tsx} | 41 ++++++++++++------- packages/web/utils/menuLinks.ts | 4 +- 3 files changed, 29 insertions(+), 18 deletions(-) rename packages/web/pages/{paths-and-playbooks.tsx => academy.tsx} (84%) diff --git a/packages/design-system/src/MetaTile.tsx b/packages/design-system/src/MetaTile.tsx index 8d3432d552..da2ea8ca34 100644 --- a/packages/design-system/src/MetaTile.tsx +++ b/packages/design-system/src/MetaTile.tsx @@ -234,7 +234,7 @@ export const MetaTilePathPlaybook = React.forwardRef< {children} - + {/* */} ); }, diff --git a/packages/web/pages/paths-and-playbooks.tsx b/packages/web/pages/academy.tsx similarity index 84% rename from packages/web/pages/paths-and-playbooks.tsx rename to packages/web/pages/academy.tsx index 287ede2ba2..f7b139b370 100644 --- a/packages/web/pages/paths-and-playbooks.tsx +++ b/packages/web/pages/academy.tsx @@ -29,7 +29,11 @@ export const getStaticProps = async () => ({ revalidate: 1, }); -const PathsAndPlaybooksPage: React.FC = () => { +/** + * This page merges Paths & Playbooks into one page. + * @returns All of the paths, playbooks, and great houses categorised in a single page. + */ +const AcademyPage: React.FC = () => { const carouselGap = useBreakpointValue({ base: 8, md: 6, lg: 32 }) || 32; const makeItemPath = (type: PathPlaybookType): string => { let urlPath: string; @@ -54,21 +58,27 @@ const PathsAndPlaybooksPage: React.FC = () => { return ( - - Paths & Playbooks - + + + The Academy + + + This place contains paths, playbooks and all things educational, + related to MetaGame and the wider web3 community. + + {Object.entries(QuestChainsCategoriesDetails).map((category, i) => { const { @@ -97,7 +107,7 @@ const PathsAndPlaybooksPage: React.FC = () => { {categoryTitle} {description ? ( - + {description} ) : null} @@ -194,6 +204,7 @@ const Card: React.FC = ({ title, link, image, length, index }) => ( p={0} fontSize={{ base: 'xs', sm: '3xl' }} fontWeight={{ base: 900, xl: 900 }} + textShadow={{ base: '0 0 0.5rem rgba(0,0,0,0.8)' }} align="center" noOfLines={4} width="full" @@ -206,4 +217,4 @@ const Card: React.FC = ({ title, link, image, length, index }) => ( ); -export default PathsAndPlaybooksPage; +export default AcademyPage; diff --git a/packages/web/utils/menuLinks.ts b/packages/web/utils/menuLinks.ts index 70f3d91769..a8c741ecaa 100644 --- a/packages/web/utils/menuLinks.ts +++ b/packages/web/utils/menuLinks.ts @@ -44,9 +44,9 @@ export const MenuSectionLinks: MenuLinkSet[] = [ url: '/community', }, { - label: 'paths & playbooks', + label: 'academy', type: 'internal-link', - url: '/paths-and-playbooks', + url: '/academy', }, { label: 'quests', From 72af99a2bc0d95481de4f8174728f5a58fc36f38 Mon Sep 17 00:00:00 2001 From: luxumbra Date: Wed, 18 Oct 2023 13:06:58 +0100 Subject: [PATCH 032/122] smol tweaks & responsive improvements - WIP --- packages/design-system/src/MetaTile.tsx | 12 ++++---- packages/web/components/Carousel/Item.tsx | 4 +-- packages/web/pages/academy.tsx | 37 +++++++++++------------ 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/packages/design-system/src/MetaTile.tsx b/packages/design-system/src/MetaTile.tsx index da2ea8ca34..7179156726 100644 --- a/packages/design-system/src/MetaTile.tsx +++ b/packages/design-system/src/MetaTile.tsx @@ -191,8 +191,8 @@ export const MetaTilePathPlaybook = React.forwardRef< data-tilt-easing="cubic-bezier(.03,.98,.52,.99)" h="full" w="full" - minH={{ base: 32, xl: '30rem' }} - mr={{ base: 2.5, xl: 6 }} + minH={{ base: 32, xl: '22.5rem', '2xl': '30rem' }} + mr={{ base: 2.5, xl: 4, '2xl': 6 }} borderRightRadius={{ base: 'lg', xl: '2xl' }} ref={(elem) => { tilt.current = elem; @@ -208,7 +208,7 @@ export const MetaTilePathPlaybook = React.forwardRef< src={image} position={'relative'} borderRightRadius={{ base: 'lg', xl: '2xl' }} - maxW={{ base: 24, xl: '20rem' }} + maxW={{ base: 24, xl: '15rem', '2xl': '20rem' }} objectFit={'cover'} borderLeftRadius={0} zIndex={0} @@ -224,7 +224,7 @@ export const MetaTilePathPlaybook = React.forwardRef< }} borderRightRadius={{ base: 'lg', xl: '2xl' }} p={{ base: 1, xl: 10 }} - maxW={{ base: 24, xl: '20rem' }} // (2 / 3.5) = ~0.571 aspect ratio desired + maxW={{ base: 24, xl: '15rem', '2xl': '20rem' }} // (2 / 3.5) = ~0.571 aspect ratio desired w="full" h="full" align="stretch" @@ -234,7 +234,7 @@ export const MetaTilePathPlaybook = React.forwardRef< {children} - {/* */} + ); }, @@ -293,7 +293,7 @@ export const MetaTilePathCosmetics: React.FC = ({ inset={0} right="auto" width="full" - maxW={{ base: 24, xl: '20rem' }} + maxW={{ base: 24, xl: '15rem', '2xl': '20rem' }} background={ 'linear-gradient(180deg, rgba(0, 0, 0, 0.35) 0.34%, rgba(0, 0, 0, 0.00) 34.08%, rgba(13, 0, 19, 0.35) 59.18%, rgba(20, 0, 28, 0.85) 100%)' } diff --git a/packages/web/components/Carousel/Item.tsx b/packages/web/components/Carousel/Item.tsx index 1d12f53133..c4afd0caea 100644 --- a/packages/web/components/Carousel/Item.tsx +++ b/packages/web/components/Carousel/Item.tsx @@ -44,8 +44,8 @@ export const Item: React.FC = ({ children, index }) => { onKeyUp={handleKeyUp} onKeyDown={handleKeyDown} w={shrinkItems ? '100%' : `${itemWidth}px`} - flex={shrinkItems ? { base: '0 0', xl: '0 0 344px' } : 'inherit'} - maxW={shrinkItems ? '344px' : 'inherit'} + flex={shrinkItems ? { base: '0 0', xl: '0 0 270px', '2xl': '0 0 344px' } : 'inherit'} + maxW={shrinkItems ? {base: '270px', '2xl': '344px'} : 'inherit'} _notLast={{ mr: `${gap}px`, }} diff --git a/packages/web/pages/academy.tsx b/packages/web/pages/academy.tsx index f7b139b370..b8ec94c0d0 100644 --- a/packages/web/pages/academy.tsx +++ b/packages/web/pages/academy.tsx @@ -13,7 +13,6 @@ import { Carousel } from 'components/Carousel'; import { PageContainer } from 'components/Container'; import { MetaLink } from 'components/Link'; import { HeadComponent } from 'components/Seo'; -import { InferGetStaticPropsType } from 'next'; import React from 'react'; import { PathPlaybookType, @@ -22,19 +21,12 @@ import { QuestChainsCategoriesDetails, } from 'utils/questChains'; -type Props = InferGetStaticPropsType; - -export const getStaticProps = async () => ({ - props: {}, - revalidate: 1, -}); - /** * This page merges Paths & Playbooks into one page. * @returns All of the paths, playbooks, and great houses categorised in a single page. */ -const AcademyPage: React.FC = () => { - const carouselGap = useBreakpointValue({ base: 8, md: 6, lg: 32 }) || 32; +const AcademyPage: React.FC = () => { + const carouselGap = useBreakpointValue({ base: 8, md: 6, xl: 24, '2xl': 32 }) || 32; const makeItemPath = (type: PathPlaybookType): string => { let urlPath: string; @@ -62,21 +54,26 @@ const AcademyPage: React.FC = () => { description="MetaGame is a Massive Online Coordination Game! The Academy is full of Paths and Playbooks to help you find your way and level up in MetaGame & life." url="https://metagame.wtf/paths-and-playbooks" /> - - + + The Academy - + This place contains paths, playbooks and all things educational, - related to MetaGame and the wider web3 community. + related to MetaGame. @@ -114,10 +111,10 @@ const AcademyPage: React.FC = () => { {categoryItems.length > 0 ? ( = ({ title, link, image, length, index }) => ( image={image} index={index} length={length} - maxW={{ base: 24, xl: '20rem' }} + maxW={{ base: 24, xl: '15rem', '2xl': '20rem' }} > Date: Wed, 18 Oct 2023 23:26:52 +0100 Subject: [PATCH 033/122] tweaks to slider to fix issues with position of the items on prev & next. Also fixd disabling the next button on the last item. --- packages/design-system/src/MetaTile.tsx | 5 ++- .../components/Carousel/CarouselContext.tsx | 2 + packages/web/components/Carousel/Item.tsx | 8 +++- packages/web/components/Carousel/Slider.tsx | 13 +++--- packages/web/components/Carousel/index.tsx | 21 +++++++++- packages/web/pages/academy.tsx | 41 ++++++++++++------- 6 files changed, 65 insertions(+), 25 deletions(-) diff --git a/packages/design-system/src/MetaTile.tsx b/packages/design-system/src/MetaTile.tsx index 7179156726..e1e396743d 100644 --- a/packages/design-system/src/MetaTile.tsx +++ b/packages/design-system/src/MetaTile.tsx @@ -192,6 +192,7 @@ export const MetaTilePathPlaybook = React.forwardRef< h="full" w="full" minH={{ base: 32, xl: '22.5rem', '2xl': '30rem' }} + maxW={{ base: 24, xl: '15rem', '2xl': '20rem' }} mr={{ base: 2.5, xl: 4, '2xl': 6 }} borderRightRadius={{ base: 'lg', xl: '2xl' }} ref={(elem) => { @@ -229,6 +230,7 @@ export const MetaTilePathPlaybook = React.forwardRef< h="full" align="stretch" justify="space-between" + gap="2px" {...props} > {children} @@ -262,10 +264,9 @@ export const MetaTilePathCosmetics: React.FC = ({ justifyContent="flex-end" gap="2px" position={'absolute'} - top={0} + inset={0} left={'auto'} right={{ base: '-0.75rem', xl: '-1.625rem' }} - bottom={0} width={{ base: 3, xl: 6 }} aria-hidden="true" > diff --git a/packages/web/components/Carousel/CarouselContext.tsx b/packages/web/components/Carousel/CarouselContext.tsx index 3bb4adbb78..0ffcf93f77 100644 --- a/packages/web/components/Carousel/CarouselContext.tsx +++ b/packages/web/components/Carousel/CarouselContext.tsx @@ -25,6 +25,7 @@ type CarouselContextType = { shrinkItems: boolean; hidePositions: boolean; hideNav: boolean; + itemsToShow: number; }; export const CarouselContext = createContext({ @@ -46,6 +47,7 @@ export const CarouselContext = createContext({ shrinkItems: false, hidePositions: false, hideNav: true, + itemsToShow: 4, }); export const useCarouselContext = () => useContext(CarouselContext); diff --git a/packages/web/components/Carousel/Item.tsx b/packages/web/components/Carousel/Item.tsx index c4afd0caea..f474257dfa 100644 --- a/packages/web/components/Carousel/Item.tsx +++ b/packages/web/components/Carousel/Item.tsx @@ -44,8 +44,12 @@ export const Item: React.FC = ({ children, index }) => { onKeyUp={handleKeyUp} onKeyDown={handleKeyDown} w={shrinkItems ? '100%' : `${itemWidth}px`} - flex={shrinkItems ? { base: '0 0', xl: '0 0 270px', '2xl': '0 0 344px' } : 'inherit'} - maxW={shrinkItems ? {base: '270px', '2xl': '344px'} : 'inherit'} + flex={ + shrinkItems + ? { base: '0 0', xl: '0 0 16.875rem', '2xl': '0 0 21.5rem' } + : 'inherit' + } + maxW={shrinkItems ? { base: '16.875rem', '2xl': '21.5rem' } : 'inherit'} _notLast={{ mr: `${gap}px`, }} diff --git a/packages/web/components/Carousel/Slider.tsx b/packages/web/components/Carousel/Slider.tsx index 75e8234c33..9b72fba1d3 100644 --- a/packages/web/components/Carousel/Slider.tsx +++ b/packages/web/components/Carousel/Slider.tsx @@ -24,6 +24,7 @@ export const Slider: React.FC = ({ children }) => { gap, hidePositions, hideNav, + itemsToShow, } = useCarouselContext(); const [ref, { width }] = useBoundingRect(); @@ -37,12 +38,12 @@ export const Slider: React.FC = ({ children }) => { const handleFocus = () => setTrackIsActive(true); return ( - + = ({ children }) => { color="gray.200" variant="link" minW={0} - isDisabled={activeItem === itemsCount - 1} - opacity={activeItem === itemsCount - 1 ? '0!important' : 1} + isDisabled={activeItem + itemsToShow >= itemsCount} + opacity={activeItem + itemsToShow >= itemsCount ? '0!important' : 1} pointerEvents="all" transition="opacity 0.2s ease-in-out" /> diff --git a/packages/web/components/Carousel/index.tsx b/packages/web/components/Carousel/index.tsx index 855f433f95..03d464381d 100644 --- a/packages/web/components/Carousel/index.tsx +++ b/packages/web/components/Carousel/index.tsx @@ -18,6 +18,7 @@ export const Carousel: React.FC<{ shrinkItems?: boolean; hidePositions?: boolean; hideNav?: boolean; + itemsToShow?: number; children: JSX.Element[]; }> = ({ children, @@ -25,6 +26,7 @@ export const Carousel: React.FC<{ hideNav = true, shrinkItems = false, hidePositions = false, + itemsToShow = 4, }) => { const [trackIsActive, setTrackIsActive] = useState(false); const [isSubmittingProof, setIsSubmittingProof] = useState(false); @@ -59,7 +61,23 @@ export const Carousel: React.FC<{ setMultiplier(0.5); setConstraint(2); } - }, [isBetweenBaseAndLg, isGreaterThanLg, sliderWidth, gap]); + if (shrinkItems && isBetweenBaseAndLg) { + setItemWidth(270); + setConstraint(1); + } + + if (shrinkItems && isGreaterThanLg) { + setItemWidth(344); + setConstraint(2); + } + }, [ + isBetweenBaseAndLg, + isGreaterThanLg, + sliderWidth, + gap, + shrinkItems, + itemsToShow, + ]); return ( {children} diff --git a/packages/web/pages/academy.tsx b/packages/web/pages/academy.tsx index b8ec94c0d0..9f40eaf511 100644 --- a/packages/web/pages/academy.tsx +++ b/packages/web/pages/academy.tsx @@ -26,7 +26,8 @@ import { * @returns All of the paths, playbooks, and great houses categorised in a single page. */ const AcademyPage: React.FC = () => { - const carouselGap = useBreakpointValue({ base: 8, md: 6, xl: 24, '2xl': 32 }) || 32; + const carouselGap = + useBreakpointValue({ base: 8, md: 6, xl: 24, '2xl': 32 }) || 32; const makeItemPath = (type: PathPlaybookType): string => { let urlPath: string; @@ -54,7 +55,11 @@ const AcademyPage: React.FC = () => { description="MetaGame is a Massive Online Coordination Game! The Academy is full of Paths and Playbooks to help you find your way and level up in MetaGame & life." url="https://metagame.wtf/paths-and-playbooks" /> - + { {categoryItems.length > 0 ? ( = ({ title, link, image, length, index }) => ( - - - + + + Date: Thu, 19 Oct 2023 02:22:22 +0100 Subject: [PATCH 034/122] more responsive styles. base, md, xl & 2xl. fixed a couple of display issues. --- packages/design-system/src/MetaTile.tsx | 10 ++--- .../components/Carousel/CarouselContext.tsx | 11 +++++- packages/web/components/Carousel/Item.tsx | 11 +++--- packages/web/components/Carousel/Slider.tsx | 25 +++++++----- packages/web/components/Carousel/index.tsx | 39 ++++++++++++++----- packages/web/pages/academy.tsx | 24 ++++++------ 6 files changed, 78 insertions(+), 42 deletions(-) diff --git a/packages/design-system/src/MetaTile.tsx b/packages/design-system/src/MetaTile.tsx index e1e396743d..eca3275614 100644 --- a/packages/design-system/src/MetaTile.tsx +++ b/packages/design-system/src/MetaTile.tsx @@ -191,8 +191,8 @@ export const MetaTilePathPlaybook = React.forwardRef< data-tilt-easing="cubic-bezier(.03,.98,.52,.99)" h="full" w="full" - minH={{ base: 32, xl: '22.5rem', '2xl': '30rem' }} - maxW={{ base: 24, xl: '15rem', '2xl': '20rem' }} + minH={{ base: 32, md: '15.187rem', xl: '22.5rem', '2xl': '30rem' }} + maxW={{ base: 24, md: '9.875rem', xl: '15rem', '2xl': '20rem' }} mr={{ base: 2.5, xl: 4, '2xl': 6 }} borderRightRadius={{ base: 'lg', xl: '2xl' }} ref={(elem) => { @@ -209,7 +209,7 @@ export const MetaTilePathPlaybook = React.forwardRef< src={image} position={'relative'} borderRightRadius={{ base: 'lg', xl: '2xl' }} - maxW={{ base: 24, xl: '15rem', '2xl': '20rem' }} + maxW={{ base: 24, md: '9.875rem', xl: '15rem', '2xl': '20rem' }} objectFit={'cover'} borderLeftRadius={0} zIndex={0} @@ -225,7 +225,7 @@ export const MetaTilePathPlaybook = React.forwardRef< }} borderRightRadius={{ base: 'lg', xl: '2xl' }} p={{ base: 1, xl: 10 }} - maxW={{ base: 24, xl: '15rem', '2xl': '20rem' }} // (2 / 3.5) = ~0.571 aspect ratio desired + maxW={{ base: 24, md: '9.875rem', xl: '15rem', '2xl': '20rem' }} // (2 / 3.5) = ~0.571 aspect ratio desired w="full" h="full" align="stretch" @@ -294,7 +294,7 @@ export const MetaTilePathCosmetics: React.FC = ({ inset={0} right="auto" width="full" - maxW={{ base: 24, xl: '15rem', '2xl': '20rem' }} + maxW={{ base: 24, md: '9.875rem', xl: '15rem', '2xl': '20rem' }} background={ 'linear-gradient(180deg, rgba(0, 0, 0, 0.35) 0.34%, rgba(0, 0, 0, 0.00) 34.08%, rgba(13, 0, 19, 0.35) 59.18%, rgba(20, 0, 28, 0.85) 100%)' } diff --git a/packages/web/components/Carousel/CarouselContext.tsx b/packages/web/components/Carousel/CarouselContext.tsx index 0ffcf93f77..ad63647f72 100644 --- a/packages/web/components/Carousel/CarouselContext.tsx +++ b/packages/web/components/Carousel/CarouselContext.tsx @@ -1,5 +1,12 @@ import { createContext, useContext } from 'react'; +type AcademySliderType = { + hidePositions: boolean; + hideNav: boolean; + shrinkItems: boolean; + itemsToShow: number; +}; + type CarouselContextType = { trackIsActive: boolean; setTrackIsActive: React.Dispatch>; @@ -22,10 +29,10 @@ type CarouselContextType = { multiplier: number; - shrinkItems: boolean; hidePositions: boolean; hideNav: boolean; itemsToShow: number; + defaultCarousel: boolean; }; export const CarouselContext = createContext({ @@ -44,10 +51,10 @@ export const CarouselContext = createContext({ itemWidth: 0, positions: [], gap: 0, - shrinkItems: false, hidePositions: false, hideNav: true, itemsToShow: 4, + defaultCarousel: true, }); export const useCarouselContext = () => useContext(CarouselContext); diff --git a/packages/web/components/Carousel/Item.tsx b/packages/web/components/Carousel/Item.tsx index f474257dfa..ad8052d22c 100644 --- a/packages/web/components/Carousel/Item.tsx +++ b/packages/web/components/Carousel/Item.tsx @@ -15,10 +15,9 @@ export const Item: React.FC = ({ children, index }) => { itemWidth, positions, gap, - shrinkItems, + defaultCarousel, } = useCarouselContext(); const [userDidTab, setUserDidTab] = useState(false); - // console.log("ItemProps", { index, activeItem, constraint, itemWidth, positions, gap, shrinkItems }); const handleFocus = () => setTrackIsActive(true); @@ -43,13 +42,15 @@ export const Item: React.FC = ({ children, index }) => { onBlur={handleBlur} onKeyUp={handleKeyUp} onKeyDown={handleKeyDown} - w={shrinkItems ? '100%' : `${itemWidth}px`} + w={!defaultCarousel ? '100%' : `${itemWidth}px`} flex={ - shrinkItems + !defaultCarousel ? { base: '0 0', xl: '0 0 16.875rem', '2xl': '0 0 21.5rem' } : 'inherit' } - maxW={shrinkItems ? { base: '16.875rem', '2xl': '21.5rem' } : 'inherit'} + maxW={ + !defaultCarousel ? { base: '16.875rem', '2xl': '21.5rem' } : 'inherit' + } _notLast={{ mr: `${gap}px`, }} diff --git a/packages/web/components/Carousel/Slider.tsx b/packages/web/components/Carousel/Slider.tsx index 9b72fba1d3..32af532592 100644 --- a/packages/web/components/Carousel/Slider.tsx +++ b/packages/web/components/Carousel/Slider.tsx @@ -23,8 +23,8 @@ export const Slider: React.FC = ({ children }) => { positions, gap, hidePositions, - hideNav, itemsToShow, + defaultCarousel, } = useCarouselContext(); const [ref, { width }] = useBoundingRect(); @@ -38,16 +38,20 @@ export const Slider: React.FC = ({ children }) => { const handleFocus = () => setTrackIsActive(true); return ( - + = ({ children }) => { > {children} - {!hideNav && positions.length > constraint && ( + {!defaultCarousel && positions.length > constraint && ( = ({ children }) => { spacing={2} > {positions.map((_, index) => { - if (!hideNav && index <= itemsCount / 3) { + if (!defaultCarousel && index <= itemsCount / 3) { return ( + ); + } + return ( + + ); + })} + + )} */} ); }; diff --git a/packages/web/components/Carousel/index.tsx b/packages/web/components/Carousel/index.tsx index 8f4708e2f5..06202d01df 100644 --- a/packages/web/components/Carousel/index.tsx +++ b/packages/web/components/Carousel/index.tsx @@ -60,19 +60,19 @@ export const Carousel: React.FC<{ constraint = 2; } if (!defaultCarousel && isGreaterThanBase) { - itemWidth = 114; - constraint = 1; + itemWidth = sliderWidth / 2 - gap; + constraint = 2; } if (!defaultCarousel && isGreaterThanMd) { - itemWidth = 158; - constraint = 1; + itemWidth = sliderWidth / 3 - gap; + constraint = 2; } if (!defaultCarousel && isGreaterThanXl) { - itemWidth = 270; - constraint = 1; + itemWidth = sliderWidth / 4 - gap; + constraint = 2; } if (!defaultCarousel && isGreaterThan2Xl) { - itemWidth = 344; + itemWidth = sliderWidth / 4 - gap; constraint = 2; } diff --git a/packages/web/pages/academy.tsx b/packages/web/pages/academy.tsx index 97131fbe15..471894d029 100644 --- a/packages/web/pages/academy.tsx +++ b/packages/web/pages/academy.tsx @@ -27,14 +27,21 @@ import { */ const AcademyPage: React.FC = () => { const carouselGap = - useBreakpointValue({ base: 8, md: 16, xl: 24, '2xl': 32 }) || 32; + useBreakpointValue({ base: 8, md: 8, xl: 24, '2xl': 32 }) || 32; const cardMinWidth = useBreakpointValue({ - base: '7.175rem', - md: '10.125', - xl: '16.875rem', + base: '9.75rem', + md: '12rem', + xl: '16.475rem', '2xl': '21.5rem', }) || '21.5rem'; + const itemsOnScreen = useBreakpointValue({ + base: 2, + md: 3, + lg: 3, + xl: 4, + '2xl': 4, + }); const makeItemPath = useCallback((type: PathPlaybookType): string => { let urlPath: string; @@ -64,24 +71,24 @@ const AcademyPage: React.FC = () => { url="https://metagame.wtf/paths-and-playbooks" /> - + The Academy { as="h2" fontFamily="body" fontWeight="600" - fontSize={{ base: '3xl', xl: '5xl' }} + fontSize={{ base: '3xl', '2xl': '5xl' }} > {categoryTitle} {description ? ( - + {description} ) : null} @@ -139,19 +146,24 @@ const AcademyPage: React.FC = () => { { {categoryItems.map( @@ -238,6 +251,7 @@ const Card: React.FC = ({ href={link} w="100%" minW={minWidth} + maxW={minWidth} p={0} > From 7a9e2057fc414b52ec52f004201c58ce992fac6b Mon Sep 17 00:00:00 2001 From: luxumbra Date: Tue, 24 Oct 2023 15:20:58 +0100 Subject: [PATCH 038/122] removed extraneous comments --- packages/web/components/Carousel/Slider.tsx | 1 - packages/web/pages/academy.tsx | 2 -- 2 files changed, 3 deletions(-) diff --git a/packages/web/components/Carousel/Slider.tsx b/packages/web/components/Carousel/Slider.tsx index e6c7734310..a804e60e3c 100644 --- a/packages/web/components/Carousel/Slider.tsx +++ b/packages/web/components/Carousel/Slider.tsx @@ -30,7 +30,6 @@ export const Slider: React.FC = ({ children }) => { const [ref, { width }] = useBoundingRect(); const itemsCount = positions.length; - // const isMobile = useBreakpointValue({ base: true, lg: false }); useEffect( () => setSliderWidth(Math.round(width ?? 0)), diff --git a/packages/web/pages/academy.tsx b/packages/web/pages/academy.tsx index 471894d029..65cd2d6ac8 100644 --- a/packages/web/pages/academy.tsx +++ b/packages/web/pages/academy.tsx @@ -9,7 +9,6 @@ import { VStack, } from '@metafam/ds'; import { Carousel } from 'components/Carousel'; -// import { Carousel } from 'components/Carousel'; import { PageContainer } from 'components/Container'; import { MetaLink } from 'components/Link'; import { HeadComponent } from 'components/Seo'; @@ -112,7 +111,6 @@ const AcademyPage: React.FC = () => { cat === categoryName && cat !== 'all', ); - // if there's an item with the category of all, add it to the categoryItems array and const allItem = Object.entries( QuestChainPathsAndPlaybooksDetails, ).filter(([name, { category: cat }]) => cat === 'all'); From f0ec9519be893ff5fc9c8425136305f4a36bd47b Mon Sep 17 00:00:00 2001 From: dysbulic Date: Mon, 16 Oct 2023 16:21:14 -0400 Subject: [PATCH 039/122] =?UTF-8?q?streamlining=20XP=20calculation,=20addi?= =?UTF-8?q?ng=20token=20multiplier,=20&=20returning=20detailed=20info=20fr?= =?UTF-8?q?om=20balance=20sync=20task=20=F0=9F=A7=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hasura/clear-xp.mjs | 80 +++++++ hasura/metadata/actions.graphql | 42 ++++ hasura/metadata/actions.yaml | 10 + hasura/metadata/tables.yaml | 6 + .../up.sql | 13 +- .../up.sql | 14 +- .../down.sql | 4 + .../up.sql | 2 + .../down.sql | 4 + .../up.sql | 2 + .../down.sql | 21 ++ .../up.sql | 19 ++ .../down.sql | 4 + .../up.sql | 2 + .../down.sql | 21 ++ .../up.sql | 19 ++ package.json | 1 + .../handlers/actions/player/syncBalances.ts | 200 ++++++++++++------ .../src/handlers/graphql/queries/token.ts | 1 + schema.graphql | 107 ++++++++++ yarn.lock | 12 ++ 21 files changed, 513 insertions(+), 71 deletions(-) create mode 100755 hasura/clear-xp.mjs create mode 100644 hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql create mode 100644 hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/up.sql create mode 100644 hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql create mode 100644 hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/up.sql create mode 100644 hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql create mode 100644 hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/up.sql create mode 100644 hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql create mode 100644 hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/up.sql create mode 100644 hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql create mode 100644 hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/up.sql diff --git a/hasura/clear-xp.mjs b/hasura/clear-xp.mjs new file mode 100755 index 0000000000..fcb2e72f8b --- /dev/null +++ b/hasura/clear-xp.mjs @@ -0,0 +1,80 @@ +#!/usr/bin/env node + +import fetch from 'node-fetch' + +/* eslint-disable no-console */ + +const TARGET_GRAPHQL_URL = ( + process.env.TARGET_GRAPHQL_URL || 'http://localhost:8080/v1/graphql' +) +const HASURA_ADMIN_SECRET = ( + process.env.HASURA_ADMIN_SECRET || 'metagame_secret' +) + +const headers = { + 'content-type': 'application/json', + 'x-hasura-admin-secret': HASURA_ADMIN_SECRET, +} + +async function fetchGraphQL({ + url = TARGET_GRAPHQL_URL, opDoc, opName = null, variables = {} +}) { + const regex = /^\s*(query|mutation)\s+(\S+)\s*\{.*/s + opName ??= opDoc.replace(regex, '$2') + const result = await fetch(url, { + method: 'POST', + body: JSON.stringify({ + query: opDoc, + variables, + operationName: opName, + }), + headers, + }) + + const body = await result.text() + try { + return JSON.parse(body) + } catch(err) { + console.error(`JSON Error: ${err.message}`) + console.error(body) + throw err + } +} + +const clearBalancesMutation = /* GraphQL */` + mutation ClearBalances { + delete_balance(where: {}) { + affected_rows + } + } +`.trim() + +async function clearBalances() { + const { data } = await fetchGraphQL({ + opDoc: clearBalancesMutation, + }) + return data.delete_balance.affected_rows +} + +const resetOffsetsMutation = /* GraphQL */` + mutation ResetOffsets { + update_token(where: {}, _set: { lastOffset: 0 }) { + affected_rows + } + } +`.trim() + +async function resetOffsets() { + const { data } = await fetchGraphQL({ + opDoc: resetOffsetsMutation, + }) + return data.update_token.affected_rows +} + +console.info(`Resetting the XP system for all guilds on ${TARGET_GRAPHQL_URL}…`) + +const numReset = await resetOffsets() +console.debug(`Reset ${numReset} guilds.`) + +const numCleared = await clearBalances() +console.debug(`Removed ${numCleared} balances.`) diff --git a/hasura/metadata/actions.graphql b/hasura/metadata/actions.graphql index 7dcdddbd38..6f8329b3c6 100644 --- a/hasura/metadata/actions.graphql +++ b/hasura/metadata/actions.graphql @@ -38,6 +38,10 @@ type Mutation { syncAllGuildDiscordMembers: [DiscordGuildsSyncOutput] } +type Mutation { + syncBalances: SyncBalancesOutput! +} + type Mutation { syncSourceCredAccounts: SourceCredSyncOutput } @@ -215,3 +219,41 @@ type UpdateComposeDBProfileResponse { fields: [String] } +type TokenReturn { + added: [BalanceOutput] + oldOffset: Int + newOffset: Int + count: Int + multiplier: Float + players: [PlayerOutput] +} + +type SyncBalancesOutput { + seasonStart: timestamp + success: Boolean + message: String + tokenReturns: [TokenReturn] +} + +type PlayerOutput { + address: String + xp: XPOutput +} + +type XPOutput { + initial: Float + accumulated: Float + calculated: Float + seasonal: Float +} + +type BalanceOutput { + executedAt: timestamp + drops: [DropOutput] +} + +type DropOutput { + playerAddress: String + amount: Float +} + diff --git a/hasura/metadata/actions.yaml b/hasura/metadata/actions.yaml index bafbd91335..756c6207ea 100644 --- a/hasura/metadata/actions.yaml +++ b/hasura/metadata/actions.yaml @@ -46,6 +46,10 @@ actions: definition: kind: synchronous handler: '{{ACTION_BASE_ENDPOINT}}/syncAllGuildDiscordMembers' + - name: syncBalances + definition: + kind: synchronous + handler: '{{ACTION_BASE_ENDPOINT}}/syncBalances' - name: syncSourceCredAccounts definition: kind: synchronous @@ -154,4 +158,10 @@ custom_types: - name: DiscordGuildsSyncOutput - name: LinkCeramicProfileNodeResponse - name: UpdateComposeDBProfileResponse + - name: TokenReturn + - name: SyncBalancesOutput + - name: PlayerOutput + - name: XPOutput + - name: BalanceOutput + - name: DropOutput scalars: [] diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index c65d62f4ed..504cfefb15 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -1499,16 +1499,22 @@ schema: public configuration: column_config: + created_at: + custom_name: createdAt player_id: custom_name: playerId seasonal_balance: custom_name: seasonalBalance token_address: custom_name: tokenAddress + updated_at: + custom_name: updatedAt custom_column_names: + created_at: createdAt player_id: playerId seasonal_balance: seasonalBalance token_address: tokenAddress + updated_at: updatedAt custom_root_fields: {} object_relationships: - name: player diff --git a/hasura/migrations/1692017802834_alter_table_public_player_account_add_unique_type_player_id/up.sql b/hasura/migrations/1692017802834_alter_table_public_player_account_add_unique_type_player_id/up.sql index b33d8f3cdc..cd94124d70 100644 --- a/hasura/migrations/1692017802834_alter_table_public_player_account_add_unique_type_player_id/up.sql +++ b/hasura/migrations/1692017802834_alter_table_public_player_account_add_unique_type_player_id/up.sql @@ -1,2 +1,11 @@ -alter table "public"."player_account" drop constraint "Account_identifier_type_key"; -alter table "public"."player_account" add constraint "player_account_type_player_id_key" unique ("type", "player_id"); +ALTER TABLE public.player_account + DROP CONSTRAINT "Account_identifier_type_key" +; + +ALTER TABLE public.player_account + DROP CONSTRAINT IF EXISTS player_account_type_player_id_key +; +ALTER TABLE public.player_account + ADD CONSTRAINT player_account_type_player_id_key + UNIQUE ("type", "player_id") +; diff --git a/hasura/migrations/1694534639232_insert_seed_for_metafam/up.sql b/hasura/migrations/1694534639232_insert_seed_for_metafam/up.sql index 8526363151..be09f262b3 100644 --- a/hasura/migrations/1694534639232_insert_seed_for_metafam/up.sql +++ b/hasura/migrations/1694534639232_insert_seed_for_metafam/up.sql @@ -1 +1,13 @@ -INSERT INTO xp(initial, player_id, token_address, balance) (SELECT total_xp, id, '0xEAeCC18198a475c921B24b8A6c1C1f0f5F3F7EA0', 0 FROM player); +INSERT INTO xp( + initial, + player_id, + token_address, + balance +) ( + SELECT + total_xp, + id, + '0xEAeCC18198a475c921B24b8A6c1C1f0f5F3F7EA0', + 0 + FROM player +); diff --git a/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql b/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql new file mode 100644 index 0000000000..17b78dee0d --- /dev/null +++ b/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."token" add column "multiplier" float8 +-- not null default '1'; diff --git a/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/up.sql b/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/up.sql new file mode 100644 index 0000000000..b3b66fd87d --- /dev/null +++ b/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/up.sql @@ -0,0 +1,2 @@ +alter table "public"."token" add column "multiplier" float8 + not null default '1'; diff --git a/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql b/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql new file mode 100644 index 0000000000..8f40543bd6 --- /dev/null +++ b/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."token" add column "created_at" timestamptz +-- null default now(); diff --git a/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/up.sql b/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/up.sql new file mode 100644 index 0000000000..92de05619e --- /dev/null +++ b/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/up.sql @@ -0,0 +1,2 @@ +alter table "public"."token" add column "created_at" timestamptz + null default now(); diff --git a/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql new file mode 100644 index 0000000000..857fc19ab4 --- /dev/null +++ b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql @@ -0,0 +1,21 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."token" add column "updated_at" timestamptz +-- null default now(); +-- +-- CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"() +-- RETURNS TRIGGER AS $$ +-- DECLARE +-- _new record; +-- BEGIN +-- _new := NEW; +-- _new."updated_at" = NOW(); +-- RETURN _new; +-- END; +-- $$ LANGUAGE plpgsql; +-- CREATE TRIGGER "set_public_token_updated_at" +-- BEFORE UPDATE ON "public"."token" +-- FOR EACH ROW +-- EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"(); +-- COMMENT ON TRIGGER "set_public_token_updated_at" ON "public"."token" +-- IS 'trigger to set value of column "updated_at" to current timestamp on row update'; diff --git a/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/up.sql b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/up.sql new file mode 100644 index 0000000000..abe65906b6 --- /dev/null +++ b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/up.sql @@ -0,0 +1,19 @@ +alter table "public"."token" add column "updated_at" timestamptz + null default now(); + +CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"() +RETURNS TRIGGER AS $$ +DECLARE + _new record; +BEGIN + _new := NEW; + _new."updated_at" = NOW(); + RETURN _new; +END; +$$ LANGUAGE plpgsql; +CREATE TRIGGER "set_public_token_updated_at" +BEFORE UPDATE ON "public"."token" +FOR EACH ROW +EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"(); +COMMENT ON TRIGGER "set_public_token_updated_at" ON "public"."token" +IS 'trigger to set value of column "updated_at" to current timestamp on row update'; diff --git a/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql b/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql new file mode 100644 index 0000000000..b712f41882 --- /dev/null +++ b/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."xp" add column "created_at" timestamptz +-- not null default now(); diff --git a/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/up.sql b/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/up.sql new file mode 100644 index 0000000000..fb8a7b3518 --- /dev/null +++ b/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/up.sql @@ -0,0 +1,2 @@ +alter table "public"."xp" add column "created_at" timestamptz + not null default now(); diff --git a/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql b/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql new file mode 100644 index 0000000000..0585164855 --- /dev/null +++ b/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql @@ -0,0 +1,21 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."xp" add column "updated_at" timestamptz +-- not null default now(); +-- +-- CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"() +-- RETURNS TRIGGER AS $$ +-- DECLARE +-- _new record; +-- BEGIN +-- _new := NEW; +-- _new."updated_at" = NOW(); +-- RETURN _new; +-- END; +-- $$ LANGUAGE plpgsql; +-- CREATE TRIGGER "set_public_xp_updated_at" +-- BEFORE UPDATE ON "public"."xp" +-- FOR EACH ROW +-- EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"(); +-- COMMENT ON TRIGGER "set_public_xp_updated_at" ON "public"."xp" +-- IS 'trigger to set value of column "updated_at" to current timestamp on row update'; diff --git a/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/up.sql b/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/up.sql new file mode 100644 index 0000000000..f3dfcb0fec --- /dev/null +++ b/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/up.sql @@ -0,0 +1,19 @@ +alter table "public"."xp" add column "updated_at" timestamptz + not null default now(); + +CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"() +RETURNS TRIGGER AS $$ +DECLARE + _new record; +BEGIN + _new := NEW; + _new."updated_at" = NOW(); + RETURN _new; +END; +$$ LANGUAGE plpgsql; +CREATE TRIGGER "set_public_xp_updated_at" +BEFORE UPDATE ON "public"."xp" +FOR EACH ROW +EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"(); +COMMENT ON TRIGGER "set_public_xp_updated_at" ON "public"."xp" +IS 'trigger to set value of column "updated_at" to current timestamp on row update'; diff --git a/package.json b/package.json index b4360af4e8..cee5411904 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "@graphql-codegen/typescript-resolvers": "^4.0.1", "@graphql-codegen/typescript-urql": "^3.7.3", "@types/jest": "^29.2.1", + "@types/node": "^20.8.6", "@types/node-fetch": "^2.6.2", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", diff --git a/packages/backend/src/handlers/actions/player/syncBalances.ts b/packages/backend/src/handlers/actions/player/syncBalances.ts index 6366931d06..f6700d2b27 100644 --- a/packages/backend/src/handlers/actions/player/syncBalances.ts +++ b/packages/backend/src/handlers/actions/player/syncBalances.ts @@ -6,8 +6,6 @@ import fetch from 'node-fetch'; import { client } from '../../../lib/hasuraClient.js'; import { computeRank } from '../../../lib/rankHelpers.js'; -const INVALIDATE_AFTER_DAYS = 4; // number of days after which to recache - type SafeResponse = { results: Array<{ origin: string; @@ -25,15 +23,26 @@ const setBalances = async ({ safeAddress, offset = 0, tokenAddress: guildTokenAddress, + chainId = 1, }: { safeAddress: string; offset?: number; tokenAddress: string; + chainId: number; }) => { - const res = await fetch( - `https://safe-transaction-polygon.safe.global/api/v1/safes/${safeAddress}/all-transactions/?limit=100&offset=${offset}&executed=true&queued=false&trusted=true`, - ); + const safes = { + 137: + 'https://safe-transaction-polygon.safe.global' + + `/api/v1/safes/${safeAddress}/all-transactions/` + + `?limit=100&offset=${offset}&executed=true` + + '&queued=false&trusted=true', + }; + const safeURL = safes[chainId as keyof typeof safes]; + if (!safeURL) { + throw new Error(`No Safe URL for chain #${chainId}.`); + } + const res = await fetch(safeURL); const { results } = (await res.json()) as SafeResponse; const uniqueDrops: Record> = {}; const airdrops = results.filter((tx) => tx.origin?.includes('CSV Airdrop')); @@ -50,86 +59,141 @@ const setBalances = async ({ }); }); - await Promise.all( - Object.entries(uniqueDrops) - .map(([executionDate, drops]) => + const added = await Promise.all( + Object.entries(uniqueDrops).map(async ([executedAt, drops]) => { + const dropsRet = await Promise.all( Object.entries(drops).map(async ([to, value]) => { - await client.AddBalance({ - amount: value, - executedAt: new Date(executionDate), + const dat = { playerAddress: to, + amount: value, + }; + await client.AddBalance({ + ...dat, + executedAt: new Date(executedAt), tokenAddress: guildTokenAddress, }); + return dat; }), - ) - .flat(), + ); + return { + executedAt: new Date(executedAt).toLocaleString('sv'), + drops: dropsRet, + }; + }), ); await client.UpdateLastOffset({ tokenAddress: guildTokenAddress, offset: offset + results.length, }); + + return { + added, + oldOffset: offset, + newOffset: offset + results.length, + }; }; // @todo only query guilds that have a token ID export default async (req: Request, res: Response): Promise => { - const expiration = new Date(); - const invalidateAfterDays = - req.query.invalidate_after_days != null - ? parseInt(req.query.invalidate_after_days as string, 10) - : INVALIDATE_AFTER_DAYS; - expiration.setDate(expiration.getDate() - invalidateAfterDays); - const { token: tokens } = await client.GetTokens(); - await Promise.allSettled( - tokens.map( - async ({ safeAddress, lastOffset: offset, guildId, address }) => { - await setBalances({ safeAddress, offset, tokenAddress: address }); - const { - guild: [{ guild_players: players }], - } = await client.GetGuildMembers({ id: guildId }); - await Promise.all( - players.map(async (player) => { - const total = await client.GetTotalForPlayer({ - tokenAddress: address, - playerAddress: player.Player.ethereumAddress, - }); - const balance = total.balance_aggregate.aggregate?.sum?.amount; + try { + const { token: tokens } = await client.GetTokens(); + const seasonStart = getCurrentSeasonStart(); + const tokenPromiseRet = await Promise.allSettled( + tokens.map( + async ({ + safeAddress, + lastOffset: offset, + guildId, + address, + chainId, + multiplier, + }) => { + const balRet = await setBalances({ + safeAddress, + offset, + tokenAddress: address, + chainId, + }); + const { + guild: [{ guild_players: players }], + } = await client.GetGuildMembers({ id: guildId }); + const playerRet = await Promise.all( + players.map( + async ({ + Player: { ethereumAddress: ethAddress, id: playerId }, + }) => { + const total = await client.GetTotalForPlayer({ + tokenAddress: address, + playerAddress: ethAddress, + }); + const balance = + total.balance_aggregate.aggregate?.sum?.amount ?? 0; - const seasonalTotal = await client.GetTotalForPlayer({ - tokenAddress: address, - playerAddress: player.Player.ethereumAddress, - executedAfter: getCurrentSeasonStart(), - }); - const seasonalBalance = - seasonalTotal.balance_aggregate.aggregate?.sum?.amount; + const seasonalTotal = await client.GetTotalForPlayer({ + tokenAddress: address, + playerAddress: ethAddress, + executedAfter: seasonStart, + }); + const seasonalBalance = + seasonalTotal.balance_aggregate.aggregate?.sum?.amount ?? 0; - const { - xp: [{ initial } = { initial: 0 }], - } = await client.GetInitialXP({ playerId: player.Player.id }); + const { + xp: [{ initial } = { initial: 0 }], + } = await client.GetInitialXP({ playerId }); + const calculated = balance * multiplier + initial; - await client.UpsertXP({ - balance: (balance ?? 0) + initial, - playerId: player.Player.id, - tokenAddress: address, - seasonalBalance, - }); - }), - ); - }, - ), - ); - const ranks = await client.GetPlayersByTotalXP(); + await client.UpsertXP({ + balance: calculated, + playerId, + tokenAddress: address, + seasonalBalance, + }); - Promise.allSettled( - ranks.xp.map(async ({ playerId, seasonalBalance, balance }, index) => { - const rank = computeRank(index); - await client.UpdateProfileXP({ - playerId, - seasonXP: seasonalBalance, - totalXP: balance, - rank, - }); - }), - ); - res.json('Complete! XP saved'); + return { + address: ethAddress, + xp: { + initial, + accumulated: balance, + calculated, + seasonal: seasonalBalance, + }, + }; + }, + ), + ); + return { + ...balRet, + multiplier, + count: players.length, + players: playerRet, + }; + }, + ), + ); + const tokenRet = tokenPromiseRet.map((t) => + t.status === 'fulfilled' ? t.value : { status: 'failed' }, + ); + + const { xp } = await client.GetPlayersByTotalXP(); + Promise.allSettled( + xp.map(async ({ playerId, seasonalBalance, balance }, index) => { + const rank = computeRank(index); + await client.UpdateProfileXP({ + playerId, + seasonXP: seasonalBalance, + totalXP: balance, + rank, + }); + }), + ); + res.json({ + success: true, + message: `Successfully synced ${xp.length} users.`, + seasonStart, + tokenReturns: tokenRet, + }); + } catch (err) { + res.status(500).json({ success: false, message: (err as Error).message }); + } }; diff --git a/packages/backend/src/handlers/graphql/queries/token.ts b/packages/backend/src/handlers/graphql/queries/token.ts index 246ad66c5a..11391e35e2 100644 --- a/packages/backend/src/handlers/graphql/queries/token.ts +++ b/packages/backend/src/handlers/graphql/queries/token.ts @@ -12,6 +12,7 @@ export const TokenQueries = /* GraphQL */ ` safeAddress lastOffset guildId + multiplier } } query GetTotalForPlayer( diff --git a/schema.graphql b/schema.graphql index bcc63395df..997114285e 100644 --- a/schema.graphql +++ b/schema.graphql @@ -7,6 +7,7 @@ scalar float8 scalar json scalar jsonb scalar numeric +scalar timestamp scalar timestamptz scalar uuid """ @@ -47,6 +48,10 @@ type AccountType_mutation_response { "data from the rows affected by the mutation" returning: [AccountType!]! } +type BalanceOutput { + drops: [DropOutput] + executedAt: timestamp +} type BrightIdStatus { app: String! context: String! @@ -198,6 +203,10 @@ type DiscordRole { name: String! position: Int! } +type DropOutput { + amount: Float + playerAddress: String +} """ columns and relationships of "ExplorerType" """ @@ -509,6 +518,10 @@ type Moloch { type PSeedInfo { priceUsd: String } +type PlayerOutput { + address: String + xp: XPOutput +} """ columns and relationships of "PlayerRank" """ @@ -836,11 +849,25 @@ type SourceCredSyncOutput { numUnclaimed: Int! numUpdated: Int! } +type SyncBalancesOutput { + message: String + seasonStart: timestamp + success: Boolean + tokenReturns: [TokenReturn] +} type TokenBalances { id: ID! pSeedBalance: String! seedBalance: String! } +type TokenReturn { + added: [BalanceOutput] + count: Int + multiplier: Float + newOffset: Int + oldOffset: Int + players: [PlayerOutput] +} type UpdateComposeDBProfileResponse { accountLinks: [String] ceramic: String! @@ -855,6 +882,12 @@ type UpdateQuestCompletionOutput { quest_completion_id: uuid success: Boolean! } +type XPOutput { + accumulated: Float + calculated: Float + initial: Float + seasonal: Float +} """ columns and relationships of "balance" """ @@ -2396,6 +2429,7 @@ type mutation_root { saveGuildInformation(guildInformation: GuildInfoInput!): SaveGuildResponse saveGuildLayout(guildLayoutInfo: GuildLayoutInfoInput!): SaveGuildLayoutResponse syncAllGuildDiscordMembers: [DiscordGuildsSyncOutput] + syncBalances: SyncBalancesOutput! syncSourceCredAccounts: SourceCredSyncOutput updateCachedProfile(playerId: uuid): uuid! updatePlayerFromComposeDB(playerId: uuid!): uuid! @@ -6905,11 +6939,14 @@ columns and relationships of "token" type token { address: String! chainId: Int! + created_at: timestamptz "An object relationship" guild: guild! guildId: uuid! lastOffset: Int! + multiplier: float8! safeAddress: String! + updated_at: timestamptz "An array relationship" xps( "distinct select on columns" @@ -6964,22 +7001,29 @@ type token_aggregate_fields { type token_avg_fields { chainId: Float lastOffset: Float + multiplier: Float } "aggregate max on columns" type token_max_fields { address: String chainId: Int + created_at: timestamptz guildId: uuid lastOffset: Int + multiplier: float8 safeAddress: String + updated_at: timestamptz } "aggregate min on columns" type token_min_fields { address: String chainId: Int + created_at: timestamptz guildId: uuid lastOffset: Int + multiplier: float8 safeAddress: String + updated_at: timestamptz } """ response of any mutation on the table "token" @@ -6994,36 +7038,43 @@ type token_mutation_response { type token_stddev_fields { chainId: Float lastOffset: Float + multiplier: Float } "aggregate stddev_pop on columns" type token_stddev_pop_fields { chainId: Float lastOffset: Float + multiplier: Float } "aggregate stddev_samp on columns" type token_stddev_samp_fields { chainId: Float lastOffset: Float + multiplier: Float } "aggregate sum on columns" type token_sum_fields { chainId: Int lastOffset: Int + multiplier: float8 } "aggregate var_pop on columns" type token_var_pop_fields { chainId: Float lastOffset: Float + multiplier: Float } "aggregate var_samp on columns" type token_var_samp_fields { chainId: Float lastOffset: Float + multiplier: Float } "aggregate variance on columns" type token_variance_fields { chainId: Float lastOffset: Float + multiplier: Float } """ fields of action: "updateCachedProfile" @@ -7056,6 +7107,7 @@ columns and relationships of "xp" """ type xp { balance: float8! + createdAt: timestamptz! id: uuid! initial: float8 "An object relationship" @@ -7065,6 +7117,7 @@ type xp { "An object relationship" token: token! tokenAddress: String! + updatedAt: timestamptz! } """ aggregated selection of "xp" @@ -7098,20 +7151,24 @@ type xp_avg_fields { "aggregate max on columns" type xp_max_fields { balance: float8 + createdAt: timestamptz id: uuid initial: float8 playerId: uuid seasonalBalance: float8 tokenAddress: String + updatedAt: timestamptz } "aggregate min on columns" type xp_min_fields { balance: float8 + createdAt: timestamptz id: uuid initial: float8 playerId: uuid seasonalBalance: float8 tokenAddress: String + updatedAt: timestamptz } """ response of any mutation on the table "xp" @@ -8468,11 +8525,17 @@ enum token_select_column { "column name" chainId "column name" + created_at + "column name" guildId "column name" lastOffset "column name" + multiplier + "column name" safeAddress + "column name" + updated_at } """ update columns of table "token" @@ -8483,11 +8546,17 @@ enum token_update_column { "column name" chainId "column name" + created_at + "column name" guildId "column name" lastOffset "column name" + multiplier + "column name" safeAddress + "column name" + updated_at } """ unique or primary key constraints on table "xp" @@ -8509,6 +8578,8 @@ enum xp_select_column { "column name" balance "column name" + createdAt + "column name" id "column name" initial @@ -8518,6 +8589,8 @@ enum xp_select_column { seasonalBalance "column name" tokenAddress + "column name" + updatedAt } """ select "xp_aggregate_bool_exp_avg_arguments_columns" columns of table "xp" @@ -8614,6 +8687,8 @@ enum xp_update_column { "column name" balance "column name" + createdAt + "column name" id "column name" initial @@ -8623,6 +8698,8 @@ enum xp_update_column { seasonalBalance "column name" tokenAddress + "column name" + updatedAt } """ Boolean expression to filter rows from the table "AccountType". All fields are combined with a logical 'AND'. @@ -12539,10 +12616,13 @@ input token_bool_exp { _or: [token_bool_exp!] address: String_comparison_exp chainId: Int_comparison_exp + created_at: timestamptz_comparison_exp guild: guild_bool_exp guildId: uuid_comparison_exp lastOffset: Int_comparison_exp + multiplier: float8_comparison_exp safeAddress: String_comparison_exp + updated_at: timestamptz_comparison_exp xps: xp_bool_exp xps_aggregate: xp_aggregate_bool_exp } @@ -12552,6 +12632,7 @@ input type for incrementing numeric columns in table "token" input token_inc_input { chainId: Int lastOffset: Int + multiplier: float8 } """ input type for inserting data into table "token" @@ -12559,10 +12640,13 @@ input type for inserting data into table "token" input token_insert_input { address: String chainId: Int + created_at: timestamptz guild: guild_obj_rel_insert_input guildId: uuid lastOffset: Int + multiplier: float8 safeAddress: String + updated_at: timestamptz xps: xp_arr_rel_insert_input } """ @@ -12587,10 +12671,13 @@ Ordering options when selecting data from "token". input token_order_by { address: order_by chainId: order_by + created_at: order_by guild: guild_order_by guildId: order_by lastOffset: order_by + multiplier: order_by safeAddress: order_by + updated_at: order_by xps_aggregate: xp_aggregate_order_by } "primary key columns input for table: token" @@ -12603,9 +12690,12 @@ input type for updating data in table "token" input token_set_input { address: String chainId: Int + created_at: timestamptz guildId: uuid lastOffset: Int + multiplier: float8 safeAddress: String + updated_at: timestamptz } """ Streaming cursor of the table "token" @@ -12620,9 +12710,12 @@ input token_stream_cursor_input { input token_stream_cursor_value_input { address: String chainId: Int + created_at: timestamptz guildId: uuid lastOffset: Int + multiplier: float8 safeAddress: String + updated_at: timestamptz } input token_updates { "increments the numeric columns with given value of the filtered values" @@ -12759,6 +12852,7 @@ input xp_bool_exp { _not: xp_bool_exp _or: [xp_bool_exp!] balance: float8_comparison_exp + createdAt: timestamptz_comparison_exp id: uuid_comparison_exp initial: float8_comparison_exp player: player_bool_exp @@ -12766,6 +12860,7 @@ input xp_bool_exp { seasonalBalance: float8_comparison_exp token: token_bool_exp tokenAddress: String_comparison_exp + updatedAt: timestamptz_comparison_exp } """ input type for incrementing numeric columns in table "xp" @@ -12780,6 +12875,7 @@ input type for inserting data into table "xp" """ input xp_insert_input { balance: float8 + createdAt: timestamptz id: uuid initial: float8 player: player_obj_rel_insert_input @@ -12787,28 +12883,33 @@ input xp_insert_input { seasonalBalance: float8 token: token_obj_rel_insert_input tokenAddress: String + updatedAt: timestamptz } """ order by max() on columns of table "xp" """ input xp_max_order_by { balance: order_by + createdAt: order_by id: order_by initial: order_by playerId: order_by seasonalBalance: order_by tokenAddress: order_by + updatedAt: order_by } """ order by min() on columns of table "xp" """ input xp_min_order_by { balance: order_by + createdAt: order_by id: order_by initial: order_by playerId: order_by seasonalBalance: order_by tokenAddress: order_by + updatedAt: order_by } """ on_conflict condition type for table "xp" @@ -12823,6 +12924,7 @@ Ordering options when selecting data from "xp". """ input xp_order_by { balance: order_by + createdAt: order_by id: order_by initial: order_by player: player_order_by @@ -12830,6 +12932,7 @@ input xp_order_by { seasonalBalance: order_by token: token_order_by tokenAddress: order_by + updatedAt: order_by } "primary key columns input for table: xp" input xp_pk_columns_input { @@ -12840,11 +12943,13 @@ input type for updating data in table "xp" """ input xp_set_input { balance: float8 + createdAt: timestamptz id: uuid initial: float8 playerId: uuid seasonalBalance: float8 tokenAddress: String + updatedAt: timestamptz } """ order by stddev() on columns of table "xp" @@ -12882,11 +12987,13 @@ input xp_stream_cursor_input { "Initial value of the column from where the streaming should start" input xp_stream_cursor_value_input { balance: float8 + createdAt: timestamptz id: uuid initial: float8 playerId: uuid seasonalBalance: float8 tokenAddress: String + updatedAt: timestamptz } """ order by sum() on columns of table "xp" diff --git a/yarn.lock b/yarn.lock index 8d8269829a..23354c89a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6561,6 +6561,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== +"@types/node@^20.8.6": + version "20.8.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.6.tgz#0dbd4ebcc82ad0128df05d0e6f57e05359ee47fa" + integrity sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ== + dependencies: + undici-types "~5.25.1" + "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -21642,6 +21649,11 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== +undici-types@~5.25.1: + version "5.25.3" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.25.3.tgz#e044115914c85f0bcbb229f346ab739f064998c3" + integrity sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA== + unfetch@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-3.1.2.tgz#dc271ef77a2800768f7b459673c5604b5101ef77" From d48a0f669684f14d08b74871832d69752e6a5c0d Mon Sep 17 00:00:00 2001 From: dysbulic Date: Mon, 16 Oct 2023 19:12:41 -0400 Subject: [PATCH 040/122] =?UTF-8?q?making=20changes=20to=20force=20a=20red?= =?UTF-8?q?eploy=20=F0=9F=8D=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- guides/DESCRIPTIVE_C4.MD | 128 +++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 59 deletions(-) diff --git a/guides/DESCRIPTIVE_C4.MD b/guides/DESCRIPTIVE_C4.MD index 6ec1f17e46..dab0525abb 100644 --- a/guides/DESCRIPTIVE_C4.MD +++ b/guides/DESCRIPTIVE_C4.MD @@ -1,14 +1,16 @@ -A deeper understanding of the C4 -================================= -I was very surprised to learn that the core software which ultra-reliable high frequency trading platforms are built upon was constructed using a software development process with no leadership, no vision, no roadmaps, no planning, and no meetings. +# A deeper understanding of the C4 + +================================== + +I was very surprised to learn that the core software which ultra-reliable high frequency trading platforms are built upon was constructed using a software development process with no leadership, no vision, no roadmaps, no planning, and no meetings. ZeroMQ is the [core that these systems are built on](https://umbrella.cisco.com/blog/2015/11/05/the-avalanche-project-when-high-frequency-trading-meets-traffic-classification/), and ZeroMQ was built with the C4. The reason Blockrazor and Krazor use the C4 is quite simple: centralization is a blocking process which yields inaccurate results. -The C4 is a hill-climbing algorithm, and an evolution of the GitHub [Fork + Pull Model](http://help.github.com/send-pull-requests/). It is an _extremely_ powerful and fully battle-tested approach to developing software, with proven results. It's probably not possible for any non-C4 project to win in the free market against a project that (properly) uses the C4. Now we get to find out if this holds true in the fierce and brutal cryptocurrency battleground. +The C4 is a hill-climbing algorithm, and an evolution of the GitHub [Fork + Pull Model](http://help.github.com/send-pull-requests/). It is an *extremely* powerful and fully battle-tested approach to developing software, with proven results. It's probably not possible for any non-C4 project to win in the free market against a project that (properly) uses the C4. Now we get to find out if this holds true in the fierce and brutal cryptocurrency battleground. +## Language -Language --------- +--- > The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. @@ -16,10 +18,11 @@ By starting with the RFC 2119 language, the C4 text makes very clear its intenti I think C4 is the first time anyone has attempted to codify a community's rulebook as a formal and reusable protocol spec. Previously, ZMQ rules were spread out over several wiki pages and were quite specific to libzmq in many ways. But experience teaches us that the more formal, accurate, and reusable the rules, the easier it is for strangers to collaborate up-front. And less friction means a more scalable community. At the time of C4, we also had some disagreement in the libzmq project over precisely what process we were using. Not everyone felt bound by the same rules. Let's just say some people felt they had a special status, which created friction with the rest of the community. So codification made things clear. -It's easy to use C4: just host your project on GitHub, get one other person to join, and open the floor to pull requests. In your README, put a link to C4 and that's it. We've done this in quite a few projects and it does seem to work. I've been pleasantly surprised a few times just applying these rules to my own work, like CZMQ. _None_ of us are so amazing that we can work without others. +It's easy to use C4: just host your project on GitHub, get one other person to join, and open the floor to pull requests. In your README, put a link to C4 and that's it. We've done this in quite a few projects and it does seem to work. I've been pleasantly surprised a few times just applying these rules to my own work, like CZMQ. *None* of us are so amazing that we can work without others. -Goals ------ +## Goals + +--- > C4 is meant to provide a reusable optimal collaboration model for open source software projects. @@ -31,19 +34,19 @@ Making C4 reusable is therefore really important. To learn more about the best p > It has these specific goals: To maximize the scale of the community around a project, by reducing the friction for new Contributors and creating a scaled participation model with strong positive feedbacks; -The number one goal is size and health of the community--not technical quality, not profits, not performance, not market share. The goal is simply the number of people who contribute to the project. The science here is simple: the larger the community, the more accurate the results (The Wisdom of Crowds provides a basic introduction to the research in this field). +The number one goal is size and health of the community—not technical quality, not profits, not performance, not market share. The goal is simply the number of people who contribute to the project. The science here is simple: the larger the community, the more accurate the results (The Wisdom of Crowds provides a basic introduction to the research in this field). > To relieve dependencies on key individuals by separating different skill sets so that there is a larger pool of competence in any required domain; -Perhaps the worst problem we faced in libzmq was dependence on people who could understand the code, manage GitHub branches, and make clean releases--all at the same time. It's like looking for athletes who can run marathons and sprint, swim, and also lift weights. We humans are really good at specialization. Asking us to be really good at two contradictory things reduces the number of candidates sharply, which is a Bad Thing for any project. We had this problem severely in libzmq in 2009 or so, and fixed it by splitting the role of maintainer into two: one person makes patches and another makes releases. +Perhaps the worst problem we faced in libzmq was dependence on people who could understand the code, manage GitHub branches, and make clean releasesu2014all at the same time. It's like looking for athletes who can run marathons and sprint, swim, and also lift weights. We humans are really good at specialization. Asking us to be really good at two contradictory things reduces the number of candidates sharply, which is a Bad Thing for any project. We had this problem severely in libzmq in 2009 or so, and fixed it by splitting the role of maintainer into two: one person makes patches and another makes releases. > To allow the project to develop faster and more accurately, by increasing the diversity of the decision making process; -This is theory--not fully proven, but not falsified. The diversity of the community and the number of people who can weigh in on discussions, without fear of being criticized or dismissed, the faster and more accurately the software develops. Speed is quite subjective here. Going very fast in the wrong direction is not just useless, it's actively damaging (and we suffered a lot of that in libzmq before we switched to C4). +This is theory—not fully proven, but not falsified. The diversity of the community and the number of people who can weigh in on discussions, without fear of being criticized or dismissed, the faster and more accurately the software develops. Speed is quite subjective here. Going very fast in the wrong direction is not just useless, it's actively damaging (and we suffered a lot of that in libzmq before we switched to C4). > To support the natural life cycle of project versions from experimental through to stable, by allowing safe experimentation, rapid failure, and isolation of stable code; -It's quite an interesting effect of the process: _the git master is almost always perfectly stable_. This has to do with the size of changes and their _latency_, i.e., the time between someone writing the code and someone actually using it fully. However, the healthy design learning process tends to cycle through drafts until becoming stable, and inviolable. +It's quite an interesting effect of the process: *the git master is almost always perfectly stable*. This has to do with the size of changes and their *latency*, i.e., the time between someone writing the code and someone actually using it fully. However, the healthy design learning process tends to cycle through drafts until becoming stable, and inviolable. > To reduce the internal complexity of project repositories, thus making it easier for Contributors to participate and reducing the scope for error; @@ -53,8 +56,9 @@ Curious observation: people who thrive in complex situations like to create comp Ultimately, we're economic creatures, and the sense that "we own this, and our work can never be used against us" makes it much easier for people to invest in an open source project like ZeroMQ. And it can't be just a feeling, it has to be real. There are a number of aspects to making collective ownership work, we'll see these one-by-one as we go through C4. -Preliminaries -------------- +## Preliminaries + +--- > The project SHALL use the git distributed revision control system. @@ -78,7 +82,7 @@ Now we move on to definitions of the parties, and the splitting of roles that sa > Contributors SHALL NOT have commit access to the repository unless they are also Maintainers. Maintainers SHALL have commit access to the repository. -What we wanted to avoid was people pushing their changes directly to master. This was the biggest source of trouble in libzmq historically: large masses of raw code that took months or years to fully stabilize. We eventually followed other ZeroMQ projects like PyZMQ in using pull requests. We went further, and stipulated that _all_ changes had to follow the same path. No exceptions for "special people". +What we wanted to avoid was people pushing their changes directly to master. This was the biggest source of trouble in libzmq historically: large masses of raw code that took months or years to fully stabilize. We eventually followed other ZeroMQ projects like PyZMQ in using pull requests. We went further, and stipulated that *all* changes had to follow the same path. No exceptions for "special people". > Everyone, without distinction or discrimination, SHALL have an equal right to become a Contributor under the terms of this contract. @@ -86,8 +90,9 @@ We had to state this explicitly. It used to be that the libzmq maintainers would I think this fight between individual expertise and collective intelligence plays out in other areas. It defined Wikipedia, and still does, a decade after that work surpassed anything built by small groups of experts. For me, we make software by slowly synthesizing the most accurate knowledge, much as we make Wikipedia articles. -Licensing and Ownership ------------------------ +## Licensing and Ownership + +--- > The project SHALL use a share-alike license such as the MPLv2, or a GPLv3 variant thereof (GPL, LGPL, AGPL). @@ -99,14 +104,15 @@ This removes the need for any specific license or contribution agreement for pat > All patches are owned by their authors. There SHALL NOT be any copyright assignment process. -Here we come to the key reason people trust their investments in ZeroMQ: it's logistically impossible to buy the copyrights to create a closed source competitor to ZeroMQ. iMatix can't do this either. And the more people that send patches, the harder it becomes. ZeroMQ isn't just free and open today--this specific rule means it will remain so forever. Note that it's not the case in all MPLv2/GPL projects, many of which still ask for copyright transfer back to the maintainers. +Here we come to the key reason people trust their investments in ZeroMQ: it's logistically impossible to buy the copyrights to create a closed source competitor to ZeroMQ. iMatix can't do this either. And the more people that send patches, the harder it becomes. ZeroMQ isn't just free and open today—this specific rule means it will remain so forever. Note that it's not the case in all MPLv2/GPL projects, many of which still ask for copyright transfer back to the maintainers. > Each Contributor SHALL be responsible for identifying themselves in the project Contributor list. In other words, the maintainers are not karma accountants. Anyone who wants credit has to claim it themselves. -Patch Requirements ------------------- +## Patch Requirements + +--- In this section, we define the obligations of the contributor: specifically, what constitutes a "valid" patch, so that maintainers have rules they can use to accept or reject patches. @@ -124,7 +130,7 @@ This is just sanity. I've spent time cleaning up other peoples' patches because > A patch MUST adhere to the "Evolution of Public Contracts" guidelines defined below. -Ah, the pain, the pain. I'm not speaking of the time at age eight when I stepped on a plank with a 4-inch nail protruding from it. That was relatively OK. I'm speaking of 2010-2011 when we had multiple parallel releases of ZeroMQ, each with different _incompatible_ APIs or wire protocols. It was an exercise in bad rules, pointlessly enforced, that still hurts us today. The rule was, "If you change the API or protocol, you SHALL create a new major version". Give me the nail through the foot; that hurt less. +Ah, the pain, the pain. I'm not speaking of the time at age eight when I stepped on a plank with a 4-inch nail protruding from it. That was relatively OK. I'm speaking of 2010-2011 when we had multiple parallel releases of ZeroMQ, each with different *incompatible* APIs or wire protocols. It was an exercise in bad rules, pointlessly enforced, that still hurts us today. The rule was, "If you change the API or protocol, you SHALL create a new major version". Give me the nail through the foot; that hurt less. One of the big changes we made with C4 was simply to ban, outright, this kind of sanctioned sabotage. Amazingly, it's not even hard. We just don't allow the breaking of existing public contracts, period, unless everyone agrees, in which case no period. As Linus Torvalds famously put it on 23 December 2012, "WE DO NOT BREAK USERSPACE!" @@ -136,7 +142,7 @@ This rule has two effects. The first is that it forces people to make minimal so For cross-platform projects, it is fair to ask that the patch works on the development box used by the contributor. -> * A patch commit message MUST consist of a single short (less than 50 characters) line stating the problem ("Problem: ...") being solved, followed by a blank line and then the proposed solution ("Solution: ..."). +> * A patch commit message MUST consist of a single short (less than 50 characters) line stating the problem ("Problem: ...") being solved, followed by a blank line and then the proposed solution ("Solution: ..."). This is a good format for commit messages that fits into email (the first line becomes the subject, and the rest becomes the email body). @@ -144,8 +150,9 @@ This is a good format for commit messages that fits into email (the first line b Just in case it wasn't clear, we're back to legalese and definitions. -Development Process -------------------- +## Development Process + +--- In this section, we aim to describe the actual development process, step-by-step. @@ -203,7 +210,7 @@ We already said this but it's worth repeating: the role of Maintainer is not to > Maintainers SHALL merge correct patches rapidly. -There is a criteria I call _change latency_, which is the round-trip time from identifying a problem to testing a solution. The faster the better. If maintainers cannot respond to pull requests as rapidly as people expect, they're not doing their job (or they need more hands). +There is a criteria I call *change latency*, which is the round-trip time from identifying a problem to testing a solution. The faster the better. If maintainers cannot respond to pull requests as rapidly as people expect, they're not doing their job (or they need more hands). > Maintainers MAY merge incorrect patches from other Contributors with the goals of (a) ending fruitless discussions, (b) capturing toxic patches in the historical record, (c) engaging with the Contributor on improving their patch quality. @@ -215,34 +222,33 @@ In the worst case, patches can wait for weeks, or months, to be accepted. Or the PM is how most projects work, and I believe most projects get it wrong. Let me start by listing the problems PM creates: -* _It tells new contributors, "guilty until proven innocent,"_ which is a negative message that creates negative emotions. Contributors who feel unwelcome will always look for alternatives. Driving away contributors is bad. Making slow, quiet enemies is worse. - -* _It gives maintainers power over new contributors_, which many maintainers abuse. This abuse can be subconscious. Yet it is widespread. Maintainers inherently strive to remain important in their project. If they can keep out potential competitors by delaying and blocking their patches, they will. - -* _It opens the door to discrimination_. One can argue, a project belongs to its maintainers, so they can choose who they want to work with. My response is: projects that are not aggressively inclusive will die, and deserve to die. - -* _It slows down the learning cycle_. Innovation demands rapid experiment-failure-success cycles. Someone identifies a problem or inefficiency in a product. Someone proposes a fix. The fix is tested and works or fails. We have learned something new. The faster this cycle happens, the faster and more accurately the project can move. - -* _It gives outsiders the chance to troll the project_. It is a simple as raising an objection to a new patch. "I don't like this code." Discussions over details can use up much more effort than writing code. It is far cheaper to attack a patch than to make one. These economics favor the trolls and punish the honest contributors. - -* _It puts the burden of work on individual contributors_, which is ironic and sad for open source. We want to work together yet we're told to fix our work alone. - +* *It tells new contributors, "guilty until proven innocent,"* which is a negative message that creates negative emotions. Contributors who feel unwelcome will always look for alternatives. Driving away contributors is bad. Making slow, quiet enemies is worse. + +* *It gives maintainers power over new contributors*, which many maintainers abuse. This abuse can be subconscious. Yet it is widespread. Maintainers inherently strive to remain important in their project. If they can keep out potential competitors by delaying and blocking their patches, they will. + +* *It opens the door to discrimination*. One can argue, a project belongs to its maintainers, so they can choose who they want to work with. My response is: projects that are not aggressively inclusive will die, and deserve to die. + +* *It slows down the learning cycle*. Innovation demands rapid experiment-failure-success cycles. Someone identifies a problem or inefficiency in a product. Someone proposes a fix. The fix is tested and works or fails. We have learned something new. The faster this cycle happens, the faster and more accurately the project can move. + +* *It gives outsiders the chance to troll the project*. It is a simple as raising an objection to a new patch. "I don't like this code." Discussions over details can use up much more effort than writing code. It is far cheaper to attack a patch than to make one. These economics favor the trolls and punish the honest contributors. + +* *It puts the burden of work on individual contributors*, which is ironic and sad for open source. We want to work together yet we're told to fix our work alone. Now let's see how this works when we use Optimistic Merging, or OM. To start with, understand that not all patches nor all contributors are the same. We see at least four main cases in our open source projects: -1. Good contributors who know the rules and write excellent, perfect patches. -2. Good contributors who make mistakes, and who write useful yet broken patches. -3. Mediocre contributors who make patches that no-one notices or cares about. -4. Trollish contributors who ignore the rules, and who write toxic patches. +1. Good contributors who know the rules and write excellent, perfect patches. +2. Good contributors who make mistakes, and who write useful yet broken patches. +3. Mediocre contributors who make patches that no-one notices or cares about. +4. Trollish contributors who ignore the rules, and who write toxic patches. PM assumes all patches are toxic until proven good (4). Whereas in reality most patches tend to be useful, and worth improving (2). Let's see how each scenario works, with PM and OM: -1. PM: depending on unspecified, arbitrary criteria, patch may be merged rapidly or slowly. At least sometimes, a good contributor will be left with bad feelings. OM: good contributors feel happy and appreciated, and continue to provide excellent patches until they are done using the project. -2. PM: contributor retreats, fixes patch, comes back somewhat humiliated. OM: second contributor joins in to help first fix their patch. We get a short, happy patch party. New contributor now has a coach and friend in the project. -3. PM: we get a flamewar and everyone wonders why the community is so hostile. OM: the mediocre contributor is largely ignored. If patch needs fixing, it'll happen rapidly. Contributor loses interest and eventually the patch is reverted. -4. PM: we get a flamewar which troll wins by sheer force of argument. Community explodes in fight-or-flee emotions. Bad patches get pushed through. OM: existing contributor immediately reverts the patch. There is no discussion. Troll may try again, and eventually may be banned. Toxic patches remain in git history forever. +1. PM: depending on unspecified, arbitrary criteria, patch may be merged rapidly or slowly. At least sometimes, a good contributor will be left with bad feelings. OM: good contributors feel happy and appreciated, and continue to provide excellent patches until they are done using the project. +2. PM: contributor retreats, fixes patch, comes back somewhat humiliated. OM: second contributor joins in to help first fix their patch. We get a short, happy patch party. New contributor now has a coach and friend in the project. +3. PM: we get a flamewar and everyone wonders why the community is so hostile. OM: the mediocre contributor is largely ignored. If patch needs fixing, it'll happen rapidly. Contributor loses interest and eventually the patch is reverted. +4. PM: we get a flamewar which troll wins by sheer force of argument. Community explodes in fight-or-flee emotions. Bad patches get pushed through. OM: existing contributor immediately reverts the patch. There is no discussion. Troll may try again, and eventually may be banned. Toxic patches remain in git history forever. In each case, OM has a better outcome than PM. @@ -260,8 +266,9 @@ In essence, the goal here is to allow users to try patches rather than to spend Just keep the issue tracker clean. -Branches and Releases ---------------------- +## Branches and Releases + +--- When C4 is working, we get two massive simplifications of our delivery process. One, we don't need or use branches. Two, we deliver from master. @@ -277,8 +284,9 @@ I'll come to branches soon. In short (or "tl;dr", as they say on the webs), bran > To make a stable release a Maintainer shall tag the repository. Stable releases SHALL always be released from the repository master. -Evolution of Public Contracts ------------------------------ +## Evolution of Public Contracts + +--- By "public contracts", I mean APIs and protocols. Up until the end of 2011, libzmq's naturally happy state was marred by broken promises and broken contracts. We stopped making promises (aka "road maps") for libzmq completely, and our dominant theory of change is now that it emerges carefully and accurately over time. At a 2012 Chicago meetup, Garrett Smith and Chuck Remes called this the "drunken stumble to greatness", which is how I think of it now. @@ -290,7 +298,7 @@ You'd think this was a given for professional software engineers but no, it's no > All Public Contracts SHOULD have space for extensibility and experimentation. -Now, the real thing is that public contracts _do change_. It's not about not changing them. It's about changing them safely. This means educating (especially protocol) designers to create that space up-front. +Now, the real thing is that public contracts *do change*. It's not about not changing them. It's about changing them safely. This means educating (especially protocol) designers to create that space up-front. > A patch that modifies a stable Public Contract SHOULD not break existing applications unless there is overriding consensus on the value of doing this. @@ -298,16 +306,16 @@ Sometimes the patch is fixing a bad API that no one is using. It's a freedom we > A patch that introduces new features SHOULD do so using new names (a new contract). -We had the experience in ZeroMQ once or twice of new features using old names (or worse, using names that were _still in use_ elsewhere). ZeroMQ v3.0 had a newly introduced "ROUTER" socket that was totally different from the existing ROUTER socket in 2.x. Dear lord, you should be face-palming, why? The reason: apparently, even smart people sometimes need regulation to stop them doing silly things. +We had the experience in ZeroMQ once or twice of new features using old names (or worse, using names that were *still in use* elsewhere). ZeroMQ v3.0 had a newly introduced "ROUTER" socket that was totally different from the existing ROUTER socket in 2.x. Dear lord, you should be face-palming, why? The reason: apparently, even smart people sometimes need regulation to stop them doing silly things. > New contracts SHOULD be marked as "draft" until they are stable and used by real users. -> +> > Old contracts SHOULD be deprecated in a systematic fashion by marking new contracts as "draft" until they are stable, then marking the old contracts as "deprecated". This life cycle notation has the great benefit of actually telling users what is going on with a consistent direction. "Draft" means "we have introduced this and intend to make it stable if it works". It does not mean, "we have introduced this and will remove it at any time if we feel like it". One assumes that code that survives more than one patch cycle is meant to be there. "Deprecated" means "we have replaced this and intend to remove it". > Old contracts SHOULD be deprecated in a systematic fashion by marking them as "deprecated" and replacing them with new contracts as needed. -> +> > When sufficient time has passed, old deprecated contracts SHOULD be removed. In theory this gives applications time to move onto stable new contracts without risk. You can upgrade first, make sure things work, and then, over time, fix things up to remove dependencies on deprecated and legacy APIs and protocols. @@ -316,8 +324,9 @@ In theory this gives applications time to move onto stable new contracts without Ah, yes, the joy when ZeroMQ v3.x renamed the top-used API functions (`zmq_send\[3\]` and `zmq_recv\[3\]`) and then recycled the old names for new methods that were utterly incompatible (and which I suspect few people actually use). You should be slapping yourself in confusion again, but really, this is what happened and I was as guilty as anyone. After all, we did change the version number! The only benefit of that experience was to get this rule. -Project Administration ----------------------- +## Project Administration + +--- > The project founders SHALL act as Administrators to manage the set of project Maintainers. @@ -339,11 +348,12 @@ This was Ian Barber's suggestion: we need a way to crop inactive maintainers. Or Now and then, your projects will attract people of the wrong character. You will get better at seeing these people, over time. C4 helps in two ways. One, by setting out strong rules, it discourages the chaos-seekers and bullies, who cannot tolerate others' rules. Two, it gives you the Administrator the power to ban them. I like to give such people time, to show themselves, and get their patches on the public record (a reason to merge bad patches, which of course you can remove after a suitable pause). +## License and Further Reading -License and Further Reading ======= + The C4, along with this breakdown of it, was created (primarily) by the late Pieter Hintjens. If the C4 interests you it's a very good idea to check out Pieter's blog and watch his videos to get a deep understanding of his research into this field. Here's a good one to start with: [How Conway's law is eating your job.](https://www.youtube.com/watch?v=7HECD3eLoVo) -The text on this page is Copyright (c) 2007-2014 iMatix Corporation and Contributors, 2017-2018 Blockrazor and Contributors and is released under [CC-BY-SA-4](https://creativecommons.org/licenses/by/4.0/) +The text on this page is Copyright ©️ 2007-2014 iMatix Corporation and Contributors, 2017-2018 Blockrazor and Contributors and is released under [CC-BY-SA-4](https://creativecommons.org/licenses/by/4.0/) -This breakdown and description of the C4 is an excerpt from [chapter 6 of _ØMQ - The Guide_](http://zguide.zeromq.org/page:all#The-ZeroMQ-Process-C), which is maintained at [booksbyus/zguide on github](https://github.com/booksbyus/zguide/blob/master/chapter6.txt). +This breakdown and description of the C4 is an excerpt from [chapter 6 of _ØMQ ― The Guide_](http://zguide.zeromq.org/page:all#The-ZeroMQ-Process-C), which is maintained at [booksbyus/zguide on github](https://github.com/booksbyus/zguide/blob/master/chapter6.txt). From 356e92c14c979c8ad18a4e1dce8ec744065ffedf Mon Sep 17 00:00:00 2001 From: dysbulic Date: Mon, 16 Oct 2023 19:54:49 -0400 Subject: [PATCH 041/122] =?UTF-8?q?adding=20a=20bunch=20of=20down=20migrat?= =?UTF-8?q?ions=20=F0=9F=94=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../down.sql | 2 +- .../down.sql | 2 +- .../down.sql | 4 +-- .../down.sql | 4 +-- .../up.sql | 2 +- .../down.sql | 4 +-- .../down.sql | 2 +- .../down.sql | 5 +--- .../up.sql | 6 +++-- .../down.sql | 5 +--- .../down.sql | 5 +--- .../up.sql | 6 +++-- .../down.sql | 25 +++---------------- .../up.sql | 20 ++++++++------- .../down.sql | 5 +--- .../up.sql | 6 +++-- .../down.sql | 25 +++---------------- 17 files changed, 45 insertions(+), 83 deletions(-) diff --git a/hasura/migrations/1694790536010_set_fk_public_link_type/down.sql b/hasura/migrations/1694790536010_set_fk_public_link_type/down.sql index b0b011d433..9d15bd9ce0 100644 --- a/hasura/migrations/1694790536010_set_fk_public_link_type/down.sql +++ b/hasura/migrations/1694790536010_set_fk_public_link_type/down.sql @@ -1 +1 @@ -alter table "public"."link" drop constraint "link_type_fkey"; +ALTER TABLE public.link DROP CONSTRAINT link_type_fkey; diff --git a/hasura/migrations/1695821615820_copy_guild_urls_into_link_table/down.sql b/hasura/migrations/1695821615820_copy_guild_urls_into_link_table/down.sql index 5967bd5392..53cfdbf9ea 100644 --- a/hasura/migrations/1695821615820_copy_guild_urls_into_link_table/down.sql +++ b/hasura/migrations/1695821615820_copy_guild_urls_into_link_table/down.sql @@ -1 +1 @@ -DELETE FROM link \ No newline at end of file +DELETE FROM link; diff --git a/hasura/migrations/1695821815342_alter_table_public_guild_drop_column_discord_invite_url/down.sql b/hasura/migrations/1695821815342_alter_table_public_guild_drop_column_discord_invite_url/down.sql index 0f4ff86cbb..7e660a1763 100644 --- a/hasura/migrations/1695821815342_alter_table_public_guild_drop_column_discord_invite_url/down.sql +++ b/hasura/migrations/1695821815342_alter_table_public_guild_drop_column_discord_invite_url/down.sql @@ -1,2 +1,2 @@ -alter table "public"."guild" alter column "discord_invite_url" drop not null; -alter table "public"."guild" add column "discord_invite_url" text; +ALTER TABLE public.guild ALTER COLUMN discord_invite_url DROP NOT NULL; +ALTER TABLE public.guild ADD COLUMN discord_invite_url text; diff --git a/hasura/migrations/1695821839485_alter_table_public_guild_drop_column_github_url/down.sql b/hasura/migrations/1695821839485_alter_table_public_guild_drop_column_github_url/down.sql index 141260651a..c0bfedd5d6 100644 --- a/hasura/migrations/1695821839485_alter_table_public_guild_drop_column_github_url/down.sql +++ b/hasura/migrations/1695821839485_alter_table_public_guild_drop_column_github_url/down.sql @@ -1,2 +1,2 @@ -alter table "public"."guild" alter column "github_url" drop not null; -alter table "public"."guild" add column "github_url" text; +ALTER TABLE public.guild ALTER COLUMN github_url DROP NOT NULL; +ALTER TABLE public.guild ADD COLUMN github_url text; diff --git a/hasura/migrations/1695821839485_alter_table_public_guild_drop_column_github_url/up.sql b/hasura/migrations/1695821839485_alter_table_public_guild_drop_column_github_url/up.sql index cf7ab21725..7cc986d549 100644 --- a/hasura/migrations/1695821839485_alter_table_public_guild_drop_column_github_url/up.sql +++ b/hasura/migrations/1695821839485_alter_table_public_guild_drop_column_github_url/up.sql @@ -1 +1 @@ -alter table "public"."guild" drop column "github_url" cascade; +ALTER TABLE public.guild DROP COLUMN github_url CASCADE; diff --git a/hasura/migrations/1695821850665_alter_table_public_guild_drop_column_twitter_url/down.sql b/hasura/migrations/1695821850665_alter_table_public_guild_drop_column_twitter_url/down.sql index e8b443960c..b1b470098c 100644 --- a/hasura/migrations/1695821850665_alter_table_public_guild_drop_column_twitter_url/down.sql +++ b/hasura/migrations/1695821850665_alter_table_public_guild_drop_column_twitter_url/down.sql @@ -1,2 +1,2 @@ -alter table "public"."guild" alter column "twitter_url" drop not null; -alter table "public"."guild" add column "twitter_url" text; +ALTER TABLE public.guild ALTER COLUMN twitter_url DROP NOT NULL; +ALTER TABLE public.guild ADD COLUMN twitter_url text; diff --git a/hasura/migrations/1695829101657_alter_table_public_guild_metadata_alter_column_discord_id/down.sql b/hasura/migrations/1695829101657_alter_table_public_guild_metadata_alter_column_discord_id/down.sql index f69e37a2b6..24a617b139 100644 --- a/hasura/migrations/1695829101657_alter_table_public_guild_metadata_alter_column_discord_id/down.sql +++ b/hasura/migrations/1695829101657_alter_table_public_guild_metadata_alter_column_discord_id/down.sql @@ -1 +1 @@ -alter table "public"."guild_metadata" alter column "discord_id" set not null; +ALTER TABLE public.guild_metadata ALTER COLUMN discord_id SET NOT NULL; diff --git a/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/down.sql b/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/down.sql index bd1b7189b5..2f9fd1bb35 100644 --- a/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/down.sql +++ b/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/down.sql @@ -1,4 +1 @@ --- Could not auto-generate a down migration. --- Please write an appropriate down migration for the SQL below: --- alter table "public"."guild" add column "updated_at" timestamptz --- null default now(); +ALTER TABLE public.guild DROP COLUMN updated_at; diff --git a/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/up.sql b/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/up.sql index 22aa64dcf5..c051702d20 100644 --- a/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/up.sql +++ b/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/up.sql @@ -1,2 +1,4 @@ -alter table "public"."guild" add column "updated_at" timestamptz - null default now(); +ALTER TABLE public.guild + ADD COLUMN updated_at timestamptz + NULL DEFAULT now() +; diff --git a/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql b/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql index 17b78dee0d..e61e3529f9 100644 --- a/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql +++ b/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql @@ -1,4 +1 @@ --- Could not auto-generate a down migration. --- Please write an appropriate down migration for the SQL below: --- alter table "public"."token" add column "multiplier" float8 --- not null default '1'; +ALTER TABLE public.token DROP COLUMN multiplier; diff --git a/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql b/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql index 8f40543bd6..da2275c028 100644 --- a/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql +++ b/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql @@ -1,4 +1 @@ --- Could not auto-generate a down migration. --- Please write an appropriate down migration for the SQL below: --- alter table "public"."token" add column "created_at" timestamptz --- null default now(); +ALTER TABLE public.token DROP COLUMN created_at; diff --git a/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/up.sql b/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/up.sql index 92de05619e..ab894f5ab3 100644 --- a/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/up.sql +++ b/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/up.sql @@ -1,2 +1,4 @@ -alter table "public"."token" add column "created_at" timestamptz - null default now(); +ALTER TABLE public.token + ADD COLUMN created_at timestamptz + NULL DEFAULT now() +; diff --git a/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql index 857fc19ab4..edb5cc364e 100644 --- a/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql +++ b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql @@ -1,21 +1,4 @@ --- Could not auto-generate a down migration. --- Please write an appropriate down migration for the SQL below: --- alter table "public"."token" add column "updated_at" timestamptz --- null default now(); --- --- CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"() --- RETURNS TRIGGER AS $$ --- DECLARE --- _new record; --- BEGIN --- _new := NEW; --- _new."updated_at" = NOW(); --- RETURN _new; --- END; --- $$ LANGUAGE plpgsql; --- CREATE TRIGGER "set_public_token_updated_at" --- BEFORE UPDATE ON "public"."token" --- FOR EACH ROW --- EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"(); --- COMMENT ON TRIGGER "set_public_token_updated_at" ON "public"."token" --- IS 'trigger to set value of column "updated_at" to current timestamp on row update'; +ALTER TABLE public.token DROP COLUMN updated_at; +DROP TRIGGER IF EXISTS public.set_current_timestamp_updated_at + ON public.token +; diff --git a/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/up.sql b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/up.sql index abe65906b6..7cfff3f7f9 100644 --- a/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/up.sql +++ b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/up.sql @@ -1,19 +1,21 @@ -alter table "public"."token" add column "updated_at" timestamptz - null default now(); +ALTER TABLE public.token + ADD COLUMN updated_at timestamptz + null default now() +; -CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"() +CREATE OR REPLACE FUNCTION public.set_current_timestamp_updated_at() RETURNS TRIGGER AS $$ DECLARE _new record; BEGIN _new := NEW; - _new."updated_at" = NOW(); + _new.updated_at = NOW(); RETURN _new; END; $$ LANGUAGE plpgsql; -CREATE TRIGGER "set_public_token_updated_at" -BEFORE UPDATE ON "public"."token" +CREATE TRIGGER set_public_token_updated_at +BEFORE UPDATE ON public.token FOR EACH ROW -EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"(); -COMMENT ON TRIGGER "set_public_token_updated_at" ON "public"."token" -IS 'trigger to set value of column "updated_at" to current timestamp on row update'; +EXECUTE PROCEDURE public.set_current_timestamp_updated_at(); +COMMENT ON TRIGGER set_public_token_updated_at ON public.token +IS 'trigger to set value of column `updated_at` to current timestamp on row update'; diff --git a/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql b/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql index b712f41882..dc9c8c097c 100644 --- a/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql +++ b/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql @@ -1,4 +1 @@ --- Could not auto-generate a down migration. --- Please write an appropriate down migration for the SQL below: --- alter table "public"."xp" add column "created_at" timestamptz --- not null default now(); +ALTER TABLE public.xp DROP COLUMN created_at; diff --git a/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/up.sql b/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/up.sql index fb8a7b3518..df6a9a4854 100644 --- a/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/up.sql +++ b/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/up.sql @@ -1,2 +1,4 @@ -alter table "public"."xp" add column "created_at" timestamptz - not null default now(); +ALTER TABLE public.xp + ADD COLUMN created_at timestamptz + NOT NULL DEFAULT now() +; diff --git a/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql b/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql index 0585164855..0affc0695d 100644 --- a/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql +++ b/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql @@ -1,21 +1,4 @@ --- Could not auto-generate a down migration. --- Please write an appropriate down migration for the SQL below: --- alter table "public"."xp" add column "updated_at" timestamptz --- not null default now(); --- --- CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"() --- RETURNS TRIGGER AS $$ --- DECLARE --- _new record; --- BEGIN --- _new := NEW; --- _new."updated_at" = NOW(); --- RETURN _new; --- END; --- $$ LANGUAGE plpgsql; --- CREATE TRIGGER "set_public_xp_updated_at" --- BEFORE UPDATE ON "public"."xp" --- FOR EACH ROW --- EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"(); --- COMMENT ON TRIGGER "set_public_xp_updated_at" ON "public"."xp" --- IS 'trigger to set value of column "updated_at" to current timestamp on row update'; +ALTER TABLE public.xp add COLUMN updated_at; +DROP TRIGGER IF EXISTS public.set_current_timestamp_updated_at + ON public.xp +; From c0e3d3124d8e9127138a0c06b433216efa353510 Mon Sep 17 00:00:00 2001 From: dysbulic Date: Mon, 16 Oct 2023 20:07:09 -0400 Subject: [PATCH 042/122] =?UTF-8?q?cascading=20column=20`drop`s=20?= =?UTF-8?q?=F0=9F=9B=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../down.sql | 2 +- .../down.sql | 2 +- .../down.sql | 2 +- .../down.sql | 2 +- .../down.sql | 2 +- .../down.sql | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/down.sql b/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/down.sql index 2f9fd1bb35..9a26dacdf9 100644 --- a/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/down.sql +++ b/hasura/migrations/1696771380738_alter_table_public_guild_add_column_updated_at/down.sql @@ -1 +1 @@ -ALTER TABLE public.guild DROP COLUMN updated_at; +ALTER TABLE public.guild DROP COLUMN updated_at CASCADE; diff --git a/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql b/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql index e61e3529f9..70a4734ebe 100644 --- a/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql +++ b/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql @@ -1 +1 @@ -ALTER TABLE public.token DROP COLUMN multiplier; +ALTER TABLE public.token DROP COLUMN multiplie CASCADE; diff --git a/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql b/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql index da2275c028..6d55713535 100644 --- a/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql +++ b/hasura/migrations/1697476304617_alter_table_public_token_add_column_created_at/down.sql @@ -1 +1 @@ -ALTER TABLE public.token DROP COLUMN created_at; +ALTER TABLE public.token DROP COLUMN created_at CASCADE; diff --git a/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql index edb5cc364e..355d61f39d 100644 --- a/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql +++ b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql @@ -1,4 +1,4 @@ -ALTER TABLE public.token DROP COLUMN updated_at; +ALTER TABLE public.token DROP COLUMN updated_at CASCADE; DROP TRIGGER IF EXISTS public.set_current_timestamp_updated_at ON public.token ; diff --git a/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql b/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql index dc9c8c097c..341c04cc57 100644 --- a/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql +++ b/hasura/migrations/1697478812713_alter_table_public_xp_add_column_created_at/down.sql @@ -1 +1 @@ -ALTER TABLE public.xp DROP COLUMN created_at; +ALTER TABLE public.xp DROP COLUMN created_at CASCADE; diff --git a/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql b/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql index 0affc0695d..a476cbbb53 100644 --- a/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql +++ b/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql @@ -1,4 +1,4 @@ -ALTER TABLE public.xp add COLUMN updated_at; +ALTER TABLE public.xp DROP COLUMN updated_at CASCADE; DROP TRIGGER IF EXISTS public.set_current_timestamp_updated_at ON public.xp ; From 6c0f3f22e602b75d64434ba46546554caa91d4ce Mon Sep 17 00:00:00 2001 From: dysbulic Date: Mon, 16 Oct 2023 20:21:49 -0400 Subject: [PATCH 043/122] =?UTF-8?q?fixing=20`trigger`=20`drop`s=20?= =?UTF-8?q?=F0=9F=93=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../down.sql | 2 +- .../down.sql | 2 +- .../down.sql | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql b/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql index 70a4734ebe..a1e0a4baac 100644 --- a/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql +++ b/hasura/migrations/1697476299259_alter_table_public_token_add_column_multiplier/down.sql @@ -1 +1 @@ -ALTER TABLE public.token DROP COLUMN multiplie CASCADE; +ALTER TABLE public.token DROP COLUMN multiplier CASCADE; diff --git a/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql index 355d61f39d..9c4bc10cfc 100644 --- a/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql +++ b/hasura/migrations/1697476310113_alter_table_public_token_add_column_updated_at/down.sql @@ -1,4 +1,4 @@ ALTER TABLE public.token DROP COLUMN updated_at CASCADE; -DROP TRIGGER IF EXISTS public.set_current_timestamp_updated_at +DROP TRIGGER IF EXISTS set_current_timestamp_updated_at ON public.token ; diff --git a/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql b/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql index a476cbbb53..47d5690ebb 100644 --- a/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql +++ b/hasura/migrations/1697478824592_alter_table_public_xp_add_column_updated_at/down.sql @@ -1,4 +1,4 @@ ALTER TABLE public.xp DROP COLUMN updated_at CASCADE; -DROP TRIGGER IF EXISTS public.set_current_timestamp_updated_at +DROP TRIGGER IF EXISTS set_current_timestamp_updated_at ON public.xp ; From 77b2716f6ff1ce8c2f8b08026dcbbbdf8a17a1d5 Mon Sep 17 00:00:00 2001 From: dysbulic Date: Mon, 23 Oct 2023 14:13:50 -0400 Subject: [PATCH 044/122] =?UTF-8?q?handling=20result=20pagination,=20rever?= =?UTF-8?q?se=20chronology,=20&=20setting=20initial=20skip=20=F0=9F=A5=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hasura/clear-xp.mjs | 5 +- hasura/metadata/actions.graphql | 4 +- hasura/metadata/tables.yaml | 12 +- .../down.sql | 1 + .../up.sql | 1 + .../handlers/actions/player/syncBalances.ts | 112 ++++++++++-------- .../src/handlers/graphql/mutations/token.ts | 8 +- .../src/handlers/graphql/queries/token.ts | 5 +- schema.graphql | 78 ++++++------ 9 files changed, 128 insertions(+), 98 deletions(-) create mode 100644 hasura/migrations/1698081953289_alter_table_public_token_alter_column_last_offset/down.sql create mode 100644 hasura/migrations/1698081953289_alter_table_public_token_alter_column_last_offset/up.sql diff --git a/hasura/clear-xp.mjs b/hasura/clear-xp.mjs index fcb2e72f8b..f2fd4f4af4 100755 --- a/hasura/clear-xp.mjs +++ b/hasura/clear-xp.mjs @@ -58,16 +58,17 @@ async function clearBalances() { const resetOffsetsMutation = /* GraphQL */` mutation ResetOffsets { - update_token(where: {}, _set: { lastOffset: 0 }) { + update_token(where: {}, _set: { lastBlockHeight: 42746520 }) { affected_rows } } `.trim() async function resetOffsets() { - const { data } = await fetchGraphQL({ + const { data, errors } = await fetchGraphQL({ opDoc: resetOffsetsMutation, }) + if(!!errors) throw errors[0] return data.update_token.affected_rows } diff --git a/hasura/metadata/actions.graphql b/hasura/metadata/actions.graphql index 6f8329b3c6..0462fd232b 100644 --- a/hasura/metadata/actions.graphql +++ b/hasura/metadata/actions.graphql @@ -221,8 +221,8 @@ type UpdateComposeDBProfileResponse { type TokenReturn { added: [BalanceOutput] - oldOffset: Int - newOffset: Int + oldHeight: Int + newHeight: Int count: Int multiplier: Float players: [PlayerOutput] diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 504cfefb15..f1f057c260 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -1470,17 +1470,23 @@ column_config: chain_id: custom_name: chainId + created_at: + custom_name: createdAt guild_id: custom_name: guildId - last_offset: - custom_name: lastOffset + last_block_height: + custom_name: lastBlockHeight safe_address: custom_name: safeAddress + updated_at: + custom_name: updatedAt custom_column_names: chain_id: chainId + created_at: createdAt guild_id: guildId - last_offset: lastOffset + last_block_height: lastBlockHeight safe_address: safeAddress + updated_at: updatedAt custom_root_fields: {} object_relationships: - name: guild diff --git a/hasura/migrations/1698081953289_alter_table_public_token_alter_column_last_offset/down.sql b/hasura/migrations/1698081953289_alter_table_public_token_alter_column_last_offset/down.sql new file mode 100644 index 0000000000..b1e0f147df --- /dev/null +++ b/hasura/migrations/1698081953289_alter_table_public_token_alter_column_last_offset/down.sql @@ -0,0 +1 @@ +alter table "public"."token" rename column "last_block_height" to "last_offset"; diff --git a/hasura/migrations/1698081953289_alter_table_public_token_alter_column_last_offset/up.sql b/hasura/migrations/1698081953289_alter_table_public_token_alter_column_last_offset/up.sql new file mode 100644 index 0000000000..511bed1d73 --- /dev/null +++ b/hasura/migrations/1698081953289_alter_table_public_token_alter_column_last_offset/up.sql @@ -0,0 +1 @@ +alter table "public"."token" rename column "last_offset" to "last_block_height"; diff --git a/packages/backend/src/handlers/actions/player/syncBalances.ts b/packages/backend/src/handlers/actions/player/syncBalances.ts index f6700d2b27..013291474d 100644 --- a/packages/backend/src/handlers/actions/player/syncBalances.ts +++ b/packages/backend/src/handlers/actions/player/syncBalances.ts @@ -1,4 +1,4 @@ -import { getCurrentSeasonStart } from '@metafam/utils'; +import { getCurrentSeasonStart, Maybe } from '@metafam/utils'; import ethers from 'ethers'; import { Request, Response } from 'express'; import fetch from 'node-fetch'; @@ -7,8 +7,10 @@ import { client } from '../../../lib/hasuraClient.js'; import { computeRank } from '../../../lib/rankHelpers.js'; type SafeResponse = { + next: Maybe; results: Array<{ origin: string; + blockNumber: number; executionDate: string; transfers: Array<{ to: string; @@ -21,76 +23,89 @@ type SafeResponse = { // @todo return balance of token of player in guild const setBalances = async ({ safeAddress, - offset = 0, + lastBlockHeight = 0, tokenAddress: guildTokenAddress, chainId = 1, }: { safeAddress: string; - offset?: number; + lastBlockHeight?: number; tokenAddress: string; chainId: number; }) => { - const safes = { - 137: - 'https://safe-transaction-polygon.safe.global' + - `/api/v1/safes/${safeAddress}/all-transactions/` + - `?limit=100&offset=${offset}&executed=true` + - '&queued=false&trusted=true', - }; - const safeURL = safes[chainId as keyof typeof safes]; - if (!safeURL) { - throw new Error(`No Safe URL for chain #${chainId}.`); - } - - const res = await fetch(safeURL); - const { results } = (await res.json()) as SafeResponse; + let safeURL; + let minBlockHeight; + let maxBlockHeight; const uniqueDrops: Record> = {}; - const airdrops = results.filter((tx) => tx.origin?.includes('CSV Airdrop')); + do { + const safes = { + 137: + 'https://safe-transaction-polygon.safe.global' + + `/api/v1/safes/${safeAddress}/all-transactions/` + + `?limit=10&executed=true` + + '&queued=false&trusted=true', + }; + safeURL ??= safes[chainId as keyof typeof safes]; + if (!safeURL) { + throw new Error(`No Safe URL for chain #${chainId}.`); + } + + // eslint-disable-next-line no-await-in-loop + const res = await fetch(safeURL); + // eslint-disable-next-line no-await-in-loop + const { next, results } = (await res.json()) as SafeResponse; + safeURL = next; + const heights = results.map(({ blockNumber }) => blockNumber); + minBlockHeight = Math.min(minBlockHeight ?? Infinity, ...heights); + maxBlockHeight = Math.max(maxBlockHeight ?? 0, ...heights); + const airdrops = results.filter((tx) => tx.origin?.includes('CSV Airdrop')); - airdrops.forEach(({ executionDate, transfers }) => { - transfers?.forEach(({ to, tokenAddress, value }) => { - uniqueDrops[executionDate] ??= {}; - uniqueDrops[executionDate][to] ??= 0; - if (tokenAddress === guildTokenAddress) { - uniqueDrops[executionDate][to] += Number( - ethers.utils.formatEther(value), - ); - } + airdrops.forEach(({ blockNumber, executionDate, transfers }) => { + if (blockNumber <= lastBlockHeight) return; + + transfers?.forEach(({ to, tokenAddress, value }) => { + uniqueDrops[executionDate] ??= {}; + uniqueDrops[executionDate][to] ??= 0; + if (tokenAddress === guildTokenAddress) { + uniqueDrops[executionDate][to] += Number( + ethers.utils.formatEther(value), + ); + } + }); }); - }); + } while (!!safeURL && minBlockHeight > lastBlockHeight); const added = await Promise.all( Object.entries(uniqueDrops).map(async ([executedAt, drops]) => { - const dropsRet = await Promise.all( + const dropsReturned = await Promise.all( Object.entries(drops).map(async ([to, value]) => { - const dat = { + const entry = { playerAddress: to, amount: value, }; await client.AddBalance({ - ...dat, + ...entry, executedAt: new Date(executedAt), tokenAddress: guildTokenAddress, }); - return dat; + return entry; }), ); return { - executedAt: new Date(executedAt).toLocaleString('sv'), - drops: dropsRet, + executedAt: new Date(executedAt).toLocaleString('sv').replace(' ', '@'), + drops: dropsReturned, }; }), ); - await client.UpdateLastOffset({ + await client.UpdateLastBlockHeight({ tokenAddress: guildTokenAddress, - offset: offset + results.length, + height: maxBlockHeight, }); return { added, - oldOffset: offset, - newOffset: offset + results.length, + oldHeight: lastBlockHeight, + newHeight: maxBlockHeight, }; }; @@ -99,26 +114,26 @@ export default async (req: Request, res: Response): Promise => { try { const { token: tokens } = await client.GetTokens(); const seasonStart = getCurrentSeasonStart(); - const tokenPromiseRet = await Promise.allSettled( + const tokenPromiseReturn = await Promise.allSettled( tokens.map( async ({ safeAddress, - lastOffset: offset, + lastBlockHeight, guildId, address, chainId, multiplier, }) => { - const balRet = await setBalances({ + const balancesReturned = await setBalances({ safeAddress, - offset, + lastBlockHeight, tokenAddress: address, chainId, }); const { guild: [{ guild_players: players }], } = await client.GetGuildMembers({ id: guildId }); - const playerRet = await Promise.all( + const playerReturned = await Promise.all( players.map( async ({ Player: { ethereumAddress: ethAddress, id: playerId }, @@ -135,8 +150,9 @@ export default async (req: Request, res: Response): Promise => { playerAddress: ethAddress, executedAfter: seasonStart, }); - const seasonalBalance = + const seasonalSum = seasonalTotal.balance_aggregate.aggregate?.sum?.amount ?? 0; + const seasonalBalance = seasonalSum * multiplier; const { xp: [{ initial } = { initial: 0 }], @@ -163,15 +179,15 @@ export default async (req: Request, res: Response): Promise => { ), ); return { - ...balRet, + ...balancesReturned, multiplier, count: players.length, - players: playerRet, + players: playerReturned, }; }, ), ); - const tokenRet = tokenPromiseRet.map((t) => + const tokenReturns = tokenPromiseReturn.map((t) => t.status === 'fulfilled' ? t.value : { status: 'failed' }, ); @@ -191,7 +207,7 @@ export default async (req: Request, res: Response): Promise => { success: true, message: `Successfully synced ${xp.length} users.`, seasonStart, - tokenReturns: tokenRet, + tokenReturns, }); } catch (err) { res.status(500).json({ success: false, message: (err as Error).message }); diff --git a/packages/backend/src/handlers/graphql/mutations/token.ts b/packages/backend/src/handlers/graphql/mutations/token.ts index 44cc7ba0da..b53f53d8a4 100644 --- a/packages/backend/src/handlers/graphql/mutations/token.ts +++ b/packages/backend/src/handlers/graphql/mutations/token.ts @@ -16,16 +16,18 @@ export const TokenMutations = /* GraphQL */ ` id } } - mutation UpdateLastOffset($tokenAddress: String!, $offset: Int!) { + + mutation UpdateLastBlockHeight($tokenAddress: String!, $height: Int!) { update_token( where: { address: { _eq: $tokenAddress } } - _set: { lastOffset: $offset } + _set: { lastBlockHeight: $height } ) { returning { - lastOffset + lastBlockHeight } } } + mutation UpsertXP( $balance: float8! $playerId: uuid! diff --git a/packages/backend/src/handlers/graphql/queries/token.ts b/packages/backend/src/handlers/graphql/queries/token.ts index 11391e35e2..9691ca89f6 100644 --- a/packages/backend/src/handlers/graphql/queries/token.ts +++ b/packages/backend/src/handlers/graphql/queries/token.ts @@ -5,16 +5,18 @@ export const TokenQueries = /* GraphQL */ ` chainId } } + query GetTokens { token { address chainId safeAddress - lastOffset + lastBlockHeight guildId multiplier } } + query GetTotalForPlayer( $playerAddress: String! $tokenAddress: String! @@ -34,6 +36,7 @@ export const TokenQueries = /* GraphQL */ ` } } } + query GetInitialXP($playerId: uuid!) { xp(where: { playerId: { _eq: $playerId } }) { initial diff --git a/schema.graphql b/schema.graphql index 997114285e..a6933db7b6 100644 --- a/schema.graphql +++ b/schema.graphql @@ -6939,14 +6939,14 @@ columns and relationships of "token" type token { address: String! chainId: Int! - created_at: timestamptz + createdAt: timestamptz "An object relationship" guild: guild! guildId: uuid! - lastOffset: Int! + lastBlockHeight: Int! multiplier: float8! safeAddress: String! - updated_at: timestamptz + updatedAt: timestamptz "An array relationship" xps( "distinct select on columns" @@ -7000,30 +7000,30 @@ type token_aggregate_fields { "aggregate avg on columns" type token_avg_fields { chainId: Float - lastOffset: Float + lastBlockHeight: Float multiplier: Float } "aggregate max on columns" type token_max_fields { address: String chainId: Int - created_at: timestamptz + createdAt: timestamptz guildId: uuid - lastOffset: Int + lastBlockHeight: Int multiplier: float8 safeAddress: String - updated_at: timestamptz + updatedAt: timestamptz } "aggregate min on columns" type token_min_fields { address: String chainId: Int - created_at: timestamptz + createdAt: timestamptz guildId: uuid - lastOffset: Int + lastBlockHeight: Int multiplier: float8 safeAddress: String - updated_at: timestamptz + updatedAt: timestamptz } """ response of any mutation on the table "token" @@ -7037,43 +7037,43 @@ type token_mutation_response { "aggregate stddev on columns" type token_stddev_fields { chainId: Float - lastOffset: Float + lastBlockHeight: Float multiplier: Float } "aggregate stddev_pop on columns" type token_stddev_pop_fields { chainId: Float - lastOffset: Float + lastBlockHeight: Float multiplier: Float } "aggregate stddev_samp on columns" type token_stddev_samp_fields { chainId: Float - lastOffset: Float + lastBlockHeight: Float multiplier: Float } "aggregate sum on columns" type token_sum_fields { chainId: Int - lastOffset: Int + lastBlockHeight: Int multiplier: float8 } "aggregate var_pop on columns" type token_var_pop_fields { chainId: Float - lastOffset: Float + lastBlockHeight: Float multiplier: Float } "aggregate var_samp on columns" type token_var_samp_fields { chainId: Float - lastOffset: Float + lastBlockHeight: Float multiplier: Float } "aggregate variance on columns" type token_variance_fields { chainId: Float - lastOffset: Float + lastBlockHeight: Float multiplier: Float } """ @@ -8525,17 +8525,17 @@ enum token_select_column { "column name" chainId "column name" - created_at + createdAt "column name" guildId "column name" - lastOffset + lastBlockHeight "column name" multiplier "column name" safeAddress "column name" - updated_at + updatedAt } """ update columns of table "token" @@ -8546,17 +8546,17 @@ enum token_update_column { "column name" chainId "column name" - created_at + createdAt "column name" guildId "column name" - lastOffset + lastBlockHeight "column name" multiplier "column name" safeAddress "column name" - updated_at + updatedAt } """ unique or primary key constraints on table "xp" @@ -12616,13 +12616,13 @@ input token_bool_exp { _or: [token_bool_exp!] address: String_comparison_exp chainId: Int_comparison_exp - created_at: timestamptz_comparison_exp + createdAt: timestamptz_comparison_exp guild: guild_bool_exp guildId: uuid_comparison_exp - lastOffset: Int_comparison_exp + lastBlockHeight: Int_comparison_exp multiplier: float8_comparison_exp safeAddress: String_comparison_exp - updated_at: timestamptz_comparison_exp + updatedAt: timestamptz_comparison_exp xps: xp_bool_exp xps_aggregate: xp_aggregate_bool_exp } @@ -12631,7 +12631,7 @@ input type for incrementing numeric columns in table "token" """ input token_inc_input { chainId: Int - lastOffset: Int + lastBlockHeight: Int multiplier: float8 } """ @@ -12640,13 +12640,13 @@ input type for inserting data into table "token" input token_insert_input { address: String chainId: Int - created_at: timestamptz + createdAt: timestamptz guild: guild_obj_rel_insert_input guildId: uuid - lastOffset: Int + lastBlockHeight: Int multiplier: float8 safeAddress: String - updated_at: timestamptz + updatedAt: timestamptz xps: xp_arr_rel_insert_input } """ @@ -12671,13 +12671,13 @@ Ordering options when selecting data from "token". input token_order_by { address: order_by chainId: order_by - created_at: order_by + createdAt: order_by guild: guild_order_by guildId: order_by - lastOffset: order_by + lastBlockHeight: order_by multiplier: order_by safeAddress: order_by - updated_at: order_by + updatedAt: order_by xps_aggregate: xp_aggregate_order_by } "primary key columns input for table: token" @@ -12690,12 +12690,12 @@ input type for updating data in table "token" input token_set_input { address: String chainId: Int - created_at: timestamptz + createdAt: timestamptz guildId: uuid - lastOffset: Int + lastBlockHeight: Int multiplier: float8 safeAddress: String - updated_at: timestamptz + updatedAt: timestamptz } """ Streaming cursor of the table "token" @@ -12710,12 +12710,12 @@ input token_stream_cursor_input { input token_stream_cursor_value_input { address: String chainId: Int - created_at: timestamptz + createdAt: timestamptz guildId: uuid - lastOffset: Int + lastBlockHeight: Int multiplier: float8 safeAddress: String - updated_at: timestamptz + updatedAt: timestamptz } input token_updates { "increments the numeric columns with given value of the filtered values" From bec60ffac488cdf9fd2295c04794e7758fd394a2 Mon Sep 17 00:00:00 2001 From: Sero <69639595+Seroxdesign@users.noreply.github.com> Date: Wed, 25 Oct 2023 06:16:22 -0400 Subject: [PATCH 045/122] Update DesktopPlayerStats.tsx --- packages/web/components/MegaMenu/DesktopPlayerStats.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/components/MegaMenu/DesktopPlayerStats.tsx b/packages/web/components/MegaMenu/DesktopPlayerStats.tsx index b17b8660ee..ced95ad2f2 100644 --- a/packages/web/components/MegaMenu/DesktopPlayerStats.tsx +++ b/packages/web/components/MegaMenu/DesktopPlayerStats.tsx @@ -28,7 +28,7 @@ export const DesktopPlayerStats: React.FC = ({ player }) => { const linkURL = usePlayerURL(player); return ( - + From b5d4eed7cdea5d050a322c9c3381f9764ec45746 Mon Sep 17 00:00:00 2001 From: luxumbra Date: Thu, 12 Oct 2023 16:39:15 +0100 Subject: [PATCH 046/122] calendar now pulling data from my endpoint and styled as per the Figma. --- .../web/components/Dashboard/Calendar.tsx | 349 +- packages/web/lib/hooks/useCalendar.ts | 83 + packages/web/package.json | 5 +- yarn.lock | 2896 +---------------- 4 files changed, 429 insertions(+), 2904 deletions(-) create mode 100644 packages/web/lib/hooks/useCalendar.ts diff --git a/packages/web/components/Dashboard/Calendar.tsx b/packages/web/components/Dashboard/Calendar.tsx index 97558383a7..490c261a64 100644 --- a/packages/web/components/Dashboard/Calendar.tsx +++ b/packages/web/components/Dashboard/Calendar.tsx @@ -1,33 +1,338 @@ -import { Flex, Text } from '@metafam/ds'; -import { CONFIG } from 'config'; +import { + Box, + ButtonGroup, + CalendarIcon, + ExternalLinkIcon, + Flex, + HStack, + IconButton, + Image, + LoadingState, + Popover, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverFooter, + PopoverHeader, + PopoverTrigger, + Portal, + Stack, + Text, + VStack, +} from '@metafam/ds'; +import { MarkdownViewer } from 'components/MarkdownViewer'; +import { useCalendar } from 'lib/hooks/useCalendar'; +import type { GoogleCalEventType } from 'lib/hooks/useCalendar'; +import { DateTime } from 'luxon'; import React, { useEffect, useState } from 'react'; +type GroupedEventsType = { + date: string; + events: GoogleCalEventType[]; +}; + export const Calendar: React.FC = () => { - const [isComponentMounted, setIsComponentMounted] = useState(false); + const [calendar, setCalendar] = useState([]); + const { events, timeZone, fetching, error } = useCalendar(); - useEffect(() => setIsComponentMounted(true), []); + const groupEventsByDay = (items: GoogleCalEventType[]) => { + const groupedEvents = items.reduce((acc, event) => { + console.log({ acc, start: event.start }); + const start = + 'dateTime' in event.start + ? DateTime.fromISO(event.start.dateTime) + : DateTime.fromISO(event.start.date); + const date = `${start}`; + if (!acc[date]) { + acc[date] = []; + } + acc[date].push(event); + return acc; + }, {} as Record); - if (!isComponentMounted) { - return null; - } + const days = Object.entries(groupedEvents).map(([date, calEvents]) => ({ + date, + events: calEvents as GoogleCalEventType[], + })); + + return days; + }; - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + useEffect(() => { + if (!fetching && calendar.length === 0 && events !== null) { + const days = groupEventsByDay(events); + console.log('days', days); + setCalendar(days); + } + return () => { + // setCalendar([]); + }; + }, [fetching]); + + if (error) { + return ( + + {error.message} + + ); + } return ( - - - Calendar - -