pl-fe: add 'expand all posts' button

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-09-12 14:12:10 +02:00
parent 32911d7056
commit 3db6c890c2
8 changed files with 89 additions and 50 deletions

View File

@ -93,7 +93,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
const node = useRef<HTMLDivElement>(null);
const spoilerNode = useRef<HTMLSpanElement>(null);
const { statuses: statusesMeta, collapseStatus, expandStatus } = useStatusMetaStore();
const { statuses: statusesMeta, collapseStatuses, expandStatuses } = useStatusMetaStore();
const statusMeta = statusesMeta[status.id] || {};
const { data: translation } = useStatusTranslation(status.id, statusMeta.targetLanguage);
@ -123,9 +123,9 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
e.stopPropagation();
if (expanded) {
collapseStatus(status.id);
collapseStatuses([status.id]);
setCollapsed(null);
} else expandStatus(status.id);
} else expandStatuses([status.id]);
};
useLayoutEffect(() => {

View File

@ -79,7 +79,7 @@ const Status: React.FC<IStatus> = (props) => {
const history = useHistory();
const dispatch = useAppDispatch();
const { toggleStatusMediaHidden } = useStatusMetaStore();
const { toggleStatusesMediaHidden } = useStatusMetaStore();
const { openModal } = useModalsStore();
const { boostModal } = useSettings();
const didShowCard = useRef(false);
@ -188,7 +188,7 @@ const Status: React.FC<IStatus> = (props) => {
};
const handleHotkeyToggleSensitive = () => {
toggleStatusMediaHidden(actualStatus.id);
toggleStatusesMediaHidden([actualStatus.id]);
};
const handleHotkeyReact = () => {

View File

@ -64,13 +64,13 @@ const SensitiveContentOverlay = React.forwardRef<HTMLDivElement, ISensitiveConte
const matchedFilters = useMemo(() => filters.map(({ filter }) => filter.title), [filters]);
const { hideStatusMedia, revealStatusMedia } = useStatusMetaStore();
const { hideStatusesMedia, revealStatusesMedia } = useStatusMetaStore();
const toggleVisibility = (event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
if (visible) hideStatusMedia(status.id);
else revealStatusMedia(status.id);
if (visible) hideStatusesMedia([status.id]);
else revealStatusesMedia([status.id]);
};
if (!useShowOverlay(status, displayMedia)) return null;

View File

@ -204,7 +204,7 @@ const Notification: React.FC<INotification> = (props) => {
const getNotification = useCallback(makeGetNotification(), []);
const { me } = useLoggedIn();
const { toggleStatusMediaHidden } = useStatusMetaStore();
const { toggleStatusesMediaHidden } = useStatusMetaStore();
const { openModal } = useModalsStore();
const { settings } = useSettingsStore();
@ -288,7 +288,7 @@ const Notification: React.FC<INotification> = (props) => {
const handleHotkeyToggleSensitive = useCallback(() => {
if (status && typeof status === 'object') {
toggleStatusMediaHidden(status.id);
toggleStatusesMediaHidden([status.id]);
}
}, [status]);

View File

@ -125,19 +125,21 @@ interface IThread {
withMedia?: boolean;
isModal?: boolean;
itemClassName?: string;
setExpandAllStatuses?: (fn: () => void) => void;
}
const Thread: React.FC<IThread> = ({
const Thread = ({
itemClassName,
status,
isModal,
withMedia = true,
}) => {
setExpandAllStatuses,
}: IThread) => {
const dispatch = useAppDispatch();
const history = useHistory();
const intl = useIntl();
const { toggleStatusMediaHidden } = useStatusMetaStore();
const { expandStatuses, revealStatusesMedia, toggleStatusesMediaHidden } = useStatusMetaStore();
const { openModal } = useModalsStore();
const { settings: { boostModal, threads: { displayMode } } } = useSettingsStore();
@ -235,7 +237,7 @@ const Thread: React.FC<IThread> = ({
};
const handleHotkeyToggleSensitive = () => {
toggleStatusMediaHidden(status.id);
toggleStatusesMediaHidden([status.id]);
};
const handleMoveUp = (id: string) => {
@ -413,6 +415,12 @@ const Thread: React.FC<IThread> = ({
const children = useMemo(() => renderChildren(thread), [thread, linear]);
if (isModal) children.unshift(<div key='padding' className='h-4' />);
useEffect(() => {
setExpandAllStatuses?.(() => {
expandStatuses(thread);
revealStatusesMedia(thread);
});
}, [thread]);
return (
<Stack

View File

@ -1716,6 +1716,7 @@
"status.show_original": "Show original",
"status.spoiler.collapse": "Collapse",
"status.spoiler.expand": "Expand",
"status.thread.expand_all": "Expand all posts",
"status.thread.linear_view": "Linear view",
"status.thread.tree_view": "Tree view",
"status.title": "Post details",

View File

@ -36,6 +36,7 @@ const messages = defineMessages({
blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block and report' },
treeView: { id: 'status.thread.tree_view', defaultMessage: 'Tree view' },
linearView: { id: 'status.thread.linear_view', defaultMessage: 'Linear view' },
expandAll: { id: 'status.thread.expand_all', defaultMessage: 'Expand all posts' },
});
type RouteParams = {
@ -55,9 +56,10 @@ const StatusPage: React.FC<IStatusDetails> = (props) => {
const getStatus = useCallback(makeGetStatus(), []);
const status = useAppSelector((state) => getStatus(state, { id: props.params.statusId }));
const [expandAllStatuses, setExpandAllStatuses] = useState<() => void>();
const [isLoaded, setIsLoaded] = useState<boolean>(!!status);
const { settings: { threads: { displayMode } } } = useSettingsStore();
const { settings: { displaySpoilers, threads: { displayMode } } } = useSettingsStore();
/** Fetch the status (and context) from the API. */
const fetchData = () => {
@ -77,22 +79,36 @@ const StatusPage: React.FC<IStatusDetails> = (props) => {
const handleRefresh = () => fetchData();
const items: Menu = useMemo(() => [
{
text: intl.formatMessage(messages.treeView),
action: () => dispatch(changeSetting(['threads', 'displayMode'], 'tree')),
icon: require('@tabler/icons/outline/list-tree.svg'),
type: 'radio',
checked: displayMode === 'tree',
},
{
text: intl.formatMessage(messages.linearView),
action: () => dispatch(changeSetting(['threads', 'displayMode'], 'linear')),
icon: require('@tabler/icons/outline/list.svg'),
type: 'radio',
checked: displayMode === 'linear',
},
], [displayMode]);
const items = useMemo(() => {
const menu: Menu = [
{
text: intl.formatMessage(messages.treeView),
action: () => dispatch(changeSetting(['threads', 'displayMode'], 'tree')),
icon: require('@tabler/icons/outline/list-tree.svg'),
type: 'radio',
checked: displayMode === 'tree',
},
{
text: intl.formatMessage(messages.linearView),
action: () => dispatch(changeSetting(['threads', 'displayMode'], 'linear')),
icon: require('@tabler/icons/outline/list.svg'),
type: 'radio',
checked: displayMode === 'linear',
},
];
if (!displaySpoilers && expandAllStatuses) {
menu.push(
null,
{
text: intl.formatMessage(messages.expandAll),
action: expandAllStatuses,
icon: require('@tabler/icons/outline/chevron-down.svg'),
},
);
}
return menu;
}, [displayMode, expandAllStatuses]);
if (status?.event) {
return (
@ -130,7 +146,7 @@ const StatusPage: React.FC<IStatusDetails> = (props) => {
action={<DropdownMenu items={items} src={require('@tabler/icons/outline/dots-vertical.svg')} />}
>
<PullToRefresh onRefresh={handleRefresh}>
<Thread key={status.id} status={status} />
<Thread key={status.id} status={status} setExpandAllStatuses={(fn) => setExpandAllStatuses(() => fn)} />
</PullToRefresh>
</Column>

View File

@ -3,11 +3,11 @@ import { mutative } from 'zustand-mutative';
type State = {
statuses: Record<string, { expanded?: boolean; mediaVisible?: boolean; currentLanguage?: string; targetLanguage?: string }>;
expandStatus: (statusId: string) => void;
collapseStatus: (statusId: string) => void;
revealStatusMedia: (statusId: string) => void;
hideStatusMedia: (statusId: string) => void;
toggleStatusMediaHidden: (statusId: string) => void;
expandStatuses: (statusIds: Array<string>) => void;
collapseStatuses: (statusIds: Array<string>) => void;
revealStatusesMedia: (statusIds: Array<string>) => void;
hideStatusesMedia: (statusIds: Array<string>) => void;
toggleStatusesMediaHidden: (statusIds: Array<string>) => void;
fetchTranslation: (statusId: string, targetLanguage: string) => void;
hideTranslation: (statusId: string) => void;
setStatusLanguage: (statusId: string, language: string) => void;
@ -15,27 +15,41 @@ type State = {
const useStatusMetaStore = create<State>()(mutative((set) => ({
statuses: {},
expandStatus: (statusId) => set((state: State) => {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
expandStatuses: (statusIds) => set((state: State) => {
for (const statusId of statusIds) {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
state.statuses[statusId].expanded = true;
state.statuses[statusId].expanded = true;
}
}),
collapseStatus: (statusId) => set((state: State) => {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
collapseStatuses: (statusIds) => set((state: State) => {
for (const statusId of statusIds) {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
state.statuses[statusId].expanded = false;
state.statuses[statusId].expanded = false;
}
}),
revealStatusMedia: (statusId) => set((state: State) => {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
revealStatusesMedia: (statusIds) => set((state: State) => {
for (const statusId of statusIds) {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
state.statuses[statusId].mediaVisible = true;
state.statuses[statusId].mediaVisible = true;
}
}),
hideStatusMedia: (statusId) => set((state: State) => {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
hideStatusesMedia: (statusIds) => set((state: State) => {
for (const statusId of statusIds) {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
state.statuses[statusId].mediaVisible = false;
state.statuses[statusId].mediaVisible = false;
}
}),
toggleStatusMediaHidden: (statusId) => (state: State) => state[state.statuses[statusId].mediaVisible ? 'hideStatusMedia' : 'revealStatusMedia'](statusId),
toggleStatusesMediaHidden: (statusIds) => (state: State) => {
for (const statusId of statusIds) {
if (!state.statuses[statusId]) state.statuses[statusId] = {};
state.statuses[statusId].mediaVisible = !state.statuses[statusId].mediaVisible;
}
},
fetchTranslation: (statusId, targetLanguage) => set((state: State) => {
if (!state.statuses[statusId]) state.statuses[statusId] = {};