pl-fe: move more files around
Signed-off-by: Nicole Mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -4,10 +4,10 @@ import { useIntl, defineMessages, IntlShape } from 'react-intl';
|
||||
import { changeComposeFederated, changeComposeVisibility } from 'pl-fe/actions/compose';
|
||||
import DropdownMenu, { MenuItem } from 'pl-fe/components/dropdown-menu';
|
||||
import Button from 'pl-fe/components/ui/button';
|
||||
import { getOrderedLists } from 'pl-fe/features/lists';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useCompose } from 'pl-fe/hooks/use-compose';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { getOrderedLists } from 'pl-fe/pages/account-lists/lists';
|
||||
import { useCircles } from 'pl-fe/queries/accounts/use-circles';
|
||||
import { useLists } from 'pl-fe/queries/accounts/use-lists';
|
||||
|
||||
|
||||
@ -1,108 +0,0 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
import { useSearchParams } from 'react-router-dom-v5-compat';
|
||||
|
||||
import LoadMore from 'pl-fe/components/load-more';
|
||||
import { RadioGroup, RadioItem } from 'pl-fe/components/radio';
|
||||
import { CardTitle } from 'pl-fe/components/ui/card';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useInstance } from 'pl-fe/hooks/use-instance';
|
||||
import { useDirectory } from 'pl-fe/queries/accounts/use-directory';
|
||||
|
||||
import AccountCard from './components/account-card';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.directory', defaultMessage: 'Browse profiles' },
|
||||
recentlyActive: { id: 'directory.recently_active', defaultMessage: 'Recently active' },
|
||||
newArrivals: { id: 'directory.new_arrivals', defaultMessage: 'New arrivals' },
|
||||
local: { id: 'directory.local', defaultMessage: 'From {domain} only' },
|
||||
federated: { id: 'directory.federated', defaultMessage: 'From known fediverse' },
|
||||
});
|
||||
|
||||
const Directory = () => {
|
||||
const intl = useIntl();
|
||||
const [params, setParams] = useSearchParams();
|
||||
const instance = useInstance();
|
||||
const features = useFeatures();
|
||||
|
||||
const order = (params.get('order') || 'active') as 'active' | 'new';
|
||||
const local = !!params.get('local');
|
||||
|
||||
const { data: accountIds = [], isLoading, hasNextPage, fetchNextPage } = useDirectory(order, local);
|
||||
|
||||
const handleChangeOrder: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
setParams({ local: local ? 'true' : '', order: e.target.value });
|
||||
};
|
||||
|
||||
const handleChangeLocal: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
setParams({ local: e.target.value === '1' ? 'true' : '', order });
|
||||
};
|
||||
|
||||
const handleLoadMore = () => {
|
||||
fetchNextPage({ cancelRefetch: false });
|
||||
};
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.title)}>
|
||||
<Stack space={4}>
|
||||
<div className='grid grid-cols-1 gap-2 md:grid-cols-2'>
|
||||
<Stack space={2}>
|
||||
<CardTitle title={<FormattedMessage id='directory.display_filter' defaultMessage='Display filter' />} />
|
||||
|
||||
<RadioGroup onChange={handleChangeOrder}>
|
||||
<RadioItem
|
||||
label={intl.formatMessage(messages.recentlyActive)}
|
||||
checked={order === 'active'}
|
||||
value='active'
|
||||
/>
|
||||
<RadioItem
|
||||
label={intl.formatMessage(messages.newArrivals)}
|
||||
checked={order === 'new'}
|
||||
value='new'
|
||||
/>
|
||||
</RadioGroup>
|
||||
</Stack>
|
||||
|
||||
{features.federating && (
|
||||
<Stack space={2}>
|
||||
<CardTitle title={<FormattedMessage id='directory.fediverse_filter' defaultMessage='Fediverse filter' />} />
|
||||
|
||||
<RadioGroup onChange={handleChangeLocal}>
|
||||
<RadioItem
|
||||
label={intl.formatMessage(messages.local, { domain: instance.title })}
|
||||
checked={local}
|
||||
value='1'
|
||||
/>
|
||||
<RadioItem
|
||||
label={intl.formatMessage(messages.federated)}
|
||||
checked={!local}
|
||||
value='0'
|
||||
/>
|
||||
</RadioGroup>
|
||||
</Stack>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={
|
||||
clsx({
|
||||
'grid grid-cols-1 sm:grid-cols-2 gap-2.5': true,
|
||||
'opacity-30': isLoading,
|
||||
})
|
||||
}
|
||||
>
|
||||
{accountIds.map((accountId) => (
|
||||
<AccountCard id={accountId} key={accountId} />),
|
||||
)}
|
||||
</div>
|
||||
|
||||
{hasNextPage && <LoadMore onClick={handleLoadMore} disabled={isLoading} />}
|
||||
</Stack>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
export { Directory as default };
|
||||
@ -1,39 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
|
||||
import Account from 'pl-fe/components/account';
|
||||
import { AuthorizeRejectButtons } from 'pl-fe/components/authorize-reject-buttons';
|
||||
import { useAcceptFollowRequestMutation, useRejectFollowRequestMutation } from 'pl-fe/queries/accounts/use-follow-requests';
|
||||
|
||||
interface IAccountAuthorize {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const AccountAuthorize: React.FC<IAccountAuthorize> = ({ id }) => {
|
||||
const { account } = useAccount(id);
|
||||
|
||||
const { mutate: authorizeFollowRequest } = useAcceptFollowRequestMutation(id);
|
||||
const { mutate: rejectFollowRequest } = useRejectFollowRequestMutation(id);
|
||||
|
||||
const onAuthorize = () => authorizeFollowRequest();
|
||||
const onReject = () => rejectFollowRequest();
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
return (
|
||||
<div className='p-2.5'>
|
||||
<Account
|
||||
account={account}
|
||||
action={
|
||||
<AuthorizeRejectButtons
|
||||
onAuthorize={onAuthorize}
|
||||
onReject={onReject}
|
||||
countdown={3000}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { AccountAuthorize as default };
|
||||
@ -1,35 +0,0 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useRouteMatch } from 'react-router-dom';
|
||||
|
||||
import Tabs from 'pl-fe/components/ui/tabs';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
|
||||
const messages = defineMessages({
|
||||
followRequests: { id: 'column.follow_requests', defaultMessage: 'Follow requests' },
|
||||
outgoingFollowRequests: { id: 'column.outgoing_follow_requests', defaultMessage: 'Outgoing follow requests' },
|
||||
});
|
||||
|
||||
const FollowRequestsTabs = () => {
|
||||
const intl = useIntl();
|
||||
const match = useRouteMatch();
|
||||
const features = useFeatures();
|
||||
|
||||
if (!features.outgoingFollowRequests) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tabs = [{
|
||||
name: '/follow_requests',
|
||||
text: intl.formatMessage(messages.followRequests),
|
||||
to: '/follow_requests',
|
||||
}, {
|
||||
name: '/outgoing_follow_requests',
|
||||
text: intl.formatMessage(messages.outgoingFollowRequests),
|
||||
to: '/outgoing_follow_requests',
|
||||
}];
|
||||
|
||||
return <Tabs items={tabs} activeItem={match.path} />;
|
||||
};
|
||||
|
||||
export { FollowRequestsTabs as default };
|
||||
@ -1,44 +0,0 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import { useFollowRequests } from 'pl-fe/queries/accounts/use-follow-requests';
|
||||
|
||||
import AccountAuthorize from './components/account-authorize';
|
||||
import FollowRequestsTabs from './components/follow-requests-tabs';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.follow_requests', defaultMessage: 'Follow requests' },
|
||||
});
|
||||
|
||||
const FollowRequests: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { data: accountIds, isLoading, hasNextPage, fetchNextPage } = useFollowRequests();
|
||||
|
||||
const body = accountIds ? (
|
||||
<ScrollableList
|
||||
scrollKey='followRequests'
|
||||
hasMore={hasNextPage}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
emptyMessage={<FormattedMessage id='empty_column.follow_requests' defaultMessage="You don't have any follow requests yet. When you receive one, it will show up here." />}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountAuthorize key={id} id={id} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
) : <Spinner />;
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.heading)}>
|
||||
<FollowRequestsTabs />
|
||||
|
||||
{body}
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
export { FollowRequests as default };
|
||||
@ -1,66 +0,0 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { changeListEditorTitle } from 'pl-fe/actions/lists';
|
||||
import Button from 'pl-fe/components/ui/button';
|
||||
import Form from 'pl-fe/components/ui/form';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
import Input from 'pl-fe/components/ui/input';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useCreateList } from 'pl-fe/queries/accounts/use-lists';
|
||||
|
||||
const messages = defineMessages({
|
||||
label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' },
|
||||
title: { id: 'lists.new.create', defaultMessage: 'Add list' },
|
||||
create: { id: 'lists.new.create_title', defaultMessage: 'Add list' },
|
||||
});
|
||||
|
||||
const NewListForm: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const { title: value, isSubmitting: disabled } = useAppSelector((state) => state.listEditor);
|
||||
|
||||
const { mutate: createList } = useCreateList();
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(changeListEditorTitle(e.target.value));
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<Element>) => {
|
||||
e.preventDefault();
|
||||
createList({ title: value });
|
||||
};
|
||||
|
||||
const label = intl.formatMessage(messages.label);
|
||||
const create = intl.formatMessage(messages.create);
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<HStack space={2} alignItems='center'>
|
||||
<label className='grow'>
|
||||
<span style={{ display: 'none' }}>{label}</span>
|
||||
|
||||
<Input
|
||||
type='text'
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
placeholder={label}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<Button
|
||||
disabled={disabled}
|
||||
onClick={handleSubmit}
|
||||
theme='primary'
|
||||
>
|
||||
{create}
|
||||
</Button>
|
||||
</HStack>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export { NewListForm as default };
|
||||
@ -77,23 +77,23 @@ export const AccountGallery = lazy(() => import('pl-fe/features/account-gallery'
|
||||
export const AccountTimeline = lazy(() => import('pl-fe/features/account-timeline'));
|
||||
export const BookmarkFolders = lazy(() => import('pl-fe/features/bookmark-folders'));
|
||||
export const Circle = lazy(() => import('pl-fe/features/circle'));
|
||||
export const Circles = lazy(() => import('pl-fe/features/circles'));
|
||||
export const Circles = lazy(() => import('pl-fe/pages/account-lists/circles'));
|
||||
export const ComposeEditor = lazy(() => import('pl-fe/features/compose/editor'));
|
||||
export const ComposeEvent = lazy(() => import('pl-fe/features/compose-event'));
|
||||
export const Directory = lazy(() => import('pl-fe/features/directory'));
|
||||
export const Directory = lazy(() => import('pl-fe/pages/account-lists/directory'));
|
||||
export const DraftStatuses = lazy(() => import('pl-fe/features/draft-statuses'));
|
||||
export const EventDiscussion = lazy(() => import('pl-fe/features/event/event-discussion'));
|
||||
export const EventInformation = lazy(() => import('pl-fe/features/event/event-information'));
|
||||
export const Events = lazy(() => import('pl-fe/features/events'));
|
||||
export const FollowedTags = lazy(() => import('pl-fe/features/followed-tags'));
|
||||
export const Followers = lazy(() => import('pl-fe/features/followers'));
|
||||
export const Following = lazy(() => import('pl-fe/features/following'));
|
||||
export const FollowRecommendations = lazy(() => import('pl-fe/features/follow-recommendations'));
|
||||
export const FollowRequests = lazy(() => import('pl-fe/features/follow-requests'));
|
||||
export const Followers = lazy(() => import('pl-fe/pages/account-lists/followers'));
|
||||
export const Following = lazy(() => import('pl-fe/pages/account-lists/following'));
|
||||
export const FollowRecommendations = lazy(() => import('pl-fe/pages/account-lists/follow-recommendations'));
|
||||
export const FollowRequests = lazy(() => import('pl-fe/pages/account-lists/follow-requests'));
|
||||
export const InteractionRequests = lazy(() => import('pl-fe/features/interaction-requests'));
|
||||
export const Lists = lazy(() => import('pl-fe/features/lists'));
|
||||
export const Lists = lazy(() => import('pl-fe/pages/account-lists/lists'));
|
||||
export const Notifications = lazy(() => import('pl-fe/features/notifications'));
|
||||
export const OutgoingFollowRequests = lazy(() => import('pl-fe/features/follow-requests/components/outgoing-follow-requests'));
|
||||
export const OutgoingFollowRequests = lazy(() => import('pl-fe/pages/account-lists/outgoing-follow-requests'));
|
||||
export const ScheduledStatuses = lazy(() => import('pl-fe/features/scheduled-statuses'));
|
||||
export const Search = lazy(() => import('pl-fe/features/search'));
|
||||
export const Status = lazy(() => import('pl-fe/features/status'));
|
||||
|
||||
@ -5,9 +5,8 @@ import { setupListAdder, resetListAdder } from 'pl-fe/actions/lists';
|
||||
import { CardHeader, CardTitle } from 'pl-fe/components/ui/card';
|
||||
import Modal from 'pl-fe/components/ui/modal';
|
||||
import AccountContainer from 'pl-fe/containers/account-container';
|
||||
import { getOrderedLists } from 'pl-fe/features/lists';
|
||||
import NewListForm from 'pl-fe/features/lists/components/new-list-form';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { NewListForm, getOrderedLists } from 'pl-fe/pages/account-lists/lists';
|
||||
import { useLists } from 'pl-fe/queries/accounts/use-lists';
|
||||
|
||||
import List from './components/list';
|
||||
|
||||
@ -11,10 +11,9 @@ import Icon from 'pl-fe/components/ui/icon';
|
||||
import Input from 'pl-fe/components/ui/input';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import { getOrderedLists } from 'pl-fe/pages/account-lists/lists';
|
||||
import { useCircles, useCreateCircle } from 'pl-fe/queries/accounts/use-circles';
|
||||
|
||||
import { getOrderedLists } from '../lists';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.circles', defaultMessage: 'Circles' },
|
||||
subheading: { id: 'circles.subheading', defaultMessage: 'Your circles' },
|
||||
@ -69,7 +68,7 @@ const NewCircleForm: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const Circles: React.FC = () => {
|
||||
const CirclesPage: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { data: circles } = useCircles(getOrderedLists);
|
||||
@ -114,4 +113,4 @@ const Circles: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export { Circles as default };
|
||||
export { CirclesPage as default };
|
||||
@ -1,21 +1,38 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useSearchParams } from 'react-router-dom-v5-compat';
|
||||
|
||||
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
|
||||
import Account from 'pl-fe/components/account';
|
||||
import Badge from 'pl-fe/components/badge';
|
||||
import HoverAccountWrapper from 'pl-fe/components/hover-account-wrapper';
|
||||
import LoadMore from 'pl-fe/components/load-more';
|
||||
import { ParsedContent } from 'pl-fe/components/parsed-content';
|
||||
import { RadioGroup, RadioItem } from 'pl-fe/components/radio';
|
||||
import RelativeTimestamp from 'pl-fe/components/relative-timestamp';
|
||||
import Avatar from 'pl-fe/components/ui/avatar';
|
||||
import { CardTitle } from 'pl-fe/components/ui/card';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import Text from 'pl-fe/components/ui/text';
|
||||
import ActionButton from 'pl-fe/features/ui/components/action-button';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useInstance } from 'pl-fe/hooks/use-instance';
|
||||
import { useDirectory } from 'pl-fe/queries/accounts/use-directory';
|
||||
import { useSettingsStore } from 'pl-fe/stores/settings';
|
||||
import { shortNumberFormat } from 'pl-fe/utils/numbers';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.directory', defaultMessage: 'Browse profiles' },
|
||||
recentlyActive: { id: 'directory.recently_active', defaultMessage: 'Recently active' },
|
||||
newArrivals: { id: 'directory.new_arrivals', defaultMessage: 'New arrivals' },
|
||||
local: { id: 'directory.local', defaultMessage: 'From {domain} only' },
|
||||
federated: { id: 'directory.federated', defaultMessage: 'From known fediverse' },
|
||||
});
|
||||
|
||||
interface IAccountCard {
|
||||
id: string;
|
||||
}
|
||||
@ -121,4 +138,87 @@ const AccountCard: React.FC<IAccountCard> = ({ id }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export { AccountCard as default };
|
||||
const DirectoryPage = () => {
|
||||
const intl = useIntl();
|
||||
const [params, setParams] = useSearchParams();
|
||||
const instance = useInstance();
|
||||
const features = useFeatures();
|
||||
|
||||
const order = (params.get('order') || 'active') as 'active' | 'new';
|
||||
const local = !!params.get('local');
|
||||
|
||||
const { data: accountIds = [], isLoading, hasNextPage, fetchNextPage } = useDirectory(order, local);
|
||||
|
||||
const handleChangeOrder: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
setParams({ local: local ? 'true' : '', order: e.target.value });
|
||||
};
|
||||
|
||||
const handleChangeLocal: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
setParams({ local: e.target.value === '1' ? 'true' : '', order });
|
||||
};
|
||||
|
||||
const handleLoadMore = () => {
|
||||
fetchNextPage({ cancelRefetch: false });
|
||||
};
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.title)}>
|
||||
<Stack space={4}>
|
||||
<div className='grid grid-cols-1 gap-2 md:grid-cols-2'>
|
||||
<Stack space={2}>
|
||||
<CardTitle title={<FormattedMessage id='directory.display_filter' defaultMessage='Display filter' />} />
|
||||
|
||||
<RadioGroup onChange={handleChangeOrder}>
|
||||
<RadioItem
|
||||
label={intl.formatMessage(messages.recentlyActive)}
|
||||
checked={order === 'active'}
|
||||
value='active'
|
||||
/>
|
||||
<RadioItem
|
||||
label={intl.formatMessage(messages.newArrivals)}
|
||||
checked={order === 'new'}
|
||||
value='new'
|
||||
/>
|
||||
</RadioGroup>
|
||||
</Stack>
|
||||
|
||||
{features.federating && (
|
||||
<Stack space={2}>
|
||||
<CardTitle title={<FormattedMessage id='directory.fediverse_filter' defaultMessage='Fediverse filter' />} />
|
||||
|
||||
<RadioGroup onChange={handleChangeLocal}>
|
||||
<RadioItem
|
||||
label={intl.formatMessage(messages.local, { domain: instance.title })}
|
||||
checked={local}
|
||||
value='1'
|
||||
/>
|
||||
<RadioItem
|
||||
label={intl.formatMessage(messages.federated)}
|
||||
checked={!local}
|
||||
value='0'
|
||||
/>
|
||||
</RadioGroup>
|
||||
</Stack>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={
|
||||
clsx({
|
||||
'grid grid-cols-1 sm:grid-cols-2 gap-2.5': true,
|
||||
'opacity-30': isLoading,
|
||||
})
|
||||
}
|
||||
>
|
||||
{accountIds.map((accountId) => (
|
||||
<AccountCard id={accountId} key={accountId} />),
|
||||
)}
|
||||
</div>
|
||||
|
||||
{hasNextPage && <LoadMore onClick={handleLoadMore} disabled={isLoading} />}
|
||||
</Stack>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
export { DirectoryPage as default };
|
||||
@ -12,7 +12,7 @@ const messages = defineMessages({
|
||||
heading: { id: 'follow_recommendations.heading', defaultMessage: 'Suggested profiles' },
|
||||
});
|
||||
|
||||
const FollowRecommendations: React.FC = () => {
|
||||
const FollowRecommendationsPage: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { data: suggestions, isFetching } = useSuggestions();
|
||||
@ -48,4 +48,4 @@ const FollowRecommendations: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export { FollowRecommendations as default };
|
||||
export { FollowRecommendationsPage as default };
|
||||
102
packages/pl-fe/src/pages/account-lists/follow-requests.tsx
Normal file
102
packages/pl-fe/src/pages/account-lists/follow-requests.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
import { useRouteMatch } from 'react-router-dom';
|
||||
|
||||
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
|
||||
import Account from 'pl-fe/components/account';
|
||||
import { AuthorizeRejectButtons } from 'pl-fe/components/authorize-reject-buttons';
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import Tabs from 'pl-fe/components/ui/tabs';
|
||||
import { useFeatures } from 'pl-fe/hooks/use-features';
|
||||
import { useAcceptFollowRequestMutation, useFollowRequests, useRejectFollowRequestMutation } from 'pl-fe/queries/accounts/use-follow-requests';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.follow_requests', defaultMessage: 'Follow requests' },
|
||||
followRequests: { id: 'column.follow_requests', defaultMessage: 'Follow requests' },
|
||||
outgoingFollowRequests: { id: 'column.outgoing_follow_requests', defaultMessage: 'Outgoing follow requests' },
|
||||
});
|
||||
|
||||
interface IAccountAuthorize {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const AccountAuthorize: React.FC<IAccountAuthorize> = ({ id }) => {
|
||||
const { account } = useAccount(id);
|
||||
|
||||
const { mutate: authorizeFollowRequest } = useAcceptFollowRequestMutation(id);
|
||||
const { mutate: rejectFollowRequest } = useRejectFollowRequestMutation(id);
|
||||
|
||||
const onAuthorize = () => authorizeFollowRequest();
|
||||
const onReject = () => rejectFollowRequest();
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
return (
|
||||
<div className='p-2.5'>
|
||||
<Account
|
||||
account={account}
|
||||
action={
|
||||
<AuthorizeRejectButtons
|
||||
onAuthorize={onAuthorize}
|
||||
onReject={onReject}
|
||||
countdown={3000}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const FollowRequestsTabs = () => {
|
||||
const intl = useIntl();
|
||||
const match = useRouteMatch();
|
||||
const features = useFeatures();
|
||||
|
||||
if (!features.outgoingFollowRequests) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tabs = [{
|
||||
name: '/follow_requests',
|
||||
text: intl.formatMessage(messages.followRequests),
|
||||
to: '/follow_requests',
|
||||
}, {
|
||||
name: '/outgoing_follow_requests',
|
||||
text: intl.formatMessage(messages.outgoingFollowRequests),
|
||||
to: '/outgoing_follow_requests',
|
||||
}];
|
||||
|
||||
return <Tabs items={tabs} activeItem={match.path} />;
|
||||
};
|
||||
|
||||
const FollowRequestsPage: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { data: accountIds, isLoading, hasNextPage, fetchNextPage } = useFollowRequests();
|
||||
|
||||
const body = accountIds ? (
|
||||
<ScrollableList
|
||||
scrollKey='followRequests'
|
||||
hasMore={hasNextPage}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
emptyMessage={<FormattedMessage id='empty_column.follow_requests' defaultMessage="You don't have any follow requests yet. When you receive one, it will show up here." />}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountAuthorize key={id} id={id} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
) : <Spinner />;
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.heading)}>
|
||||
<FollowRequestsTabs />
|
||||
|
||||
{body}
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
export { FollowRequestsPage as default, FollowRequestsTabs };
|
||||
@ -13,14 +13,14 @@ const messages = defineMessages({
|
||||
heading: { id: 'column.followers', defaultMessage: 'Followers' },
|
||||
});
|
||||
|
||||
interface IFollowers {
|
||||
interface IFollowersPage {
|
||||
params?: {
|
||||
username?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/** Displays a list of accounts who follow the given account. */
|
||||
const Followers: React.FC<IFollowers> = ({ params }) => {
|
||||
const FollowersPage: React.FC<IFollowersPage> = ({ params }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { account, isUnavailable } = useAccountLookup(params?.username);
|
||||
@ -74,4 +74,4 @@ const Followers: React.FC<IFollowers> = ({ params }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export { Followers as default };
|
||||
export { FollowersPage as default };
|
||||
@ -13,14 +13,14 @@ const messages = defineMessages({
|
||||
heading: { id: 'column.following', defaultMessage: 'Following' },
|
||||
});
|
||||
|
||||
interface IFollowing {
|
||||
interface IFollowingPage {
|
||||
params?: {
|
||||
username?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/** Displays a list of accounts the given user is following. */
|
||||
const Following: React.FC<IFollowing> = ({ params }) => {
|
||||
const FollowingPage: React.FC<IFollowingPage> = ({ params }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { account, isUnavailable } = useAccountLookup(params?.username);
|
||||
@ -74,4 +74,4 @@ const Following: React.FC<IFollowing> = ({ params }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export { Following as default };
|
||||
export { FollowingPage as default };
|
||||
@ -1,22 +1,29 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { changeListEditorTitle } from 'pl-fe/actions/lists';
|
||||
import List, { ListItem } from 'pl-fe/components/list';
|
||||
import Button from 'pl-fe/components/ui/button';
|
||||
import Card from 'pl-fe/components/ui/card';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
import Form from 'pl-fe/components/ui/form';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
import Icon from 'pl-fe/components/ui/icon';
|
||||
import Input from 'pl-fe/components/ui/input';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import { useLists } from 'pl-fe/queries/accounts/use-lists';
|
||||
|
||||
import NewListForm from './components/new-list-form';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { useCreateList, useLists } from 'pl-fe/queries/accounts/use-lists';
|
||||
|
||||
import type { List as ListEntity } from 'pl-api';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.lists', defaultMessage: 'Lists' },
|
||||
subheading: { id: 'lists.subheading', defaultMessage: 'Your lists' },
|
||||
label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' },
|
||||
title: { id: 'lists.new.create', defaultMessage: 'Add list' },
|
||||
create: { id: 'lists.new.create_title', defaultMessage: 'Add list' },
|
||||
});
|
||||
|
||||
const getOrderedLists = (lists: Array<Pick<ListEntity, 'title'>>) => {
|
||||
@ -27,7 +34,54 @@ const getOrderedLists = (lists: Array<Pick<ListEntity, 'title'>>) => {
|
||||
return Object.values(lists).filter((item): item is ListEntity => !!item).sort((a, b) => a.title.localeCompare(b.title));
|
||||
};
|
||||
|
||||
const Lists: React.FC = () => {
|
||||
const NewListForm: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const { title: value, isSubmitting: disabled } = useAppSelector((state) => state.listEditor);
|
||||
|
||||
const { mutate: createList } = useCreateList();
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(changeListEditorTitle(e.target.value));
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<Element>) => {
|
||||
e.preventDefault();
|
||||
createList({ title: value });
|
||||
};
|
||||
|
||||
const label = intl.formatMessage(messages.label);
|
||||
const create = intl.formatMessage(messages.create);
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<HStack space={2} alignItems='center'>
|
||||
<label className='grow'>
|
||||
<span style={{ display: 'none' }}>{label}</span>
|
||||
|
||||
<Input
|
||||
type='text'
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
placeholder={label}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<Button
|
||||
disabled={disabled}
|
||||
onClick={handleSubmit}
|
||||
theme='primary'
|
||||
>
|
||||
{create}
|
||||
</Button>
|
||||
</HStack>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
const ListsPage: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { data: lists } = useLists(getOrderedLists);
|
||||
@ -72,4 +126,4 @@ const Lists: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export { Lists as default, getOrderedLists };
|
||||
export { ListsPage as default, NewListForm, getOrderedLists };
|
||||
@ -7,13 +7,13 @@ import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import AccountContainer from 'pl-fe/containers/account-container';
|
||||
import { useOutgoingFollowRequests } from 'pl-fe/queries/accounts/use-follow-requests';
|
||||
|
||||
import FollowRequestsTabs from './follow-requests-tabs';
|
||||
import { FollowRequestsTabs } from './follow-requests';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.outgoing_follow_requests', defaultMessage: 'Outgoing follow requests' },
|
||||
});
|
||||
|
||||
const OutgoingFollowRequests: React.FC = () => {
|
||||
const OutgoingFollowRequestsPage: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const { data: accountIds, isLoading, hasNextPage, fetchNextPage } = useOutgoingFollowRequests();
|
||||
@ -42,4 +42,4 @@ const OutgoingFollowRequests: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export { OutgoingFollowRequests as default };
|
||||
export { OutgoingFollowRequestsPage as default };
|
||||
Reference in New Issue
Block a user