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)
err = UpdateFile(localPath, fileData)
if err != nil {
@@ -124,7 +124,7 @@ func (a *Addon) Update(body []byte) (err error) {
return nil
}
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) {
url := a.GetRemoteTocURL()

93
app.go
View File

@@ -2,6 +2,8 @@ package main
import (
"context"
"os"
"path/filepath"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
@@ -37,42 +39,89 @@ type StringResponse struct {
Data string `json:"data"`
Error string `json:"error,omitempty"`
}
func (a *App) GetAddons() AddonsResponse {
return AddonsResponse{
Data: addonService.Addons,
Error: "",
}
type BoolResponse struct {
Data bool `json:"data"`
Error string `json:"error,omitempty"`
}
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)
return AddonResponse{
Data: addon,
Error: err.Error(),
res.Data = addon
if err != nil {
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)
return StringResponse{
Data: version,
Error: err.Error(),
res.Data = version
if err != nil {
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)
return StringResponse{
Data: version,
Error: err.Error(),
res.Data = version
if err != nil {
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)
return AddonResponse{
Data: addon,
Error: err.Error(),
res.Data = addon
if err != nil {
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>
<template>
<header
class="flex h-22 items-center justify-center bg-base-100 shadow-lg sticky top-0 z-50 border-b
border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60"
>
Biiiiiiiig header
</header>
</template>
<header class="bg-gray-900 text-white py-4">
<div class="container mx-auto px-4 relative">
<div class="flex justify-between items-center">
<div class="flex-1"></div>
<h1 class="text-3xl font-bold text-center relative flex-1">
{name}
</h1>
<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">
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>
<template>
Hello, world
</template>
{#if gamePathValid}
<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"),
$router: join(__dirname, "src/lib/router"),
$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 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>;

View File

@@ -22,6 +22,22 @@ export function 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) {
return window['go']['main']['App']['UpdateAddon'](arg1);
}

View File

@@ -78,6 +78,20 @@ export namespace main {
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 {
data: string;
error?: string;

22
main.go
View File

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