Create a dedicated contentLoading action that keeps old content

This change makes sure that the content is removed when an error occurs such as a link to a missing page. It also stills shows the old content while the new one is loaded. The (delayed) loading indicator still shows that new content is loaded.

Signed-off-by: Dominik Henneke <dominik.henneke@sda-se.com>
This commit is contained in:
Dominik Henneke
2021-07-15 14:05:51 +02:00
parent 378cc6a54b
commit 3d3c43f0a7
3 changed files with 58 additions and 15 deletions
@@ -96,6 +96,10 @@ export const Reader = ({ entityId, onReady }: Props) => {
useEffect(() => {
if (!rawPage || !shadowDomRef.current) {
// clear the shadow dom if no content is available
if (shadowDomRef.current?.shadowRoot) {
shadowDomRef.current.shadowRoot.innerHTML = '';
}
return;
}
if (onReady) {
@@ -102,11 +102,13 @@ describe('useReaderState', () => {
});
it.each`
type | oldActiveSyncState | newActiveSyncState
${'content'} | ${'BUILD_READY'} | ${'UP_TO_DATE'}
${'content'} | ${'BUILD_READY_RELOAD'} | ${'UP_TO_DATE'}
${'sync'} | ${'BUILD_READY'} | ${undefined}
${'sync'} | ${'BUILD_READY_RELOAD'} | ${undefined}
type | oldActiveSyncState | newActiveSyncState
${'contentLoading'} | ${'BUILD_READY'} | ${'UP_TO_DATE'}
${'contentLoading'} | ${'BUILD_READY_RELOAD'} | ${'UP_TO_DATE'}
${'content'} | ${'BUILD_READY'} | ${'UP_TO_DATE'}
${'content'} | ${'BUILD_READY_RELOAD'} | ${'UP_TO_DATE'}
${'sync'} | ${'BUILD_READY'} | ${undefined /* undefined, because we don't set an input */}
${'sync'} | ${'BUILD_READY_RELOAD'} | ${undefined /* undefined, because we don't set an input */}
`(
'should, when type=$type and activeSyncState=$oldActiveSyncState, set activeSyncState=$newActiveSyncState',
({ type, oldActiveSyncState, newActiveSyncState }) => {
@@ -122,18 +124,45 @@ describe('useReaderState', () => {
},
);
describe('"content" action', () => {
describe('"contentLoading" action', () => {
it('should set loading', () => {
expect(
reducer(oldState, {
type: 'contentLoading',
}),
).toEqual({
...oldState,
contentLoading: true,
});
});
it('should keep content', () => {
expect(
reducer(
{
...oldState,
content: 'some-old-content',
},
{
type: 'contentLoading',
},
),
).toEqual({
...oldState,
contentLoading: true,
content: 'some-old-content',
});
});
it('should reset errors', () => {
expect(
reducer(
{
...oldState,
contentError: new Error(),
},
{
type: 'content',
contentLoading: true,
type: 'contentLoading',
},
),
).toEqual({
@@ -141,7 +170,9 @@ describe('useReaderState', () => {
contentLoading: true,
});
});
});
describe('"content" action', () => {
it('should set content', () => {
expect(
reducer(
@@ -457,7 +488,7 @@ describe('useReaderState', () => {
expect(result.current).toEqual({
state: 'CHECKING',
path: '/example',
content: undefined,
content: 'my content',
contentErrorMessage: undefined,
syncErrorMessage: undefined,
buildLog: [],
@@ -538,7 +569,7 @@ describe('useReaderState', () => {
expect(result.current).toEqual({
state: 'CHECKING',
path: '/example',
content: undefined,
content: 'my content',
contentErrorMessage: undefined,
syncErrorMessage: undefined,
buildLog: [],
@@ -131,11 +131,11 @@ type ReducerActions =
state: SyncStates;
syncError?: Error;
}
| { type: 'contentLoading' }
| {
type: 'content';
path?: string;
content?: string;
contentLoading?: true;
contentError?: Error;
}
| { type: 'buildLog'; log: string };
@@ -186,13 +186,21 @@ export function reducer(
newState.syncError = action.syncError;
break;
case 'contentLoading':
newState.contentLoading = true;
// only reset errors but keep the old content until it is replaced by the 'content' action
newState.contentError = undefined;
break;
case 'content':
// only override the path if it is part of the action
if (typeof action.path === 'string') {
newState.path = action.path;
}
newState.contentLoading = false;
newState.content = action.content;
newState.contentLoading = action.contentLoading ?? false;
newState.contentError = action.contentError;
break;
@@ -204,10 +212,10 @@ export function reducer(
throw new Error();
}
// a navigation or a content update loads fresh content so the build is updated to being up-to-date
// a content update loads fresh content so the build is updated to being up-to-date
if (
['BUILD_READY', 'BUILD_READY_RELOAD'].includes(newState.activeSyncState) &&
['content'].includes(action.type)
['contentLoading', 'content'].includes(action.type)
) {
newState.activeSyncState = 'UP_TO_DATE';
newState.buildLog = [];
@@ -241,7 +249,7 @@ export function useReaderState(
// try to load the content. the function will fire events and we don't care for the return values
const { retry: contentReload } = useAsyncRetry(async () => {
dispatch({ type: 'content', contentLoading: true });
dispatch({ type: 'contentLoading' });
try {
const entityDocs = await techdocsStorageApi.getEntityDocs(