pl-fe: move more files around

Signed-off-by: Nicole Mikołajczyk <git@mkljczk.pl>
This commit is contained in:
Nicole Mikołajczyk
2025-05-15 21:02:42 +02:00
parent 5fc25a2820
commit 573f6a5910
16 changed files with 287 additions and 325 deletions

View File

@ -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';

View File

@ -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 };

View File

@ -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 };

View File

@ -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 };

View File

@ -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 };

View File

@ -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 };

View File

@ -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'));

View File

@ -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';

View File

@ -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 };

View File

@ -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 };

View File

@ -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 };

View 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 };

View File

@ -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 };

View File

@ -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 };

View File

@ -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 };

View File

@ -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 };