From 849f7cbde4b1764df6a5aa74d1d204eca34f3911 Mon Sep 17 00:00:00 2001 From: martines3000 Date: Wed, 27 Mar 2024 13:49:11 +0100 Subject: [PATCH 1/5] chore: should fix docusaurus errors --- packages/docs/vercel.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 packages/docs/vercel.json diff --git a/packages/docs/vercel.json b/packages/docs/vercel.json new file mode 100644 index 000000000..bd6fd8faa --- /dev/null +++ b/packages/docs/vercel.json @@ -0,0 +1,3 @@ +{ + "cleanUrls": true +} From 5cb712ad40156207f41c3f2fd863f0ad0472791a Mon Sep 17 00:00:00 2001 From: Urban Date: Sat, 30 Mar 2024 14:59:01 +0100 Subject: [PATCH 2/5] fix: visual bugs and fixes (#609) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Urban Vidovič --- .changeset/five-poets-yell.md | 5 ++ .../app/[locale]/(public)/ecosystem/page.tsx | 2 +- .../dapp/src/app/[locale]/(public)/layout.tsx | 6 +- .../app/(protected)/settings/page.tsx | 2 +- .../[locale]/app/(public)/campaigns/page.tsx | 2 +- packages/dapp/src/app/[locale]/app/layout.tsx | 20 ++--- .../src/app/api/campaigns/claims/route.ts | 58 ++++++++++++++ .../dapp/src/app/api/campaigns/issue/route.ts | 75 ++++++++++++------- .../dapp/src/components/AppNavbar/index.tsx | 6 +- .../components/AuthorizationRequest/index.tsx | 10 --- .../CampaignsDisplay/CampaignDisplay.tsx | 32 +++++++- .../CampaignsDisplay/RewardDisplay.tsx | 17 +++++ .../src/components/CampaignsDisplay/index.tsx | 44 +++++++++-- .../components/ConnectedProvider/index.tsx | 24 +++--- .../components/CredentialDisplay/index.tsx | 7 +- .../CredentialTable/index.tsx | 8 +- .../dapp/src/components/DeleteModal/index.tsx | 8 +- .../dapp/src/components/LandingPage/index.tsx | 2 +- .../src/components/MetaMaskProvider/index.tsx | 2 +- .../src/components/ModifyDSModal/index.tsx | 4 +- .../src/components/PublicNavbar/index.tsx | 4 +- .../src/components/SettingsCard/index.tsx | 2 +- .../dapp/src/components/ToggleTheme/index.tsx | 2 +- packages/dapp/src/hooks/useCampaignClaims.ts | 36 +++++++++ packages/dapp/src/hooks/useClaimCampaign.ts | 1 + packages/dapp/src/messages/en.json | 12 ++- packages/dapp/src/utils/format.ts | 4 + .../dapp/src/utils/supabase/database.types.ts | 10 +++ .../docs/docs/integrate-masca/start-here.md | 2 +- packages/docs/docs/roadmap.md | 5 +- packages/docs/docusaurus.config.js | 2 +- 31 files changed, 313 insertions(+), 101 deletions(-) create mode 100644 .changeset/five-poets-yell.md create mode 100644 packages/dapp/src/app/api/campaigns/claims/route.ts create mode 100644 packages/dapp/src/components/CampaignsDisplay/RewardDisplay.tsx create mode 100644 packages/dapp/src/hooks/useCampaignClaims.ts diff --git a/.changeset/five-poets-yell.md b/.changeset/five-poets-yell.md new file mode 100644 index 000000000..2a50ed7ca --- /dev/null +++ b/.changeset/five-poets-yell.md @@ -0,0 +1,5 @@ +--- +"@blockchain-lab-um/dapp": patch +--- + +Adds reclaim option for campaigns. diff --git a/packages/dapp/src/app/[locale]/(public)/ecosystem/page.tsx b/packages/dapp/src/app/[locale]/(public)/ecosystem/page.tsx index bd389d73a..6fbbb5087 100644 --- a/packages/dapp/src/app/[locale]/(public)/ecosystem/page.tsx +++ b/packages/dapp/src/app/[locale]/(public)/ecosystem/page.tsx @@ -80,7 +80,7 @@ const projectsDark: ProjectIconProps[] = [ export default function Page() { return ( -
+

Applications diff --git a/packages/dapp/src/app/[locale]/(public)/layout.tsx b/packages/dapp/src/app/[locale]/(public)/layout.tsx index a2bcc5996..f50522c79 100644 --- a/packages/dapp/src/app/[locale]/(public)/layout.tsx +++ b/packages/dapp/src/app/[locale]/(public)/layout.tsx @@ -7,8 +7,10 @@ export default async function PublicLayout({ children: React.ReactNode; }) { return ( -
- +
+
+ +
{children} diff --git a/packages/dapp/src/app/[locale]/app/(protected)/settings/page.tsx b/packages/dapp/src/app/[locale]/app/(protected)/settings/page.tsx index d399f8beb..ba2d3ea18 100644 --- a/packages/dapp/src/app/[locale]/app/(protected)/settings/page.tsx +++ b/packages/dapp/src/app/[locale]/app/(protected)/settings/page.tsx @@ -9,7 +9,7 @@ export const metadata: Metadata = { export default function Page() { return ( -
+
diff --git a/packages/dapp/src/app/[locale]/app/(public)/campaigns/page.tsx b/packages/dapp/src/app/[locale]/app/(public)/campaigns/page.tsx index a1e0f24b3..600bee2b4 100644 --- a/packages/dapp/src/app/[locale]/app/(public)/campaigns/page.tsx +++ b/packages/dapp/src/app/[locale]/app/(public)/campaigns/page.tsx @@ -8,7 +8,7 @@ export const metadata: Metadata = { export default function Page() { return ( -
+
); diff --git a/packages/dapp/src/app/[locale]/app/layout.tsx b/packages/dapp/src/app/[locale]/app/layout.tsx index 8cb014258..94aa900d3 100644 --- a/packages/dapp/src/app/[locale]/app/layout.tsx +++ b/packages/dapp/src/app/[locale]/app/layout.tsx @@ -5,6 +5,7 @@ import AppNavbar from '@/components/AppNavbar'; import { SignInModal } from '@/components/SignInModal'; import ToastWrapper from '@/components/ToastWrapper'; import { Providers } from '@/components/Providers'; +import { ScrollShadow } from '@nextui-org/react'; export default async function AppLayout({ children, @@ -13,16 +14,17 @@ export default async function AppLayout({ }) { return ( - -
-
- {children} +
+
+
+ + {children} +
diff --git a/packages/dapp/src/app/api/campaigns/claims/route.ts b/packages/dapp/src/app/api/campaigns/claims/route.ts new file mode 100644 index 000000000..a393bc8d3 --- /dev/null +++ b/packages/dapp/src/app/api/campaigns/claims/route.ts @@ -0,0 +1,58 @@ +import { type NextRequest, NextResponse } from 'next/server'; +import jwt from 'jsonwebtoken'; +import { supabaseServiceRoleClient } from '@/utils/supabase/supabaseServiceRoleClient'; +import { supabaseClient } from '@/utils/supabase/supabaseClient'; + +const CORS_HEADERS = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type', +}; + +export const dynamic = 'force-dynamic'; + +export async function GET(request: NextRequest) { + const token = request.headers.get('Authorization')?.replace('Bearer ', ''); + if (!token) { + return new NextResponse('Unauthorized', { + status: 401, + headers: { + ...CORS_HEADERS, + }, + }); + } + const user = jwt.verify(token, process.env.SUPABASE_JWT_SECRET!) as { + sub: string; + address: string; + aud: string; + role: string; + iat: number; + exp: number; + }; + + const supabase = supabaseServiceRoleClient(); + const { data: userId } = await supabase + .from('users') + .select('id') + .eq('address', user.address.toLowerCase()) + .single(); + if (!userId) { + return NextResponse.json({ claims: [] }, { headers: { ...CORS_HEADERS } }); + } + const { data: claims, error } = await supabase + .from('claims') + .select('*') + .eq('user_id', userId.id); + + if (error) { + console.error('Error getting claims', error); + return new NextResponse('Error getting claims', { + status: 500, + headers: { + ...CORS_HEADERS, + }, + }); + } + + return NextResponse.json({ claims }, { headers: { ...CORS_HEADERS } }); +} diff --git a/packages/dapp/src/app/api/campaigns/issue/route.ts b/packages/dapp/src/app/api/campaigns/issue/route.ts index a2c9cd04e..4ebd7fb38 100644 --- a/packages/dapp/src/app/api/campaigns/issue/route.ts +++ b/packages/dapp/src/app/api/campaigns/issue/route.ts @@ -58,6 +58,54 @@ export async function POST(request: NextRequest) { }, }); } + const supabase = supabaseServiceRoleClient(); + const { data: campaign, error: campaignError } = await supabase + .from('campaigns') + .select('*, requirements(id, *)') + .eq('id', campaignId) + .order('created_at', { ascending: false }) + .single() + .throwOnError(); + + if (campaignError) { + return new NextResponse('Campaign not found', { + status: 404, + headers: { + ...CORS_HEADERS, + }, + }); + } + + if (campaign.total && campaign.claimed >= campaign.total) { + return new NextResponse('Campaign is already fully claimed', { + status: 400, + headers: { + ...CORS_HEADERS, + }, + }); + } + const { data: completedRequirements, error: completedRequirementsError } = + await supabase.rpc('get_num_of_users_requirements_by_campaign', { + campaign_id: campaignId, + user_id: user.sub, + }); + + if (completedRequirementsError) { + return new NextResponse('Internal Server Error', { + status: 500, + headers: { + ...CORS_HEADERS, + }, + }); + } + if (completedRequirements !== campaign.requirements.length) { + return new NextResponse('User has not completed all requirements', { + status: 400, + headers: { + ...CORS_HEADERS, + }, + }); + } const agent = await getAgent(); const didResolution = await agent.resolveDid({ didUrl: did }); @@ -87,33 +135,6 @@ export async function POST(request: NextRequest) { }); } - const supabase = supabaseServiceRoleClient(); - - const { data: campaign, error: campaignError } = await supabase - .from('campaigns') - .select('*') - .eq('id', campaignId) - .single() - .throwOnError(); - - if (campaignError) { - return new NextResponse('Campaign not found', { - status: 404, - headers: { - ...CORS_HEADERS, - }, - }); - } - - if (campaign.total && campaign.claimed >= campaign.total) { - return new NextResponse('Campaign is already fully claimed', { - status: 400, - headers: { - ...CORS_HEADERS, - }, - }); - } - const { data: claim, error: claimError } = await supabase .from('claims') .select('*') diff --git a/packages/dapp/src/components/AppNavbar/index.tsx b/packages/dapp/src/components/AppNavbar/index.tsx index 349de074d..96d945548 100644 --- a/packages/dapp/src/components/AppNavbar/index.tsx +++ b/packages/dapp/src/components/AppNavbar/index.tsx @@ -35,8 +35,8 @@ export default function AppNavbar() { const { isConnected } = useAccount(); return ( -
-
+
+
@@ -45,7 +45,7 @@ export default function AppNavbar() {

-
+
{MAIN_LINKS.map(({ name, href, requiresConnection }) => { if ((requiresConnection && isConnected) || !requiresConnection) { return ( diff --git a/packages/dapp/src/components/AuthorizationRequest/index.tsx b/packages/dapp/src/components/AuthorizationRequest/index.tsx index 400489cf0..703c97a1f 100644 --- a/packages/dapp/src/components/AuthorizationRequest/index.tsx +++ b/packages/dapp/src/components/AuthorizationRequest/index.tsx @@ -112,14 +112,6 @@ const AuthorizationRequestFlow = () => { } }; - const sendAuthorizationResponse = async () => { - if (!api || !authorizationRequestURI || !parsedAuthorizationRequestURI) { - return; - } - - console.log('here'); - }; - useEffect(() => { if (!credentials.length) return; setIsSelectModalOpen(true); @@ -127,8 +119,6 @@ const AuthorizationRequestFlow = () => { useEffect(() => { if (!selectedCredentials.length) return; - - console.log(selectedCredentials); // TODO: // sendAuthorizationResponse().catch((e) => console.log(e)); }, [selectedCredentials]); diff --git a/packages/dapp/src/components/CampaignsDisplay/CampaignDisplay.tsx b/packages/dapp/src/components/CampaignsDisplay/CampaignDisplay.tsx index 1ba73ffef..5182f05b7 100644 --- a/packages/dapp/src/components/CampaignsDisplay/CampaignDisplay.tsx +++ b/packages/dapp/src/components/CampaignsDisplay/CampaignDisplay.tsx @@ -14,9 +14,11 @@ import { useSwitchChain, } from '@/hooks'; import { useAccount } from 'wagmi'; +import { RewardDisplay } from './RewardDisplay'; type CampaignProps = { campaign: Campaigns[number]; + alreadyClaimed: boolean; }; export const CampaignDisplay = ({ @@ -28,7 +30,9 @@ export const CampaignDisplay = ({ total, image_url: imageUrl, requirements, + rewards, }, + alreadyClaimed, }: CampaignProps) => { const t = useTranslations('CampaignDisplay'); @@ -122,6 +126,12 @@ export const CampaignDisplay = ({

{description}

+
+ {t('rewards')} +
+
+ +
{requirements.length > 0 && (
{t('requirements')} @@ -142,13 +152,27 @@ export const CampaignDisplay = ({
diff --git a/packages/dapp/src/components/CampaignsDisplay/RewardDisplay.tsx b/packages/dapp/src/components/CampaignsDisplay/RewardDisplay.tsx new file mode 100644 index 000000000..9559eaf33 --- /dev/null +++ b/packages/dapp/src/components/CampaignsDisplay/RewardDisplay.tsx @@ -0,0 +1,17 @@ +import { Chip } from '@nextui-org/react'; + +type RequirementProps = { + reward: string; +}; + +export const RewardDisplay = ({ reward }: RequirementProps) => { + return ( +
+ +

+ {reward} +

+
+
+ ); +}; diff --git a/packages/dapp/src/components/CampaignsDisplay/index.tsx b/packages/dapp/src/components/CampaignsDisplay/index.tsx index 30e3ebb69..509d59df4 100644 --- a/packages/dapp/src/components/CampaignsDisplay/index.tsx +++ b/packages/dapp/src/components/CampaignsDisplay/index.tsx @@ -5,10 +5,28 @@ import { CampaignDisplay } from './CampaignDisplay'; import { useCampaigns } from '@/hooks'; import { Spinner } from '@nextui-org/react'; import { useTranslations } from 'next-intl'; +import { useAuthStore } from '@/stores'; +import { shallow } from 'zustand/shallow'; +import { useCampaignClaims } from '@/hooks/useCampaignClaims'; export const CampaignsDisplay = () => { const t = useTranslations('CampaignsDisplay'); + const { data, status } = useCampaigns(); + const campaigns = data?.campaigns || []; + const { token } = useAuthStore( + (state) => ({ + token: state.token, + isSignedIn: state.isSignedIn, + changeIsSignInModalOpen: state.changeIsSignInModalOpen, + }), + shallow + ); + const { data: claimsData } = useCampaignClaims(token); + + if (campaigns.length === 0) { + return
{t('no-campaigns')}
; + } if (status === 'pending') { return ( @@ -18,16 +36,26 @@ export const CampaignsDisplay = () => { ); } - const campaigns = data?.campaigns || []; - - if (campaigns.length === 0) { - return
{t('no-campaigns')}
; - } - return ( -
+
+
+
+ {t('title')} +
+

{t('description')}

+
{campaigns.map((campaign) => ( - +
+ claim.campaign_id === campaign.id + ) + } + /> +
))}
); diff --git a/packages/dapp/src/components/ConnectedProvider/index.tsx b/packages/dapp/src/components/ConnectedProvider/index.tsx index 8c52f9738..3c97a8787 100644 --- a/packages/dapp/src/components/ConnectedProvider/index.tsx +++ b/packages/dapp/src/components/ConnectedProvider/index.tsx @@ -22,9 +22,9 @@ const ConnectedProvider = ({ children }: ConnectedProviderProps) => { return isConnected ? ( <>{children} ) : ( -
-
-
+
+
+

{t('connect')}

@@ -32,16 +32,16 @@ const ConnectedProvider = ({ children }: ConnectedProviderProps) => { {t('version')}
-
+
-
+
{t('masca')}
-
+
{t('masca-desc')}
@@ -49,14 +49,14 @@ const ConnectedProvider = ({ children }: ConnectedProviderProps) => {