Prettier format pending files
This commit is contained in:
parent
7bf4ce124d
commit
db87f720a1
5 changed files with 157 additions and 53 deletions
|
|
@ -115,14 +115,24 @@ export function FeedItemCard({
|
|||
<div className="rounded-2xl border border-border/40 bg-background/80 p-4">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 text-sm text-muted-foreground">
|
||||
<div className="flex items-center gap-3">
|
||||
<Button variant="ghost" size="sm" className="gap-2 pl-2 pr-3" onClick={() => onLike(item.id)}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="gap-2 pl-2 pr-3"
|
||||
onClick={() => onLike(item.id)}
|
||||
>
|
||||
<Heart className="h-4 w-4 text-aethex-400" />
|
||||
<span className="font-medium text-foreground">
|
||||
{item.likes.toLocaleString()}
|
||||
</span>
|
||||
<span className="hidden sm:inline">Like</span>
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="gap-2 pl-2 pr-3" onClick={() => onComment(item.id)}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="gap-2 pl-2 pr-3"
|
||||
onClick={() => onComment(item.id)}
|
||||
>
|
||||
<MessageCircle className="h-4 w-4 text-aethex-400" />
|
||||
<span className="font-medium text-foreground">
|
||||
{item.comments.toLocaleString()}
|
||||
|
|
|
|||
|
|
@ -255,9 +255,15 @@ export const communityService = {
|
|||
if (!error) {
|
||||
return (Array.isArray(data) ? data : []) as CommunityPost[];
|
||||
}
|
||||
console.warn("Supabase getPosts relational select failed:", (error as any)?.message || error);
|
||||
console.warn(
|
||||
"Supabase getPosts relational select failed:",
|
||||
(error as any)?.message || error,
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn("Supabase getPosts relational select threw:", (e as any)?.message || e);
|
||||
console.warn(
|
||||
"Supabase getPosts relational select threw:",
|
||||
(e as any)?.message || e,
|
||||
);
|
||||
}
|
||||
|
||||
// 2) Fallback to simple posts select, then hydrate author profiles manually
|
||||
|
|
@ -269,7 +275,9 @@ export const communityService = {
|
|||
.order("created_at", { ascending: false })
|
||||
.limit(limit);
|
||||
if (!postsErr && Array.isArray(posts) && posts.length) {
|
||||
const authorIds = Array.from(new Set(posts.map((p: any) => p.author_id).filter(Boolean)));
|
||||
const authorIds = Array.from(
|
||||
new Set(posts.map((p: any) => p.author_id).filter(Boolean)),
|
||||
);
|
||||
let profilesById: Record<string, any> = {};
|
||||
if (authorIds.length) {
|
||||
const { data: profiles, error: profErr } = await supabase
|
||||
|
|
@ -278,20 +286,39 @@ export const communityService = {
|
|||
.in("id", authorIds);
|
||||
if (!profErr && Array.isArray(profiles)) {
|
||||
profilesById = Object.fromEntries(
|
||||
profiles.map((u: any) => [u.id, { username: u.username, full_name: u.full_name, avatar_url: u.avatar_url }]),
|
||||
profiles.map((u: any) => [
|
||||
u.id,
|
||||
{
|
||||
username: u.username,
|
||||
full_name: u.full_name,
|
||||
avatar_url: u.avatar_url,
|
||||
},
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
return posts.map((p: any) => ({ ...p, user_profiles: profilesById[p.author_id] || null })) as CommunityPost[];
|
||||
return posts.map((p: any) => ({
|
||||
...p,
|
||||
user_profiles: profilesById[p.author_id] || null,
|
||||
})) as CommunityPost[];
|
||||
}
|
||||
if (postsErr) console.warn("Supabase getPosts simple select failed:", (postsErr as any)?.message || postsErr);
|
||||
if (postsErr)
|
||||
console.warn(
|
||||
"Supabase getPosts simple select failed:",
|
||||
(postsErr as any)?.message || postsErr,
|
||||
);
|
||||
} catch (e2) {
|
||||
console.warn("Supabase getPosts simple select threw:", (e2 as any)?.message || e2);
|
||||
console.warn(
|
||||
"Supabase getPosts simple select threw:",
|
||||
(e2 as any)?.message || e2,
|
||||
);
|
||||
}
|
||||
|
||||
// 3) Final fallback to API if available
|
||||
try {
|
||||
const resp = await fetch(`/api/posts?limit=${encodeURIComponent(String(limit))}`);
|
||||
const resp = await fetch(
|
||||
`/api/posts?limit=${encodeURIComponent(String(limit))}`,
|
||||
);
|
||||
if (resp.ok) {
|
||||
const ct = resp.headers.get("content-type") || "";
|
||||
if (ct.includes("application/json") || ct.includes("json")) {
|
||||
|
|
@ -299,13 +326,24 @@ export const communityService = {
|
|||
return (Array.isArray(payload) ? payload : []) as CommunityPost[];
|
||||
} else {
|
||||
const text = await resp.text();
|
||||
console.warn("API fallback returned non-JSON content-type:", ct, text.slice(0, 120));
|
||||
console.warn(
|
||||
"API fallback returned non-JSON content-type:",
|
||||
ct,
|
||||
text.slice(0, 120),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.warn("API fallback /api/posts not ok:", resp.status, resp.statusText);
|
||||
console.warn(
|
||||
"API fallback /api/posts not ok:",
|
||||
resp.status,
|
||||
resp.statusText,
|
||||
);
|
||||
}
|
||||
} catch (apiErr) {
|
||||
console.error("API fallback for getPosts failed:", (apiErr as any)?.message || apiErr);
|
||||
console.error(
|
||||
"API fallback for getPosts failed:",
|
||||
(apiErr as any)?.message || apiErr,
|
||||
);
|
||||
}
|
||||
|
||||
// Return actual empty array (no demo/mocks)
|
||||
|
|
@ -364,11 +402,14 @@ export const communityService = {
|
|||
|
||||
async likePost(postId: string, userId: string): Promise<number | null> {
|
||||
try {
|
||||
const resp = await fetch(`/api/community/posts/${encodeURIComponent(postId)}/like`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ user_id: userId }),
|
||||
});
|
||||
const resp = await fetch(
|
||||
`/api/community/posts/${encodeURIComponent(postId)}/like`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ user_id: userId }),
|
||||
},
|
||||
);
|
||||
if (resp.ok) {
|
||||
const json = await resp.json();
|
||||
return typeof json?.likes === "number" ? json.likes : null;
|
||||
|
|
@ -379,11 +420,14 @@ export const communityService = {
|
|||
|
||||
async unlikePost(postId: string, userId: string): Promise<number | null> {
|
||||
try {
|
||||
const resp = await fetch(`/api/community/posts/${encodeURIComponent(postId)}/unlike`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ user_id: userId }),
|
||||
});
|
||||
const resp = await fetch(
|
||||
`/api/community/posts/${encodeURIComponent(postId)}/unlike`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ user_id: userId }),
|
||||
},
|
||||
);
|
||||
if (resp.ok) {
|
||||
const json = await resp.json();
|
||||
return typeof json?.likes === "number" ? json.likes : null;
|
||||
|
|
@ -394,7 +438,9 @@ export const communityService = {
|
|||
|
||||
async listComments(postId: string): Promise<any[]> {
|
||||
try {
|
||||
const resp = await fetch(`/api/community/posts/${encodeURIComponent(postId)}/comments`);
|
||||
const resp = await fetch(
|
||||
`/api/community/posts/${encodeURIComponent(postId)}/comments`,
|
||||
);
|
||||
if (!resp.ok) return [];
|
||||
return await resp.json();
|
||||
} catch {
|
||||
|
|
@ -402,12 +448,19 @@ export const communityService = {
|
|||
}
|
||||
},
|
||||
|
||||
async addComment(postId: string, userId: string, content: string): Promise<any | null> {
|
||||
const resp = await fetch(`/api/community/posts/${encodeURIComponent(postId)}/comments`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ user_id: userId, content }),
|
||||
});
|
||||
async addComment(
|
||||
postId: string,
|
||||
userId: string,
|
||||
content: string,
|
||||
): Promise<any | null> {
|
||||
const resp = await fetch(
|
||||
`/api/community/posts/${encodeURIComponent(postId)}/comments`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ user_id: userId, content }),
|
||||
},
|
||||
);
|
||||
if (!resp.ok) return null;
|
||||
return await resp.json();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -133,7 +133,13 @@ export default function Feed() {
|
|||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [aethexSocialService, communityService, mapPostsToFeedItems, toast, user?.id]);
|
||||
}, [
|
||||
aethexSocialService,
|
||||
communityService,
|
||||
mapPostsToFeedItems,
|
||||
toast,
|
||||
user?.id,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchFeed();
|
||||
|
|
@ -246,10 +252,16 @@ export default function Feed() {
|
|||
const content = prompt("Add a comment:")?.trim();
|
||||
if (!content) return;
|
||||
try {
|
||||
const created = await communityService.addComment(postId, user.id, content);
|
||||
const created = await communityService.addComment(
|
||||
postId,
|
||||
user.id,
|
||||
content,
|
||||
);
|
||||
if (created) {
|
||||
setItems((prev) =>
|
||||
prev.map((it) => (it.id === postId ? { ...it, comments: it.comments + 1 } : it)),
|
||||
prev.map((it) =>
|
||||
it.id === postId ? { ...it, comments: it.comments + 1 } : it,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,10 @@ const ProfilePassport = () => {
|
|||
(AethexUserProfile & { email?: string | null }) | null
|
||||
>(null);
|
||||
const [achievements, setAchievements] = useState<AethexAchievement[]>([]);
|
||||
const [followStats, setFollowStats] = useState<{ followers: number; following: number }>({ followers: 0, following: 0 });
|
||||
const [followStats, setFollowStats] = useState<{
|
||||
followers: number;
|
||||
following: number;
|
||||
}>({ followers: 0, following: 0 });
|
||||
const [degree, setDegree] = useState<string>("");
|
||||
const [projects, setProjects] = useState<ProjectPreview[]>([]);
|
||||
const [interests, setInterests] = useState<string[]>([]);
|
||||
|
|
@ -266,7 +269,11 @@ const ProfilePassport = () => {
|
|||
aethexSocialService.getFollowing(resolvedId),
|
||||
aethexSocialService.getFollowers(resolvedId),
|
||||
]);
|
||||
if (!cancelled) setFollowStats({ following: followingIds.length, followers: followerIds.length });
|
||||
if (!cancelled)
|
||||
setFollowStats({
|
||||
following: followingIds.length,
|
||||
followers: followerIds.length,
|
||||
});
|
||||
} catch {}
|
||||
try {
|
||||
const me = user?.id || null;
|
||||
|
|
@ -276,9 +283,13 @@ const ProfilePassport = () => {
|
|||
if (first.has(resolvedId)) setDegree("1st");
|
||||
else {
|
||||
const secondLists = await Promise.all(
|
||||
Array.from(first).slice(0, 50).map((id) => aethexSocialService.getConnections(id)),
|
||||
Array.from(first)
|
||||
.slice(0, 50)
|
||||
.map((id) => aethexSocialService.getConnections(id)),
|
||||
);
|
||||
const second = new Set(
|
||||
secondLists.flat().map((c: any) => c.connection_id),
|
||||
);
|
||||
const second = new Set(secondLists.flat().map((c: any) => c.connection_id));
|
||||
setDegree(second.has(resolvedId) ? "2nd" : "3rd+");
|
||||
}
|
||||
} else if (me && resolvedId && me === resolvedId) {
|
||||
|
|
@ -547,14 +558,22 @@ const ProfilePassport = () => {
|
|||
)}
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 text-sm text-slate-300">
|
||||
<Badge variant="outline" className="border-slate-700/70 bg-slate-900/40">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="border-slate-700/70 bg-slate-900/40"
|
||||
>
|
||||
Followers: {followStats.followers}
|
||||
</Badge>
|
||||
<Badge variant="outline" className="border-slate-700/70 bg-slate-900/40">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="border-slate-700/70 bg-slate-900/40"
|
||||
>
|
||||
Following: {followStats.following}
|
||||
</Badge>
|
||||
{degree && (
|
||||
<Badge className="bg-aethex-500/20 text-aethex-100">{degree} degree</Badge>
|
||||
<Badge className="bg-aethex-500/20 text-aethex-100">
|
||||
{degree} degree
|
||||
</Badge>
|
||||
)}
|
||||
{profile.github_url && (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -911,14 +911,19 @@ export function createServer() {
|
|||
.from("community_post_likes")
|
||||
.select("post_id", { count: "exact", head: true })
|
||||
.eq("post_id", postId);
|
||||
const count = (c as any)?.length ? (c as any).length : (c as any)?.count || null;
|
||||
const count = (c as any)?.length
|
||||
? (c as any).length
|
||||
: (c as any)?.count || null;
|
||||
if (typeof count === "number") {
|
||||
await adminSupabase
|
||||
.from("community_posts")
|
||||
.update({ likes_count: count })
|
||||
.eq("id", postId);
|
||||
}
|
||||
return res.json({ ok: true, likes: typeof count === "number" ? count : undefined });
|
||||
return res.json({
|
||||
ok: true,
|
||||
likes: typeof count === "number" ? count : undefined,
|
||||
});
|
||||
} catch (e: any) {
|
||||
return res.status(500).json({ error: e?.message || String(e) });
|
||||
}
|
||||
|
|
@ -939,14 +944,19 @@ export function createServer() {
|
|||
.from("community_post_likes")
|
||||
.select("post_id", { count: "exact", head: true })
|
||||
.eq("post_id", postId);
|
||||
const count = (c as any)?.length ? (c as any).length : (c as any)?.count || null;
|
||||
const count = (c as any)?.length
|
||||
? (c as any).length
|
||||
: (c as any)?.count || null;
|
||||
if (typeof count === "number") {
|
||||
await adminSupabase
|
||||
.from("community_posts")
|
||||
.update({ likes_count: count })
|
||||
.eq("id", postId);
|
||||
}
|
||||
return res.json({ ok: true, likes: typeof count === "number" ? count : undefined });
|
||||
return res.json({
|
||||
ok: true,
|
||||
likes: typeof count === "number" ? count : undefined,
|
||||
});
|
||||
} catch (e: any) {
|
||||
return res.status(500).json({ error: e?.message || String(e) });
|
||||
}
|
||||
|
|
@ -958,7 +968,9 @@ export function createServer() {
|
|||
try {
|
||||
const { data, error } = await adminSupabase
|
||||
.from("community_comments")
|
||||
.select("*, user_profiles:user_id ( id, full_name, username, avatar_url )")
|
||||
.select(
|
||||
"*, user_profiles:user_id ( id, full_name, username, avatar_url )",
|
||||
)
|
||||
.eq("post_id", postId)
|
||||
.order("created_at", { ascending: true });
|
||||
if (error) return res.status(500).json({ error: error.message });
|
||||
|
|
@ -1077,14 +1089,12 @@ export function createServer() {
|
|||
title: string,
|
||||
message?: string,
|
||||
) => {
|
||||
await adminSupabase
|
||||
.from("notifications")
|
||||
.insert({
|
||||
user_id: userId,
|
||||
type: "info",
|
||||
title,
|
||||
message: message || null,
|
||||
});
|
||||
await adminSupabase.from("notifications").insert({
|
||||
user_id: userId,
|
||||
type: "info",
|
||||
title,
|
||||
message: message || null,
|
||||
});
|
||||
};
|
||||
|
||||
// Notify explicit targets
|
||||
|
|
|
|||
Loading…
Reference in a new issue