Skip to content

Commit

Permalink
Fishing-map/report-errors (#2785)
Browse files Browse the repository at this point in the history
  • Loading branch information
j8seangel authored Aug 16, 2024
2 parents 764ada6 + fdff266 commit 599b801
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 142 deletions.
25 changes: 11 additions & 14 deletions apps/fishing-map/features/download/DownloadActivityByVessel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
selectIsDownloadActivityError,
DateRange,
selectDownloadActivityAreaKey,
selectIsDownloadAreaTooBig,
selectIsDownloadActivityTimeoutError,
} from 'features/download/downloadActivity.slice'
import { EMPTY_FIELD_PLACEHOLDER } from 'utils/info'
import { TimelineDatesRange } from 'features/map/controls/MapInfo'
Expand Down Expand Up @@ -56,6 +56,7 @@ import {
getSupportedGroupByOptions,
getSupportedTemporalResolutions,
} from './download.utils'
import ActivityDownloadError, { useActivityDownloadTimeoutRefresh } from './DownloadActivityError'

function DownloadActivityByVessel() {
const { t } = useTranslation()
Expand All @@ -71,7 +72,7 @@ function DownloadActivityByVessel() {
const isDownloadLoading = useSelector(selectIsDownloadActivityLoading)
const isDownloadError = useSelector(selectIsDownloadActivityError)
const isDownloadFinished = useSelector(selectIsDownloadActivityFinished)
const isDownloadAreaTooBig = useSelector(selectIsDownloadAreaTooBig)
const isDownloadTimeoutError = useSelector(selectIsDownloadActivityTimeoutError)
const [format, setFormat] = useState(VESSEL_FORMAT_OPTIONS[0].id)
const isDownloadReportSupported = getDownloadReportSupported(start, end)
const downloadAreaKey = useSelector(selectDownloadActivityAreaKey)
Expand Down Expand Up @@ -146,7 +147,7 @@ function DownloadActivityByVessel() {
bufferValue,
bufferOperation,
}
await dispatch(downloadActivityThunk(downloadParams))
const action = await dispatch(downloadActivityThunk(downloadParams))

trackEvent({
category: TrackCategory.DataDownloads,
Expand All @@ -158,9 +159,14 @@ function DownloadActivityByVessel() {
.flat(),
]),
})
return action
}

useActivityDownloadTimeoutRefresh(onDownloadClick)

const parsedLabel =
typeof downloadAreaName === 'string' ? parse(downloadAreaName) : downloadAreaName

return (
<Fragment>
<div className={styles.container} data-test="download-activity-byvessel">
Expand Down Expand Up @@ -226,20 +232,11 @@ function DownloadActivityByVessel() {
))}
</p>
) : null}
{isDownloadError && (
<p className={cx(styles.footerLabel, styles.error)}>
{isDownloadAreaTooBig
? `${t(
'analysis.errorTooComplex',
'The geometry of the area is too complex to perform a report, try to simplify and upload again.'
)}`
: `${t('analysis.errorMessage', 'Something went wrong')} 🙈`}
</p>
)}
{isDownloadError && <ActivityDownloadError />}
<Button
testId="download-activity-vessel-button"
onClick={onDownloadClick}
loading={isDownloadLoading}
loading={isDownloadLoading || isDownloadTimeoutError}
className={styles.downloadBtn}
disabled={!isDownloadReportSupported || isDownloadAreaLoading || isDownloadError}
>
Expand Down
18 changes: 5 additions & 13 deletions apps/fishing-map/features/download/DownloadActivityEnvironment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
selectIsDownloadActivityError,
DateRange,
selectDownloadActivityAreaKey,
selectIsDownloadAreaTooBig,
} from 'features/download/downloadActivity.slice'
import { EMPTY_FIELD_PLACEHOLDER } from 'utils/info'
import { TimelineDatesRange } from 'features/map/controls/MapInfo'
Expand Down Expand Up @@ -50,6 +49,7 @@ import {
ENVIRONMENT_FORMAT_OPTIONS,
} from './downloadActivity.config'
import { getDownloadReportSupported, getSupportedTemporalResolutions } from './download.utils'
import ActivityDownloadError, { useActivityDownloadTimeoutRefresh } from './DownloadActivityError'

