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