Skip to content

Commit

Permalink
feat: upgrade to nextjs 14 and react 18 (#430)
Browse files Browse the repository at this point in the history
  • Loading branch information
pyphilia authored Dec 22, 2023
1 parent 95d91cc commit 6fe3343
Show file tree
Hide file tree
Showing 47 changed files with 940 additions and 806 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ concurrency:

# Set environment variables that are available to the steps of all jobs in the workflow
# env:
# Allows to increase Node's max heap size
# NODE_OPTIONS: '--max_old_space_size=8192'
# Allows to increase Node's max heap size
# NODE_OPTIONS: '--max_old_space_size=8192'

jobs:

cypress:
name: Cypress
# The type of runner that the job will run on
Expand Down Expand Up @@ -53,7 +52,7 @@ jobs:
NEXT_PUBLIC_API_HOST: http://localhost:3000
NEXT_PUBLIC_GOOGLE_ANALYTICS_ID: G-H48F1X7SWK
NEXT_PUBLIC_GRAASP_ANALYTICS_HOST: http://localhost:3003
NEXT_PUBLIC_GRAASP_AUTH_HOST: http://localhost:3001
NEXT_PUBLIC_GRAASP_AUTH_HOST: http://localhost:3001
NEXT_PUBLIC_GRAASP_PERFORM_HOST: http://localhost:3112
NEXT_PUBLIC_GRAASPER_ID: 12345678-dead-beef-cafe-123456789abc
NEXT_PUBLIC_NODE_ENV: test
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
graasp-deploy-ecs-workflow:
name: Graasp Library
# Reference reusable workflow file. Using the commit SHA is the safest for stability and security
uses: graasp/graasp-deploy/.github/workflows/library-deploy-dev.yml@v1
uses: graasp/graasp-deploy/.github/workflows/library-deploy-dev.yml@library-env-build
# abort previous deployment if a newer one is in progress
concurrency:
group: deploy-development
Expand All @@ -43,8 +43,6 @@ jobs:
next-public-graasp-perform-host: ${{ secrets.PLAYER_CLIENT_HOST_DEV }}
next-public-graasper-id: ${{ secrets.NEXT_PUBLIC_GRAASPER_ID }}
next-public-show-notifications: ${{ secrets.NEXT_PUBLIC_SHOW_NOTIFICATIONS }}
# to be removed ! we do not use it anymore
next-public-published-tag-id: ${{ secrets.NEXT_PUBLIC_PUBLISHED_TAG_ID }}
next-public-sentry-dsn: ${{ secrets.SENTRY_DSN }}
next-public-sentry-env: development
port: ${{ secrets.PORT }}
2 changes: 0 additions & 2 deletions .github/workflows/deploy-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ jobs:
next-public-graasp-perform-host: ${{ secrets.PLAYER_CLIENT_HOST_PROD }}
next-public-graasper-id: ${{ secrets.NEXT_PUBLIC_GRAASPER_ID }}
next-public-show-notifications: ${{ secrets.NEXT_PUBLIC_SHOW_NOTIFICATIONS }}
# to be removed ! we do not use it anymore
next-public-published-tag-id: ${{ secrets.NEXT_PUBLIC_PUBLISHED_TAG_ID }}
next-public-sentry-dsn: ${{ secrets.SENTRY_DSN }}
next-public-sentry-env: production
port: ${{ secrets.PORT }}
2 changes: 0 additions & 2 deletions .github/workflows/deploy-stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ jobs:
next-public-graasp-perform-host: ${{ secrets.PLAYER_CLIENT_HOST_STAGE }}
next-public-graasper-id: ${{ secrets.NEXT_PUBLIC_GRAASPER_ID }}
next-public-show-notifications: ${{ secrets.NEXT_PUBLIC_SHOW_NOTIFICATIONS }}
# to be removed ! we do not use it anymore
next-public-published-tag-id: ${{ secrets.NEXT_PUBLIC_PUBLISHED_TAG_ID }}
next-public-sentry-dsn: ${{ secrets.SENTRY_DSN }}
next-public-sentry-env: staging
port: ${{ secrets.PORT }}
2 changes: 1 addition & 1 deletion .github/workflows/sentry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
sentry-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0

Expand Down
18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,31 @@ FROM node:18-alpine
# install git, necessary for github dependencies
RUN apk add --no-cache git

ARG NEXT_PUBLIC_API_HOST=${NEXT_PUBLIC_API_HOST}
ARG NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=${NEXT_PUBLIC_GOOGLE_ANALYTICS_ID}
ARG NEXT_PUBLIC_GRAASP_ANALYTICS_HOST=${NEXT_PUBLIC_GRAASP_ANALYTICS_HOST}
ARG NEXT_PUBLIC_GRAASP_AUTH_HOST=${NEXT_PUBLIC_GRAASP_AUTH_HOST}
ARG NEXT_PUBLIC_GRAASP_ACCOUNT_HOST=${NEXT_PUBLIC_GRAASP_ACCOUNT_HOST}
ARG NEXT_PUBLIC_GRAASP_BUILDER_HOST=${NEXT_PUBLIC_GRAASP_BUILDER_HOST}
ARG NEXT_PUBLIC_GRAASP_PERFORM_HOST=${NEXT_PUBLIC_GRAASP_PERFORM_HOST}
ARG NEXT_PUBLIC_GRAASPER_ID=${NEXT_PUBLIC_GRAASPER_ID}
ARG NEXT_PUBLIC_GA_MEASUREMENT_ID=${NEXT_PUBLIC_GA_MEASUREMENT_ID}
ARG NEXT_PUBLIC_SENTRY_DSN=${NEXT_PUBLIC_SENTRY_DSN}
ARG NEXT_PUBLIC_SENTRY_ENV=${NEXT_PUBLIC_SENTRY_ENV}
ARG NEXT_PUBLIC_DOMAIN=${NEXT_PUBLIC_DOMAIN}
ARG NEXT_PUBLIC_SHOW_NOTIFICATIONS=${NEXT_PUBLIC_SHOW_NOTIFICATIONS}
ARG NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME}
ARG NEXT_PUBLIC_APP_VERSION=${NEXT_PUBLIC_APP_VERSION}

