diff --git a/packages/desktop-client/src/components/FatalError.js b/packages/desktop-client/src/components/FatalError.js index d111e8ffae5c88224c90b364d4c7ca9dae270405..2f40dca257e8dee9b7b29fb68f18fc141d30b2ef 100644 --- a/packages/desktop-client/src/components/FatalError.js +++ b/packages/desktop-client/src/components/FatalError.js @@ -8,11 +8,11 @@ import { Text, Block, Modal, - P, LinkButton, Button, ExternalLink, } from './common'; +import Paragraph from './common/Paragraph'; import { Checkbox } from './forms'; class FatalError extends Component { @@ -112,20 +112,22 @@ class FatalError extends Component { <Modal isCurrent={true} showClose={false} title="Fatal Error"> {() => ( <View style={{ maxWidth: 500 }}> - <P>There was an unrecoverable error in the UI. Sorry!</P> - <P> + <Paragraph> + There was an unrecoverable error in the UI. Sorry! + </Paragraph> + <Paragraph> If this error persists, please get{' '} <ExternalLink to="https://actualbudget.org/contact"> in touch </ExternalLink>{' '} so it can be investigated. - </P> - <P> + </Paragraph> + <Paragraph> <Button onClick={() => window.Actual.relaunch()}> {buttonText} </Button> - </P> - <P isLast={true} style={{ fontSize: 11 }}> + </Paragraph> + <Paragraph isLast={true} style={{ fontSize: 11 }}> <LinkButton onClick={() => this.setState({ showError: true })}> Show Error </LinkButton> @@ -140,7 +142,7 @@ class FatalError extends Component { {error.stack} </Block> )} - </P> + </Paragraph> </View> )} </Modal> @@ -155,13 +157,13 @@ function SharedArrayBufferOverride() { return expanded ? ( <> - <P style={{ marginTop: 10 }}> + <Paragraph style={{ marginTop: 10 }}> Actual uses <code>SharedArrayBuffer</code> to allow usage from multiple tabs at once and to ensure correct behavior when switching files. While it can run without access to <code>SharedArrayBuffer</code>, you may encounter data loss or notice multiple budget files being merged with each other. - </P> + </Paragraph> <label style={{ display: 'flex', alignItems: 'center', marginBottom: 10 }} > diff --git a/packages/desktop-client/src/components/Titlebar.js b/packages/desktop-client/src/components/Titlebar.js index 6b825577bdb2c61e13c11b9ac22c1406d45a8054..c0d61d3cdc9ce039328d1d630767c61c450409b5 100644 --- a/packages/desktop-client/src/components/Titlebar.js +++ b/packages/desktop-client/src/components/Titlebar.js @@ -35,9 +35,9 @@ import { Button, ButtonWithLoading, Tooltip, - P, ExternalLink, } from './common'; +import Paragraph from './common/Paragraph'; import { useSidebar } from './FloatableSidebar'; import LoggedInUser from './LoggedInUser'; import { useServerURL } from './ServerContext'; @@ -240,7 +240,7 @@ function BudgetTitlebar() { maxWidth: 400, }} > - <P> + <Paragraph> You are currently using a{' '} <Text style={{ fontWeight: 600 }}> {budgetType === 'report' @@ -249,8 +249,8 @@ function BudgetTitlebar() { . </Text>{' '} Switching will not lose any data and you can always switch back. - </P> - <P> + </Paragraph> + <Paragraph> <ButtonWithLoading type="primary" loading={loading} @@ -261,15 +261,15 @@ function BudgetTitlebar() { ? 'Rollover budget' : 'Report budget'} </ButtonWithLoading> - </P> - <P isLast={true}> + </Paragraph> + <Paragraph isLast={true}> <ExternalLink to="https://actualbudget.org/docs/experimental/report-budget" linkColor="muted" > How do these types of budgeting work? </ExternalLink> - </P> + </Paragraph> </Tooltip> )} </View> diff --git a/packages/desktop-client/src/components/common.tsx b/packages/desktop-client/src/components/common.tsx index 0c8e33dd06e4de357c0c0ef49dae3504a0d0f6bc..8118eae545228924ec09c3873aaa417c25364c36 100644 --- a/packages/desktop-client/src/components/common.tsx +++ b/packages/desktop-client/src/components/common.tsx @@ -1,21 +1,14 @@ -import React, { - useRef, - useLayoutEffect, - useCallback, - type ComponentProps, - type ReactNode, - forwardRef, -} from 'react'; -import { NavLink, useMatch, useNavigate } from 'react-router-dom'; +import React, { type ComponentProps, type ReactNode, forwardRef } from 'react'; +import { useMatch, useNavigate } from 'react-router-dom'; -import { type CSSProperties, css } from 'glamor'; +import { type CSSProperties } from 'glamor'; -import { styles, colors } from '../style'; -import type { HTMLPropsWithStyle } from '../types/utils'; +import { colors } from '../style'; import Button from './common/Button'; export { default as AlignedText } from './common/AlignedText'; +export { default as AnchorLink } from './common/AnchorLink'; export { default as Block } from './common/Block'; export { default as Button, ButtonWithLoading } from './common/Button'; export { default as Card } from './common/Card'; @@ -38,45 +31,6 @@ export { default as TextOneLine } from './common/TextOneLine'; export { default as View } from './common/View'; export { default as LinkButton } from './common/LinkButton'; -type UseStableCallbackArg = (...args: unknown[]) => unknown; - -export const useStableCallback = (callback: UseStableCallbackArg) => { - const callbackRef = useRef<UseStableCallbackArg>(); - const memoCallback = useCallback( - (...args) => callbackRef.current && callbackRef.current(...args), - [], - ); - useLayoutEffect(() => { - callbackRef.current = callback; - }); - return memoCallback; -}; - -type AnchorLinkProps = { - to: string; - style?: CSSProperties; - activeStyle?: CSSProperties; - children?: ReactNode; -}; - -export function AnchorLink({ - to, - style, - activeStyle, - children, -}: AnchorLinkProps) { - let match = useMatch({ path: to }); - - return ( - <NavLink - to={to} - {...css([styles.smallText, style, match ? activeStyle : null])} - > - {children} - </NavLink> - ); -} - let externalLinkColors = { purple: colors.p4, blue: colors.b4, @@ -131,19 +85,5 @@ export function ButtonLink({ ); } -type PProps = HTMLPropsWithStyle<HTMLDivElement> & { - isLast?: boolean; -}; -export function P({ style, isLast, children, ...props }: PProps) { - return ( - <div - {...props} - {...css(!isLast && { marginBottom: 15 }, style, { lineHeight: '1.5em' })} - > - {children} - </div> - ); -} - export * from './tooltips'; export { useTooltip } from './tooltips'; diff --git a/packages/desktop-client/src/components/common/AnchorLink.tsx b/packages/desktop-client/src/components/common/AnchorLink.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3f9ae4b6f11ac8e382aed664c8f4a7254368e196 --- /dev/null +++ b/packages/desktop-client/src/components/common/AnchorLink.tsx @@ -0,0 +1,31 @@ +import { type ReactNode } from 'react'; +import { NavLink, useMatch } from 'react-router-dom'; + +import { type CSSProperties, css } from 'glamor'; + +import { styles } from '../../style'; + +type AnchorLinkProps = { + to: string; + style?: CSSProperties; + activeStyle?: CSSProperties; + children?: ReactNode; +}; + +export default function AnchorLink({ + to, + style, + activeStyle, + children, +}: AnchorLinkProps) { + let match = useMatch({ path: to }); + + return ( + <NavLink + to={to} + {...css([styles.smallText, style, match ? activeStyle : null])} + > + {children} + </NavLink> + ); +} diff --git a/packages/desktop-client/src/components/common/Paragraph.tsx b/packages/desktop-client/src/components/common/Paragraph.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a938a643db69571cabf2ee9499ce891096371068 --- /dev/null +++ b/packages/desktop-client/src/components/common/Paragraph.tsx @@ -0,0 +1,23 @@ +import { css } from 'glamor'; + +import { type HTMLPropsWithStyle } from '../../types/utils'; + +type ParagraphProps = HTMLPropsWithStyle<HTMLDivElement> & { + isLast?: boolean; +}; + +export default function Paragraph({ + style, + isLast, + children, + ...props +}: ParagraphProps) { + return ( + <div + {...props} + {...css(!isLast && { marginBottom: 15 }, style, { lineHeight: '1.5em' })} + > + {children} + </div> + ); +} diff --git a/packages/desktop-client/src/components/gocardless/GoCardlessLink.js b/packages/desktop-client/src/components/gocardless/GoCardlessLink.js index 4f2cfe57d6a64534d94f8e7b0401a4332c6b8fec..a2cac94998ba688ae569046d3f93a35498316b06 100644 --- a/packages/desktop-client/src/components/gocardless/GoCardlessLink.js +++ b/packages/desktop-client/src/components/gocardless/GoCardlessLink.js @@ -1,6 +1,7 @@ import React from 'react'; -import { Modal, P, View } from '../common'; +import { Modal, View } from '../common'; +import Paragraph from '../common/Paragraph'; export default function GoCardlessLink() { window.close(); @@ -9,11 +10,11 @@ export default function GoCardlessLink() { <Modal isCurrent={true} showClose={false} title="Account sync"> {() => ( <View style={{ maxWidth: 500 }}> - <P>Please wait...</P> - <P> + <Paragraph>Please wait...</Paragraph> + <Paragraph> The window should close automatically. If nothing happend you can close this window or tab. - </P> + </Paragraph> </View> )} </Modal> diff --git a/packages/desktop-client/src/components/manager/ImportActual.js b/packages/desktop-client/src/components/manager/ImportActual.js index a4c66d7a9b56fbde7d18ae6c133ed500b6e712de..3d763af7e39afeaa4740e749fd779bdfc50ab82c 100644 --- a/packages/desktop-client/src/components/manager/ImportActual.js +++ b/packages/desktop-client/src/components/manager/ImportActual.js @@ -4,7 +4,8 @@ import { useDispatch } from 'react-redux'; import { importBudget } from 'loot-core/src/client/actions/budgets'; import { styles, colors } from '../../style'; -import { View, Block, Modal, ButtonWithLoading, P } from '../common'; +import { View, Block, Modal, ButtonWithLoading } from '../common'; +import Paragraph from '../common/Paragraph'; function getErrorMessage(error) { switch (error) { @@ -61,14 +62,16 @@ function Import({ modalProps }) { )} <View style={{ '& > div': { lineHeight: '1.7em' } }}> - <P> + <Paragraph> You can import data from another Actual account or instance. First export your data from a different account, and it will give you a compressed file. This file is a simple zip file that contains the{' '} <code>db.sqlite</code> and <code>metadata.json</code> files. - </P> + </Paragraph> - <P>Select one of these compressed files and import it here.</P> + <Paragraph> + Select one of these compressed files and import it here. + </Paragraph> <View style={{ alignSelf: 'center' }}> <ButtonWithLoading diff --git a/packages/desktop-client/src/components/manager/ImportYNAB4.js b/packages/desktop-client/src/components/manager/ImportYNAB4.js index ce4d1dbebe7b6a40495568be91ec5444e729748d..0486e2efd15d5f0cb48943804f9f24317c60dc4c 100644 --- a/packages/desktop-client/src/components/manager/ImportYNAB4.js +++ b/packages/desktop-client/src/components/manager/ImportYNAB4.js @@ -4,7 +4,8 @@ import { useDispatch } from 'react-redux'; import { importBudget } from 'loot-core/src/client/actions/budgets'; import { styles, colors } from '../../style'; -import { View, Block, Modal, ButtonWithLoading, P } from '../common'; +import { View, Block, Modal, ButtonWithLoading } from '../common'; +import Paragraph from '../common/Paragraph'; function getErrorMessage(error) { switch (error) { @@ -49,18 +50,18 @@ function Import({ modalProps }) { )} <View style={{ alignItems: 'center' }}> - <P> + <Paragraph> To import data from YNAB4, locate where your YNAB4 data is stored. It is usually in your Documents folder under YNAB. Your data is a directory inside that with the <code>.ynab4</code> suffix. - </P> - <P> + </Paragraph> + <Paragraph> When you’ve located your data,{' '} <strong>compress it into a zip file</strong>. On macOS, right-click the folder and select “Compressâ€. On Windows, right-click and select “Send to → Compressed (zipped) folderâ€. Upload the zipped folder for importing. - </P> + </Paragraph> <View> <ButtonWithLoading type="primary" diff --git a/packages/desktop-client/src/components/manager/ImportYNAB5.js b/packages/desktop-client/src/components/manager/ImportYNAB5.js index 01cdc854ea081df6ce1b3608fbbb62f2a7ecbab7..3af439e1f9a23b720118d10d0fafed749fdc091e 100644 --- a/packages/desktop-client/src/components/manager/ImportYNAB5.js +++ b/packages/desktop-client/src/components/manager/ImportYNAB5.js @@ -4,14 +4,8 @@ import { useDispatch } from 'react-redux'; import { importBudget } from 'loot-core/src/client/actions/budgets'; import { styles, colors } from '../../style'; -import { - View, - Block, - Modal, - ButtonWithLoading, - P, - ExternalLink, -} from '../common'; +import { View, Block, Modal, ButtonWithLoading, ExternalLink } from '../common'; +import Paragraph from '../common/Paragraph'; function getErrorMessage(error) { switch (error) { @@ -60,20 +54,20 @@ function Import({ modalProps }) { <View style={{ alignItems: 'center', '& > div': { lineHeight: '1.7em' } }} > - <P> + <Paragraph> <ExternalLink to="https://actualbudget.org/docs/migration/nynab"> Read here </ExternalLink>{' '} for instructions on how to migrate your data from YNAB. You need to export your data as JSON, and that page explains how to do that. - </P> - <P> + </Paragraph> + <Paragraph> Once you have exported your data, select the file and Actual will import it. Budgets may not match up exactly because things work slightly differently, but you should be able to fix up any problems. - </P> + </Paragraph> <View> <ButtonWithLoading type="primary" diff --git a/packages/desktop-client/src/components/manager/WelcomeScreen.js b/packages/desktop-client/src/components/manager/WelcomeScreen.js index f0765c406382cd7f52ec3ea4a4b26649570c5c9f..d2b1927c50499dbf2ddfbff91f8c2d04226f8755 100644 --- a/packages/desktop-client/src/components/manager/WelcomeScreen.js +++ b/packages/desktop-client/src/components/manager/WelcomeScreen.js @@ -2,7 +2,8 @@ import React from 'react'; import { useActions } from '../../hooks/useActions'; import { colors, styles } from '../../style'; -import { View, Button, Text, P, ExternalLink } from '../common'; +import { View, Button, Text, ExternalLink } from '../common'; +import Paragraph from '../common/Paragraph'; export default function WelcomeScreen() { let { createBudget, pushModal } = useActions(); @@ -19,14 +20,14 @@ export default function WelcomeScreen() { > <Text style={styles.veryLargeText}>Let’s get started!</Text> <View style={{ overflowY: 'auto' }}> - <P> + <Paragraph> Actual is a personal finance tool that focuses on beautiful design and a slick user experience.{' '} <strong>Editing your data should be as fast as possible.</strong> On top of that, we want to provide powerful tools to allow you to do whatever you want with your data. - </P> - <P> + </Paragraph> + <Paragraph> Currently, Actual implements budgeting based on a{' '} <ExternalLink to="https://actualbudget.org/docs/budgeting/" @@ -43,12 +44,12 @@ export default function WelcomeScreen() { </ExternalLink>{' '} to help you get your bearings, and check out the rest of the documentation while you’re there to learn more about advanced topics. - </P> - <P style={{ color: colors.n5 }}> + </Paragraph> + <Paragraph style={{ color: colors.n5 }}> Get started by importing an existing budget file from Actual or another budgeting app, or start fresh with an empty budget. You can always create or import another budget later. - </P> + </Paragraph> </View> <View style={{ diff --git a/packages/desktop-client/src/components/manager/subscribe/Bootstrap.tsx b/packages/desktop-client/src/components/manager/subscribe/Bootstrap.tsx index 49a0b89c60c37dfe4735ec54067889763087747c..c39f2f39e301110a92b9a96321b5c2b2a7ed6894 100644 --- a/packages/desktop-client/src/components/manager/subscribe/Bootstrap.tsx +++ b/packages/desktop-client/src/components/manager/subscribe/Bootstrap.tsx @@ -6,7 +6,8 @@ import { loggedIn } from 'loot-core/src/client/actions/user'; import { send } from 'loot-core/src/platform/client/fetch'; import { colors } from '../../../style'; -import { View, Text, Button, P, ExternalLink } from '../../common'; +import { View, Text, Button, ExternalLink } from '../../common'; +import Paragraph from '../../common/Paragraph'; import { useBootstrapped, Title } from './common'; import { ConfirmPasswordForm } from './ConfirmPasswordForm'; @@ -52,19 +53,19 @@ export default function Bootstrap() { return ( <View style={{ maxWidth: 450, marginTop: -30 }}> <Title text="Welcome to Actual!" /> - <P style={{ fontSize: 16, color: colors.n2 }}> + <Paragraph style={{ fontSize: 16, color: colors.n2 }}> Actual is a super fast privacy-focused app for managing your finances. To secure your data, you’ll need to set a password for your server. - </P> + </Paragraph> - <P isLast style={{ fontSize: 16, color: colors.n2 }}> + <Paragraph isLast style={{ fontSize: 16, color: colors.n2 }}> Consider opening{' '} <ExternalLink to="https://actualbudget.org/docs/tour/"> our tour </ExternalLink>{' '} in a new tab for some guidance on what to do when you’ve set your password. - </P> + </Paragraph> {error && ( <Text diff --git a/packages/desktop-client/src/components/modals/CloseAccount.js b/packages/desktop-client/src/components/modals/CloseAccount.js index f8315bb73c10beef214f81a316b4235842639fa2..524e78b0fdc835e4e99c6a9b6fc7f9029a6b3001 100644 --- a/packages/desktop-client/src/components/modals/CloseAccount.js +++ b/packages/desktop-client/src/components/modals/CloseAccount.js @@ -5,10 +5,11 @@ import { integerToCurrency } from 'loot-core/src/shared/util'; import { colors } from '../../style'; import AccountAutocomplete from '../autocomplete/AccountAutocomplete'; import CategoryAutocomplete from '../autocomplete/CategorySelect'; -import { P, LinkButton } from '../common'; import Button from '../common/Button'; import FormError from '../common/FormError'; +import LinkButton from '../common/LinkButton'; import Modal from '../common/Modal'; +import Paragraph from '../common/Paragraph'; import Text from '../common/Text'; import View from '../common/View'; @@ -46,7 +47,7 @@ function CloseAccount({ > {() => ( <View> - <P> + <Paragraph> Are you sure you want to close <strong>{account.name}</strong>?{' '} {canDelete ? ( <span> @@ -58,7 +59,7 @@ function CloseAccount({ This account has transactions so we can’t permanently delete it. </span> )} - </P> + </Paragraph> <form onSubmit={event => { event.preventDefault(); @@ -83,12 +84,12 @@ function CloseAccount({ > {balance !== 0 && ( <View> - <P> + <Paragraph> This account has a balance of{' '} <strong>{integerToCurrency(balance)}</strong>. To close this account, select a different account to transfer this balance to: - </P> + </Paragraph> <View style={{ marginBottom: 15 }}> <AccountAutocomplete @@ -115,11 +116,11 @@ function CloseAccount({ {needsCategory(account, transfer, accounts) && ( <View style={{ marginBottom: 15 }}> - <P> + <Paragraph> Since you are transferring the balance from a budgeted account to an off-budget account, this transaction must be categorized. Select a category: - </P> + </Paragraph> <CategoryAutocomplete categoryGroups={categoryGroups} diff --git a/packages/desktop-client/src/components/modals/CreateAccount.js b/packages/desktop-client/src/components/modals/CreateAccount.js index a674d84c9abd06684cf07c968cfd98289ee101e7..f6c4371a3cd99755e5c6031c4d5c59ee1a0f87e5 100644 --- a/packages/desktop-client/src/components/modals/CreateAccount.js +++ b/packages/desktop-client/src/components/modals/CreateAccount.js @@ -9,11 +9,11 @@ import { View, Text, Modal, - P, Button, ButtonWithLoading, ExternalLink, } from '../common'; +import Paragraph from '../common/Paragraph'; export default function CreateAccount({ modalProps, syncServerStatus }) { const dispatch = useDispatch(); @@ -111,7 +111,7 @@ export default function CreateAccount({ modalProps, syncServerStatus }) { > Set up GoCardless for bank sync </Button> - <P style={{ fontSize: 15 }}> + <Paragraph style={{ fontSize: 15 }}> Connect to an Actual server to set up{' '} <ExternalLink to="https://actualbudget.org/docs/advanced/bank-sync" @@ -120,7 +120,7 @@ export default function CreateAccount({ modalProps, syncServerStatus }) { automatic syncing with GoCardless </ExternalLink> . - </P> + </Paragraph> </> )} </View> diff --git a/packages/desktop-client/src/components/modals/CreateEncryptionKey.js b/packages/desktop-client/src/components/modals/CreateEncryptionKey.js index c0d10b1588fe44522a959dd59a41b31e8a9d6d5a..ffe36d82c712a9bd134b18edf23be24312e101bc 100644 --- a/packages/desktop-client/src/components/modals/CreateEncryptionKey.js +++ b/packages/desktop-client/src/components/modals/CreateEncryptionKey.js @@ -12,11 +12,11 @@ import { Modal, ButtonWithLoading, ExternalLink, - P, ModalButtons, Input, InitialFocus, } from '../common'; +import Paragraph from '../common/Paragraph'; export default function CreateEncryptionKey({ modalProps, @@ -62,7 +62,7 @@ export default function CreateEncryptionKey({ <> {!isRecreating ? ( <> - <P style={{ marginTop: 5 }}> + <Paragraph style={{ marginTop: 5 }}> To enable end-to-end encryption, you need to create a key. We will generate a key based on a password and use it to encrypt from now on. <strong>This requires a sync reset</strong> and all @@ -70,8 +70,8 @@ export default function CreateEncryptionKey({ <ExternalLink to="https://actualbudget.org/docs/getting-started/sync/#end-to-end-encryption"> Learn more </ExternalLink> - </P> - <P> + </Paragraph> + <Paragraph> <ul {...css({ marginTop: 0, '& li': { marginBottom: 8 } })}> <li> <strong>Important:</strong> if you forget this password{' '} @@ -94,11 +94,11 @@ export default function CreateEncryptionKey({ protect your data. </li> </ul> - </P> + </Paragraph> </> ) : ( <> - <P style={{ marginTop: 5 }}> + <Paragraph style={{ marginTop: 5 }}> This will generate a new key for encrypting your data.{' '} <strong>This requires a sync reset</strong> and all other devices will have to revert to this version of your data. Actual @@ -106,12 +106,12 @@ export default function CreateEncryptionKey({ <ExternalLink to="https://actualbudget.org/docs/getting-started/sync/#end-to-end-encryption"> Learn more </ExternalLink> - </P> - <P> + </Paragraph> + <Paragraph> Key generation is randomized. The same password will create different keys, so this will change your key regardless of the password being different. - </P> + </Paragraph> </> )} <form diff --git a/packages/desktop-client/src/components/modals/FixEncryptionKey.js b/packages/desktop-client/src/components/modals/FixEncryptionKey.js index 6fccc259b5050878a7d6c27276e78be1add5fc88..610a8686c8e05c8dfe17b9b28c1beb5c244e2f69 100644 --- a/packages/desktop-client/src/components/modals/FixEncryptionKey.js +++ b/packages/desktop-client/src/components/modals/FixEncryptionKey.js @@ -10,12 +10,12 @@ import { Modal, Button, ButtonWithLoading, - P, ModalButtons, Input, InitialFocus, ExternalLink, } from '../common'; +import Paragraph from '../common/Paragraph'; export default function FixEncryptionKey({ modalProps, @@ -66,22 +66,22 @@ export default function FixEncryptionKey({ : 'This file is encrypted'} </Text> {hasExistingKey ? ( - <P> + <Paragraph> This file was encrypted with a different key than you are currently using. This probably means you changed your password. Enter your current password to update your key.{' '} <ExternalLink to="https://actualbudget.org/docs/getting-started/sync/#end-to-end-encryption"> Learn more </ExternalLink> - </P> + </Paragraph> ) : ( - <P> + <Paragraph> We don’t have a key that encrypts or decrypts this file. Enter the password for this file to create the key for encryption.{' '} <ExternalLink to="https://actualbudget.org/docs/getting-started/sync/#end-to-end-encryption"> Learn more </ExternalLink> - </P> + </Paragraph> )} <form onSubmit={e => { diff --git a/packages/desktop-client/src/components/modals/GoCardlessExternalMsg.js b/packages/desktop-client/src/components/modals/GoCardlessExternalMsg.js index 2a764a76cf82ddbdae838132cba87b75843893e8..910f487b33c693aa2275ee6dd1e124f07c6c3e00 100644 --- a/packages/desktop-client/src/components/modals/GoCardlessExternalMsg.js +++ b/packages/desktop-client/src/components/modals/GoCardlessExternalMsg.js @@ -14,12 +14,12 @@ import { View, Modal, Button, - P, LinkButton, Menu, Tooltip, ExternalLink, } from '../common'; +import Paragraph from '../common/Paragraph'; import { FormField, FormLabel } from '../forms'; import { COUNTRY_OPTIONS } from './countries'; @@ -257,11 +257,11 @@ export default function GoCardlessExternalMsg({ > {() => ( <View> - <P style={{ fontSize: 15 }}> + <Paragraph style={{ fontSize: 15 }}> To link your bank account, you will be redirected to a new page where GoCardless will ask to connect to your bank. GoCardless will not be able to withdraw funds from your accounts. - </P> + </Paragraph> {error && renderError(error)} @@ -306,9 +306,9 @@ export default function GoCardlessExternalMsg({ renderLinkButton() ) : ( <> - <P style={{ color: colors.r5 }}> + <Paragraph style={{ color: colors.r5 }}> GoCardless integration has not yet been configured. - </P> + </Paragraph> <Button type="primary" onClick={onGoCardlessInit}> Configure GoCardless integration </Button> diff --git a/packages/desktop-client/src/components/modals/MergeUnusedPayees.js b/packages/desktop-client/src/components/modals/MergeUnusedPayees.js index 44bc4e2b52305a2ce27382ee3ff42cbc4e0f4617..9814e1ad02eafc5e1e8611e4f9daa24732bcd6d5 100644 --- a/packages/desktop-client/src/components/modals/MergeUnusedPayees.js +++ b/packages/desktop-client/src/components/modals/MergeUnusedPayees.js @@ -6,7 +6,8 @@ import { send } from 'loot-core/src/platform/client/fetch'; import { colors } from '../../style'; import { Information } from '../alerts'; -import { View, Text, Modal, ModalButtons, Button, P } from '../common'; +import { View, Text, Modal, ModalButtons, Button } from '../common'; +import Paragraph from '../common/Paragraph'; let highlightStyle = { color: colors.p5 }; @@ -89,7 +90,7 @@ export default function MergeUnusedPayees({ {() => ( <View style={{ padding: 20, maxWidth: 500 }}> <View> - <P style={{ marginBottom: 10, fontWeight: 500 }}> + <Paragraph style={{ marginBottom: 10, fontWeight: 500 }}> {payees.length === 1 ? ( <> The payee <Text style={highlightStyle}>{payees[0].name}</Text>{' '} @@ -118,7 +119,7 @@ export default function MergeUnusedPayees({ </ul> </> )} - </P> + </Paragraph> <Information> Merging will remove the payee and transfer any existing rules to diff --git a/packages/desktop-client/src/components/modals/PlaidExternalMsg.js b/packages/desktop-client/src/components/modals/PlaidExternalMsg.js index b3266efca6c8cb9d52b4f6f0645952a1fe18b323..f390f53d0e58090e2207fe22507c7fa094768de5 100644 --- a/packages/desktop-client/src/components/modals/PlaidExternalMsg.js +++ b/packages/desktop-client/src/components/modals/PlaidExternalMsg.js @@ -3,7 +3,8 @@ import React, { useState, useRef } from 'react'; import AnimatedLoading from '../../icons/AnimatedLoading'; import { colors } from '../../style'; import { Error } from '../alerts'; -import { View, Text, Modal, Button, P, ModalButtons } from '../common'; +import { View, Text, Modal, Button, ModalButtons } from '../common'; +import Paragraph from '../common/Paragraph'; function renderError(error) { return ( @@ -62,11 +63,11 @@ export default function PlaidExternalMsg({ > {() => ( <View> - <P style={{ fontSize: 15 }}> + <Paragraph style={{ fontSize: 15 }}> To link your bank account, you will be moved to your browser for enhanced security. Click below and Actual will automatically resume when you have given your bank’s credentials. - </P> + </Paragraph> {error && renderError(error)} {waiting ? ( diff --git a/packages/desktop-client/src/components/payees/index.js b/packages/desktop-client/src/components/payees/index.js index dd6ea78441ad379b4544622c888f669127f4cc98..4b31e81e546f03eecfd82fa33f6acefcce4da7f1 100644 --- a/packages/desktop-client/src/components/payees/index.js +++ b/packages/desktop-client/src/components/payees/index.js @@ -19,20 +19,13 @@ import useSelected, { useSelectedItems, useSelectedDispatch, } from '../../hooks/useSelected'; +import useStableCallback from '../../hooks/useStableCallback'; import Delete from '../../icons/v0/Delete'; import ExpandArrow from '../../icons/v0/ExpandArrow'; import Merge from '../../icons/v0/Merge'; import ArrowThinRight from '../../icons/v1/ArrowThinRight'; import { colors } from '../../style'; -import { - useStableCallback, - View, - Text, - Input, - Button, - Tooltip, - Menu, -} from '../common'; +import { View, Text, Input, Button, Tooltip, Menu } from '../common'; import { Table, TableHeader, diff --git a/packages/desktop-client/src/components/reports/CashFlow.js b/packages/desktop-client/src/components/reports/CashFlow.js index 07d4a21c65bc913edefb7c808fc437ed305b5eb5..afd58c3083f98103a07b4737d83df8f784c4698b 100644 --- a/packages/desktop-client/src/components/reports/CashFlow.js +++ b/packages/desktop-client/src/components/reports/CashFlow.js @@ -8,7 +8,8 @@ import { integerToCurrency } from 'loot-core/src/shared/util'; import useFilters from '../../hooks/useFilters'; import { colors, styles } from '../../style'; -import { View, Text, Block, P, AlignedText } from '../common'; +import { View, Text, Block, AlignedText } from '../common'; +import Paragraph from '../common/Paragraph'; import Change from './Change'; import { cashFlowByDate } from './graphs/cash-flow-spreadsheet'; @@ -165,15 +166,15 @@ function CashFlow() { /> <View style={{ marginTop: 30 }}> - <P> + <Paragraph> <strong>How is cash flow calculated?</strong> - </P> - <P> + </Paragraph> + <Paragraph> Cash flow shows the balance of your budgeted accounts over time, and the amount of expenses/income each day or month. Your budgeted accounts are considered to be “cash on hand,†so this gives you a picture of how available money fluctuates. - </P> + </Paragraph> </View> </View> </View> diff --git a/packages/desktop-client/src/components/reports/NetWorth.js b/packages/desktop-client/src/components/reports/NetWorth.js index 343568eeba2fe96c5c42817a839b3652c89350a3..b1cb62127f986288c911d25193dbeb8716a96d04 100644 --- a/packages/desktop-client/src/components/reports/NetWorth.js +++ b/packages/desktop-client/src/components/reports/NetWorth.js @@ -9,7 +9,8 @@ import { integerToCurrency } from 'loot-core/src/shared/util'; import useFilters from '../../hooks/useFilters'; import { styles } from '../../style'; -import { View, P } from '../common'; +import Paragraph from '../common/Paragraph'; +import View from '../common/View'; import Change from './Change'; import netWorthSpreadsheet from './graphs/net-worth-spreadsheet'; @@ -124,16 +125,16 @@ export default function NetWorth() { <NetWorthGraph start={start} end={end} graphData={data.graphData} /> <View style={{ marginTop: 30 }}> - <P> + <Paragraph> <strong>How is net worth calculated?</strong> - </P> - <P> + </Paragraph> + <Paragraph> Net worth shows the balance of all accounts over time, including all of your investments. Your “net worth†is considered to be the amount you’d have if you sold all your assets and paid off as much debt as possible. If you hover over the graph, you can also see the amount of assets and debt individually. - </P> + </Paragraph> </View> </View> </View> diff --git a/packages/desktop-client/src/components/schedules/DiscoverSchedules.js b/packages/desktop-client/src/components/schedules/DiscoverSchedules.js index df03567842284656dbfd8afc8c5834b05f214cba..574e488cba175223c25d95f6d54bd5cf4126e1f3 100644 --- a/packages/desktop-client/src/components/schedules/DiscoverSchedules.js +++ b/packages/desktop-client/src/components/schedules/DiscoverSchedules.js @@ -13,7 +13,8 @@ import useSelected, { import useSendPlatformRequest from '../../hooks/useSendPlatformRequest'; import { colors } from '../../style'; import { getParent } from '../../util/router-tools'; -import { View, Stack, ButtonWithLoading, P } from '../common'; +import { View, Stack, ButtonWithLoading } from '../common'; +import Paragraph from '../common/Paragraph'; import { Page, usePageType } from '../Page'; import { Table, TableHeader, Row, Field, SelectCell } from '../table'; import DisplayId from '../util/DisplayId'; @@ -157,15 +158,15 @@ export default function DiscoverSchedules() { return ( <Page title="Found schedules" modalSize={{ width: 850, height: 650 }}> - <P> + <Paragraph> We found some possible schedules in your current transactions. Select the ones you want to create. - </P> - <P> + </Paragraph> + <Paragraph> If you expected a schedule here and don’t see it, it might be because the payees of the transactions don’t match. Make sure you rename payees on all transactions for a schedule to be the same payee. - </P> + </Paragraph> <SelectedProvider instance={selectedInst}> <DiscoverSchedulesTable loading={isLoading} schedules={schedules} /> diff --git a/packages/desktop-client/src/components/schedules/PostsOfflineNotification.js b/packages/desktop-client/src/components/schedules/PostsOfflineNotification.js index 34a6f03ee927354843f3a8a291d1d9dfd32f7070..34375205bc8df2e762206e58e6f3a92667b958c1 100644 --- a/packages/desktop-client/src/components/schedules/PostsOfflineNotification.js +++ b/packages/desktop-client/src/components/schedules/PostsOfflineNotification.js @@ -4,7 +4,8 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { send } from 'loot-core/src/platform/client/fetch'; import { colors } from '../../style'; -import { Text, P, Button, Stack } from '../common'; +import { Text, Button, Stack } from '../common'; +import Paragraph from '../common/Paragraph'; import { Page } from '../Page'; import DisplayId from '../util/DisplayId'; @@ -26,7 +27,7 @@ export default function PostsOfflineNotification() { return ( <Page title="Post transactions?" modalSize="small"> - <P> + <Paragraph> {payees.length > 0 ? ( <Text> The {plural ? 'payees ' : 'payee '} @@ -53,17 +54,17 @@ export default function PostsOfflineNotification() { syncing failed. In order to avoid duplicate transactions, we let you choose whether or not to create transactions for these schedules. </Text> - </P> - <P> + </Paragraph> + <Paragraph> Be aware that other devices may have already created these transactions. If you have multiple devices, make sure you only do this on one device or you will have duplicate transactions. - </P> - <P> + </Paragraph> + <Paragraph> You can always manually post a transaction later for a due schedule by selecting the schedule and clicking “Post transaction†in the action menu. - </P> + </Paragraph> <Stack direction="row" justify="flex-end" diff --git a/packages/desktop-client/src/components/settings/FixSplits.js b/packages/desktop-client/src/components/settings/FixSplits.js index 1167bfeeaa10d92cc7ff2227e73d46cf70d9964b..fa3eeb0db10ced059ae4088a481395f8761b5bc4 100644 --- a/packages/desktop-client/src/components/settings/FixSplits.js +++ b/packages/desktop-client/src/components/settings/FixSplits.js @@ -3,7 +3,8 @@ import React, { useState } from 'react'; import { send } from 'loot-core/src/platform/client/fetch'; import { colors } from '../../style'; -import { View, Text, P, ButtonWithLoading } from '../common'; +import { View, Text, ButtonWithLoading } from '../common'; +import Paragraph from '../common/Paragraph'; import { Setting } from './UI'; @@ -31,7 +32,7 @@ function renderResults(results) { } return ( - <P + <Paragraph style={{ color: colors.g3, marginBottom: 0, @@ -41,7 +42,7 @@ function renderResults(results) { }} > {result} - </P> + </Paragraph> ); } diff --git a/packages/desktop-client/src/hooks/useStableCallback.ts b/packages/desktop-client/src/hooks/useStableCallback.ts new file mode 100644 index 0000000000000000000000000000000000000000..8bcc9f34389a810f7893a1884b8e73e7c9b028ca --- /dev/null +++ b/packages/desktop-client/src/hooks/useStableCallback.ts @@ -0,0 +1,15 @@ +import { useRef, useLayoutEffect, useCallback } from 'react'; + +type UseStableCallbackArg = (...args: unknown[]) => unknown; + +export default function useStableCallback(callback: UseStableCallbackArg) { + const callbackRef = useRef<UseStableCallbackArg>(); + const memoCallback = useCallback( + (...args) => callbackRef.current && callbackRef.current(...args), + [], + ); + useLayoutEffect(() => { + callbackRef.current = callback; + }); + return memoCallback; +} diff --git a/upcoming-release-notes/1413.md b/upcoming-release-notes/1413.md new file mode 100644 index 0000000000000000000000000000000000000000..08d8c378c4d3e4a802c8f3a3f270875963d89bec --- /dev/null +++ b/upcoming-release-notes/1413.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [MatissJanis] +--- + +Moving `P` (`Paragraph`), `AnchorLink` and `useStableCallback` to a separate files