f4688137bc
Five small UX improvements bundled together.
Default post-to-country with localStorage persistence
- New `useDefaultPostCountry` hook (localStorage-backed) hydrates the
composer's destination from a saved preference on every fresh compose.
- ComposeBox's country-destination picker (formerly a shadcn Select)
becomes a DropdownMenu so it can mix country options with an action
item.
- New "Set as default" item appears at the bottom of the dropdown
when the current selection is not already the saved default; clicking
it persists the choice and shows a toast.
- A passive "Country X is your default" label replaces the action
item when the current selection already is the default.
- resetComposeState now resets to the saved default instead of
hardcoded 'world', so the next compose lands where the user expects.
- The existing snap-back-to-world guard now also clears the saved
default if it points at a country the user has just unfollowed.
Remove weather from country feed pages
- Drop `WeatherVitalsRow`'s weather panel — the row keeps the
population / languages / currency vitals but no longer renders
temperature, sky description, or icon.
- Remove the live day/night sky-overlay flip on the country hero;
default to the warm daytime gradient.
- Remove the `PrecipitationEffect` overlay (animated rain/snow) from
country pages.
- Delete the now-orphan `useWeather` hook and
`PrecipitationEffect` component.
Remove organization activity feed from /communities
- Drop the `OrganizationActivityFeed` section and its helpers.
- /communities is now a directory page: hero + My organizations shelf
+ Featured organizations shelf.
- Delete the now-orphan `useOrganizationHomeActivityFeed` and
`useOrganizationMembersOnlyFilter` hooks.
Compact FeedModeSwitcher
- Strip the per-item descriptions ("Campaigns, pledges, donations,
and Agora posts", etc.) from the home-feed mode dropdown.
- Each menu item is now a single line: icon + label + optional check.
- Shrink menu width from w-72 to w-56 to match the new content density.
- Keep the disabled-Following tooltip — that's a state explanation,
not help text.
126 lines
4.1 KiB
TypeScript
126 lines
4.1 KiB
TypeScript
import { Check, ChevronDown, Globe, Sparkles, Users } from 'lucide-react';
|
|
import type { ComponentType } from 'react';
|
|
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from '@/components/ui/dropdown-menu';
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from '@/components/ui/tooltip';
|
|
import type { FeedMode } from '@/hooks/useMixedFeed';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface FeedModeOption {
|
|
mode: FeedMode;
|
|
label: string;
|
|
icon: ComponentType<{ className?: string }>;
|
|
}
|
|
|
|
const OPTIONS: FeedModeOption[] = [
|
|
{ mode: 'agora', label: 'Agora', icon: Sparkles },
|
|
{ mode: 'all-nostr', label: 'All Nostr', icon: Globe },
|
|
{ mode: 'following', label: 'Following', icon: Users },
|
|
];
|
|
|
|
interface FeedModeSwitcherProps {
|
|
value: FeedMode;
|
|
onChange: (mode: FeedMode) => void;
|
|
/** When false, Following mode is disabled (requires login). */
|
|
followingAvailable: boolean;
|
|
/** Click handler for the disabled Following item (typically opens the auth dialog). */
|
|
onLoginRequested?: () => void;
|
|
className?: string;
|
|
}
|
|
|
|
/**
|
|
* The primary feed-mode picker rendered at the top-left of the home feed page.
|
|
*
|
|
* Visually anchored as the page heading — the active mode label is the largest
|
|
* text on the page. Clicking opens a compact dropdown menu offering the three
|
|
* modes; the active one is marked with a check.
|
|
*
|
|
* Logged-out users see "Following" greyed out; clicking it invokes
|
|
* {@link FeedModeSwitcherProps.onLoginRequested} to surface the auth dialog.
|
|
*/
|
|
export function FeedModeSwitcher({
|
|
value,
|
|
onChange,
|
|
followingAvailable,
|
|
onLoginRequested,
|
|
className,
|
|
}: FeedModeSwitcherProps) {
|
|
const active = OPTIONS.find((opt) => opt.mode === value) ?? OPTIONS[0];
|
|
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger
|
|
className={cn(
|
|
'group inline-flex items-center gap-2 rounded-lg -ml-1 px-1 py-1 outline-none',
|
|
'text-foreground hover:text-foreground motion-safe:transition-colors',
|
|
'focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background',
|
|
className,
|
|
)}
|
|
aria-label={`Feed mode: ${active.label}. Click to change.`}
|
|
>
|
|
<span className="text-2xl sm:text-3xl font-bold tracking-tight leading-none">
|
|
{active.label}
|
|
</span>
|
|
<ChevronDown
|
|
className="size-5 text-muted-foreground motion-safe:transition-transform group-data-[state=open]:rotate-180"
|
|
aria-hidden
|
|
/>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="start" sideOffset={8} className="w-56 p-1.5">
|
|
{OPTIONS.map((opt) => {
|
|
const Icon = opt.icon;
|
|
const isActive = opt.mode === value;
|
|
const isFollowing = opt.mode === 'following';
|
|
const disabled = isFollowing && !followingAvailable;
|
|
|
|
const handleSelect = (event: Event) => {
|
|
if (disabled) {
|
|
event.preventDefault();
|
|
onLoginRequested?.();
|
|
return;
|
|
}
|
|
onChange(opt.mode);
|
|
};
|
|
|
|
const itemContent = (
|
|
<DropdownMenuItem
|
|
key={opt.mode}
|
|
onSelect={handleSelect}
|
|
className={cn(
|
|
'flex items-center gap-3 rounded-md px-3 py-2 cursor-pointer',
|
|
disabled && 'opacity-60 data-[disabled]:opacity-60',
|
|
)}
|
|
data-disabled={disabled || undefined}
|
|
>
|
|
<Icon className="size-4 shrink-0 text-muted-foreground" aria-hidden />
|
|
<span className="flex-1 text-sm font-medium">{opt.label}</span>
|
|
{isActive && <Check className="size-4 shrink-0 text-primary" aria-hidden />}
|
|
</DropdownMenuItem>
|
|
);
|
|
|
|
if (disabled) {
|
|
return (
|
|
<Tooltip key={opt.mode}>
|
|
<TooltipTrigger asChild>{itemContent}</TooltipTrigger>
|
|
<TooltipContent side="right">
|
|
Log in to see posts from people you follow
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
);
|
|
}
|
|
return itemContent;
|
|
})}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
);
|
|
}
|