Backend (voice-call groundwork, no call engine yet): - Profile.callsEnabled (Boolean default true) + migration - canCall(caller,callee): mutual-follow AND callee.callsEnabled — server-side hard guard - POST /api/me/calls-enabled (opt-out toggle), GET /api/chat/can-call/:userId - expose callsEnabled in /api/auth/me Frontend: - "Allow calls" toggle in Profile privacy section (default on, optimistic+rollback) - Me.callsEnabled + i18n DE/EN/FR/AR Bundled DM UI work from this session: - image lightbox is now a swipeable carousel over all shared images (+ counter) - keyboard stays open after sending (input ref refocus) - voice notes: Instagram-style waveforms (own=white/mint, other=black/grey), removed the blue progress dot; lazy-load expo-media-library with clean fallback - expo-linear-gradient + expo-media-library deps Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
105 lines
3.2 KiB
TypeScript
105 lines
3.2 KiB
TypeScript
import { usePrisma } from "../utils/prisma";
|
|
|
|
export async function getFollowRelation(
|
|
followerId: string,
|
|
followingId: string,
|
|
) {
|
|
const db = usePrisma();
|
|
return db.userFollow.findUnique({
|
|
where: { followerId_followingId: { followerId, followingId } },
|
|
});
|
|
}
|
|
|
|
export async function createFollow(followerId: string, followingId: string) {
|
|
const db = usePrisma();
|
|
const exists = await db.userFollow.findUnique({
|
|
where: { followerId_followingId: { followerId, followingId } },
|
|
});
|
|
if (exists) return;
|
|
await db.userFollow.create({ data: { followerId, followingId } });
|
|
await db.profile.update({
|
|
where: { id: followingId },
|
|
data: { followersCount: { increment: 1 } },
|
|
});
|
|
}
|
|
|
|
export async function deleteFollow(followerId: string, followingId: string) {
|
|
const db = usePrisma();
|
|
const exists = await db.userFollow.findUnique({
|
|
where: { followerId_followingId: { followerId, followingId } },
|
|
});
|
|
if (!exists) return;
|
|
await db.userFollow.delete({
|
|
where: { followerId_followingId: { followerId, followingId } },
|
|
});
|
|
// Nie unter 0
|
|
const profile = await db.profile.findUnique({
|
|
where: { id: followingId },
|
|
select: { followersCount: true },
|
|
});
|
|
if (profile && profile.followersCount > 0) {
|
|
await db.profile.update({
|
|
where: { id: followingId },
|
|
data: { followersCount: { decrement: 1 } },
|
|
});
|
|
}
|
|
}
|
|
|
|
/** Gibt ein Set der userIds zurück, denen followerId bereits folgt (Batch-Variante). */
|
|
export async function getFollowingSet(
|
|
followerId: string,
|
|
followingIds: string[],
|
|
): Promise<Set<string>> {
|
|
if (followingIds.length === 0) return new Set();
|
|
const db = usePrisma();
|
|
const rows = await db.userFollow.findMany({
|
|
where: { followerId, followingId: { in: followingIds } },
|
|
select: { followingId: true },
|
|
});
|
|
return new Set(rows.map((r) => r.followingId));
|
|
}
|
|
|
|
export async function getProfileWithFollowers(userId: string) {
|
|
const db = usePrisma();
|
|
return db.profile.findUnique({
|
|
where: { id: userId },
|
|
select: {
|
|
followersCount: true,
|
|
username: true,
|
|
avatar: true,
|
|
nickname: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Voice-Call erlaubt? Nur wenn (1) beide sich GEGENSEITIG folgen UND (2) der
|
|
* Angerufene Anrufe nicht deaktiviert hat (callsEnabled). Server-seitige
|
|
* Hard-Schranke — die UI blendet den Call-Button zwar aus, aber der Call-Start
|
|
* MUSS das hier zusätzlich prüfen.
|
|
*/
|
|
export async function canCall(
|
|
callerId: string,
|
|
calleeId: string,
|
|
): Promise<boolean> {
|
|
if (!callerId || !calleeId || callerId === calleeId) return false;
|
|
const db = usePrisma();
|
|
const [callerFollowsCallee, calleeFollowsCaller, callee] = await Promise.all([
|
|
db.userFollow.findUnique({
|
|
where: { followerId_followingId: { followerId: callerId, followingId: calleeId } },
|
|
select: { followerId: true },
|
|
}),
|
|
db.userFollow.findUnique({
|
|
where: { followerId_followingId: { followerId: calleeId, followingId: callerId } },
|
|
select: { followerId: true },
|
|
}),
|
|
db.profile.findUnique({
|
|
where: { id: calleeId },
|
|
select: { callsEnabled: true },
|
|
}),
|
|
]);
|
|
return (
|
|
!!callerFollowsCallee && !!calleeFollowsCaller && (callee?.callsEnabled ?? true)
|
|
);
|
|
}
|