1
0
Fork 0

feat(preferences): add background texture toggle

This commit is contained in:
Jason Fraley 2026-08-28 01:20:12 -04:00
parent a6b06569da
commit 1f2f7b6269
10 changed files with 137 additions and 78 deletions

View file

@ -205,6 +205,7 @@ export async function updatePreferences(input: {
mutedTypes: string[];
showCallsign: boolean;
showVersionOverlay: boolean;
showBackgroundTexture: boolean;
leadOrActual: "lead" | "actual";
discordEnabled: boolean;
discordMutedTypes: string[];
@ -221,7 +222,7 @@ export async function updatePreferences(input: {
(MUTEABLE_NOTIFICATION_VALUES as readonly string[]).includes(t),
);
await payload.update({
await payload.update({
collection: "users",
id: userId,
data: {
@ -230,6 +231,7 @@ export async function updatePreferences(input: {
display: {
showCallsign: input.showCallsign,
showVersionOverlay: input.showVersionOverlay,
showBackgroundTexture: input.showBackgroundTexture,
leadOrActual: input.leadOrActual,
xpDisplayMini: input.xpDisplayMini,
},

View file

@ -81,24 +81,25 @@ export default async function AccountPage() {
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Preferences</CardTitle>
<CardDescription>Control in-app notifications and display options.</CardDescription>
</CardHeader>
<CardContent>
<PreferencesForm
mutedAll={notifications?.mutedAll ?? false}
mutedTypes={notifications?.mutedTypes ?? []}
showCallsign={fullUser.preferences?.display?.showCallsign ?? false}
showVersionOverlay={fullUser.preferences?.display?.showVersionOverlay ?? true}
leadOrActual={fullUser.preferences?.display?.leadOrActual ?? "lead"}
discordEnabled={discord?.enabled ?? false}
discordMutedTypes={discord?.mutedTypes ?? []}
xpDisplayMini={fullUser.preferences?.display?.xpDisplayMini ?? false}
/>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Preferences</CardTitle>
<CardDescription>Control in-app notifications and display options.</CardDescription>
</CardHeader>
<CardContent>
<PreferencesForm
mutedAll={notifications?.mutedAll ?? false}
mutedTypes={notifications?.mutedTypes ?? []}
showCallsign={fullUser.preferences?.display?.showCallsign ?? false}
showVersionOverlay={fullUser.preferences?.display?.showVersionOverlay ?? true}
showBackgroundTexture={fullUser.preferences?.display?.showBackgroundTexture ?? true}
leadOrActual={fullUser.preferences?.display?.leadOrActual ?? "lead"}
discordEnabled={discord?.enabled ?? false}
discordMutedTypes={discord?.mutedTypes ?? []}
xpDisplayMini={fullUser.preferences?.display?.xpDisplayMini ?? false}
/>
</CardContent>
</Card>
</div>
</div>
);

View file

@ -133,7 +133,13 @@ export default async function RootLayout(props: { children: React.ReactNode }) {
preferences={user.preferences}
className="border-r"
/>
<SidebarInset>
<SidebarInset
className={
user.preferences?.display?.showBackgroundTexture !== false
? "app-background-texture"
: undefined
}
>
<SiteHeader />
{impersonating && <ImpersonationBanner />}
{children}
@ -156,11 +162,7 @@ export default async function RootLayout(props: { children: React.ReactNode }) {
{user && <GameTickRealtime />}
{isLogistics && <ShipmentToasts />}
<Toaster />
<ShimLoader
shims={shimEntries}
userId={user?.id ?? null}
userRoles={user?.roles ?? []}
/>
<ShimLoader shims={shimEntries} userId={user?.id ?? null} userRoles={user?.roles ?? []} />
</body>
</html>
);

View file

@ -268,41 +268,47 @@ export const Users: CollectionConfig = {
},
],
},
{
name: "display",
label: "Display",
type: "group",
fields: [
{
name: "showCallsign",
label: "Show callsign (username) instead of display name in the sidebar",
type: "checkbox",
defaultValue: false,
},
{
name: "leadOrActual",
label: 'Use "Lead" or "Actual" when reviewing the unit roster',
type: "select",
options: [
{ label: "Lead", value: "lead" },
{ label: "Actual", value: "actual" },
],
defaultValue: "lead",
},
{
name: "showVersionOverlay",
label: "Show version overlay at top of page",
type: "checkbox",
defaultValue: true,
},
{
name: "xpDisplayMini",
label: "Use mini mode for XP display",
type: "checkbox",
defaultValue: false,
},
],
},
{
name: "display",
label: "Display",
type: "group",
fields: [
{
name: "showCallsign",
label: "Show callsign (username) instead of display name in the sidebar",
type: "checkbox",
defaultValue: false,
},
{
name: "leadOrActual",
label: 'Use "Lead" or "Actual" when reviewing the unit roster',
type: "select",
options: [
{ label: "Lead", value: "lead" },
{ label: "Actual", value: "actual" },
],
defaultValue: "lead",
},
{
name: "showVersionOverlay",
label: "Show version overlay at top of page",
type: "checkbox",
defaultValue: true,
},
{
name: "xpDisplayMini",
label: "Use mini mode for XP display",
type: "checkbox",
defaultValue: false,
},
{
name: "showBackgroundTexture",
label: "Show diagonal background texture",
type: "checkbox",
defaultValue: true,
},
],
},
{
name: "discord",
label: "Discord",

View file

@ -17,6 +17,7 @@ interface PreferencesFormProps {
readonly mutedTypes: readonly string[];
readonly showCallsign: boolean;
readonly showVersionOverlay: boolean;
readonly showBackgroundTexture: boolean;
readonly leadOrActual: "lead" | "actual";
readonly discordEnabled: boolean;
readonly discordMutedTypes: readonly string[];
@ -28,6 +29,7 @@ export function PreferencesForm({
mutedTypes: initialMutedTypes,
showCallsign: initialShowCallsign,
showVersionOverlay: initialShowVersionOverlay,
showBackgroundTexture: initialShowBackgroundTexture,
leadOrActual: initialLeadOrActual,
discordEnabled: initialDiscordEnabled,
discordMutedTypes: initialDiscordMutedTypes,
@ -38,6 +40,7 @@ export function PreferencesForm({
const [mutedTypes, setMutedTypes] = useState<string[]>(() => [...initialMutedTypes]);
const [showCallsign, setShowCallsign] = useState(initialShowCallsign);
const [showVersionOverlay, setShowVersionOverlay] = useState(initialShowVersionOverlay);
const [showBackgroundTexture, setShowBackgroundTexture] = useState(initialShowBackgroundTexture);
const [leadOrActual, setLeadOrActual] = useState<"lead" | "actual">(initialLeadOrActual);
const [discordEnabled, setDiscordEnabled] = useState(initialDiscordEnabled);
const [discordMutedTypes, setDiscordMutedTypes] = useState<string[]>(() => [
@ -72,6 +75,7 @@ export function PreferencesForm({
mutedTypes,
showCallsign,
showVersionOverlay,
showBackgroundTexture,
leadOrActual,
discordEnabled,
discordMutedTypes,
@ -104,10 +108,7 @@ export function PreferencesForm({
<CardDescription>Choose what appears in the app shell and roster views.</CardDescription>
</CardHeader>
<CardContent className="flex flex-col gap-4">
<Label
htmlFor="showCallsign"
className="flex items-center justify-between gap-4 text-sm"
>
<Label htmlFor="showCallsign" className="flex items-center justify-between gap-4 text-sm">
<span className="flex min-w-0 flex-col gap-1">
<span>Show callsign instead of display name</span>
<span className="text-sm font-normal text-muted-foreground">
@ -120,9 +121,12 @@ export function PreferencesForm({
onCheckedChange={setShowCallsign}
aria-label="Show callsign instead of display name"
/>
</Label>
</Label>
<Label htmlFor="xpDisplayMini" className="flex items-center justify-between gap-4 text-sm">
<Label
htmlFor="xpDisplayMini"
className="flex items-center justify-between gap-4 text-sm"
>
<span className="flex min-w-0 flex-col gap-1">
<span>Use mini mode for XP display</span>
<span className="text-sm font-normal text-muted-foreground">
@ -137,6 +141,24 @@ export function PreferencesForm({
/>
</Label>
<Label
htmlFor="showBackgroundTexture"
className="flex items-center justify-between gap-4 text-sm"
>
<span className="flex min-w-0 flex-col gap-1">
<span>Show diagonal background texture</span>
<span className="text-sm font-normal text-muted-foreground">
Adds the subtle tactical line pattern behind page content.
</span>
</span>
<Switch
id="showBackgroundTexture"
checked={showBackgroundTexture}
onCheckedChange={setShowBackgroundTexture}
aria-label="Show diagonal background texture"
/>
</Label>
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-1">
<span className="text-sm font-medium">Roster leader label</span>

View file

@ -194,6 +194,7 @@ export function AppSidebar({
mutedTypes: preferences?.notifications?.mutedTypes ?? [],
showCallsign: preferences?.display?.showCallsign ?? false,
showVersionOverlay: preferences?.display?.showVersionOverlay ?? true,
showBackgroundTexture: preferences?.display?.showBackgroundTexture ?? true,
leadOrActual: preferences?.display?.leadOrActual ?? "lead",
discordEnabled: preferences?.discord?.enabled ?? false,
discordMutedTypes: preferences?.discord?.mutedTypes ?? [],

View file

@ -0,0 +1,15 @@
import { MigrateDownArgs, MigrateUpArgs, sql } from "@payloadcms/db-postgres";
export async function up({ db }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "users"
ADD COLUMN "preferences_display_show_background_texture" boolean DEFAULT true;
`);
}
export async function down({ db }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
ALTER TABLE "users"
DROP COLUMN "preferences_display_show_background_texture";
`);
}

View file

@ -1,39 +1,45 @@
import * as migration_20260820_004157 from './20260820_004157';
import * as migration_20260825_220000 from './20260825_220000';
import * as migration_20260826_050000_repair_shims_auxiliary_tables from './20260826_050000_repair_shims_auxiliary_tables';
import * as migration_20260826_060000_add_game_servers_event_target from './20260826_060000_add_game_servers_event_target';
import * as migration_20260827_005757 from './20260827_005757';
import * as migration_20260827_022334_add_eod_minesweeper_best_flags from './20260827_022334_add_eod_minesweeper_best_flags';
import * as migration_20260820_004157 from "./20260820_004157";
import * as migration_20260825_220000 from "./20260825_220000";
import * as migration_20260826_050000_repair_shims_auxiliary_tables from "./20260826_050000_repair_shims_auxiliary_tables";
import * as migration_20260826_060000_add_game_servers_event_target from "./20260826_060000_add_game_servers_event_target";
import * as migration_20260827_005757 from "./20260827_005757";
import * as migration_20260827_022334_add_eod_minesweeper_best_flags from "./20260827_022334_add_eod_minesweeper_best_flags";
import * as migration_20260828_011500_add_background_texture_preference from "./20260828_011500_add_background_texture_preference";
export const migrations = [
{
up: migration_20260820_004157.up,
down: migration_20260820_004157.down,
name: '20260820_004157',
name: "20260820_004157",
},
{
up: migration_20260825_220000.up,
down: migration_20260825_220000.down,
name: '20260825_220000',
name: "20260825_220000",
},
{
up: migration_20260826_050000_repair_shims_auxiliary_tables.up,
down: migration_20260826_050000_repair_shims_auxiliary_tables.down,
name: '20260826_050000_repair_shims_auxiliary_tables',
name: "20260826_050000_repair_shims_auxiliary_tables",
},
{
up: migration_20260826_060000_add_game_servers_event_target.up,
down: migration_20260826_060000_add_game_servers_event_target.down,
name: '20260826_060000_add_game_servers_event_target',
name: "20260826_060000_add_game_servers_event_target",
},
{
up: migration_20260827_005757.up,
down: migration_20260827_005757.down,
name: '20260827_005757',
name: "20260827_005757",
},
{
up: migration_20260827_022334_add_eod_minesweeper_best_flags.up,
down: migration_20260827_022334_add_eod_minesweeper_best_flags.down,
name: '20260827_022334_add_eod_minesweeper_best_flags'
name: "20260827_022334_add_eod_minesweeper_best_flags",
},
{
up: migration_20260828_011500_add_background_texture_preference.up,
down: migration_20260828_011500_add_background_texture_preference.down,
name: "20260828_011500_add_background_texture_preference",
},
];

View file

@ -1174,6 +1174,7 @@ export interface User {
leadOrActual?: ('lead' | 'actual') | null;
showVersionOverlay?: boolean | null;
xpDisplayMini?: boolean | null;
showBackgroundTexture?: boolean | null;
};
discord?: {
enabled?: boolean | null;
@ -3366,6 +3367,7 @@ export interface UsersSelect<T extends boolean = true> {
leadOrActual?: T;
showVersionOverlay?: T;
xpDisplayMini?: T;
showBackgroundTexture?: T;
};
discord?:
| T

View file

@ -37,6 +37,7 @@ describe("PreferencesForm", () => {
mutedTypes: ["market:offer"],
showCallsign: false,
showVersionOverlay: true,
showBackgroundTexture: true,
leadOrActual: "lead",
discordEnabled: false,
discordMutedTypes: [],
@ -62,6 +63,7 @@ describe("PreferencesForm", () => {
mutedTypes: ["market:offer"],
showCallsign: true,
showVersionOverlay: true,
showBackgroundTexture: true,
leadOrActual: "actual",
discordEnabled: true,
discordMutedTypes: ["market:offer"],