fix: await Discord SDK voice subscribes and add missing Activity API endpoints
Some checks failed
Build / build (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Lint & Type Check / lint (push) Has been cancelled
Security Scan / semgrep (push) Has been cancelled
Security Scan / dependency-check (push) Has been cancelled
Test / test (18.x) (push) Has been cancelled
Test / test (20.x) (push) Has been cancelled

- Await SPEAKING_START/SPEAKING_STOP subscribes so try-catch handles 4006 scope errors
- Add GET /api/feed and POST /api/feed/:id/like (community posts alias for Activity)
- Add DELETE /api/activity/polls/:id (soft-delete via is_active=false)
- Add POST /api/activity/challenges/:id/claim (marks challenge progress as completed)
- Add GET /api/activity/badges (all badges, no userId required)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
AeThex 2026-04-15 02:24:32 +00:00
parent be30e01a50
commit 1a4321a531
2 changed files with 95 additions and 4 deletions

View file

@ -284,7 +284,7 @@ export const DiscordActivityProvider: React.FC<
// Subscribe to speaking updates if in voice channel // Subscribe to speaking updates if in voice channel
if (sdk.channelId) { if (sdk.channelId) {
try { try {
sdk.subscribe("SPEAKING_START", (data: any) => { await sdk.subscribe("SPEAKING_START", (data: any) => {
console.log("[Discord Activity] Speaking start:", data); console.log("[Discord Activity] Speaking start:", data);
if (data?.user_id) { if (data?.user_id) {
setSpeakingUsers(prev => new Set(prev).add(data.user_id)); setSpeakingUsers(prev => new Set(prev).add(data.user_id));
@ -294,7 +294,7 @@ export const DiscordActivityProvider: React.FC<
} }
}, { channel_id: sdk.channelId }); }, { channel_id: sdk.channelId });
sdk.subscribe("SPEAKING_STOP", (data: any) => { await sdk.subscribe("SPEAKING_STOP", (data: any) => {
console.log("[Discord Activity] Speaking stop:", data); console.log("[Discord Activity] Speaking stop:", data);
if (data?.user_id) { if (data?.user_id) {
setSpeakingUsers(prev => { setSpeakingUsers(prev => {

View file

@ -2817,6 +2817,41 @@ export function createServer() {
} }
}); });
// Activity feed alias (used by Discord Activity)
app.get("/api/feed", async (req, res) => {
const limit = Math.max(1, Math.min(50, Number(req.query.limit) || 10));
try {
const { data, error } = await adminSupabase
.from("community_posts")
.select(`id, title, content, likes_count, author_id, created_at, user_profiles ( username, full_name, avatar_url )`)
.eq("is_published", true)
.order("created_at", { ascending: false })
.limit(limit);
if (error) return res.status(500).json({ error: error.message });
res.json({ data: data || [] });
} catch (e: any) {
res.status(500).json({ error: e?.message || String(e) });
}
});
app.post("/api/feed/:id/like", async (req, res) => {
const postId = req.params.id;
try {
const { data: post } = await adminSupabase
.from("community_posts")
.select("likes_count")
.eq("id", postId)
.single();
await adminSupabase
.from("community_posts")
.update({ likes_count: (post?.likes_count || 0) + 1 })
.eq("id", postId);
res.json({ ok: true });
} catch (e: any) {
res.status(500).json({ error: e?.message || String(e) });
}
});
app.get("/api/user/:id/posts", async (req, res) => { app.get("/api/user/:id/posts", async (req, res) => {
const userId = req.params.id; const userId = req.params.id;
try { try {
@ -3862,6 +3897,20 @@ export function createServer() {
} }
}); });
app.delete("/api/activity/polls/:id", async (req, res) => {
const pollId = req.params.id;
try {
const { error } = await adminSupabase
.from("activity_polls")
.update({ is_active: false })
.eq("id", pollId);
if (error) return res.status(500).json({ error: error.message });
res.json({ ok: true });
} catch (e: any) {
res.status(500).json({ error: e?.message || String(e) });
}
});
app.post("/api/activity/polls/:id/vote", async (req, res) => { app.post("/api/activity/polls/:id/vote", async (req, res) => {
const pollId = req.params.id; const pollId = req.params.id;
const { user_id, option_index } = req.body || {}; const { user_id, option_index } = req.body || {};
@ -3942,6 +3991,29 @@ export function createServer() {
} }
}); });
app.post("/api/activity/challenges/:id/claim", async (req, res) => {
const challengeId = req.params.id;
const { user_id } = req.body || {};
if (!user_id) return res.status(400).json({ error: "user_id required" });
try {
const { data, error } = await adminSupabase
.from("activity_challenge_progress")
.upsert({
challenge_id: challengeId,
user_id,
progress: 1,
completed: true,
completed_at: new Date().toISOString(),
}, { onConflict: "challenge_id,user_id" })
.select()
.single();
if (error) return res.status(500).json({ error: error.message });
res.json(data);
} catch (e: any) {
res.status(500).json({ error: e?.message || String(e) });
}
});
app.get("/api/activity/challenges/:id/progress", async (req, res) => { app.get("/api/activity/challenges/:id/progress", async (req, res) => {
const challengeId = req.params.id; const challengeId = req.params.id;
const user_id = req.query.user_id as string; const user_id = req.query.user_id as string;
@ -4053,6 +4125,25 @@ export function createServer() {
} }
}); });
// All badges (for Activity badges tab when no userId)
app.get("/api/activity/badges", async (req, res) => {
try {
const { data, error } = await adminSupabase
.from("badges")
.select("*")
.order("created_at", { ascending: true });
if (error) {
if (error.code === "42P01" || error.message?.includes("does not exist")) {
return res.json([]);
}
return res.status(500).json({ error: error.message });
}
res.json(data || []);
} catch (e: any) {
res.status(500).json({ error: e?.message || String(e) });
}
});
// User Badges (real data) // User Badges (real data)
app.get("/api/activity/badges/:userId", async (req, res) => { app.get("/api/activity/badges/:userId", async (req, res) => {
const userId = req.params.userId; const userId = req.params.userId;