refactor: move error messages to separate files

This commit is contained in:
tiddlygit-test 2021-12-25 21:13:39 +08:00
parent 2563aabdf9
commit d9b8eac75e
9 changed files with 218 additions and 141 deletions

View file

@ -0,0 +1,93 @@
import { useCallback } from 'react';
import Button from '@material-ui/core/Button';
import { Trans, useTranslation } from 'react-i18next';
import styled from 'styled-components';
import { usePromiseValue } from '@/helpers/useServiceValue';
import { IWorkspaceWithMetadata, IWorkspaceMetaData } from '@services/workspaces/interface';
import { Typography } from '@material-ui/core';
const HelperTextsList = styled.ul`
margin-top: 0;
margin-bottom: 1.5rem;
max-width: 70vw;
`;
interface IWikiErrorMessagesProps {
activeWorkspace: IWorkspaceWithMetadata;
}
export function WikiErrorMessages(props: IWikiErrorMessagesProps): JSX.Element {
const { t } = useTranslation();
const wikiLogs = usePromiseValue(async () => await window.service.wiki.getWikiLogs(props.activeWorkspace.wikiFolderLocation));
if (wikiLogs !== undefined) {
return (
<div>
<Button onClick={async () => await window.service.native.open(wikiLogs.filePath)}>{t('Preference.OpenLogFolder')}</Button>
<div>
<pre>
<code>{wikiLogs.content}</code>
</pre>
</div>
</div>
);
}
return <div />;
}
interface IViewLoadErrorMessagesProps {
activeWorkspaceMetadata: IWorkspaceMetaData;
}
export function ViewLoadErrorMessages(props: IViewLoadErrorMessagesProps): JSX.Element {
const { t } = useTranslation();
const requestReload = useCallback(async (): Promise<void> => {
await window.service.window.reload(window.meta.windowName);
}, []);
return (
<div>
<Typography align="left" variant="h5">
{t('AddWorkspace.WikiNotStarted')}
</Typography>
<Typography align="left" variant="body2">
{props.activeWorkspaceMetadata.didFailLoadErrorMessage}
</Typography>
<br />
<Trans t={t} i18nKey="AddWorkspace.MainPageReloadTip">
<Typography align="left" variant="body2">
<>
Try:
<HelperTextsList>
<li>
Click{' '}
<b onClick={requestReload} onKeyPress={requestReload} role="button" tabIndex={0} style={{ cursor: 'pointer' }}>
Reload
</b>{' '}
button below or press <b>CMD_or_Ctrl + R</b> to reload the page.
</li>
<li>
Check the{' '}
<b
onClick={async () => await window.service.native.open(await window.service.context.get('LOG_FOLDER'), true)}
onKeyPress={async () => await window.service.native.open(await window.service.context.get('LOG_FOLDER'), true)}
role="button"
tabIndex={0}
style={{ cursor: 'pointer' }}>
Log Folder
</b>{' '}
to see what happened.
</li>
<li>Backup your file, remove workspace and recreate one.</li>
</HelperTextsList>
</>
</Typography>
</Trans>
<Button variant="outlined" onClick={requestReload}>
{t('AddWorkspace.Reload')}
</Button>
</div>
);
}

View file

