Skip to content

Commit

Permalink
chore: progress on settings
Browse files Browse the repository at this point in the history
  • Loading branch information
Tormak9970 committed May 9, 2024
1 parent 1e6c1f4 commit dff8931
Show file tree
Hide file tree
Showing 11 changed files with 345 additions and 2 deletions.
17 changes: 17 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
<!--
Tunistic is an offline music player.
Copyright (C) 2023 Travis Lane (Tormak)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
-->
<!doctype html>
<html lang="en">
<head>
Expand Down
70 changes: 70 additions & 0 deletions src/lib/controllers/LogController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Copyright (C) 2023 Travis Lane (Tormak)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>
*/

/**
* Controller class that handles all logging done by the app.
* ! Should do no logging here.
*/
export class LogController {
private static APP_NAME = "Tunistic";
private static APP_THEME_COLOR = "#7bdd69";
private static APP_INFO_COLOR = "#1abc9c";
private static APP_WARN_COLOR = "#e3c907";
private static APP_ERROR_COLOR = "#c70808";

/**
* Logs a message with level [INFO] to the core log file.
* @param message Message to log.
*/
static async log(message:string): Promise<void> {
console.log(
`%c ${LogController.APP_NAME} %c INFO %c`,
`background: ${LogController.APP_THEME_COLOR}; color: black;`,
`background: ${LogController.APP_INFO_COLOR}; color: black;`,
"background: transparent;",
message
);
}

/**
* Logs a message with level [WARNING] to the core log file.
* @param message Message to log.
*/
static async warn(message:string): Promise<void> {
console.warn(
`%c ${LogController.APP_NAME} %c WARNING %c`,
`background: ${LogController.APP_THEME_COLOR}; color: black;`,
`background: ${LogController.APP_WARN_COLOR}; color: black;`,
"background: transparent;",
message
);
}

/**
* Logs a message with level [ERROR] to the core log file.
* @param message Message to log.
*/
static async error(message:string): Promise<void> {
console.error(
`%c ${LogController.APP_NAME} %c ERROR %c`,
`background: ${LogController.APP_THEME_COLOR}; color: black;`,
`background: ${LogController.APP_ERROR_COLOR}; color: black;`,
"background: transparent;",
message
);
}
}
182 changes: 182 additions & 0 deletions src/lib/controllers/SettingsController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/**
* Steam Art Manager is a tool for setting the artwork of your Steam library.
* Copyright (C) 2023 Travis Lane (Tormak)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>
*/
import { type Settings, DEFAULT_SETTINGS } from "../../types/Settings";
import { LogController } from "../controllers/LogController";

