mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-15 16:20:03 +00:00
Move package "de.grimsi.gameyfin" to "org.gameyfin"
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import {proxy} from 'valtio';
|
||||
import ConfigEntryDto from "Frontend/generated/org/gameyfin/app/config/dto/ConfigEntryDto";
|
||||
import {ConfigEndpoint} from "Frontend/generated/endpoints";
|
||||
import ConfigUpdateDto from "Frontend/generated/org/gameyfin/app/config/dto/ConfigUpdateDto";
|
||||
import {Subscription} from "@vaadin/hilla-frontend";
|
||||
|
||||
type ConfigState = {
|
||||
subscription?: Subscription<ConfigUpdateDto[]>;
|
||||
isLoaded: boolean;
|
||||
state: Record<string, ConfigEntryDto>;
|
||||
config: NestedConfig;
|
||||
};
|
||||
|
||||
export const configState = proxy<ConfigState>({
|
||||
get isLoaded() {
|
||||
return this.subscription != null;
|
||||
},
|
||||
state: {},
|
||||
get config() {
|
||||
return toNestedConfig(Object.values(this.state));
|
||||
}
|
||||
});
|
||||
|
||||
/** Subscribe to and process state updates from backend **/
|
||||
export async function initializeConfigState() {
|
||||
if (configState.isLoaded) return;
|
||||
|
||||
// Fetch initial configuration data
|
||||
const initialEntries = await ConfigEndpoint.getAll();
|
||||
initialEntries.forEach((entry) => {
|
||||
configState.state[entry.key] = entry;
|
||||
});
|
||||
|
||||
// Subscribe to real-time updates
|
||||
configState.subscription = ConfigEndpoint.subscribe().onNext((updateDtos: ConfigUpdateDto[]) => {
|
||||
updateDtos.forEach((updateDto: ConfigUpdateDto) => {
|
||||
Object.entries(updateDto.updates).forEach(([key, value]) => {
|
||||
if (configState.state[key]) {
|
||||
configState.state[key].value = value;
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/** Computed properties **/
|
||||
|
||||
export type NestedConfig = {
|
||||
[field: string]: any;
|
||||
}
|
||||
|
||||
function toNestedConfig(entries: ConfigEntryDto[]): NestedConfig {
|
||||
const result: Record<string, any> = {};
|
||||
|
||||
for (const entry of entries) {
|
||||
const keys = entry.key.split('.');
|
||||
let current = result;
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
|
||||
if (i === keys.length - 1) {
|
||||
current[key] = entry.value;
|
||||
} else {
|
||||
current[key] = current[key] || {};
|
||||
current = current[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import {Subscription} from "@vaadin/hilla-frontend";
|
||||
import {proxy} from "valtio/index";
|
||||
import {GameEndpoint} from "Frontend/generated/endpoints";
|
||||
import GameEvent from "Frontend/generated/org/gameyfin/app/libraries/dto/LibraryEvent";
|
||||
import GameDto from "Frontend/generated/org/gameyfin/app/games/dto/GameDto";
|
||||
import Rand from "rand-seed";
|
||||
|
||||
type GameState = {
|
||||
subscription?: Subscription<GameEvent[]>;
|
||||
isLoaded: boolean;
|
||||
state: Record<number, GameDto>;
|
||||
games: GameDto[];
|
||||
gamesByLibraryId: Record<number, GameDto[]>;
|
||||
sortedAlphabetically: GameDto[];
|
||||
recentlyAdded: GameDto[];
|
||||
recentlyUpdated: GameDto[];
|
||||
randomlyOrderedGamesByLibraryId: Record<number, GameDto[]>;
|
||||
knownPublishers: Set<string>;
|
||||
knownDevelopers: Set<string>;
|
||||
knownGenres: Set<string>;
|
||||
knownThemes: Set<string>;
|
||||
knownKeywords: Set<string>;
|
||||
knownFeatures: Set<string>;
|
||||
knownPerspectives: Set<string>;
|
||||
};
|
||||
|
||||
export const gameState = proxy<GameState>({
|
||||
get isLoaded() {
|
||||
return this.subscription != null;
|
||||
},
|
||||
state: {},
|
||||
get games() {
|
||||
return Object.values<GameDto>(this.state);
|
||||
},
|
||||
get gamesByLibraryId() {
|
||||
return this.sortedAlphabetically.reduce((acc: Record<number, GameDto[]>, game: GameDto) => {
|
||||
(acc[game.libraryId] ||= []).push(game);
|
||||
return acc;
|
||||
}, {});
|
||||
},
|
||||
get sortedAlphabetically() {
|
||||
return this.games
|
||||
.sort((a: GameDto, b: GameDto) => a.title.localeCompare(b.title, undefined, {sensitivity: 'base'}));
|
||||
},
|
||||
get recentlyAdded() {
|
||||
return this.games
|
||||
.sort((a: GameDto, b: GameDto) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
||||
.slice(0, 25);
|
||||
},
|
||||
get recentlyUpdated() {
|
||||
return this.games
|
||||
.sort((a: GameDto, b: GameDto) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())
|
||||
.slice(0, 25);
|
||||
},
|
||||
get randomlyOrderedGamesByLibraryId() {
|
||||
const result: Record<number, GameDto[]> = {};
|
||||
for (const libraryId in this.gamesByLibraryId) {
|
||||
const rand = new Rand(libraryId.toString());
|
||||
result[libraryId] = this.gamesByLibraryId[libraryId]
|
||||
.filter((g: GameDto) => g.coverId && g.imageIds && g.imageIds.length > 0)
|
||||
.sort((a: GameDto, b: GameDto) => a.id - b.id)
|
||||
.sort(() => rand.next() - 0.5);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
get knownPublishers() {
|
||||
return new Set<string>(
|
||||
this.games
|
||||
.flatMap((game: GameDto) => game.publishers ? game.publishers : [])
|
||||
.sort()
|
||||
);
|
||||
},
|
||||
get knownDevelopers() {
|
||||
return new Set<string>(
|
||||
this.games
|
||||
.flatMap((game: GameDto) => game.developers ? game.developers : [])
|
||||
.sort()
|
||||
);
|
||||
},
|
||||
get knownGenres() {
|
||||
return new Set<string>(
|
||||
this.games
|
||||
.flatMap((game: GameDto) => game.genres ? game.genres : [])
|
||||
.sort()
|
||||
);
|
||||
},
|
||||
get knownThemes() {
|
||||
return new Set<string>(
|
||||
this.games
|
||||
.flatMap((game: GameDto) => game.themes ? game.themes : [])
|
||||
.sort()
|
||||
);
|
||||
},
|
||||
get knownKeywords() {
|
||||
return new Set<string>(
|
||||
this.games
|
||||
.flatMap((game: GameDto) => game.keywords ? game.keywords : [])
|
||||
.sort()
|
||||
);
|
||||
},
|
||||
get knownFeatures() {
|
||||
return new Set<string>(
|
||||
this.games
|
||||
.flatMap((game: GameDto) => game.features ? game.features : [])
|
||||
.sort()
|
||||
);
|
||||
},
|
||||
get knownPerspectives() {
|
||||
return new Set<string>(
|
||||
this.games
|
||||
.flatMap((game: GameDto) => game.perspectives ? game.perspectives : [])
|
||||
.sort()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/** Subscribe to and process state updates from backend **/
|
||||
export async function initializeGameState() {
|
||||
if (gameState.isLoaded) return gameState;
|
||||
|
||||
// Fetch initial library list
|
||||
const initialEntries = await GameEndpoint.getAll();
|
||||
initialEntries.forEach((game: GameDto) => {
|
||||
gameState.state[game.id] = game;
|
||||
});
|
||||
|
||||
// Subscribe to real-time updates
|
||||
gameState.subscription = GameEndpoint.subscribe().onNext((gameEvents: GameEvent[]) => {
|
||||
gameEvents.forEach((gameEvent: GameEvent) => {
|
||||
switch (gameEvent.type) {
|
||||
case "created":
|
||||
case "updated":
|
||||
//@ts-ignore
|
||||
gameState.state[gameEvent.game.id] = gameEvent.game;
|
||||
break;
|
||||
case "deleted":
|
||||
//@ts-ignore
|
||||
delete gameState.state[gameEvent.gameId];
|
||||
break;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return gameState;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import {Subscription} from "@vaadin/hilla-frontend";
|
||||
import {proxy} from "valtio/index";
|
||||
import {LibraryEndpoint} from "Frontend/generated/endpoints";
|
||||
import LibraryDto from "Frontend/generated/org/gameyfin/app/libraries/dto/LibraryDto";
|
||||
import LibraryEvent from "Frontend/generated/org/gameyfin/app/libraries/dto/LibraryEvent";
|
||||
import {handleLibraryDeletion} from "./ScanState";
|
||||
|
||||
type LibraryState = {
|
||||
subscription?: Subscription<LibraryEvent[]>;
|
||||
isLoaded: boolean;
|
||||
state: Record<number, LibraryDto>;
|
||||
libraries: LibraryDto[];
|
||||
sorted: LibraryDto[];
|
||||
};
|
||||
|
||||
export const libraryState = proxy<LibraryState>({
|
||||
get isLoaded() {
|
||||
return this.subscription != null;
|
||||
},
|
||||
state: {},
|
||||
get libraries() {
|
||||
return Object.values<LibraryDto>(this.state);
|
||||
},
|
||||
get sorted() {
|
||||
return Object.values<LibraryDto>(this.state).sort((a, b) => {
|
||||
if (a.name === undefined || b.name === undefined) return 0;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/** Subscribe to and process state updates from backend **/
|
||||
export async function initializeLibraryState() {
|
||||
if (libraryState.isLoaded) return libraryState;
|
||||
|
||||
// Fetch initial library list
|
||||
const initialEntries = await LibraryEndpoint.getAll();
|
||||
initialEntries.forEach((library: LibraryDto) => {
|
||||
libraryState.state[library.id] = library;
|
||||
});
|
||||
|
||||
// Subscribe to real-time updates
|
||||
libraryState.subscription = LibraryEndpoint.subscribeToLibraryEvents().onNext((libraryEvents: LibraryEvent[]) => {
|
||||
libraryEvents.forEach((libraryEvent: LibraryEvent) => {
|
||||
switch (libraryEvent.type) {
|
||||
case "created":
|
||||
case "updated":
|
||||
//@ts-ignore
|
||||
libraryState.state[libraryEvent.library.id] = libraryEvent.library;
|
||||
break;
|
||||
case "deleted":
|
||||
//@ts-ignore
|
||||
handleLibraryDeletion(libraryEvent.libraryId);
|
||||
//@ts-ignore
|
||||
delete libraryState.state[libraryEvent.libraryId];
|
||||
break;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return libraryState;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import {Subscription} from "@vaadin/hilla-frontend";
|
||||
import PluginDto from "Frontend/generated/org/gameyfin/app/core/plugins/dto/PluginDto";
|
||||
import PluginUpdateDto from "Frontend/generated/org/gameyfin/app/core/plugins/dto/PluginUpdateDto";
|
||||
import {proxy} from "valtio/index";
|
||||
import {PluginEndpoint} from "Frontend/generated/endpoints";
|
||||
|
||||
type PluginState = {
|
||||
subscription?: Subscription<PluginUpdateDto[]>;
|
||||
isLoaded: boolean;
|
||||
state: Record<string, PluginDto>;
|
||||
plugins: PluginDto[];
|
||||
pluginsByType: Record<string, PluginDto[]>;
|
||||
};
|
||||
|
||||
export const pluginState = proxy<PluginState>({
|
||||
get isLoaded() {
|
||||
return this.subscription != null;
|
||||
},
|
||||
state: {},
|
||||
get plugins() {
|
||||
return Object.values<PluginDto>(this.state);
|
||||
},
|
||||
get pluginsByType() {
|
||||
return groupPluginsByType(this.state);
|
||||
}
|
||||
});
|
||||
|
||||
/** Subscribe to and process state updates from backend **/
|
||||
export async function initializePluginState() {
|
||||
if (pluginState.isLoaded) return;
|
||||
|
||||
// Fetch initial plugin list
|
||||
const initialEntries = await PluginEndpoint.getAll();
|
||||
initialEntries.forEach((plugin: PluginDto) => {
|
||||
pluginState.state[plugin.id] = plugin;
|
||||
});
|
||||
|
||||
// Subscribe to real-time updates
|
||||
pluginState.subscription = PluginEndpoint.subscribe().onNext((updateDtos: PluginUpdateDto[]) => {
|
||||
updateDtos.forEach((updateDto: PluginUpdateDto) => {
|
||||
// Make sure the plugin exists in the state
|
||||
if (pluginState.state[updateDto.id]) {
|
||||
// Update the existing plugin by merging the new data using destructuring
|
||||
pluginState.state[updateDto.id] = {
|
||||
...pluginState.state[updateDto.id],
|
||||
...updateDto
|
||||
};
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/** Computed **/
|
||||
|
||||
function groupPluginsByType(pluginsMap: Record<string, PluginDto>): Record<string, PluginDto[]> {
|
||||
const pluginsByType: Record<string, PluginDto[]> = {};
|
||||
|
||||
// Convert map to array of plugins
|
||||
const plugins = Object.values(pluginsMap);
|
||||
|
||||
// Iterate through each plugin
|
||||
for (const plugin of plugins) {
|
||||
// Each plugin can have multiple types
|
||||
for (const type of plugin.types) {
|
||||
// Initialize array for this type if it doesn't exist
|
||||
if (!pluginsByType[type]) {
|
||||
pluginsByType[type] = [];
|
||||
}
|
||||
|
||||
// Add plugin to the appropriate type array
|
||||
pluginsByType[type].push(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
return pluginsByType;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import {proxy} from 'valtio';
|
||||
import type LibraryScanProgress from "Frontend/generated/org/gameyfin/app/libraries/dto/LibraryScanProgress";
|
||||
import {LibraryEndpoint} from "Frontend/generated/endpoints";
|
||||
import {Subscription} from "@vaadin/hilla-frontend";
|
||||
import LibraryScanStatus from "Frontend/generated/org/gameyfin/app/libraries/dto/LibraryScanStatus";
|
||||
import {libraryState} from "Frontend/state/LibraryState";
|
||||
|
||||
type ScanState = {
|
||||
subscription?: Subscription<LibraryScanProgress[]>;
|
||||
state: Record<string, LibraryScanProgress>;
|
||||
hasContent: boolean,
|
||||
isScanning: boolean,
|
||||
sortedByStartTime: LibraryScanProgress[];
|
||||
};
|
||||
|
||||
export const scanState = proxy<ScanState>({
|
||||
state: {},
|
||||
get hasContent(): boolean {
|
||||
return Object.values(this.state).length > 0;
|
||||
},
|
||||
get isScanning(): boolean {
|
||||
return Object.values(this.state)
|
||||
.some((scanProgress: LibraryScanProgress) => scanProgress.status === LibraryScanStatus.IN_PROGRESS);
|
||||
},
|
||||
get sortedByStartTime(): LibraryScanProgress[] {
|
||||
return Object.values(this.state).sort((a: LibraryScanProgress, b: LibraryScanProgress) => {
|
||||
return new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/** Subscribe to and process state updates from backend **/
|
||||
export function initializeScanState() {
|
||||
if (scanState.subscription) return;
|
||||
|
||||
// Subscribe to real-time updates
|
||||
scanState.subscription = LibraryEndpoint.subscribeToScanProgressEvents().onNext((scanProgresses: LibraryScanProgress[]) => {
|
||||
scanProgresses.forEach((scanProgress: LibraryScanProgress) => {
|
||||
// Filter out scans for libraries that are not in the current state
|
||||
if (!libraryState.state[scanProgress.libraryId]) return;
|
||||
|
||||
scanState.state[scanProgress.scanId] = scanProgress;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
export function handleLibraryDeletion(libraryId: number) {
|
||||
for (const scanId in scanState.state) {
|
||||
if (scanState.state[scanId].libraryId === libraryId) {
|
||||
delete scanState.state[scanId];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user