Skip to content

Commit d76ca3d

Browse files
authored
Merge pull request #9 from truearken/dev
Dev
2 parents bc8283a + 44ad0f3 commit d76ca3d

File tree

10 files changed

+139
-15
lines changed

10 files changed

+139
-15
lines changed

TODO.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99
- [x] Gun Buddy support
1010
- [x] Version Control in top left
1111
- [ ] Create "Variants" for Presets so you don't have to change agents for every preset
12-
- [ ] Tray App
12+
- [x] Tray App
1313
- [ ] Autostart Option
14-
- [ ] Hide unowned agents
15-
- [ ] Import/Export presets
14+
- [x] Hide unowned agents
1615

1716
### Frontend
1817

backend/handlers/handlers.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,25 @@ func (h *Handler) GetOwnedGunBuddies(w http.ResponseWriter, r *http.Request) {
6868
h.returnAny(w, &OwnedGunBuddiesResponse{LevelIds: buddies})
6969
}
7070

71+
type OwnedAgentsResponse struct {
72+
AgentIds []string
73+
}
74+
75+
func (h *Handler) GetOwnedAgents(w http.ResponseWriter, r *http.Request) {
76+
ownedAgents, err := h.Val.GetOwnedItems(valclient.ITEM_TYPE_AGENTS)
77+
if err != nil {
78+
h.returnError(w, err)
79+
return
80+
}
81+
82+
agents := make([]string, 0, len(ownedAgents.Entitlements))
83+
for _, b := range ownedAgents.Entitlements {
84+
agents = append(agents, b.ItemID)
85+
}
86+
87+
h.returnAny(w, &OwnedAgentsResponse{AgentIds: agents})
88+
}
89+
7190
func (h *Handler) GetPlayerLoadout(w http.ResponseWriter, r *http.Request) {
7291
loadout, err := h.Val.GetPlayerLoadout()
7392
if err != nil {

backend/main.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@ import (
44
"backend/handlers"
55
"backend/settings"
66
"backend/tick"
7+
"log"
78
"log/slog"
89
"net/http"
10+
"os"
11+
"path/filepath"
912
"strings"
1013
"time"
1114

1215
"github.com/truearken/valclient/valclient"
1316
)
1417

1518
func main() {
19+
initLogger()
20+
1621
var val *valclient.ValClient
1722
var err error
1823
slog.Info("waiting for valorant to start")
@@ -27,7 +32,7 @@ func main() {
2732

2833
settings, err := settings.Get()
2934
if err != nil {
30-
panic(err)
35+
log.Fatalf("unable to get settings: %v", err)
3136
}
3237
slog.Info("found settings", "settings", settings)
3338

@@ -41,6 +46,7 @@ func main() {
4146
mux.HandleFunc("POST /v1/presets", h.PostPresets)
4247
mux.HandleFunc("GET /v1/owned-skins", h.GetOwnedSkins)
4348
mux.HandleFunc("GET /v1/owned-gun-buddies", h.GetOwnedGunBuddies)
49+
mux.HandleFunc("GET /v1/owned-agents", h.GetOwnedAgents)
4450
mux.HandleFunc("GET /v1/player-loadout", h.GetPlayerLoadout)
4551
mux.HandleFunc("POST /v1/apply-loadout", h.PostApplyLoadout)
4652
mux.HandleFunc("GET /v1/settings", h.GetSettings)
@@ -55,6 +61,26 @@ func main() {
5561
}
5662
}
5763

64+
func initLogger() {
65+
configDir, err := os.UserConfigDir()
66+
if err != nil {
67+
log.Fatalf("unable to get config dir: %v", err)
68+
}
69+
70+
logDir := filepath.Join(configDir, "valovault/logs")
71+
72+
if err := os.MkdirAll(logDir, 0755); err != nil {
73+
log.Fatalf("error opening file: %v", err)
74+
}
75+
76+
f, err := os.OpenFile(filepath.Join(logDir, time.Now().Format("2006-01-02")+".log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
77+
if err != nil {
78+
log.Fatalf("error opening file: %v", err)
79+
}
80+
81+
log.SetOutput(f)
82+
}
83+
5884
func logMiddleware(next http.Handler) http.Handler {
5985
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
6086
if !strings.Contains(r.URL.String(), "/health") {

backend/tick/tick.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,23 @@ func NewTicker(val *valclient.ValClient) *Ticker {
2020
return &Ticker{Val: val}
2121
}
2222

23+
func (t *Ticker) Refresh() {
24+
refreshTicker := time.NewTicker(time.Minute * 1)
25+
for range refreshTicker.C {
26+
newVal, err := valclient.NewClient()
27+
if err != nil {
28+
slog.Warn("unable to refresh client", "err", err)
29+
continue
30+
}
31+
t.Val = newVal
32+
slog.Info("client refreshed succesfully")
33+
}
34+
}
35+
2336
func (t *Ticker) Start() {
2437
ticker := time.NewTicker(TICK_SPEED_SECONDS * time.Second * 3)
25-
defer ticker.Stop()
38+
39+
go t.Refresh()
2640

2741
lastAgentUuid := ""
2842
for range ticker.C {

frontend/src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ tauri-build = { version = "2.4.1", features = [] }
2121
serde_json = "1.0"
2222
serde = { version = "1.0", features = ["derive"] }
2323
log = "0.4"
24-
tauri = { version = "2.8.5", features = [] }
24+
tauri = { version = "2.8.5", features = ["tray-icon"] }
2525
tauri-plugin-log = "2"
2626
tauri-plugin-http = "2"
2727
tauri-plugin-shell = "2"

frontend/src-tauri/src/lib.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
use std::sync::Mutex;
2-
use tauri::{Manager, RunEvent, State};
2+
use tauri::{
3+
menu::{Menu, MenuItem},
4+
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
5+
Manager, RunEvent, State, WindowEvent,
6+
};
37
use tauri_plugin_shell::process::CommandChild;
48
use tauri_plugin_shell::ShellExt;
59

@@ -32,6 +36,36 @@ pub fn run() {
3236
*state.child.lock().unwrap() = Some(child);
3337
}
3438

39+
let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
40+
let menu = Menu::with_items(app, &[&quit_i])?;
41+
42+
let _tray = TrayIconBuilder::new()
43+
.icon(app.default_window_icon().unwrap().clone())
44+
.menu(&menu)
45+
.show_menu_on_left_click(false)
46+
.on_menu_event(|app, event| match event.id.as_ref() {
47+
"quit" => {
48+
app.exit(0);
49+
}
50+
_ => {}
51+
})
52+
.on_tray_icon_event(|tray, event| match event {
53+
TrayIconEvent::Click {
54+
button: MouseButton::Left,
55+
button_state: MouseButtonState::Up,
56+
..
57+
} => {
58+
let app = tray.app_handle();
59+
if let Some(window) = app.get_webview_window("main") {
60+
let _ = window.unminimize();
61+
let _ = window.show();
62+
let _ = window.set_focus();
63+
}
64+
}
65+
_ => {}
66+
})
67+
.build(app)?;
68+
3569
Ok(())
3670
});
3771

@@ -41,12 +75,24 @@ pub fn run() {
4175

4276
app.run(|app_handle, event| {
4377
if !cfg!(dev) {
44-
if let RunEvent::ExitRequested { .. } = event {
78+
if let RunEvent::ExitRequested { .. } = &event {
4579
let state: State<AppState> = app_handle.state();
4680
if let Some(child) = state.child.lock().unwrap().take() {
4781
child.kill().expect("Failed to kill sidecar");
4882
};
4983
}
5084
}
85+
86+
if let RunEvent::WindowEvent {
87+
event: WindowEvent::Resized(_),
88+
..
89+
} = &event
90+
{
91+
if let Some(window) = app_handle.get_webview_window("main") {
92+
if let Ok(true) = window.is_minimized() {
93+
let _ = window.hide();
94+
}
95+
}
96+
}
5197
});
5298
}

frontend/src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
33
"productName": "ValoVault",
4-
"version": "0.2.4",
4+
"version": "0.2.5",
55
"identifier": "me.truearken",
66
"build": {
77
"frontendDist": "../out",

frontend/src/context/DataContext.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';
44
import { Agent, Weapon, GunBuddy, ContentTier } from '@/lib/types';
5-
import { getAgents, getWeapons, getGunBuddies, getContentTiers, getOwnedSkins, getOwnedGunBuddies, getHealth } from '@/services/api';
5+
import { getAgents, getWeapons, getGunBuddies, getContentTiers, getOwnedSkins, getOwnedGunBuddies, getHealth, getOwnedAgents } from '@/services/api';
66
import { LocalClientError } from '@/lib/errors';
77

88
interface DataContextType {
@@ -33,15 +33,19 @@ export function DataProvider({ children }: { children: ReactNode }) {
3333
const loadData = useCallback(async () => {
3434
try {
3535
setLoading(true);
36-
const [agentsData, weaponsData, gunBuddiesData, contentTiersData, ownedSkins, ownedGunBuddies] = await Promise.all([
36+
const [agentsData, weaponsData, gunBuddiesData, contentTiersData, ownedSkins, ownedGunBuddies, ownedAgents] = await Promise.all([
3737
getAgents(),
3838
getWeapons(),
3939
getGunBuddies(),
4040
getContentTiers(),
4141
getOwnedSkins(),
4242
getOwnedGunBuddies(),
43+
getOwnedAgents(),
4344
]);
44-
setAgents(agentsData);
45+
46+
const ownedAgentDetails = agentsData.filter(a => ownedAgents.AgentIds.includes(a.uuid))
47+
setAgents(ownedAgentDetails);
48+
4549
setWeapons(weaponsData);
4650
setContentTiers(contentTiersData);
4751

@@ -100,4 +104,4 @@ export function useData() {
100104
throw new Error('useData must be used within a DataProvider');
101105
}
102106
return context;
103-
}
107+
}

frontend/src/lib/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ export interface OwnedGunBuddiesResponse {
7272
LevelIds: string[];
7373
}
7474

75+
export interface OwnedAgentsResponse {
76+
AgentIds: string[];
77+
}
78+
7579
export interface ContentTier {
7680
uuid: string;
7781
displayName: string;

frontend/src/services/api.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { Weapon, Agent, OwnedSkinsResponse, LoadoutItemV1, Preset, GunBuddy, ContentTier } from '@/lib/types';
1+
import { Weapon, Agent, OwnedSkinsResponse, LoadoutItemV1, Preset, GunBuddy, ContentTier, OwnedGunBuddiesResponse, OwnedAgentsResponse } from '@/lib/types';
22
import { LocalClientError } from '@/lib/errors';
33
import { fetch } from '@tauri-apps/plugin-http';
4-
import { OwnedGunBuddiesResponse } from '../lib/types';
54

65
export const LOCAL_URL = "http://localhost:31719/v1"
76

@@ -111,6 +110,19 @@ export async function getOwnedGunBuddies(): Promise<OwnedGunBuddiesResponse> {
111110
}
112111
}
113112

113+
export async function getOwnedAgents(): Promise<OwnedAgentsResponse> {
114+
try {
115+
const response = await fetch(LOCAL_URL+'/owned-agents');
116+
if (!response.ok) {
117+
throw new Error('Failed to fetch owned agents. The local client might not be running or there was a server error.');
118+
}
119+
return await response.json();
120+
} catch (error) {
121+
console.error(error);
122+
throw new LocalClientError();
123+
}
124+
}
125+
114126
export async function getPresets(): Promise<Preset[]> {
115127
try {
116128
const response = await fetch(LOCAL_URL+'/presets');

0 commit comments

Comments
 (0)