/**
* The controller for settings.
*/
export class SettingsController {
private static settings: Settings;

/**
* Initializes the SettingsController.
*/
static init() {
this.settings = this.loadSettings();
this.registerSubs();
}

/**
* Migrate the settings structure to account for changes in the structure.
*/
private static migrateSettingsStructure(oldSettings: Settings): Settings {
// ? Handle any changes to settings from version to version here. Ex:
// if (oldSettings?.filters) {
// oldSettings.windowSettings.main.filters = oldSettings.filters ?? DEFAULT_SETTINGS.windowSettings.main.filters;
// delete oldSettings.filters;
// }

return oldSettings;
}

/**
* Gets the default value for the given settings field.
* @param field The settings property to get.
* @returns The default value for the field.
*/
static getDefault<T>(field: string): T {
const settings: any = structuredClone(DEFAULT_SETTINGS);
const fieldPath = field.split(".");
let parentObject = settings;

for (let i = 0; i < fieldPath. length - 1; i++) {
parentObject = parentObject[fieldPath[i]];
}

return parentObject[fieldPath[fieldPath.length - 1]];
}

/**
* Gets the given settings field.
* @param field The settings property to get.
* @returns The given setting, or its default value if it does not exist.
*/
static getSetting<T>(field: string): T {
const settings: any = structuredClone(this.settings);
const fieldPath = field.split(".");
let parentObject = settings;

for (let i = 0; i < fieldPath. length - 1; i++) {
const key = fieldPath[i];

if (Object.keys(parentObject).includes(key)) {
parentObject = parentObject[key];
} else {
const defaultValue = this.getDefault<T>(field);
LogController.log(`Field ${field} didn't exist. Returning default value ${defaultValue}.`);
return defaultValue;
}
}

const finalKey = fieldPath[fieldPath.length - 1];
if (Object.keys(parentObject).includes(finalKey)) {
return parentObject[finalKey];
} else {
const defaultValue = this.getDefault<T>(field);
LogController.log(`Field ${field} didn't exist. Returning default value ${defaultValue}.`);
return defaultValue;
}
}

/**
* Updates the given settings field with the provided data.
* @param field The setting to update.
* @param val The new value.
*/
static async updateSetting<T>(field: string, val: T): Promise<void> {
const settings = structuredClone(this.settings);
const fieldPath = field.split(".");
let parentObject = settings;

for (let i = 0; i < fieldPath. length - 1; i++) {
// @ts-ignore
parentObject = parentObject[fieldPath[i]];
}

// @ts-ignore
parentObject[fieldPath[fieldPath.length - 1]] = val;

this.settings = settings;
this.save();

LogController.log(`Updated setting ${field} to ${JSON.stringify(val)}.`);
}

/**
* Loads the settings.
*/
private static loadSettings(): Settings {
const currentSettings: any = JSON.parse(localStorage.getItem("tunistic-settings") ?? JSON.stringify(DEFAULT_SETTINGS));

let settings: Settings = { ...currentSettings };

const defaultSettings = structuredClone(DEFAULT_SETTINGS);

const curKeys = Object.keys(currentSettings);
const defEntries = Object.entries(defaultSettings);
const defKeys = Object.keys(defaultSettings);

for (const [ key, val ] of defEntries) {
if (!curKeys.includes(key)) {
// @ts-ignore
settings[key] = val;
}
}

for (const key in currentSettings) {
if (!defKeys.includes(key)) {
// @ts-ignore
delete settings[key];
}
}

settings = this.migrateSettingsStructure(settings);

settings.version = APP_VERSION;

this.save();

LogController.log("Finished checking settings for new app version and/or migration.");

return settings;
}

/**
* Registers the subscriptions to stores.
*/
private static registerSubs() {

}

/**
* Saves the settings object.
*/
private static async save() {
localStorage.setItem("tunistic-settings", JSON.stringify(this.settings));
}

/**
* Handles destroying the settings.
*/
static destroy() {
this.save();

// TODO: unregister subscriptions
}
}
Empty file removed src/lib/models/.gitkeep
Empty file.
4 changes: 4 additions & 0 deletions src/lib/models/Album.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export class Album {

}
4 changes: 4 additions & 0 deletions src/lib/models/Artist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export class Artist {

}
4 changes: 4 additions & 0 deletions src/lib/models/Playlist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export class Playlist {

}
4 changes: 4 additions & 0 deletions src/lib/models/Song.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export class Song {

}
2 changes: 1 addition & 1 deletion src/stores/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { View } from "../types/View";
// * View stores
export const showViewNav = writable(true);
export const selectedView: Writable<View> = writable(0);
export const viewsToRender: Writable<View[]> = writable([ View.PLAYLISTS, View.ALBUMS, View.SONGS, View.SETTINGS])
export const viewsToRender: Writable<View[]> = writable([ View.PLAYLISTS, View.ALBUMS, View.SONGS, View.GENRES, View.SETTINGS])


export const showNowPlayingSmall = writable(true);
56 changes: 56 additions & 0 deletions src/types/Settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { Album } from "../lib/models/Album";
import type { Playlist } from "../lib/models/Playlist";
import type { Song } from "../lib/models/Song";
import type { View } from "./View";

export enum GridSize {
LIST,
TWO,
THEE
}

export type PlaylistSortOrder = "Alphabetical" | "Last Played";
export type AlbumSortOrder = "Alphabetical" | "Last Played" | "Year" | "Length" | "Song Count";
export type SongSortOrder = "Alphabetical" | "Last Played" | "Year" | "Length";

export type Settings = {
version: string,
themePrimaryColor: string,
selectedView: View,
viewsToRender: View[],

nowPlaying: {
progress: number,
layout: number // ! This is a potential feature for the future
},

queue: Song[],

playlistsView: {
gridSize: GridSize,
sortOrder: PlaylistSortOrder,
playlists: Playlist[]
},
albumsView: {
gridSize: GridSize,
sortOrder: AlbumSortOrder,
albums: Album[]
},
songsView: {
gridSize: GridSize,
sortOrder: SongSortOrder,
albums: Song[]
},
// genresView: {
// gridSize: GridSize
// },
// artistsView: {
// gridSize: GridSize
// }
}

export const DEFAULT_SETTINGS: Settings = {
"version": "",
"selectedView": 0,
"viewsToRender": [0, 1, 2, 3, 5]
};
4 changes: 3 additions & 1 deletion src/types/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />
/// <reference types="vite/client" />

declare const APP_VERSION: string;

0 comments on commit dff8931

Please sign in to comment.