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:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user