Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
adamhaeger committed Sep 18, 2024
2 parents 1d70edc + f5084af commit 53527d1
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 143 deletions.
22 changes: 20 additions & 2 deletions src/components/ReadyForPrint.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import React from 'react';

import { useDataLoadingStore } from 'src/core/contexts/dataLoadingContext';
import type { DataLoading } from 'src/core/contexts/dataLoadingContext';

/**
* This element only serves to let our PDF generator know the app is ready and have rendered its content.
* It should be included in the app DOM for every possible execution path, except those where we're showing
* loading indicators to the user while waiting for content to get ready.
*/
export function ReadyForPrint() {
const [assetsLoaded, setAssetsLoaded] = React.useState(false);
const dataLoadingIsDone = useDataLoadingStore((state) => state.isDone);

React.useLayoutEffect(() => {
if (assetsLoaded) {
return;
}

const dataPromise = waitForDataLoading(dataLoadingIsDone);
const imagePromise = waitForImages();
const fontPromise = document.fonts.ready;

Promise.all([imagePromise, fontPromise]).then(() => {
Promise.all([imagePromise, fontPromise, dataPromise]).then(() => {
setAssetsLoaded(true);
});
}, []);
}, [assetsLoaded, dataLoadingIsDone]);

if (!assetsLoaded) {
return null;
Expand Down Expand Up @@ -57,3 +66,12 @@ async function waitForImages() {
});
} while (nodes.some((node) => !node.complete));
}

async function waitForDataLoading(dataLoadingIsDone: DataLoading['isDone']) {
let done: boolean = dataLoadingIsDone();

while (!done) {
await new Promise((r) => setTimeout(r, 1000));
done = dataLoadingIsDone();
}
}
42 changes: 42 additions & 0 deletions src/core/contexts/dataLoadingContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { createContext, useContext } from 'react';

import { create } from 'zustand';

export interface DataLoading {
dataElements: Record<string, boolean>;
isDone: () => boolean;
setDataElements: (dataElements: Record<string, boolean>) => void;
}

export const createDataLoadingStore = () =>
create<DataLoading>((set, state) => ({
dataElements: {},
isDone() {
return Object.values(state().dataElements).every((v) => v === true);
},
setDataElements: (newDataElements: Record<string, boolean>) => {
set((state) => ({
dataElements: {
...state.dataElements,
...newDataElements,
},
}));
},
}));

const StoreContext = createContext<ReturnType<typeof createDataLoadingStore> | null>(null);

export function DataLoadingProvider({ children }: React.PropsWithChildren) {
const store = createDataLoadingStore();

return <StoreContext.Provider value={store}>{children}</StoreContext.Provider>;
}

export const useDataLoadingStore = <T,>(selector: (state: DataLoading) => T) => {
const store = useContext(StoreContext);
if (!store) {
throw new Error('useDataLoadingStore must be used within a DataLoadingProvider');
}

return store(selector);
};
15 changes: 9 additions & 6 deletions src/features/instance/InstanceContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { AxiosError } from 'axios';

import { useAppQueries } from 'src/core/contexts/AppQueriesProvider';
import { createContext } from 'src/core/contexts/context';
import { DataLoadingProvider } from 'src/core/contexts/dataLoadingContext';
import { DisplayError } from 'src/core/errorHandling/DisplayError';
import { Loader } from 'src/core/loading/Loader';
import { ProcessProvider } from 'src/features/instance/ProcessContext';
Expand Down Expand Up @@ -72,12 +73,14 @@ export const InstanceProvider = ({ children }: { children: React.ReactNode }) =>
}

return (
<InnerInstanceProvider
partyId={partyId}
instanceGuid={instanceGuid}
>
{children}
</InnerInstanceProvider>
<DataLoadingProvider>
<InnerInstanceProvider
partyId={partyId}
instanceGuid={instanceGuid}
>
{children}
</InnerInstanceProvider>
</DataLoadingProvider>
);
};

Expand Down
179 changes: 91 additions & 88 deletions src/features/instantiate/containers/PartySelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Grid, makeStyles, Typography } from '@material-ui/core';
import { PlusIcon } from '@navikt/aksel-icons';

import { AltinnParty } from 'src/components/altinnParty';
import { DataLoadingProvider } from 'src/core/contexts/dataLoadingContext';
import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider';
import { InstantiationContainer } from 'src/features/instantiate/containers/InstantiationContainer';
import { Lang } from 'src/features/language/Lang';
Expand Down Expand Up @@ -226,110 +227,112 @@ export const PartySelection = () => {
};

