From 450e02fbae3d9edbcaf097b2f80184b9e759af14 Mon Sep 17 00:00:00 2001 From: Mike Kiss Date: Fri, 10 May 2024 15:35:16 -0400 Subject: [PATCH] Add dummy dark theme, convert to useTheme hook --- packages/desktop/src/renderer/Root.tsx | 4 +- .../File/FileComponent/FileComponent.tsx | 5 +- .../UploadedImagePlaceholder.tsx | 5 +- .../ContextMenu/ContextMenu.component.tsx | 10 +- .../MathMessage/MathMessageComponent.tsx | 3 +- .../src/renderer/components/Titlebar.tsx | 4 +- .../ui/OpenlinkModal/OpenlinkModal.tsx | 6 +- .../widgets/channels/NestedMessageContent.tsx | 7 +- .../widgets/channels/TextMessage.tsx | 3 +- .../widgets/channels/WelcomeMessage.test.tsx | 2 +- .../PossibleImpersonationAttackModal.test.tsx | 2 +- .../update/UpdateModalComponent.stories.tsx | 2 +- .../widgets/userLabel/UserLabel.test.tsx | 2 +- .../duplicate/DuplicateModal.test.tsx | 2 +- .../unregistered/UnregisteredModal.test.tsx | 2 +- .../BreakingChangesWarning.tsx | 3 +- .../containers/widgets/update/UpdateModal.tsx | 3 +- .../src/renderer/storybook/decorators.tsx | 2 +- .../renderer/testUtils/renderComponent.tsx | 2 +- packages/desktop/src/renderer/theme.ts | 190 +++++++++++++++++- 20 files changed, 228 insertions(+), 31 deletions(-) diff --git a/packages/desktop/src/renderer/Root.tsx b/packages/desktop/src/renderer/Root.tsx index 32c931de55..b9bd2b9e9a 100644 --- a/packages/desktop/src/renderer/Root.tsx +++ b/packages/desktop/src/renderer/Root.tsx @@ -14,7 +14,7 @@ import SentryWarning from './containers/widgets/sentryWarning/sentryWarning' import SettingsModal from './components/Settings/Settings' import UpdateModal from './containers/widgets/update/UpdateModal' import QuitAppDialog from './containers/ui/QuitAppDialog' -import theme from './theme' +import { lightTheme, darkTheme } from './theme' import CreateCommunity from './components/CreateJoinCommunity/CreateCommunity/CreateCommunity' import JoinCommunity from './components/CreateJoinCommunity/JoinCommunity/JoinCommunity' import CreateChannel from './components/Channel/CreateChannel/CreateChannel' @@ -43,7 +43,7 @@ export const persistor = persistStore(store) export default () => { return ( - + diff --git a/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.tsx b/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.tsx index abb1818f39..d9c7c14015 100644 --- a/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.tsx +++ b/packages/desktop/src/renderer/components/Channel/File/FileComponent/FileComponent.tsx @@ -1,9 +1,8 @@ import React, { useState } from 'react' import { styled } from '@mui/material/styles' -import { CircularProgress, Typography } from '@mui/material' +import { CircularProgress, Typography, useTheme } from '@mui/material' import { DisplayableMessage, DownloadState, DownloadStatus, FileMetadata, CancelDownload } from '@quiet/types' import { formatBytes } from '@quiet/state-manager' -import theme from '../../../../theme' import Icon from '../../../ui/Icon/Icon' import fileIcon from '../../../../static/images/fileIcon.svg' import clockIconGray from '../../../../static/images/clockIconGray.svg' @@ -141,6 +140,8 @@ export const FileComponent: React.FC = ({ downloadFile, cancelDownload, }) => { + const theme = useTheme() + if (!message.media) return null const { cid, path, name, ext } = message.media diff --git a/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.tsx b/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.tsx index bbf25e41f2..ee0f633abe 100644 --- a/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.tsx +++ b/packages/desktop/src/renderer/components/Channel/File/UploadedImagePlaceholder/UploadedImagePlaceholder.tsx @@ -1,10 +1,9 @@ -import { CircularProgress } from '@mui/material' +import { CircularProgress, useTheme } from '@mui/material' import { styled } from '@mui/material/styles' import { DownloadStatus, DownloadState } from '@quiet/types' import { formatBytes } from '@quiet/state-manager' import React from 'react' import imageIcon from '../../../../static/images/imageIcon.svg' -import theme from '../../../../theme' import Icon from '../../../ui/Icon/Icon' import Tooltip from '../../../ui/Tooltip/Tooltip' @@ -78,6 +77,8 @@ export const UploadedImagePlaceholder: React.FC = ext, downloadStatus, }) => { + const theme = useTheme() + const width = imageWidth >= 400 ? 400 : imageWidth const downloadState = downloadStatus?.downloadState diff --git a/packages/desktop/src/renderer/components/ContextMenu/ContextMenu.component.tsx b/packages/desktop/src/renderer/components/ContextMenu/ContextMenu.component.tsx index 05aa046e87..b5e57a1567 100644 --- a/packages/desktop/src/renderer/components/ContextMenu/ContextMenu.component.tsx +++ b/packages/desktop/src/renderer/components/ContextMenu/ContextMenu.component.tsx @@ -1,5 +1,5 @@ import React, { FC, useEffect, useRef } from 'react' -import { Grid, List, Typography } from '@mui/material' +import { Grid, List, Typography, useTheme } from '@mui/material' import { ContextMenuProps, ContextMenuHintProps, @@ -10,9 +10,9 @@ import Icon from '../ui/Icon/Icon' import arrowLeft from '../../static/images/arrowLeft.svg' import arrowRightShort from '../../static/images/arrowRightShort.svg' -import theme from '../../theme' - export const ContextMenu: FC = ({ visible, handleClose, handleBack, title, children }) => { + const theme = useTheme() + const ref = useRef(null) useEffect(() => { @@ -97,6 +97,8 @@ export const ContextMenu: FC = ({ visible, handleClose, handle } export const ContextMenuHint: FC = ({ hint }) => { + const theme = useTheme() + return ( = ({ items }) => } export const ContextMenuItem: FC = ({ title, action }) => { + const theme = useTheme() + return ( ({ +const StyledMath = styled('span')(({ theme }) => ({ [`&.${classes.message}`]: { marginLeft: '20px', }, diff --git a/packages/desktop/src/renderer/components/Titlebar.tsx b/packages/desktop/src/renderer/components/Titlebar.tsx index da6b6caaf8..87ecc99007 100644 --- a/packages/desktop/src/renderer/components/Titlebar.tsx +++ b/packages/desktop/src/renderer/components/Titlebar.tsx @@ -1,5 +1,7 @@ +import { useTheme } from '@mui/material' import { Titlebar, Color } from 'custom-electron-titlebar' -import theme from '../theme' + +import { lightTheme as theme } from '../theme' export const addTitlebar = () => { setTimeout(() => { diff --git a/packages/desktop/src/renderer/components/ui/OpenlinkModal/OpenlinkModal.tsx b/packages/desktop/src/renderer/components/ui/OpenlinkModal/OpenlinkModal.tsx index ddc6f5c1d6..87477e86c1 100644 --- a/packages/desktop/src/renderer/components/ui/OpenlinkModal/OpenlinkModal.tsx +++ b/packages/desktop/src/renderer/components/ui/OpenlinkModal/OpenlinkModal.tsx @@ -1,6 +1,6 @@ import React from 'react' -import { styled } from '@mui/material/styles' +import { styled, useTheme } from '@mui/material/styles' import { AutoSizer } from 'react-virtualized' import { Scrollbars } from 'rc-scrollbars' @@ -16,8 +16,6 @@ import Icon from '../Icon/Icon' import exclamationMark from '../../../static/images/exclamationMark.svg' import Modal from '../Modal/Modal' -import theme from '../../../theme' - const PREFIX = 'OpenlinkModal' const classes = { @@ -104,6 +102,8 @@ export const OpenlinkModal: React.FC = ({ const [allowAllLink, setAllowAllLink] = React.useState(false) const [dontAutoload, setDontAutoload] = React.useState(false) + const theme = useTheme() + const uri = new URL(url) return ( diff --git a/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.tsx b/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.tsx index 0bc230cfad..2059da123e 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/NestedMessageContent.tsx @@ -1,8 +1,7 @@ import React from 'react' import { styled } from '@mui/material/styles' -import theme from '../../../theme' import classNames from 'classnames' -import { Grid } from '@mui/material' +import { Grid, useTheme } from '@mui/material' import { AUTODOWNLOAD_SIZE_LIMIT, DownloadState, DownloadStatus } from '@quiet/state-manager' import UploadedImage from '../../Channel/File/UploadedImage/UploadedImage' @@ -21,7 +20,7 @@ const classes = { info: `${PREFIX}info`, } -const StyledGrid = styled(Grid)(() => ({ +const StyledGrid = styled(Grid)(({ theme }) => ({ [`& .${classes.message}`]: { fontSize: '0.855rem', whiteSpace: 'pre-line', @@ -60,6 +59,8 @@ export const NestedMessageContent: React.FC { + const theme = useTheme() + const renderMessage = () => { const isMalicious = downloadStatus?.downloadState === DownloadState.Malicious diff --git a/packages/desktop/src/renderer/components/widgets/channels/TextMessage.tsx b/packages/desktop/src/renderer/components/widgets/channels/TextMessage.tsx index 51ad2dcb41..ba2e1eb124 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/TextMessage.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/TextMessage.tsx @@ -3,7 +3,6 @@ import classNames from 'classnames' import React, { ReactNode } from 'react' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' -import theme from '../../../theme' import { isAllEmoji } from '../../../../../../common/src/emojis' const PREFIX = 'TextMessage' @@ -24,7 +23,7 @@ const classes = { tableRowCell: `${PREFIX}tableRowCell`, } -const StyledTypography = styled(Typography)(() => ({ +const StyledTypography = styled(Typography)(({ theme }) => ({ [`&.${classes.message}`]: { fontSize: '0.855rem', whiteSpace: 'pre-line', diff --git a/packages/desktop/src/renderer/components/widgets/channels/WelcomeMessage.test.tsx b/packages/desktop/src/renderer/components/widgets/channels/WelcomeMessage.test.tsx index 70f9a847e3..ce3879a7f7 100644 --- a/packages/desktop/src/renderer/components/widgets/channels/WelcomeMessage.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/channels/WelcomeMessage.test.tsx @@ -1,5 +1,5 @@ import React from 'react' -import theme from '../../../theme' +import { lightTheme as theme } from '../../../theme' import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles' import { WelcomeMessage } from './WelcomeMessage' import { renderComponent } from '../../../testUtils/renderComponent' diff --git a/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.test.tsx b/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.test.tsx index e8677f7de2..794672545b 100644 --- a/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.test.tsx @@ -1,5 +1,5 @@ import React from 'react' -import theme from '../../../theme' +import { lightTheme as theme } from '../../../theme' import { ThemeProvider } from '@mui/material/styles' import { renderComponent } from '../../../testUtils/renderComponent' import PossibleImpersonationAttackModalComponent from './PossibleImpersonationAttackModal.component' diff --git a/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.stories.tsx b/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.stories.tsx index d8c17ce5b9..70ccaa13fc 100644 --- a/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.stories.tsx +++ b/packages/desktop/src/renderer/components/widgets/update/UpdateModalComponent.stories.tsx @@ -4,7 +4,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react' import UpdateModal, { UpdateModalProps } from './UpdateModalComponent' import { withTheme } from '../../../storybook/decorators' -import theme from '../../../theme' +import { lightTheme as theme } from '../../../theme' import Button from '@mui/material/Button' diff --git a/packages/desktop/src/renderer/components/widgets/userLabel/UserLabel.test.tsx b/packages/desktop/src/renderer/components/widgets/userLabel/UserLabel.test.tsx index 2f2768df8b..782975336a 100644 --- a/packages/desktop/src/renderer/components/widgets/userLabel/UserLabel.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/userLabel/UserLabel.test.tsx @@ -1,5 +1,5 @@ import React from 'react' -import theme from '../../../theme' +import { lightTheme as theme } from '../../../theme' import { ThemeProvider } from '@mui/material/styles' import { renderComponent } from '../../../testUtils/renderComponent' import UserLabel from './UserLabel.component' diff --git a/packages/desktop/src/renderer/components/widgets/userLabel/duplicate/DuplicateModal.test.tsx b/packages/desktop/src/renderer/components/widgets/userLabel/duplicate/DuplicateModal.test.tsx index 62e2f26f56..3ab642a415 100644 --- a/packages/desktop/src/renderer/components/widgets/userLabel/duplicate/DuplicateModal.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/userLabel/duplicate/DuplicateModal.test.tsx @@ -1,5 +1,5 @@ import React from 'react' -import theme from '../../../../theme' +import { lightTheme as theme } from '../../../../theme' import { ThemeProvider } from '@mui/material/styles' import { renderComponent } from '../../../../testUtils/renderComponent' import DuplicateModalComponent from './DuplicateModal.component' diff --git a/packages/desktop/src/renderer/components/widgets/userLabel/unregistered/UnregisteredModal.test.tsx b/packages/desktop/src/renderer/components/widgets/userLabel/unregistered/UnregisteredModal.test.tsx index f6d3b47b49..3aefe0205f 100644 --- a/packages/desktop/src/renderer/components/widgets/userLabel/unregistered/UnregisteredModal.test.tsx +++ b/packages/desktop/src/renderer/components/widgets/userLabel/unregistered/UnregisteredModal.test.tsx @@ -1,5 +1,5 @@ import React from 'react' -import theme from '../../../../theme' +import { lightTheme as theme } from '../../../../theme' import { ThemeProvider } from '@mui/material/styles' import { renderComponent } from '../../../../testUtils/renderComponent' import UnregisteredModalComponent from './UnregisteredModal.component' diff --git a/packages/desktop/src/renderer/containers/widgets/breakingChangesWarning/BreakingChangesWarning.tsx b/packages/desktop/src/renderer/containers/widgets/breakingChangesWarning/BreakingChangesWarning.tsx index 92bf605cfd..3fa2cb67cb 100644 --- a/packages/desktop/src/renderer/containers/widgets/breakingChangesWarning/BreakingChangesWarning.tsx +++ b/packages/desktop/src/renderer/containers/widgets/breakingChangesWarning/BreakingChangesWarning.tsx @@ -1,16 +1,17 @@ import React, { useCallback, useEffect } from 'react' +import { useTheme } from '@mui/material' import { ModalName } from '../../../sagas/modals/modals.types' import { useModal } from '../../hooks' import UpdateModalComponent from '../../../components/widgets/update/UpdateModalComponent' import Button from '@mui/material/Button' -import theme from '../../../theme' import { shell } from 'electron' import { Site } from '@quiet/common' const BreakingChangesWarning = () => { const modal = useModal(ModalName.breakingChangesWarning) + const theme = useTheme() const title = 'Update available' const message = diff --git a/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx b/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx index fd5d4897cb..11b86b13b2 100644 --- a/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx +++ b/packages/desktop/src/renderer/containers/widgets/update/UpdateModal.tsx @@ -9,7 +9,7 @@ import { ModalName } from '../../../sagas/modals/modals.types' import UpdateModalComponent from '../../../components/widgets/update/UpdateModalComponent' import Button from '@mui/material/Button' -import theme from '../../../theme' +import { useTheme } from '@mui/material' export const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators( @@ -22,6 +22,7 @@ export const mapDispatchToProps = (dispatch: Dispatch) => const ApplicationUpdateModal: React.FC = () => { const dispatch = useDispatch() + const theme = useTheme() const actions = mapDispatchToProps(dispatch) const modal = useModal(ModalName.applicationUpdate) diff --git a/packages/desktop/src/renderer/storybook/decorators.tsx b/packages/desktop/src/renderer/storybook/decorators.tsx index c6cfdd7bc4..b30558c4da 100644 --- a/packages/desktop/src/renderer/storybook/decorators.tsx +++ b/packages/desktop/src/renderer/storybook/decorators.tsx @@ -2,7 +2,7 @@ import React from 'react' import { Provider } from 'react-redux' import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles' -import theme from '../theme' +import { lightTheme as theme } from '../theme' import { Store } from '../sagas/store.types' export const withStore = (store: Store) => (Story: React.FC) => ( diff --git a/packages/desktop/src/renderer/testUtils/renderComponent.tsx b/packages/desktop/src/renderer/testUtils/renderComponent.tsx index b341fac9ff..c9293b3a50 100644 --- a/packages/desktop/src/renderer/testUtils/renderComponent.tsx +++ b/packages/desktop/src/renderer/testUtils/renderComponent.tsx @@ -4,7 +4,7 @@ import { Provider } from 'react-redux' import { render } from '@testing-library/react' -import theme from '../theme' +import { lightTheme as theme } from '../theme' import store from '../store' import { Store } from 'redux' import { DndProvider } from 'react-dnd' diff --git a/packages/desktop/src/renderer/theme.ts b/packages/desktop/src/renderer/theme.ts index 1bfb06938a..71bf92bb6d 100644 --- a/packages/desktop/src/renderer/theme.ts +++ b/packages/desktop/src/renderer/theme.ts @@ -3,7 +3,7 @@ import { createTheme } from '@mui/material/styles' const font = "'Rubik', sans-serif" const fontLogs = 'Menlo Regular' -export default createTheme({ +const lightTheme = createTheme({ typography: { fontFamily: [font, fontLogs].join(','), fontStyle: 'normal', @@ -188,3 +188,191 @@ export default createTheme({ }, }, }) + +const darkTheme = createTheme({ + typography: { + fontFamily: [font, fontLogs].join(','), + fontStyle: 'normal', + fontWeight: 'normal', + useNextVariants: true, + overline: { + fontSize: 10, + lineHeight: '16px', + fontWeight: 500, + }, + caption: { + fontSize: 12, + lineHeight: '20px', + color: '#b2b2b2', + }, + body1: { + fontSize: 16, + lineHeight: '26px', + }, + body2: { + fontSize: 14, + lineHeight: '24px', + }, + subtitle1: { + fontSize: 16, + lineHeight: '26px', + }, + subtitle2: { + fontSize: 14, + lineHeight: '23px', + }, + h1: { + fontWeight: 500, + fontSize: 48, + lineHeight: '40px', + }, + h2: { + fontWeight: 500, + fontSize: 34, + lineHeight: '40px', + }, + h3: { + fontWeight: 500, + fontSize: 28, + lineHeight: '34px', + }, + h4: { + fontWeight: 500, + fontSize: 18, + lineHeight: '27px', + }, + h5: { + fontSize: 16, + lineHeight: '26px', + fontWeight: 500, + }, + }, + palette: { + background: { + // Background colors (white-ish for light theme, black-ish for dark) + default: '#ffffff', + paper: '#F0F0F0', + }, + // text: {}, // font colors (black-ish for light theme, white-ish for dark) + primary: { + // Primary Quiet brand purple + main: '#521C74', + dark: '#461863', + }, + secondary: { + // Secondary Quiet brand red (TODO: Make sure this is Secondary, not error/warning) + main: '#E42656', + dark: '#C41743', + }, + error: { + main: '#D13135', + }, + warning: { + main: '#FFCC00', + }, + // TO BE ADDED IF NEEDED: Success, Warning, Neutral + colors: { + // Misc colors. For primary / secondary brand, text, and background colors, use those objects + // For canonical colors, see: https://www.figma.com/file/0j7Nna9zWmfOSNmRmQK1Uh/Quiet-Design-Library?type=design&node-id=2667-0&mode=design&t=i0cXovHohRKxWGaA-0 + // Blues + blue: '#2196f3', + purple: '#521C74', // To be replaced with theme.palette.primary.main + quietBlue: '#521c74', // To be replaced with theme.palette.primary.main + darkPurple: '#4d1a6d', // To be replaced with theme.palette.primary.dark? + lushSky: '#67BFD3', + lushSky12: '#EDF7FA', + linkBlue: '#59c0d5', // Used in a variety of places - likely wants to be split / consolidated + // Reds + red: '#FF0000', // Replace with D13135 ? + hotRed: '#E42656', // Replaced by theme.palette.secondary.main? + hotPink: '#E42656', // Replaced by theme.palette.secondary.main? + // Grays (including white and black) + white: '#FFFFFF', + trueBlack: '#000000', // To be replaced with text color and border color + gray: '#e7e7e7', + darkGray: '#7F7F7F', + mediumGray: '#8d8d8d', + lightGray: '#B2B2B2', // To be replaced with gray30? + gray03: '#F7F7F7', + gray30: '#FAFAFA', // Unused and not aligned with Figma + gray40: '#999999', + gray50: '#7F7F7F', + gray70: '#4C4C4C', + // Border colors + border01: '#F0F0F0', + border02: '#B3B3B3', + border03: '#D2D2D2', + }, + }, + //@ts-ignore MUI types expect 25 shadows - see: https://github.com/mui/material-ui/issues/28820 + shadows: [ + 'none', + '0px 0px 4px rgba(0, 0, 0, 0.25)', + '0px 1px 0px #F0F0F0', + '0px 1px 3px rgba(0, 0, 0, 0.0)', + '0px 2px 25px rgba(0, 0, 0, 0.2)', + ], + components: { + // Body font size changed in mui v5: https://mui.com/material-ui/migration/v5-component-changes/#update-body-font-size + MuiCssBaseline: { + styleOverrides: { + body: { + fontSize: '14px', + lineHeight: '24px', + letterSpacing: '0.01071em', + }, + }, + }, + MuiSnackbarContent: { + // Replace with atomic Snackbar component. Put styling in that file. + styleOverrides: { + root: { + wordBreak: 'break-all', + }, + }, + }, + MuiButton: { + // Replace with atomic Button component. Put styling in that file. + styleOverrides: { + sizeSmall: { + textTransform: 'none', + boxShadow: 'none', + paddingLeft: '16px', + paddingRight: '14px', + fontWeight: 400, + fontSize: '14px', + '&:active': { + boxShadow: 'none', + }, + }, + sizeLarge: { + textTransform: 'none', + boxShadow: 'none', + fontWeight: 400, + paddingTop: 12, + paddingBottom: 12, + fontSize: 14, + '&:active': { + boxShadow: 'none', + }, + }, + }, + }, + MuiOutlinedInput: { + // Replace with atomic Input component. Put styling in that file. + styleOverrides: { + input: {}, + }, + }, + MuiPopover: { + // Replace with atomic Popover component. Put styling in that file. + styleOverrides: { + paper: { + borderRadius: 8, + }, + }, + }, + }, +}) + +export { lightTheme, darkTheme }