pl-fe: backups, polls reducers

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-11-06 13:06:41 +01:00
parent 0811af7ad7
commit 542ba9ab78
9 changed files with 64 additions and 41 deletions

View file

@ -2765,8 +2765,6 @@ class PlApiClient {
return response.json as {};
},
};
/**

View file

@ -1,5 +1,6 @@
import { getClient } from '../api';
import type { Backup } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';
const BACKUPS_FETCH_REQUEST = 'BACKUPS_FETCH_REQUEST' as const;
@ -10,27 +11,63 @@ const BACKUPS_CREATE_REQUEST = 'BACKUPS_CREATE_REQUEST' as const;
const BACKUPS_CREATE_SUCCESS = 'BACKUPS_CREATE_SUCCESS' as const;
const BACKUPS_CREATE_FAIL = 'BACKUPS_CREATE_FAIL' as const;
interface BackupsFetchRequestAction {
type: typeof BACKUPS_FETCH_REQUEST;
}
interface BackupsFetchSuccessAction {
type: typeof BACKUPS_FETCH_SUCCESS;
backups: Array<Backup>;
}
interface BackupsFetchFailAction {
type: typeof BACKUPS_FETCH_FAIL;
error: unknown;
}
const fetchBackups = () =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: BACKUPS_FETCH_REQUEST });
dispatch<BackupsFetchRequestAction>({ type: BACKUPS_FETCH_REQUEST });
return getClient(getState).settings.getBackups().then((backups) =>
dispatch({ type: BACKUPS_FETCH_SUCCESS, backups }),
dispatch<BackupsFetchSuccessAction>({ type: BACKUPS_FETCH_SUCCESS, backups }),
).catch(error => {
dispatch({ type: BACKUPS_FETCH_FAIL, error });
dispatch<BackupsFetchFailAction>({ type: BACKUPS_FETCH_FAIL, error });
});
};
interface BackupsCreateRequestAction {
type: typeof BACKUPS_CREATE_REQUEST;
}
interface BackupsCreateSuccessAction {
type: typeof BACKUPS_CREATE_SUCCESS;
backups: Array<Backup>;
}
interface BackupsCreateFailAction {
type: typeof BACKUPS_CREATE_FAIL;
error: unknown;
}
const createBackup = () =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: BACKUPS_CREATE_REQUEST });
return getClient(getState).settings.createBackup().then((backups) =>
dispatch({ type: BACKUPS_CREATE_SUCCESS, backups }),
dispatch<BackupsCreateRequestAction>({ type: BACKUPS_CREATE_REQUEST });
return getClient(getState).settings.createBackup().then((backup) =>
dispatch<BackupsCreateSuccessAction>({ type: BACKUPS_CREATE_SUCCESS, backups: [backup] }),
).catch(error => {
dispatch({ type: BACKUPS_CREATE_FAIL, error });
dispatch<BackupsCreateFailAction>({ type: BACKUPS_CREATE_FAIL, error });
});
};
type BackupsAction =
| BackupsFetchRequestAction
| BackupsFetchSuccessAction
| BackupsFetchFailAction
| BackupsCreateRequestAction
| BackupsCreateSuccessAction
| BackupsCreateFailAction;
export {
BACKUPS_FETCH_REQUEST,
BACKUPS_FETCH_SUCCESS,
@ -40,4 +77,5 @@ export {
BACKUPS_CREATE_FAIL,
fetchBackups,
createBackup,
type BackupsAction,
};

View file

@ -76,7 +76,6 @@ const updateNotifications = (notification: BaseNotification) =>
statuses: [getNotificationStatus(notification)],
}));
if (showInColumn) {
const normalizedNotification = normalizeNotification(notification);

View file

@ -97,7 +97,7 @@ const editStatus = (statusId: string) => (dispatch: AppDispatch, getState: () =>
const state = getState();
const status = state.statuses[statusId]!;
const poll = status.poll_id ? state.polls.get(status.poll_id) : undefined;
const poll = status.poll_id ? state.polls[status.poll_id] : undefined;
dispatch({ type: STATUS_FETCH_SOURCE_REQUEST });
@ -134,7 +134,7 @@ const deleteStatus = (statusId: string, withRedraft = false) =>
const state = getState();
const status = state.statuses[statusId]!;
const poll = status.poll_id ? state.polls.get(status.poll_id) : undefined;
const poll = status.poll_id ? state.polls[status.poll_id] : undefined;
dispatch({ type: STATUS_DELETE_REQUEST, params: status });

View file

@ -30,7 +30,7 @@ const Poll: React.FC<IPoll> = ({ id, status }): JSX.Element | null => {
const intl = useIntl();
const isLoggedIn = useAppSelector((state) => state.me);
const poll = useAppSelector((state) => state.polls.get(id));
const poll = useAppSelector((state) => state.polls[id]);
const [selected, setSelected] = useState({} as Selected);

View file

@ -65,7 +65,7 @@ const Backups = () => {
const intl = useIntl();
const dispatch = useAppDispatch();
const backups = useAppSelector((state) => state.backups.toList().sortBy((backup) => backup.inserted_at));
const backups = useAppSelector((state) => Object.values(state.backups).toSorted((a, b) => a.inserted_at.localeCompare(b.inserted_at)));
const [isLoading, setIsLoading] = useState(true);
@ -80,7 +80,7 @@ const Backups = () => {
}).catch(() => {});
}, []);
const showLoading = isLoading && backups.count() === 0;
const showLoading = isLoading && backups.length === 0;
const emptyMessage = (
<Card variant='rounded' size='lg'>
@ -96,11 +96,11 @@ const Backups = () => {
</Card>
);
const body = showLoading ? <Spinner /> : backups.isEmpty() ? emptyMessage : (
const body = showLoading ? <Spinner /> : backups.length ? (
<div className='mb-4 grid grid-cols-1 gap-4 sm:grid-cols-2'>
{backups.map((backup) => <Backup key={backup.id} backup={backup} />)}
</div>
);
) : emptyMessage;
return (
<Column label={intl.formatMessage(messages.heading)}>

View file

@ -1,25 +1,18 @@
import { Map as ImmutableMap } from 'immutable';
import { create } from 'mutative';
import { BACKUPS_FETCH_SUCCESS, BACKUPS_CREATE_SUCCESS } from '../actions/backups';
import { BACKUPS_FETCH_SUCCESS, BACKUPS_CREATE_SUCCESS, type BackupsAction } from '../actions/backups';
import type { Backup } from 'pl-api';
import type { AnyAction } from 'redux';
type State = ImmutableMap<string, Backup>;
type State = Record<string, Backup>;
const initialState: State = ImmutableMap();
const initialState: State = {};
const importBackup = (state: State, backup: Backup) => state.set(backup.inserted_at, backup);
const importBackups = (state: State, backups: Array<Backup>) => state.withMutations(mutable => {
backups.forEach(backup => importBackup(mutable, backup));
});
const backups = (state = initialState, action: AnyAction) => {
const backups = (state = initialState, action: BackupsAction) => {
switch (action.type) {
case BACKUPS_FETCH_SUCCESS:
case BACKUPS_CREATE_SUCCESS:
return importBackups(state, action.backups);
return create(state, (draft) => action.backups.forEach((backup) => draft[backup.inserted_at] = backup));
default:
return state;
}

View file

@ -1,22 +1,17 @@
import { Map as ImmutableMap } from 'immutable';
import { create } from 'mutative';
import { POLLS_IMPORT, type ImporterAction } from 'pl-fe/actions/importer';
import type { Poll, Status } from 'pl-api';
import type { Poll } from 'pl-api';
type State = ImmutableMap<string, Poll>;
type State = Record<string, Poll>;
const importPolls = (state: State, polls: Array<Exclude<Status['poll'], null>>) =>
state.withMutations(map =>
polls.forEach(poll => map.set(poll.id, poll)),
);
const initialState: State = ImmutableMap();
const initialState: State = {};
const polls = (state: State = initialState, action: ImporterAction): State => {
switch (action.type) {
case POLLS_IMPORT:
return importPolls(state, action.polls);
return create(state, (draft) => action.polls.forEach(poll => draft[poll.id] = poll));
default:
return state;
}

View file

@ -135,7 +135,7 @@ const makeGetStatus = () => createSelector(
if (group) return state.entities[Entities.GROUPS]?.store[group] as Group;
return undefined;
},
(state: RootState, { id }: APIStatus) => state.polls.get(id) || null,
(state: RootState, { id }: APIStatus) => state.polls[id] || null,
(_state: RootState, { username }: APIStatus) => username,
getFilters,
(state: RootState) => state.me,