show case data

This commit is contained in:
Trit0 2025-09-15 20:43:45 -04:00
parent 08efedc31b
commit b85ca7c3cf
11 changed files with 170 additions and 21 deletions

9
app.go
View File

@ -98,8 +98,15 @@ func (a *App) StartGame(id string) {
fmt.Println()
}
func (a *App) LoadGames() []models.Metadata {
func (a *App) LoadGames(mode string) []models.Metadata {
games = provider.GetConjureGameInfo()
if mode == "remote-showcase" {
games = provider.Filter(games, func(game models.Metadata) bool {
return game.Game == "Soul Shaper" || game.Game == "One Pixel Remaning" || game.Game == "Pong"
})
}
return games
}

View File

@ -30,8 +30,8 @@
</div>
</div>
<OptionsModal v-if="optionsOpen" @close="optionsOpen = false"/>
<QrModal v-if="qrLink" :link="qrLink" @close="qrLink = ''"/>
<OptionsModal v-if="optionsOpen" @close="store.closeModal()"/>
<QrModal v-if="qrLink" :link="qrLink" @close="store.closeModal()"/>
<LoadingModal v-if="gameIsStarting"/>
</div>
</template>

View File

@ -9,6 +9,7 @@
<select v-model="dataSource" class="mt-1 block w-full">
<option value="local">Local (Mock)</option>
<option value="remote">Remote (API)</option>
<option value="remote-showcase">Showcase</option>
</select>
</label>
@ -28,15 +29,34 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useAppStore } from "../stores/app-store";
import { log } from "../services/logger-service";
const emit = defineEmits(['close']);
const store = useAppStore();
const dataSource = ref(localStorage.getItem('dataSource') || 'local');
const volume = ref(+localStorage.getItem('volume') || 50);
function save() {
async function save() {
const oldSource = localStorage.getItem('dataSource');
log(dataSource.value);
localStorage.setItem('dataSource', dataSource.value);
localStorage.setItem('volume', volume.value.toString());
try {
await store.loadGames();
}
catch (error) {
console.error(error);
}
//
// if (oldSource !== dataSource.value) {
// log("Reloading games")
// await store.loadGames();
// }
emit('close');
}
</script>

View File

@ -1,5 +1,6 @@
import { KeyContext } from "./key-context";
import { InputManager } from "../input-manager";
import { mod } from "../../stores/app-store";
export class GamePreviewKeyContext extends KeyContext {
readonly name: string = "GamePreviewKeyContext";
@ -58,7 +59,7 @@ export class GamePreviewKeyContext extends KeyContext {
.filter(el => (el as any).offsetParent !== null); // filter out hidden elements
const currentIndex = focusableElements.indexOf(document.activeElement);
const nextIndex = (currentIndex - 1) % focusableElements.length;
const nextIndex = mod((currentIndex - 1), focusableElements.length);
(focusableElements[nextIndex] as any).focus();
}

View File

@ -1,5 +1,7 @@
import { InputManager } from "../input-manager";
import { ModalKeyContext } from "./modal-key-context";
import { log } from "../../services/logger-service";
import { mod } from "../../stores/app-store";
export class OptionsKeyContext extends ModalKeyContext {
public name: string = "OptionsKeyContext";
@ -8,7 +10,110 @@ export class OptionsKeyContext extends ModalKeyContext {
super.onEscape();
this.store.optionsOpen = false;
this.store.qrLink = "";
(document.activeElement as any).blur();
InputManager.switchContext('carousel');
}
protected onKeyUp() {
super.onKeyUp();
this.focusLastElement()
}
protected onKeyDown() {
super.onKeyDown();
this.focusNextElement();
}
private focusNextElement() {
const focusableSelectors = [
'a[href]',
'button:not([disabled])',
'input:not([disabled]):not([type="hidden"])',
'select:not([disabled])',
'textarea:not([disabled])',
'[tabindex]:not([tabindex="-1"])'
];
const focusableElements = Array.from(document.querySelectorAll(focusableSelectors.join(',')))
.filter(el => (el as any).offsetParent !== null); // filter out hidden elements
const currentIndex = focusableElements.indexOf(document.activeElement);
const nextIndex = (currentIndex + 1) % focusableElements.length;
(focusableElements[nextIndex] as any).focus();
}
protected onEnter() {
super.onEnter();
const activeElement = document.activeElement;
console.log(activeElement);
(document.activeElement as any).click();
}
private focusLastElement() {
const focusableSelectors = [
'a[href]',
'button:not([disabled])',
'input:not([disabled]):not([type="hidden"])',
'select:not([disabled])',
'textarea:not([disabled])',
'[tabindex]:not([tabindex="-1"])'
];
const focusableElements = Array.from(document.querySelectorAll(focusableSelectors.join(',')))
.filter(el => (el as any).offsetParent !== null); // filter out hidden elements
const currentIndex = focusableElements.indexOf(document.activeElement);
const nextIndex = mod((currentIndex - 1), focusableElements.length);
(focusableElements[nextIndex] as any).focus();
}
protected onKeyRight() {
super.onKeyRight();
const activeElement = document.activeElement;
if (activeElement.tagName.toLowerCase() === 'select') {
const mySelect = document.activeElement as HTMLSelectElement;
const currentIndex = mySelect.selectedIndex;
mySelect.selectedIndex = (currentIndex + 1) % mySelect.options.length;
}
else if (activeElement && activeElement.tagName.toLowerCase() === 'input' && (activeElement as HTMLInputElement).type === 'range') {
// Get the current value, convert to number, add 10, and set the new value
const range = (activeElement as HTMLInputElement);
let currentValue = parseInt(range.value);
range.value = (currentValue + 10).toString();
}
else {
this.focusNextElement();
}
}
protected onKeyLeft() {
super.onKeyLeft();
const activeElement = document.activeElement;
if (activeElement.tagName.toLowerCase() === 'select') {
const mySelect = document.activeElement as HTMLSelectElement;
const currentIndex = mySelect.selectedIndex;
mySelect.selectedIndex = mod((currentIndex - 1), mySelect.options.length);
}
else if (activeElement && activeElement.tagName.toLowerCase() === 'input' && (activeElement as HTMLInputElement).type === 'range') {
// Get the current value, convert to number, add 10, and set the new value
const range = (activeElement as HTMLInputElement);
let currentValue = parseInt(range.value);
range.value = (currentValue - 10).toString();
}
else {
this.focusLastElement();
}
}
setAvailableActions() {
this.store.currentAvailableActions = {
"order": "controller,keyboard",
"1,Enter": "Click",
"Power,Escape": "Back"
};
}
}

View File

@ -4,7 +4,7 @@ import Metadata = models.Metadata;
import Game = models.Game;
import Developer = models.Developer;
const localGames: Game[] = [
const mockGames: Game[] = [
new Game({
id: "ddf1ab0c-d86e-442f-8fd8-cfe8a0dc0a52",
title: "Soul Shaper",
@ -98,13 +98,13 @@ const localGames: Game[] = [
export async function fetchGames(): Promise<Game[]> {
const source = localStorage.getItem('dataSource') || 'local';
if (source === 'local') return localGames;
if (source === 'local') return mockGames;
const games = await LoadGames();
const games = (await LoadGames(source)) ?? [];
for (const game of games) {
console.log(game)
}
return [...games.map(convertToNewFormat), ...localGames];
return [...games.map(convertToNewFormat)];
}
function convertToNewFormat(metadata: Metadata): Game {

View File

@ -34,8 +34,10 @@ export const useAppStore = defineStore('app', {
console.log(this.games);
this.tags = [...new Set(this.games.flatMap(game => game.genres.split(",")))];
if (this.tags.length > 0) {
this.selectTag(this.tags[0])
this.selectTag(this.tags[0]);
}
this.selectGame(0);
},
moveGameRight() {
this.selectGame(this.selectedGameIndex + 1);
@ -69,6 +71,11 @@ export const useAppStore = defineStore('app', {
this.qrLink = link;
InputManager.switchContext("options");
},
closeModal() {
this.qrLink = "";
this.optionsOpen = false;
InputManager.switchContext("carousel");
},
async startSelectedGame() {
if (this.selectedGame && !this.gameIsStarting) {
this.gameIsStarting = true;
@ -82,6 +89,6 @@ export const useAppStore = defineStore('app', {
}
});
function mod(n, m) {
export function mod(n, m) {
return ((n % m) + m) % m;
}

View File

@ -3,7 +3,7 @@
import {models} from '../models';
import {provider} from '../models';
export function LoadGames():Promise<Array<models.Metadata>>;
export function LoadGames(arg1:string):Promise<Array<models.Metadata>>;
export function LoadGamesNewModel():Promise<Array<models.Game>>;

View File

@ -2,8 +2,8 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function LoadGames() {
return window['go']['main']['App']['LoadGames']();
export function LoadGames(arg1) {
return window['go']['main']['App']['LoadGames'](arg1);
}
export function LoadGamesNewModel() {

View File

@ -31,6 +31,7 @@ func GetDefaultConjureOsDirectory() string {
case "windows":
return filepath.Join(cacheDir, conjureDirectoryName, conjureOsDirectoryName)
case "darwin":
return filepath.Join(configDir, conjureDirectoryName, conjureOsDirectoryName)
case "linux":
return filepath.Join(configDir, conjureDirectoryName, conjureOsDirectoryName)
default:

View File

@ -182,16 +182,14 @@ func ExtractGame(game models.Metadata) string {
func GetConjureGameInfo() []models.Metadata {
gamePath := config.GetDefaultConjureGamesDirectory()
fmt.Println("Loading games....")
fmt.Println(gamePath)
fmt.Println("Loading games from path " + gamePath + "....")
var games []models.Metadata
entries, err := os.ReadDir(gamePath)
if err != nil {
log.Fatal(err)
return games
}
var games []models.Metadata
for _, e := range entries {
if e.IsDir() {
continue
@ -228,13 +226,23 @@ func GetConjureGameInfo() []models.Metadata {
}
if len(games) > 0 {
fmt.Println("Found Conjure Games:", len(games))
fmt.Println("Found Conjure Games: ", len(games))
} else {
fmt.Println("No Conjure games Found")
fmt.Println("No Conjure games found")
}
return games
}
func Filter[T any](s []T, predicate func(T) bool) []T {
result := make([]T, 0, len(s)) // Pre-allocate for efficiency
for _, v := range s {
if predicate(v) {
result = append(result, v)
}
}
return result
}
// Function to escape backslashes in the YAML string
func escapeBackslashes(input string) string {
// Replace every single backslash with double backslashes