Compare commits
32 Commits
main
...
tristan-wi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29bcd3cd6b | ||
|
|
b85ca7c3cf | ||
|
|
08efedc31b | ||
|
|
5cec1493f6 | ||
|
|
87f34a05db | ||
|
|
c704b72a03 | ||
|
|
3161bbbba1 | ||
|
|
11681873b6 | ||
|
|
2368a00157 | ||
|
|
4a88270535 | ||
|
|
2763543104 | ||
|
|
8a5de26ca3 | ||
|
|
87eb3de238 | ||
|
|
8282473657 | ||
|
|
d246b5cd3d | ||
|
|
f4e6226d5a | ||
|
|
552d30098f | ||
|
|
0b0e6af52a | ||
|
|
7f483c8d74 | ||
|
|
49ddcfe130 | ||
|
|
ac5eff0c09 | ||
|
|
c5086f0ecc | ||
|
|
563070aeeb | ||
|
|
5a2a070f64 | ||
|
|
b46eefa34a | ||
|
|
22e2cc088c | ||
|
|
4ce60e0844 | ||
|
|
9180a94c56 | ||
|
|
e3910c064f | ||
|
|
41460c0ca2 | ||
|
|
77e1ee0c73 | ||
|
|
c350aedcb5 |
21
app.go
21
app.go
@ -98,13 +98,28 @@ 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
|
||||
}
|
||||
|
||||
func (a *App) LoadGamesNewModel() []models.Game {
|
||||
return []models.Game{}
|
||||
func (a *App) LoadGamesNewModel(mode string) []models.Game {
|
||||
mgames := provider.GetConjureGameInfoNew()
|
||||
|
||||
if mode == "remote-showcase" {
|
||||
mgames = provider.Filter(mgames, func(game models.Game) bool {
|
||||
return game.Title == "Soul Shaper" || game.Title == "One Pixel Remaning" || game.Title == "Pong"
|
||||
})
|
||||
}
|
||||
|
||||
return mgames
|
||||
}
|
||||
|
||||
func (a *App) Log(message string) {
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
<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>
|
||||
<option value="new-model">New Model</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
@ -28,15 +30,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>
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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"
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
import {LoadGames} from "../../wailsjs/go/main/App";
|
||||
import { LoadGames, LoadGamesNewModel } from "../../wailsjs/go/main/App";
|
||||
import {models} from "../../wailsjs/go/models";
|
||||
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,17 @@ 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();
|
||||
if (source === "new-model") {
|
||||
return (await LoadGamesNewModel(source)) ?? [];
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
4
frontend/wailsjs/go/main/App.d.ts
vendored
4
frontend/wailsjs/go/main/App.d.ts
vendored
@ -3,9 +3,9 @@
|
||||
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>>;
|
||||
export function LoadGamesNewModel(arg1:string):Promise<Array<models.Game>>;
|
||||
|
||||
export function LoadImage(arg1:string,arg2:string):Promise<provider.FileBlob>;
|
||||
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
// 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() {
|
||||
return window['go']['main']['App']['LoadGamesNewModel']();
|
||||
export function LoadGamesNewModel(arg1) {
|
||||
return window['go']['main']['App']['LoadGamesNewModel'](arg1);
|
||||
}
|
||||
|
||||
export function LoadImage(arg1, arg2) {
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
@ -219,7 +217,7 @@ func GetConjureGameInfo() []models.Metadata {
|
||||
fmt.Println("Contents of metadata.txt:")
|
||||
metadata, err := io.ReadAll(rc)
|
||||
check(err)
|
||||
game := parseGameInfo([]byte(escapeBackslashes(string(metadata))))
|
||||
game := parseGameInfoOld([]byte(escapeBackslashes(string(metadata))))
|
||||
fmt.Println(game.ThumbnailPath)
|
||||
games = append(games, game)
|
||||
}
|
||||
@ -230,11 +228,80 @@ func GetConjureGameInfo() []models.Metadata {
|
||||
if len(games) > 0 {
|
||||
fmt.Println("Found Conjure Games: ", len(games))
|
||||
} else {
|
||||
fmt.Println("No Conjure games Found")
|
||||
fmt.Println("No Conjure games found")
|
||||
}
|
||||
return games
|
||||
}
|
||||
|
||||
func GetConjureGameInfoNew() []models.Game {
|
||||
gamePath := config.GetDefaultConjureGamesDirectory()
|
||||
|
||||
fmt.Println("Loading games from path " + gamePath + "....")
|
||||
var games []models.Game
|
||||
|
||||
entries, err := os.ReadDir(gamePath)
|
||||
if err != nil {
|
||||
return games
|
||||
}
|
||||
|
||||
for _, e := range entries {
|
||||
if e.IsDir() {
|
||||
continue
|
||||
} else if filepath.Ext(e.Name()) == ".conj" {
|
||||
conjPath := filepath.Join(gamePath, e.Name())
|
||||
conjBase := strings.TrimSuffix(conjPath, ".conj")
|
||||
|
||||
// Check if the destination folder already exists
|
||||
if _, err := os.Stat(conjBase); os.IsNotExist(err) {
|
||||
err = extractZipToSiblingFolder(conjPath)
|
||||
check(err)
|
||||
}
|
||||
|
||||
// Now read metadata from the extracted directory
|
||||
entries, err := os.ReadDir(conjBase)
|
||||
check(err)
|
||||
|
||||
fmt.Println("Contents of", conjPath)
|
||||
for _, f := range entries {
|
||||
if f.Name() == "metadata.txt" {
|
||||
rc, err := os.Open(filepath.Join(conjBase, f.Name()))
|
||||
check(err)
|
||||
defer rc.Close()
|
||||
|
||||
fmt.Println("Contents of metadata.txt:")
|
||||
metadata, err := io.ReadAll(rc)
|
||||
check(err)
|
||||
game, err := parseGameInfo([]byte(escapeBackslashes(string(metadata))))
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Println(game.ThumbnailPath)
|
||||
games = append(games, game)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(games) > 0 {
|
||||
fmt.Println("Found Conjure Games: ", len(games))
|
||||
} else {
|
||||
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
|
||||
@ -253,13 +320,19 @@ func printIndentedPath(path string) {
|
||||
}
|
||||
}
|
||||
|
||||
func parseGameInfo(data []byte) models.Metadata {
|
||||
func parseGameInfoOld(data []byte) models.Metadata {
|
||||
game := models.Metadata{}
|
||||
err := yaml.Unmarshal(data, &game)
|
||||
check(err)
|
||||
return game
|
||||
}
|
||||
|
||||
func parseGameInfo(data []byte) (models.Game, error) {
|
||||
game := models.Game{}
|
||||
err := yaml.Unmarshal(data, &game)
|
||||
return game, err
|
||||
}
|
||||
|
||||
func check(e error) {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user