From 681285e42d6e2cd517ee3af82c0d41014d33f799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Thu, 5 Mar 2026 13:41:42 +0100 Subject: [PATCH] nicolium: experimental timeline: display reblog/hashtag follow information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/nicolium/src/columns/timeline.tsx | 106 +++++++++++++++++- .../src/components/statuses/status.tsx | 13 ++- .../src/queries/timelines/use-timeline.ts | 4 +- 3 files changed, 113 insertions(+), 10 deletions(-) diff --git a/packages/nicolium/src/columns/timeline.tsx b/packages/nicolium/src/columns/timeline.tsx index 60482fd7e..c63bb3bd4 100644 --- a/packages/nicolium/src/columns/timeline.tsx +++ b/packages/nicolium/src/columns/timeline.tsx @@ -1,15 +1,21 @@ +import { Link } from '@tanstack/react-router'; import clsx from 'clsx'; import React, { useRef } from 'react'; -import { defineMessages } from 'react-intl'; +import { defineMessages, FormattedList, FormattedMessage } from 'react-intl'; import LoadMore from '@/components/load-more'; import ScrollTopButton from '@/components/scroll-top-button'; import ScrollableList from '@/components/scrollable-list'; -import Status from '@/components/statuses/status'; +import Status, { StatusFollowedTagInfo } from '@/components/statuses/status'; +import StatusInfo from '@/components/statuses/status-info'; import Tombstone from '@/components/statuses/tombstone'; +import Icon from '@/components/ui/icon'; import Portal from '@/components/ui/portal'; +import Emojify from '@/features/emoji/emojify'; import PlaceholderStatus from '@/features/placeholder/components/placeholder-status'; -import { useStatus } from '@/queries/statuses/use-status'; +import { useFeatures } from '@/hooks/use-features'; +import { useAccounts } from '@/queries/accounts/use-accounts'; +import { type SelectedStatus, useStatus } from '@/queries/statuses/use-status'; import { useAntennaTimeline, useBubbleTimeline, @@ -44,8 +50,90 @@ const PlaceholderTimelineStatus = () => ( ); +interface ITimelineStatusInfo { + status: SelectedStatus; + rebloggedBy: Array; + timelineId: string; +} + +const TimelineStatusInfo: React.FC = ({ status, rebloggedBy, timelineId }) => { + const features = useFeatures(); + const isReblogged = rebloggedBy.length > 0; + + const { data: accounts } = useAccounts(rebloggedBy); + + if (isReblogged) { + const renderedAccounts = accounts.slice(0, 2).map( + (account) => + !!account && ( + + + + + + + + ), + ); + + if (accounts.length > 2) { + renderedAccounts.push( + , + ); + } + + const values = { + name: , + count: accounts.length, + }; + + return ( + + } + text={ + // status.visibility === 'private' ? ( + // + // ) : ( + + // ) + } + /> + ); + } + if (timelineId.split(':')[0] === 'home' && features.followHashtags) { + return ; + } +}; + interface ITimelineStatus { id: string; + rebloggedBy: Array; + timelineId: string; contextType?: FilterContextType; isConnectedTop?: boolean; isConnectedBottom?: boolean; @@ -92,6 +180,13 @@ const TimelineStatus: React.FC = (props): React.JSX.Element => '⁂-timeline-status--connected-top': isConnectedTop, })} > + {statusQuery.data && ( + + )} {renderConnector()} {statusQuery.isPending ? ( @@ -110,7 +205,8 @@ interface ITimeline { const Timeline: React.FC = ({ query, contextType = 'public' }) => { const node = useRef(null); - const { entries, queuedCount, fetchNextPage, dequeueEntries, isFetching, isPending } = query; + const { timelineId, entries, queuedCount, fetchNextPage, dequeueEntries, isFetching, isPending } = + query; const handleMoveUp = (index: number) => selectChild(index - 1, node, document.getElementById('status-list') ?? undefined); @@ -134,6 +230,8 @@ const Timeline: React.FC = ({ query, contextType = 'public' }) => { contextType={contextType} onMoveUp={() => handleMoveUp(index)} onMoveDown={() => handleMoveDown(index)} + rebloggedBy={entry.rebloggedBy} + timelineId={timelineId} // contextType={timelineId} // showGroup={showGroup} // variant={divideType === 'border' ? 'slim' : 'rounded'} diff --git a/packages/nicolium/src/components/statuses/status.tsx b/packages/nicolium/src/components/statuses/status.tsx index 721719695..f8cfed55f 100644 --- a/packages/nicolium/src/components/statuses/status.tsx +++ b/packages/nicolium/src/components/statuses/status.tsx @@ -98,11 +98,16 @@ const AccountInfo: React.FC = React.memo(({ status }) => { AccountInfo.displayName = 'AccountInfo'; interface IStatusFollowedTagInfo { + className?: string; status: SelectedStatus; avatarSize: number; } -const StatusFollowedTagInfo: React.FC = ({ status, avatarSize }) => { +const StatusFollowedTagInfo: React.FC = ({ + className, + status, + avatarSize, +}) => { const { data: followedTags } = useFollowedTags(); const filteredTags = status.tags.filter((tag) => @@ -130,7 +135,7 @@ const StatusFollowedTagInfo: React.FC = ({ status, avata return ( = React.memo((props) => { } else if (fromHomeTimeline) { return ( features.followHashtags && ( - + ) ); } @@ -691,4 +696,4 @@ const Status: React.FC = React.memo((props) => { Status.displayName = 'Status'; -export { type IStatus, Status as default }; +export { type IStatus, Status as default, StatusFollowedTagInfo }; diff --git a/packages/nicolium/src/queries/timelines/use-timeline.ts b/packages/nicolium/src/queries/timelines/use-timeline.ts index ddae246ca..5add2d601 100644 --- a/packages/nicolium/src/queries/timelines/use-timeline.ts +++ b/packages/nicolium/src/queries/timelines/use-timeline.ts @@ -57,8 +57,8 @@ const useTimeline = (timelineId: string, fetcher: TimelineFetcher, streamConfig? }, [timelineId]); return useMemo( - () => ({ ...timeline, fetchNextPage, dequeueEntries }), - [timeline, fetchNextPage, dequeueEntries], + () => ({ ...timeline, timelineId, fetchNextPage, dequeueEntries }), + [timeline, timelineId, fetchNextPage, dequeueEntries], ); };