bigbuffet-rw/app/soapbox/reducers/group-memberships.ts
marcin mikołajczak 12825f9350 Groups: actions, normalizers, reducers
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2022-12-11 22:24:01 +01:00

100 lines
3.8 KiB
TypeScript

import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord } from 'immutable';
import {
GROUP_DELETE_SUCCESS,
GROUP_MEMBERSHIPS_FETCH_REQUEST,
GROUP_MEMBERSHIPS_FETCH_FAIL,
GROUP_MEMBERSHIPS_FETCH_SUCCESS,
GROUP_MEMBERSHIPS_EXPAND_REQUEST,
GROUP_MEMBERSHIPS_EXPAND_FAIL,
GROUP_MEMBERSHIPS_EXPAND_SUCCESS,
GROUP_PROMOTE_SUCCESS,
GROUP_DEMOTE_SUCCESS,
GROUP_KICK_SUCCESS,
GROUP_BLOCK_SUCCESS,
} from 'soapbox/actions/groups';
import type { AnyAction } from 'redux';
import type { APIEntity } from 'soapbox/types/entities';
const ListRecord = ImmutableRecord({
next: null as string | null,
isLoading: false,
items: ImmutableOrderedSet<string>(),
});
const ReducerRecord = ImmutableRecord({
admin: ImmutableMap<string, List>({}),
moderator: ImmutableMap<string, List>({}),
user: ImmutableMap<string, List>({}),
});
export type GroupRole = 'admin' | 'moderator' | 'user';
type List = ReturnType<typeof ListRecord>;
type State = ReturnType<typeof ReducerRecord>;
const normalizeList = (state: State, path: string[], memberships: APIEntity[], next: string | null) => {
return state.setIn(path, ImmutableMap({
next,
items: ImmutableOrderedSet(memberships.map(item => item.account.id)),
isLoading: false,
}));
};
const appendToList = (state: State, path: string[], memberships: APIEntity[], next: string | null) => {
return state.updateIn(path, map => {
return (map as List).set('next', next).set('isLoading', false).update('items', list => list.concat(memberships.map(item => item.account.id)));
});
};
const updateLists = (state: State, groupId: string, memberships: APIEntity[]) => {
const updateList = (state: State, role: string, membership: APIEntity) => {
if (role === membership.role) {
return state.updateIn([role, groupId], map => (map as List).update('items', set => set.add(membership.account.id)));
} else {
return state.updateIn([role, groupId], map => (map as List).update('items', set => set.delete(membership.account.id)));
}
};
memberships.forEach(membership => {
state = updateList(state, 'admin', membership);
state = updateList(state, 'moderator', membership);
state = updateList(state, 'user', membership);
});
return state;
};
const removeFromList = (state: State, path: string[], accountId: string) => {
return state.updateIn(path, map => {
return (map as List).update('items', set => set.delete(accountId));
});
};
export default function groupMemberships(state: State = ReducerRecord(), action: AnyAction) {
switch (action.type) {
case GROUP_DELETE_SUCCESS:
return state.deleteIn(['admin', action.id]).deleteIn(['moderator', action.id]).deleteIn(['user', action.id]);
case GROUP_MEMBERSHIPS_FETCH_REQUEST:
case GROUP_MEMBERSHIPS_EXPAND_REQUEST:
return state.updateIn([action.role, action.id], map => (map as List || ListRecord()).set('isLoading', true));
case GROUP_MEMBERSHIPS_FETCH_FAIL:
case GROUP_MEMBERSHIPS_EXPAND_FAIL:
return state.updateIn([action.role, action.id], map => (map as List || ListRecord()).set('isLoading', false));
case GROUP_MEMBERSHIPS_FETCH_SUCCESS:
return normalizeList(state, [action.role, action.id], action.memberships, action.next);
case GROUP_MEMBERSHIPS_EXPAND_SUCCESS:
return appendToList(state, [action.role, action.id], action.memberships, action.next);
case GROUP_PROMOTE_SUCCESS:
case GROUP_DEMOTE_SUCCESS:
return updateLists(state, action.groupId, action.memberships);
case GROUP_KICK_SUCCESS:
case GROUP_BLOCK_SUCCESS:
state = removeFromList(state, ['admin', action.groupId], action.accountId);
state = removeFromList(state, ['moderator', action.groupId], action.accountId);
state = removeFromList(state, ['user', action.groupId], action.accountId);
return state;
default:
return state;
}
}