pl-fe: migrate status lists to mutative
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
fad9714475
commit
b2d5bbf537
16 changed files with 142 additions and 146 deletions
|
@ -17,7 +17,7 @@ const noOp = () => new Promise(f => f(undefined));
|
|||
|
||||
const fetchBookmarkedStatuses = (folderId?: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (getState().status_lists.get(folderId ? `bookmarks:${folderId}` : 'bookmarks')?.isLoading) {
|
||||
if (getState().status_lists[folderId ? `bookmarks:${folderId}` : 'bookmarks']?.isLoading) {
|
||||
return dispatch(noOp);
|
||||
}
|
||||
|
||||
|
@ -52,9 +52,9 @@ const fetchBookmarkedStatusesFail = (error: unknown, folderId?: string) => ({
|
|||
const expandBookmarkedStatuses = (folderId?: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const list = folderId ? `bookmarks:${folderId}` : 'bookmarks';
|
||||
const next = getState().status_lists.get(list)?.next || null;
|
||||
const next = getState().status_lists[list]?.next || null;
|
||||
|
||||
if (next === null || getState().status_lists.get(list)?.isLoading) {
|
||||
if (next === null || getState().status_lists[list]?.isLoading) {
|
||||
return dispatch(noOp);
|
||||
}
|
||||
|
||||
|
|
|
@ -448,7 +448,7 @@ const initEventEdit = (statusId: string) => (dispatch: AppDispatch, getState: ()
|
|||
|
||||
const fetchRecentEvents = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (getState().status_lists.get('recent_events')?.isLoading) {
|
||||
if (getState().status_lists.recent_events?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -470,7 +470,7 @@ const fetchRecentEvents = () =>
|
|||
|
||||
const fetchJoinedEvents = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (getState().status_lists.get('joined_events')?.isLoading) {
|
||||
if (getState().status_lists.joined_events?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ const fetchFavouritedStatuses = () =>
|
|||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
if (getState().status_lists.get('favourites')?.isLoading) {
|
||||
if (getState().status_lists.favourites?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -60,9 +60,9 @@ const expandFavouritedStatuses = () =>
|
|||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
const next = getState().status_lists.get('favourites')?.next || null;
|
||||
const next = getState().status_lists.favourites?.next || null;
|
||||
|
||||
if (next === null || getState().status_lists.get('favourites')?.isLoading) {
|
||||
if (next === null || getState().status_lists.favourites?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ const fetchAccountFavouritedStatuses = (accountId: string) =>
|
|||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
if (getState().status_lists.get(`favourites:${accountId}`)?.isLoading) {
|
||||
if (getState().status_lists[`favourites:${accountId}`]?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -131,9 +131,9 @@ const expandAccountFavouritedStatuses = (accountId: string) =>
|
|||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
const next = getState().status_lists.get(`favourites:${accountId}`)?.next || null;
|
||||
const next = getState().status_lists[`favourites:${accountId}`]?.next || null;
|
||||
|
||||
if (next === null || getState().status_lists.get(`favourites:${accountId}`)?.isLoading) {
|
||||
if (next === null || getState().status_lists[`favourites:${accountId}`]?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ const fetchScheduledStatuses = () =>
|
|||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
|
||||
if (state.status_lists.get('scheduled_statuses')?.isLoading) {
|
||||
if (state.status_lists.scheduled_statuses?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -63,9 +63,9 @@ const fetchScheduledStatusesFail = (error: unknown) => ({
|
|||
|
||||
const expandScheduledStatuses = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const next = getState().status_lists.get('scheduled_statuses')?.next as any as () => Promise<PaginatedResponse<ScheduledStatus>> || null;
|
||||
const next = getState().status_lists.scheduled_statuses?.next as any as () => Promise<PaginatedResponse<ScheduledStatus>> || null;
|
||||
|
||||
if (next === null || getState().status_lists.get('scheduled_statuses')?.isLoading) {
|
||||
if (next === null || getState().status_lists.scheduled_statuses?.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ interface FetchStatusQuotesFailAction {
|
|||
|
||||
const fetchStatusQuotes = (statusId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (getState().status_lists.getIn([`quotes:${statusId}`, 'isLoading'])) {
|
||||
if (getState().status_lists[`quotes:${statusId}`]?.isLoading) {
|
||||
return dispatch(noOp);
|
||||
}
|
||||
|
||||
|
@ -78,9 +78,9 @@ interface ExpandStatusQuotesFailAction {
|
|||
|
||||
const expandStatusQuotes = (statusId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const next = getState().status_lists.get(`quotes:${statusId}`)?.next || null;
|
||||
const next = getState().status_lists[`quotes:${statusId}`]?.next || null;
|
||||
|
||||
if (next === null || getState().status_lists.getIn([`quotes:${statusId}`, 'isLoading'])) {
|
||||
if (next === null || getState().status_lists[`quotes:${statusId}`]?.isLoading) {
|
||||
return dispatch(noOp);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import clsx from 'clsx';
|
||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import debounce from 'lodash/debounce';
|
||||
import React, { useCallback } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
@ -18,11 +17,11 @@ interface IStatusList extends Omit<IScrollableListWithContainer, 'onLoadMore' |
|
|||
/** Unique key to preserve the scroll position when navigating back. */
|
||||
scrollKey: string;
|
||||
/** List of status IDs to display. */
|
||||
statusIds: ImmutableOrderedSet<string>;
|
||||
statusIds: Array<string>;
|
||||
/** Last _unfiltered_ status ID (maxId) for pagination. */
|
||||
lastStatusId?: string;
|
||||
/** Pinned statuses to show at the top of the feed. */
|
||||
featuredStatusIds?: ImmutableOrderedSet<string>;
|
||||
featuredStatusIds?: Array<string>;
|
||||
/** Pagination callback when the end of the list is reached. */
|
||||
onLoadMore?: (lastStatusId: string) => void;
|
||||
/** Whether the data is currently being fetched. */
|
||||
|
@ -57,13 +56,13 @@ const StatusList: React.FC<IStatusList> = ({
|
|||
}) => {
|
||||
const plFeConfig = usePlFeConfig();
|
||||
|
||||
const getFeaturedStatusCount = () => featuredStatusIds?.size || 0;
|
||||
const getFeaturedStatusCount = () => featuredStatusIds?.length || 0;
|
||||
|
||||
const getCurrentStatusIndex = (id: string, featured: boolean): number => {
|
||||
if (featured) {
|
||||
return featuredStatusIds?.keySeq().findIndex(key => key === id) || 0;
|
||||
return featuredStatusIds?.findIndex(key => key === id) || 0;
|
||||
} else {
|
||||
return statusIds.keySeq().findIndex(key => key === id) + getFeaturedStatusCount();
|
||||
return statusIds.findIndex(key => key === id) + getFeaturedStatusCount();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -78,11 +77,11 @@ const StatusList: React.FC<IStatusList> = ({
|
|||
};
|
||||
|
||||
const handleLoadOlder = useCallback(debounce(() => {
|
||||
const maxId = lastStatusId || statusIds.last();
|
||||
const maxId = lastStatusId || statusIds.at(-1);
|
||||
if (onLoadMore && maxId) {
|
||||
onLoadMore(maxId.replace('末suggestions-', ''));
|
||||
}
|
||||
}, 300, { leading: true }), [onLoadMore, lastStatusId, statusIds.last()]);
|
||||
}, 300, { leading: true }), [onLoadMore, lastStatusId, statusIds.at(-1)]);
|
||||
|
||||
const selectChild = (index: number) => {
|
||||
const selector = `#status-list [data-index="${index}"] .focusable`;
|
||||
|
@ -92,9 +91,9 @@ const StatusList: React.FC<IStatusList> = ({
|
|||
};
|
||||
|
||||
const renderLoadGap = (index: number) => {
|
||||
const ids = statusIds.toList();
|
||||
const nextId = ids.get(index + 1);
|
||||
const prevId = ids.get(index - 1);
|
||||
const ids = statusIds;
|
||||
const nextId = ids[index + 1];
|
||||
const prevId = ids[index - 1];
|
||||
|
||||
if (index < 1 || !nextId || !prevId || !onLoadMore) return null;
|
||||
|
||||
|
@ -136,7 +135,7 @@ const StatusList: React.FC<IStatusList> = ({
|
|||
const renderFeaturedStatuses = (): React.ReactNode[] => {
|
||||
if (!featuredStatusIds) return [];
|
||||
|
||||
return featuredStatusIds.toArray().map(statusId => (
|
||||
return featuredStatusIds.map(statusId => (
|
||||
<StatusContainer
|
||||
key={`f-${statusId}`}
|
||||
id={statusId}
|
||||
|
@ -160,8 +159,8 @@ const StatusList: React.FC<IStatusList> = ({
|
|||
);
|
||||
|
||||
const renderStatuses = (): React.ReactNode[] => {
|
||||
if (isLoading || statusIds.size > 0) {
|
||||
return statusIds.toList().reduce((acc, statusId, index) => {
|
||||
if (isLoading || statusIds.length > 0) {
|
||||
return statusIds.reduce((acc, statusId, index) => {
|
||||
if (statusId === null) {
|
||||
const gap = renderLoadGap(index);
|
||||
if (gap) {
|
||||
|
@ -214,7 +213,7 @@ const StatusList: React.FC<IStatusList> = ({
|
|||
id='status-list'
|
||||
key='scrollable-list'
|
||||
isLoading={isLoading}
|
||||
showLoading={isLoading && statusIds.size === 0}
|
||||
showLoading={isLoading && statusIds.length === 0}
|
||||
onLoadMore={handleLoadOlder}
|
||||
placeholderComponent={() => <PlaceholderStatus variant={divideType === 'border' ? 'slim' : 'rounded'} />}
|
||||
placeholderCount={20}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import debounce from 'lodash/debounce';
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
@ -54,9 +53,9 @@ const Bookmarks: React.FC<IBookmarks> = ({ params }) => {
|
|||
|
||||
const bookmarksKey = folderId ? `bookmarks:${folderId}` : 'bookmarks';
|
||||
|
||||
const statusIds = useAppSelector((state) => state.status_lists.get(bookmarksKey)?.items || ImmutableOrderedSet<string>());
|
||||
const isLoading = useAppSelector((state) => state.status_lists.get(bookmarksKey)?.isLoading === true);
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists.get(bookmarksKey)?.next);
|
||||
const statusIds = useAppSelector((state) => state.status_lists[bookmarksKey]?.items || []);
|
||||
const isLoading = useAppSelector((state) => state.status_lists[bookmarksKey]?.isLoading === true);
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists[bookmarksKey]?.next);
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchBookmarkedStatuses(folderId));
|
||||
|
|
|
@ -10,8 +10,6 @@ import { makeGetStatus } from 'pl-fe/selectors';
|
|||
|
||||
import PlaceholderEventPreview from '../../placeholder/components/placeholder-event-preview';
|
||||
|
||||
import type { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
|
||||
const Event = ({ id }: { id: string }) => {
|
||||
const getStatus = useCallback(makeGetStatus(), []);
|
||||
const status = useAppSelector(state => getStatus(state, { id }));
|
||||
|
@ -29,7 +27,7 @@ const Event = ({ id }: { id: string }) => {
|
|||
};
|
||||
|
||||
interface IEventCarousel {
|
||||
statusIds: ImmutableOrderedSet<string>;
|
||||
statusIds: Array<string>;
|
||||
isLoading?: boolean | null;
|
||||
emptyMessage: React.ReactNode;
|
||||
}
|
||||
|
@ -38,10 +36,10 @@ const EventCarousel: React.FC<IEventCarousel> = ({ statusIds, isLoading, emptyMe
|
|||
const [index, setIndex] = useState(0);
|
||||
|
||||
const handleChangeIndex = (index: number) => {
|
||||
setIndex(index % statusIds.size);
|
||||
setIndex(index % statusIds.length);
|
||||
};
|
||||
|
||||
if (statusIds.size === 0) {
|
||||
if (statusIds.length === 0) {
|
||||
if (isLoading) {
|
||||
return <PlaceholderEventPreview />;
|
||||
}
|
||||
|
@ -67,7 +65,7 @@ const EventCarousel: React.FC<IEventCarousel> = ({ statusIds, isLoading, emptyMe
|
|||
<ReactSwipeableViews animateHeight index={index} onChangeIndex={handleChangeIndex}>
|
||||
{statusIds.map(statusId => <Event key={statusId} id={statusId} />)}
|
||||
</ReactSwipeableViews>
|
||||
{index !== statusIds.size - 1 && (
|
||||
{index !== statusIds.length - 1 && (
|
||||
<div className='absolute right-3 top-1/2 z-10 -mt-4'>
|
||||
<button
|
||||
onClick={() => handleChangeIndex(index + 1)}
|
||||
|
|
|
@ -20,10 +20,10 @@ const Events = () => {
|
|||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const recentEvents = useAppSelector((state) => state.status_lists.get('recent_events')!.items);
|
||||
const recentEventsLoading = useAppSelector((state) => state.status_lists.get('recent_events')!.isLoading);
|
||||
const joinedEvents = useAppSelector((state) => state.status_lists.get('joined_events')!.items);
|
||||
const joinedEventsLoading = useAppSelector((state) => state.status_lists.get('joined_events')!.isLoading);
|
||||
const recentEvents = useAppSelector((state) => state.status_lists.recent_events!.items);
|
||||
const recentEventsLoading = useAppSelector((state) => state.status_lists.recent_events!.isLoading);
|
||||
const joinedEvents = useAppSelector((state) => state.status_lists.joined_events!.items);
|
||||
const joinedEventsLoading = useAppSelector((state) => state.status_lists.joined_events!.isLoading);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchRecentEvents());
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import debounce from 'lodash/debounce';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
@ -34,9 +33,9 @@ const Favourites: React.FC<IFavourites> = ({ params }) => {
|
|||
const isOwnAccount = username.toLowerCase() === ownAccount?.acct?.toLowerCase();
|
||||
|
||||
const timelineKey = isOwnAccount ? 'favourites' : `favourites:${account?.id}`;
|
||||
const statusIds = useAppSelector(state => state.status_lists.get(timelineKey)?.items || ImmutableOrderedSet<string>());
|
||||
const isLoading = useAppSelector(state => state.status_lists.get(timelineKey)?.isLoading === true);
|
||||
const hasMore = useAppSelector(state => !!state.status_lists.get(timelineKey)?.next);
|
||||
const statusIds = useAppSelector(state => state.status_lists[timelineKey]?.items || []);
|
||||
const isLoading = useAppSelector(state => state.status_lists[timelineKey]?.isLoading === true);
|
||||
const hasMore = useAppSelector(state => !!state.status_lists[timelineKey]?.next);
|
||||
|
||||
const handleLoadMore = useCallback(debounce(() => {
|
||||
if (isOwnAccount) {
|
||||
|
|
|
@ -20,9 +20,9 @@ const PinnedStatuses = () => {
|
|||
const { username } = useParams<{ username: string }>();
|
||||
|
||||
const meUsername = useAppSelector((state) => selectOwnAccount(state)?.username || '');
|
||||
const statusIds = useAppSelector((state) => state.status_lists.get('pins')!.items);
|
||||
const isLoading = useAppSelector((state) => !!state.status_lists.get('pins')!.isLoading);
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists.get('pins')!.next);
|
||||
const statusIds = useAppSelector((state) => state.status_lists.pins!.items);
|
||||
const isLoading = useAppSelector((state) => !!state.status_lists.pins!.isLoading);
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists.pins!.next);
|
||||
|
||||
const isMyAccount = username.toLowerCase() === meUsername.toLowerCase();
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import debounce from 'lodash/debounce';
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
@ -26,9 +25,9 @@ const Quotes: React.FC = () => {
|
|||
const theme = useTheme();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const statusIds = useAppSelector((state) => state.status_lists.getIn([`quotes:${statusId}`, 'items'], ImmutableOrderedSet<string>()));
|
||||
const isLoading = useAppSelector((state) => state.status_lists.getIn([`quotes:${statusId}`, 'isLoading'], true));
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists.getIn([`quotes:${statusId}`, 'next']));
|
||||
const statusIds = useAppSelector((state) => state.status_lists[`quotes:${statusId}`]?.items || []);
|
||||
const isLoading = useAppSelector((state) => state.status_lists[`quotes:${statusId}`]?.isLoading !== false);
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists[`quotes:${statusId}`]?.next);
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchStatusQuotes(statusId));
|
||||
|
@ -41,7 +40,7 @@ const Quotes: React.FC = () => {
|
|||
<StatusList
|
||||
className='black:p-0 black:sm:p-4 black:sm:pt-0'
|
||||
loadMoreClassName='black:sm:mx-4'
|
||||
statusIds={statusIds as ImmutableOrderedSet<string>}
|
||||
statusIds={statusIds}
|
||||
scrollKey={`quotes:${statusId}`}
|
||||
hasMore={hasMore}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
|
|
|
@ -22,9 +22,9 @@ const ScheduledStatuses = () => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const statusIds = useAppSelector((state) => state.status_lists.get('scheduled_statuses')!.items);
|
||||
const isLoading = useAppSelector((state) => state.status_lists.get('scheduled_statuses')!.isLoading);
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists.get('scheduled_statuses')!.next);
|
||||
const statusIds = useAppSelector((state) => state.status_lists.scheduled_statuses!.items);
|
||||
const isLoading = useAppSelector((state) => state.status_lists.scheduled_statuses!.isLoading);
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists.scheduled_statuses!.next);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchScheduledStatuses());
|
||||
|
|
|
@ -34,7 +34,7 @@ const Timeline: React.FC<ITimeline> = ({
|
|||
const getStatusIds = useCallback(makeGetStatusIds(), []);
|
||||
|
||||
const statusIds = useAppSelector(state => getStatusIds(state, { type: timelineId, prefix }));
|
||||
const lastStatusId = statusIds.last();
|
||||
const lastStatusId = statusIds.at(-1);
|
||||
const isLoading = useAppSelector(state => (state.timelines.get(timelineId) || { isLoading: true }).isLoading === true);
|
||||
const isPartial = useAppSelector(state => (state.timelines.get(timelineId)?.isPartial || false) === true);
|
||||
const hasMore = useAppSelector(state => state.timelines.get(timelineId)?.hasMore === true);
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
import {
|
||||
Map as ImmutableMap,
|
||||
OrderedSet as ImmutableOrderedSet,
|
||||
Record as ImmutableRecord,
|
||||
} from 'immutable';
|
||||
import { create } from 'mutative';
|
||||
|
||||
import {
|
||||
STATUS_QUOTES_EXPAND_FAIL,
|
||||
|
@ -72,62 +68,71 @@ import {
|
|||
import type { PaginatedResponse, ScheduledStatus, Status } from 'pl-api';
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
const StatusListRecord = ImmutableRecord({
|
||||
next: null as (() => Promise<PaginatedResponse<Status>>) | null,
|
||||
interface StatusList {
|
||||
next: (() => Promise<PaginatedResponse<Status>>) | null;
|
||||
loaded: boolean;
|
||||
isLoading: boolean | null;
|
||||
items: Array<string>;
|
||||
}
|
||||
|
||||
const newStatusList = (): StatusList => ({
|
||||
next: null,
|
||||
loaded: false,
|
||||
isLoading: null as boolean | null,
|
||||
items: ImmutableOrderedSet<string>(),
|
||||
isLoading: null,
|
||||
items: [],
|
||||
});
|
||||
|
||||
type State = ImmutableMap<string, StatusList>;
|
||||
type StatusList = ReturnType<typeof StatusListRecord>;
|
||||
type State = Record<string, StatusList>;
|
||||
|
||||
const initialState: State = ImmutableMap({
|
||||
favourites: StatusListRecord(),
|
||||
bookmarks: StatusListRecord(),
|
||||
pins: StatusListRecord(),
|
||||
scheduled_statuses: StatusListRecord(),
|
||||
recent_events: StatusListRecord(),
|
||||
joined_events: StatusListRecord(),
|
||||
});
|
||||
const initialState: State = {
|
||||
favourites: newStatusList(),
|
||||
bookmarks: newStatusList(),
|
||||
pins: newStatusList(),
|
||||
scheduled_statuses: 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'>> = []) => (
|
||||
ImmutableOrderedSet(statuses.map(getStatusId))
|
||||
);
|
||||
const getStatusIds = (statuses: Array<string | Pick<Status, 'id'>> = []) => statuses.map(getStatusId);
|
||||
|
||||
const setLoading = (state: State, listType: string, loading: boolean) =>
|
||||
state.update(listType, StatusListRecord(), listMap => listMap.set('isLoading', loading));
|
||||
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) =>
|
||||
state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
|
||||
map.set('next', next);
|
||||
map.set('loaded', true);
|
||||
map.set('isLoading', false);
|
||||
map.set('items', getStatusIds(statuses));
|
||||
}));
|
||||
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);
|
||||
|
||||
return state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
|
||||
map.set('next', next);
|
||||
map.set('isLoading', false);
|
||||
map.update('items', items => items.union(newIds));
|
||||
}));
|
||||
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);
|
||||
return state.update(listType, StatusListRecord(), listMap => listMap.update('items', items =>
|
||||
ImmutableOrderedSet([statusId]).union(items as ImmutableOrderedSet<string>),
|
||||
));
|
||||
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);
|
||||
return state.update(listType, StatusListRecord(), listMap => listMap.update('items', items => items.delete(statusId)));
|
||||
const list = state[listType] = state[listType] || newStatusList();
|
||||
|
||||
list.items = list.items.filter(id => id !== statusId);
|
||||
};
|
||||
|
||||
const maybeAppendScheduledStatus = (state: State, status: Pick<ScheduledStatus | Status, 'id' | 'scheduled_at'>) => {
|
||||
|
@ -136,112 +141,109 @@ const maybeAppendScheduledStatus = (state: State, status: Pick<ScheduledStatus |
|
|||
};
|
||||
|
||||
const addBookmarkToLists = (state: State, status: Pick<Status, 'id' | 'bookmark_folder'>) => {
|
||||
state = prependOneToList(state, 'bookmarks', status);
|
||||
prependOneToList(state, 'bookmarks', status);
|
||||
const folderId = status.bookmark_folder;
|
||||
if (folderId) {
|
||||
return prependOneToList(state, `bookmarks:${folderId}`, status);
|
||||
prependOneToList(state, `bookmarks:${folderId}`, status);
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
const removeBookmarkFromLists = (state: State, status: Pick<Status, 'id' | 'bookmark_folder'>) => {
|
||||
state = removeOneFromList(state, 'bookmarks', status);
|
||||
removeOneFromList(state, 'bookmarks', status);
|
||||
const folderId = status.bookmark_folder;
|
||||
if (folderId) {
|
||||
return removeOneFromList(state, `bookmarks:${folderId}`, status);
|
||||
removeOneFromList(state, `bookmarks:${folderId}`, status);
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
const statusLists = (state = initialState, action: AnyAction | BookmarksAction | EventsAction | FavouritesAction | InteractionsAction | PinStatusesAction | StatusQuotesAction) => {
|
||||
const statusLists = (state = initialState, action: AnyAction | BookmarksAction | EventsAction | FavouritesAction | InteractionsAction | PinStatusesAction | StatusQuotesAction): State => {
|
||||
switch (action.type) {
|
||||
case FAVOURITED_STATUSES_FETCH_REQUEST:
|
||||
case FAVOURITED_STATUSES_EXPAND_REQUEST:
|
||||
return setLoading(state, 'favourites', true);
|
||||
return create(state, draft => setLoading(draft, 'favourites', true));
|
||||
case FAVOURITED_STATUSES_FETCH_FAIL:
|
||||
case FAVOURITED_STATUSES_EXPAND_FAIL:
|
||||
return setLoading(state, 'favourites', false);
|
||||
return create(state, draft => setLoading(draft, 'favourites', false));
|
||||
case FAVOURITED_STATUSES_FETCH_SUCCESS:
|
||||
return normalizeList(state, 'favourites', action.statuses, action.next);
|
||||
return create(state, draft => normalizeList(draft, 'favourites', action.statuses, action.next));
|
||||
case FAVOURITED_STATUSES_EXPAND_SUCCESS:
|
||||
return appendToList(state, 'favourites', action.statuses, action.next);
|
||||
return create(state, draft => appendToList(draft, 'favourites', action.statuses, action.next));
|
||||
case ACCOUNT_FAVOURITED_STATUSES_FETCH_REQUEST:
|
||||
case ACCOUNT_FAVOURITED_STATUSES_EXPAND_REQUEST:
|
||||
return setLoading(state, `favourites:${action.accountId}`, true);
|
||||
return create(state, draft => setLoading(draft, `favourites:${action.accountId}`, true));
|
||||
case ACCOUNT_FAVOURITED_STATUSES_FETCH_FAIL:
|
||||
case ACCOUNT_FAVOURITED_STATUSES_EXPAND_FAIL:
|
||||
return setLoading(state, `favourites:${action.accountId}`, false);
|
||||
return create(state, draft => setLoading(draft, `favourites:${action.accountId}`, false));
|
||||
case ACCOUNT_FAVOURITED_STATUSES_FETCH_SUCCESS:
|
||||
return normalizeList(state, `favourites:${action.accountId}`, action.statuses, action.next);
|
||||
return create(state, draft => normalizeList(draft, `favourites:${action.accountId}`, action.statuses, action.next));
|
||||
case ACCOUNT_FAVOURITED_STATUSES_EXPAND_SUCCESS:
|
||||
return appendToList(state, `favourites:${action.accountId}`, action.statuses, action.next);
|
||||
return create(state, draft => appendToList(draft, `favourites:${action.accountId}`, action.statuses, action.next));
|
||||
case BOOKMARKED_STATUSES_FETCH_REQUEST:
|
||||
case BOOKMARKED_STATUSES_EXPAND_REQUEST:
|
||||
return setLoading(state, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', true);
|
||||
return create(state, draft => setLoading(draft, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', true));
|
||||
case BOOKMARKED_STATUSES_FETCH_FAIL:
|
||||
case BOOKMARKED_STATUSES_EXPAND_FAIL:
|
||||
return setLoading(state, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', false);
|
||||
return create(state, draft => setLoading(draft, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', false));
|
||||
case BOOKMARKED_STATUSES_FETCH_SUCCESS:
|
||||
return normalizeList(state, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', action.statuses, action.next);
|
||||
return create(state, draft => normalizeList(draft, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', action.statuses, action.next));
|
||||
case BOOKMARKED_STATUSES_EXPAND_SUCCESS:
|
||||
return appendToList(state, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', action.statuses, action.next);
|
||||
return create(state, draft => appendToList(draft, action.folderId ? `bookmarks:${action.folderId}` : 'bookmarks', action.statuses, action.next));
|
||||
case FAVOURITE_SUCCESS:
|
||||
return prependOneToList(state, 'favourites', action.status);
|
||||
return create(state, draft => prependOneToList(draft, 'favourites', action.status));
|
||||
case UNFAVOURITE_SUCCESS:
|
||||
return removeOneFromList(state, 'favourites', action.status);
|
||||
return create(state, draft => removeOneFromList(draft, 'favourites', action.status));
|
||||
case BOOKMARK_SUCCESS:
|
||||
return addBookmarkToLists(state, action.status);
|
||||
return create(state, draft => addBookmarkToLists(draft, action.status));
|
||||
case UNBOOKMARK_SUCCESS:
|
||||
return removeBookmarkFromLists(state, action.status);
|
||||
return create(state, draft => removeBookmarkFromLists(draft, action.status));
|
||||
case PINNED_STATUSES_FETCH_SUCCESS:
|
||||
return normalizeList(state, 'pins', action.statuses, action.next);
|
||||
return create(state, draft => normalizeList(draft, 'pins', action.statuses, action.next));
|
||||
case PIN_SUCCESS:
|
||||
return prependOneToList(state, 'pins', action.status);
|
||||
return create(state, draft => prependOneToList(draft, 'pins', action.status));
|
||||
case UNPIN_SUCCESS:
|
||||
return removeOneFromList(state, 'pins', action.status);
|
||||
return create(state, draft => removeOneFromList(draft, 'pins', action.status));
|
||||
case SCHEDULED_STATUSES_FETCH_REQUEST:
|
||||
case SCHEDULED_STATUSES_EXPAND_REQUEST:
|
||||
return setLoading(state, 'scheduled_statuses', true);
|
||||
return create(state, draft => setLoading(draft, 'scheduled_statuses', true));
|
||||
case SCHEDULED_STATUSES_FETCH_FAIL:
|
||||
case SCHEDULED_STATUSES_EXPAND_FAIL:
|
||||
return setLoading(state, 'scheduled_statuses', false);
|
||||
return create(state, draft => setLoading(draft, 'scheduled_statuses', false));
|
||||
case SCHEDULED_STATUSES_FETCH_SUCCESS:
|
||||
return normalizeList(state, 'scheduled_statuses', action.statuses, action.next);
|
||||
return create(state, draft => normalizeList(draft, 'scheduled_statuses', action.statuses, action.next));
|
||||
case SCHEDULED_STATUSES_EXPAND_SUCCESS:
|
||||
return appendToList(state, 'scheduled_statuses', action.statuses, action.next);
|
||||
return create(state, draft => appendToList(draft, 'scheduled_statuses', action.statuses, action.next));
|
||||
case SCHEDULED_STATUS_CANCEL_REQUEST:
|
||||
case SCHEDULED_STATUS_CANCEL_SUCCESS:
|
||||
return removeOneFromList(state, 'scheduled_statuses', action.statusId);
|
||||
return create(state, draft => removeOneFromList(draft, 'scheduled_statuses', action.statusId));
|
||||
case STATUS_QUOTES_FETCH_REQUEST:
|
||||
case STATUS_QUOTES_EXPAND_REQUEST:
|
||||
return setLoading(state, `quotes:${action.statusId}`, true);
|
||||
return create(state, draft => setLoading(draft, `quotes:${action.statusId}`, true));
|
||||
case STATUS_QUOTES_FETCH_FAIL:
|
||||
case STATUS_QUOTES_EXPAND_FAIL:
|
||||
return setLoading(state, `quotes:${action.statusId}`, false);
|
||||
return create(state, draft => setLoading(draft, `quotes:${action.statusId}`, false));
|
||||
case STATUS_QUOTES_FETCH_SUCCESS:
|
||||
return normalizeList(state, `quotes:${action.statusId}`, action.statuses, action.next);
|
||||
return create(state, draft => normalizeList(draft, `quotes:${action.statusId}`, action.statuses, action.next));
|
||||
case STATUS_QUOTES_EXPAND_SUCCESS:
|
||||
return appendToList(state, `quotes:${action.statusId}`, action.statuses, action.next);
|
||||
return create(state, draft => appendToList(draft, `quotes:${action.statusId}`, action.statuses, action.next));
|
||||
case RECENT_EVENTS_FETCH_REQUEST:
|
||||
return setLoading(state, 'recent_events', true);
|
||||
return create(state, draft => setLoading(draft, 'recent_events', true));
|
||||
case RECENT_EVENTS_FETCH_FAIL:
|
||||
return setLoading(state, 'recent_events', false);
|
||||
return create(state, draft => setLoading(draft, 'recent_events', false));
|
||||
case RECENT_EVENTS_FETCH_SUCCESS:
|
||||
return normalizeList(state, 'recent_events', action.statuses, action.next);
|
||||
return create(state, draft => normalizeList(draft, 'recent_events', action.statuses, action.next));
|
||||
case JOINED_EVENTS_FETCH_REQUEST:
|
||||
return setLoading(state, 'joined_events', true);
|
||||
return create(state, draft => setLoading(draft, 'joined_events', true));
|
||||
case JOINED_EVENTS_FETCH_FAIL:
|
||||
return setLoading(state, 'joined_events', false);
|
||||
return create(state, draft => setLoading(draft, 'joined_events', false));
|
||||
case JOINED_EVENTS_FETCH_SUCCESS:
|
||||
return normalizeList(state, 'joined_events', action.statuses, action.next);
|
||||
return create(state, draft => normalizeList(draft, 'joined_events', action.statuses, action.next));
|
||||
case STATUS_CREATE_SUCCESS:
|
||||
return maybeAppendScheduledStatus(state, action.status);
|
||||
return create(state, draft => maybeAppendScheduledStatus(draft, action.status));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
StatusListRecord,
|
||||
statusLists as default,
|
||||
};
|
||||
|
|
|
@ -347,7 +347,7 @@ const makeGetStatusIds = () => createSelector([
|
|||
const status = statuses[id];
|
||||
if (!status) return true;
|
||||
return !shouldFilter(status, columnSettings);
|
||||
}),
|
||||
}).toArray(),
|
||||
);
|
||||
|
||||
export {
|
||||
|
|
Loading…
Reference in a new issue