pl-fe: zustand migrations
Signed-off-by: mkljczk <git@mkljczk.pl>
This commit is contained in:
parent
4722bccb5c
commit
9f0a4818f2
9 changed files with 34 additions and 64 deletions
|
@ -44,8 +44,6 @@ const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL' as const;
|
|||
|
||||
const STATUS_UNFILTER = 'STATUS_UNFILTER' as const;
|
||||
|
||||
const STATUS_LANGUAGE_CHANGE = 'STATUS_LANGUAGE_CHANGE' as const;
|
||||
|
||||
const createStatus = (params: CreateStatusParams, idempotencyKey: string, statusId: string | null) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
dispatch<StatusesAction>({ type: STATUS_CREATE_REQUEST, params, idempotencyKey, editing: !!statusId });
|
||||
|
@ -260,12 +258,6 @@ const unfilterStatus = (statusId: string) => ({
|
|||
statusId,
|
||||
});
|
||||
|
||||
const changeStatusLanguage = (statusId: string, language: string) => ({
|
||||
type: STATUS_LANGUAGE_CHANGE,
|
||||
statusId,
|
||||
language,
|
||||
});
|
||||
|
||||
type StatusesAction =
|
||||
| { type: typeof STATUS_CREATE_REQUEST; params: CreateStatusParams; idempotencyKey: string; editing: boolean }
|
||||
| { type: typeof STATUS_CREATE_SUCCESS; status: BaseStatus | ScheduledStatus; params: CreateStatusParams; idempotencyKey: string; editing: boolean }
|
||||
|
@ -289,7 +281,6 @@ type StatusesAction =
|
|||
| { type: typeof STATUS_UNMUTE_SUCCESS; statusId: string }
|
||||
| { type: typeof STATUS_UNMUTE_FAIL; statusId: string; error: unknown }
|
||||
| ReturnType<typeof unfilterStatus>
|
||||
| ReturnType<typeof changeStatusLanguage>;
|
||||
|
||||
export {
|
||||
STATUS_CREATE_REQUEST,
|
||||
|
@ -314,7 +305,6 @@ export {
|
|||
STATUS_UNMUTE_SUCCESS,
|
||||
STATUS_UNMUTE_FAIL,
|
||||
STATUS_UNFILTER,
|
||||
STATUS_LANGUAGE_CHANGE,
|
||||
createStatus,
|
||||
editStatus,
|
||||
fetchStatus,
|
||||
|
@ -326,6 +316,5 @@ export {
|
|||
unmuteStatus,
|
||||
toggleMuteStatus,
|
||||
unfilterStatus,
|
||||
changeStatusLanguage,
|
||||
type StatusesAction,
|
||||
};
|
||||
|
|
|
@ -17,14 +17,15 @@ type Selected = Record<number, boolean>;
|
|||
|
||||
interface IPoll {
|
||||
id: string;
|
||||
status?: Pick<Status, 'url' | 'currentLanguage'>;
|
||||
status?: Pick<Status, 'url'>;
|
||||
language?: string;
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
multiple: { id: 'poll.choose_multiple', defaultMessage: 'Choose as many as you\'d like.' },
|
||||
});
|
||||
|
||||
const Poll: React.FC<IPoll> = ({ id, status }): JSX.Element | null => {
|
||||
const Poll: React.FC<IPoll> = ({ id, status, language }): JSX.Element | null => {
|
||||
const { openModal } = useModalsStore();
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
@ -87,7 +88,7 @@ const Poll: React.FC<IPoll> = ({ id, status }): JSX.Element | null => {
|
|||
showResults={showResults}
|
||||
active={!!selected[i]}
|
||||
onToggle={toggleOption}
|
||||
language={status?.currentLanguage}
|
||||
language={language}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
|
|
|
@ -130,10 +130,10 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
|
|||
const content = useMemo(
|
||||
(): string => translation
|
||||
? translation.content
|
||||
: (status.content_map && status.currentLanguage)
|
||||
? (status.content_map[status.currentLanguage] || status.content)
|
||||
: (status.content_map && statusMeta.currentLanguage)
|
||||
? (status.content_map[statusMeta.currentLanguage] || status.content)
|
||||
: status.content,
|
||||
[status.content, translation, status.currentLanguage],
|
||||
[status.content, translation, statusMeta.currentLanguage],
|
||||
);
|
||||
|
||||
const { content: parsedContent, hashtags } = useMemo(() => parseContent({
|
||||
|
@ -149,8 +149,8 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
|
|||
|
||||
const withSpoiler = status.spoiler_text.length > 0;
|
||||
|
||||
const spoilerText = status.spoiler_text_map && status.currentLanguage
|
||||
? status.spoiler_text_map[status.currentLanguage] || status.spoiler_text
|
||||
const spoilerText = status.spoiler_text_map && statusMeta.currentLanguage
|
||||
? status.spoiler_text_map[statusMeta.currentLanguage] || status.spoiler_text
|
||||
: status.spoiler_text;
|
||||
|
||||
const direction = getTextDirection(status.search_index);
|
||||
|
@ -245,7 +245,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
|
|||
}
|
||||
|
||||
if (status.poll_id) {
|
||||
output.push(<Poll id={status.poll_id} key='poll' status={status} />);
|
||||
output.push(<Poll id={status.poll_id} key='poll' status={status} language={statusMeta.currentLanguage} />);
|
||||
}
|
||||
|
||||
if (translatable) {
|
||||
|
@ -283,7 +283,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
|
|||
}
|
||||
|
||||
if (status.poll_id) {
|
||||
output.push(<Poll id={status.poll_id} key='poll' status={status} />);
|
||||
output.push(<Poll id={status.poll_id} key='poll' status={status} language={statusMeta.currentLanguage} />);
|
||||
}
|
||||
|
||||
if (translatable) {
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { changeStatusLanguage } from 'pl-fe/actions/statuses';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
import Icon from 'pl-fe/components/ui/icon';
|
||||
import Text from 'pl-fe/components/ui/text';
|
||||
import { type Language, languages } from 'pl-fe/features/preferences';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useStatusMetaStore } from 'pl-fe/stores/status-meta';
|
||||
|
||||
import DropdownMenu from './dropdown-menu';
|
||||
|
||||
|
@ -17,13 +16,16 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
interface IStatusLanguagePicker {
|
||||
status: Pick<Status, 'id' | 'content_map' | 'currentLanguage'>;
|
||||
status: Pick<Status, 'id' | 'content_map'>;
|
||||
showLabel?: boolean;
|
||||
}
|
||||
|
||||
const StatusLanguagePicker: React.FC<IStatusLanguagePicker> = ({ status, showLabel }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { statuses, setStatusLanguage } = useStatusMetaStore();
|
||||
|
||||
const { currentLanguage } = statuses[status.id] || {};
|
||||
|
||||
if (!status.content_map || Object.keys(status.content_map).length < 2) return null;
|
||||
|
||||
|
@ -36,8 +38,8 @@ const StatusLanguagePicker: React.FC<IStatusLanguagePicker> = ({ status, showLab
|
|||
<DropdownMenu
|
||||
items={Object.keys(status.content_map).map((language) => ({
|
||||
text: languages[language as Language] || language,
|
||||
action: () => dispatch(changeStatusLanguage(status.id, language)),
|
||||
active: language === status.currentLanguage,
|
||||
action: () => setStatusLanguage(status.id, language),
|
||||
active: language === currentLanguage,
|
||||
}))}
|
||||
>
|
||||
<button title={intl.formatMessage(messages.languageVersions)} className='hover:underline'>
|
||||
|
@ -45,7 +47,7 @@ const StatusLanguagePicker: React.FC<IStatusLanguagePicker> = ({ status, showLab
|
|||
<HStack space={1} alignItems='center'>
|
||||
{icon}
|
||||
<Text tag='span' theme='muted' size='sm'>
|
||||
{languages[status.currentLanguage as Language] || status.currentLanguage}
|
||||
{languages[currentLanguage as Language] || currentLanguage}
|
||||
</Text>
|
||||
</HStack>
|
||||
) : icon}
|
||||
|
|
|
@ -43,7 +43,7 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
interface ISensitiveContentOverlay {
|
||||
status: Pick<Status, 'id' | 'sensitive' | 'spoiler_text' | 'media_attachments' | 'currentLanguage'>;
|
||||
status: Pick<Status, 'id' | 'sensitive' | 'spoiler_text' | 'media_attachments'>;
|
||||
}
|
||||
|
||||
const SensitiveContentOverlay = React.forwardRef<HTMLDivElement, ISensitiveContentOverlay>((props, ref) => {
|
||||
|
|
|
@ -16,12 +16,7 @@ const domParser = new DOMParser();
|
|||
type StatusApprovalStatus = Exclude<BaseStatus['approval_status'], null>;
|
||||
type StatusVisibility = 'public' | 'unlisted' | 'private' | 'direct' | 'group' | 'mutuals_only' | 'local';
|
||||
|
||||
type CalculatedValues = {
|
||||
search_index: string;
|
||||
currentLanguage?: string;
|
||||
};
|
||||
|
||||
type OldStatus = Pick<BaseStatus, 'content' | 'spoiler_text'> & CalculatedValues;
|
||||
type OldStatus = Pick<BaseStatus, 'content' | 'spoiler_text'> & { search_index: string };
|
||||
|
||||
// Gets titles of poll options from status
|
||||
const getPollOptionTitles = ({ poll }: Pick<BaseStatus, 'poll'>): readonly string[] => {
|
||||
|
@ -51,28 +46,20 @@ const buildSearchContent = (status: Pick<BaseStatus, 'poll' | 'mentions' | 'spoi
|
|||
return unescapeHTML(fields.join('\n\n')) || '';
|
||||
};
|
||||
|
||||
const calculateStatus = (status: BaseStatus, oldStatus?: OldStatus): CalculatedValues => {
|
||||
const getSearchIndex = (status: BaseStatus, oldStatus?: OldStatus) => {
|
||||
if (oldStatus && oldStatus.content === status.content && oldStatus.spoiler_text === status.spoiler_text) {
|
||||
const {
|
||||
search_index, currentLanguage,
|
||||
} = oldStatus;
|
||||
|
||||
return {
|
||||
search_index, currentLanguage,
|
||||
};
|
||||
return oldStatus.search_index;
|
||||
} else {
|
||||
const searchContent = buildSearchContent(status);
|
||||
|
||||
return {
|
||||
search_index: domParser.parseFromString(searchContent, 'text/html').documentElement.textContent || '',
|
||||
};
|
||||
return domParser.parseFromString(searchContent, 'text/html').documentElement.textContent || '';
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeStatus = (status: BaseStatus & {
|
||||
accounts?: Array<BaseAccount>;
|
||||
}, oldStatus?: OldStatus) => {
|
||||
const calculated = calculateStatus(status, oldStatus);
|
||||
const searchIndex = getSearchIndex(status, oldStatus);
|
||||
|
||||
// Sort the replied-to mention to the top
|
||||
let mentions = status.mentions.toSorted((a, _b) => {
|
||||
|
@ -137,7 +124,7 @@ const normalizeStatus = (status: BaseStatus & {
|
|||
event,
|
||||
group,
|
||||
media_attachments,
|
||||
...calculated,
|
||||
search_index: searchIndex,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ import {
|
|||
STATUS_MUTE_SUCCESS,
|
||||
STATUS_UNFILTER,
|
||||
STATUS_UNMUTE_SUCCESS,
|
||||
STATUS_LANGUAGE_CHANGE,
|
||||
type StatusesAction,
|
||||
} from '../actions/statuses';
|
||||
import { TIMELINE_DELETE, type TimelineAction } from '../actions/timelines';
|
||||
|
@ -258,13 +257,6 @@ const statuses = (state = initialState, action: EmojiReactsAction | EventsAction
|
|||
status.showFiltered = false;
|
||||
}
|
||||
});
|
||||
case STATUS_LANGUAGE_CHANGE:
|
||||
return create(state, (draft) => {
|
||||
const status = draft[action.statusId];
|
||||
if (status) {
|
||||
status.currentLanguage = action.language;
|
||||
}
|
||||
});
|
||||
case TIMELINE_DELETE:
|
||||
return create(state, (draft) => deleteStatus(draft, action.statusId, action.references));
|
||||
case EVENT_JOIN_REQUEST:
|
||||
|
|
|
@ -162,13 +162,6 @@ const makeGetStatus = () => createSelector(
|
|||
poll,
|
||||
filtered,
|
||||
};
|
||||
// if (map.currentLanguage === null && map.content_map?.size) {
|
||||
// let currentLanguage: string | null = null;
|
||||
// if (map.content_map.has(locale)) currentLanguage = locale;
|
||||
// else if (map.language && map.content_map.has(map.language)) currentLanguage = map.language;
|
||||
// else currentLanguage = map.content_map.keySeq().first();
|
||||
// map.set('currentLanguage', currentLanguage);
|
||||
// }
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { create } from 'zustand';
|
|||
import { mutative } from 'zustand-mutative';
|
||||
|
||||
type State = {
|
||||
statuses: Record<string, { expanded?: boolean; mediaVisible?: boolean; targetLanguage?: string }>;
|
||||
statuses: Record<string, { expanded?: boolean; mediaVisible?: boolean; currentLanguage?: string; targetLanguage?: string }>;
|
||||
expandStatus: (statusId: string) => void;
|
||||
collapseStatus: (statusId: string) => void;
|
||||
revealStatusMedia: (statusId: string) => void;
|
||||
|
@ -10,6 +10,7 @@ type State = {
|
|||
toggleStatusMediaHidden: (statusId: string) => void;
|
||||
fetchTranslation: (statusId: string, targetLanguage: string) => void;
|
||||
hideTranslation: (statusId: string) => void;
|
||||
setStatusLanguage: (statusId: string, language: string) => void;
|
||||
};
|
||||
|
||||
const useStatusMetaStore = create<State>()(mutative((set) => ({
|
||||
|
@ -45,6 +46,11 @@ const useStatusMetaStore = create<State>()(mutative((set) => ({
|
|||
|
||||
state.statuses[statusId].targetLanguage = undefined;
|
||||
}),
|
||||
setStatusLanguage: (statusId, language) => set((state: State) => {
|
||||
if (!state.statuses[statusId]) state.statuses[statusId] = {};
|
||||
|
||||
state.statuses[statusId].currentLanguage = language;
|
||||
}),
|
||||
})));
|
||||
|
||||
export { useStatusMetaStore };
|
||||
|
|
Loading…
Reference in a new issue