WORKDIR /usr/src/app
RUN mkdir -p .

# install app dependencies with yarn 3
COPY package.json .
COPY .yarnrc.yml .
COPY .yarn .yarn


RUN yarn install

# bundle app source
Expand Down
82 changes: 82 additions & 0 deletions app/ThemeRegistry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use client';

import createCache from '@emotion/cache';
import { CacheProvider } from '@emotion/react';
import { useServerInsertedHTML } from 'next/navigation';

import { ReactNode, useState } from 'react';

import CssBaseline from '@mui/material/CssBaseline';
import { ThemeProvider } from '@mui/material/styles';

import { theme } from '../src/config/theme';

type Props = {
options: any;
children: ReactNode;
};

// This implementation is from emotion-js
// https://github.com/emotion-js/emotion/issues/2928#issuecomment-1319747902
// eslint-disable-next-line react/function-component-definition
export default function ThemeRegistry(props: Props) {
const { options, children } = props;

// Remove the server-side injected CSS.
// useEffect(() => {
// const jssStyles = document.querySelector('#jss-server-side');
// if (jssStyles && jssStyles.parentElement) {
// jssStyles.parentElement.removeChild(jssStyles);
// }
// }, []);

const [{ cache, flush }] = useState(() => {
const newCache = createCache(options);
newCache.compat = true;
const prevInsert = newCache.insert;
let inserted: string[] = [];
newCache.insert = (...args) => {
const serialized = args[1];
if (newCache.inserted[serialized.name] === undefined) {
inserted.push(serialized.name);
}
return prevInsert(...args);
};
const newFlush = () => {
const prevInserted = inserted;
inserted = [];
return prevInserted;
};
return { cache: newCache, flush: newFlush };
});

useServerInsertedHTML(() => {
const names = flush();
if (names.length === 0) {
return null;
}
let styles = '';
// eslint-disable-next-line no-restricted-syntax
for (const name of names) {
styles += cache.inserted[name];
}
return (
<style
key={cache.key}
data-emotion={`${cache.key} ${names.join(' ')}`}
dangerouslySetInnerHTML={{
__html: styles,
}}
/>
);
});

return (
<CacheProvider value={cache}>
<ThemeProvider theme={theme}>
<CssBaseline />
{children}
</ThemeProvider>
</CacheProvider>
);
}
51 changes: 51 additions & 0 deletions app/all-collections/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Head from 'next/head';

import { dehydrate } from 'react-query/core';

import Hydrate from '../../src/components/HydrateClient';
import Wrapper from '../../src/components/common/Wrapper';
import AllCollections from '../../src/components/pages/AllCollections';
import { APP_AUTHOR } from '../../src/config/constants';
import getQueryClient from '../../src/config/get-query-client';
import LIBRARY from '../../src/langs/constants';
import en from '../../src/langs/en.json';
import { buildSeo } from '../seo';

