Files
ncd-fe/packages/pl-fe/src/reducers/status-lists.ts
2025-03-15 12:13:31 +01:00

198 lines
8.1 KiB
TypeScript

import { create } from 'mutative';
import {
BOOKMARKED_STATUSES_FETCH_REQUEST,
BOOKMARKED_STATUSES_FETCH_SUCCESS,
BOOKMARKED_STATUSES_FETCH_FAIL,
BOOKMARKED_STATUSES_EXPAND_REQUEST,
BOOKMARKED_STATUSES_EXPAND_SUCCESS,
BOOKMARKED_STATUSES_EXPAND_FAIL,
type BookmarksAction,
} from 'pl-fe/actions/bookmarks';
import {
RECENT_EVENTS_FETCH_REQUEST,
RECENT_EVENTS_FETCH_SUCCESS,
RECENT_EVENTS_FETCH_FAIL,
JOINED_EVENTS_FETCH_REQUEST,
JOINED_EVENTS_FETCH_SUCCESS,
JOINED_EVENTS_FETCH_FAIL,
type EventsAction,
} from 'pl-fe/actions/events';
import {
FAVOURITED_STATUSES_FETCH_REQUEST,
FAVOURITED_STATUSES_FETCH_SUCCESS,
FAVOURITED_STATUSES_FETCH_FAIL,
FAVOURITED_STATUSES_EXPAND_REQUEST,
FAVOURITED_STATUSES_EXPAND_SUCCESS,
FAVOURITED_STATUSES_EXPAND_FAIL,
ACCOUNT_FAVOURITED_STATUSES_FETCH_REQUEST,
ACCOUNT_FAVOURITED_STATUSES_FETCH_SUCCESS,
ACCOUNT_FAVOURITED_STATUSES_FETCH_FAIL,
ACCOUNT_FAVOURITED_STATUSES_EXPAND_REQUEST,
ACCOUNT_FAVOURITED_STATUSES_EXPAND_SUCCESS,
ACCOUNT_FAVOURITED_STATUSES_EXPAND_FAIL,
type FavouritesAction,
} from 'pl-fe/actions/favourites';
import {
FAVOURITE_SUCCESS,
UNFAVOURITE_SUCCESS,
BOOKMARK_SUCCESS,
UNBOOKMARK_SUCCESS,
PIN_SUCCESS,
UNPIN_SUCCESS,
type InteractionsAction,
} from 'pl-fe/actions/interactions';
import { PINNED_STATUSES_FETCH_SUCCESS, type PinStatusesAction } from 'pl-fe/actions/pin-statuses';
import type { PaginatedResponse, Status } from 'pl-api';
import type { StatusesAction } from 'pl-fe/actions/statuses';
interface StatusList {
next: (() => Promise<PaginatedResponse<Status>>) | null;
loaded: boolean;
isLoading: boolean | null;
items: Array<string>;
}
const newStatusList = (): StatusList => ({
next: null,
loaded: false,
isLoading: null,
items: [],
});
type State = Record<string, StatusList>;
const initialState: State = {
favourites: newStatusList(),
bookmarks: newStatusList(),
pins: newStatusList(),
recent_events: newStatusList(),
joined_events: newStatusList(),
};
const getStatusId = (status: string | Pick<Status, 'id'>) => typeof status === 'string' ? status : status.id;
const getStatusIds = (statuses: Array<string | Pick<Status, 'id'>> = []) => statuses.map(getStatusId);
const setLoading = (state: State, listType: string, loading: boolean) => {
const list = state[listType] = state[listType] || newStatusList();
list.isLoading = loading;
};
const normalizeList = (state: State, listType: string, statuses: Array<string | Pick<Status, 'id'>>, next: (() => Promise<PaginatedResponse<Status>>) | null) => {
const list = state[listType] = state[listType] || newStatusList();
list.next = next;
list.loaded = true;
list.isLoading = false;
list.items = getStatusIds(statuses);
};
const appendToList = (state: State, listType: string, statuses: Array<string | Pick<Status, 'id'>>, next: (() => Promise<PaginatedResponse<Status>>) | null) => {
const newIds = getStatusIds(statuses);
const list = state[listType] = state[listType] || newStatusList();
list.next = next;
list.isLoading = false;
list.items = [...new Set([...list.items, ...newIds])];
};
const prependOneToList = (state: State, listType: string, status: string | Pick<Status, 'id'>) => {
const statusId = getStatusId(status);
const list = state[listType] = state[listType] || newStatusList();
list.items = [...new Set([statusId, ...list.items])];
};
const removeOneFromList = (state: State, listType: string, status: string | Pick<Status, 'id'>) => {
const statusId = getStatusId(status);
const list = state[listType] = state[listType] || newStatusList();
list.items = list.items.filter(id => id !== statusId);
};
const addBookmarkToLists = (state: State, status: Pick<Status, 'id' | 'bookmark_folder'>) => {
prependOneToList(state, 'bookmarks', status);
const folderId = status.bookmark_folder;
if (folderId) {
prependOneToList(state, `bookmarks:${folderId}`, status);
}
};
const removeBookmarkFromLists = (state: State, status: Pick<Status, 'id' | 'bookmark_folder'>) => {
removeOneFromList(state, 'bookmarks', status);
const folderId = status.bookmark_folder;
if (folderId) {
removeOneFromList(state, `bookmarks:${folderId}`, status);
}
};
const statusLists = (state = initialState, action: BookmarksAction | EventsAction | FavouritesAction | InteractionsAction | PinStatusesAction | StatusesAction): State => {
switch (action.type) {
case FAVOURITED_STATUSES_FETCH_REQUEST:
case FAVOURITED_STATUSES_EXPAND_REQUEST:
return create(state, draft => setLoading(draft, 'favourites', true));
case FAVOURITED_STATUSES_FETCH_FAIL:
case FAVOURITED_STATUSES_EXPAND_FAIL:
return create(state, draft => setLoading(draft, 'favourites', false));
case FAVOURITED_STATUSES_FETCH_SUCCESS:
return create(state, draft => normalizeList(draft, 'favourites', action.statuses, action.next));
case FAVOURITED_STATUSES_EXPAND_SUCCESS:
return create(state, draft => appendToList(draft, 'favourites', action.statuses, action.next));
case ACCOUNT_FAVOURITED_STATUSES_FETCH_REQUEST:
case ACCOUNT_FAVOURITED_STATUSES_EXPAND_REQUEST:
return create(state, draft => setLoading(draft, `favourites:${action.accountId}`, true));
case ACCOUNT_FAVOURITED_STATUSES_FETCH_FAIL:
case ACCOUNT_FAVOURITED_STATUSES_EXPAND_FAIL:
return create(state, draft => setLoading(draft, `favourites:${action.accountId}`, false));
case ACCOUNT_FAVOURITED_STATUSES_FETCH_SUCCESS:
return create(state, draft => normalizeList(draft, `favourites:${action.accountId}`, action.statuses, action.next));
case ACCOUNT_FAVOURITED_STATUSES_EXPAND_SUCCESS:
return create(state, draft => appendToList(draft, `favourites:${action.accountId}`, action.statuses, action.next));
case BOOKMARKED_STATUSES_FETCH_REQUEST:
case BOOKMARKED_STATUSES_EXPAND_REQUEST:
return create(state, draft => setLoading(draft, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', true));
case BOOKMARKED_STATUSES_FETCH_FAIL:
case BOOKMARKED_STATUSES_EXPAND_FAIL:
return create(state, draft => setLoading(draft, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', false));
case BOOKMARKED_STATUSES_FETCH_SUCCESS:
return create(state, draft => normalizeList(draft, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', action.statuses, action.next));
case BOOKMARKED_STATUSES_EXPAND_SUCCESS:
return create(state, draft => appendToList(draft, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', action.statuses, action.next));
case FAVOURITE_SUCCESS:
return create(state, draft => prependOneToList(draft, 'favourites', action.status));
case UNFAVOURITE_SUCCESS:
return create(state, draft => removeOneFromList(draft, 'favourites', action.status));
case BOOKMARK_SUCCESS:
return create(state, draft => addBookmarkToLists(draft, action.status));
case UNBOOKMARK_SUCCESS:
return create(state, draft => removeBookmarkFromLists(draft, action.status));
case PINNED_STATUSES_FETCH_SUCCESS:
return create(state, draft => normalizeList(draft, 'pins', action.statuses, action.next));
case PIN_SUCCESS:
return create(state, draft => prependOneToList(draft, 'pins', action.status));
case UNPIN_SUCCESS:
return create(state, draft => removeOneFromList(draft, 'pins', action.status));
case RECENT_EVENTS_FETCH_REQUEST:
return create(state, draft => setLoading(draft, 'recent_events', true));
case RECENT_EVENTS_FETCH_FAIL:
return create(state, draft => setLoading(draft, 'recent_events', false));
case RECENT_EVENTS_FETCH_SUCCESS:
return create(state, draft => normalizeList(draft, 'recent_events', action.statuses, action.next));
case JOINED_EVENTS_FETCH_REQUEST:
return create(state, draft => setLoading(draft, 'joined_events', true));
case JOINED_EVENTS_FETCH_FAIL:
return create(state, draft => setLoading(draft, 'joined_events', false));
case JOINED_EVENTS_FETCH_SUCCESS:
return create(state, draft => normalizeList(draft, 'joined_events', action.statuses, action.next));
default:
return state;
}
};
export {
statusLists as default,
};