pl-fe: backups, polls reducers
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
0811af7ad7
commit
542ba9ab78
9 changed files with 64 additions and 41 deletions
|
@ -2765,8 +2765,6 @@ class PlApiClient {
|
||||||
|
|
||||||
return response.json as {};
|
return response.json as {};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { getClient } from '../api';
|
import { getClient } from '../api';
|
||||||
|
|
||||||
|
import type { Backup } from 'pl-api';
|
||||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||||
|
|
||||||
const BACKUPS_FETCH_REQUEST = 'BACKUPS_FETCH_REQUEST' as const;
|
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_SUCCESS = 'BACKUPS_CREATE_SUCCESS' as const;
|
||||||
const BACKUPS_CREATE_FAIL = 'BACKUPS_CREATE_FAIL' 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 = () =>
|
const fetchBackups = () =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: BACKUPS_FETCH_REQUEST });
|
dispatch<BackupsFetchRequestAction>({ type: BACKUPS_FETCH_REQUEST });
|
||||||
|
|
||||||
return getClient(getState).settings.getBackups().then((backups) =>
|
return getClient(getState).settings.getBackups().then((backups) =>
|
||||||
dispatch({ type: BACKUPS_FETCH_SUCCESS, backups }),
|
dispatch<BackupsFetchSuccessAction>({ type: BACKUPS_FETCH_SUCCESS, backups }),
|
||||||
).catch(error => {
|
).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 = () =>
|
const createBackup = () =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
dispatch({ type: BACKUPS_CREATE_REQUEST });
|
dispatch<BackupsCreateRequestAction>({ type: BACKUPS_CREATE_REQUEST });
|
||||||
return getClient(getState).settings.createBackup().then((backups) =>
|
return getClient(getState).settings.createBackup().then((backup) =>
|
||||||
dispatch({ type: BACKUPS_CREATE_SUCCESS, backups }),
|
dispatch<BackupsCreateSuccessAction>({ type: BACKUPS_CREATE_SUCCESS, backups: [backup] }),
|
||||||
).catch(error => {
|
).catch(error => {
|
||||||
dispatch({ type: BACKUPS_CREATE_FAIL, error });
|
dispatch<BackupsCreateFailAction>({ type: BACKUPS_CREATE_FAIL, error });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type BackupsAction =
|
||||||
|
| BackupsFetchRequestAction
|
||||||
|
| BackupsFetchSuccessAction
|
||||||
|
| BackupsFetchFailAction
|
||||||
|
| BackupsCreateRequestAction
|
||||||
|
| BackupsCreateSuccessAction
|
||||||
|
| BackupsCreateFailAction;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
BACKUPS_FETCH_REQUEST,
|
BACKUPS_FETCH_REQUEST,
|
||||||
BACKUPS_FETCH_SUCCESS,
|
BACKUPS_FETCH_SUCCESS,
|
||||||
|
@ -40,4 +77,5 @@ export {
|
||||||
BACKUPS_CREATE_FAIL,
|
BACKUPS_CREATE_FAIL,
|
||||||
fetchBackups,
|
fetchBackups,
|
||||||
createBackup,
|
createBackup,
|
||||||
|
type BackupsAction,
|
||||||
};
|
};
|
||||||
|
|
|
@ -76,7 +76,6 @@ const updateNotifications = (notification: BaseNotification) =>
|
||||||
statuses: [getNotificationStatus(notification)],
|
statuses: [getNotificationStatus(notification)],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
if (showInColumn) {
|
if (showInColumn) {
|
||||||
const normalizedNotification = normalizeNotification(notification);
|
const normalizedNotification = normalizeNotification(notification);
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ const editStatus = (statusId: string) => (dispatch: AppDispatch, getState: () =>
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
const status = state.statuses[statusId]!;
|
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 });
|
dispatch({ type: STATUS_FETCH_SOURCE_REQUEST });
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ const deleteStatus = (statusId: string, withRedraft = false) =>
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
const status = state.statuses[statusId]!;
|
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 });
|
dispatch({ type: STATUS_DELETE_REQUEST, params: status });
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ const Poll: React.FC<IPoll> = ({ id, status }): JSX.Element | null => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const isLoggedIn = useAppSelector((state) => state.me);
|
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);
|
const [selected, setSelected] = useState({} as Selected);
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ const Backups = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
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);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ const Backups = () => {
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const showLoading = isLoading && backups.count() === 0;
|
const showLoading = isLoading && backups.length === 0;
|
||||||
|
|
||||||
const emptyMessage = (
|
const emptyMessage = (
|
||||||
<Card variant='rounded' size='lg'>
|
<Card variant='rounded' size='lg'>
|
||||||
|
@ -96,11 +96,11 @@ const Backups = () => {
|
||||||
</Card>
|
</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'>
|
<div className='mb-4 grid grid-cols-1 gap-4 sm:grid-cols-2'>
|
||||||
{backups.map((backup) => <Backup key={backup.id} backup={backup} />)}
|
{backups.map((backup) => <Backup key={backup.id} backup={backup} />)}
|
||||||
</div>
|
</div>
|
||||||
);
|
) : emptyMessage;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column label={intl.formatMessage(messages.heading)}>
|
<Column label={intl.formatMessage(messages.heading)}>
|
||||||
|
|
|
@ -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 { 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 backups = (state = initialState, action: BackupsAction) => {
|
||||||
|
|
||||||
const importBackups = (state: State, backups: Array<Backup>) => state.withMutations(mutable => {
|
|
||||||
backups.forEach(backup => importBackup(mutable, backup));
|
|
||||||
});
|
|
||||||
|
|
||||||
const backups = (state = initialState, action: AnyAction) => {
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case BACKUPS_FETCH_SUCCESS:
|
case BACKUPS_FETCH_SUCCESS:
|
||||||
case BACKUPS_CREATE_SUCCESS:
|
case BACKUPS_CREATE_SUCCESS:
|
||||||
return importBackups(state, action.backups);
|
return create(state, (draft) => action.backups.forEach((backup) => draft[backup.inserted_at] = backup));
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 { 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>>) =>
|
const initialState: State = {};
|
||||||
state.withMutations(map =>
|
|
||||||
polls.forEach(poll => map.set(poll.id, poll)),
|
|
||||||
);
|
|
||||||
|
|
||||||
const initialState: State = ImmutableMap();
|
|
||||||
|
|
||||||
const polls = (state: State = initialState, action: ImporterAction): State => {
|
const polls = (state: State = initialState, action: ImporterAction): State => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case POLLS_IMPORT:
|
case POLLS_IMPORT:
|
||||||
return importPolls(state, action.polls);
|
return create(state, (draft) => action.polls.forEach(poll => draft[poll.id] = poll));
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,7 @@ const makeGetStatus = () => createSelector(
|
||||||
if (group) return state.entities[Entities.GROUPS]?.store[group] as Group;
|
if (group) return state.entities[Entities.GROUPS]?.store[group] as Group;
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
(state: RootState, { id }: APIStatus) => state.polls.get(id) || null,
|
(state: RootState, { id }: APIStatus) => state.polls[id] || null,
|
||||||
(_state: RootState, { username }: APIStatus) => username,
|
(_state: RootState, { username }: APIStatus) => username,
|
||||||
getFilters,
|
getFilters,
|
||||||
(state: RootState) => state.me,
|
(state: RootState) => state.me,
|
||||||
|
|
Loading…
Reference in a new issue