pl-fe: migrate drafts to mutative
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
4ddc5b38d1
commit
5c58196201
6 changed files with 74 additions and 48 deletions
|
@ -3,27 +3,40 @@ import { makeGetAccount } from 'pl-fe/selectors';
|
|||
import KVStore from 'pl-fe/storage/kv-store';
|
||||
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
import type { APIEntity } from 'pl-fe/types/entities';
|
||||
|
||||
const DRAFT_STATUSES_FETCH_SUCCESS = 'DRAFT_STATUSES_FETCH_SUCCESS';
|
||||
|
||||
const PERSIST_DRAFT_STATUS = 'PERSIST_DRAFT_STATUS';
|
||||
const CANCEL_DRAFT_STATUS = 'DELETE_DRAFT_STATUS';
|
||||
const DRAFT_STATUSES_FETCH_SUCCESS = 'DRAFT_STATUSES_FETCH_SUCCESS' as const;
|
||||
|
||||
const PERSIST_DRAFT_STATUS = 'PERSIST_DRAFT_STATUS' as const;
|
||||
const CANCEL_DRAFT_STATUS = 'DELETE_DRAFT_STATUS' as const;
|
||||
|
||||
const getAccount = makeGetAccount();
|
||||
|
||||
interface DraftStatusesFetchSuccessAction {
|
||||
type: typeof DRAFT_STATUSES_FETCH_SUCCESS;
|
||||
statuses: Array<APIEntity>;
|
||||
}
|
||||
|
||||
const fetchDraftStatuses = () =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
const accountUrl = getAccount(state, state.me as string)!.url;
|
||||
|
||||
return KVStore.getItem(`drafts:${accountUrl}`).then((statuses) => {
|
||||
dispatch({
|
||||
return KVStore.getItem<Array<APIEntity>>(`drafts:${accountUrl}`).then((statuses) => {
|
||||
dispatch<DraftStatusesFetchSuccessAction>({
|
||||
type: DRAFT_STATUSES_FETCH_SUCCESS,
|
||||
statuses,
|
||||
});
|
||||
}).catch(() => {});
|
||||
};
|
||||
|
||||
interface PersistDraftStatusAction {
|
||||
type: typeof PERSIST_DRAFT_STATUS;
|
||||
status: Record<string, any>;
|
||||
accountUrl: string;
|
||||
}
|
||||
|
||||
const saveDraftStatus = (composeId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
|
@ -36,25 +49,36 @@ const saveDraftStatus = (composeId: string) =>
|
|||
draft_id: compose.draft_id || crypto.randomUUID(),
|
||||
};
|
||||
|
||||
dispatch({
|
||||
dispatch<PersistDraftStatusAction>({
|
||||
type: PERSIST_DRAFT_STATUS,
|
||||
status: draft,
|
||||
accountUrl,
|
||||
});
|
||||
};
|
||||
|
||||
interface CancelDraftStatusAction {
|
||||
type: typeof CANCEL_DRAFT_STATUS;
|
||||
statusId: string;
|
||||
accountUrl: string;
|
||||
}
|
||||
|
||||
const cancelDraftStatus = (statusId: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
const accountUrl = getAccount(state, state.me as string)!.url;
|
||||
|
||||
dispatch({
|
||||
dispatch<CancelDraftStatusAction>({
|
||||
type: CANCEL_DRAFT_STATUS,
|
||||
statusId,
|
||||
accountUrl,
|
||||
});
|
||||
};
|
||||
|
||||
type DraftStatusesAction =
|
||||
| DraftStatusesFetchSuccessAction
|
||||
| PersistDraftStatusAction
|
||||
| CancelDraftStatusAction
|
||||
|
||||
export {
|
||||
DRAFT_STATUSES_FETCH_SUCCESS,
|
||||
PERSIST_DRAFT_STATUS,
|
||||
|
@ -62,4 +86,5 @@ export {
|
|||
fetchDraftStatuses,
|
||||
saveDraftStatus,
|
||||
cancelDraftStatus,
|
||||
type DraftStatusesAction,
|
||||
};
|
||||
|
|
|
@ -99,7 +99,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
const followRequestsCount = useAppSelector((state) => state.user_lists.follow_requests.items.length);
|
||||
const interactionRequestsCount = useInteractionRequestsCount().data || 0;
|
||||
const scheduledStatusCount = useAppSelector((state) => state.scheduled_statuses.size);
|
||||
const draftCount = useAppSelector((state) => state.draft_statuses.size);
|
||||
const draftCount = useAppSelector((state) => Object.keys(state.draft_statuses).length);
|
||||
// const dashboardCount = useAppSelector((state) => state.admin.openReports.count() + state.admin.awaitingApproval.count());
|
||||
const [sidebarVisible, setSidebarVisible] = useState(isSidebarOpen);
|
||||
const touchStart = useRef(0);
|
||||
|
|
|
@ -52,7 +52,7 @@ const SidebarNavigation = () => {
|
|||
const interactionRequestsCount = useInteractionRequestsCount().data || 0;
|
||||
const dashboardCount = useAppSelector((state) => state.admin.openReports.length + state.admin.awaitingApproval.length);
|
||||
const scheduledStatusCount = useAppSelector((state) => state.scheduled_statuses.size);
|
||||
const draftCount = useAppSelector((state) => state.draft_statuses.size);
|
||||
const draftCount = useAppSelector((state) => Object.keys(state.draft_statuses).length);
|
||||
|
||||
const restrictUnauth = instance.pleroma.metadata.restrict_unauthenticated;
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@ import type { DraftStatus } from 'pl-fe/reducers/draft-statuses';
|
|||
import type { RootState } from 'pl-fe/store';
|
||||
|
||||
const buildPoll = (draftStatus: DraftStatus) => {
|
||||
if (draftStatus.hasIn(['poll', 'options'])) {
|
||||
if (draftStatus.poll?.options) {
|
||||
return {
|
||||
...draftStatus.poll!.toJS(),
|
||||
...draftStatus.poll,
|
||||
id: `${draftStatus.draft_id}-poll`,
|
||||
options: draftStatus.poll!.get('options').map((title: string) => ({ title })).toArray(),
|
||||
options: draftStatus.poll.options.map((title: string) => ({ title })).toArray(),
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
|
|
|
@ -31,7 +31,7 @@ const DraftStatuses = () => {
|
|||
emptyMessage={emptyMessage}
|
||||
listClassName='divide-y divide-solid divide-gray-200 dark:divide-gray-800'
|
||||
>
|
||||
{drafts.toOrderedSet().reverse().map((draft) => <DraftStatus key={draft.draft_id} draftStatus={draft} />)}
|
||||
{Object.values(drafts).toReversed().map((draft) => <DraftStatus key={draft.draft_id} draftStatus={draft} />)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
);
|
||||
|
|
|
@ -1,63 +1,64 @@
|
|||
import { List as ImmutableList, Map as ImmutableMap, OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord, fromJS } from 'immutable';
|
||||
import { create } from 'mutative';
|
||||
import { mediaAttachmentSchema } from 'pl-api';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import { COMPOSE_SUBMIT_SUCCESS, type ComposeAction } from 'pl-fe/actions/compose';
|
||||
import { DRAFT_STATUSES_FETCH_SUCCESS, PERSIST_DRAFT_STATUS, CANCEL_DRAFT_STATUS } from 'pl-fe/actions/draft-statuses';
|
||||
import { DRAFT_STATUSES_FETCH_SUCCESS, PERSIST_DRAFT_STATUS, CANCEL_DRAFT_STATUS, DraftStatusesAction } from 'pl-fe/actions/draft-statuses';
|
||||
import { filteredArray } from 'pl-fe/schemas/utils';
|
||||
import KVStore from 'pl-fe/storage/kv-store';
|
||||
|
||||
import type { MediaAttachment } from 'pl-api';
|
||||
import type { StatusVisibility } from 'pl-fe/normalizers/status';
|
||||
import type { APIEntity } from 'pl-fe/types/entities';
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
const DraftStatusRecord = ImmutableRecord({
|
||||
content_type: 'text/plain',
|
||||
draft_id: '',
|
||||
editorState: null as string | null,
|
||||
group_id: null as string | null,
|
||||
in_reply_to: null as string | null,
|
||||
media_attachments: ImmutableList<MediaAttachment>(),
|
||||
poll: null as ImmutableMap<string, any> | null,
|
||||
privacy: 'public' as StatusVisibility,
|
||||
quote: null as string | null,
|
||||
schedule: null as Date | null,
|
||||
sensitive: false,
|
||||
spoiler: false,
|
||||
spoiler_text: '',
|
||||
text: '',
|
||||
to: ImmutableOrderedSet<string>(),
|
||||
const draftStatusSchema = v.object({
|
||||
content_type: v.fallback(v.string(), 'text/plain'),
|
||||
draft_id: v.string(),
|
||||
editorState: v.fallback(v.nullable(v.string()), null),
|
||||
group_id: v.fallback(v.nullable(v.string()), null),
|
||||
in_reply_to: v.fallback(v.nullable(v.string()), null),
|
||||
media_attachments: filteredArray(mediaAttachmentSchema),
|
||||
poll: v.fallback(v.nullable(v.record(v.string(), v.any())), null),
|
||||
privacy: v.fallback(v.string(), 'public'),
|
||||
quote: v.fallback(v.nullable(v.string()), null),
|
||||
schedule: v.fallback(v.nullable(v.string()), null),
|
||||
sensitive: v.fallback(v.boolean(), false),
|
||||
spoiler: v.fallback(v.boolean(), false),
|
||||
spoiler_text: v.fallback(v.string(), ''),
|
||||
text: v.fallback(v.string(), ''),
|
||||
});
|
||||
|
||||
type DraftStatus = ReturnType<typeof DraftStatusRecord>;
|
||||
type State = ImmutableMap<string, DraftStatus>;
|
||||
type DraftStatus = v.InferOutput<typeof draftStatusSchema>;
|
||||
type State = Record<string, DraftStatus>;
|
||||
|
||||
const initialState: State = ImmutableMap();
|
||||
const initialState: State = {};
|
||||
|
||||
const importStatus = (state: State, status: APIEntity) =>
|
||||
state.set(status.draft_id, DraftStatusRecord(ImmutableMap(fromJS(status))));
|
||||
const importStatus = (state: State, status: APIEntity) => {
|
||||
state[status.draft_id] = v.parse(draftStatusSchema, status);
|
||||
};
|
||||
|
||||
const importStatuses = (state: State, statuses: APIEntity[]) =>
|
||||
state.withMutations(mutable => Object.values(statuses || {}).forEach(status => importStatus(mutable, status)));
|
||||
const importStatuses = (state: State, statuses: APIEntity[]) => {
|
||||
Object.values(statuses || {}).forEach(status => importStatus(state, status));
|
||||
};
|
||||
|
||||
const deleteStatus = (state: State, statusId: string) => {
|
||||
if (statusId) return state.delete(statusId);
|
||||
if (statusId) delete state[statusId];
|
||||
return state;
|
||||
};
|
||||
|
||||
const persistState = (state: State, accountUrl: string) => {
|
||||
KVStore.setItem(`drafts:${accountUrl}`, state.toJS());
|
||||
KVStore.setItem(`drafts:${accountUrl}`, state);
|
||||
return state;
|
||||
};
|
||||
|
||||
const scheduled_statuses = (state: State = initialState, action: AnyAction | ComposeAction) => {
|
||||
const scheduled_statuses = (state: State = initialState, action: DraftStatusesAction | ComposeAction) => {
|
||||
switch (action.type) {
|
||||
case DRAFT_STATUSES_FETCH_SUCCESS:
|
||||
return importStatuses(state, action.statuses);
|
||||
return create(state, (draft) => importStatuses(draft, action.statuses));
|
||||
case PERSIST_DRAFT_STATUS:
|
||||
return persistState(importStatus(state, action.status), action.accountUrl);
|
||||
return persistState(create(state, (draft) => importStatus(draft, action.status)), action.accountUrl);
|
||||
case CANCEL_DRAFT_STATUS:
|
||||
return persistState(deleteStatus(state, action.statusId), action.accountUrl);
|
||||
return persistState(create(state, (draft) => deleteStatus(draft, action.statusId)), action.accountUrl);
|
||||
case COMPOSE_SUBMIT_SUCCESS:
|
||||
return persistState(deleteStatus(state, action.draftId), action.accountUrl);
|
||||
return action.draftId ? persistState(create(state, (draft) => deleteStatus(draft, action.draftId!)), action.accountUrl) : state;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue