frontend-rw #1
15 changed files with 91 additions and 187 deletions
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'>
|
||||
|
|
|
@ -708,7 +708,7 @@ const MenuButton: React.FC<IMenuButton> = ({
|
|||
});
|
||||
};
|
||||
|
||||
const handleOpenReactionsModal = (): void => {
|
||||
const handleOpenReactionsModal = () => {
|
||||
openModal('REACTIONS', { statusId: status.id });
|
||||
};
|
||||
|
||||
|
|
|
@ -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 = [];
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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] = {};
|
||||
|
||||
|
|
|
@ -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,
|
||||
];
|
||||
|
|
Loading…
Reference in a new issue