@ -0,0 +1,97 @@
import { Trans, useTranslation } from 'react-i18next';
import styled from 'styled-components';
import { WindowNames } from '@services/windows/WindowProperties';
import { IPreferences } from '@services/preferences/interface';
import arrowWhite from '@/images/arrow-white.png';
import arrowBlack from '@/images/arrow-black.png';
const Arrow = styled.div<{ image: string }>`
height: 202px;
width: 150px;
position: absolute;
top: 50px;
left: 72px;
background-image: url(${({ image }) => image});
background-size: 150px 202px;
`;
const Avatar = styled.div`
display: inline-block;
height: 32px;
width: 32px;
/** // TODO: dark theme */
/* background: theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black; */
border-radius: 4;
/** // TODO: dark theme */
/* color: theme.palette.getContrastText(theme.palette.type === 'dark' ? theme.palette.common.white: theme.palette.common.black); */
line-height: 32px;
text-align: center;
font-weight: 500;
text-transform: uppercase;
margin-left: 10px;
margin-right: 10px;
/** // TODO: dark theme */
/* border: theme.palette.type === 'dark' ? 'none' : 1px solid rgba(0, 0, 0, 0.12); */
`;
const Tip2Text = styled.span`
display: inline-block;
font-size: 18px;
/** // TODO: dark theme */
/* color: theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black; */
`;
const TipWithSidebar = styled.div`
position: absolute;
top: 112px;
left: 180px;
user-select: none;
`;
const TipWithoutSidebar = styled.div`
user-select: none;
`;
const AddWorkspaceGuideInfoContainer = styled.div`
cursor: pointer;
`;
export interface IProps {
sidebar: IPreferences['sidebar'];
themeSource: IPreferences['themeSource'];
}
export function NewUserMessage(props: IProps): JSX.Element {
const { t } = useTranslation();
return (
<AddWorkspaceGuideInfoContainer onClick={async () => await window.service.window.open(WindowNames.addWorkspace)}>
{props.sidebar ? (
<>
<Arrow image={props.themeSource === 'dark' ? arrowWhite : arrowBlack} />
<TipWithSidebar id="new-user-tip">
<Trans t={t} i18nKey="AddWorkspace.MainPageTipWithSidebar">
<Tip2Text>Click</Tip2Text>
<Avatar>+</Avatar>
<Tip2Text>to get started!</Tip2Text>
</Trans>
</TipWithSidebar>
</>
) : (
<TipWithoutSidebar id="new-user-tip">
<Tip2Text>
<Trans t={t} i18nKey="AddWorkspace.MainPageTipWithoutSidebar">
<span>Click </span>
<strong>Workspaces &gt; Add Workspace</strong>
<span>Or </span>
<strong>Click Here</strong>
<span> to get started!</span>
</Trans>
</Tip2Text>
</TipWithoutSidebar>
)}
</AddWorkspaceGuideInfoContainer>
);
}

View file