return (
<InstantiationContainer>
<Grid
container={true}
direction='row'
style={{
display: 'flex',
flexDirection: 'row',
}}
>
<Typography
variant='h1'
className={classes.partySelectionTitle}
>
{langAsString('party_selection.header')}
</Typography>
{templateErrorMessage()}
</Grid>
<Grid
container={true}
direction='column'
className={classes.partySearchFieldContainer}
>
<Textfield
aria-label={langAsString('party_selection.search_placeholder')}
placeholder={langAsString('party_selection.search_placeholder')}
onChange={onFilterStringChange}
value={filterString}
inputMode='search'
/>
</Grid>
<Grid
container={true}
direction='column'
>
<DataLoadingProvider>
<InstantiationContainer>
<Grid
container={true}
justifyContent='space-between'
direction='row'
style={{
display: 'flex',
flexDirection: 'row',
}}
>
<Grid item={true}>
<Typography className={classes.partySelectionSubTitle}>
{langAsString('party_selection.subheader')}
</Typography>
</Grid>
<Typography
variant='h1'
className={classes.partySelectionTitle}
>
{langAsString('party_selection.header')}
</Typography>
{templateErrorMessage()}
</Grid>
<Grid
container={true}
direction='column'
className={classes.partySearchFieldContainer}
>
<Textfield
aria-label={langAsString('party_selection.search_placeholder')}
placeholder={langAsString('party_selection.search_placeholder')}
onChange={onFilterStringChange}
value={filterString}
inputMode='search'
/>
</Grid>
<Grid
container={true}
direction='column'
>
<Grid
container={true}
justifyContent='space-between'
direction='row'
>
<Grid item={true}>
<Typography className={classes.partySelectionSubTitle}>
{langAsString('party_selection.subheader')}
</Typography>
</Grid>

<Grid item={true}>
<Grid
container={true}
direction='row'
>
<Grid item={true}>
<Grid
item={true}
className={classes.partySelectionCheckbox}
container={true}
direction='row'
>
<Grid
container={true}
direction='row'
item={true}
className={classes.partySelectionCheckbox}
>
<LegacyCheckbox
checked={showDeleted}
onChange={toggleShowDeleted}
label={langAsString('party_selection.show_deleted')}
/>
<Grid
container={true}
direction='row'
>
<LegacyCheckbox
checked={showDeleted}
onChange={toggleShowDeleted}
label={langAsString('party_selection.show_deleted')}
/>
</Grid>
</Grid>
</Grid>
<Grid
item={true}
className={classes.partySelectionCheckbox}
>
<Grid
container={true}
direction='row'
item={true}
className={classes.partySelectionCheckbox}
>
<LegacyCheckbox
checked={showSubUnits}
onChange={toggleShowSubUnits}
label={langAsString('party_selection.show_sub_unit')}
/>
<Grid
container={true}
direction='row'
>
<LegacyCheckbox
checked={showSubUnits}
onChange={toggleShowSubUnits}
label={langAsString('party_selection.show_sub_unit')}
/>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
{renderParties()}
{errorCode === 'explained' && (
<Grid style={{ padding: 12 }}>
<Typography
variant='h2'
style={{ fontSize: '1.5rem', fontWeight: '500', marginBottom: 12 }}
>
{langAsString('party_selection.why_seeing_this')}
</Typography>
<Typography variant='body1'>
<Lang
id={
appPromptForPartyOverride === 'always'
? 'party_selection.seeing_this_override'
: 'party_selection.seeing_this_preference'
}
/>
</Typography>
</Grid>
)}
</Grid>
{renderParties()}
{errorCode === 'explained' && (
<Grid style={{ padding: 12 }}>
<Typography
variant='h2'
style={{ fontSize: '1.5rem', fontWeight: '500', marginBottom: 12 }}
>
{langAsString('party_selection.why_seeing_this')}
</Typography>
<Typography variant='body1'>
<Lang
id={
appPromptForPartyOverride === 'always'
? 'party_selection.seeing_this_override'
: 'party_selection.seeing_this_preference'
}
/>
</Typography>
</Grid>
)}
</Grid>
</InstantiationContainer>
</InstantiationContainer>
</DataLoadingProvider>
);
};
5 changes: 4 additions & 1 deletion src/features/instantiate/selection/InstanceSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { DescriptionText } from '@altinn/altinn-design-system/dist/types/sr

import { PresentationComponent } from 'src/components/presentation/Presentation';
import { ReadyForPrint } from 'src/components/ReadyForPrint';
import { DataLoadingProvider } from 'src/core/contexts/dataLoadingContext';
import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider';
import { useInstantiation } from 'src/features/instantiate/InstantiationContext';
import {
Expand Down Expand Up @@ -40,7 +41,9 @@ function getDateDisplayString(timeStamp: string) {
export const InstanceSelectionWrapper = () => (
<ActiveInstancesProvider>
<PresentationComponent type={ProcessTaskType.Unknown}>
<InstanceSelection />
<DataLoadingProvider>
<InstanceSelection />
</DataLoadingProvider>
</PresentationComponent>
</ActiveInstancesProvider>
);
Expand Down
Loading

0 comments on commit 53527d1

Please sign in to comment.