feat: v2 rewrite — hexagonal arch, ActivityPub federation, NATS, deployment-ready #1

Merged
GKaszewski merged 334 commits from v2 into master 2026-05-16 09:42:43 +00:00
2 changed files with 26 additions and 57 deletions
Showing only changes of commit 688e7b0018 - Show all commits

View File

@@ -3,9 +3,8 @@
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useRouter } from "next/navigation";
import { useAuth } from "@/hooks/use-auth";
import { Me, UpdateProfileSchema, updateProfile } from "@/lib/api";
import { Me, UpdateProfileSchema } from "@/lib/api";
import { updateProfile } from "@/app/actions/profile";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter } from "@/components/ui/card";
@@ -25,8 +24,6 @@ interface EditProfileFormProps {
}
export function EditProfileForm({ currentUser }: EditProfileFormProps) {
const router = useRouter();
const { token } = useAuth();
const form = useForm<z.infer<typeof UpdateProfileSchema>>({
resolver: zodResolver(UpdateProfileSchema),
@@ -40,13 +37,10 @@ export function EditProfileForm({ currentUser }: EditProfileFormProps) {
});
async function onSubmit(values: z.infer<typeof UpdateProfileSchema>) {
if (!token) return;
toast.info("Updating your profile...");
try {
await updateProfile(values, token);
await updateProfile(currentUser.username, values);
toast.success("Profile updated successfully!");
router.push(`/users/${currentUser.username}`);
router.refresh();
} catch (err) {
toast.error(`Failed to update profile. ${err}`);
}

View File

@@ -1,66 +1,41 @@
"use client";
"use client"
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useAuth } from "@/hooks/use-auth";
import { followUser, unfollowUser } from "@/lib/api";
import { Button } from "@/components/ui/button";
import { toast } from "sonner";
import { UserPlus, UserMinus } from "lucide-react";
import { useOptimistic } from "react"
import { followUser, unfollowUser } from "@/app/actions/social"
import { Button } from "@/components/ui/button"
import { toast } from "sonner"
import { UserPlus, UserMinus } from "lucide-react"
interface FollowButtonProps {
username: string;
isInitiallyFollowing: boolean;
username: string
isInitiallyFollowing: boolean
}
export function FollowButton({
username,
isInitiallyFollowing,
}: FollowButtonProps) {
const [isFollowing, setIsFollowing] = useState(isInitiallyFollowing);
const [isLoading, setIsLoading] = useState(false);
const { token } = useAuth();
const router = useRouter();
const handleClick = async () => {
if (!token) {
toast.error("You must be logged in to follow users.");
return;
}
setIsLoading(true);
const action = isFollowing ? unfollowUser : followUser;
export function FollowButton({ username, isInitiallyFollowing }: FollowButtonProps) {
const [optimisticFollowing, setOptimisticFollowing] = useOptimistic(isInitiallyFollowing)
async function handleClick() {
const next = !optimisticFollowing
setOptimisticFollowing(next)
try {
// Optimistic update
setIsFollowing(!isFollowing);
await action(username, token);
router.refresh(); // Re-fetch server component data to get the latest follower count etc.
await (next ? followUser(username) : unfollowUser(username))
} catch {
// Revert on error
setIsFollowing(isFollowing);
toast.error(`Failed to ${isFollowing ? "unfollow" : "follow"} user.`);
} finally {
setIsLoading(false);
setOptimisticFollowing(!next) // revert
toast.error(`Failed to ${next ? "follow" : "unfollow"} user.`)
}
};
}
return (
<Button
onClick={handleClick}
disabled={isLoading}
variant={isFollowing ? "secondary" : "default"}
data-following={isFollowing}
variant={optimisticFollowing ? "secondary" : "default"}
data-following={optimisticFollowing}
>
{isFollowing ? (
<>
<UserMinus className="mr-2 h-4 w-4" /> Unfollow
</>
{optimisticFollowing ? (
<><UserMinus className="mr-2 h-4 w-4" /> Unfollow</>
) : (
<>
<UserPlus className="mr-2 h-4 w-4" /> Follow
</>
<><UserPlus className="mr-2 h-4 w-4" /> Follow</>
)}
</Button>
);
)
}