pl-fe: improve empty column messages
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@@ -249,7 +249,7 @@ const NotificationsColumn = () => {
|
||||
isLoading={isLoading}
|
||||
showLoading={isLoading && displayedNotifications.length === 0}
|
||||
hasMore={hasMore}
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
placeholderComponent={PlaceholderNotification}
|
||||
placeholderCount={20}
|
||||
onLoadMore={handleLoadOlder}
|
||||
|
||||
24
packages/pl-fe/src/components/empty-message.tsx
Normal file
24
packages/pl-fe/src/components/empty-message.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
|
||||
import Icon from './ui/icon';
|
||||
import Stack from './ui/stack';
|
||||
import Text from './ui/text';
|
||||
|
||||
interface IEmptyMessage {
|
||||
text: React.ReactNode;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
const EmptyMessage: React.FC<IEmptyMessage> = ({ text, icon = require('@phosphor-icons/core/regular/empty.svg') }) => (
|
||||
<Stack space={4} className='⁂-empty-message py-6' justifyContent='center' alignItems='center'>
|
||||
<div className='rounded-full bg-gray-200 p-4 dark:bg-gray-800'>
|
||||
<Icon src={icon} className='size-6 text-gray-600' />
|
||||
</div>
|
||||
|
||||
<Text theme='muted' align='center'>
|
||||
{text}
|
||||
</Text>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
export { EmptyMessage };
|
||||
@@ -4,10 +4,11 @@ import { useHistory } from 'react-router-dom';
|
||||
import { Virtuoso, Components, VirtuosoProps, VirtuosoHandle, ListRange, IndexLocationWithAlign } from 'react-virtuoso';
|
||||
|
||||
import LoadMore from 'pl-fe/components/load-more';
|
||||
import Card from 'pl-fe/components/ui/card';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import { useSettings } from 'pl-fe/hooks/use-settings';
|
||||
|
||||
import { EmptyMessage } from './empty-message';
|
||||
|
||||
/** Custom Viruoso component context. */
|
||||
type Context = {
|
||||
itemClassName?: string;
|
||||
@@ -48,12 +49,12 @@ interface IScrollableList extends VirtuosoProps<any, any> {
|
||||
hasMore?: boolean;
|
||||
/** Additional element to display at the top of the list. */
|
||||
prepend?: React.ReactNode;
|
||||
/** Whether to display the prepended element. */
|
||||
alwaysPrepend?: boolean;
|
||||
/** Message to display when the list is loaded but empty. */
|
||||
emptyMessage?: React.ReactNode;
|
||||
/** Should the empty message be displayed in a Card */
|
||||
emptyMessageCard?: boolean;
|
||||
/** Message to display when the list is loaded but empty. */
|
||||
emptyMessageText?: React.ReactNode;
|
||||
/** Message to display next to the emptyMessage text. */
|
||||
emptyMessageIcon?: string;
|
||||
/** Scrollable content. */
|
||||
children: Iterable<React.ReactNode>;
|
||||
/** Callback when the list is scrolled to the top. */
|
||||
@@ -83,11 +84,11 @@ interface IScrollableList extends VirtuosoProps<any, any> {
|
||||
const ScrollableList = React.forwardRef<VirtuosoHandle, IScrollableList>(({
|
||||
scrollKey,
|
||||
prepend = null,
|
||||
alwaysPrepend,
|
||||
children,
|
||||
isLoading,
|
||||
emptyMessage,
|
||||
emptyMessageCard = true,
|
||||
emptyMessageText,
|
||||
emptyMessageIcon,
|
||||
showLoading,
|
||||
onScroll,
|
||||
onScrollToTop,
|
||||
@@ -153,23 +154,13 @@ const ScrollableList = React.forwardRef<VirtuosoHandle, IScrollableList>(({
|
||||
}, []);
|
||||
|
||||
/* Render an empty state instead of the scrollable list. */
|
||||
const renderEmpty = (): JSX.Element => (
|
||||
<div className='mt-2'>
|
||||
{alwaysPrepend && prepend}
|
||||
|
||||
{isLoading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<>
|
||||
{emptyMessageCard ? (
|
||||
<Card variant='rounded' size='lg'>
|
||||
{emptyMessage}
|
||||
</Card>
|
||||
) : emptyMessage}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
const renderEmpty = (): JSX.Element => {
|
||||
return isLoading ? (
|
||||
<Spinner />
|
||||
) : emptyMessageText ? (
|
||||
<EmptyMessage text={emptyMessageText} icon={emptyMessageIcon} />
|
||||
) : <>{emptyMessage}</>;
|
||||
};
|
||||
|
||||
/** Render a single item. */
|
||||
const renderItem = (_i: number, element: JSX.Element): JSX.Element => {
|
||||
@@ -250,9 +241,9 @@ const ScrollableList = React.forwardRef<VirtuosoHandle, IScrollableList>(({
|
||||
itemClassName,
|
||||
}}
|
||||
components={{
|
||||
Header: () => <>{prepend}</>,
|
||||
Header: prepend ? () => <>{prepend}</> : undefined,
|
||||
ScrollSeekPlaceholder: Placeholder as any,
|
||||
EmptyPlaceholder: () => renderEmpty(),
|
||||
EmptyPlaceholder: renderEmpty,
|
||||
List,
|
||||
Item,
|
||||
Footer: loadMore,
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import { useAdminAccounts } from 'pl-fe/queries/admin/use-accounts';
|
||||
|
||||
import UnapprovedAccount from '../components/unapproved-account';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.admin.awaiting_approval', defaultMessage: 'Awaiting Approval' },
|
||||
emptyMessage: { id: 'admin.awaiting_approval.empty_message', defaultMessage: 'There is nobody waiting for approval. When a new user signs up, you can review them here.' },
|
||||
});
|
||||
|
||||
const AwaitingApproval: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const { data, isPending, isFetching } = useAdminAccounts({
|
||||
origin: 'local',
|
||||
status: 'pending',
|
||||
@@ -29,7 +23,7 @@ const AwaitingApproval: React.FC = () => {
|
||||
scrollKey='awaitingApproval'
|
||||
isLoading={isFetching}
|
||||
showLoading={isPending}
|
||||
emptyMessage={intl.formatMessage(messages.emptyMessage)}
|
||||
emptyMessageText={<FormattedMessage id='admin.awaiting_approval.empty_message' defaultMessage='There is nobody waiting for approval. When a new user signs up, you can review them here.' />}
|
||||
listClassName='divide-y divide-solid divide-gray-200 dark:divide-gray-800'
|
||||
>
|
||||
{accountIds.map(id => (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedList, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { FormattedList, FormattedMessage } from 'react-intl';
|
||||
import { useSearchParams } from 'react-router-dom-v5-compat';
|
||||
|
||||
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
|
||||
@@ -11,14 +11,7 @@ import { useReports } from 'pl-fe/queries/admin/use-reports';
|
||||
|
||||
import Report from '../components/report';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.admin.reports', defaultMessage: 'Reports' },
|
||||
modlog: { id: 'column.admin.reports.menu.moderation_log', defaultMessage: 'Moderation log' },
|
||||
emptyMessage: { id: 'admin.reports.empty_message', defaultMessage: 'There are no open reports. If a user gets reported, they will show up here.' },
|
||||
});
|
||||
|
||||
const Reports: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const [params, setParams] = useSearchParams();
|
||||
|
||||
const resolved = params.get('resolved') as any as boolean || undefined;
|
||||
@@ -70,7 +63,7 @@ const Reports: React.FC = () => {
|
||||
scrollKey='adminReports'
|
||||
isLoading={isPending}
|
||||
showLoading={isPending}
|
||||
emptyMessage={intl.formatMessage(messages.emptyMessage)}
|
||||
emptyMessageText={<FormattedMessage id='admin.reports.empty_message' defaultMessage='There are no open reports. If a user gets reported, they will show up here.' />}
|
||||
hasMore={hasNextPage}
|
||||
onLoadMore={fetchNextPage}
|
||||
itemClassName='pt-4'
|
||||
|
||||
@@ -61,7 +61,7 @@ const ManagePendingParticipants: React.FC<IManagePendingParticipants> = ({ statu
|
||||
<Stack space={3}>
|
||||
<ScrollableList
|
||||
scrollKey={`eventPendingParticipants:${statusId}`}
|
||||
emptyMessage={<FormattedMessage id='empty_column.event_participant_requests' defaultMessage='There are no pending event participation requests.' />}
|
||||
emptyMessageText={<FormattedMessage id='empty_column.event_participant_requests' defaultMessage='There are no pending event participation requests.' />}
|
||||
hasMore={hasNextPage}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
|
||||
@@ -45,7 +45,7 @@ const ConversationsList: React.FC = () => {
|
||||
id='direct-list'
|
||||
isLoading={isLoading}
|
||||
showLoading={isLoading && conversations.length === 0}
|
||||
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
|
||||
emptyMessageText={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
|
||||
>
|
||||
{conversations.map((item: any) => (
|
||||
<Conversation
|
||||
|
||||
@@ -217,7 +217,6 @@
|
||||
"admin.statuses.status_marked_message_sensitive": "Post by @{acct} was marked sensitive",
|
||||
"admin.theme.title": "Theme",
|
||||
"admin.user_index.empty": "No users found.",
|
||||
"admin.user_index.search_input_placeholder": "Who are you looking for?",
|
||||
"admin.users.actions.deactivate_user": "Deactivate @{name}",
|
||||
"admin.users.actions.delete_user": "Delete @{name}",
|
||||
"admin.users.actions.demote_to_moderator_message": "@{acct} was demoted to a moderator",
|
||||
@@ -363,7 +362,6 @@
|
||||
"circles.subheading": "Your circles",
|
||||
"column.admin.account": "Moderate @{acct}",
|
||||
"column.admin.announcements": "Announcements",
|
||||
"column.admin.awaiting_approval": "Awaiting Approval",
|
||||
"column.admin.create_announcement": "Create announcement",
|
||||
"column.admin.create_domain": "Create domain",
|
||||
"column.admin.create_rule": "Create rule",
|
||||
@@ -374,11 +372,9 @@
|
||||
"column.admin.edit_rule": "Edit rule",
|
||||
"column.admin.moderation_log": "Moderation log",
|
||||
"column.admin.relays": "Instance relays",
|
||||
"column.admin.reports": "Reports",
|
||||
"column.admin.reports.filter_message": "You are displaying reports {query}.",
|
||||
"column.admin.reports.filter_message.account": "from @{acct}",
|
||||
"column.admin.reports.filter_message.target_account": "targeting @{acct}",
|
||||
"column.admin.reports.menu.moderation_log": "Moderation log",
|
||||
"column.admin.rules": "Instance rules",
|
||||
"column.admin.users": "Users",
|
||||
"column.aliases": "Account aliases",
|
||||
|
||||
@@ -27,7 +27,7 @@ const BirthdaysModal = ({ onClose }: BaseModalProps) => {
|
||||
|
||||
body = (
|
||||
<ScrollableList
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
listClassName='max-w-full'
|
||||
itemClassName='pb-3'
|
||||
useWindowScroll={false}
|
||||
|
||||
@@ -31,7 +31,7 @@ const DislikesModal: React.FC<BaseModalProps & DislikesModalProps> = ({ onClose,
|
||||
body = (
|
||||
<PullToRefresh onRefresh={refetch}>
|
||||
<ScrollableList
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
listClassName='max-w-full'
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
|
||||
@@ -31,7 +31,7 @@ const EventParticipantsModal: React.FC<BaseModalProps & EventParticipantsModalPr
|
||||
body = (
|
||||
<PullToRefresh onRefresh={refetch}>
|
||||
<ScrollableList
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
listClassName='max-w-full'
|
||||
itemClassName='pb-3'
|
||||
hasMore={hasNextPage}
|
||||
|
||||
@@ -41,7 +41,7 @@ const FamiliarFollowersModal: React.FC<BaseModalProps & FamiliarFollowersModalPr
|
||||
|
||||
body = (
|
||||
<ScrollableList
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
useWindowScroll={false}
|
||||
|
||||
@@ -31,7 +31,7 @@ const FavouritesModal: React.FC<BaseModalProps & FavouritesModalProps> = ({ onCl
|
||||
body = (
|
||||
<PullToRefresh onRefresh={refetch}>
|
||||
<ScrollableList
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
listClassName='max-w-full'
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
|
||||
@@ -85,7 +85,7 @@ const ReactionsModal: React.FC<BaseModalProps & ReactionsModalProps> = ({ onClos
|
||||
{reactions.length > 0 && renderFilterBar()}
|
||||
<PullToRefresh onRefresh={refetch}>
|
||||
<ScrollableList
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
listClassName={clsx('max-w-full', {
|
||||
'!mt-4': reactions.length > 0,
|
||||
})}
|
||||
|
||||
@@ -31,7 +31,7 @@ const ReblogsModal: React.FC<BaseModalProps & ReblogsModalProps> = ({ onClose, s
|
||||
body = (
|
||||
<PullToRefresh onRefresh={refetch}>
|
||||
<ScrollableList
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
listClassName='max-w-full'
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
|
||||
@@ -82,7 +82,7 @@ const FollowRequestsPage: React.FC = () => {
|
||||
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." />}
|
||||
emptyMessageText={<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} />,
|
||||
|
||||
@@ -59,7 +59,7 @@ const FollowersPage: React.FC<IFollowersPage> = ({ params }) => {
|
||||
scrollKey='followers'
|
||||
hasMore={hasNextPage}
|
||||
onLoadMore={fetchNextPage}
|
||||
emptyMessage={<FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />}
|
||||
emptyMessageText={<FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />}
|
||||
itemClassName='pb-4'
|
||||
isLoading={isFetching}
|
||||
>
|
||||
|
||||
@@ -59,7 +59,7 @@ const FollowingPage: React.FC<IFollowingPage> = ({ params }) => {
|
||||
scrollKey='following'
|
||||
hasMore={hasNextPage}
|
||||
onLoadMore={fetchNextPage}
|
||||
emptyMessage={<FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />}
|
||||
emptyMessageText={<FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />}
|
||||
itemClassName='pb-4'
|
||||
isLoading={isFetching}
|
||||
>
|
||||
|
||||
@@ -24,7 +24,7 @@ const OutgoingFollowRequestsPage: React.FC = () => {
|
||||
hasMore={hasNextPage}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
emptyMessage={<FormattedMessage id='empty_column.outgoing_follow_requests' defaultMessage="You don't have any outgoing follow requests yet. When you try to follow a user, it will show up here." />}
|
||||
emptyMessageText={<FormattedMessage id='empty_column.outgoing_follow_requests' defaultMessage="You don't have any outgoing follow requests yet. When you try to follow a user, it will show up here." />}
|
||||
itemClassName='p-2.5'
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
|
||||
@@ -98,7 +98,7 @@ const AccountTimelinePage: React.FC<IAccountTimelinePage> = ({ params, withRepli
|
||||
isLoading={isLoading}
|
||||
hasMore={hasMore}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.account_timeline' defaultMessage='No posts here!' />}
|
||||
emptyMessageText={<FormattedMessage id='empty_column.account_timeline' defaultMessage='No posts here!' />}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -118,7 +118,7 @@ const AdminAnnouncementsPage: React.FC = () => {
|
||||
</Button>
|
||||
<ScrollableList
|
||||
scrollKey='announcements'
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
itemClassName='py-3 first:pt-0 last:pb-0'
|
||||
isLoading={isLoading}
|
||||
showLoading={isLoading && isPending}
|
||||
|
||||
@@ -132,7 +132,7 @@ const AdminDomainsPage: React.FC = () => {
|
||||
{domains && (
|
||||
<ScrollableList
|
||||
scrollKey='domains'
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
itemClassName='py-3 first:pt-0 last:pb-0'
|
||||
isLoading={isFetching}
|
||||
showLoading={isFetching && !domains?.length}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedDate, useIntl } from 'react-intl';
|
||||
import { defineMessages, FormattedDate, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
@@ -11,7 +11,6 @@ import type { AdminModerationLogEntry } from 'pl-api';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.admin.moderation_log', defaultMessage: 'Moderation log' },
|
||||
emptyMessage: { id: 'admin.moderation_log.empty_message', defaultMessage: 'You have not performed any moderation actions yet. When you do, a history will be shown here.' },
|
||||
});
|
||||
|
||||
const ModerationLogPage = () => {
|
||||
@@ -36,7 +35,7 @@ const ModerationLogPage = () => {
|
||||
scrollKey='moderationLog'
|
||||
isLoading={isLoading}
|
||||
showLoading={showLoading}
|
||||
emptyMessage={intl.formatMessage(messages.emptyMessage)}
|
||||
emptyMessageText={<FormattedMessage id='admin.moderation_log.empty_message' defaultMessage='You have not performed any moderation actions yet. When you do, a history will be shown here.' />}
|
||||
hasMore={hasNextPage}
|
||||
onLoadMore={handleLoadMore}
|
||||
listClassName='divide-y divide-solid divide-gray-200 dark:divide-gray-800'
|
||||
|
||||
@@ -127,7 +127,7 @@ const RelaysPage: React.FC = () => {
|
||||
{relays && (
|
||||
<ScrollableList
|
||||
scrollKey='relays'
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
itemClassName='py-3 first:pt-0 last:pb-0'
|
||||
isLoading={isFetching}
|
||||
showLoading={isFetching && !relays?.length}
|
||||
|
||||
@@ -98,7 +98,7 @@ const RulesPage: React.FC = () => {
|
||||
</Button>
|
||||
<ScrollableList
|
||||
scrollKey='rules'
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
itemClassName='py-3 first:pt-0 last:pb-0'
|
||||
isLoading={isLoading}
|
||||
showLoading={isLoading}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { useSearchParams } from 'react-router-dom-v5-compat';
|
||||
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
@@ -11,8 +11,6 @@ import { SearchInput } from '../search/search';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.admin.users', defaultMessage: 'Users' },
|
||||
empty: { id: 'admin.user_index.empty', defaultMessage: 'No users found.' },
|
||||
searchPlaceholder: { id: 'admin.user_index.search_input_placeholder', defaultMessage: 'Who are you looking for?' },
|
||||
});
|
||||
|
||||
|
||||
@@ -37,7 +35,7 @@ const UserIndexPage: React.FC = () => {
|
||||
isLoading={isFetching}
|
||||
showLoading={isPending}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
emptyMessage={intl.formatMessage(messages.empty)}
|
||||
emptyMessageText={<FormattedMessage id='admin.user_index.empty' defaultMessage='No users found.' />}
|
||||
itemClassName='pb-4'
|
||||
>
|
||||
{(accountIds || []).map(id =>
|
||||
|
||||
@@ -84,8 +84,7 @@ const GroupBlockedMembers: React.FC<IGroupBlockedMembers> = ({ params }) => {
|
||||
<Column label={intl.formatMessage(messages.heading)} backHref={`/groups/${group.id}/manage`}>
|
||||
<ScrollableList
|
||||
scrollKey={`groupBlockedMembers:${groupId}`}
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageCard={false}
|
||||
emptyMessageText={emptyMessage}
|
||||
>
|
||||
{accountIds.map((accountId) =>
|
||||
<BlockedMember key={accountId} accountId={accountId} groupId={groupId} />,
|
||||
|
||||
@@ -108,7 +108,7 @@ const GroupMembershipRequests: React.FC<IGroupMembershipRequests> = ({ params })
|
||||
<Column label={intl.formatMessage(messages.heading)}>
|
||||
<ScrollableList
|
||||
scrollKey={`groupMembershipRequests:${groupId}`}
|
||||
emptyMessage={<FormattedMessage id='empty_column.group_membership_requests' defaultMessage='There are no pending membership requests for this group.' />}
|
||||
emptyMessageText={<FormattedMessage id='empty_column.group_membership_requests' defaultMessage='There are no pending membership requests for this group.' />}
|
||||
>
|
||||
{accounts.map((account) => (
|
||||
<MembershipRequest
|
||||
|
||||
@@ -68,8 +68,7 @@ const Groups: React.FC = () => {
|
||||
|
||||
<ScrollableList
|
||||
scrollKey='groups'
|
||||
emptyMessage={renderBlankslate()}
|
||||
emptyMessageCard={false}
|
||||
emptyMessageText={renderBlankslate()}
|
||||
itemClassName='pb-4 last:pb-0'
|
||||
isLoading={isLoading}
|
||||
showLoading={isLoading && groups.length === 0}
|
||||
|
||||
@@ -159,7 +159,7 @@ const AliasesPage = () => {
|
||||
<div className='flex-1'>
|
||||
<ScrollableList
|
||||
scrollKey='aliases'
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
>
|
||||
{aliases.map((alias, i) => (
|
||||
<HStack alignItems='center' justifyContent='between' space={1} key={i} className='p-2'>
|
||||
|
||||
@@ -39,8 +39,7 @@ const BlocksPage: React.FC = () => {
|
||||
scrollKey='blocks'
|
||||
onLoadMore={fetchNextPage}
|
||||
hasMore={hasNextPage}
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageCard={false}
|
||||
emptyMessageText={emptyMessage}
|
||||
itemClassName={clsx('pb-4', { 'last:pb-0': !hasNextPage })}
|
||||
isLoading={isFetching}
|
||||
>
|
||||
|
||||
@@ -40,7 +40,7 @@ const DomainBlocksPage: React.FC = () => {
|
||||
scrollKey='domainBlocks'
|
||||
onLoadMore={handleLoadMore}
|
||||
hasMore={hasNextPage}
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
listClassName='divide-y divide-gray-200 dark:divide-gray-800'
|
||||
>
|
||||
{domains.map((domain) =>
|
||||
|
||||
@@ -71,7 +71,7 @@ const FiltersPage = () => {
|
||||
|
||||
<ScrollableList
|
||||
scrollKey='filters'
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
itemClassName='pb-4 last:pb-0'
|
||||
>
|
||||
{filters.map((filter) => (
|
||||
|
||||
@@ -22,7 +22,7 @@ const FollowedTagsPage = () => {
|
||||
<Column label={intl.formatMessage(messages.heading)}>
|
||||
<ScrollableList
|
||||
scrollKey='followedTags'
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
isLoading={isLoading}
|
||||
hasMore={hasNextPage}
|
||||
onLoadMore={fetchNextPage}
|
||||
|
||||
@@ -31,10 +31,9 @@ const MutesPage: React.FC = () => {
|
||||
isLoading={isFetching}
|
||||
onLoadMore={fetchNextPage}
|
||||
hasMore={hasNextPage}
|
||||
emptyMessage={
|
||||
emptyMessageText={
|
||||
<FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />
|
||||
}
|
||||
emptyMessageCard={false}
|
||||
>
|
||||
{data.map((accountId) =>
|
||||
<AccountContainer key={accountId} id={accountId} actionType='muting' />,
|
||||
|
||||
@@ -96,7 +96,7 @@ const BookmarksPage: React.FC<IBookmarks> = ({ params }) => {
|
||||
hasMore={hasNextPage}
|
||||
isLoading={isFetching}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
</Column>
|
||||
|
||||
@@ -21,7 +21,7 @@ const DraftStatusesPage = () => {
|
||||
<Column label={intl.formatMessage(messages.heading)}>
|
||||
<ScrollableList
|
||||
scrollKey='draftStatuses'
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
listClassName='divide-y divide-solid divide-gray-200 dark:divide-gray-800'
|
||||
>
|
||||
{drafts.toReversed().map((draft) => <DraftStatus key={draft.draft_id} draftStatus={draft} />)}
|
||||
|
||||
@@ -58,7 +58,7 @@ const FavouritedStatusesPage: React.FC<IFavourites> = ({ params }) => {
|
||||
hasMore={hasNextPage}
|
||||
isLoading={isFetching}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
|
||||
@@ -244,7 +244,7 @@ const InteractionRequestsPage = () => {
|
||||
isLoading={isFetching}
|
||||
showLoading={isLoading}
|
||||
hasMore={hasNextPage}
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
onLoadMore={() => fetchNextPage()}
|
||||
listClassName={clsx('divide-y divide-solid divide-gray-200 black:divide-gray-800 dark:divide-primary-800', {
|
||||
'animate-pulse': data?.length === 0,
|
||||
|
||||
@@ -33,7 +33,7 @@ const PinnedStatusesPage = () => {
|
||||
scrollKey='pinned_statuses'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
emptyMessage={<FormattedMessage id='pinned_statuses.none' defaultMessage='No pins to show.' />}
|
||||
emptyMessageText={<FormattedMessage id='pinned_statuses.none' defaultMessage='No pins to show.' />}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
|
||||
@@ -37,7 +37,7 @@ const QuotesPage: React.FC = () => {
|
||||
hasMore={hasNextPage}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
</Column>
|
||||
|
||||
@@ -24,7 +24,7 @@ const ScheduledStatusesPage = () => {
|
||||
hasMore={hasNextPage}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
listClassName='divide-y divide-solid divide-gray-200 dark:divide-gray-800'
|
||||
>
|
||||
{scheduledStatuses.map((status) => <ScheduledStatus key={status.id} scheduledStatus={status} />)}
|
||||
|
||||
@@ -141,7 +141,7 @@ const EventDiscussionPage: React.FC<IEventDiscussion> = ({ params: { statusId: s
|
||||
id='thread'
|
||||
placeholderComponent={() => <PlaceholderStatus variant='slim' />}
|
||||
initialTopMostItemIndex={0}
|
||||
emptyMessage={<FormattedMessage id='event.discussion.empty' defaultMessage='No one has commented this event yet. When someone does, they will appear here.' />}
|
||||
emptyMessageText={<FormattedMessage id='event.discussion.empty' defaultMessage='No one has commented this event yet. When someone does, they will appear here.' />}
|
||||
>
|
||||
{children}
|
||||
</ScrollableList>
|
||||
|
||||
@@ -45,7 +45,8 @@ const BubbleTimelinePage = () => {
|
||||
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
|
||||
prefix='home'
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.bubble' defaultMessage='There is nothing here! Write something publicly to fill it up' />}
|
||||
emptyMessageText={<FormattedMessage id='empty_column.bubble' defaultMessage='There is nothing here! Write something publicly to fill it up' />}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
</Column>
|
||||
|
||||
@@ -102,7 +102,8 @@ const CircleTimelinePage: React.FC = () => {
|
||||
scrollKey='circle_timeline'
|
||||
timelineId={`circle:${id}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
|
||||
@@ -43,7 +43,8 @@ const CommunityTimelinePage = () => {
|
||||
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
|
||||
prefix='home'
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
|
||||
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')}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
</Column>
|
||||
|
||||
@@ -9,9 +9,7 @@ import { useGroup } from 'pl-fe/api/hooks/groups/use-group';
|
||||
import { useGroupStream } from 'pl-fe/api/hooks/streaming/use-group-stream';
|
||||
import Avatar from 'pl-fe/components/ui/avatar';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
import Icon from 'pl-fe/components/ui/icon';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import Text from 'pl-fe/components/ui/text';
|
||||
import Timeline from 'pl-fe/features/ui/components/timeline';
|
||||
import { ComposeForm } from 'pl-fe/features/ui/util/async-components';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
@@ -95,21 +93,8 @@ const GroupTimelinePage: React.FC<IGroupTimelinePage> = (props) => {
|
||||
scrollKey='group_timeline'
|
||||
timelineId={composeId}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={
|
||||
<Stack space={4} className='py-6' justifyContent='center' alignItems='center'>
|
||||
<div className='rounded-full bg-gray-200 p-4 dark:bg-gray-800'>
|
||||
<Icon
|
||||
src={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
className='size-6 text-gray-600'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Text theme='muted'>
|
||||
<FormattedMessage id='empty_column.group' defaultMessage='There are no posts in this group yet.' />
|
||||
</Text>
|
||||
</Stack>
|
||||
}
|
||||
emptyMessageCard={false}
|
||||
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}
|
||||
featuredStatusIds={featuredStatusIds}
|
||||
/>
|
||||
|
||||
@@ -69,7 +69,7 @@ const HashtagTimelinePage: React.FC<IHashtagTimelinePage> = ({ params }) => {
|
||||
scrollKey='hashtag_timeline'
|
||||
timelineId={`hashtag:${tagId}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
|
||||
emptyMessageText={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
|
||||
@@ -63,7 +63,7 @@ const HomeTimelinePage: React.FC = () => {
|
||||
scrollKey='home_timeline'
|
||||
onLoadMore={handleLoadMore}
|
||||
timelineId='home'
|
||||
emptyMessage={
|
||||
emptyMessageText={
|
||||
<Stack space={1}>
|
||||
<Text size='xl' weight='medium' align='center'>
|
||||
<FormattedMessage
|
||||
@@ -97,6 +97,7 @@ const HomeTimelinePage: React.FC = () => {
|
||||
)}
|
||||
</Stack>
|
||||
}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
</Column>
|
||||
|
||||
@@ -105,7 +105,8 @@ const LandingTimelinePage = () => {
|
||||
timelineId={timelineId}
|
||||
prefix='home'
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
|
||||
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')}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
) : (
|
||||
|
||||
@@ -40,7 +40,8 @@ const LinkTimelinePage: React.FC<ILinkTimelinePage> = ({ params }) => {
|
||||
scrollKey='link_timeline'
|
||||
timelineId={`link:${url}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.link_timeline' defaultMessage='There are no posts with this link yet.' />}
|
||||
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>
|
||||
);
|
||||
|
||||
@@ -105,7 +105,8 @@ const ListTimelinePage: React.FC = () => {
|
||||
scrollKey='list_timeline'
|
||||
timelineId={`list:${id}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={emptyMessage}
|
||||
emptyMessageText={emptyMessage}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/list-bullets.svg')}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
|
||||
@@ -90,7 +90,8 @@ const PublicTimelinePage = () => {
|
||||
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
|
||||
prefix='home'
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up' />}
|
||||
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>
|
||||
</Column>
|
||||
|
||||
@@ -68,13 +68,14 @@ const RemoteTimelinePage: React.FC<IRemoteTimelinePage> = ({ params }) => {
|
||||
scrollKey={`${timelineId}_${instance}_timeline`}
|
||||
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}:${instance}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={
|
||||
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')}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
|
||||
@@ -40,7 +40,8 @@ const TestTimelinePage: React.FC = () => {
|
||||
<Timeline
|
||||
scrollKey={`${timelineId}_timeline`}
|
||||
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
|
||||
emptyMessage={<FormattedMessage id='empty_column.test' defaultMessage='The test timeline is empty.' />}
|
||||
emptyMessageText={<FormattedMessage id='empty_column.test' defaultMessage='The test timeline is empty.' />}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
|
||||
@@ -41,7 +41,8 @@ const WrenchedTimelinePage = () => {
|
||||
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
|
||||
prefix='home'
|
||||
onLoadMore={handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.wrenched' defaultMessage='There is nothing here! 🔧 a public post to fill it up' />}
|
||||
emptyMessageText={<FormattedMessage id='empty_column.wrenched' defaultMessage='There is nothing here! 🔧 a public post to fill it up' />}
|
||||
emptyMessageIcon={require('@phosphor-icons/core/regular/wrench.svg')}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
</Column>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import Accordion from 'pl-fe/components/ui/accordion';
|
||||
@@ -14,8 +14,6 @@ const messages = defineMessages({
|
||||
heading: { id: 'column.federation_restrictions', defaultMessage: 'Federation restrictions' },
|
||||
boxTitle: { id: 'federation_restrictions.explanation_box.title', defaultMessage: 'Instance-specific policies' },
|
||||
boxMessage: { id: 'federation_restrictions.explanation_box.message', defaultMessage: 'Normally servers on the Fediverse can communicate freely. {siteTitle} has imposed restrictions on the following servers.' },
|
||||
emptyMessage: { id: 'federation_restrictions.empty_message', defaultMessage: '{siteTitle} has not restricted any instances.' },
|
||||
notDisclosed: { id: 'federation_restrictions.not_disclosed_message', defaultMessage: '{siteTitle} does not disclose federation restrictions through the API.' },
|
||||
});
|
||||
|
||||
const FederationRestrictionsPage = () => {
|
||||
@@ -33,7 +31,9 @@ const FederationRestrictionsPage = () => {
|
||||
setExplanationBoxExpanded(setting);
|
||||
};
|
||||
|
||||
const emptyMessage = disclosed ? messages.emptyMessage : messages.notDisclosed;
|
||||
const emptyMessage = disclosed
|
||||
? <FormattedMessage id='federation_restrictions.empty_message' defaultMessage='{siteTitle} has not restricted any instances.' values={{ siteTitle: instance.title }} />
|
||||
: <FormattedMessage id='federation_restrictions.not_disclosed_message' defaultMessage='{siteTitle} does not disclose federation restrictions through the API.' values={{ siteTitle: instance.title }} />;
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.heading)}>
|
||||
@@ -46,7 +46,7 @@ const FederationRestrictionsPage = () => {
|
||||
</Accordion>
|
||||
|
||||
<div className='pt-4'>
|
||||
<ScrollableList emptyMessage={intl.formatMessage(emptyMessage, { siteTitle: instance.title })}>
|
||||
<ScrollableList emptyMessageText={emptyMessage}>
|
||||
{hosts.map((host) => <RestrictedInstance key={host} host={host} />)}
|
||||
</ScrollableList>
|
||||
</div>
|
||||
|
||||
@@ -171,3 +171,7 @@ a.⁂-list-item,
|
||||
align-items: stretch;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div[data-viewport-type="window"]:has(.⁂-empty-message) {
|
||||
position: initial!important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user