Compare commits

...

6 Commits

Author SHA1 Message Date
06bf344ea7 Implement updating addons 2025-01-11 23:30:29 +01:00
e0f3361efe Implement picking game dir 2025-01-11 23:28:25 +01:00
156c5770e8 Implement game path manipulation 2025-01-11 23:21:01 +01:00
c975aee3cb Add some sort of display 2025-01-11 21:48:37 +01:00
a45e3a5d87 Implement header 2025-01-11 21:14:48 +01:00
10faaf93c8 Assign addon names on startup 2025-01-11 20:57:48 +01:00
11 changed files with 262 additions and 42 deletions

View File

@@ -111,7 +111,7 @@ func (a *Addon) Update(body []byte) (err error) {
} }
} }
localPath := filepath.Join(gamePath, "Interface", "AddOns", file.Name) localPath := filepath.Join(settings.GamePath, "Interface", "AddOns", file.Name)
log.Printf("Updating file %s", localPath) log.Printf("Updating file %s", localPath)
err = UpdateFile(localPath, fileData) err = UpdateFile(localPath, fileData)
if err != nil { if err != nil {
@@ -124,7 +124,7 @@ func (a *Addon) Update(body []byte) (err error) {
return nil return nil
} }
func (a *Addon) GetLocalTocPath() string { func (a *Addon) GetLocalTocPath() string {
return filepath.Join(gamePath, "Interface", "AddOns", a.Name, a.Name+".toc") return filepath.Join(settings.GamePath, "Interface", "AddOns", a.Name, a.Name+".toc")
} }
func (a *Addon) GetRemoteVersion() (version string, err error) { func (a *Addon) GetRemoteVersion() (version string, err error) {
url := a.GetRemoteTocURL() url := a.GetRemoteTocURL()

93
app.go
View File

@@ -2,6 +2,8 @@ package main
import ( import (
"context" "context"
"os"
"path/filepath"
"github.com/wailsapp/wails/v2/pkg/runtime" "github.com/wailsapp/wails/v2/pkg/runtime"
) )
@@ -37,42 +39,89 @@ type StringResponse struct {
Data string `json:"data"` Data string `json:"data"`
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
} }
type BoolResponse struct {
func (a *App) GetAddons() AddonsResponse { Data bool `json:"data"`
return AddonsResponse{ Error string `json:"error,omitempty"`
Data: addonService.Addons,
Error: "",
}
} }
func (a *App) GetAddon(name string) AddonResponse { func (a *App) GetAddons() (res AddonsResponse) {
res.Data = addonService.Addons
return res
}
func (a *App) GetAddon(name string) (res AddonResponse) {
addon, err := addonService.GetAddon(name) addon, err := addonService.GetAddon(name)
return AddonResponse{ res.Data = addon
Data: addon, if err != nil {
Error: err.Error(), res.Error = err.Error()
} }
return res
} }
func (a *App) GetAddonRemoteVersion(name string) StringResponse { func (a *App) GetAddonRemoteVersion(name string) (res StringResponse) {
version, err := addonService.GetRemoteVersion(name) version, err := addonService.GetRemoteVersion(name)
return StringResponse{ res.Data = version
Data: version, if err != nil {
Error: err.Error(), res.Error = err.Error()
} }
return res
} }
func (a *App) GetAddonLocalVersion(name string) StringResponse { func (a *App) GetAddonLocalVersion(name string) (res StringResponse) {
version, err := addonService.GetLocalVersion(name) version, err := addonService.GetLocalVersion(name)
return StringResponse{ res.Data = version
Data: version, if err != nil {
Error: err.Error(), res.Error = err.Error()
} }
return res
} }
func (a *App) UpdateAddon(name string) AddonResponse { func (a *App) UpdateAddon(name string) (res AddonResponse) {
addon, err := addonService.UpdateAddon(name) addon, err := addonService.UpdateAddon(name)
return AddonResponse{ res.Data = addon
Data: addon, if err != nil {
Error: err.Error(), res.Error = err.Error()
} }
return res
}
func (a *App) GetGamePath() (res StringResponse) {
res.Data = settings.GamePath
return res
}
func (a *App) SetGamePath(path string) (res StringResponse) {
settings.GamePath = path
err := SaveSettings(*settings)
if err != nil {
res.Error = err.Error()
}
return res
}
func (a *App) IsGamePathValid() (res BoolResponse) {
if settings.GamePath == "" {
res.Data = false
return res
}
// Check if the path exists and contains the Interface/AddOns directory
addonPath := filepath.Join(settings.GamePath, "Interface", "AddOns")
if _, err := os.Stat(addonPath); os.IsNotExist(err) {
res.Data = false
return res
}
res.Data = true
return res
}
func (a *App) SelectDirectory() (res StringResponse) {
path, err := runtime.OpenDirectoryDialog(a.ctx, runtime.OpenDialogOptions{
Title: "Select World of Warcraft Directory",
})
if err != nil {
res.Error = err.Error()
return
}
res.Data = path
return
} }

View File

@@ -0,0 +1,39 @@
<script lang="ts">
import { GetAddonLocalVersion, GetAddonRemoteVersion, UpdateAddon } from "$wails/main/App";
import { type main } from "$wails/models";
export let addon: main.Addon;
let localVersion = "0.0.0";
let remoteVersion = "0.0.0";
let upToDate = true;
GetAddonLocalVersion(addon.name).then((res) => {
localVersion = res.data;
});
GetAddonRemoteVersion(addon.name).then((res) => {
remoteVersion = res.data;
});
$: upToDate = localVersion === remoteVersion;
async function updateAddon() {
const res = await UpdateAddon(addon.name);
if (res.error) {
console.error(res.error);
}
GetAddonLocalVersion(addon.name).then((res) => {
localVersion = res.data;
});
GetAddonRemoteVersion(addon.name).then((res) => {
remoteVersion = res.data;
});
}
</script>
<div class="bg-gray-900 text-white p-4 m-3 mx-2 rounded-lg">
<h2 class="text-2xl font-bold">{addon.name}</h2>
<p class="text-gray-300">Local Version: {localVersion}</p>
<p class="text-gray-300">Remote Version: {remoteVersion}</p>
{#if upToDate}
<button class="bg-green-500 disabled text-white p-2 my-2 cursor-default rounded-lg">Up to date</button>
{:else}
<button class="bg-blue-500 text-white p-2 my-2 rounded-lg" on:click={updateAddon}>Update</button>
{/if}
</div>

View File

@@ -1,11 +1,62 @@
<script lang="ts"> <script>
import { GetGamePath, IsGamePathValid, SelectDirectory, SetGamePath } from "$wails/main/App";
let name = "Yggdrasil";
let description = "The wow addon manager";
let gamePathValid = false;
IsGamePathValid().then((res) => {
if (res.error) {
console.error(res.error);
} else {
gamePathValid = res.data;
}
});
async function handleSelectDirectory() {
try {
const result = await SelectDirectory();
if (result.error) {
console.error("Error selecting directory:", result.error);
return;
}
if (result.data) {
const response = await SetGamePath(result.data);
if (response.error) {
console.error("Error setting game path:", response.error);
} else {
window.location.reload();
}
}
} catch (error) {
console.error("Error selecting directory:", error);
}
}
</script> </script>
<template> <header class="bg-gray-900 text-white py-4">
<header <div class="container mx-auto px-4 relative">
class="flex h-22 items-center justify-center bg-base-100 shadow-lg sticky top-0 z-50 border-b <div class="flex justify-between items-center">
border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60" <div class="flex-1"></div>
> <h1 class="text-3xl font-bold text-center relative flex-1">
Biiiiiiiig header {name}
</header> </h1>
</template> <div class="flex-1 flex justify-end">
<button
class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg flex items-center gap-2 transition-colors"
on:click={handleSelectDirectory}
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z"
clip-rule="evenodd"
/>
<path d="M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H8a2 2 0 01-2-2v-2z" />
</svg>
{gamePathValid ? "Change Game Path" : "Set Game Path"}
</button>
</div>
</div>
</div>
</header>

View File

@@ -1,7 +1,39 @@
<script lang="ts"> <script lang="ts">
import AddonCard from "$lib/components/AddonCard.svelte";
import { GetAddons, IsGamePathValid } from "$wails/main/App";
import { type main } from "$wails/models";
let addons: { [key: string]: main.Addon } = {};
GetAddons().then((res) => {
if (res.error) {
console.error(res.error);
} else {
addons = res.data;
}
});
let gamePath = "";
let gamePathValid = false;
IsGamePathValid().then((res) => {
if (res.error) {
console.error(res.error);
} else {
gamePathValid = res.data;
}
});
</script> </script>
<template> <template>
Hello, world {#if gamePathValid}
</template> <div class="grid grid-cols-3 gap-4">
{#each Object.values(addons) as addon}
<AddonCard {addon} />
{/each}
</div>
{:else}
<div class="flex flex-col items-center justify-center h-full">
<p class="text-gray-300">Game path is not valid</p>
<button class="bg-blue-500 text-white p-2 my-2 rounded-lg">Set Game Path</button>
</div>
{/if}
</template>

View File

@@ -12,6 +12,7 @@ export default defineConfig({
$components: join(__dirname, "src/lib/components"), $components: join(__dirname, "src/lib/components"),
$router: join(__dirname, "src/lib/router"), $router: join(__dirname, "src/lib/router"),
$wails: join(__dirname, "wailsjs/go"), $wails: join(__dirname, "wailsjs/go"),
$runtime: join(__dirname, "wailsjs/runtime"),
}, },
}, },

View File

@@ -12,4 +12,12 @@ export function GetAddonRemoteVersion(arg1:string):Promise<main.StringResponse>;
export function GetAddons():Promise<main.AddonsResponse>; export function GetAddons():Promise<main.AddonsResponse>;
export function GetGamePath():Promise<main.StringResponse>;
export function IsGamePathValid():Promise<main.BoolResponse>;
export function SelectDirectory():Promise<main.StringResponse>;
export function SetGamePath(arg1:string):Promise<main.StringResponse>;
export function UpdateAddon(arg1:string):Promise<main.AddonResponse>; export function UpdateAddon(arg1:string):Promise<main.AddonResponse>;

View File

@@ -22,6 +22,22 @@ export function GetAddons() {
return window['go']['main']['App']['GetAddons'](); return window['go']['main']['App']['GetAddons']();
} }
export function GetGamePath() {
return window['go']['main']['App']['GetGamePath']();
}
export function IsGamePathValid() {
return window['go']['main']['App']['IsGamePathValid']();
}
export function SelectDirectory() {
return window['go']['main']['App']['SelectDirectory']();
}
export function SetGamePath(arg1) {
return window['go']['main']['App']['SetGamePath'](arg1);
}
export function UpdateAddon(arg1) { export function UpdateAddon(arg1) {
return window['go']['main']['App']['UpdateAddon'](arg1); return window['go']['main']['App']['UpdateAddon'](arg1);
} }

View File

@@ -78,6 +78,20 @@ export namespace main {
return a; return a;
} }
} }
export class BoolResponse {
data: boolean;
error?: string;
static createFrom(source: any = {}) {
return new BoolResponse(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.data = source["data"];
this.error = source["error"];
}
}
export class StringResponse { export class StringResponse {
data: string; data: string;
error?: string; error?: string;

22
main.go
View File

@@ -30,7 +30,7 @@ func init() {
//go:embed all:frontend/dist //go:embed all:frontend/dist
var assets embed.FS var assets embed.FS
var settingsFilePath = "settings.json" var settingsFilePath = "settings.json"
var gamePath string var settings *Settings
var addonService *AddonService var addonService *AddonService
type Settings struct { type Settings struct {
@@ -55,24 +55,34 @@ func main() {
Error.Printf("error opening settings file: %s", err) Error.Printf("error opening settings file: %s", err)
return return
} }
settings := Settings{}
err = json.NewDecoder(settingsFile).Decode(&settings) err = json.NewDecoder(settingsFile).Decode(&settings)
if err != nil { if err != nil {
Warning.Printf("error decoding settings: %s", err) Warning.Printf("error decoding settings: %s", err)
settings = Settings{} settings = &Settings{}
} }
settingsFile.Close() settingsFile.Close()
log.Printf("Loaded settings: %+v", settings) log.Printf("Loaded settings: %+v", settings)
for name, addon := range settings.Addons {
addon.Name = name
settings.Addons[name] = addon
}
addonService = &AddonService{} addonService = &AddonService{}
addonService.Addons = settings.Addons addonService.Addons = settings.Addons
for _, addon := range addonService.Addons {
version, err := addonService.GetLocalVersion(addon.Name)
if err != nil {
Warning.Printf("error getting local version: %s", err)
}
log.Printf("%s %#v", addon.Name, version)
}
app := NewApp() app := NewApp()
err = wails.Run(&options.App{ err = wails.Run(&options.App{
Title: "wails-template", Title: "wails-template",
Width: 1024, Width: 700,
Height: 768, Height: 700,
AssetServer: &assetserver.Options{ AssetServer: &assetserver.Options{
Assets: assets, Assets: assets,
}, },
@@ -86,5 +96,5 @@ func main() {
if err != nil { if err != nil {
println("Error:", err.Error()) println("Error:", err.Error())
} }
SaveSettings(settings) SaveSettings(*settings)
} }

View File

@@ -1 +1 @@
{"gamePath":"","addons":{"Channeler":{"name":"","url":"https://git.site.quack-lab.dev/dave/wow_channeler"},"Dechickenator":{"name":"","url":"https://git.site.quack-lab.dev/dave/wow_dechickenator"},"Heimdall":{"name":"","url":"https://git.site.quack-lab.dev/dave/wow-Heimdall"}}} {"gamePath":"C:\\Games\\WoWRuski","addons":{"Channeler":{"name":"Channeler","url":"https://git.site.quack-lab.dev/dave/wow_channeler"},"Dechickenator":{"name":"Dechickenator","url":"https://git.site.quack-lab.dev/dave/wow_dechickenator"},"Heimdall":{"name":"Heimdall","url":"https://git.site.quack-lab.dev/dave/wow-Heimdall"}}}