Skip to content

Commit

Permalink
feat: Show toaster when load record fails
Browse files Browse the repository at this point in the history
  • Loading branch information
tohjustin committed Dec 23, 2019
1 parent 547c7bb commit 51c4e49
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 16 deletions.
16 changes: 13 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConnectedRouter } from "connected-react-router";
import { ThemeProvider } from "evergreen-ui";
import { ThemeProvider, toaster } from "evergreen-ui";
import React, { useEffect, useMemo } from "react";
import { BarChart2, Clock, Settings } from "react-feather";
import { hot } from "react-hot-loader";
Expand Down Expand Up @@ -31,14 +31,19 @@ import HistoryView from "./views/history";
import SettingsView from "./views/settings";

interface AppShellProps {
loadRecords: () => void;
loadRecords: (
onSuccess?: () => void,
onError?: (error: Error) => void
) => void;
isExportingDatabaseRecords: boolean;
isImportingDatabaseRecords: boolean;
searchParams: string;
selectedDomain: string | null;
selectedTimeRange: TimeRange;
}

const LOAD_TOASTER_ID = "app-shell-load-toaster";

const AppShell = ({
loadRecords,
isExportingDatabaseRecords,
Expand All @@ -54,7 +59,12 @@ const AppShell = ({
]).toString();
}, [searchParams]);
useEffect(() => {
loadRecords();
loadRecords(undefined, error => {
toaster.danger("Fail to load records", {
id: LOAD_TOASTER_ID,
description: error.message
});
});
}, [loadRecords, selectedDomain, selectedTimeRange]);

return (
Expand Down
14 changes: 13 additions & 1 deletion src/store/activity/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,19 @@ export const getIsInitialized = (state: RootState) =>
* Retrieves the status of loading activity records
*/
export const getIsLoadingRecords = (state: RootState) =>
state.activity.isLoading;
state.activity.isLoadingRecords;

/**
* Retrieves the error occurred from loading activity records
*/
export const getLoadingRecordsError = (state: RootState) =>
state.activity.loadingRecordsError;

/**
* Retrieves the success status from loading activity records
*/
export const getLoadingRecordsSuccess = (state: RootState) =>
state.activity.loadingRecordsSuccess;

/**
* Retrieves time range of all (fetched) activity records in store
Expand Down
43 changes: 32 additions & 11 deletions src/store/activity/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ export interface ActivityState {
/**
* Loading status of `loadRecords` thunk
*/
isLoading: boolean;
isLoadingRecords: boolean;
/**
* Error resulting from `loadRecords` thunk
* Error resulting from `loadRecords` thunks
*/
error: Error | null;
loadingRecordsError: Error | null;
/**
* Success status from `loadRecords` thunks
*/
loadingRecordsSuccess: boolean | null;
/**
* Map of all (fetched) domain records, keyed by domain value
*/
Expand All @@ -65,10 +69,11 @@ const INITIAL_STATE: ActivityState = {
deletingRecordsError: null,
deletingRecordsSuccess: null,
domains: {},
error: null,
isDeletingRecords: false,
isInitialized: false,
isLoading: false,
isLoadingRecords: false,
loadingRecordsError: null,
loadingRecordsSuccess: null,
records: [],
recordsTimeRange: null,
selectedTimeRange: DEFAULT_TIME_RANGE,
Expand Down Expand Up @@ -97,18 +102,21 @@ const activity = createSlice({
state.deletingRecordsSuccess = false;
},
getRecordsStart(state: ActivityState) {
state.isLoading = true;
state.isLoadingRecords = true;
state.loadingRecordsSuccess = null;
},
getRecordsSuccess(state, action: PayloadAction<Activity[]>) {
state.records = action.payload;
state.isInitialized = true;
state.isLoading = false;
state.error = null;
state.isLoadingRecords = false;
state.loadingRecordsError = null;
state.loadingRecordsSuccess = true;
},
getRecordsFailure(state: ActivityState, action: PayloadAction<Error>) {
state.isInitialized = true;
state.isLoading = false;
state.error = action.payload;
state.isLoadingRecords = false;
state.loadingRecordsError = action.payload;
state.loadingRecordsSuccess = false;
},
setDomains(state, action: PayloadAction<Record<string, Domain>>) {
state.domains = action.payload;
Expand All @@ -126,6 +134,8 @@ const activity = createSlice({
});

const loadRecords = (
onSuccess?: () => void,
onError?: (error: Error) => void,
options: { forceReload: boolean } = { forceReload: false }
): ThunkAction => async (dispatch, getState, { databaseService }) => {
const state = getState();
Expand All @@ -150,7 +160,7 @@ const loadRecords = (
dispatch(activity.actions.getRecordsStart());
try {
if (databaseService === undefined) {
throw Error("Unable to connect to DB");
throw Error("Unable to connect to database");
}

// Only fetch all domains & activity time range on initialization
Expand All @@ -161,6 +171,10 @@ const loadRecords = (
databaseService.fetchActivityRecords(requiredTimeRange)
]);

if (onSuccess) {
onSuccess();
}

// Batch actions to ensure smooth UI transition on store updates
batch(() => [
dispatch(activity.actions.getRecordsSuccess(records || [])),
Expand All @@ -174,6 +188,10 @@ const loadRecords = (
requiredTimeRange
);

if (onSuccess) {
onSuccess();
}

// Batch actions to ensure smooth UI transition on store updates
batch(() => [
dispatch(activity.actions.getRecordsSuccess(records || [])),
Expand All @@ -182,6 +200,9 @@ const loadRecords = (
]);
}
} catch (error) {
if (onError) {
onError(error);
}
dispatch(activity.actions.getRecordsFailure(error));
}
};
Expand Down
4 changes: 3 additions & 1 deletion src/store/dataMigration/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ const importDatabaseRecords = (rawData: string): ThunkAction => async (

batch(() => [
dispatch(dataMigration.actions.importDatabaseRecordsSuccess()),
dispatch(activityActions.loadRecords({ forceReload: true }))
dispatch(
activityActions.loadRecords(undefined, undefined, { forceReload: true })
)
]);
} catch (error) {
console.error(error);
Expand Down

0 comments on commit 51c4e49

Please sign in to comment.