feat(profile): add privacy controls and progressive photo gallery
CI / check (push) Failing after 53s
CI / deploy (push) Skipped
CI / publish-container (push) Skipped

This commit is contained in:
Simo committed 2026-09-11 00:31:29 +02:00
1 parent ba9c61d808
commit 74223984dc
13 files changed
+764 -167

No files matched your search

+65
View File
@@ -0,0 +1,65 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const state = vi.hoisted(() => ({
fail: false,
rows: [] as unknown[][],
query: "",
params: [] as unknown[],
}));
vi.mock("@/lib/db", async () => {
const { drizzle } = await import("drizzle-orm/mysql-proxy");
return {
db: drizzle(async (query, params) => {
state.query = query;
state.params = params;
if (state.fail) throw new Error("offline");
return { rows: state.rows };
}),
};
});
import { loadProfilePrivacy } from "./profile-privacy";
describe("profile privacy", () => {
beforeEach(() => {
state.fail = false;
state.rows = [];
});
it("defaults the wallet to private while retaining public social sections", async () => {
expect(await loadProfilePrivacy(42)).toEqual({
unavailable: false,
values: {
wallet: false,
online: true,
friends: true,
photos: true,
registered: true,
},
});
expect(state.params).toEqual([42, 1]);
});
it("fails closed if saved visibility cannot be read", async () => {
state.fail = true;
expect(await loadProfilePrivacy(42)).toEqual({
unavailable: true,
values: {
wallet: false,
online: false,
friends: false,
photos: false,
registered: false,
},
});
});
it("reads the specific profile owners explicit visibility choices", async () => {
state.rows = [[42, 1, 0, 0, 0, 0]];
const result = await loadProfilePrivacy(42);
expect(result.values).toMatchObject({
wallet: true,
online: false,
friends: false,
photos: false,
registered: false,
});
});
});
+42
View File
@@ -0,0 +1,42 @@
import "server-only";
import { eq } from "drizzle-orm";
import { WebsiteProfilePrivacy } from "@/db/profile-privacy";
import { db } from "@/lib/db";
export const profilePrivacyKeys = [
"wallet",
"online",
"friends",
"photos",
"registered",
] as const;
export type ProfilePrivacy = Record<
(typeof profilePrivacyKeys)[number],
boolean
>;
export const defaultProfilePrivacy: ProfilePrivacy = {
wallet: false,
online: true,
friends: true,
photos: true,
registered: true,
};
const hiddenProfilePrivacy: ProfilePrivacy = {
wallet: false,
online: false,
friends: false,
photos: false,
registered: false,
};
export async function loadProfilePrivacy(userId: number) {
try {
const [row] = await db
.select()
.from(WebsiteProfilePrivacy)
.where(eq(WebsiteProfilePrivacy.userId, userId))
.limit(1);
return { values: row ?? defaultProfilePrivacy, unavailable: false };
} catch {
// A failed privacy lookup must never expose fields whose visibility is unknown.
return { values: hiddenProfilePrivacy, unavailable: true };
}
}