Skip to content

Commit

Permalink
feat: add webworker and resolve timeout issue on loading images
Browse files Browse the repository at this point in the history
  • Loading branch information
maximeperrault committed Jan 29, 2025
1 parent 5b53880 commit d47f19e
Show file tree
Hide file tree
Showing 17 changed files with 590 additions and 432 deletions.
468 changes: 274 additions & 194 deletions frontend/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@tanstack/react-table": "8.20.6",
"@tanstack/react-virtual": "3.11.2",
"classnames": "2.5.1",
"comlink": "4.4.2",
"compressorjs": "1.2.1",
"dayjs": "1.11.13",
"deep-object-diff": "1.1.9",
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/features/Dashboard/components/Pdf/renderPdf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { pdf } from '@react-pdf/renderer'
import { createElement } from 'react'

import { Brief as BriefV1, type BriefProps as BriefPropsV1 } from './v1/Brief'
import { Brief, type BriefProps } from './v2/Brief'

export const renderPDF = async (props: BriefProps) => pdf(createElement(Brief, props)).toBlob()

export const renderPDFV1 = async (props: BriefPropsV1) => pdf(createElement(BriefV1, props)).toBlob()
9 changes: 6 additions & 3 deletions frontend/src/features/Dashboard/components/Pdf/v1/Brief.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@ import { VigilanceAreas } from './VigilanceAreas'

import type { Dashboard } from '@features/Dashboard/types'