function DownloadActivityGridded() {
const { t } = useTranslation()
Expand All @@ -60,7 +60,6 @@ function DownloadActivityGridded() {
const isDownloadLoading = useSelector(selectIsDownloadActivityLoading)
const isDownloadError = useSelector(selectIsDownloadActivityError)
const isDownloadFinished = useSelector(selectIsDownloadActivityFinished)
const isDownloadAreaTooBig = useSelector(selectIsDownloadAreaTooBig)
const [format, setFormat] = useState(GRIDDED_FORMAT_OPTIONS[0].id)

const downloadArea = useSelector(selectDownloadActivityArea)
Expand Down Expand Up @@ -164,7 +163,7 @@ function DownloadActivityGridded() {
bufferValue,
bufferOperation,
}
await dispatch(downloadActivityThunk(downloadParams))
const action = await dispatch(downloadActivityThunk(downloadParams))

trackEvent({
category: TrackCategory.DataDownloads,
Expand All @@ -176,7 +175,9 @@ function DownloadActivityGridded() {
.flat(),
]),
})
return action.payload
}
useActivityDownloadTimeoutRefresh(onDownloadClick)

const isDownloadReportSupported = getDownloadReportSupported(start, end)
const parsedLabel =
Expand Down Expand Up @@ -236,16 +237,7 @@ function DownloadActivityGridded() {
{t('download.timerangeTooLong', 'The maximum time range is 1 year')}
</p>
)}
{isDownloadError && (
<p className={cx(styles.footerLabel, styles.error)}>
{isDownloadAreaTooBig
? `${t(
'analysis.errorTooComplex',
'The geometry of the area is too complex to perform a report, try to simplify and upload again.'
)}`
: `${t('analysis.errorMessage', 'Something went wrong')} 🙈`}
</p>
)}
{isDownloadError && <ActivityDownloadError />}
<Button
testId="download-activity-gridded-button"
onClick={onDownloadClick}
Expand Down
55 changes: 55 additions & 0 deletions apps/fishing-map/features/download/DownloadActivityError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useSelector } from 'react-redux'
import cx from 'classnames'
import { useTranslation } from 'react-i18next'
import { useEffect } from 'react'
import {
selectDownloadActivityErrorMsg,
selectIsDownloadActivityConcurrentError,
selectIsDownloadActivityTimeoutError,
selectIsDownloadAreaTooBig,
} from './downloadActivity.slice'
import styles from './DownloadModal.module.css'

export function useActivityDownloadTimeoutRefresh(callback: () => void) {
const isDownloadTimeoutError = useSelector(selectIsDownloadActivityTimeoutError)

useEffect(() => {
if (isDownloadTimeoutError) {
const timeout = setTimeout(() => {
callback()
}, 5000)

return () => clearTimeout(timeout)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDownloadTimeoutError])
}

function ActivityDownloadError() {
const { t } = useTranslation()
const apiDownloadErrorMsg = useSelector(selectDownloadActivityErrorMsg)
const isDownloadAreaTooBig = useSelector(selectIsDownloadAreaTooBig)
const isDownloadTimeoutError = useSelector(selectIsDownloadActivityTimeoutError)
const isDownloadConcurrentError = useSelector(selectIsDownloadActivityConcurrentError)

let downloadErrorMsg = apiDownloadErrorMsg
if (isDownloadConcurrentError) {
downloadErrorMsg = t(
'download.errorConcurrentReport',
'Your account is currently loading an analysis report, please wait for the report to finish before downloading data'
)
} else if (isDownloadTimeoutError) {
downloadErrorMsg = t('analysis.timeoutError', 'This is taking more than expected, please wait')
} else if (isDownloadAreaTooBig) {
downloadErrorMsg = `${t(
'analysis.errorTooComplex',
'The geometry of the area is too complex to perform a report, try to simplify and upload again.'
)}`
} else if (!downloadErrorMsg) {
downloadErrorMsg = `${t('analysis.errorMessage', 'Something went wrong')} 🙈`
}

return <p className={cx(styles.footerLabel, styles.error)}>{downloadErrorMsg}</p>
}

export default ActivityDownloadError
19 changes: 5 additions & 14 deletions apps/fishing-map/features/download/DownloadActivityGridded.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
selectIsDownloadActivityFinished,
selectIsDownloadActivityError,
selectDownloadActivityAreaKey,
selectIsDownloadAreaTooBig,
} from 'features/download/downloadActivity.slice'
import { EMPTY_FIELD_PLACEHOLDER } from 'utils/info'
import { TimelineDatesRange } from 'features/map/controls/MapInfo'
Expand Down Expand Up @@ -60,6 +59,7 @@ import {
getSupportedGroupByOptions,
getSupportedTemporalResolutions,
} from './download.utils'
import ActivityDownloadError, { useActivityDownloadTimeoutRefresh } from './DownloadActivityError'

