frontend-rw #1

Merged
marcin merged 347 commits from frontend-rw into develop 2024-12-05 15:32:18 -08:00
15 changed files with 91 additions and 187 deletions
Showing only changes of commit 4722bccb5c - Show all commits

View file

@ -71,7 +71,7 @@ const updateNotifications = (notification: BaseNotification) =>
dispatch(importEntities({
accounts: [notification.account, notification.type === 'move' ? notification.target : undefined],
statuses: [getNotificationStatus(notification)],
statuses: [getNotificationStatus(notification) as any],
}));
if (showInColumn) {

View file

@ -42,12 +42,6 @@ const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST' as const;
const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS' as const;
const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL' as const;
const STATUS_REVEAL_MEDIA = 'STATUS_REVEAL_MEDIA' as const;
const STATUS_HIDE_MEDIA = 'STATUS_HIDE_MEDIA' as const;
const STATUS_EXPAND_SPOILER = 'STATUS_EXPAND_SPOILER' as const;
const STATUS_COLLAPSE_SPOILER = 'STATUS_COLLAPSE_SPOILER' as const;
const STATUS_UNFILTER = 'STATUS_UNFILTER' as const;
const STATUS_LANGUAGE_CHANGE = 'STATUS_LANGUAGE_CHANGE' as const;
@ -212,58 +206,6 @@ const toggleMuteStatus = (status: Pick<Status, 'id' | 'muted'>) =>
}
};
const hideStatusMedia = (statusIds: string[] | string) => {
if (!Array.isArray(statusIds)) {
statusIds = [statusIds];
}
return {
type: STATUS_HIDE_MEDIA,
statusIds,
};
};
const revealStatusMedia = (statusIds: string[] | string) => {
if (!Array.isArray(statusIds)) {
statusIds = [statusIds];
}
return {
type: STATUS_REVEAL_MEDIA,
statusIds,
};
};
const toggleStatusMediaHidden = (status: Pick<Status, 'id' | 'hidden'>) => {
if (status.hidden) {
return revealStatusMedia(status.id);
} else {
return hideStatusMedia(status.id);
}
};
const collapseStatusSpoiler = (statusIds: string[] | string) => {
if (!Array.isArray(statusIds)) {
statusIds = [statusIds];
}
return {
type: STATUS_COLLAPSE_SPOILER,
statusIds,
};
};
const expandStatusSpoiler = (statusIds: string[] | string) => {
if (!Array.isArray(statusIds)) {
statusIds = [statusIds];
}
return {
type: STATUS_EXPAND_SPOILER,
statusIds,
};
};
// let TRANSLATIONS_QUEUE: Set<string> = new Set();
// let TRANSLATIONS_TIMEOUT: NodeJS.Timeout | null = null;
@ -346,10 +288,6 @@ type StatusesAction =
| { type: typeof STATUS_UNMUTE_REQUEST; statusId: string }
| { type: typeof STATUS_UNMUTE_SUCCESS; statusId: string }
| { type: typeof STATUS_UNMUTE_FAIL; statusId: string; error: unknown }
| ReturnType<typeof hideStatusMedia>
| ReturnType<typeof revealStatusMedia>
| ReturnType<typeof collapseStatusSpoiler>
| ReturnType<typeof expandStatusSpoiler>
| ReturnType<typeof unfilterStatus>
| ReturnType<typeof changeStatusLanguage>;
@ -375,10 +313,6 @@ export {
STATUS_UNMUTE_REQUEST,
STATUS_UNMUTE_SUCCESS,
STATUS_UNMUTE_FAIL,
STATUS_REVEAL_MEDIA,
STATUS_HIDE_MEDIA,
STATUS_EXPAND_SPOILER,
STATUS_COLLAPSE_SPOILER,
STATUS_UNFILTER,
STATUS_LANGUAGE_CHANGE,
createStatus,
@ -391,11 +325,6 @@ export {
muteStatus,
unmuteStatus,
toggleMuteStatus,
hideStatusMedia,
revealStatusMedia,
toggleStatusMediaHidden,
expandStatusSpoiler,
collapseStatusSpoiler,
unfilterStatus,
changeStatusLanguage,
type StatusesAction,

View file

@ -4,7 +4,7 @@ import { MediaGallery } from 'pl-fe/features/ui/util/async-components';
import { useSettings } from 'pl-fe/hooks/use-settings';
import { useModalsStore } from 'pl-fe/stores/modals';
import { isMediaVisible } from './statuses/sensitive-content-overlay';
import { useMediaVisible } from './statuses/sensitive-content-overlay';
import type { MediaAttachment } from 'pl-api';
import type { Status } from 'pl-fe/normalizers/status';
@ -21,7 +21,7 @@ const AttachmentThumbs = ({ status, onClick }: IAttachmentThumbs) => {
const fallback = <div className='media-gallery--compact' />;
const onOpenMedia = (media: Array<MediaAttachment>, index: number) => openModal('MEDIA', { media, index });
const visible = isMediaVisible(status, displayMedia);
const visible = useMediaVisible(status, displayMedia);
return (
<div className='relative'>

View file

@ -708,7 +708,7 @@ const MenuButton: React.FC<IMenuButton> = ({
});
};
const handleOpenReactionsModal = (): void => {
const handleOpenReactionsModal = () => {
openModal('REACTIONS', { statusId: status.id });
};

View file

@ -2,7 +2,6 @@ import clsx from 'clsx';
import React, { useState, useRef, useLayoutEffect, useMemo, useEffect } from 'react';
import { FormattedMessage } from 'react-intl';
import { collapseStatusSpoiler, expandStatusSpoiler } from 'pl-fe/actions/statuses';
import { useStatusTranslation } from 'pl-fe/api/hooks/statuses/use-status-translation';
import Icon from 'pl-fe/components/icon';
import Button from 'pl-fe/components/ui/button';
@ -10,7 +9,6 @@ import Stack from 'pl-fe/components/ui/stack';
import Text from 'pl-fe/components/ui/text';
import Emojify from 'pl-fe/features/emoji/emojify';
import QuotedStatus from 'pl-fe/features/status/containers/quoted-status-container';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useSettings } from 'pl-fe/hooks/use-settings';
import { useStatusMetaStore } from 'pl-fe/stores/status-meta';
import { onlyEmoji as isOnlyEmoji } from 'pl-fe/utils/rich-content';
@ -83,7 +81,6 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
preview,
withMedia,
}) => {
const dispatch = useAppDispatch();
const { displaySpoilers } = useSettings();
const [collapsed, setCollapsed] = useState(false);
@ -93,8 +90,9 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
const node = useRef<HTMLDivElement>(null);
const spoilerNode = useRef<HTMLSpanElement>(null);
const { statuses: statusesMeta } = useStatusMetaStore();
const { data: translation } = useStatusTranslation(status.id, statusesMeta[status.id]?.targetLanguage);
const { statuses: statusesMeta, collapseStatus, expandStatus } = useStatusMetaStore();
const statusMeta = statusesMeta[status.id] || {};
const { data: translation } = useStatusTranslation(status.id, statusMeta.targetLanguage);
const maybeSetCollapsed = (): void => {
if (!node.current) return;
@ -120,8 +118,8 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
e.preventDefault();
e.stopPropagation();
if (expanded) dispatch(collapseStatusSpoiler(status.id));
else dispatch(expandStatusSpoiler(status.id));
if (expanded) collapseStatus(status.id);
else expandStatus(status.id);
};
useLayoutEffect(() => {
@ -166,7 +164,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
});
const expandable = !displaySpoilers;
const expanded = !withSpoiler || status.expanded || false;
const expanded = !withSpoiler || statusMeta.expanded || false;
const output = [];

View file

@ -7,14 +7,14 @@ import { MediaGallery, Video, Audio } from 'pl-fe/features/ui/util/async-compone
import { useSettings } from 'pl-fe/hooks/use-settings';
import { useModalsStore } from 'pl-fe/stores/modals';
import { isMediaVisible } from './statuses/sensitive-content-overlay';
import { useMediaVisible } from './statuses/sensitive-content-overlay';
import type { MediaAttachment } from 'pl-api';
import type { Status } from 'pl-fe/normalizers/status';
interface IStatusMedia {
/** Status entity to render media for. */
status: Pick<Status, 'id' | 'account' | 'card' | 'expectsCard' | 'hidden' | 'media_attachments' | 'quote_id' | 'sensitive' | 'spoiler_text' | 'visibility'>;
status: Pick<Status, 'id' | 'account' | 'card' | 'expectsCard' | 'media_attachments' | 'quote_id' | 'sensitive' | 'spoiler_text' | 'visibility'>;
/** Whether to display compact media. */
muted?: boolean;
/** Callback when compact media is clicked. */
@ -30,7 +30,7 @@ const StatusMedia: React.FC<IStatusMedia> = ({
const { openModal } = useModalsStore();
const { displayMedia } = useSettings();
const visible = isMediaVisible(status, displayMedia);
const visible = useMediaVisible(status, displayMedia);
const size = status.media_attachments.length;
const firstAttachment = status.media_attachments[0];

View file

@ -5,7 +5,7 @@ import { Link, useHistory } from 'react-router-dom';
import { mentionCompose, replyCompose } from 'pl-fe/actions/compose';
import { toggleFavourite, toggleReblog } from 'pl-fe/actions/interactions';
import { toggleStatusMediaHidden, unfilterStatus } from 'pl-fe/actions/statuses';
import { unfilterStatus } from 'pl-fe/actions/statuses';
import Card from 'pl-fe/components/ui/card';
import Icon from 'pl-fe/components/ui/icon';
import Stack from 'pl-fe/components/ui/stack';
@ -19,6 +19,7 @@ import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useSettings } from 'pl-fe/hooks/use-settings';
import { makeGetStatus, type SelectedStatus } from 'pl-fe/selectors';
import { useModalsStore } from 'pl-fe/stores/modals';
import { useStatusMetaStore } from 'pl-fe/stores/status-meta';
import { textForScreenReader } from 'pl-fe/utils/status';
import EventPreview from './event-preview';
@ -77,6 +78,7 @@ const Status: React.FC<IStatus> = (props) => {
const history = useHistory();
const dispatch = useAppDispatch();
const { toggleStatusMediaHidden } = useStatusMetaStore();
const { openModal } = useModalsStore();
const { boostModal } = useSettings();
const didShowCard = useRef(false);
@ -96,7 +98,7 @@ const Status: React.FC<IStatus> = (props) => {
didShowCard.current = Boolean(!muted && !hidden && status?.card);
}, []);
const handleClick = (e?: React.MouseEvent): void => {
const handleClick = (e?: React.MouseEvent) => {
e?.stopPropagation();
// If the user is selecting text, don't focus the status.
@ -115,7 +117,7 @@ const Status: React.FC<IStatus> = (props) => {
}
};
const handleHotkeyOpenMedia = (e?: KeyboardEvent): void => {
const handleHotkeyOpenMedia = (e?: KeyboardEvent) => {
const status = actualStatus;
const firstAttachment = status.media_attachments[0];
@ -130,17 +132,17 @@ const Status: React.FC<IStatus> = (props) => {
}
};
const handleHotkeyReply = (e?: KeyboardEvent): void => {
const handleHotkeyReply = (e?: KeyboardEvent) => {
e?.preventDefault();
dispatch(replyCompose(actualStatus, status.reblog_id ? status.account : undefined));
};
const handleHotkeyFavourite = (e?: KeyboardEvent): void => {
const handleHotkeyFavourite = (e?: KeyboardEvent) => {
e?.preventDefault();
dispatch(toggleFavourite(actualStatus));
};
const handleHotkeyBoost = (e?: KeyboardEvent): void => {
const handleHotkeyBoost = (e?: KeyboardEvent) => {
const modalReblog = () => dispatch(toggleReblog(actualStatus));
if ((e && e.shiftKey) || !boostModal) {
modalReblog();
@ -149,36 +151,36 @@ const Status: React.FC<IStatus> = (props) => {
}
};
const handleHotkeyMention = (e?: KeyboardEvent): void => {
const handleHotkeyMention = (e?: KeyboardEvent) => {
e?.preventDefault();
dispatch(mentionCompose(actualStatus.account));
};
const handleHotkeyOpen = (): void => {
const handleHotkeyOpen = () => {
history.push(statusUrl);
};
const handleHotkeyOpenProfile = (): void => {
const handleHotkeyOpenProfile = () => {
history.push(`/@${actualStatus.account.acct}`);
};
const handleHotkeyMoveUp = (e?: KeyboardEvent): void => {
const handleHotkeyMoveUp = (e?: KeyboardEvent) => {
if (onMoveUp) {
onMoveUp(status.id, featured);
}
};
const handleHotkeyMoveDown = (e?: KeyboardEvent): void => {
const handleHotkeyMoveDown = (e?: KeyboardEvent) => {
if (onMoveDown) {
onMoveDown(status.id, featured);
}
};
const handleHotkeyToggleSensitive = (): void => {
dispatch(toggleStatusMediaHidden(actualStatus));
const handleHotkeyToggleSensitive = () => {
toggleStatusMediaHidden(actualStatus.id);
};
const handleHotkeyReact = (): void => {
const handleHotkeyReact = () => {
(node.current?.querySelector('.emoji-picker-dropdown') as HTMLButtonElement)?.click();
};

View file

@ -2,27 +2,29 @@ import clsx from 'clsx';
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { hideStatusMedia, revealStatusMedia } from 'pl-fe/actions/statuses';
import Button from 'pl-fe/components/ui/button';
import HStack from 'pl-fe/components/ui/hstack';
import Text from 'pl-fe/components/ui/text';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useSettings } from 'pl-fe/hooks/use-settings';
import { useStatusMetaStore } from 'pl-fe/stores/status-meta';
import type { Status } from 'pl-fe/normalizers/status';
const isMediaVisible = (status: Pick<Status, 'media_attachments' | 'sensitive' | 'spoiler_text'> & { hidden?: boolean | null }, displayMedia: 'default' | 'show_all' | 'hide_all') => {
const useMediaVisible = (status: Pick<Status, 'media_attachments' | 'sensitive' | 'spoiler_text'> & { id?: string }, displayMedia: 'default' | 'show_all' | 'hide_all') => {
let visible = !(status.sensitive || status.spoiler_text);
if (status.hidden !== null) visible = !status.hidden;
const statusesMeta = useStatusMetaStore().statuses;
const mediaVisible = status.id ? statusesMeta[status.id]?.mediaVisible : undefined;
if (mediaVisible !== undefined) visible = mediaVisible;
else if (displayMedia === 'show_all') visible = true;
else if (displayMedia === 'hide_all' && status.media_attachments.length) visible = false;
return visible;
};
const showOverlay = (status: Pick<Status, 'hidden' | 'media_attachments' | 'sensitive' | 'spoiler_text'>, displayMedia: 'default' | 'show_all' | 'hide_all') => {
const visible = isMediaVisible(status, displayMedia);
const useShowOverlay = (status: Pick<Status, 'id' | 'media_attachments' | 'sensitive' | 'spoiler_text'>, displayMedia: 'default' | 'show_all' | 'hide_all') => {
const visible = useMediaVisible(status, displayMedia);
const showHideButton = status.sensitive || (status.media_attachments.length && displayMedia === 'hide_all');
@ -41,26 +43,27 @@ const messages = defineMessages({
});
interface ISensitiveContentOverlay {
status: Pick<Status, 'id' | 'sensitive' | 'spoiler_text' | 'hidden' | 'media_attachments' | 'currentLanguage'>;
status: Pick<Status, 'id' | 'sensitive' | 'spoiler_text' | 'media_attachments' | 'currentLanguage'>;
}
const SensitiveContentOverlay = React.forwardRef<HTMLDivElement, ISensitiveContentOverlay>((props, ref) => {
const { status } = props;
const dispatch = useAppDispatch();
const intl = useIntl();
const { displayMedia } = useSettings();
const visible = isMediaVisible(status, displayMedia);
const visible = useMediaVisible(status, displayMedia);
const { hideStatusMedia, revealStatusMedia } = useStatusMetaStore();
const toggleVisibility = (event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
if (visible) dispatch(hideStatusMedia(status.id));
else dispatch(revealStatusMedia(status.id));
if (visible) hideStatusMedia(status.id);
else revealStatusMedia(status.id);
};
if (!showOverlay(status, displayMedia)) return null;
if (!useShowOverlay(status, displayMedia)) return null;
return (
<div
@ -110,4 +113,4 @@ const SensitiveContentOverlay = React.forwardRef<HTMLDivElement, ISensitiveConte
);
});
export { SensitiveContentOverlay as default, isMediaVisible };
export { SensitiveContentOverlay as default, useMediaVisible };

View file

@ -12,7 +12,7 @@ import type { Status } from 'pl-fe/normalizers/status';
interface IReplyIndicator {
className?: string;
status?: Pick<Status, 'account_id' | 'content' | 'created_at' | 'emojis' | 'hidden' | 'media_attachments' | 'mentions' | 'search_index' | 'sensitive' | 'spoiler_text' | 'quote_id'>;
status?: Pick<Status, 'account_id' | 'content' | 'created_at' | 'emojis' | 'media_attachments' | 'mentions' | 'search_index' | 'sensitive' | 'spoiler_text' | 'quote_id'>;
onCancel?: () => void;
hideActions: boolean;
}

View file

@ -4,7 +4,6 @@ import { Link, useHistory } from 'react-router-dom';
import { mentionCompose } from 'pl-fe/actions/compose';
import { reblog, favourite, unreblog, unfavourite } from 'pl-fe/actions/interactions';
import { toggleStatusMediaHidden } from 'pl-fe/actions/statuses';
import HoverAccountWrapper from 'pl-fe/components/hover-account-wrapper';
import Icon from 'pl-fe/components/icon';
import RelativeTimestamp from 'pl-fe/components/relative-timestamp';
@ -22,6 +21,7 @@ import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
import { makeGetNotification } from 'pl-fe/selectors';
import { useModalsStore } from 'pl-fe/stores/modals';
import { useSettingsStore } from 'pl-fe/stores/settings';
import { useStatusMetaStore } from 'pl-fe/stores/status-meta';
import { NotificationType } from 'pl-fe/utils/notification';
import type { NotificationGroup } from 'pl-api';
@ -188,7 +188,7 @@ interface INotification {
onReblog?: (status: StatusEntity, e?: KeyboardEvent) => void;
}
const getNotificationStatus = (n: Pick<NotificationGroup, 'type'> & ({ status: StatusEntity } | { })) => {
const getNotificationStatus = (n: Pick<NotificationGroup, 'type'> & ({ status: StatusEntity } | { })): StatusEntity | null => {
if (['mention', 'status', 'reblog', 'favourite', 'poll', 'update', 'emoji_reaction', 'event_reminder', 'participation_accepted', 'participation_request'].includes(n.type))
// @ts-ignore
return n.status;
@ -203,6 +203,7 @@ const Notification: React.FC<INotification> = (props) => {
const getNotification = useCallback(makeGetNotification(), []);
const { me } = useLoggedIn();
const { toggleStatusMediaHidden } = useStatusMetaStore();
const { openModal } = useModalsStore();
const { settings } = useSettingsStore();
@ -281,9 +282,9 @@ const Notification: React.FC<INotification> = (props) => {
}
}, [status]);
const handleHotkeyToggleSensitive = useCallback((e?: KeyboardEvent) => {
const handleHotkeyToggleSensitive = useCallback(() => {
if (status && typeof status === 'object') {
dispatch(toggleStatusMediaHidden(status));
toggleStatusMediaHidden(status.id);
}
}, [status]);
@ -299,7 +300,7 @@ const Notification: React.FC<INotification> = (props) => {
}
};
const displayedType = notification.type === 'mention' && (notification.subtype === 'reply' || status.in_reply_to_account_id === me) ? 'reply' : notification.type;
const displayedType = notification.type === 'mention' && (notification.subtype === 'reply' || status?.in_reply_to_account_id === me) ? 'reply' : notification.type;
const renderIcon = (): React.ReactNode => {
if (type === 'emoji_reaction' && notification.emoji) {

View file

@ -7,7 +7,6 @@ import { useHistory } from 'react-router-dom';
import { type ComposeReplyAction, mentionCompose, replyCompose } from 'pl-fe/actions/compose';
import { reblog, toggleFavourite, unreblog } from 'pl-fe/actions/interactions';
import { toggleStatusMediaHidden } from 'pl-fe/actions/statuses';
import ScrollableList from 'pl-fe/components/scrollable-list';
import StatusActionBar from 'pl-fe/components/status-action-bar';
import Tombstone from 'pl-fe/components/tombstone';
@ -20,6 +19,7 @@ import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { RootState } from 'pl-fe/store';
import { useModalsStore } from 'pl-fe/stores/modals';
import { useSettingsStore } from 'pl-fe/stores/settings';
import { useStatusMetaStore } from 'pl-fe/stores/status-meta';
import { textForScreenReader } from 'pl-fe/utils/status';
import DetailedStatus from './detailed-status';
@ -113,6 +113,7 @@ const Thread: React.FC<IThread> = ({
const history = useHistory();
const intl = useIntl();
const { toggleStatusMediaHidden } = useStatusMetaStore();
const { openModal } = useModalsStore();
const { settings } = useSettingsStore();
@ -159,7 +160,7 @@ const Thread: React.FC<IThread> = ({
const handleMentionClick = (account: Pick<Account, 'acct'>) => dispatch(mentionCompose(account));
const handleHotkeyOpenMedia = (e?: KeyboardEvent) => {
const media = status?.media_attachments;
const media = status.media_attachments;
e?.preventDefault();
@ -175,43 +176,43 @@ const Thread: React.FC<IThread> = ({
};
const handleHotkeyMoveUp = () => {
handleMoveUp(status!.id);
handleMoveUp(status.id);
};
const handleHotkeyMoveDown = () => {
handleMoveDown(status!.id);
handleMoveDown(status.id);
};
const handleHotkeyReply = (e?: KeyboardEvent) => {
e?.preventDefault();
handleReplyClick(status!);
handleReplyClick(status);
};
const handleHotkeyFavourite = () => {
handleFavouriteClick(status!);
handleFavouriteClick(status);
};
const handleHotkeyBoost = () => {
handleReblogClick(status!);
handleReblogClick(status);
};
const handleHotkeyMention = (e?: KeyboardEvent) => {
e?.preventDefault();
const { account } = status!;
const { account } = status;
if (!account || typeof account !== 'object') return;
handleMentionClick(account);
};
const handleHotkeyOpenProfile = () => {
history.push(`/@${status!.account.acct}`);
history.push(`/@${status.account.acct}`);
};
const handleHotkeyToggleSensitive = () => {
dispatch(toggleStatusMediaHidden(status));
toggleStatusMediaHidden(status.id);
};
const handleMoveUp = (id: string) => {
if (id === status?.id) {
if (id === status.id) {
_selectChild(ancestorsIds.length - 1);
} else {
let index = ancestorsIds.indexOf(id);
@ -226,7 +227,7 @@ const Thread: React.FC<IThread> = ({
};
const handleMoveDown = (id: string) => {
if (id === status?.id) {
if (id === status.id) {
_selectChild(ancestorsIds.length + 1);
} else {
let index = ancestorsIds.indexOf(id);
@ -269,7 +270,7 @@ const Thread: React.FC<IThread> = ({
<ThreadStatus
key={id}
id={id}
focusedStatusId={status!.id}
focusedStatusId={status.id}
onMoveUp={handleMoveUp}
onMoveDown={handleMoveDown}
contextType='thread'

View file

@ -18,8 +18,6 @@ type StatusVisibility = 'public' | 'unlisted' | 'private' | 'direct' | 'group' |
type CalculatedValues = {
search_index: string;
expanded?: boolean | null;
hidden?: boolean | null;
currentLanguage?: string;
};
@ -56,11 +54,11 @@ const buildSearchContent = (status: Pick<BaseStatus, 'poll' | 'mentions' | 'spoi
const calculateStatus = (status: BaseStatus, oldStatus?: OldStatus): CalculatedValues => {
if (oldStatus && oldStatus.content === status.content && oldStatus.spoiler_text === status.spoiler_text) {
const {
search_index, hidden, expanded, currentLanguage,
search_index, currentLanguage,
} = oldStatus;
return {
search_index, hidden, expanded, currentLanguage,
search_index, currentLanguage,
};
} else {
const searchContent = buildSearchContent(status);
@ -135,8 +133,6 @@ const normalizeStatus = (status: BaseStatus & {
account: normalizeAccount(status.account),
accounts: status.accounts?.map(normalizeAccount),
mentions,
expanded: null,
hidden: null,
filtered: status.filtered?.map(result => result.filter.title),
event,
group,

View file

@ -36,14 +36,10 @@ import {
STATUS_CREATE_FAIL,
STATUS_DELETE_REQUEST,
STATUS_DELETE_FAIL,
STATUS_HIDE_MEDIA,
STATUS_MUTE_SUCCESS,
STATUS_REVEAL_MEDIA,
STATUS_UNFILTER,
STATUS_UNMUTE_SUCCESS,
STATUS_LANGUAGE_CHANGE,
STATUS_COLLAPSE_SPOILER,
STATUS_EXPAND_SPOILER,
type StatusesAction,
} from '../actions/statuses';
import { TIMELINE_DELETE, type TimelineAction } from '../actions/timelines';
@ -251,42 +247,6 @@ const statuses = (state = initialState, action: EmojiReactsAction | EventsAction
status.muted = false;
}
});
case STATUS_REVEAL_MEDIA:
return create(state, (draft) => {
action.statusIds.forEach((id: string) => {
const status = draft[id];
if (status) {
status.hidden = false;
}
});
});
case STATUS_HIDE_MEDIA:
return create(state, (draft) => {
action.statusIds.forEach((id: string) => {
const status = draft[id];
if (status) {
status.hidden = true;
}
});
});
case STATUS_EXPAND_SPOILER:
return create(state, (draft) => {
action.statusIds.forEach((id: string) => {
const status = draft[id];
if (status) {
status.expanded = true;
}
});
});
case STATUS_COLLAPSE_SPOILER:
return create(state, (draft) => {
action.statusIds.forEach((id: string) => {
const status = draft[id];
if (status) {
status.expanded = false;
}
});
});
case STATUS_DELETE_REQUEST:
return create(state, (draft) => decrementReplyCount(draft, action.params));
case STATUS_DELETE_FAIL:

View file

@ -2,25 +2,39 @@ import { create } from 'zustand';
import { mutative } from 'zustand-mutative';
type State = {
statuses: Record<string, { visible?: boolean; targetLanguage?: string }>;
revealStatus: (statusId: string) => void;
hideStatus: (statusId: string) => void;
statuses: Record<string, { expanded?: boolean; mediaVisible?: boolean; targetLanguage?: string }>;
expandStatus: (statusId: string) => void;
collapseStatus: (statusId: string) => void;
revealStatusMedia: (statusId: string) => void;
hideStatusMedia: (statusId: string) => void;
toggleStatusMediaHidden: (statusId: string) => void;
fetchTranslation: (statusId: string, targetLanguage: string) => void;
hideTranslation: (statusId: string) => void;
};
const useStatusMetaStore = create<State>()(mutative((set) => ({
statuses: {},
revealStatus: (statusId) => set((state: State) => {
expandStatus: (statusId) => set((state: State) => {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
state.statuses[statusId].visible = true;
state.statuses[statusId].expanded = true;
}),
hideStatus: (statusId) => set((state: State) => {
collapseStatus: (statusId) => set((state: State) => {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
state.statuses[statusId].visible = false;
state.statuses[statusId].expanded = false;
}),
revealStatusMedia: (statusId) => set((state: State) => {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
state.statuses[statusId].mediaVisible = true;
}),
hideStatusMedia: (statusId) => set((state: State) => {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
state.statuses[statusId].mediaVisible = false;
}),
toggleStatusMediaHidden: (statusId) => (state: State) => state[state.statuses[statusId].mediaVisible ? 'hideStatusMedia' : 'revealStatusMedia'](statusId),
fetchTranslation: (statusId, targetLanguage) => set((state: State) => {
if (!state.statuses[statusId]) state.statuses[statusId] = {};

View file

@ -34,7 +34,7 @@ const hasIntegerMediaIds = (status: Pick<Status, 'media_attachments'>): boolean
/** Sanitize status text for use with screen readers. */
const textForScreenReader = (
intl: IntlShape,
status: Pick<Status, 'account' | 'spoiler_text' | 'hidden' | 'search_index' | 'created_at'>,
status: Pick<Status, 'account' | 'spoiler_text' | 'search_index' | 'created_at'>,
rebloggedByText?: string,
): string => {
const { account } = status;
@ -44,7 +44,7 @@ const textForScreenReader = (
const values = [
displayName.length === 0 ? account.acct.split('@')[0] : displayName,
status.spoiler_text && status.hidden ? status.spoiler_text : status.search_index?.slice(status.spoiler_text.length) || '',
status.spoiler_text ? status.spoiler_text : status.search_index?.slice(status.spoiler_text.length) || '',
intl.formatDate(status.created_at, { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }),
account.acct,
];