app/Values/UserPreferences.php
<?php
namespace App\Values;
use Illuminate\Contracts\Support\Arrayable;
use JsonSerializable;
use Webmozart\Assert\Assert;
final class UserPreferences implements Arrayable, JsonSerializable
{
private const CASTS = [
'volume' => 'float',
'show_now_playing_notification' => 'boolean',
'confirm_before_closing' => 'boolean',
'transcode_on_mobile' => 'boolean',
'transcode_quality' => 'integer',
'show_album_art_overlay' => 'boolean',
'lyrics_zoom_level' => 'integer',
'make_uploads_public' => 'boolean',
'continuous_playback' => 'boolean',
];
private const CUSTOMIZABLE_KEYS = [
'volume',
'repeat_mode',
'equalizer',
'artists_view_mode',
'albums_view_mode',
'theme',
'show_now_playing_notification',
'confirm_before_closing',
'transcode_on_mobile',
'transcode_quality',
'show_album_art_overlay',
'make_uploads_public',
'support_bar_no_bugging',
'lyrics_zoom_level',
'visualizer',
'active_extra_panel_tab',
'continuous_playback',
];
private const ALL_KEYS = self::CUSTOMIZABLE_KEYS + ['lastfm_session_key'];
private function __construct(
public float $volume,
public string $repeatMode,
public Equalizer $equalizer,
public string $artistsViewMode,
public string $albumsViewMode,
public string $theme,
public bool $showNowPlayingNotification,
public bool $confirmBeforeClosing,
public bool $transcodeOnMobile,
public int $transcodeQuality,
public bool $showAlbumArtOverlay,
public bool $makeUploadsPublic,
public bool $supportBarNoBugging,
public bool $continuousPlayback,
public int $lyricsZoomLevel,
public string $visualizer,
public ?string $activeExtraPanelTab,
public ?string $lastFmSessionKey
) {
Assert::oneOf($this->repeatMode, ['NO_REPEAT', 'REPEAT_ALL', 'REPEAT_ONE']);
Assert::oneOf($this->artistsViewMode, ['list', 'thumbnails']);
Assert::oneOf($this->albumsViewMode, ['list', 'thumbnails']);
Assert::oneOf($this->activeExtraPanelTab, [null, 'Lyrics', 'Artist', 'Album', 'YouTube']);
if (!in_array($this->transcodeQuality, [64, 96, 128, 192, 256, 320], true)) {
$this->transcodeQuality = 128;
}
}
public static function fromArray(array $data): self
{
return new self(
volume: $data['volume'] ?? 7.0,
repeatMode: $data['repeat_mode'] ?? 'NO_REPEAT',
equalizer: isset($data['equalizer']) ? Equalizer::tryMake($data['equalizer']) : Equalizer::default(),
artistsViewMode: $data['artists_view_mode'] ?? 'thumbnails',
albumsViewMode: $data['albums_view_mode'] ?? 'thumbnails',
theme: $data['theme'] ?? 'classic',
showNowPlayingNotification: $data['show_now_playing_notification'] ?? true,
confirmBeforeClosing: $data['confirm_before_closing'] ?? false,
transcodeOnMobile: $data['transcode_on_mobile'] ?? true,
transcodeQuality: $data['transcode_quality'] ?? 128,
showAlbumArtOverlay: $data['show_album_art_overlay'] ?? true,
makeUploadsPublic: $data['make_uploads_public'] ?? false,
supportBarNoBugging: $data['support_bar_no_bugging'] ?? false,
continuousPlayback: $data['continuous_playback'] ?? false,
lyricsZoomLevel: $data['lyrics_zoom_level'] ?? 1,
visualizer: $data['visualizer'] ?? 'default',
activeExtraPanelTab: $data['active_extra_panel_tab'] ?? null,
lastFmSessionKey: $data['lastfm_session_key'] ?? null,
);
}
public static function customizable(string $key): bool
{
return in_array($key, self::CUSTOMIZABLE_KEYS, true);
}
public function set(string $key, mixed $value): self
{
self::assertValidKey($key);
$cast = self::CASTS[$key] ?? null;
$value = match ($cast) {
'boolean' => filter_var($value, FILTER_VALIDATE_BOOLEAN),
'integer' => (int) $value,
'float' => (float) $value,
default => $value,
};
$arr = $this->toArray();
$arr[$key] = $value;
return self::fromArray($arr);
}
public static function assertValidKey(string $key): void
{
Assert::inArray($key, self::ALL_KEYS);
}
/** @return array<mixed> */
public function toArray(): array
{
return [
'theme' => $this->theme,
'show_now_playing_notification' => $this->showNowPlayingNotification,
'confirm_before_closing' => $this->confirmBeforeClosing,
'show_album_art_overlay' => $this->showAlbumArtOverlay,
'transcode_on_mobile' => $this->transcodeOnMobile,
'transcode_quality' => $this->transcodeQuality,
'make_uploads_public' => $this->makeUploadsPublic,
'lastfm_session_key' => $this->lastFmSessionKey,
'support_bar_no_bugging' => $this->supportBarNoBugging,
'artists_view_mode' => $this->artistsViewMode,
'albums_view_mode' => $this->albumsViewMode,
'repeat_mode' => $this->repeatMode,
'volume' => $this->volume,
'equalizer' => $this->equalizer->toArray(),
'lyrics_zoom_level' => $this->lyricsZoomLevel,
'visualizer' => $this->visualizer,
'active_extra_panel_tab' => $this->activeExtraPanelTab,
'continuous_playback' => $this->continuousPlayback,
];
}
/** @return array<mixed> */
public function jsonSerialize(): array
{
return $this->toArray();
}
}