c74dbb6a3b
Extends the feed.* namespace with five new sub-namespaces:
compose.placeholder, tabs (Follows / Following / Global), empty (the
six per-tab + per-mode empty-state messages plus the three CTA labels
that show under them), modeSwitcher (the home feed's mode-picker dropdown
with Agora / All Nostr / Following options, the dropdown trigger's aria
label, and the disabled-Following tooltip), replyContext (Replying to,
the and-joiner, plus a pluralized andOthers_one/andOthers_other), and
actions (Reply / Repost / Undo repost / React / Zap / Share / More + the
Link-copied toast).
PostActionBar's replyLabel prop default went from 'Reply' to undefined;
the component falls back to t('feed.actions.reply') at render time so
existing call sites that pass an already-translated string keep working
unchanged. FeedModeSwitcher's OPTIONS array swapped its hard-coded
label string for an i18nKey suffix, materialized through t() inside the
component.
ar / es / zh / fa / ps / km / sn translations drafted inline; km, sn,
ps, fa still pending native-speaker review.
69 lines
2.3 KiB
TypeScript
69 lines
2.3 KiB
TypeScript
import { Link } from 'react-router-dom';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Users } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { useAppContext } from '@/hooks/useAppContext';
|
|
import { getStorageKey } from '@/lib/storageKey';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface FeedEmptyStateProps {
|
|
/** Primary empty-state message. */
|
|
message: string;
|
|
/** Called when the user clicks "Switch to Global". Omit to hide the button. */
|
|
onSwitchToGlobal?: () => void;
|
|
/** Show a "Discover people" link to /packs. */
|
|
showDiscover?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
/**
|
|
* Consistent empty state for Follows/Global feed tabs across all feed pages.
|
|
*
|
|
* - Follows tab: pass `onSwitchToGlobal` and `showDiscover` to render CTAs.
|
|
* - Global tab: omit both; the message should guide the user
|
|
* to check their relay connections.
|
|
*/
|
|
export function FeedEmptyState({
|
|
message,
|
|
onSwitchToGlobal,
|
|
showDiscover,
|
|
className,
|
|
}: FeedEmptyStateProps) {
|
|
const { t } = useTranslation();
|
|
const { config } = useAppContext();
|
|
|
|
// The /packs page defaults to the Follows tab, which is also empty when the
|
|
// user doesn't follow anyone. Pre-seed its saved tab to Global so the link
|
|
// lands on a populated view.
|
|
const handleDiscoverClick = () => {
|
|
try {
|
|
sessionStorage.setItem(getStorageKey(config.appId, 'feed-tab:packs'), 'global');
|
|
} catch { /* sessionStorage unavailable */ }
|
|
};
|
|
|
|
return (
|
|
<div className={cn('py-20 px-8 flex flex-col items-center text-center', className)}>
|
|
<div className="size-12 rounded-full bg-muted flex items-center justify-center mb-4">
|
|
<Users className="size-6 text-muted-foreground" />
|
|
</div>
|
|
|
|
<p className="text-muted-foreground max-w-xs">{message}</p>
|
|
|
|
{(showDiscover || onSwitchToGlobal) && (
|
|
<div className="flex flex-col gap-2 mt-5 w-full max-w-xs">
|
|
{showDiscover && (
|
|
<Button asChild className="rounded-full">
|
|
<Link to="/packs" onClick={handleDiscoverClick}>{t('feed.empty.discoverPeople')}</Link>
|
|
</Button>
|
|
)}
|
|
{onSwitchToGlobal && (
|
|
<Button variant="ghost" className="rounded-full" onClick={onSwitchToGlobal}>
|
|
{t('feed.empty.browseGlobal')}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|