pl-fe: migrate pinned statuses list
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -1,35 +0,0 @@
|
||||
import { isLoggedIn } from 'pl-fe/utils/auth';
|
||||
|
||||
import { getClient } from '../api';
|
||||
|
||||
import { importEntities } from './importer';
|
||||
|
||||
import type { PaginatedResponse, Status } from 'pl-api';
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
|
||||
const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS' as const;
|
||||
|
||||
const fetchPinnedStatuses = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
const me = getState().me;
|
||||
|
||||
return getClient(getState()).accounts.getAccountStatuses(me as string, { pinned: true }).then(response => {
|
||||
dispatch(importEntities({ statuses: response.items }));
|
||||
dispatch(fetchPinnedStatusesSuccess(response.items, response.next));
|
||||
});
|
||||
};
|
||||
|
||||
const fetchPinnedStatusesSuccess = (statuses: Array<Status>, next: (() => Promise<PaginatedResponse<Status>>) | null) => ({
|
||||
type: PINNED_STATUSES_FETCH_SUCCESS,
|
||||
statuses,
|
||||
next,
|
||||
});
|
||||
|
||||
type PinStatusesAction = ReturnType<typeof fetchPinnedStatusesSuccess>;
|
||||
|
||||
export {
|
||||
PINNED_STATUSES_FETCH_SUCCESS,
|
||||
fetchPinnedStatuses,
|
||||
type PinStatusesAction,
|
||||
};
|
||||
@ -1,14 +1,12 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { fetchPinnedStatuses } from 'pl-fe/actions/pin-statuses';
|
||||
import MissingIndicator from 'pl-fe/components/missing-indicator';
|
||||
import StatusList from 'pl-fe/components/status-list';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { selectOwnAccount } from 'pl-fe/selectors';
|
||||
import { useOwnAccount } from 'pl-fe/hooks/use-own-account';
|
||||
import { usePinnedStatuses } from 'pl-fe/queries/status-lists/use-pinned-statuses';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.pins', defaultMessage: 'Pinned posts' },
|
||||
@ -16,19 +14,11 @@ const messages = defineMessages({
|
||||
|
||||
const PinnedStatusesPage = () => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const { username } = useParams<{ username: string }>();
|
||||
|
||||
const meUsername = useAppSelector((state) => selectOwnAccount(state)?.username || '');
|
||||
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();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchPinnedStatuses());
|
||||
}, []);
|
||||
const { account } = useOwnAccount();
|
||||
const { data: statusIds = [], isFetching: isLoading, hasNextPage: hasMore } = usePinnedStatuses(account?.id || '');
|
||||
const isMyAccount = username.toLowerCase() === account?.username.toLowerCase();
|
||||
|
||||
if (!isMyAccount) {
|
||||
return (
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import { makePaginatedResponseQuery } from 'pl-fe/queries/utils/make-paginated-response-query';
|
||||
import { minifyStatusList } from 'pl-fe/queries/utils/minify-list';
|
||||
|
||||
const usePinnedStatuses = makePaginatedResponseQuery(
|
||||
(accountId: string) => ['statusLists', 'pins', accountId],
|
||||
(client, [accountId]) => client.accounts.getAccountStatuses(accountId as string, { pinned: true }).then(minifyStatusList),
|
||||
undefined,
|
||||
(accountId: string) => !!accountId,
|
||||
);
|
||||
|
||||
export { usePinnedStatuses };
|
||||
@ -6,6 +6,7 @@ import { PIN_SUCCESS, UNPIN_SUCCESS, type InteractionsAction } from 'pl-fe/actio
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useClient } from 'pl-fe/hooks/use-client';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
|
||||
import { makePaginatedResponseQuery } from 'pl-fe/queries/utils/make-paginated-response-query';
|
||||
import { minifyAccountList } from 'pl-fe/queries/utils/minify-list';
|
||||
import { useModalsStore } from 'pl-fe/stores/modals';
|
||||
@ -237,12 +238,15 @@ const useUnbookmarkStatus = (statusId: string) => {
|
||||
const usePinStatus = (statusId: string) => {
|
||||
const client = useClient();
|
||||
const dispatch = useAppDispatch();
|
||||
const queryClient = useQueryClient();
|
||||
const { me } = useLoggedIn();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['statuses', 'pin', statusId],
|
||||
mutationFn: () => client.statuses.pinStatus(statusId),
|
||||
onSuccess: (status) => {
|
||||
dispatch(importEntities({ statuses: [status] }));
|
||||
queryClient.invalidateQueries({ queryKey: ['statusLists', 'pins', me] });
|
||||
dispatch<InteractionsAction>({ type: PIN_SUCCESS, statusId: status.id, accountId: status.account.id });
|
||||
},
|
||||
});
|
||||
@ -251,12 +255,15 @@ const usePinStatus = (statusId: string) => {
|
||||
const useUnpinStatus = (statusId: string) => {
|
||||
const client = useClient();
|
||||
const dispatch = useAppDispatch();
|
||||
const queryClient = useQueryClient();
|
||||
const { me } = useLoggedIn();
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ['statuses', 'unpin', statusId],
|
||||
mutationFn: () => client.statuses.unpinStatus(statusId),
|
||||
onSuccess: (status) => {
|
||||
dispatch(importEntities({ statuses: [status] }));
|
||||
queryClient.setQueryData(['statusLists', 'pins', me], filterById(statusId));
|
||||
dispatch<InteractionsAction>({ type: UNPIN_SUCCESS, statusId: status.id, accountId: status.account.id });
|
||||
},
|
||||
});
|
||||
|
||||
@ -21,7 +21,6 @@ import plfe from './pl-fe';
|
||||
import polls from './polls';
|
||||
import push_notifications from './push-notifications';
|
||||
import shoutbox from './shoutbox';
|
||||
import status_lists from './status-lists';
|
||||
import statuses from './statuses';
|
||||
import timelines from './timelines';
|
||||
|
||||
@ -44,7 +43,6 @@ const reducers = {
|
||||
polls,
|
||||
push_notifications,
|
||||
shoutbox,
|
||||
status_lists,
|
||||
statuses,
|
||||
timelines,
|
||||
};
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
import reducer from './status-lists';
|
||||
|
||||
describe('status_lists reducer', () => {
|
||||
it('should return the initial state', () => {
|
||||
expect(reducer(undefined, {} as any).toJS()).toEqual({
|
||||
pins: {
|
||||
next: null,
|
||||
loaded: false,
|
||||
isLoading: null,
|
||||
items: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,75 +0,0 @@
|
||||
import { create } from 'mutative';
|
||||
|
||||
import {
|
||||
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';
|
||||
|
||||
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(),
|
||||
pins: 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 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 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 statusLists = (state = initialState, action: InteractionsAction | PinStatusesAction): State => {
|
||||
switch (action.type) {
|
||||
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.statusId));
|
||||
case UNPIN_SUCCESS:
|
||||
return create(state, draft => removeOneFromList(draft, 'pins', action.statusId));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
statusLists as default,
|
||||
};
|
||||
Reference in New Issue
Block a user