function DownloadActivityGridded() {
const { t } = useTranslation()
Expand All @@ -73,7 +73,6 @@ function DownloadActivityGridded() {
userData?.permissions || []
)
const isDownloadLoading = useSelector(selectIsDownloadActivityLoading)
const isDownloadAreaTooBig = useSelector(selectIsDownloadAreaTooBig)
const isDownloadError = useSelector(selectIsDownloadActivityError)
const isDownloadFinished = useSelector(selectIsDownloadActivityFinished)
const [format, setFormat] = useState(GRIDDED_FORMAT_OPTIONS[0].id)
Expand Down Expand Up @@ -188,7 +187,7 @@ function DownloadActivityGridded() {
bufferValue,
bufferOperation,
}
await dispatch(downloadActivityThunk(downloadParams))
const action = await dispatch(downloadActivityThunk(downloadParams))

trackEvent({
category: TrackCategory.DataDownloads,
Expand All @@ -200,8 +199,9 @@ function DownloadActivityGridded() {
.flat(),
]),
})
return action
}

useActivityDownloadTimeoutRefresh(onDownloadClick)
const isDownloadReportSupported = getDownloadReportSupported(start, end)
const parsedLabel =
typeof downloadAreaName === 'string' ? parse(downloadAreaName) : downloadAreaName
Expand Down Expand Up @@ -285,16 +285,7 @@ function DownloadActivityGridded() {
))}
</p>
) : null}
{isDownloadError && (
<p className={cx(styles.footerLabel, styles.error)}>
{isDownloadAreaTooBig
? `${t(
'analysis.errorTooComplex',
'The geometry of the area is too complex to perform a report, try to simplify and upload again.'
)}`
: `${t('analysis.errorMessage', 'Something went wrong')} 🙈`}
</p>
)}
{isDownloadError && <ActivityDownloadError />}
<Button
testId="download-activity-gridded-button"
onClick={onDownloadClick}
Expand Down
39 changes: 31 additions & 8 deletions apps/fishing-map/features/download/downloadActivity.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { stringify } from 'qs'
import { saveAs } from 'file-saver'
import { RootState } from 'reducers'
import { Dataview, DownloadActivity } from '@globalfishingwatch/api-types'
import { GFWAPI, parseAPIError } from '@globalfishingwatch/api-client'
import {
getIsConcurrentError,
getIsTimeoutError,
GFWAPI,
parseAPIError,
} from '@globalfishingwatch/api-client'
import { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client'
import { AsyncError, AsyncReducerStatus } from 'utils/async-slice'
import { AreaKeyId, AreaKeys } from 'features/areas/areas.slice'
Expand All @@ -25,15 +30,15 @@ export type DateRange = {
interface DownloadActivityState {
areaKey: AreaKeys | undefined
areaDataview: Dataview | UrlDataviewInstance | undefined
error: string
error: AsyncError | undefined
status: AsyncReducerStatus
activeTabId: HeatmapDownloadTab
}

const initialState: DownloadActivityState = {
areaKey: undefined,
areaDataview: undefined,
error: '',
error: undefined,
status: AsyncReducerStatus.Idle,
activeTabId: HeatmapDownloadTab.ByVessel,
}
Expand Down Expand Up @@ -121,6 +126,12 @@ export const downloadActivityThunk = createAsyncThunk<
console.warn(e)
return rejectWithValue(parseAPIError(e))
}
},
{
condition: (_, { getState }) => {
const { downloadActivity } = getState() as RootState
return downloadActivity?.status !== AsyncReducerStatus.Loading
},
}
)

