feat: volume settings

This commit is contained in:
Face 2025-05-29 17:41:09 +03:00
parent 8a69bbca88
commit 95df713b06
16 changed files with 1794 additions and 112 deletions

View file

@ -64,6 +64,8 @@ export const auth = betterAuth({
banReason: { type: "string", required: false, input: false },
baseCurrencyBalance: { type: "string", required: false, input: false },
bio: { type: "string", required: false },
volumeMaster: { type: "string", required: false, input: false },
volumeMuted: { type: "boolean", required: false, input: false },
}
},
session: {

View file

@ -162,6 +162,8 @@
import confetti from 'canvas-confetti';
import { toast } from 'svelte-sonner';
import { formatValue, playSound, showConfetti } from '$lib/utils';
import { volumeSettings } from '$lib/stores/volume-settings';
import { onMount } from 'svelte';
interface CoinflipResult {
won: boolean;
@ -311,6 +313,10 @@
activeSoundTimeouts = [];
}
}
onMount(() => {
volumeSettings.load();
});
</script>
<Card>

View file

@ -11,6 +11,8 @@
import confetti from 'canvas-confetti';
import { toast } from 'svelte-sonner';
import { formatValue, playSound, showConfetti, showSchoolPrideCannons } from '$lib/utils';
import { volumeSettings } from '$lib/stores/volume-settings';
import { onMount } from 'svelte';
interface SlotsResult {
won: boolean;
@ -208,6 +210,10 @@
}
}
});
onMount(() => {
volumeSettings.load();
});
</script>
<Card>

View file

@ -0,0 +1,7 @@
import Root from "./slider.svelte";
export {
Root,
//
Root as Slider,
};

View file

@ -0,0 +1,52 @@
<script lang="ts">
import { Slider as SliderPrimitive } from "bits-ui";
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
let {
ref = $bindable(null),
value = $bindable(),
orientation = "horizontal",
class: className,
...restProps
}: WithoutChildrenOrChild<SliderPrimitive.RootProps> = $props();
</script>
<!--
Discriminated Unions + Destructing (required for bindable) do not
get along, so we shut typescript up by casting `value` to `never`.
-->
<SliderPrimitive.Root
bind:ref
bind:value={value as never}
data-slot="slider"
{orientation}
class={cn(
"relative flex w-full touch-none select-none items-center data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col data-[disabled]:opacity-50",
className
)}
{...restProps}
>
{#snippet children({ thumbs })}
<span
data-orientation={orientation}
data-slot="slider-track"
class={cn(
"bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-1.5"
)}
>
<SliderPrimitive.Range
data-slot="slider-range"
class={cn(
"bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
)}
/>
</span>
{#each thumbs as thumb (thumb)}
<SliderPrimitive.Thumb
data-slot="slider-thumb"
index={thumb}
class="border-primary bg-background ring-ring/50 focus-visible:outline-hidden block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 disabled:pointer-events-none disabled:opacity-50"
/>
{/each}
{/snippet}
</SliderPrimitive.Root>

View file

@ -21,6 +21,9 @@ export const user = pgTable("user", {
}).notNull().default("10000.00000000"), // 10,000 *BUSS
bio: varchar("bio", { length: 160 }).default("Hello am 48 year old man from somalia. Sorry for my bed england. I selled my wife for internet connection for play “conter stirk”"),
username: varchar("username", { length: 30 }).notNull().unique(),
volumeMaster: decimal("volume_master", { precision: 3, scale: 2 }).notNull().default("0.70"),
volumeMuted: boolean("volume_muted").notNull().default(false),
lastRewardClaim: timestamp("last_reward_claim", { withTimezone: true }),
totalRewardsClaimed: decimal("total_rewards_claimed", {

View file

@ -13,6 +13,9 @@ export type User = {
baseCurrencyBalance: number;
bio: string;
volumeMaster: number;
volumeMuted: boolean;
} | null;
export const USER_DATA = writable<User>(undefined);

View file

@ -0,0 +1,53 @@
import { writable } from 'svelte/store';
import { browser } from '$app/environment';
export interface VolumeSettings {
master: number;
muted: boolean;
}
const defaultSettings: VolumeSettings = {
master: 0.7,
muted: false
};
function createVolumeSettings() {
const { subscribe, set, update } = writable<VolumeSettings>(defaultSettings);
return {
subscribe,
load: () => {
if (browser) {
const stored = localStorage.getItem('volume-settings');
if (stored) {
try {
const settings = JSON.parse(stored);
set({ ...defaultSettings, ...settings });
} catch (e) {
console.error('Failed to parse volume settings:', e);
}
}
}
},
setMaster: (value: number) => {
update(settings => {
const newSettings = { ...settings, master: Math.max(0, Math.min(1, value)) };
if (browser) {
localStorage.setItem('volume-settings', JSON.stringify(newSettings));
}
return newSettings;
});
},
setMuted: (value: boolean) => {
update(settings => {
const newSettings = { ...settings, muted: value };
if (browser) {
localStorage.setItem('volume-settings', JSON.stringify(newSettings));
}
return newSettings;
});
}
};
}
export const volumeSettings = createVolumeSettings();

View file

@ -1,6 +1,8 @@
import { PUBLIC_B2_BUCKET, PUBLIC_B2_ENDPOINT } from "$env/static/public";
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
import { volumeSettings } from '$lib/stores/volume-settings';
import { get } from 'svelte/store';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
@ -235,16 +237,13 @@ export function getTimeframeInSeconds(timeframe: string): number {
let availableSounds = [1, 2, 3, 4, 5, 6, 7];
export function playRandomFireworkSound() {
// If no sounds available, reset the array
if (availableSounds.length === 0) {
availableSounds = [1, 2, 3, 4, 5, 6, 7];
}
// Pick a random sound from available ones
const randomIndex = Math.floor(Math.random() * availableSounds.length);
const soundNumber = availableSounds[randomIndex];
// Remove the sound from available array to prevent repetition
availableSounds = availableSounds.filter((_, index) => index !== randomIndex);
playSound(`firework${soundNumber}`);
@ -252,8 +251,14 @@ export function playRandomFireworkSound() {
export function playSound(sound: string) {
try {
const audio = new Audio(`sound/${sound}.mp3`);
audio.volume = 0.3; // TODO: volume control
const settings = get(volumeSettings);
if (settings.muted) {
return;
}
const audio = new Audio(`/sound/${sound}.mp3`);
audio.volume = Math.max(0, Math.min(1, settings.master));
audio.play().catch(console.error);
} catch (error) {
console.error('Error playing sound:', error);