export async function generateMetadata() {
// todo: get lang from location and crawler
// question: how to get language from
// @ts-ignore
const t = (s: string): string => en[s];

return buildSeo({
title: t(LIBRARY.GRAASP_LIBRARY),
description: t(LIBRARY.GRAASP_LIBRARY_DESCRIPTION),
author: APP_AUTHOR,
});
}

const Page = async () => {
const queryClient = getQueryClient();
// await queryClient.prefetchQuery(['items', 'collections', 'all'], () =>
// Api.getAllPublishedItems({}, { API_HOST: GRAASP_API_HOST, axios }),
// );
const dehydratedState = dehydrate(queryClient);

return (
<>
<Head>
<title>Graasp Library</title>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width"
/>
</Head>
<Hydrate state={dehydratedState}>
<Wrapper dehydratedState={dehydratedState}>
<AllCollections />
</Wrapper>
</Hydrate>
</>
);
};
export default Page;
20 changes: 20 additions & 0 deletions app/api/coverage/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const dynamic = 'force-dynamic'; // defaults to force-static

export async function GET() {
return new Response(
JSON.stringify({
// eslint-disable-next-line no-underscore-dangle
// @ts-ignore
coverage: global.__coverage__ || null,
}),
{
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Content-Type': 'application/json',
},
},
);
}
13 changes: 13 additions & 0 deletions app/api/status/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const dynamic = 'force-dynamic'; // defaults to force-static

export async function GET() {
return new Response(JSON.stringify({ status: 'ok' }), {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Content-Type': 'application/json',
},
});
}
74 changes: 74 additions & 0 deletions app/collections/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// import { useRouter } from 'next/navigation';
// import { useEffect } from 'react';
// import ReactGA from 'react-ga4';
// import { hasAcceptedCookies } from '@graasp/sdk';
// import { ENV, UrlSearch } from '../src/config/constants';
// import createEmotionCache from '../src/config/createEmotionCache';
// import { GA_MEASUREMENT_ID, NODE_ENV } from '../src/config/env';
// Client-side cache, shared for the whole session of the user in the browser.
// const clientSideEmotionCache = createEmotionCache();
// import { Api, DATA_KEYS, configureQueryClient } from '@graasp/query-client';
import Head from 'next/head';

import { dehydrate } from 'react-query/core';

import Hydrate from '../../../src/components/HydrateClient';
import Collection from '../../../src/components/collection/Collection';
import Wrapper from '../../../src/components/common/Wrapper';
import { APP_AUTHOR } from '../../../src/config/constants';
import getQueryClient from '../../../src/config/get-query-client';
import LIBRARY from '../../../src/langs/constants';
import en from '../../../src/langs/en.json';
import { buildSeo } from '../../seo';

export async function generateMetadata() {
// TODO: get id from params

// const name = collection?.name || '';
// const parsedDescription = collection?.description || '';
// const author = collection?.creator?.name || '';
// // todo: handle image
// const imageUrl = DEFAULT_ITEM_IMAGE_PATH;

// todo: get lang from location and crawler
// question: how to get language from
// @ts-ignore
const t = (s: string): string => en[s];

return buildSeo({
title: t(LIBRARY.GRAASP_LIBRARY),
description: t(LIBRARY.GRAASP_LIBRARY_DESCRIPTION),
author: APP_AUTHOR, // todo: use item creator?
});
}

const Page = async ({ params: { id } }: { params: { id: string } }) => {
const queryClient = getQueryClient();
const dehydratedState = dehydrate(queryClient);

// if (id) {
// // prefetch data in query client

// await queryClient.prefetchQuery(DATA_KEYS.buildItemKey(id), () =>
// Api.getItem(id, { ...QUERY_CLIENT_OPTIONS, axios }),
// );
// }

return (
<>
<Head>
<title>Graasp Library</title>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width"
/>
</Head>
<Hydrate state={dehydratedState}>
<Wrapper dehydratedState={dehydratedState}>
<Collection id={id} />
</Wrapper>
</Hydrate>
</>
);
};
export default Page;
19 changes: 19 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'react-toastify/dist/ReactToastify.css';

import ThemeRegistry from './ThemeRegistry';
import Providers from './providers';

const RootLayout = ({ children }: { children: React.ReactNode }) => {
return (
// TODO: change lang
<html lang="en">
<body>
<Providers>
<ThemeRegistry options={{ key: 'mui' }}>{children}</ThemeRegistry>
</Providers>
</body>
</html>
);
};

export default RootLayout;
Loading

0 comments on commit 6fe3343

Please sign in to comment.