Expand All @@ -142,7 +153,7 @@ const downloadActivitySlice = createSlice({
extraReducers: (builder) => {
builder.addCase(downloadActivityThunk.pending, (state) => {
state.status = AsyncReducerStatus.Loading
state.error = ''
state.error = undefined
})
builder.addCase(downloadActivityThunk.fulfilled, (state) => {
state.status = AsyncReducerStatus.Finished
Expand All @@ -151,7 +162,7 @@ const downloadActivitySlice = createSlice({
state.status =
action.error.message === 'Aborted' ? AsyncReducerStatus.Aborted : AsyncReducerStatus.Error
if (action.payload?.message) {
state.error = action.payload?.message
state.error = action.payload
}
})
},
Expand All @@ -161,7 +172,9 @@ export const { setDownloadActiveTab, setDownloadActivityAreaKey, resetDownloadAc
downloadActivitySlice.actions

const selectDownloadActivityStatus = (state: RootState) => state.downloadActivity.status
const selectDownloadActivityErrorMsg = (state: RootState) => state.downloadActivity.error
export const selectDownloadActivityError = (state: RootState) => state.downloadActivity.error
export const selectDownloadActivityErrorMsg = (state: RootState) =>
state.downloadActivity.error?.message
export const selectDownloadActivityAreaKey = (state: RootState) => state.downloadActivity.areaKey
export const selectDownloadActiveTabId = (state: RootState) => state.downloadActivity.activeTabId

Expand All @@ -181,8 +194,18 @@ export const selectIsDownloadActivityFinished = createSelector(
)

export const selectIsDownloadAreaTooBig = createSelector(
[selectDownloadActivityErrorMsg],
(message) => message === 'Geometry too large'
[selectDownloadActivityError],
(downloadError) => downloadError?.message === 'Geometry too large'
)

export const selectIsDownloadActivityTimeoutError = createSelector(
[selectDownloadActivityError],
(downloadError) => getIsTimeoutError(downloadError)
)

export const selectIsDownloadActivityConcurrentError = createSelector(
[selectDownloadActivityError],
(downloadError) => getIsConcurrentError(downloadError)
)

export default downloadActivitySlice.reducer
12 changes: 7 additions & 5 deletions apps/fishing-map/features/reports/Report.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { useSelector } from 'react-redux'
import { uniq } from 'es-toolkit'
import isEqual from 'lodash/isEqual'
import { Button, Tab, Tabs } from '@globalfishingwatch/ui-components'
import { crossBrowserTypeErrorMessages, isAuthError } from '@globalfishingwatch/api-client'
import {
getIsConcurrentError,
getIsTimeoutError,
isAuthError,
} from '@globalfishingwatch/api-client'
import { useLocalStorage } from '@globalfishingwatch/react-hooks'
import { ContextFeature } from '@globalfishingwatch/deck-layers'
import { AsyncReducerStatus } from 'utils/async-slice'
Expand Down Expand Up @@ -119,13 +123,11 @@ function ActivityReport({ reportName }: { reportName: string }) {
return isEqual(currentReportParams, reportParams)
})
: undefined
const concurrentReportError = statusError?.status === 429
const concurrentReportError = getIsConcurrentError(statusError!)
const isSameWorkspaceReport =
concurrentReportError && window?.location.href === lastReport?.workspaceUrl

const isTimeoutError =
statusError?.message &&
crossBrowserTypeErrorMessages.some((error) => error.includes(statusError.message as string))
const isTimeoutError = getIsTimeoutError(statusError!)
useEffect(() => {
if (isTimeoutError) {
dispatchTimeoutRef.current = setTimeout(() => {
Expand Down
2 changes: 1 addition & 1 deletion apps/fishing-map/features/workspace/workspace.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ interface WorkspaceSliceState {
const initialState: WorkspaceSliceState = {
status: AsyncReducerStatus.Idle,
customStatus: AsyncReducerStatus.Idle,
error: {},
error: {} as AsyncError,
data: null,
password: '',
lastVisited: undefined,
Expand Down
1 change: 1 addition & 0 deletions apps/fishing-map/public/locales/source/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@
"descriptionReport": "You can download a list of the activity or a heat map bitmap for this area in different formats",
"downloadOptions": "Download options",
"doYouNeedAnAPI": "Does your application need continuous data?",
"errorConcurrentReport": "Your account is currently loading an analysis report, please wait for the report to finish before downloading data",
"eventsDownloadLogin": "Register and login to download vessel events (it is free and takes 2 minutes)",
"format": "Format",
"fullDataset": "Do you need the full dataset?",
Expand Down
Loading

0 comments on commit 599b801

Please sign in to comment.