Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fishing-map/report-errors #2785

Merged
merged 2 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading