From edbe04b9fc19355eedfed6a122c291221f243f51 Mon Sep 17 00:00:00 2001 From: AzenKain Date: Tue, 7 Oct 2025 13:14:18 +0700 Subject: [PATCH] UPDATE: System tray setting --- .../internal/app-service/appservice.js | 14 + frontend/src/components/closeModal/index.tsx | 83 ++++ frontend/src/components/header/index.tsx | 86 ++++ .../src/components/settingModal/index.tsx | 99 +++++ .../src/components/themeController/index.tsx | 3 +- frontend/src/components/updateModal/index.tsx | 64 +++ frontend/src/hooks/useGlobalEvents.tsx | 45 ++- frontend/src/pages/fireflytools/index.tsx | 240 +++++------ frontend/src/pages/launcher/index.tsx | 374 +++++++----------- frontend/src/routes/__root.tsx | 102 +---- frontend/src/stores/modalStore.ts | 8 + frontend/src/stores/settingStore.ts | 10 + internal/app-service/app.go | 17 +- main.go | 12 +- pkg/constant/constant.go | 2 +- 15 files changed, 682 insertions(+), 477 deletions(-) create mode 100644 frontend/src/components/closeModal/index.tsx create mode 100644 frontend/src/components/header/index.tsx create mode 100644 frontend/src/components/settingModal/index.tsx create mode 100644 frontend/src/components/updateModal/index.tsx diff --git a/frontend/bindings/firefly-launcher/internal/app-service/appservice.js b/frontend/bindings/firefly-launcher/internal/app-service/appservice.js index 8155bfe..31fed0c 100644 --- a/frontend/bindings/firefly-launcher/internal/app-service/appservice.js +++ b/frontend/bindings/firefly-launcher/internal/app-service/appservice.js @@ -6,6 +6,13 @@ // @ts-ignore: Unused imports import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime"; +/** + * @returns {$CancellablePromise<[boolean, string]>} + */ +export function CloseApp() { + return $Call.ByID(3422460836); +} + /** * @param {number} timeout * @returns {$CancellablePromise<[boolean, string]>} @@ -20,3 +27,10 @@ export function CloseAppAfterTimeout(timeout) { export function GetCurrentLauncherVersion() { return $Call.ByID(3575133982); } + +/** + * @returns {$CancellablePromise<[boolean, string]>} + */ +export function MinimizeApp() { + return $Call.ByID(3434614194); +} diff --git a/frontend/src/components/closeModal/index.tsx b/frontend/src/components/closeModal/index.tsx new file mode 100644 index 0000000..7b2fad5 --- /dev/null +++ b/frontend/src/components/closeModal/index.tsx @@ -0,0 +1,83 @@ +import { motion } from "motion/react" +import { AppService } from "@bindings/firefly-launcher/internal/app-service" +import { toast } from "react-toastify" +import useSettingStore from "@/stores/settingStore" + +export default function CloseModal({ + isOpen, + onClose +}: { + isOpen: boolean + onClose: () => void +}) { + if (!isOpen) return null + const { closingOption, setClosingOption } = useSettingStore() + + return ( +
+
+
+

+ Confirm Action +

+ + ✕ + +
+ +
+

+ Do you want to minimize the application to the system tray or close the application? +

+ +
+ setClosingOption({ isMinimize: closingOption.isMinimize, isAsk: !e.target.checked })} + /> + +
+ +
+ + +
+
+
+
+ ) +} \ No newline at end of file diff --git a/frontend/src/components/header/index.tsx b/frontend/src/components/header/index.tsx new file mode 100644 index 0000000..b09be11 --- /dev/null +++ b/frontend/src/components/header/index.tsx @@ -0,0 +1,86 @@ +import { Link } from "@tanstack/react-router"; +import ThemeController from "../themeController"; +import useModalStore from "@/stores/modalStore"; +import { Settings2 } from "lucide-react"; + +export default function Header() { + const { setIsOpenSettingModal } = useModalStore() + return ( +
+
+
+
+ +
+
    +
  • Home
  • + +
  • + Tools +
      +
    • Language
    • +
    • Diff
    • +
    +
  • +
  • + Plugins +
      +
    • Analysis (Veritas)
    • +
    • SrTools
    • +
    +
  • +
  • How to?
  • +
  • About
  • +
+
+ +
+ Logo +
+

+ Firefly + + Launcher + +

+

By Kain

+
+
+ +
+
+
    +
  • Home
  • +
  • +
    + Tools +
      +
    • Language
    • +
    • Diff
    • +
    +
    +
  • +
  • +
    + Plugins +
      +
    • Analysis (Veritas)
    • +
    • Firefly Tools
    • +
    +
    +
  • +
  • How to?
  • +
  • About
  • +
+
+
+ + +
+
+ ) +} \ No newline at end of file diff --git a/frontend/src/components/settingModal/index.tsx b/frontend/src/components/settingModal/index.tsx new file mode 100644 index 0000000..b491e0e --- /dev/null +++ b/frontend/src/components/settingModal/index.tsx @@ -0,0 +1,99 @@ +import { CheckUpdateLauncher } from "@/helper" +import useModalStore from "@/stores/modalStore" +import useSettingStore from "@/stores/settingStore" +import useLauncherStore from "@/stores/launcherStore" +import { toast } from "react-toastify" + +export default function SettingModal({ + isOpen, + onClose +}: { + isOpen: boolean + onClose: () => void +}) { + if (!isOpen) return null + + const { setIsOpenSelfUpdateModal } = useModalStore() + const { closingOption, setClosingOption } = useSettingStore() + const { setUpdateData, updateData } = useLauncherStore() + const CheckUpdate = async () => { + const launcherData = await CheckUpdateLauncher() + if (!launcherData.isUpdate) { + toast.success("Launcher is already up to date") + return + } + setUpdateData({ + server: updateData.server, + proxy: updateData.proxy, + launcher: launcherData + }) + + setIsOpenSelfUpdateModal(true) + } + + return ( +
+
+ {/* Header */} +
+

+ Settings +

+ +
+ + {/* Content */} +
+ {/* Section 1: Launcher Update */} +
+

Launcher Update

+

+ Check if your launcher is up to date. +

+ +
+ + {/* Section 2: Closing Option */} +
+

Closing Options

+ +
+
+ +
+
+ ) +} diff --git a/frontend/src/components/themeController/index.tsx b/frontend/src/components/themeController/index.tsx index b7acdb6..3e41dcf 100644 --- a/frontend/src/components/themeController/index.tsx +++ b/frontend/src/components/themeController/index.tsx @@ -1,7 +1,8 @@ import { useEffect, useState } from "react"; export default function ThemeController() { - const [theme, setTheme] = useState(localStorage.getItem("theme") ?? "light"); + const [theme, setTheme] = useState(localStorage.getItem("theme") ?? "night"); + const handleToggle = (e: any) => { if (e.target.checked) { setTheme("cupcake"); diff --git a/frontend/src/components/updateModal/index.tsx b/frontend/src/components/updateModal/index.tsx new file mode 100644 index 0000000..2e525fa --- /dev/null +++ b/frontend/src/components/updateModal/index.tsx @@ -0,0 +1,64 @@ +import { motion } from "framer-motion" + +interface UpdateModalProps { + isOpen: boolean + title: string + message: string + buttons: { + text: string + onClick: () => Promise | void + variant?: "primary" | "error" | "outline" + }[] + onClose: () => void + } + +export default function UpdateModal({ isOpen, title, message, buttons, onClose }: UpdateModalProps) { + if (!isOpen) return null + + return ( +
+
+ + ✕ + + +
+

+ {title} +

+
+ +
+
+

{message}

+
+ +
+ {buttons.map((btn, idx) => ( + + {btn.text} + + ))} +
+
+
+
+ ) +} diff --git a/frontend/src/hooks/useGlobalEvents.tsx b/frontend/src/hooks/useGlobalEvents.tsx index f5013ec..b497862 100644 --- a/frontend/src/hooks/useGlobalEvents.tsx +++ b/frontend/src/hooks/useGlobalEvents.tsx @@ -2,29 +2,17 @@ import { useEffect } from "react"; import { Events } from "@wailsio/runtime"; import { toast } from "react-toastify"; +import useSettingStore from "@/stores/settingStore"; +import { AppService } from "@bindings/firefly-launcher/internal/app-service"; +import useModalStore from "@/stores/modalStore"; +import useDiffStore from "@/stores/diffStore"; +import useLauncherStore from "@/stores/launcherStore"; -export function useGlobalEvents({ - setGameRunning, - setServerRunning, - setProxyRunning, - setProgressUpdate, - setMaxProgressUpdate, - setProgressDownload, - setDownloadSpeed, - setMessageUpdate, - setStageType, +export function useGlobalEvents() { + const { setIsOpenCloseModal } = useModalStore() + const { setGameRunning, setServerRunning, setProxyRunning, setProgressDownload, setDownloadSpeed } = useLauncherStore() + const { setProgressUpdate, setMaxProgressUpdate, setMessageUpdate, setStageType } = useDiffStore() -}: { - setGameRunning: (v: boolean) => void; - setServerRunning: (v: boolean) => void; - setProxyRunning: (v: boolean) => void; - setProgressUpdate: (v: number) => void; - setMaxProgressUpdate: (v: number) => void; - setProgressDownload: (v: number) => void; - setDownloadSpeed: (v: string) => void; - setMessageUpdate: (v: string) => void; - setStageType: (v: string) => void, -}) { useEffect(() => { const onGameExit = () => setGameRunning(false); const onServerExit = () => setServerRunning(false); @@ -64,6 +52,20 @@ export function useGlobalEvents({ const { message } = event.data[0]; toast.error(message); }); + Events.On("window:close", async () => { + const option = useSettingStore.getState().closingOption + if (option.isAsk) { + setIsOpenCloseModal(true); + return + } + if (option.isMinimize) { + const [success, message] = await AppService.MinimizeApp() + if (!success) toast.error(message) + } else { + const [success, message] = await AppService.CloseApp() + if (!success) toast.error(message) + } + }); return () => { Events.Off("download:server"); @@ -75,6 +77,7 @@ export function useGlobalEvents({ Events.Off("diff:message"); Events.Off("diff:stage"); Events.Off("version:check"); + Events.Off("window:close"); }; }, []); } diff --git a/frontend/src/pages/fireflytools/index.tsx b/frontend/src/pages/fireflytools/index.tsx index adbbba0..aa1ebf9 100644 --- a/frontend/src/pages/fireflytools/index.tsx +++ b/frontend/src/pages/fireflytools/index.tsx @@ -1,28 +1,28 @@ import { Link } from "@tanstack/react-router"; export default function FireflyToolsPage() { - return ( -
-
-

Firefly Tools

+ return ( +
+
+

Firefly Tools

- {/* Section 1: About SR Tools */} -
-

- ℹ️ - About Firefly Tools -

-
-
-
🏠
-

- This site is a another version of {" "} - Firefly Tools {" "} - developed by {" "} - Me {"(Kain)"} -

-
-
+ {/* Section 1: About SR Tools */} +
+

+ ℹ️ + About Firefly Tools +

+
+
+
🏠
+

+ This site is a another version of {" "} + Firefly Tools {" "} + developed by {" "} + Me {"(Kain)"} +

+
+
🏆 @@ -37,7 +37,7 @@ export default function FireflyToolsPage() { https://srtools.kain.id.vn/
- +
🔄 @@ -53,106 +53,106 @@ export default function FireflyToolsPage() {
-
-
👨‍💻
-

The original tool was created by a third-party developer named Amazing. This version is directly based on that work, without modification to core logic.

-
-
-
🔗
-

There is also a more modern version by the same author available at{" "} - - srtools.neonteam.dev - - {" "}and the original version at{" "} - - srtools.pages.dev - -

-
-
-
+
+
👨‍💻
+

The original tool was created by a third-party developer named Amazing. This version is directly based on that work, without modification to core logic.

+
+
+
🔗
+

There is also a more modern version by the same author available at{" "} + + srtools.neonteam.dev + + {" "}and the original version at{" "} + + srtools.pages.dev + +

+
+
+
- {/* Section 2: Main Features */} -
-

- 🔧 - Main Features -

-
-
-
⚙️
-

Configure characters, light cones, relics, traces, and eidolons easily in your browser.

-
-
-
🔌
-

Instantly apply setups to Firefly GO Server using Connect PS — no manual file uploads required.

-
-
-
-
-

Extra Settings

-

- Enhance your Firefly GO Server experience with extra features: -

-
    -
  • 🎭 Hidden Game UI — remove the entire game interface.
  • -
  • 🚫 Disable Censorship — get rid of Lens Flare censor 💀.
  • -
  • 🧪 Theorycraft Mode — configure HP, cycles, and more via the web.
  • -
-
-
+ {/* Section 2: Main Features */} +
+

+ 🔧 + Main Features +

+
+
+
⚙️
+

Configure characters, light cones, relics, traces, and eidolons easily in your browser.

+
+
+
🔌
+

Instantly apply setups to Firefly GO Server using Connect PS — no manual file uploads required.

+
+
+
+
+

Extra Settings

+

+ Enhance your Firefly GO Server experience with extra features: +

+
    +
  • 🎭 Hidden Game UI — remove the entire game interface.
  • +
  • 🚫 Disable Censorship — get rid of Lens Flare censor 💀.
  • +
  • 🧪 Theorycraft Mode — configure HP, cycles, and more via the web.
  • +
+
+
-
-
📂
-

Export and import full builds using freesr-data.json.

-
-
-
-

Fast testing workflow — no sync cooldowns, instant in-game updates.

-
-
-
+
+
📂
+

Export and import full builds using freesr-data.json.

+
+
+
+

Fast testing workflow — no sync cooldowns, instant in-game updates.

+
+
+
- {/* Section 3: Getting Started */} -
-

- 🚀 - Getting Started -

-
-
-
1️⃣
-

Access the tool through your browser at the self-hosted instance.

-
-
-
2️⃣
-

Configure your character builds with the intuitive web interface.

-
-
-
3️⃣
-

Use Connect PS feature to instantly sync with your private server.

-
-
-
4️⃣
-

Test your builds in-game with real-time updates and modifications.

-
-
-
+ {/* Section 3: Getting Started */} +
+

+ 🚀 + Getting Started +

+
+
+
1️⃣
+

Access the tool through your browser at the self-hosted instance.

+
+
+
2️⃣
+

Configure your character builds with the intuitive web interface.

+
+
+
3️⃣
+

Use Connect PS feature to instantly sync with your private server.

+
+
+
4️⃣
+

Test your builds in-game with real-time updates and modifications.

+
+
+
-
- Back to Home -
-
-
- ); +
+ Back to Home +
+
+
+ ); } \ No newline at end of file diff --git a/frontend/src/pages/launcher/index.tsx b/frontend/src/pages/launcher/index.tsx index 616e1a6..472035b 100644 --- a/frontend/src/pages/launcher/index.tsx +++ b/frontend/src/pages/launcher/index.tsx @@ -10,6 +10,7 @@ import useLauncherStore from '@/stores/launcherStore'; import { motion } from 'motion/react'; import { Link } from '@tanstack/react-router'; import { CheckUpdateLauncher, CheckUpdateProxy, CheckUpdateServer, sleep, UpdateLauncher, UpdateProxy, UpdateServer } from '@/helper'; +import UpdateModal from '@/components/updateModal'; export default function LauncherPage() { const { gamePath, @@ -21,6 +22,7 @@ export default function LauncherPage() { serverVersion, proxyVersion, } = useSettingStore() + const { isOpenDownloadDataModal, isOpenUpdateDataModal, @@ -29,6 +31,7 @@ export default function LauncherPage() { setIsOpenUpdateDataModal, setIsOpenSelfUpdateModal } = useModalStore() + const { isLoading, downloadType, @@ -54,6 +57,33 @@ export default function LauncherPage() { setUpdateData, } = useLauncherStore() + const widgetLinks = [ + { + tooltip: "Firefly SRAnalysis", + href: "https://sranalysis.kain.id.vn/", + img: "https://sranalysis.kain.id.vn/ff-sranalysis.png", + btnClass: "btn-primary" + }, + { + tooltip: "Firefly SRTools", + href: "https://srtools.kain.id.vn/", + img: "https://srtools.kain.id.vn/ff-srtool.png", + btnClass: "btn-secondary" + }, + { + tooltip: "Amazing's SRTools (Original UI)", + href: "https://srtools.pages.dev/", + img: "https://icons.duckduckgo.com/ip3/srtools.pages.dev.ico", + btnClass: "btn-primary" + }, + { + tooltip: "Amazing's SRTools (Modern UI)", + href: "https://srtools.neonteam.dev/", + img: "https://icons.duckduckgo.com/ip3/srtools.neonteam.dev.ico", + btnClass: "btn-secondary" + } + ] + useEffect(() => { const check = async () => { if (!serverVersion || !proxyVersion) { @@ -200,18 +230,18 @@ export default function LauncherPage() { setIsDownloading(true) if (updateData.launcher.isUpdate) { await UpdateLauncher(updateData.launcher.version) - setUpdateData({...updateData, launcher: { isUpdate: false, isExists: true, version: updateData.launcher.version }}) + setUpdateData({ ...updateData, launcher: { isUpdate: false, isExists: true, version: updateData.launcher.version } }) setIsOpenSelfUpdateModal(true) } if (updateData.server.isUpdate || !updateData.server.isExists) { await UpdateServer(updateData.server.version) setServerReady(true) - setUpdateData({...updateData, server: { isUpdate: false, isExists: true, version: updateData.server.version }}) + setUpdateData({ ...updateData, server: { isUpdate: false, isExists: true, version: updateData.server.version } }) } if (updateData.proxy.isUpdate || !updateData.proxy.isExists) { await UpdateProxy(updateData.proxy.version) setProxyReady(true) - setUpdateData({...updateData, proxy: { isUpdate: false, isExists: true, version: updateData.proxy.version }}) + setUpdateData({ ...updateData, proxy: { isUpdate: false, isExists: true, version: updateData.proxy.version } }) } setDownloadType("") @@ -251,60 +281,21 @@ export default function LauncherPage() {
- -
- - SRAnalysis Logo - -
- -
- - SRTools Logo - -
-
- - SRTools Logo - -
-
- - SRTools Logo - -
+ {widgetLinks.map((link, idx) => ( +
+ + {link.tooltip} + +
+ ))}
  • -
  • +
  • + +
  • +
  • + +