c5d3ac01ca
- Group polls with option/rollcall modes, edit by creator, auto-settle - Multimedia memories with upload, preview, inline video playback - In-app notifications for poll/team/group events - Points system and group stats dashboard - Group detail tabs with icons (activity/polls/memories/stats) - Fix: nginx file upload size, static cache blocking API, timezone, auto-cancel Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
93 lines
2.5 KiB
TypeScript
93 lines
2.5 KiB
TypeScript
import { pb } from './pocketbase'
|
|
import type { PointLog, PointAction } from '@/types'
|
|
|
|
const POINT_MAP: Record<PointAction, number> = {
|
|
vote: 1,
|
|
team: 2,
|
|
memory: 1
|
|
}
|
|
|
|
export async function awardPoints(action: PointAction, relatedId: string): Promise<void> {
|
|
const user = pb.authStore.model
|
|
if (!user) return
|
|
|
|
const existing = await pb.collection('point_logs').getList(1, 1, {
|
|
filter: `user="${user.id}" && action="${action}" && relatedId="${relatedId}"`,
|
|
$autoCancel: false
|
|
})
|
|
if (existing.items.length > 0) return
|
|
|
|
const points = POINT_MAP[action]
|
|
|
|
try {
|
|
await pb.collection('point_logs').create({
|
|
user: user.id,
|
|
action,
|
|
points,
|
|
relatedId
|
|
})
|
|
} catch (error: any) {
|
|
if (error?.response?.data?.user || error?.message?.includes('unique')) {
|
|
return
|
|
}
|
|
throw error
|
|
}
|
|
|
|
const currentUser = await pb.collection('users').getOne(user.id)
|
|
await pb.collection('users').update(user.id, {
|
|
points: ((currentUser as any).points || 0) + points
|
|
})
|
|
}
|
|
|
|
export async function deductPoints(action: PointAction, relatedId: string): Promise<void> {
|
|
const user = pb.authStore.model
|
|
if (!user) return
|
|
|
|
const existing = await pb.collection('point_logs').getList(1, 1, {
|
|
filter: `user="${user.id}" && action="${action}" && relatedId="${relatedId}"`,
|
|
$autoCancel: false
|
|
})
|
|
|
|
if (existing.items.length === 0) return
|
|
const log = existing.items[0]
|
|
|
|
try {
|
|
await pb.collection('point_logs').delete(log.id)
|
|
} catch (error: any) {
|
|
if (error?.status !== 404) {
|
|
throw error
|
|
}
|
|
}
|
|
|
|
const pointsToDeduct = POINT_MAP[action]
|
|
const currentUser = await pb.collection('users').getOne(user.id)
|
|
const currentPoints = (currentUser as any).points || 0
|
|
const newPoints = Math.max(0, currentPoints - pointsToDeduct)
|
|
|
|
await pb.collection('users').update(user.id, {
|
|
points: newPoints
|
|
})
|
|
}
|
|
|
|
export async function getUserPointLogs(userId: string): Promise<PointLog[]> {
|
|
const result = await pb.collection('point_logs').getList(1, 100, {
|
|
filter: `user="${userId}"`,
|
|
sort: '-created',
|
|
$autoCancel: false
|
|
})
|
|
return result.items as unknown as PointLog[]
|
|
}
|
|
|
|
export async function getGroupMemberRanking(groupId: string, limit = 20) {
|
|
const group = await pb.collection('groups').getOne(groupId, {
|
|
expand: 'members',
|
|
$autoCancel: false
|
|
}) as any
|
|
|
|
const members: any[] = group.expand?.members || []
|
|
return members
|
|
.map((m: any) => ({ userId: m.id, points: m.points || 0, name: m.name || m.username }))
|
|
.sort((a: any, b: any) => b.points - a.points)
|
|
.slice(0, limit)
|
|
}
|