@ -1,6 +1,6 @@
import React, { useCallback } from 'react';
import styled, { css } from 'styled-components';
import { useTranslation, Trans } from 'react-i18next';
import { useTranslation } from 'react-i18next';
import { Helmet } from 'react-helmet';
import { DndContext, useSensor, useSensors, PointerSensor } from '@dnd-kit/core';
import { SortableContext, arrayMove, verticalListSortingStrategy } from '@dnd-kit/sortable';
@ -9,7 +9,7 @@ import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
import SimpleBar from 'simplebar-react';
import 'simplebar/dist/simplebar.min.css';
import { Button, Typography, Tooltip, IconButton as IconButtonRaw } from '@material-ui/core';
import { Typography, Tooltip, IconButton as IconButtonRaw } from '@material-ui/core';
import { Settings as SettingsIcon, Upgrade as UpgradeIcon } from '@material-ui/icons';
import { WindowNames } from '@services/windows/WindowProperties';
@ -22,14 +22,14 @@ import WorkspaceSelector from './WorkspaceSelector';
import FindInPage from '../../components/FindInPage';
import { latestUpdateUrl } from '@/constants/urls';
import arrowWhite from '@/images/arrow-white.png';
import arrowBlack from '@/images/arrow-black.png';
import { SortableWorkspaceSelector } from './SortableWorkspaceSelector';
import { IWorkspace } from '@services/workspaces/interface';
import { useWorkspacesListObservable } from '@services/workspaces/hooks';
import { usePreferenceObservable } from '@services/preferences/hooks';
import { CommandPaletteIcon } from '@/components/icon/CommandPaletteSVG';
import { Languages } from '../Preferences/sections/Languages';
import { NewUserMessage } from './NewUserMessage';
import { WikiErrorMessages, ViewLoadErrorMessages } from './ErrorMessage';
const OuterRoot = styled.div`
display: flex;
@ -116,70 +116,12 @@ const ContentRoot = styled.div`
width: 100%;
`;
const Arrow = styled.div<{ image: string }>`
height: 202px;
width: 150px;
position: absolute;
top: 50px;
left: 72px;
background-image: url(${({ image }) => image});
background-size: 150px 202px;
`;
const Avatar = styled.div`
display: inline-block;
height: 32px;
width: 32px;
/** // TODO: dark theme */
/* background: theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black; */
border-radius: 4;
/** // TODO: dark theme */
/* color: theme.palette.getContrastText(theme.palette.type === 'dark' ? theme.palette.common.white: theme.palette.common.black); */
line-height: 32px;
text-align: center;
font-weight: 500;
text-transform: uppercase;
margin-left: 10px;
margin-right: 10px;
/** // TODO: dark theme */
/* border: theme.palette.type === 'dark' ? 'none' : 1px solid rgba(0, 0, 0, 0.12); */
`;
const Tip2Text = styled.span`
display: inline-block;
font-size: 18px;
/** // TODO: dark theme */
/* color: theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black; */
`;
const TipWithSidebar = styled.div`
position: absolute;
top: 112px;
left: 180px;
user-select: none;
`;
const TipWithoutSidebar = styled.div`
user-select: none;
`;
const AddWorkspaceGuideInfoContainer = styled.div`
cursor: pointer;
`;
const SideBarEnd = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`;
const HelperTextsList = styled.ul`
margin-top: 0;
margin-bottom: 1.5rem;
max-width: 70vw;
`;
const SidebarContainer = ({ children }: { children: React.ReactNode }): JSX.Element => {
const platform = usePromiseValue(async () => await window.service.context.get('platform'));
// use native scroll bar on macOS
@ -194,9 +136,6 @@ export default function Main(): JSX.Element {
const workspacesList = useWorkspacesListObservable();
const preferences = usePreferenceObservable();
const isFullScreen = usePromiseValue<boolean | undefined>(window.service.window.isFullScreen, false)!;
const requestReload = useCallback(async (): Promise<void> => {
await window.service.window.reload(window.meta.windowName);
}, []);
const dndSensors = useSensors(
useSensor(PointerSensor, {
@ -210,6 +149,7 @@ export default function Main(): JSX.Element {
const activeWorkspaceMetadata = workspacesList
?.map((workspace) => ({ active: workspace.active, ...workspace.metadata }))
?.find((workspace) => workspace.active);
const activeWorkspace = workspacesList?.find((workspace) => workspace.active);
const updaterMetaData = useUpdaterObservable();
if (preferences === undefined) return <div>{t('Loading')}</div>;
@ -302,86 +242,16 @@ export default function Main(): JSX.Element {
<ContentRoot>
<FindInPage />
<InnerContentRoot>
{activeWorkspace !== undefined && <WikiErrorMessages activeWorkspace={activeWorkspace} />}
{Array.isArray(workspacesList) &&
workspacesList.length > 0 &&
typeof activeWorkspaceMetadata?.didFailLoadErrorMessage === 'string' &&
activeWorkspaceMetadata?.didFailLoadErrorMessage.length > 0 &&
activeWorkspaceMetadata?.isLoading === false && (
<div>
<Typography align="left" variant="h5">
{t('AddWorkspace.WikiNotStarted')}
</Typography>
<Typography align="left" variant="body2">
{activeWorkspaceMetadata.didFailLoadErrorMessage}
</Typography>
<br />
<Trans t={t} i18nKey="AddWorkspace.MainPageReloadTip">
<Typography align="left" variant="body2">
<>
Try:
<HelperTextsList>
<li>
Click{' '}
<b onClick={requestReload} onKeyPress={requestReload} role="button" tabIndex={0} style={{ cursor: 'pointer' }}>
Reload
</b>{' '}
button below or press <b>CMD_or_Ctrl + R</b> to reload the page.
</li>
<li>
Check the{' '}
<b
onClick={async () => await window.service.native.open(await window.service.context.get('LOG_FOLDER'), true)}
onKeyPress={async () => await window.service.native.open(await window.service.context.get('LOG_FOLDER'), true)}
role="button"
tabIndex={0}
style={{ cursor: 'pointer' }}>
Log Folder
</b>{' '}
to see what happened.
</li>
<li>Backup your file, remove workspace and recreate one.</li>
</HelperTextsList>
</>
</Typography>
</Trans>
<Button variant="outlined" onClick={requestReload}>
{t('AddWorkspace.Reload')}
</Button>
</div>
)}
activeWorkspaceMetadata?.isLoading === false && <ViewLoadErrorMessages activeWorkspaceMetadata={activeWorkspaceMetadata} />}
{Array.isArray(workspacesList) && workspacesList.length > 0 && activeWorkspaceMetadata?.isLoading && (
<Typography color="textSecondary">{t('Loading')}</Typography>
)}
{Array.isArray(workspacesList) && workspacesList.length === 0 && (
<AddWorkspaceGuideInfoContainer onClick={async () => await window.service.window.open(WindowNames.addWorkspace)}>
{sidebar ? (
<>
<Arrow image={themeSource === 'dark' ? arrowWhite : arrowBlack} />
<TipWithSidebar id="new-user-tip">
<Trans t={t} i18nKey="AddWorkspace.MainPageTipWithSidebar">
<Tip2Text>Click</Tip2Text>
<Avatar>+</Avatar>
<Tip2Text>to get started!</Tip2Text>
</Trans>
</TipWithSidebar>
</>
) : (
<TipWithoutSidebar id="new-user-tip">
<Tip2Text>
<Trans t={t} i18nKey="AddWorkspace.MainPageTipWithoutSidebar">
<span>Click </span>
<strong>Workspaces &gt; Add Workspace</strong>
<span>Or </span>
<strong>Click Here</strong>
<span> to get started!</span>
</Trans>
</Tip2Text>
</TipWithoutSidebar>
)}
</AddWorkspaceGuideInfoContainer>
)}
{Array.isArray(workspacesList) && workspacesList.length === 0 && <NewUserMessage sidebar={sidebar} themeSource={themeSource} />}
</InnerContentRoot>
<Languages languageSelectorOnly />
</ContentRoot>

View file

@ -3,7 +3,7 @@ import fs from 'fs-extra';
import { LOG_FOLDER } from '@/constants/paths';
function getWikiLogFilePath(wikiName: string): string {
export function getWikiLogFilePath(wikiName: string): string {
const logFileName = wikiName.replace(/["*/:<>?\\|]/g, '_');
const logFilePath = path.join(LOG_FOLDER, `${logFileName}.log`);
return logFilePath;

View file

@ -304,6 +304,12 @@ export class View implements IViewService {
workspace.name
}`,
);
// will set again in view.webContents.on('did-start-loading'), but that one sometimes is too late to block services that wait for `isLoading`
await this.workspaceService.updateMetaData(workspace.id, {
// eslint-disable-next-line unicorn/no-null
didFailLoadErrorMessage: null,
isLoading: true,
});
await view.webContents.loadURL(hostReplacedUrl);
logger.debug('loadInitialUrlWithCatch() await loadURL() done');
const unregisterContextMenu = await this.menuService.initContextMenuForWindowWebContents(view.webContents);

View file

@ -128,6 +128,7 @@ export default function setupViewEventHandlers(
return;
}
await workspaceService.updateMetaData(workspace.id, {
isLoading: false,
didFailLoadErrorMessage: `${errorCode} ${errorDesc}`,
});
if (workspaceObject.active && browserWindow !== undefined && !browserWindow.isDestroyed()) {

View file

@ -19,7 +19,7 @@ import type { IWorkspaceService, IWorkspace } from '@services/workspaces/interfa
import type { IGitService, IGitUserInfos } from '@services/git/interface';
import type { IWorkspaceViewService } from '@services/workspacesView/interface';
import { WindowNames } from '@services/windows/WindowProperties';
import { logger, wikiOutputToFile, refreshOutputFile } from '@services/libs/log';
import { logger, wikiOutputToFile, refreshOutputFile, getWikiLogFilePath } from '@services/libs/log';
import i18n from '@services/libs/i18n';
import { lazyInject } from '@services/container';
import { TIDDLYWIKI_TEMPLATE_FOLDER_PATH, TIDDLERS_PATH } from '@/constants/paths';
@ -636,4 +636,13 @@ export class Wiki implements IWikiService {
}
}
}
public async getWikiLogs(homePath: string): Promise<{ content: string; filePath: string }> {
const filePath = getWikiLogFilePath(homePath);
const content = await fs.readFile(filePath, 'utf-8');
return {
content,
filePath,
};
}
}

View file

@ -51,6 +51,7 @@ export interface IWikiService {
createSubWiki(parentFolderLocation: string, folderName: string, mainWikiPath: string, tagName?: string, onlyLink?: boolean): Promise<void>;
ensureWikiExist(wikiPath: string, shouldBeMainWiki: boolean): Promise<void>;
getSubWikiPluginContent(mainWikiPath: string): Promise<ISubWikiPluginContent[]>;
getWikiLogs(homePath: string): Promise<{ content: string; filePath: string }>;
linkWiki(mainWikiPath: string, folderName: string, subWikiPath: string): Promise<void>;
/**
* Open image or PDF in OS native viewer or some else usage like this.
@ -91,6 +92,7 @@ export const WikiServiceIPCDescriptor = {
createSubWiki: ProxyPropertyType.Function,
ensureWikiExist: ProxyPropertyType.Function,
getSubWikiPluginContent: ProxyPropertyType.Function,
getWikiLogs: ProxyPropertyType.Function,
linkWiki: ProxyPropertyType.Function,
openTiddlerInExternal: ProxyPropertyType.Function,
removeWiki: ProxyPropertyType.Function,

View file

@ -5,7 +5,6 @@ import tiddlywiki, { I$TW } from '@tiddlygit/tiddlywiki';
import { Observable } from 'rxjs';
import intercept from 'intercept-stdout';
import { Server } from 'http';
import { shell } from 'electron';
import { IWikiMessage, WikiControlActions } from './interface';
import { defaultServerIP } from '@/constants/urls';