type BriefProps = {
export type BriefProps = {
author?: string
brief: Dashboard.Brief
description?: string
title?: string
}

registerFonts()

export function Brief({ brief }: BriefProps) {
export function Brief({ author, brief, description, title }: BriefProps) {
return (
<Document>
<Document author={author} subject={description} title={title}>
<Page style={layoutStyle.page}>
<Headings name={brief.name} />
<View style={layoutStyle.section}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ import { getVigilanceAreasByIds } from '@api/vigilanceAreasAPI'
import { useAppSelector } from '@hooks/useAppSelector'
import { useGetControlPlans } from '@hooks/useGetControlPlans'
import { Button, Icon } from '@mtes-mct/monitor-ui'
import { usePDF } from '@react-pdf/renderer'
import { useEffect, useMemo, useState } from 'react'
import styled from 'styled-components'

import { Brief } from './Brief'
import { MonitorEnvWebWorker } from 'workers/MonitorEnvWebWorker'

import type { Dashboard } from '@features/Dashboard/types'

Expand All @@ -19,7 +17,9 @@ type GeneratePdfButtonProps = {
}

export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) {
const [isGenerating, setIsGenerating] = useState(false)
const [shouldTriggerExport, setShouldTriggerExport] = useState(false)
const [isOpening, setIsOpening] = useState(false)

const { subThemes, themes } = useGetControlPlans()

const controlUnits = useAppSelector(state => getControlUnitsByIds(state, dashboard.controlUnitIds))
Expand Down Expand Up @@ -61,6 +61,8 @@ export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) {
vigilanceAreas
}),
[
allLinkedAMPs,
allLinkedRegulatoryAreas,
amps,
dashboard.comments,
dashboard.name,
Expand All @@ -70,39 +72,52 @@ export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) {
reportings?.entities,
subThemes,
themes,
allLinkedAMPs,
vigilanceAreas,
allLinkedRegulatoryAreas
vigilanceAreas
]
)

const [pdf, update] = usePDF()

const handleDownload = () => {
setIsGenerating(true)

update(<Brief brief={brief} />)
const handleDownload = async () => {
setShouldTriggerExport(true)
}

useEffect(() => {
if (isGenerating && !pdf.loading && pdf.blob && pdf.url) {
setIsGenerating(false)
const renderPdf = async () => {
if (shouldTriggerExport) {
setIsOpening(true)

const monitorEnvWorker = await MonitorEnvWebWorker

const url = await monitorEnvWorker.renderPDFInWorkerV1({ brief })

if (url) {
const link = document.createElement('a')
link.href = url
link.download = `${dashboard.name}.pdf`
link.click()
link.remove()
URL.revokeObjectURL(url)
}
setShouldTriggerExport(false)
setIsOpening(false)
}
}
renderPdf()
}, [brief, dashboard.name, shouldTriggerExport])

const link = document.createElement('a')
link.href = pdf.url
link.download = `${dashboard.name}.pdf`
link.click()
const getLoadingText = () => {
if (isOpening) {
return 'Chargement du brief'
}
}, [isGenerating, pdf.loading, pdf.blob, pdf.url, dashboard.name])

return 'Générer un brief'
}

return (
<StyledLinkButton
disabled={pdf.loading || isGenerating}
Icon={pdf.loading || isGenerating ? Icon.Reset : Icon.Document}
onClick={handleDownload}
>
{pdf.loading || isGenerating ? 'Chargement du brief' : 'Générer un brief'}
</StyledLinkButton>
<>
<StyledLinkButton disabled={isOpening} Icon={isOpening ? Icon.Reset : Icon.Document} onClick={handleDownload}>
{getLoadingText()}
</StyledLinkButton>
</>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getTitle } from 'domain/entities/layers/utils'
import { areaStyle, layoutStyle } from '../style'
import { getImage } from '../utils'

import type { ExportImageType } from '../../../Layers/ExportLayer'
import type { ExportImageType } from '../../../../hooks/useExportImages'
import type { AMPFromAPI } from 'domain/entities/AMPs'

export function Amps({ amps, images }: { amps: AMPFromAPI[]; images: ExportImageType[] }) {
Expand Down
9 changes: 6 additions & 3 deletions frontend/src/features/Dashboard/components/Pdf/v2/Brief.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ import { VigilanceAreas } from './VigilanceAreas'

import type { Dashboard } from '@features/Dashboard/types'

type BriefProps = {
export type BriefProps = {
author?: string
brief: Dashboard.Brief
description?: string
title?: string
}

registerFonts()

export function Brief({ brief }: BriefProps) {
export function Brief({ author, brief, description, title }: BriefProps) {
return (
<Document>
<Document author={author} subject={description} title={title}>
<Page style={layoutStyle.page}>
<Headings name={brief.name} />
<View style={layoutStyle.section}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ import { getControlUnitsByIds } from '@api/controlUnitsAPI'
import { getRegulatoryAreasByIds } from '@api/regulatoryLayersAPI'
import { useGetReportingsByIdsQuery } from '@api/reportingsAPI'
import { getVigilanceAreasByIds } from '@api/vigilanceAreasAPI'
import { useAppDispatch } from '@hooks/useAppDispatch'
import { useAppSelector } from '@hooks/useAppSelector'
import { useGetControlPlans } from '@hooks/useGetControlPlans'
import { Button, Icon } from '@mtes-mct/monitor-ui'
import { usePDF } from '@react-pdf/renderer'
import { useEffect, useMemo, useState } from 'react'
import styled from 'styled-components'
import { MonitorEnvWebWorker } from 'workers/MonitorEnvWebWorker'

import { Brief } from './Brief'
import { ExportLayer, type ExportImageType } from '../../Layers/ExportLayer'
import { useExportImages } from '../../../hooks/useExportImages'

import type { Dashboard } from '@features/Dashboard/types'

Expand All @@ -21,10 +19,8 @@ type GeneratePdfButtonProps = {
}

export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) {
const dispatch = useAppDispatch()

const [isGenerating, setIsGenerating] = useState(false)
const [shouldLoadImage, setShouldLoadImage] = useState(false)
const [shouldTriggerExport, setShouldTriggerExport] = useState(false)
const [isOpening, setIsOpening] = useState(false)

const { subThemes, themes } = useGetControlPlans()

Expand All @@ -50,14 +46,16 @@ export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) {

const allLinkedAMPs = useAppSelector(state => getAmpsByIds(state, allLinkedAMPIds))

const { images, loading } = useExportImages({ triggerExport: shouldTriggerExport })

const brief: Dashboard.Brief = useMemo(
() => ({
allLinkedAMPs,
allLinkedRegulatoryAreas,
amps,
comments: dashboard.comments,
controlUnits,
images: [],
images,
name: dashboard.name,
regulatoryAreas,
reportings: Object.values(reportings?.entities ?? []),
Expand All @@ -74,6 +72,7 @@ export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) {
dashboard.name,
dashboard.updatedAt,
controlUnits,
images,
regulatoryAreas,
reportings?.entities,
subThemes,
Expand All @@ -82,39 +81,53 @@ export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) {
]
)

const [pdf, update] = usePDF()

const handleDownload = () => {
setIsGenerating(true)
setShouldLoadImage(true)
}

const updateBrief = (imagesToUpdate: ExportImageType[]) => {
update(<Brief brief={{ ...brief, images: imagesToUpdate }} />)
setShouldLoadImage(false)
const handleDownload = async () => {
setShouldTriggerExport(true)
}

useEffect(() => {
if (isGenerating && !shouldLoadImage && !pdf.loading && pdf.blob && pdf.url) {
setIsGenerating(false)

const link = document.createElement('a')
link.href = pdf.url
link.download = `${dashboard.name}.pdf`
link.click()
link.remove()
const renderPdf = async () => {
if (brief.images && !loading && shouldTriggerExport) {
setIsOpening(true)

const monitorEnvWorker = await MonitorEnvWebWorker

const url = await monitorEnvWorker.renderPDFInWorker({ brief })

if (url) {
const link = document.createElement('a')
link.href = url
link.download = `${dashboard.name}.pdf`
link.click()
link.remove()
URL.revokeObjectURL(url)
}
setShouldTriggerExport(false)
setIsOpening(false)
}
}
}, [dashboard.name, dispatch, isGenerating, pdf.blob, pdf.loading, pdf.url, shouldLoadImage])
renderPdf()
}, [brief, dashboard.name, loading, shouldTriggerExport])

const getLoadingText = () => {
if (loading) {
return 'Chargement des images'
}
if (isOpening) {
return 'Chargement du brief'
}

return 'Générer un brief'
}

return (
<>
<ExportLayer onImagesReady={updateBrief} shouldLoadImages={shouldLoadImage} />
<StyledLinkButton
disabled={isGenerating}
Icon={isGenerating ? Icon.Reset : Icon.Document}
disabled={loading || isOpening}
Icon={loading || isOpening ? Icon.Reset : Icon.Document}
onClick={handleDownload}
>
{isGenerating ? 'Chargement du brief' : 'Générer un brief'}
{getLoadingText()}
</StyledLinkButton>
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getTitle } from 'domain/entities/layers/utils'
import { areaStyle, layoutStyle } from '../style'
import { getImage } from '../utils'

import type { ExportImageType } from '../../../Layers/ExportLayer'
import type { ExportImageType } from '../../../../hooks/useExportImages'
import type { RegulatoryLayerWithMetadata } from 'domain/entities/regulatory'

export function RegulatoryAreas({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { StyleSheet, Text, View, Image } from '@react-pdf/renderer'

import { layoutStyle } from '../style'

import type { ExportImageType } from '../../../Layers/ExportLayer'
import type { ExportImageType } from '../../../../hooks/useExportImages'
import type { VigilanceArea } from '@features/VigilanceArea/types'
import type { AMPFromAPI } from 'domain/entities/AMPs'
import type { RegulatoryLayerWithMetadata } from 'domain/entities/regulatory'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Image, Link, Text, View } from '@react-pdf/renderer'
import { areaStyle, layoutStyle } from '../style'
import { getImage } from '../utils'

import type { ExportImageType } from '../../../Layers/ExportLayer'
import type { ExportImageType } from '../../../../hooks/useExportImages'
import type { AMPFromAPI } from 'domain/entities/AMPs'
import type { RegulatoryLayerWithMetadata } from 'domain/entities/regulatory'

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/Dashboard/components/Pdf/v2/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Dashboard } from '@features/Dashboard/types'

import type { ExportImageType } from '../../Layers/ExportLayer'
import type { ExportImageType } from '../../../hooks/useExportImages'

export function getImage(images: ExportImageType[], type: Dashboard.Layer, id: number | undefined): string | undefined {
return images.find(image => {
Expand Down
Loading

0 comments on commit d47f19e

Please sign in to comment.