diff --git a/packages/nicolium/src/columns/timeline.tsx b/packages/nicolium/src/columns/timeline.tsx index 3a8f604d6..2dfcd6c3a 100644 --- a/packages/nicolium/src/columns/timeline.tsx +++ b/packages/nicolium/src/columns/timeline.tsx @@ -7,7 +7,17 @@ import Status from '@/components/statuses/status'; import Tombstone from '@/components/statuses/tombstone'; import PlaceholderStatus from '@/features/placeholder/components/placeholder-status'; import { useStatus } from '@/queries/statuses/use-status'; -import { useHomeTimeline } from '@/queries/timelines/use-timelines'; +import { + useAntennaTimeline, + useBubbleTimeline, + useGroupTimeline, + useHashtagTimeline, + useHomeTimeline, + useLinkTimeline, + useListTimeline, + usePublicTimeline, + useWrenchedTimeline, +} from '@/queries/timelines/use-timelines'; import type { FilterContextType } from '@/queries/settings/use-filters'; import type { TimelineEntry } from '@/queries/timelines/use-timeline'; @@ -70,8 +80,13 @@ const TimelineStatus: React.FC = (props): React.JSX.Element => ); }; -const NewTimelineColumn = () => { - const { data, handleLoadMore, isLoading } = useHomeTimeline(); +interface ITimeline { + query: ReturnType; + contextType?: FilterContextType; +} + +const Timeline: React.FC = ({ query, contextType = 'public' }) => { + const { data, handleLoadMore, isLoading } = query; const renderEntry = (entry: TimelineEntry) => { if (entry.type === 'status') { @@ -81,7 +96,7 @@ const NewTimelineColumn = () => { id={entry.id} isConnectedTop={entry.isConnectedTop} isConnectedBottom={entry.isConnectedBottom} - contextType='home' + contextType={contextType} // onMoveUp={handleMoveUp} // onMoveDown={handleMoveDown} // contextType={timelineId} @@ -122,4 +137,94 @@ const NewTimelineColumn = () => { ); }; -export { NewTimelineColumn }; +const HomeTimelineColumn = () => { + const timelineQuery = useHomeTimeline(); + + return ; +}; + +interface IPublicTimelineColumn { + local?: boolean; + remote?: boolean; + instance?: string; +} + +const PublicTimelineColumn: React.FC = (params) => { + const timelineQuery = usePublicTimeline(params); + + return ; +}; + +interface IHashtagTimelineColumn { + hashtag: string; +} + +const HashtagTimelineColumn: React.FC = ({ hashtag }) => { + const timelineQuery = useHashtagTimeline(hashtag); + + return ; +}; + +interface ILinkTimelineColumn { + url: string; +} + +const LinkTimelineColumn: React.FC = ({ url }) => { + const timelineQuery = useLinkTimeline(url); + + return ; +}; + +interface IListTimelineColumn { + listId: string; +} + +const ListTimelineColumn: React.FC = ({ listId }) => { + const timelineQuery = useListTimeline(listId); + + return ; +}; + +interface IGroupTimelineColumn { + groupId: string; +} + +const GroupTimelineColumn: React.FC = ({ groupId }) => { + const timelineQuery = useGroupTimeline(groupId); + + return ; +}; + +const BubbleTimelineColumn = () => { + const timelineQuery = useBubbleTimeline(); + + return ; +}; + +interface IAntennaTimelineColumn { + antennaId: string; +} + +const AntennaTimelineColumn: React.FC = ({ antennaId }) => { + const timelineQuery = useAntennaTimeline(antennaId); + + return ; +}; + +const WrenchedTimelineColumn = () => { + const timelineQuery = useWrenchedTimeline(); + + return ; +}; + +export { + HomeTimelineColumn, + PublicTimelineColumn, + HashtagTimelineColumn, + LinkTimelineColumn, + ListTimelineColumn, + GroupTimelineColumn, + BubbleTimelineColumn, + AntennaTimelineColumn, + WrenchedTimelineColumn, +}; diff --git a/packages/nicolium/src/components/announcements/announcement-content.tsx b/packages/nicolium/src/components/announcements/announcement-content.tsx index c5a343ed3..f93a847a0 100644 --- a/packages/nicolium/src/components/announcements/announcement-content.tsx +++ b/packages/nicolium/src/components/announcements/announcement-content.tsx @@ -35,7 +35,7 @@ const AnnouncementContent: React.FC = ({ announcement }) = if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { e.preventDefault(); e.stopPropagation(); - navigate({ to: '/tags/$id', params: { id: hashtag } }); + navigate({ to: '/tags/$hashtag', params: { hashtag } }); } }; diff --git a/packages/nicolium/src/components/hashtag-link.tsx b/packages/nicolium/src/components/hashtag-link.tsx index 4b2c21cdc..89bf653f4 100644 --- a/packages/nicolium/src/components/hashtag-link.tsx +++ b/packages/nicolium/src/components/hashtag-link.tsx @@ -7,7 +7,7 @@ interface IHashtagLink { } const HashtagLink: React.FC = ({ hashtag }) => ( - e.stopPropagation()} */> + e.stopPropagation()} */> #{hashtag} ); diff --git a/packages/nicolium/src/components/hashtag.tsx b/packages/nicolium/src/components/hashtag.tsx index e182415cd..51cd333f7 100644 --- a/packages/nicolium/src/components/hashtag.tsx +++ b/packages/nicolium/src/components/hashtag.tsx @@ -34,7 +34,7 @@ const Hashtag: React.FC = ({ hashtag }) => { return ( - + #{hashtag.name} diff --git a/packages/nicolium/src/components/statuses/hashtags-bar.tsx b/packages/nicolium/src/components/statuses/hashtags-bar.tsx index 0da413eca..0ba1b6fa8 100644 --- a/packages/nicolium/src/components/statuses/hashtags-bar.tsx +++ b/packages/nicolium/src/components/statuses/hashtags-bar.tsx @@ -32,8 +32,8 @@ const HashtagsBar: React.FC = ({ hashtags }) => { {revealedHashtags.map((hashtag) => ( { e.stopPropagation(); }} diff --git a/packages/nicolium/src/features/ui/router/index.tsx b/packages/nicolium/src/features/ui/router/index.tsx index f41e7bc47..1f01f29f0 100644 --- a/packages/nicolium/src/features/ui/router/index.tsx +++ b/packages/nicolium/src/features/ui/router/index.tsx @@ -438,7 +438,7 @@ export const conversationsRoute = createRoute({ // Tags and links export const hashtagTimelineRoute = createRoute({ getParentRoute: () => layouts.default, - path: '/tags/$id', + path: '/tags/$hashtag', component: HashtagTimeline, beforeLoad: (options) => { const { @@ -1271,10 +1271,10 @@ const redirectPlFeRoute = createRoute({ }); const redirectTagRoute = createRoute({ getParentRoute: () => rootRoute, - path: '/tag/$id', + path: '/tag/$hashtag', component: () => { - const { id } = redirectTagRoute.useParams(); - return ; + const { hashtag } = redirectTagRoute.useParams(); + return ; }, }); const redirectNoticeStatusRoute = createRoute({ diff --git a/packages/nicolium/src/pages/statuses/event-discussion.tsx b/packages/nicolium/src/pages/statuses/event-discussion.tsx index ea869da2e..b775aa02f 100644 --- a/packages/nicolium/src/pages/statuses/event-discussion.tsx +++ b/packages/nicolium/src/pages/statuses/event-discussion.tsx @@ -97,7 +97,7 @@ const EventDiscussionPage: React.FC = () => { {me && (
- +
)}
diff --git a/packages/nicolium/src/pages/timelines/antenna-timeline.tsx b/packages/nicolium/src/pages/timelines/antenna-timeline.tsx index c1d9f9d66..9a168c97f 100644 --- a/packages/nicolium/src/pages/timelines/antenna-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/antenna-timeline.tsx @@ -3,6 +3,7 @@ import React, { useEffect } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { fetchAntennaTimeline } from '@/actions/timelines'; +import { AntennaTimelineColumn } from '@/columns/timeline'; import DropdownMenu from '@/components/dropdown-menu'; import MissingIndicator from '@/components/missing-indicator'; // import Button from '@/components/ui/button'; @@ -13,6 +14,7 @@ import { antennaTimelineRoute } from '@/features/ui/router'; import { useAppDispatch } from '@/hooks/use-app-dispatch'; import { useAntenna, useDeleteAntenna } from '@/queries/accounts/use-antennas'; import { useModalsActions } from '@/stores/modals'; +import { useSettings } from '@/stores/settings'; const messages = defineMessages({ deleteHeading: { id: 'confirmations.delete_antenna.heading', defaultMessage: 'Delete antenna' }, @@ -25,16 +27,12 @@ const messages = defineMessages({ deleteAntenna: { id: 'antennas.delete', defaultMessage: 'Delete antenna' }, }); -const AntennaTimelinePage: React.FC = () => { - const { antennaId } = antennaTimelineRoute.useParams(); +interface IAntennaTimeline { + antennaId: string; +} - const intl = useIntl(); +const AntennaTimeline: React.FC = ({ antennaId }) => { const dispatch = useAppDispatch(); - const { openModal } = useModalsActions(); - const navigate = useNavigate(); - - const { data: antenna, isFetching } = useAntenna(antennaId); - const { mutate: deleteAntenna } = useDeleteAntenna(); useEffect(() => { dispatch(fetchAntennaTimeline(antennaId)); @@ -44,6 +42,40 @@ const AntennaTimelinePage: React.FC = () => { dispatch(fetchAntennaTimeline(antennaId, true)); }; + const emptyMessage = ( +
+ + {/*

+ */} +
+ ); + + return ( + + ); +}; + +const AntennaTimelinePage: React.FC = () => { + const { antennaId } = antennaTimelineRoute.useParams(); + + const intl = useIntl(); + const { experimentalTimeline } = useSettings(); + const { openModal } = useModalsActions(); + const navigate = useNavigate(); + + const { data: antenna, isFetching } = useAntenna(antennaId); + const { mutate: deleteAntenna } = useDeleteAntenna(); + const handleEditClick = () => { openModal('ANTENNA_EDITOR', { antennaId }); }; @@ -79,17 +111,6 @@ const AntennaTimelinePage: React.FC = () => { return ; } - const emptyMessage = ( -
- - {/*

- */} -
- ); - const items = [ { text: intl.formatMessage(messages.editAntenna), @@ -113,14 +134,11 @@ const AntennaTimelinePage: React.FC = () => { /> } > - + {experimentalTimeline ? ( + + ) : ( + + )} ); }; diff --git a/packages/nicolium/src/pages/timelines/bubble-timeline.tsx b/packages/nicolium/src/pages/timelines/bubble-timeline.tsx index fb0d8dd85..3b6cfca7e 100644 --- a/packages/nicolium/src/pages/timelines/bubble-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/bubble-timeline.tsx @@ -3,6 +3,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { fetchBubbleTimeline } from '@/actions/timelines'; import { useBubbleStream } from '@/api/hooks/streaming/use-bubble-stream'; +import { BubbleTimelineColumn } from '@/columns/timeline'; import PullToRefresh from '@/components/pull-to-refresh'; import Column from '@/components/ui/column'; import Timeline from '@/features/ui/components/timeline'; @@ -14,12 +15,11 @@ const messages = defineMessages({ title: { id: 'column.bubble', defaultMessage: 'Bubble timeline' }, }); -const BubbleTimelinePage = () => { - const intl = useIntl(); +const BubbleTimeline = () => { const dispatch = useAppDispatch(); - const features = useFeatures(); const settings = useSettings(); + const onlyMedia = settings.timelines.bubble?.other.onlyMedia ?? false; const timelineId = 'bubble'; @@ -36,24 +36,35 @@ const BubbleTimelinePage = () => { dispatch(fetchBubbleTimeline({ onlyMedia })); }, [onlyMedia]); + return ( + + + } + emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + /> + + ); +}; + +const BubbleTimelinePage = () => { + const intl = useIntl(); + + const settings = useSettings(); + const { experimentalTimeline } = settings; + return ( - - - } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} - /> - + {experimentalTimeline ? : } ); }; diff --git a/packages/nicolium/src/pages/timelines/community-timeline.tsx b/packages/nicolium/src/pages/timelines/community-timeline.tsx index e7affae86..7f7776724 100644 --- a/packages/nicolium/src/pages/timelines/community-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/community-timeline.tsx @@ -3,6 +3,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { fetchPublicTimeline } from '@/actions/timelines'; import { useCommunityStream } from '@/api/hooks/streaming/use-community-stream'; +import { PublicTimelineColumn } from '@/columns/timeline'; import PullToRefresh from '@/components/pull-to-refresh'; import Column from '@/components/ui/column'; import Timeline from '@/features/ui/components/timeline'; @@ -13,20 +14,27 @@ const messages = defineMessages({ title: { id: 'column.community', defaultMessage: 'Local timeline' }, }); -const CommunityTimelinePage = () => { - const intl = useIntl(); - const dispatch = useAppDispatch(); +interface ICommunityTimeline { + onTimelineFailed?: () => void; +} +const CommunityTimeline: React.FC = ({ onTimelineFailed }) => { + const dispatch = useAppDispatch(); const settings = useSettings(); + const onlyMedia = settings.timelines['public:local']?.other.onlyMedia ?? false; const timelineId = 'public:local'; const handleLoadMore = () => { - dispatch(fetchPublicTimeline({ onlyMedia, local: true }, true)); + dispatch( + fetchPublicTimeline({ onlyMedia, local: true }, true, undefined, () => { + onTimelineFailed?.(); + }), + ); }; - const handleRefresh = () => dispatch(fetchPublicTimeline({ onlyMedia, local: true })); + const handleRefresh = () => dispatch(fetchPublicTimeline({ onlyMedia, local: true }, true)); useCommunityStream({ onlyMedia }); @@ -34,26 +42,36 @@ const CommunityTimelinePage = () => { dispatch(fetchPublicTimeline({ onlyMedia, local: true })); }, [onlyMedia]); + return ( + + + } + emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + /> + + ); +}; + +const CommunityTimelinePage = () => { + const intl = useIntl(); + + const { experimentalTimeline } = useSettings(); + return ( - - - } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} - /> - + {experimentalTimeline ? : } ); }; -export { CommunityTimelinePage as default }; +export { CommunityTimeline, CommunityTimelinePage as default }; diff --git a/packages/nicolium/src/pages/timelines/group-timeline.tsx b/packages/nicolium/src/pages/timelines/group-timeline.tsx index d8d2eae86..a5ffe5e82 100644 --- a/packages/nicolium/src/pages/timelines/group-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/group-timeline.tsx @@ -5,6 +5,7 @@ import { FormattedMessage } from 'react-intl'; import { fetchGroupTimeline } from '@/actions/timelines'; import { useGroupStream } from '@/api/hooks/streaming/use-group-stream'; +import { GroupTimelineColumn } from '@/columns/timeline'; import Avatar from '@/components/ui/avatar'; import HStack from '@/components/ui/hstack'; import Stack from '@/components/ui/stack'; @@ -18,31 +19,21 @@ import { useOwnAccount } from '@/hooks/use-own-account'; import { useGroupQuery } from '@/queries/groups/use-group'; import { makeGetStatusIds } from '@/selectors'; import { useComposeActions, useUploadCompose } from '@/stores/compose'; +import { useSettings } from '@/stores/settings'; const getStatusIds = makeGetStatusIds(); -const GroupTimelinePage: React.FC = () => { - const { groupId } = groupTimelineRoute.useParams(); +interface IGroupTimeline { + groupId: string; +} - const composeId = `group:${groupId}`; - - const { data: account } = useOwnAccount(); +const GroupTimeline: React.FC = ({ groupId }) => { const dispatch = useAppDispatch(); - const uploadCompose = useUploadCompose(composeId); - const { updateCompose } = useComposeActions(); - const composer = useRef(null); - const { data: group } = useGroupQuery(groupId); - - const canComposeGroupStatus = !!account && group?.relationship?.member; const featuredStatusIds = useAppSelector((state) => - getStatusIds(state, { type: `group:${group?.id}:pinned` }), + getStatusIds(state, { type: `group:${groupId}:pinned` }), ); - const { isDragging, isDraggedOver } = useDraggedFiles(composer, (files) => { - uploadCompose(files); - }); - const handleLoadMore = () => { dispatch(fetchGroupTimeline(groupId, {}, true)); }; @@ -51,7 +42,46 @@ const GroupTimelinePage: React.FC = () => { useEffect(() => { dispatch(fetchGroupTimeline(groupId, {})); - // dispatch(fetchGroupTimeline(groupId, { pinned: true })); + }, [groupId]); + + return ( + + } + emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + showGroup={false} + featuredStatusIds={featuredStatusIds} + /> + ); +}; + +const GroupTimelinePage: React.FC = () => { + const { groupId } = groupTimelineRoute.useParams(); + + const composeId = `group:${groupId}`; + + const { data: account } = useOwnAccount(); + const { experimentalTimeline } = useSettings(); + const uploadCompose = useUploadCompose(composeId); + const { updateCompose } = useComposeActions(); + const composer = useRef(null); + + const { data: group } = useGroupQuery(groupId); + + const canComposeGroupStatus = !!account && group?.relationship?.member; + + const { isDragging, isDraggedOver } = useDraggedFiles(composer, (files) => { + uploadCompose(files); + }); + + useEffect(() => { updateCompose(composeId, (draft) => { draft.visibility = 'group'; draft.groupId = groupId; @@ -85,32 +115,16 @@ const GroupTimelinePage: React.FC = () => { /> - +
)} - - } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} - showGroup={false} - featuredStatusIds={featuredStatusIds} - /> + {experimentalTimeline ? ( + + ) : ( + + )}
); }; diff --git a/packages/nicolium/src/pages/timelines/hashtag-timeline.tsx b/packages/nicolium/src/pages/timelines/hashtag-timeline.tsx index a3c5e6ff1..386f31653 100644 --- a/packages/nicolium/src/pages/timelines/hashtag-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/hashtag-timeline.tsx @@ -3,6 +3,7 @@ import { FormattedMessage } from 'react-intl'; import { fetchHashtagTimeline, clearTimeline } from '@/actions/timelines'; import { useHashtagStream } from '@/api/hooks/streaming/use-hashtag-stream'; +import { HashtagTimelineColumn } from '@/columns/timeline'; import List, { ListItem } from '@/components/list'; import Column from '@/components/ui/column'; import Toggle from '@/components/ui/toggle'; @@ -16,22 +17,53 @@ import { useUnfollowHashtagMutation, } from '@/queries/hashtags/use-followed-tags'; import { useHashtag } from '@/queries/hashtags/use-hashtag'; +import { useSettings } from '@/stores/settings'; -const HashtagTimelinePage: React.FC = () => { - const { id: tagId } = hashtagTimelineRoute.useParams(); +interface IHashtagTimeline { + hashtag: string; +} - const features = useFeatures(); +const HashtagTimeline: React.FC = ({ hashtag }) => { const dispatch = useAppDispatch(); - const { data: tag } = useHashtag(tagId); - const { isLoggedIn } = useLoggedIn(); - - const { mutate: followHashtag } = useFollowHashtagMutation(tagId); - const { mutate: unfollowHashtag } = useUnfollowHashtagMutation(tagId); const handleLoadMore = () => { - dispatch(fetchHashtagTimeline(tagId, {}, true)); + dispatch(fetchHashtagTimeline(hashtag, {}, true)); }; + useHashtagStream(hashtag); + + useEffect(() => { + dispatch(clearTimeline(`hashtag:${hashtag}`)); + dispatch(fetchHashtagTimeline(hashtag)); + }, [hashtag]); + + return ( + + } + /> + ); +}; + +const HashtagTimelinePage: React.FC = () => { + const { hashtag } = hashtagTimelineRoute.useParams(); + + const features = useFeatures(); + const { experimentalTimeline } = useSettings(); + const { data: tag } = useHashtag(hashtag); + const { isLoggedIn } = useLoggedIn(); + + const { mutate: followHashtag } = useFollowHashtagMutation(hashtag); + const { mutate: unfollowHashtag } = useUnfollowHashtagMutation(hashtag); + const handleFollow = () => { if (tag?.following) { unfollowHashtag(); @@ -40,15 +72,8 @@ const HashtagTimelinePage: React.FC = () => { } }; - useHashtagStream(tagId); - - useEffect(() => { - dispatch(clearTimeline(`hashtag:${tagId}`)); - dispatch(fetchHashtagTimeline(tagId)); - }, [tagId]); - return ( - + {features.followHashtags && isLoggedIn && ( { )} - - } - /> + {experimentalTimeline ? ( + + ) : ( + + )} ); }; diff --git a/packages/nicolium/src/pages/timelines/home-timeline.tsx b/packages/nicolium/src/pages/timelines/home-timeline.tsx index 7ea5516ce..82b4d8bd1 100644 --- a/packages/nicolium/src/pages/timelines/home-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/home-timeline.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef } from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { fetchHomeTimeline } from '@/actions/timelines'; -import { NewTimelineColumn } from '@/columns/timeline'; +import { HomeTimelineColumn } from '@/columns/timeline'; import { Link } from '@/components/link'; import PullToRefresh from '@/components/pull-to-refresh'; import Column from '@/components/ui/column'; @@ -110,7 +110,7 @@ const HomeTimelinePage: React.FC = () => { return ( - {experimentalTimeline ? : } + {experimentalTimeline ? : } ); }; diff --git a/packages/nicolium/src/pages/timelines/landing-timeline.tsx b/packages/nicolium/src/pages/timelines/landing-timeline.tsx index 58cbb585a..8aed62096 100644 --- a/packages/nicolium/src/pages/timelines/landing-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/landing-timeline.tsx @@ -1,23 +1,22 @@ import clsx from 'clsx'; -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { FormattedMessage } from 'react-intl'; -import { fetchPublicTimeline } from '@/actions/timelines'; -import { useCommunityStream } from '@/api/hooks/streaming/use-community-stream'; +import { PublicTimelineColumn } from '@/columns/timeline'; import Markup from '@/components/markup'; -import PullToRefresh from '@/components/pull-to-refresh'; import { ParsedContent } from '@/components/statuses/parsed-content'; import Button from '@/components/ui/button'; import Column from '@/components/ui/column'; import HStack from '@/components/ui/hstack'; import Stack from '@/components/ui/stack'; -import Timeline from '@/features/ui/components/timeline'; -import { useAppDispatch } from '@/hooks/use-app-dispatch'; import { useInstance } from '@/hooks/use-instance'; import { useRegistrationStatus } from '@/hooks/use-registration-status'; import { About } from '@/pages/utils/about'; +import { useSettings } from '@/stores/settings'; import { getTextDirection } from '@/utils/rtl'; +import { CommunityTimeline } from './community-timeline'; + interface ILogoText extends Pick, 'className' | 'dir'> { children: React.ReactNode; } @@ -54,34 +53,14 @@ const SiteBanner: React.FC = () => { }; const LandingTimelinePage = () => { - const dispatch = useAppDispatch(); const instance = useInstance(); const { isOpen } = useRegistrationStatus(); + const { experimentalTimeline } = useSettings(); const [timelineFailed, setTimelineFailed] = useState(false); const timelineEnabled = !instance.pleroma.metadata.restrict_unauthenticated.timelines.local; - const timelineId = 'public:local'; - - const handleLoadMore = () => { - dispatch(fetchPublicTimeline({ local: true }, true)); - }; - - const handleRefresh = () => dispatch(fetchPublicTimeline({ local: true })); - - useCommunityStream({ enabled: timelineEnabled }); - - useEffect(() => { - if (timelineEnabled) { - dispatch( - fetchPublicTimeline({ local: true }, false, undefined, () => { - setTimelineFailed(true); - }), - ); - } - }, []); - return (
@@ -100,22 +79,11 @@ const LandingTimelinePage = () => { {timelineEnabled && !timelineFailed ? ( - - - } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} - /> - + experimentalTimeline ? ( + + ) : ( + setTimelineFailed(true)} /> + ) ) : ( )} diff --git a/packages/nicolium/src/pages/timelines/link-timeline.tsx b/packages/nicolium/src/pages/timelines/link-timeline.tsx index 66d7f5182..a9a864347 100644 --- a/packages/nicolium/src/pages/timelines/link-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/link-timeline.tsx @@ -2,48 +2,66 @@ import React, { useEffect } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { clearTimeline, fetchLinkTimeline } from '@/actions/timelines'; +import { LinkTimelineColumn } from '@/columns/timeline'; import Column from '@/components/ui/column'; import Timeline from '@/features/ui/components/timeline'; import { linkTimelineRoute } from '@/features/ui/router'; import { useAppDispatch } from '@/hooks/use-app-dispatch'; +import { useSettings } from '@/stores/settings'; const messages = defineMessages({ header: { id: 'column.link_timeline', defaultMessage: 'Posts linking to {url}' }, }); +interface ILinkTimeline { + url: string; +} + +const LinkTimeline: React.FC = ({ url }) => { + const dispatch = useAppDispatch(); + + const handleLoadMore = () => { + dispatch(fetchLinkTimeline(url, true)); + }; + + useEffect(() => { + dispatch(clearTimeline(`link:${url}`)); + dispatch(fetchLinkTimeline(url)); + }, [url]); + + return ( + + } + emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + /> + ); +}; + const LinkTimelinePage: React.FC = () => { const { url } = linkTimelineRoute.useParams(); const decodedUrl = decodeURIComponent(url || ''); const intl = useIntl(); - const dispatch = useAppDispatch(); - - const handleLoadMore = () => { - dispatch(fetchLinkTimeline(decodedUrl, true)); - }; - - useEffect(() => { - dispatch(clearTimeline(`link:${decodedUrl}`)); - dispatch(fetchLinkTimeline(decodedUrl)); - }, [decodedUrl]); + const { experimentalTimeline } = useSettings(); return ( - - } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} - /> + {experimentalTimeline ? ( + + ) : ( + + )} ); }; diff --git a/packages/nicolium/src/pages/timelines/list-timeline.tsx b/packages/nicolium/src/pages/timelines/list-timeline.tsx index 90c38656d..0b8c8a157 100644 --- a/packages/nicolium/src/pages/timelines/list-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/list-timeline.tsx @@ -4,6 +4,7 @@ import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { fetchListTimeline } from '@/actions/timelines'; import { useListStream } from '@/api/hooks/streaming/use-list-stream'; +import { ListTimelineColumn } from '@/columns/timeline'; import DropdownMenu from '@/components/dropdown-menu'; import MissingIndicator from '@/components/missing-indicator'; import Button from '@/components/ui/button'; @@ -14,6 +15,7 @@ import { listTimelineRoute } from '@/features/ui/router'; import { useAppDispatch } from '@/hooks/use-app-dispatch'; import { useDeleteList, useList } from '@/queries/accounts/use-lists'; import { useModalsActions } from '@/stores/modals'; +import { useSettings } from '@/stores/settings'; const messages = defineMessages({ deleteHeading: { id: 'confirmations.delete_list.heading', defaultMessage: 'Delete list' }, @@ -26,27 +28,66 @@ const messages = defineMessages({ deleteList: { id: 'lists.delete', defaultMessage: 'Delete list' }, }); +interface IListTimeline { + listId: string; +} + +const ListTimeline: React.FC = ({ listId }) => { + const dispatch = useAppDispatch(); + const settings = useSettings(); + const { openModal } = useModalsActions(); + + const onlyMedia = settings.timelines[`list:${listId}`]?.other.onlyMedia ?? false; + + const handleLoadMore = () => { + dispatch(fetchListTimeline(listId, true)); + }; + + useListStream(listId); + + useEffect(() => { + dispatch(fetchListTimeline(listId, false)); + }, [listId, onlyMedia]); + + const handleEditClick = () => { + openModal('LIST_EDITOR', { listId }); + }; + + return ( + + +
+
+ +
+ } + emptyMessageIcon={require('@phosphor-icons/core/regular/list-bullets.svg')} + /> + ); +}; + const ListTimelinePage: React.FC = () => { const { listId } = listTimelineRoute.useParams(); const intl = useIntl(); - const dispatch = useAppDispatch(); + const { experimentalTimeline } = useSettings(); const { openModal } = useModalsActions(); const navigate = useNavigate(); const { data: list, isFetching } = useList(listId); const { mutate: deleteList } = useDeleteList(); - useListStream(listId); - - useEffect(() => { - dispatch(fetchListTimeline(listId)); - }, [listId]); - - const handleLoadMore = () => { - dispatch(fetchListTimeline(listId, true)); - }; - const handleEditClick = () => { openModal('LIST_EDITOR', { listId }); }; @@ -82,20 +123,6 @@ const ListTimelinePage: React.FC = () => { return ; } - const emptyMessage = ( -
- -
-
- -
- ); - const items = [ { text: intl.formatMessage(messages.editList), @@ -119,14 +146,11 @@ const ListTimelinePage: React.FC = () => { /> } > - + {experimentalTimeline ? ( + + ) : ( + + )}
); }; diff --git a/packages/nicolium/src/pages/timelines/public-timeline.tsx b/packages/nicolium/src/pages/timelines/public-timeline.tsx index 51e4a83e5..8558dd59e 100644 --- a/packages/nicolium/src/pages/timelines/public-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/public-timeline.tsx @@ -5,6 +5,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { changeSetting } from '@/actions/settings'; import { fetchPublicTimeline } from '@/actions/timelines'; import { usePublicStream } from '@/api/hooks/streaming/use-public-stream'; +import { PublicTimelineColumn } from '@/columns/timeline'; import PullToRefresh from '@/components/pull-to-refresh'; import Accordion from '@/components/ui/accordion'; import Column from '@/components/ui/column'; @@ -19,27 +20,13 @@ const messages = defineMessages({ dismiss: { id: 'fediverse_tab.explanation_box.dismiss', defaultMessage: "Don't show again" }, }); -const PublicTimelinePage = () => { - const intl = useIntl(); +const PublicTimeline = () => { const dispatch = useAppDispatch(); - - const instance = useInstance(); const settings = useSettings(); const onlyMedia = settings.timelines.public?.other.onlyMedia ?? false; const timelineId = 'public'; - const explanationBoxExpanded = settings.explanationBox; - const showExplanationBox = settings.showExplanationBox; - - const dismissExplanationBox = () => { - dispatch(changeSetting(['showExplanationBox'], false)); - }; - - const toggleExplanationBox = (setting: boolean) => { - dispatch(changeSetting(['explanationBox'], setting)); - }; - const handleLoadMore = () => { dispatch(fetchPublicTimeline({ onlyMedia }, true)); }; @@ -52,6 +39,45 @@ const PublicTimelinePage = () => { dispatch(fetchPublicTimeline({ onlyMedia }, true)); }, [onlyMedia]); + return ( + + + } + emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + /> + + ); +}; + +const PublicTimelinePage = () => { + const dispatch = useAppDispatch(); + const intl = useIntl(); + + const instance = useInstance(); + const settings = useSettings(); + const { experimentalTimeline } = settings; + + const explanationBoxExpanded = settings.explanationBox; + const showExplanationBox = settings.showExplanationBox; + + const dismissExplanationBox = () => { + dispatch(changeSetting(['showExplanationBox'], false)); + }; + + const toggleExplanationBox = (setting: boolean) => { + dispatch(changeSetting(['explanationBox'], setting)); + }; + return ( @@ -89,22 +115,7 @@ const PublicTimelinePage = () => { /> )} - - - } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} - /> - + {experimentalTimeline ? : } ); }; diff --git a/packages/nicolium/src/pages/timelines/remote-timeline.tsx b/packages/nicolium/src/pages/timelines/remote-timeline.tsx index c991ebc9c..26bc776dc 100644 --- a/packages/nicolium/src/pages/timelines/remote-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/remote-timeline.tsx @@ -4,6 +4,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { fetchPublicTimeline } from '@/actions/timelines'; import { useRemoteStream } from '@/api/hooks/streaming/use-remote-stream'; +import { PublicTimelineColumn } from '@/columns/timeline'; import Column from '@/components/ui/column'; import HStack from '@/components/ui/hstack'; import IconButton from '@/components/ui/icon-button'; @@ -18,25 +19,17 @@ const messages = defineMessages({ close: { id: 'remote_timeline.close', defaultMessage: 'Close remote timeline' }, }); -/** View statuses from a remote instance. */ -const RemoteTimelinePage: React.FC = () => { - const { instance } = remoteTimelineRoute.useParams(); +interface IRemoteTimeline { + instance: string; +} - const intl = useIntl(); - const navigate = useNavigate(); +const RemoteTimeline: React.FC = ({ instance }) => { const dispatch = useAppDispatch(); - const settings = useSettings(); const timelineId = 'remote'; const onlyMedia = settings.timelines.remote?.other.onlyMedia ?? false; - const pinned = settings.remote_timeline.pinnedHosts.includes(instance); - - const handleCloseClick: React.MouseEventHandler = () => { - navigate({ to: '/timeline/fediverse' }); - }; - const handleLoadMore = () => { dispatch(fetchPublicTimeline({ onlyMedia, instance }, true)); }; @@ -45,7 +38,41 @@ const RemoteTimelinePage: React.FC = () => { useEffect(() => { dispatch(fetchPublicTimeline({ onlyMedia, instance })); - }, [onlyMedia]); + }, [onlyMedia, instance]); + + return ( + + } + emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + /> + ); +}; + +/** View statuses from a remote instance. */ +const RemoteTimelinePage: React.FC = () => { + const { instance } = remoteTimelineRoute.useParams(); + + const intl = useIntl(); + const navigate = useNavigate(); + + const settings = useSettings(); + const { experimentalTimeline } = settings; + + const pinned = settings.remote_timeline.pinnedHosts.includes(instance); + + const handleCloseClick: React.MouseEventHandler = () => { + navigate({ to: '/timeline/fediverse' }); + }; return ( @@ -70,20 +97,11 @@ const RemoteTimelinePage: React.FC = () => {
)} - - } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} - /> + {experimentalTimeline ? ( + + ) : ( + + )} ); }; diff --git a/packages/nicolium/src/pages/timelines/wrenched-timeline.tsx b/packages/nicolium/src/pages/timelines/wrenched-timeline.tsx index 0fa4b3ee1..0b1d47d26 100644 --- a/packages/nicolium/src/pages/timelines/wrenched-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/wrenched-timeline.tsx @@ -2,6 +2,7 @@ import React, { useEffect } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { fetchWrenchedTimeline } from '@/actions/timelines'; +import { WrenchedTimelineColumn } from '@/columns/timeline'; import PullToRefresh from '@/components/pull-to-refresh'; import Column from '@/components/ui/column'; import Timeline from '@/features/ui/components/timeline'; @@ -12,11 +13,10 @@ const messages = defineMessages({ title: { id: 'column.wrenched', defaultMessage: 'Recent wrenches timeline' }, }); -const WrenchedTimelinePage = () => { - const intl = useIntl(); +const WrenchedTimeline = () => { const dispatch = useAppDispatch(); - const settings = useSettings(); + const onlyMedia = settings.timelines.wrenched?.other.onlyMedia ?? false; const timelineId = 'wrenched'; @@ -31,24 +31,35 @@ const WrenchedTimelinePage = () => { dispatch(fetchWrenchedTimeline({ onlyMedia })); }, [onlyMedia]); + return ( + + + } + emptyMessageIcon={require('@phosphor-icons/core/regular/wrench.svg')} + /> + + ); +}; + +const WrenchedTimelinePage = () => { + const intl = useIntl(); + + const settings = useSettings(); + const { experimentalTimeline } = settings; + return ( - - - } - emptyMessageIcon={require('@phosphor-icons/core/regular/wrench.svg')} - /> - + {experimentalTimeline ? : } ); };