diff --git a/packages/pl-fe/src/actions/statuses.ts b/packages/pl-fe/src/actions/statuses.ts index bb8e5c767..005794f65 100644 --- a/packages/pl-fe/src/actions/statuses.ts +++ b/packages/pl-fe/src/actions/statuses.ts @@ -1,6 +1,7 @@ import { queryClient } from '@/queries/client'; import { scheduledStatusesQueryOptions } from '@/queries/statuses/scheduled-statuses'; import { useModalsStore } from '@/stores/modals'; +import { usePendingStatusesStore } from '@/stores/pending-statuses'; import { useSettingsStore } from '@/stores/settings'; import { isLoggedIn } from '@/utils/auth'; import { shouldHaveCard } from '@/utils/status'; @@ -50,7 +51,8 @@ const createStatus = redacting = false, ) => (dispatch: AppDispatch, getState: () => RootState) => { - if (!params.preview) + if (!params.preview) { + usePendingStatusesStore.getState().actions.importStatus(params, idempotencyKey); dispatch({ type: STATUS_CREATE_REQUEST, params, @@ -58,6 +60,7 @@ const createStatus = editing: !!editedId, redacting, }); + } const client = getClient(getState()); @@ -116,6 +119,7 @@ const createStatus = return status; }) .catch((error) => { + usePendingStatusesStore.getState().actions.deleteStatus(idempotencyKey); dispatch({ type: STATUS_CREATE_FAIL, error, @@ -195,6 +199,7 @@ const deleteStatus = : getClient(state).statuses.deleteStatus(statusId) ) .then((response) => { + usePendingStatusesStore.getState().actions.deleteStatus(statusId); dispatch({ type: STATUS_DELETE_SUCCESS, statusId }); dispatch(deleteFromTimelines(statusId)); diff --git a/packages/pl-fe/src/actions/timelines.ts b/packages/pl-fe/src/actions/timelines.ts index 79a0e88da..ca72eedd3 100644 --- a/packages/pl-fe/src/actions/timelines.ts +++ b/packages/pl-fe/src/actions/timelines.ts @@ -1,4 +1,5 @@ import { getLocale } from '@/actions/settings'; +import { usePendingStatusesStore } from '@/stores/pending-statuses'; import { useSettingsStore } from '@/stores/settings'; import { shouldFilter } from '@/utils/timelines'; @@ -39,7 +40,8 @@ const processTimelineUpdate = (timeline: string, status: BaseStatus) => (dispatch: AppDispatch, getState: () => RootState) => { const me = getState().me; const ownStatus = status.account?.id === me; - const hasPendingStatuses = !!getState().pending_statuses.length; + + const hasPendingStatuses = Object.keys(usePendingStatusesStore.getState().statuses).length > 0; const columnSettings = useSettingsStore.getState().settings.timelines[timeline]; const shouldSkipQueue = shouldFilter( diff --git a/packages/pl-fe/src/features/ui/components/pending-status.tsx b/packages/pl-fe/src/features/ui/components/pending-status.tsx index 3ad1ab47e..f35365550 100644 --- a/packages/pl-fe/src/features/ui/components/pending-status.tsx +++ b/packages/pl-fe/src/features/ui/components/pending-status.tsx @@ -11,6 +11,7 @@ import PlaceholderCard from '@/features/placeholder/components/placeholder-card' import PlaceholderMediaGallery from '@/features/placeholder/components/placeholder-media-gallery'; import QuotedStatus from '@/features/status/containers/quoted-status-container'; import { useAppSelector } from '@/hooks/use-app-selector'; +import { usePendingStatus } from '@/stores/pending-statuses'; import { buildStatus } from '../util/pending-status-builder'; @@ -48,8 +49,9 @@ const PendingStatus: React.FC = ({ muted, variant = 'rounded', }) => { + const pendingStatus = usePendingStatus(idempotencyKey); + const status = useAppSelector((state) => { - const pendingStatus = state.pending_statuses[idempotencyKey]; return pendingStatus ? buildStatus(state, pendingStatus, idempotencyKey) : null; }); diff --git a/packages/pl-fe/src/reducers/index.ts b/packages/pl-fe/src/reducers/index.ts index 3ba8d9eb8..68bb0fdaf 100644 --- a/packages/pl-fe/src/reducers/index.ts +++ b/packages/pl-fe/src/reducers/index.ts @@ -15,7 +15,6 @@ import instance from './instance'; import me from './me'; import meta from './meta'; import notifications from './notifications'; -import pending_statuses from './pending-statuses'; import push_notifications from './push-notifications'; import statuses from './statuses'; import timelines from './timelines'; @@ -33,7 +32,6 @@ const reducers = { me, meta, notifications, - pending_statuses, push_notifications, statuses, timelines, diff --git a/packages/pl-fe/src/reducers/pending-statuses.ts b/packages/pl-fe/src/reducers/pending-statuses.ts deleted file mode 100644 index 167d79422..000000000 --- a/packages/pl-fe/src/reducers/pending-statuses.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { create } from 'mutative'; -import { CreateStatusParams } from 'pl-api'; - -import { - STATUS_CREATE_FAIL, - STATUS_CREATE_REQUEST, - STATUS_CREATE_SUCCESS, - type StatusesAction, -} from '@/actions/statuses'; - -import type { StatusVisibility } from '@/normalizers/status'; - -interface PendingStatus { - content_type: string; - in_reply_to_id: string | null; - media_ids: Array | null; - quote_id: string | null; - poll: Exclude | null; - sensitive: boolean; - spoiler_text: string; - status: string; - to: Array | null; - visibility: StatusVisibility; -} - -const newPendingStatus = (props: Partial = {}): PendingStatus => ({ - content_type: '', - in_reply_to_id: null, - media_ids: null, - quote_id: null, - poll: null, - sensitive: false, - spoiler_text: '', - status: '', - to: null, - visibility: 'public', - ...props, -}); - -type State = Record; - -const initialState: State = {}; - -const importStatus = (state: State, params: Record, idempotencyKey: string) => { - state[idempotencyKey] = newPendingStatus(params); -}; - -const deleteStatus = (state: State, idempotencyKey: string) => { - delete state[idempotencyKey]; -}; - -const pending_statuses = (state = initialState, action: StatusesAction): State => { - switch (action.type) { - case STATUS_CREATE_REQUEST: - if (action.editing) return state; - return create(state, (draft) => { - importStatus(draft, action.params, action.idempotencyKey); - }); - case STATUS_CREATE_FAIL: - case STATUS_CREATE_SUCCESS: - return create(state, (draft) => { - deleteStatus(draft, action.idempotencyKey); - }); - default: - return state; - } -}; - -export { type PendingStatus, pending_statuses as default }; diff --git a/packages/pl-fe/src/stores/pending-statuses.ts b/packages/pl-fe/src/stores/pending-statuses.ts new file mode 100644 index 000000000..32af2bd5f --- /dev/null +++ b/packages/pl-fe/src/stores/pending-statuses.ts @@ -0,0 +1,61 @@ +import { CreateStatusParams } from 'pl-api'; +import { create } from 'zustand'; +import { mutative } from 'zustand-mutative'; + +import type { StatusVisibility } from '@/normalizers/status'; + +interface PendingStatus { + content_type: string; + in_reply_to_id: string | null; + media_ids: Array | null; + quote_id: string | null; + poll: Exclude | null; + sensitive: boolean; + spoiler_text: string; + status: string; + to: Array | null; + visibility: StatusVisibility; +} + +type State = { + statuses: Record; + actions: { + importStatus: (params: Partial, idempotencyKey: string) => void; + deleteStatus: (idempotencyKey: string) => void; + }; +}; + +const usePendingStatusesStore = create()( + mutative((set) => ({ + statuses: {}, + actions: { + importStatus: (params, idempotencyKey) => { + set((state: State) => { + state.statuses[idempotencyKey] = { + content_type: '', + in_reply_to_id: null, + media_ids: null, + quote_id: null, + poll: null, + sensitive: false, + spoiler_text: '', + status: '', + to: null, + visibility: 'public', + ...params, + }; + }); + }, + deleteStatus: (idempotencyKey) => { + set((state: State) => { + delete state.statuses[idempotencyKey]; + }); + }, + }, + })), +); + +const usePendingStatus = (id: string) => usePendingStatusesStore((state) => state.statuses[id]); +const usePendingStatusesActions = () => usePendingStatusesStore((state) => state.actions); + +export { usePendingStatusesStore, usePendingStatus, usePendingStatusesActions };