setup emotion

This commit is contained in:
Abhinav 2022-06-13 19:02:33 +05:30
parent 91709f8541
commit a027ea721c
6 changed files with 243 additions and 103 deletions

View file

@ -13,7 +13,9 @@
"prepare": "husky install"
},
"dependencies": {
"@emotion/cache": "^11.9.3",
"@emotion/react": "^11.9.3",
"@emotion/server": "^11.4.0",
"@emotion/styled": "^11.9.3",
"@ente-io/next-with-workbox": "^1.0.3",
"@mui/icons-material": "^5.6.2",

View file

@ -0,0 +1,7 @@
import createCache from '@emotion/cache';
// prepend: true moves MUI styles to the top of the <head> so they're loaded first.
// It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
export default function createEmotionCache() {
return createCache({ key: 'css', prepend: true });
}

View file

@ -1,5 +1,4 @@
import React, { createContext, useEffect, useRef, useState } from 'react';
import styled, { ThemeProvider as SThemeProvider } from 'styled-components';
import AppNavbar from 'components/Navbar/app';
import constants from 'utils/strings/constants';
import { useRouter } from 'next/router';
@ -17,19 +16,21 @@ import Head from 'next/head';
import { logUploadInfo } from 'utils/upload';
import LoadingBar from 'react-top-loading-bar';
import DialogBox from 'components/DialogBox';
import { ThemeProvider as MThemeProvider } from '@mui/material/styles';
import { styled, ThemeProvider } from '@mui/material/styles';
import { CacheProvider, EmotionCache } from '@emotion/react';
import createEmotionCache from '../createEmotionCache';
import darkThemeOptions from 'themes/darkThemeOptions';
import { CssBaseline } from '@mui/material';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import * as types from 'styled-components/cssprop'; // need to css prop on styled component
import { SetDialogBoxAttributes, DialogBoxAttributes } from 'types/dialogBox';
import {
getFamilyPortalRedirectURL,
getRoadmapRedirectURL,
} from 'services/userService';
import { CustomError } from 'utils/error';
import PropTypes from 'prop-types';
import { AppProps } from 'next/app';
export const MessageContainer = styled.div`
export const MessageContainer = styled('div')`
background-color: #111;
padding: 0;
font-size: 14px;
@ -72,7 +73,19 @@ const redirectMap = new Map([
['families', getFamilyPortalRedirectURL],
]);
export default function App({ Component, err }) {
// Client-side cache, shared for the whole session of the user in the browser.
const clientSideEmotionCache = createEmotionCache();
interface MyAppProps extends AppProps {
emotionCache?: EmotionCache;
}
export default function MyApp(props: MyAppProps) {
const {
Component,
emotionCache = clientSideEmotionCache,
pageProps,
} = props;
const router = useRouter();
const [loading, setLoading] = useState(false);
const [offline, setOffline] = useState(
@ -223,7 +236,7 @@ export default function App({ Component, err }) {
const closeMessageDialog = () => setMessageDialogView(false);
return (
<>
<CacheProvider value={emotionCache}>
<Head>
<title>{constants.TITLE}</title>
<meta
@ -232,66 +245,68 @@ export default function App({ Component, err }) {
/>
</Head>
<MThemeProvider theme={darkThemeOptions}>
<SThemeProvider theme={darkThemeOptions}>
<CssBaseline />
{showNavbar && <AppNavbar />}
<MessageContainer>
{offline && constants.OFFLINE_MSG}
</MessageContainer>
{sharedFiles &&
(router.pathname === '/gallery' ? (
<MessageContainer>
{constants.FILES_TO_BE_UPLOADED(
sharedFiles.length
)}
</MessageContainer>
) : (
<MessageContainer>
{constants.LOGIN_TO_UPLOAD_FILES(
sharedFiles.length
)}
</MessageContainer>
))}
{flashMessage && (
<FlashMessageBar
flashMessage={flashMessage}
onClose={() => setFlashMessage(null)}
/>
)}
<LoadingBar color="#51cd7c" ref={loadingBar} />
<DialogBox
open={messageDialogView}
onClose={closeMessageDialog}
attributes={dialogMessage}
<ThemeProvider theme={darkThemeOptions}>
<CssBaseline />
{showNavbar && <AppNavbar />}
<MessageContainer>
{offline && constants.OFFLINE_MSG}
</MessageContainer>
{sharedFiles &&
(router.pathname === '/gallery' ? (
<MessageContainer>
{constants.FILES_TO_BE_UPLOADED(sharedFiles.length)}
</MessageContainer>
) : (
<MessageContainer>
{constants.LOGIN_TO_UPLOAD_FILES(
sharedFiles.length
)}
</MessageContainer>
))}
{flashMessage && (
<FlashMessageBar
flashMessage={flashMessage}
onClose={() => setFlashMessage(null)}
/>
)}
<LoadingBar color="#51cd7c" ref={loadingBar} />
<AppContext.Provider
value={{
showNavBar,
sharedFiles,
resetSharedFiles,
setDisappearingFlashMessage,
redirectURL,
setRedirectURL,
startLoading,
finishLoading,
closeMessageDialog,
setDialogMessage,
}}>
{loading ? (
<VerticallyCentered>
<EnteSpinner>
<span className="sr-only">Loading...</span>
</EnteSpinner>
</VerticallyCentered>
) : (
<Component err={err} setLoading={setLoading} />
)}
</AppContext.Provider>
</SThemeProvider>
</MThemeProvider>
</>
<DialogBox
open={messageDialogView}
onClose={closeMessageDialog}
attributes={dialogMessage}
/>
<AppContext.Provider
value={{
showNavBar,
sharedFiles,
resetSharedFiles,
setDisappearingFlashMessage,
redirectURL,
setRedirectURL,
startLoading,
finishLoading,
closeMessageDialog,
setDialogMessage,
}}>
{loading ? (
<VerticallyCentered>
<EnteSpinner>
<span className="sr-only">Loading...</span>
</EnteSpinner>
</VerticallyCentered>
) : (
<Component {...pageProps} />
)}
</AppContext.Provider>
</ThemeProvider>
</CacheProvider>
);
}
MyApp.propTypes = {
Component: PropTypes.elementType.isRequired,
emotionCache: PropTypes.object,
pageProps: PropTypes.object.isRequired,
};

View file

@ -1,38 +1,19 @@
import React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
import createEmotionServer from '@emotion/server/create-instance';
import theme from '../themes/darkThemeOptions';
import createEmotionCache from '../createEmotionCache';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
render() {
return (
<Html lang="en">
<Head>
<meta
name="theme-color"
content={theme.palette.primary.main}
/>
<meta
name="description"
content="ente is a privacy focussed photo storage service that offers end-to-end encryption."
@ -55,6 +36,8 @@ export default class MyDocument extends Document {
name="apple-mobile-web-app-status-bar-style"
content="black"
/>
{/* Inject MUI styles first to match with the prepend: true configuration. */}
{(this.props as any).emotionStyleTags}
</Head>
<body>
<Main />
@ -64,3 +47,62 @@ export default class MyDocument extends Document {
);
}
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with static-site generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
// Resolution order
//
// On the server:
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. document.getInitialProps
// 4. app.render
// 5. page.render
// 6. document.render
//
// On the server with error:
// 1. document.getInitialProps
// 2. app.render
// 3. page.render
// 4. document.render
//
// On the client
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. app.render
// 4. page.render
const originalRenderPage = ctx.renderPage;
// You can consider sharing the same Emotion cache between all the SSR requests to speed up performance.
// However, be aware that it can have global side effects.
const cache = createEmotionCache();
const { extractCriticalToChunks } = createEmotionServer(cache);
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App: any) =>
function EnhanceApp(props) {
return <App emotionCache={cache} {...props} />;
},
});
const initialProps = await Document.getInitialProps(ctx);
// This is important. It prevents Emotion to render invalid HTML.
// See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153
const emotionStyles = extractCriticalToChunks(initialProps.html);
const emotionStyleTags = emotionStyles.styles.map((style) => (
<style
data-emotion={`${style.key} ${style.ids.join(' ')}`}
key={style.key}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }}
/>
));
return {
...initialProps,
emotionStyleTags,
};
};

View file

@ -1,7 +0,0 @@
// styled.d.ts
import 'styled-components';
import { Theme } from '@mui/material';
declare module 'styled-components' {
export interface DefaultTheme extends Theme {}
}

View file

@ -1035,6 +1035,16 @@
"@emotion/utils" "^1.0.0"
csstype "^3.0.2"
"@emotion/server@^11.4.0":
version "11.4.0"
resolved "https://registry.yarnpkg.com/@emotion/server/-/server-11.4.0.tgz#3ae1d74cb31c7d013c3c76e88c0c4439076e9f66"
integrity sha512-IHovdWA3V0DokzxLtUNDx4+hQI82zUXqQFcVz/om2t44O0YSc+NHB+qifnyAOoQwt3SXcBTgaSntobwUI9gnfA==
dependencies:
"@emotion/utils" "^1.0.0"
html-tokenize "^2.0.0"
multipipe "^1.0.2"
through "^2.3.8"
"@emotion/sheet@^1.0.0", "@emotion/sheet@^1.0.2":
version "1.0.2"
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.0.2.tgz"
@ -2232,6 +2242,11 @@ buffer-from@^1.0.0:
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
buffer-from@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-0.1.2.tgz#15f4b9bcef012044df31142c14333caf6e0260d0"
integrity sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==
builtin-modules@^3.1.0:
version "3.2.0"
resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz"
@ -2655,6 +2670,13 @@ dom-helpers@^5.0.1, dom-helpers@^5.2.0, dom-helpers@^5.2.1:
"@babel/runtime" "^7.8.7"
csstype "^3.0.2"
duplexer2@^0.1.2:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==
dependencies:
readable-stream "^2.0.2"
duplexer@^0.1.1:
version "0.1.2"
resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz"
@ -3439,6 +3461,17 @@ hosted-git-info@^2.1.4:
resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
html-tokenize@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/html-tokenize/-/html-tokenize-2.0.1.tgz#c3b2ea6e2837d4f8c06693393e9d2a12c960be5f"
integrity sha512-QY6S+hZ0f5m1WT8WffYN+Hg+xm/w5I8XeUcAq/ZYP5wVC8xbKi4Whhru3FtrAebD5EhBW8rmFzkDI6eCAuFe2w==
dependencies:
buffer-from "~0.1.1"
inherits "~2.0.1"
minimist "~1.2.5"
readable-stream "~1.0.27-1"
through2 "~0.4.1"
http-errors@1.7.2:
version "1.7.2"
resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz"
@ -3557,7 +3590,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -3780,6 +3813,11 @@ is-weakref@^1.0.1:
dependencies:
call-bind "^1.0.2"
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
@ -4174,7 +4212,7 @@ minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
minimist@^1.2.0, minimist@^1.2.5:
minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
@ -4201,6 +4239,14 @@ ms@2.1.2, ms@^2.1.1:
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
multipipe@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-1.0.2.tgz#cc13efd833c9cda99f224f868461b8e1a3fd939d"
integrity sha512-6uiC9OvY71vzSGX8lZvSqscE7ft9nPupJ8fMjrCNRAUy2LREUW42UL+V/NTrogr6rFgRydUrCX4ZitfpSNkSCQ==
dependencies:
duplexer2 "^0.1.2"
object-assign "^4.1.0"
nanoid@^3.1.30:
version "3.3.1"
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz"
@ -4316,6 +4362,11 @@ object-keys@^1.0.12, object-keys@^1.1.1:
resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object-keys@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336"
integrity sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==
object.assign@^4.1.0, object.assign@^4.1.2:
version "4.1.2"
resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz"
@ -4900,7 +4951,7 @@ read-pkg@^3.0.0:
normalize-package-data "^2.3.2"
path-type "^3.0.0"
readable-stream@^2.0.6, readable-stream@~2.3.6:
readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@ -4922,6 +4973,16 @@ readable-stream@^3.6.0:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-stream@~1.0.17, readable-stream@~1.0.27-1:
version "1.0.34"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
integrity sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.1"
isarray "0.0.1"
string_decoder "~0.10.x"
readable-web-to-node-stream@^3.0.0:
version "3.0.2"
resolved "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz"
@ -5434,6 +5495,11 @@ string_decoder@^1.1.1:
dependencies:
safe-buffer "~5.2.0"
string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
@ -5593,6 +5659,14 @@ text-table@^0.2.0:
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
through2@~0.4.1:
version "0.4.2"
resolved "https://registry.yarnpkg.com/through2/-/through2-0.4.2.tgz#dbf5866031151ec8352bb6c4db64a2292a840b9b"
integrity sha512-45Llu+EwHKtAZYTPPVn3XZHBgakWMN3rokhEv5hu596XP+cNgplMg+Gj+1nmAvj+L0K7+N49zBKx5rah5u0QIQ==
dependencies:
readable-stream "~1.0.17"
xtend "~2.1.1"
through@^2.3.8:
version "2.3.8"
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
@ -6142,6 +6216,13 @@ xml-js@^1.6.11:
dependencies:
sax "^1.2.4"
xtend@~2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b"
integrity sha512-vMNKzr2rHP9Dp/e1NQFnLQlwlhp9L/LfvnsVdHxN1f+uggyVI3i08uD14GPvCToPkdsRfyPqIyYGmIk58V98ZQ==
dependencies:
object-keys "~0.4.0"
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz"