nicolium: remove legacy timeline
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -887,29 +887,6 @@ const Preferences = () => {
|
||||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
|
||||
<List>
|
||||
<ListItem
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='preferences.fields.experimental_timeline_label'
|
||||
defaultMessage='Enable experimental timeline'
|
||||
/>
|
||||
}
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='preferences.fields.experimental_timeline_hint'
|
||||
defaultMessage='It replaces the stable timeline experience and might not offer all features.'
|
||||
/>
|
||||
}
|
||||
>
|
||||
<SettingToggle
|
||||
settings={settings}
|
||||
settingPath={['experimentalTimeline']}
|
||||
onChange={onToggleChange}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1568,8 +1568,6 @@
|
||||
"preferences.fields.display_media.default": "Hide posts marked as sensitive",
|
||||
"preferences.fields.display_media.hide_all": "Always hide media posts",
|
||||
"preferences.fields.display_media.show_all": "Always show posts",
|
||||
"preferences.fields.experimental_timeline_hint": "It replaces the stable timeline experience and might not offer all features.",
|
||||
"preferences.fields.experimental_timeline_label": "Enable experimental timeline",
|
||||
"preferences.fields.implicit_addressing_label": "Include mentions in post content when replying",
|
||||
"preferences.fields.interface_size": "Interface size",
|
||||
"preferences.fields.known_languages_label": "Languages you know",
|
||||
|
||||
@ -4,20 +4,14 @@ import { FormattedMessage } from 'react-intl';
|
||||
import { fetchAccountTimeline } from '@/actions/timelines';
|
||||
import { AccountTimelineColumn } from '@/columns/timeline';
|
||||
import MissingIndicator from '@/components/missing-indicator';
|
||||
import StatusList from '@/components/statuses/status-list';
|
||||
import Card, { CardBody } from '@/components/ui/card';
|
||||
import Spinner from '@/components/ui/spinner';
|
||||
import Text from '@/components/ui/text';
|
||||
import { profileRoute } from '@/features/ui/router';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from '@/hooks/use-app-selector';
|
||||
import { useFeatures } from '@/hooks/use-features';
|
||||
import { useAccountLookup } from '@/queries/accounts/use-account-lookup';
|
||||
import { usePinnedStatuses } from '@/queries/status-lists/use-pinned-statuses';
|
||||
import { makeGetStatusIds } from '@/selectors';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
|
||||
const getStatusIds = makeGetStatusIds();
|
||||
|
||||
const AccountTimelinePage: React.FC = () => {
|
||||
const { username } = profileRoute.useParams();
|
||||
@ -25,20 +19,12 @@ const AccountTimelinePage: React.FC = () => {
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const features = useFeatures();
|
||||
const settings = useSettings();
|
||||
|
||||
const { data: account, isPending } = useAccountLookup(username);
|
||||
|
||||
const path = withReplies ? `${account?.id}:with_replies` : account?.id;
|
||||
const showPins = settings.account_timeline.shows.pinned && !withReplies;
|
||||
const statusIds = useAppSelector((state) =>
|
||||
getStatusIds(state, { type: `account:${path}`, prefix: 'account_timeline' }),
|
||||
);
|
||||
const { data: featuredStatusIds } = usePinnedStatuses(account?.id || '');
|
||||
const { data: _featuredStatusIds } = usePinnedStatuses(account?.id || '');
|
||||
|
||||
const isBlocked = account?.relationship?.blocked_by && !features.blockersVisible;
|
||||
const isLoading = useAppSelector((state) => state.timelines[`account:${path}`]?.isLoading);
|
||||
const hasMore = useAppSelector((state) => state.timelines[`account:${path}`]?.hasMore);
|
||||
|
||||
const accountUsername = account?.username ?? username;
|
||||
|
||||
@ -52,12 +38,6 @@ const AccountTimelinePage: React.FC = () => {
|
||||
}
|
||||
}, [account?.id, withReplies]);
|
||||
|
||||
const handleLoadMore = () => {
|
||||
if (account) {
|
||||
dispatch(fetchAccountTimeline(account.id, { exclude_replies: !withReplies }, true));
|
||||
}
|
||||
};
|
||||
|
||||
if (!account && isPending) {
|
||||
return <Spinner />;
|
||||
} else if (!account) {
|
||||
@ -80,21 +60,11 @@ const AccountTimelinePage: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
return settings.experimentalTimeline ? (
|
||||
return (
|
||||
<AccountTimelineColumn
|
||||
accountId={account.id}
|
||||
excludeReplies={!withReplies}
|
||||
// featuredStatusIds={showPins ? featuredStatusIds : undefined}
|
||||
/>
|
||||
) : (
|
||||
<StatusList
|
||||
timelineId={`account:${path}`}
|
||||
scrollKey='account_timeline'
|
||||
statusIds={statusIds}
|
||||
featuredStatusIds={showPins ? featuredStatusIds : undefined}
|
||||
isLoading={isLoading}
|
||||
hasMore={hasMore}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessageText={
|
||||
<FormattedMessage id='empty_column.account_timeline' defaultMessage='No posts here!' />
|
||||
}
|
||||
|
||||
@ -1,20 +1,16 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import React, { useEffect } from 'react';
|
||||
import React 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';
|
||||
import Column from '@/components/ui/column';
|
||||
import Spinner from '@/components/ui/spinner';
|
||||
import Timeline from '@/features/ui/components/timeline';
|
||||
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' },
|
||||
@ -27,47 +23,10 @@ const messages = defineMessages({
|
||||
deleteAntenna: { id: 'antennas.delete', defaultMessage: 'Delete antenna' },
|
||||
});
|
||||
|
||||
interface IAntennaTimeline {
|
||||
antennaId: string;
|
||||
}
|
||||
|
||||
const AntennaTimeline: React.FC<IAntennaTimeline> = ({ antennaId }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchAntennaTimeline(antennaId));
|
||||
}, [antennaId]);
|
||||
|
||||
const handleLoadMore = () => {
|
||||
dispatch(fetchAntennaTimeline(antennaId, true));
|
||||
};
|
||||
|
||||
const emptyMessage = (
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='empty_column.antenna'
|
||||
defaultMessage='There is nothing in this antenna yet. When posts matching the criteria will be created, they will appear here.'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Timeline
|
||||
loadMoreClassName='sm:pb-4 black:sm:pb-0 black:sm:mx-4'
|
||||
scrollKey='antenna_timeline'
|
||||
timelineId={`antenna:${antennaId}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessageText={emptyMessage}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const AntennaTimelinePage: React.FC = () => {
|
||||
const { antennaId } = antennaTimelineRoute.useParams();
|
||||
|
||||
const intl = useIntl();
|
||||
const { experimentalTimeline } = useSettings();
|
||||
const { openModal } = useModalsActions();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -132,11 +91,18 @@ const AntennaTimelinePage: React.FC = () => {
|
||||
/>
|
||||
}
|
||||
>
|
||||
{experimentalTimeline ? (
|
||||
<AntennaTimelineColumn antennaId={antennaId} />
|
||||
) : (
|
||||
<AntennaTimeline antennaId={antennaId} />
|
||||
)}
|
||||
<AntennaTimelineColumn
|
||||
antennaId={antennaId}
|
||||
emptyMessageText={
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='empty_column.antenna'
|
||||
defaultMessage='There is nothing in this antenna yet. When posts matching the criteria will be created, they will appear here.'
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,49 +1,19 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
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';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useFeatures } from '@/hooks/use-features';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.bubble', defaultMessage: 'Bubble timeline' },
|
||||
});
|
||||
|
||||
const BubbleTimeline = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const features = useFeatures();
|
||||
const settings = useSettings();
|
||||
|
||||
const onlyMedia = settings.timelines.bubble?.other.onlyMedia ?? false;
|
||||
|
||||
const timelineId = 'bubble';
|
||||
|
||||
const handleLoadMore = () => {
|
||||
dispatch(fetchBubbleTimeline({ onlyMedia }, true));
|
||||
};
|
||||
|
||||
const handleRefresh = () => dispatch(fetchBubbleTimeline({ onlyMedia }, true));
|
||||
|
||||
useBubbleStream({ onlyMedia, enabled: features.bubbleTimelineStreaming });
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchBubbleTimeline({ onlyMedia }));
|
||||
}, [onlyMedia]);
|
||||
const BubbleTimelinePage = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<PullToRefresh onRefresh={handleRefresh}>
|
||||
<Timeline
|
||||
loadMoreClassName='sm:pb-4 black:sm:pb-0 black:sm:mx-4'
|
||||
scrollKey={`${timelineId}_timeline`}
|
||||
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
|
||||
prefix='home'
|
||||
onLoadMore={handleLoadMore}
|
||||
<Column className='-mt-3 sm:mt-0' label={intl.formatMessage(messages.title)}>
|
||||
<BubbleTimelineColumn
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.bubble'
|
||||
@ -52,19 +22,6 @@ const BubbleTimeline = () => {
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
);
|
||||
};
|
||||
|
||||
const BubbleTimelinePage = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const settings = useSettings();
|
||||
const { experimentalTimeline } = settings;
|
||||
|
||||
return (
|
||||
<Column className='-mt-3 sm:mt-0' label={intl.formatMessage(messages.title)}>
|
||||
{experimentalTimeline ? <BubbleTimelineColumn /> : <BubbleTimeline />}
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@ -9,12 +9,10 @@ import MissingIndicator from '@/components/missing-indicator';
|
||||
import Button from '@/components/ui/button';
|
||||
import Column from '@/components/ui/column';
|
||||
import Spinner from '@/components/ui/spinner';
|
||||
import Timeline from '@/features/ui/components/timeline';
|
||||
import { circleTimelineRoute } from '@/features/ui/router';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useCircle, useDeleteCircle } from '@/queries/accounts/use-circles';
|
||||
import { useModalsActions } from '@/stores/modals';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteHeading: { id: 'confirmations.delete_circle.heading', defaultMessage: 'Delete circle' },
|
||||
@ -27,48 +25,6 @@ const messages = defineMessages({
|
||||
deleteCircle: { id: 'circles.delete', defaultMessage: 'Delete circle' },
|
||||
});
|
||||
|
||||
interface ICircleTimeline {
|
||||
circleId: string;
|
||||
}
|
||||
|
||||
const CircleTimeline: React.FC<ICircleTimeline> = ({ circleId }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { openModal } = useModalsActions();
|
||||
|
||||
const handleLoadMore = () => {
|
||||
dispatch(fetchCircleTimeline(circleId, true));
|
||||
};
|
||||
|
||||
const handleEditClick = () => {
|
||||
openModal('CIRCLE_EDITOR', { circleId });
|
||||
};
|
||||
|
||||
const emptyMessage = (
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='empty_column.circle'
|
||||
defaultMessage='There is nothing in this circle yet. When members of this circle create new posts, they will appear here.'
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Button onClick={handleEditClick}>
|
||||
<FormattedMessage id='circle.click_to_add' defaultMessage='Click here to add people' />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Timeline
|
||||
loadMoreClassName='sm:pb-4 black:sm:pb-0 black:sm:mx-4'
|
||||
scrollKey='circle_timeline'
|
||||
timelineId={`circle:${circleId}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessageText={emptyMessage}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const CircleTimelinePage: React.FC = () => {
|
||||
const { circleId } = circleTimelineRoute.useParams();
|
||||
|
||||
@ -76,7 +32,6 @@ const CircleTimelinePage: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { openModal } = useModalsActions();
|
||||
const navigate = useNavigate();
|
||||
const { experimentalTimeline } = useSettings();
|
||||
|
||||
const { data: circle, isFetching } = useCircle(circleId);
|
||||
const { mutate: deleteCircle } = useDeleteCircle();
|
||||
@ -143,11 +98,26 @@ const CircleTimelinePage: React.FC = () => {
|
||||
/>
|
||||
}
|
||||
>
|
||||
{experimentalTimeline ? (
|
||||
<CircleTimelineColumn circleId={circleId} />
|
||||
) : (
|
||||
<CircleTimeline circleId={circleId} />
|
||||
)}
|
||||
<CircleTimelineColumn
|
||||
circleId={circleId}
|
||||
emptyMessageText={
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='empty_column.circle'
|
||||
defaultMessage='There is nothing in this circle yet. When members of this circle create new posts, they will appear here.'
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Button onClick={handleEditClick}>
|
||||
<FormattedMessage
|
||||
id='circle.click_to_add'
|
||||
defaultMessage='Click here to add people'
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,55 +1,20 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
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';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.community', defaultMessage: 'Local timeline' },
|
||||
});
|
||||
|
||||
interface ICommunityTimeline {
|
||||
onTimelineFailed?: () => void;
|
||||
}
|
||||
|
||||
const CommunityTimeline: React.FC<ICommunityTimeline> = ({ 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, undefined, () => {
|
||||
onTimelineFailed?.();
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleRefresh = () => dispatch(fetchPublicTimeline({ onlyMedia, local: true }, true));
|
||||
|
||||
useCommunityStream({ onlyMedia });
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchPublicTimeline({ onlyMedia, local: true }));
|
||||
}, [onlyMedia]);
|
||||
const CommunityTimelinePage = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<PullToRefresh onRefresh={handleRefresh}>
|
||||
<Timeline
|
||||
loadMoreClassName='sm:pb-4 black:sm:pb-0 black:sm:mx-4'
|
||||
scrollKey={`${timelineId}_timeline`}
|
||||
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
|
||||
prefix='home'
|
||||
onLoadMore={handleLoadMore}
|
||||
<Column className='-mt-3 sm:mt-0' label={intl.formatMessage(messages.title)}>
|
||||
<PublicTimelineColumn
|
||||
local
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.community'
|
||||
@ -58,20 +23,8 @@ const CommunityTimeline: React.FC<ICommunityTimeline> = ({ onTimelineFailed }) =
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
);
|
||||
};
|
||||
|
||||
const CommunityTimelinePage = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { experimentalTimeline } = useSettings();
|
||||
|
||||
return (
|
||||
<Column className='-mt-3 sm:mt-0' label={intl.formatMessage(messages.title)}>
|
||||
{experimentalTimeline ? <PublicTimelineColumn local /> : <CommunityTimeline />}
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
export { CommunityTimeline, CommunityTimelinePage as default };
|
||||
export { CommunityTimelinePage as default };
|
||||
|
||||
@ -3,55 +3,16 @@ import clsx from 'clsx';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
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';
|
||||
import Timeline from '@/features/ui/components/timeline';
|
||||
import { groupTimelineRoute } from '@/features/ui/router';
|
||||
import { ComposeForm } from '@/features/ui/util/async-components';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useDraggedFiles } from '@/hooks/use-dragged-files';
|
||||
import { useOwnAccount } from '@/hooks/use-own-account';
|
||||
import { useGroupQuery } from '@/queries/groups/use-group';
|
||||
import { useComposeActions, useUploadCompose } from '@/stores/compose';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
|
||||
interface IGroupTimeline {
|
||||
groupId: string;
|
||||
}
|
||||
|
||||
const GroupTimeline: React.FC<IGroupTimeline> = ({ groupId }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleLoadMore = () => {
|
||||
dispatch(fetchGroupTimeline(groupId, {}, true));
|
||||
};
|
||||
|
||||
useGroupStream(groupId);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchGroupTimeline(groupId, {}));
|
||||
}, [groupId]);
|
||||
|
||||
return (
|
||||
<Timeline
|
||||
scrollKey='group_timeline'
|
||||
timelineId={`group:${groupId}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.group'
|
||||
defaultMessage='There are no posts in this group yet.'
|
||||
/>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
showGroup={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const GroupTimelinePage: React.FC = () => {
|
||||
const { groupId } = groupTimelineRoute.useParams();
|
||||
@ -59,7 +20,6 @@ const GroupTimelinePage: React.FC = () => {
|
||||
const composeId = `group:${groupId}`;
|
||||
|
||||
const { data: account } = useOwnAccount();
|
||||
const { experimentalTimeline } = useSettings();
|
||||
const uploadCompose = useUploadCompose(composeId);
|
||||
const { updateCompose } = useComposeActions();
|
||||
const composer = useRef<HTMLDivElement>(null);
|
||||
@ -111,11 +71,17 @@ const GroupTimelinePage: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{experimentalTimeline ? (
|
||||
<GroupTimelineColumn groupId={groupId} />
|
||||
) : (
|
||||
<GroupTimeline groupId={groupId} />
|
||||
)}
|
||||
<GroupTimelineColumn
|
||||
groupId={groupId}
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.group'
|
||||
defaultMessage='There are no posts in this group yet.'
|
||||
/>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
// showGroup={falsse}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
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';
|
||||
import Timeline from '@/features/ui/components/timeline';
|
||||
import { hashtagTimelineRoute } from '@/features/ui/router';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useFeatures } from '@/hooks/use-features';
|
||||
import { useLoggedIn } from '@/hooks/use-logged-in';
|
||||
import {
|
||||
@ -17,47 +13,11 @@ import {
|
||||
useUnfollowHashtagMutation,
|
||||
} from '@/queries/hashtags/use-followed-tags';
|
||||
import { useHashtag } from '@/queries/hashtags/use-hashtag';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
|
||||
interface IHashtagTimeline {
|
||||
hashtag: string;
|
||||
}
|
||||
|
||||
const HashtagTimeline: React.FC<IHashtagTimeline> = ({ hashtag }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleLoadMore = () => {
|
||||
dispatch(fetchHashtagTimeline(hashtag, {}, true));
|
||||
};
|
||||
|
||||
useHashtagStream(hashtag);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(clearTimeline(`hashtag:${hashtag}`));
|
||||
dispatch(fetchHashtagTimeline(hashtag));
|
||||
}, [hashtag]);
|
||||
|
||||
return (
|
||||
<Timeline
|
||||
loadMoreClassName='sm:pb-4 black:sm:pb-0 black:sm:mx-4'
|
||||
scrollKey='hashtag_timeline'
|
||||
timelineId={`hashtag:${hashtag}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.hashtag'
|
||||
defaultMessage='There is nothing in this hashtag yet.'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const HashtagTimelinePage: React.FC = () => {
|
||||
const { hashtag } = hashtagTimelineRoute.useParams();
|
||||
|
||||
const features = useFeatures();
|
||||
const { experimentalTimeline } = useSettings();
|
||||
const { data: tag } = useHashtag(hashtag);
|
||||
const { isLoggedIn } = useLoggedIn();
|
||||
|
||||
@ -84,11 +44,15 @@ const HashtagTimelinePage: React.FC = () => {
|
||||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
{experimentalTimeline ? (
|
||||
<HashtagTimelineColumn hashtag={hashtag} />
|
||||
) : (
|
||||
<HashtagTimeline hashtag={hashtag} />
|
||||
)}
|
||||
<HashtagTimelineColumn
|
||||
hashtag={hashtag}
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.hashtag'
|
||||
defaultMessage='There is nothing in this hashtag yet.'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,66 +1,49 @@
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { fetchHomeTimeline } from '@/actions/timelines';
|
||||
import { HomeTimelineColumn } from '@/columns/timeline';
|
||||
import { Link } from '@/components/link';
|
||||
import PullToRefresh from '@/components/pull-to-refresh';
|
||||
import Column from '@/components/ui/column';
|
||||
import Stack from '@/components/ui/stack';
|
||||
import Text from '@/components/ui/text';
|
||||
import Timeline from '@/features/ui/components/timeline';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from '@/hooks/use-app-selector';
|
||||
import { useFeatures } from '@/hooks/use-features';
|
||||
import { useInstance } from '@/hooks/use-instance';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.home', defaultMessage: 'Home' },
|
||||
});
|
||||
|
||||
const HomeTimeline: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
// TODO restore this
|
||||
// const isPartial = useAppSelector((state) => state.timelines.home?.isPartial);
|
||||
|
||||
// // Mastodon generates the feed in Redis, and can return a partial timeline
|
||||
// // (HTTP 206) for new users. Poll until we get a full page of results.
|
||||
// const checkIfReloadNeeded = useCallback((isPartial: boolean) => {
|
||||
// if (isPartial) {
|
||||
// polling.current = setInterval(() => {
|
||||
// dispatch(fetchHomeTimeline());
|
||||
// }, 3000);
|
||||
// } else if (polling.current) {
|
||||
// clearInterval(polling.current);
|
||||
// polling.current = null;
|
||||
// }
|
||||
|
||||
// return () => {
|
||||
// if (polling.current) {
|
||||
// clearInterval(polling.current);
|
||||
// polling.current = null;
|
||||
// }
|
||||
// };
|
||||
// }, []);
|
||||
|
||||
const HomeTimelinePage: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const features = useFeatures();
|
||||
const instance = useInstance();
|
||||
|
||||
const polling = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const isPartial = useAppSelector((state) => state.timelines.home?.isPartial);
|
||||
|
||||
// Mastodon generates the feed in Redis, and can return a partial timeline
|
||||
// (HTTP 206) for new users. Poll until we get a full page of results.
|
||||
const checkIfReloadNeeded = useCallback((isPartial: boolean) => {
|
||||
if (isPartial) {
|
||||
polling.current = setInterval(() => {
|
||||
dispatch(fetchHomeTimeline());
|
||||
}, 3000);
|
||||
} else if (polling.current) {
|
||||
clearInterval(polling.current);
|
||||
polling.current = null;
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (polling.current) {
|
||||
clearInterval(polling.current);
|
||||
polling.current = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleLoadMore = useCallback(() => dispatch(fetchHomeTimeline(true)), []);
|
||||
|
||||
const handleRefresh = useCallback(() => dispatch(fetchHomeTimeline(false)), []);
|
||||
|
||||
useEffect(() => checkIfReloadNeeded(isPartial), [isPartial]);
|
||||
|
||||
return (
|
||||
<PullToRefresh onRefresh={handleRefresh}>
|
||||
<Timeline
|
||||
loadMoreClassName='sm:pb-4 black:sm:pb-0 black:sm:mx-4'
|
||||
scrollKey='home_timeline'
|
||||
onLoadMore={handleLoadMore}
|
||||
timelineId='home'
|
||||
<Column className='py-0' label={intl.formatMessage(messages.title)} withHeader={false}>
|
||||
<HomeTimelineColumn
|
||||
emptyMessageText={
|
||||
<Stack space={1}>
|
||||
<Text size='xl' weight='medium' align='center'>
|
||||
@ -100,17 +83,6 @@ const HomeTimeline: React.FC = () => {
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
);
|
||||
};
|
||||
|
||||
const HomeTimelinePage: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const { experimentalTimeline } = useSettings();
|
||||
|
||||
return (
|
||||
<Column className='py-0' label={intl.formatMessage(messages.title)} withHeader={false}>
|
||||
{experimentalTimeline ? <HomeTimelineColumn /> : <HomeTimeline />}
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@ -12,11 +12,8 @@ import Stack from '@/components/ui/stack';
|
||||
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<React.HTMLAttributes<HTMLHeadingElement>, 'className' | 'dir'> {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
@ -55,9 +52,9 @@ const SiteBanner: React.FC = () => {
|
||||
const LandingTimelinePage = () => {
|
||||
const instance = useInstance();
|
||||
const { isOpen } = useRegistrationStatus();
|
||||
const { experimentalTimeline } = useSettings();
|
||||
|
||||
const [timelineFailed, setTimelineFailed] = useState(false);
|
||||
// todo fix this
|
||||
const [timelineFailed, _setTimelineFailed] = useState(false);
|
||||
|
||||
const timelineEnabled = !instance.pleroma.metadata.restrict_unauthenticated.timelines.local;
|
||||
|
||||
@ -79,11 +76,16 @@ const LandingTimelinePage = () => {
|
||||
</HStack>
|
||||
|
||||
{timelineEnabled && !timelineFailed ? (
|
||||
experimentalTimeline ? (
|
||||
<PublicTimelineColumn local />
|
||||
) : (
|
||||
<CommunityTimeline onTimelineFailed={() => setTimelineFailed(true)} />
|
||||
)
|
||||
<PublicTimelineColumn
|
||||
local
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.community'
|
||||
defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!'
|
||||
/>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
) : (
|
||||
<About slug='index' />
|
||||
)}
|
||||
|
||||
@ -1,67 +1,34 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React 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<ILinkTimeline> = ({ url }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleLoadMore = () => {
|
||||
dispatch(fetchLinkTimeline(url, true));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(clearTimeline(`link:${url}`));
|
||||
dispatch(fetchLinkTimeline(url));
|
||||
}, [url]);
|
||||
|
||||
return (
|
||||
<Timeline
|
||||
loadMoreClassName='sm:pb-4 black:sm:pb-0 black:sm:mx-4'
|
||||
scrollKey='link_timeline'
|
||||
timelineId={`link:${url}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.link_timeline'
|
||||
defaultMessage='There are no posts with this link yet.'
|
||||
/>
|
||||
}
|
||||
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 { experimentalTimeline } = useSettings();
|
||||
|
||||
return (
|
||||
<Column
|
||||
label={intl.formatMessage(messages.header, { url: decodedUrl.replace(/^https?:\/\//, '') })}
|
||||
>
|
||||
{experimentalTimeline ? (
|
||||
<LinkTimelineColumn url={decodedUrl} />
|
||||
) : (
|
||||
<LinkTimeline url={decodedUrl} />
|
||||
)}
|
||||
<LinkTimelineColumn
|
||||
url={decodedUrl}
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.link_timeline'
|
||||
defaultMessage='There are no posts with this link yet.'
|
||||
/>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,21 +1,16 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
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';
|
||||
import Column from '@/components/ui/column';
|
||||
import Spinner from '@/components/ui/spinner';
|
||||
import Timeline from '@/features/ui/components/timeline';
|
||||
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' },
|
||||
@ -28,60 +23,10 @@ const messages = defineMessages({
|
||||
deleteList: { id: 'lists.delete', defaultMessage: 'Delete list' },
|
||||
});
|
||||
|
||||
interface IListTimeline {
|
||||
listId: string;
|
||||
}
|
||||
|
||||
const ListTimeline: React.FC<IListTimeline> = ({ 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 (
|
||||
<Timeline
|
||||
loadMoreClassName='sm:pb-4 black:sm:pb-0 black:sm:mx-4'
|
||||
scrollKey={`list_${listId}_timeline`}
|
||||
timelineId={`list:${listId}${onlyMedia ? ':media' : ''}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessageText={
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='empty_column.list'
|
||||
defaultMessage='There is nothing in this list yet. When members of this list create new posts, they will appear here.'
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Button onClick={handleEditClick}>
|
||||
<FormattedMessage id='list.click_to_add' defaultMessage='Click here to add people' />
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/list-bullets.svg')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ListTimelinePage: React.FC = () => {
|
||||
const { listId } = listTimelineRoute.useParams();
|
||||
|
||||
const intl = useIntl();
|
||||
const { experimentalTimeline } = useSettings();
|
||||
const { openModal } = useModalsActions();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -146,11 +91,23 @@ const ListTimelinePage: React.FC = () => {
|
||||
/>
|
||||
}
|
||||
>
|
||||
{experimentalTimeline ? (
|
||||
<ListTimelineColumn listId={listId} />
|
||||
) : (
|
||||
<ListTimeline listId={listId} />
|
||||
)}
|
||||
<ListTimelineColumn
|
||||
listId={listId}
|
||||
emptyMessageText={
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='empty_column.list'
|
||||
defaultMessage='There is nothing in this list yet. When members of this list create new posts, they will appear here.'
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Button onClick={handleEditClick}>
|
||||
<FormattedMessage id='list.click_to_add' defaultMessage='Click here to add people' />
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/list-bullets.svg')}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,16 +1,12 @@
|
||||
import { Link } from '@tanstack/react-router';
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
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';
|
||||
import PinnedHostsPicker from '@/features/remote-timeline/components/pinned-hosts-picker';
|
||||
import Timeline from '@/features/ui/components/timeline';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useInstance } from '@/hooks/use-instance';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
@ -20,52 +16,12 @@ const messages = defineMessages({
|
||||
dismiss: { id: 'fediverse_tab.explanation_box.dismiss', defaultMessage: "Don't show again" },
|
||||
});
|
||||
|
||||
const PublicTimeline = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const settings = useSettings();
|
||||
const onlyMedia = settings.timelines.public?.other.onlyMedia ?? false;
|
||||
|
||||
const timelineId = 'public';
|
||||
|
||||
const handleLoadMore = () => {
|
||||
dispatch(fetchPublicTimeline({ onlyMedia }, true));
|
||||
};
|
||||
|
||||
const handleRefresh = () => dispatch(fetchPublicTimeline({ onlyMedia }));
|
||||
|
||||
usePublicStream({ onlyMedia });
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchPublicTimeline({ onlyMedia }, true));
|
||||
}, [onlyMedia]);
|
||||
|
||||
return (
|
||||
<PullToRefresh onRefresh={handleRefresh}>
|
||||
<Timeline
|
||||
loadMoreClassName='sm:pb-4 black:sm:pb-0 black:sm:mx-4'
|
||||
scrollKey={`${timelineId}_timeline`}
|
||||
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
|
||||
prefix='home'
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.public'
|
||||
defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up'
|
||||
/>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
||||
@ -115,7 +71,15 @@ const PublicTimelinePage = () => {
|
||||
/>
|
||||
</Accordion>
|
||||
)}
|
||||
{experimentalTimeline ? <PublicTimelineColumn /> : <PublicTimeline />}
|
||||
<PublicTimelineColumn
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.public'
|
||||
defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up'
|
||||
/>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,63 +1,20 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
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';
|
||||
import Text from '@/components/ui/text';
|
||||
import PinnedHostsPicker from '@/features/remote-timeline/components/pinned-hosts-picker';
|
||||
import Timeline from '@/features/ui/components/timeline';
|
||||
import { remoteTimelineRoute } from '@/features/ui/router';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
|
||||
const messages = defineMessages({
|
||||
close: { id: 'remote_timeline.close', defaultMessage: 'Close remote timeline' },
|
||||
});
|
||||
|
||||
interface IRemoteTimeline {
|
||||
instance: string;
|
||||
}
|
||||
|
||||
const RemoteTimeline: React.FC<IRemoteTimeline> = ({ instance }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const settings = useSettings();
|
||||
|
||||
const timelineId = 'remote';
|
||||
const onlyMedia = settings.timelines.remote?.other.onlyMedia ?? false;
|
||||
|
||||
const handleLoadMore = () => {
|
||||
dispatch(fetchPublicTimeline({ onlyMedia, instance }, true));
|
||||
};
|
||||
|
||||
useRemoteStream({ instance, onlyMedia });
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchPublicTimeline({ onlyMedia, instance }));
|
||||
}, [onlyMedia, instance]);
|
||||
|
||||
return (
|
||||
<Timeline
|
||||
loadMoreClassName='sm:pb-4 black:sm:pb-0 black:sm:mx-4'
|
||||
scrollKey={`${timelineId}_${instance}_timeline`}
|
||||
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}:${instance}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.remote'
|
||||
defaultMessage='There is nothing here! Manually follow users from {instance} to fill it up.'
|
||||
values={{ instance }}
|
||||
/>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
/** View statuses from a remote instance. */
|
||||
const RemoteTimelinePage: React.FC = () => {
|
||||
const { instance } = remoteTimelineRoute.useParams();
|
||||
@ -66,7 +23,6 @@ const RemoteTimelinePage: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const settings = useSettings();
|
||||
const { experimentalTimeline } = settings;
|
||||
|
||||
const pinned = settings.remote_timeline.pinnedHosts.includes(instance);
|
||||
|
||||
@ -97,11 +53,17 @@ const RemoteTimelinePage: React.FC = () => {
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
{experimentalTimeline ? (
|
||||
<PublicTimelineColumn instance={instance} />
|
||||
) : (
|
||||
<RemoteTimeline instance={instance} />
|
||||
)}
|
||||
<PublicTimelineColumn
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.remote'
|
||||
defaultMessage='There is nothing here! Manually follow users from {instance} to fill it up.'
|
||||
values={{ instance }}
|
||||
/>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
instance={instance}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,44 +1,19 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React 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';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.wrenched', defaultMessage: 'Recent wrenches timeline' },
|
||||
});
|
||||
|
||||
const WrenchedTimeline = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const settings = useSettings();
|
||||
|
||||
const onlyMedia = settings.timelines.wrenched?.other.onlyMedia ?? false;
|
||||
|
||||
const timelineId = 'wrenched';
|
||||
|
||||
const handleLoadMore = () => {
|
||||
dispatch(fetchWrenchedTimeline({ onlyMedia }, true));
|
||||
};
|
||||
|
||||
const handleRefresh = () => dispatch(fetchWrenchedTimeline({ onlyMedia }, true));
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchWrenchedTimeline({ onlyMedia }));
|
||||
}, [onlyMedia]);
|
||||
const WrenchedTimelinePage = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<PullToRefresh onRefresh={handleRefresh}>
|
||||
<Timeline
|
||||
loadMoreClassName='sm:pb-4 black:sm:pb-0 black:sm:mx-4'
|
||||
scrollKey={`${timelineId}_timeline`}
|
||||
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
|
||||
prefix='home'
|
||||
onLoadMore={handleLoadMore}
|
||||
<Column className='-mt-3 sm:mt-0' label={intl.formatMessage(messages.title)}>
|
||||
<WrenchedTimelineColumn
|
||||
emptyMessageText={
|
||||
<FormattedMessage
|
||||
id='empty_column.wrenched'
|
||||
@ -47,19 +22,6 @@ const WrenchedTimeline = () => {
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/wrench.svg')}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
);
|
||||
};
|
||||
|
||||
const WrenchedTimelinePage = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const settings = useSettings();
|
||||
const { experimentalTimeline } = settings;
|
||||
|
||||
return (
|
||||
<Column className='-mt-3 sm:mt-0' label={intl.formatMessage(messages.title)}>
|
||||
{experimentalTimeline ? <WrenchedTimelineColumn /> : <WrenchedTimeline />}
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@ -122,8 +122,6 @@ const settingsSchema = v.object({
|
||||
saved: v.fallback(v.boolean(), true),
|
||||
|
||||
demo: v.fallback(v.boolean(), false),
|
||||
|
||||
experimentalTimeline: v.fallback(v.boolean(), false),
|
||||
});
|
||||
|
||||
type Settings = v.InferOutput<typeof settingsSchema>;
|
||||
|
||||
Reference in New Issue
Block a user