Skip to content
Snippets Groups Projects
  • Joel Jeremy Marquez's avatar
    44ce976f
    [React Aria Button] - Migrate sidebar, notifications, transactions, recurring... · 44ce976f
    Joel Jeremy Marquez authored
    [React Aria Button] - Migrate sidebar, notifications, transactions, recurring schedule picker buttons (#2984)
    
    * More components to use react aria Button
    
    * Release notes
    
    * vrt
    
    * Fix typecheck error
    
    * Fix account menu test
    
    * Fix typecheck error
    
    * Fix typecheck error
    
    * Remove unnecessary aria-labels
    
    * Fix payee icons and category notes
    
    * vrt
    
    * vrt
    
    * Fix notes button
    
    * Fix typecheck error
    
    * Fix lint error
    
    * VRT
    
    * Remove default :focus on Button2
    
    * Add Button2 defaultClassName
    
    * Update className
    
    * Fix typecheck error
    
    * Cleanup
    
    * VRT
    
    * Fix typecheck error
    
    * Fix typecheck error
    
    * Fix typecheck error
    [React Aria Button] - Migrate sidebar, notifications, transactions, recurring...
    Joel Jeremy Marquez authored
    [React Aria Button] - Migrate sidebar, notifications, transactions, recurring schedule picker buttons (#2984)
    
    * More components to use react aria Button
    
    * Release notes
    
    * vrt
    
    * Fix typecheck error
    
    * Fix account menu test
    
    * Fix typecheck error
    
    * Fix typecheck error
    
    * Remove unnecessary aria-labels
    
    * Fix payee icons and category notes
    
    * vrt
    
    * vrt
    
    * Fix notes button
    
    * Fix typecheck error
    
    * Fix lint error
    
    * VRT
    
    * Remove default :focus on Button2
    
    * Add Button2 defaultClassName
    
    * Update className
    
    * Fix typecheck error
    
    * Cleanup
    
    * VRT
    
    * Fix typecheck error
    
    * Fix typecheck error
    
    * Fix typecheck error
FatalError.tsx 5.87 KiB
import React, { useState, type ReactNode } from 'react';

import { LazyLoadFailedError } from 'loot-core/src/shared/errors';

import { Block } from './common/Block';
import { Button } from './common/Button2';
import { Link } from './common/Link';
import { Modal } from './common/Modal';
import { Paragraph } from './common/Paragraph';
import { Stack } from './common/Stack';
import { Text } from './common/Text';
import { View } from './common/View';
import { Checkbox } from './forms';

type AppError = Error & {
  type?: string;
  IDBFailure?: boolean;
  SharedArrayBufferMissing?: boolean;
  BackendInitFailure?: boolean;
};

type FatalErrorProps = {
  error: Error | AppError;
};

type RenderSimpleProps = FatalErrorProps;

function RenderSimple({ error }: RenderSimpleProps) {
  let msg: ReactNode;

  if ('IDBFailure' in error && error.IDBFailure) {
    // IndexedDB wasn't able to open the database
    msg = (
      <Text>
        Your browser doesn’t support IndexedDB in this environment, a feature
        that Actual requires to run. This might happen if you are in private
        browsing mode. Please try a different browser or turn off private
        browsing.
      </Text>
    );
  } else if (
    'SharedArrayBufferMissing' in error &&
    error.SharedArrayBufferMissing
  ) {
    // SharedArrayBuffer isn't available
    msg = (
      <Text>
        Actual requires access to <code>SharedArrayBuffer</code> in order to
        function properly. If you’re seeing this error, either your browser does
        not support <code>SharedArrayBuffer</code>, or your server is not
        sending the appropriate headers, or you are not using HTTPS. See{' '}
        <Link
          variant="external"
          linkColor="muted"
          to="https://actualbudget.org/docs/troubleshooting/shared-array-buffer"
        >
          our troubleshooting documentation
        </Link>{' '}
        to learn more. <SharedArrayBufferOverride />
      </Text>
    );
  } else {
    // This indicates the backend failed to initialize. Show the
    // user something at least so they aren't looking at a blank
    // screen
    msg = (
      <Text>There was a problem loading the app in this browser version.</Text>
    );
  }

  return (
    <Stack
      style={{
        paddingBottom: 15,
        lineHeight: '1.5em',
        fontSize: 15,
      }}
    >
      <Text>{msg}</Text>
      <Text>
        Please get{' '}
        <Link
          variant="external"
          linkColor="muted"
          to="https://actualbudget.org/contact"
        >
          in touch
        </Link>{' '}
        for support
      </Text>
    </Stack>
  );
}

function RenderLazyLoadError() {
  return (
    <Stack
      style={{
        paddingBottom: 15,
        lineHeight: '1.5em',
        fontSize: 15,
      }}
    >
      <Text>
        There was a problem loading one of the chunks of the application. Please
        reload the page and try again. If the issue persists - there might be an
        issue with either your internet connection and/or the server where the
        app is hosted.
      </Text>
    </Stack>
  );
}

function RenderUIError() {
  return (
    <>
      <Paragraph>There was an unrecoverable error in the UI. Sorry!</Paragraph>
      <Paragraph>
        If this error persists, please get{' '}
        <Link variant="external" to="https://actualbudget.org/contact">
          in touch
        </Link>{' '}
        so it can be investigated.
      </Paragraph>
    </>
  );
}

function SharedArrayBufferOverride() {
  const [expanded, setExpanded] = useState(false);
  const [understand, setUnderstand] = useState(false);

  return expanded ? (
    <>
      <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.
      </Paragraph>
      <label
        style={{ display: 'flex', alignItems: 'center', marginBottom: 10 }}
      >
        <Checkbox
          checked={understand}
          onChange={() => setUnderstand(!understand)}
        />{' '}
        I understand the risks, run Actual in the unsupported fallback mode
      </label>
      <Button
        isDisabled={!understand}
        onPress={() => {
          window.localStorage.setItem('SharedArrayBufferOverride', 'true');
          window.location.reload();
        }}
      >
        Open Actual
      </Button>
    </>
  ) : (
    <Link
      variant="text"
      onClick={() => setExpanded(true)}
      style={{ marginLeft: 5 }}
    >
      Advanced options
    </Link>
  );
}

export function FatalError({ error }: FatalErrorProps) {
  const [showError, setShowError] = useState(false);

  const showSimpleRender = 'type' in error && error.type === 'app-init-failure';
  const isLazyLoadError = error instanceof LazyLoadFailedError;

  return (
    <Modal isCurrent title={isLazyLoadError ? 'Loading Error' : 'Fatal Error'}>
      <View
        style={{
          maxWidth: 500,
        }}
      >
        {isLazyLoadError ? (
          <RenderLazyLoadError />
        ) : showSimpleRender ? (
          <RenderSimple error={error} />
        ) : (
          <RenderUIError />
        )}

        <Paragraph>
          <Button onPress={() => window.Actual?.relaunch()}>Restart app</Button>
        </Paragraph>
        <Paragraph isLast={true} style={{ fontSize: 11 }}>
          <Link variant="text" onClick={() => setShowError(state => !state)}>
            Show Error
          </Link>
          {showError && (
            <Block
              style={{
                marginTop: 5,
                height: 100,
                overflow: 'auto',
              }}
            >
              {error.stack}
            </Block>
          )}
        </Paragraph>
      </View>
    </Modal>
  );
}