UPDATE: System tray setting
This commit is contained in:
@@ -6,6 +6,13 @@
|
|||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime";
|
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
|
* @param {number} timeout
|
||||||
* @returns {$CancellablePromise<[boolean, string]>}
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
@@ -20,3 +27,10 @@ export function CloseAppAfterTimeout(timeout) {
|
|||||||
export function GetCurrentLauncherVersion() {
|
export function GetCurrentLauncherVersion() {
|
||||||
return $Call.ByID(3575133982);
|
return $Call.ByID(3575133982);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function MinimizeApp() {
|
||||||
|
return $Call.ByID(3434614194);
|
||||||
|
}
|
||||||
|
|||||||
83
frontend/src/components/closeModal/index.tsx
Normal file
83
frontend/src/components/closeModal/index.tsx
Normal file
@@ -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 (
|
||||||
|
<div className="fixed inset-0 z-50 h-full flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
||||||
|
<div className="relative w-[90%] max-w-2xl bg-base-100 text-base-content rounded-xl border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
||||||
|
<div className="border-b border-purple-500/30 px-6 py-4 mb-4 flex justify-between items-center">
|
||||||
|
<h3 className="font-bold text-xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-600">
|
||||||
|
Confirm Action
|
||||||
|
</h3>
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.1, rotate: 90 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className="btn btn-circle btn-md btn-error absolute right-3 top-3"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</motion.button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-6 pt-2 pb-6">
|
||||||
|
<p className="mb-4 text-lg">
|
||||||
|
Do you want to minimize the application to the system tray or close the application?
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex items-center mb-4">
|
||||||
|
<input
|
||||||
|
id="dontAskAgain"
|
||||||
|
type="checkbox"
|
||||||
|
className="checkbox checkbox-sm mr-2"
|
||||||
|
checked={!closingOption.isAsk}
|
||||||
|
onChange={(e) => setClosingOption({ isMinimize: closingOption.isMinimize, isAsk: !e.target.checked })}
|
||||||
|
/>
|
||||||
|
<label htmlFor="dontAskAgain" className="text-sm font-semibold text-accent">
|
||||||
|
Do not ask me again
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 justify-end gap-3">
|
||||||
|
<button
|
||||||
|
className="btn btn-warning"
|
||||||
|
onClick={async () => {
|
||||||
|
onClose()
|
||||||
|
const [success, message] = await AppService.MinimizeApp()
|
||||||
|
if (!success) toast.error(message)
|
||||||
|
if (!closingOption.isAsk) {
|
||||||
|
setClosingOption({ isMinimize: true, isAsk: false })
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Minimize
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-error btn-outline"
|
||||||
|
onClick={async () => {
|
||||||
|
onClose()
|
||||||
|
const [success, message] = await AppService.CloseApp()
|
||||||
|
if (!success) toast.error(message)
|
||||||
|
if (!closingOption.isAsk) {
|
||||||
|
setClosingOption({ isMinimize: false, isAsk: false })
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
86
frontend/src/components/header/index.tsx
Normal file
86
frontend/src/components/header/index.tsx
Normal file
@@ -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 (
|
||||||
|
<div className="navbar bg-base-100 shadow-sm sticky top-0 z-50 px-3">
|
||||||
|
<div className="navbar-start">
|
||||||
|
<div className="dropdown">
|
||||||
|
<div tabIndex={0} role="button" className="btn btn-ghost md:hidden">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h8m-8 6h16" /> </svg>
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
tabIndex={0}
|
||||||
|
className="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow">
|
||||||
|
<li><Link to="/">Home</Link></li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a>Tools</a>
|
||||||
|
<ul className="p-2">
|
||||||
|
<li><Link to="/language">Language</Link></li>
|
||||||
|
<li><Link to="/diff">Diff</Link></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a>Plugins</a>
|
||||||
|
<ul className="p-2">
|
||||||
|
<li><Link to="/analysis">Analysis (Veritas)</Link></li>
|
||||||
|
<li><Link to="/srtools">SrTools</Link></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><Link to="/howto">How to?</Link></li>
|
||||||
|
<li><Link to="/about">About</Link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<Link to="/" className="grid grid-cols-1 items-start text-left gap-0 hover:scale-105 px-2">
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<img src="/appicon.png" alt="Logo" className='w-13 h-13 rounded-lg mx-2' />
|
||||||
|
<div className="flex flex-col justify-center items-start">
|
||||||
|
<h1 className="text-xl font-bold">
|
||||||
|
<span className="text-emerald-500">Firefly </span>
|
||||||
|
<span className="bg-clip-text text-transparent bg-gradient-to-r from-emerald-400 via-orange-500 to-red-500">
|
||||||
|
Launcher
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
<p className="text-sm text-gray-500">By Kain</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div className="navbar-center hidden md:flex">
|
||||||
|
<ul className="menu menu-horizontal px-1 gap-4">
|
||||||
|
<li><Link to="/">Home</Link></li>
|
||||||
|
<li>
|
||||||
|
<details>
|
||||||
|
<summary>Tools</summary>
|
||||||
|
<ul className="p-2">
|
||||||
|
<li><Link to="/language">Language</Link></li>
|
||||||
|
<li><Link to="/diff">Diff</Link></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<details>
|
||||||
|
<summary>Plugins</summary>
|
||||||
|
<ul className="p-2">
|
||||||
|
<li><Link to="/analysis">Analysis (Veritas)</Link></li>
|
||||||
|
<li><Link to="/srtools">Firefly Tools</Link></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</li>
|
||||||
|
<li><Link to="/howto">How to?</Link></li>
|
||||||
|
<li><Link to="/about">About</Link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="navbar-end flex gap-2">
|
||||||
|
<ThemeController />
|
||||||
|
<button className="btn btn-ghost btn-circle" onClick={() => setIsOpenSettingModal(true)}>
|
||||||
|
<Settings2 className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
99
frontend/src/components/settingModal/index.tsx
Normal file
99
frontend/src/components/settingModal/index.tsx
Normal file
@@ -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 (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
||||||
|
<div className="relative w-[90%] max-w-md bg-base-100 text-base-content rounded-2xl border border-purple-500/30 shadow-2xl shadow-purple-500/30 p-6">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<h3 className="font-extrabold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-500">
|
||||||
|
Settings
|
||||||
|
</h3>
|
||||||
|
<button
|
||||||
|
className="btn btn-circle btn-sm bg-red-600 hover:bg-red-700 text-white border-none shadow-lg"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
{/* Section 1: Launcher Update */}
|
||||||
|
<div className="p-4 bg-base-200 rounded-xl border border-purple-300 shadow-sm">
|
||||||
|
<h4 className="font-bold text-lg mb-2">Launcher Update</h4>
|
||||||
|
<p className="text-sm text-info mb-3">
|
||||||
|
Check if your launcher is up to date.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
className="btn btn-primary bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-400 hover:to-red-500 text-white shadow-md hover:shadow-lg transition-all duration-200"
|
||||||
|
onClick={CheckUpdate}
|
||||||
|
>
|
||||||
|
Check for Launcher Updates
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Section 2: Closing Option */}
|
||||||
|
<div className="p-4 bg-base-200 rounded-xl border border-purple-300 shadow-sm">
|
||||||
|
<h4 className="font-bold text-lg mb-2">Closing Options</h4>
|
||||||
|
<label className="flex items-start gap-3 cursor-pointer select-none">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="checkbox checkbox-primary w-5 h-5 mt-1"
|
||||||
|
checked={!closingOption.isAsk}
|
||||||
|
onChange={(e) => {
|
||||||
|
console.log(!e.target.checked)
|
||||||
|
setClosingOption({
|
||||||
|
isMinimize: closingOption.isMinimize,
|
||||||
|
isAsk: !e.target.checked
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-base font-medium text-info">
|
||||||
|
Set do not ask again
|
||||||
|
</span>
|
||||||
|
<span className="text-sm text-warning">
|
||||||
|
Next time you close the app, it will automatically{" "}
|
||||||
|
{closingOption.isMinimize ? "minimize to system tray" : "quit the app"}{" "}
|
||||||
|
without asking.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function ThemeController() {
|
export default function ThemeController() {
|
||||||
const [theme, setTheme] = useState(localStorage.getItem("theme") ?? "light");
|
const [theme, setTheme] = useState(localStorage.getItem("theme") ?? "night");
|
||||||
|
|
||||||
const handleToggle = (e: any) => {
|
const handleToggle = (e: any) => {
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
setTheme("cupcake");
|
setTheme("cupcake");
|
||||||
|
|||||||
64
frontend/src/components/updateModal/index.tsx
Normal file
64
frontend/src/components/updateModal/index.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { motion } from "framer-motion"
|
||||||
|
|
||||||
|
interface UpdateModalProps {
|
||||||
|
isOpen: boolean
|
||||||
|
title: string
|
||||||
|
message: string
|
||||||
|
buttons: {
|
||||||
|
text: string
|
||||||
|
onClick: () => Promise<void> | void
|
||||||
|
variant?: "primary" | "error" | "outline"
|
||||||
|
}[]
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UpdateModal({ isOpen, title, message, buttons, onClose }: UpdateModalProps) {
|
||||||
|
if (!isOpen) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
||||||
|
<div className="relative w-[90%] max-w-5xl bg-base-100 text-base-content rounded-xl border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.1, rotate: 90 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className="btn btn-circle btn-md btn-error absolute right-3 top-3"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</motion.button>
|
||||||
|
|
||||||
|
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
||||||
|
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-400">
|
||||||
|
{title}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-6 pb-6">
|
||||||
|
<div className="mb-6">
|
||||||
|
<p className="text-warning text-lg">{message}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-3">
|
||||||
|
{buttons.map((btn, idx) => (
|
||||||
|
<motion.button
|
||||||
|
key={idx}
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
|
className={`btn ${
|
||||||
|
btn.variant === "primary"
|
||||||
|
? "btn-primary bg-gradient-to-r from-orange-200 to-red-400 border-none"
|
||||||
|
: btn.variant === "error"
|
||||||
|
? "btn-error"
|
||||||
|
: "btn-outline btn-error"
|
||||||
|
}`}
|
||||||
|
onClick={btn.onClick}
|
||||||
|
>
|
||||||
|
{btn.text}
|
||||||
|
</motion.button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,29 +2,17 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Events } from "@wailsio/runtime";
|
import { Events } from "@wailsio/runtime";
|
||||||
import { toast } from "react-toastify";
|
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({
|
export function useGlobalEvents() {
|
||||||
setGameRunning,
|
const { setIsOpenCloseModal } = useModalStore()
|
||||||
setServerRunning,
|
const { setGameRunning, setServerRunning, setProxyRunning, setProgressDownload, setDownloadSpeed } = useLauncherStore()
|
||||||
setProxyRunning,
|
const { setProgressUpdate, setMaxProgressUpdate, setMessageUpdate, setStageType } = useDiffStore()
|
||||||
setProgressUpdate,
|
|
||||||
setMaxProgressUpdate,
|
|
||||||
setProgressDownload,
|
|
||||||
setDownloadSpeed,
|
|
||||||
setMessageUpdate,
|
|
||||||
setStageType,
|
|
||||||
|
|
||||||
}: {
|
|
||||||
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(() => {
|
useEffect(() => {
|
||||||
const onGameExit = () => setGameRunning(false);
|
const onGameExit = () => setGameRunning(false);
|
||||||
const onServerExit = () => setServerRunning(false);
|
const onServerExit = () => setServerRunning(false);
|
||||||
@@ -64,6 +52,20 @@ export function useGlobalEvents({
|
|||||||
const { message } = event.data[0];
|
const { message } = event.data[0];
|
||||||
toast.error(message);
|
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 () => {
|
return () => {
|
||||||
Events.Off("download:server");
|
Events.Off("download:server");
|
||||||
@@ -75,6 +77,7 @@ export function useGlobalEvents({
|
|||||||
Events.Off("diff:message");
|
Events.Off("diff:message");
|
||||||
Events.Off("diff:stage");
|
Events.Off("diff:stage");
|
||||||
Events.Off("version:check");
|
Events.Off("version:check");
|
||||||
|
Events.Off("window:close");
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
import { Link } from "@tanstack/react-router";
|
import { Link } from "@tanstack/react-router";
|
||||||
|
|
||||||
export default function FireflyToolsPage() {
|
export default function FireflyToolsPage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-base-200 flex items-center justify-center p-6">
|
<div className="min-h-screen bg-base-200 flex items-center justify-center p-6">
|
||||||
<div className="w-full bg-base-100 shadow-xl rounded-2xl p-8 space-y-8">
|
<div className="w-full bg-base-100 shadow-xl rounded-2xl p-8 space-y-8">
|
||||||
<h1 className="text-4xl font-bold text-primary text-center">Firefly Tools</h1>
|
<h1 className="text-4xl font-bold text-primary text-center">Firefly Tools</h1>
|
||||||
|
|
||||||
{/* Section 1: About SR Tools */}
|
{/* Section 1: About SR Tools */}
|
||||||
<div className="bg-blue-50 border-l-4 border-blue-400 p-6 rounded-r-lg">
|
<div className="bg-blue-50 border-l-4 border-blue-400 p-6 rounded-r-lg">
|
||||||
<h2 className="text-2xl font-bold text-blue-800 flex items-center gap-2 mb-4">
|
<h2 className="text-2xl font-bold text-blue-800 flex items-center gap-2 mb-4">
|
||||||
<span>ℹ️</span>
|
<span>ℹ️</span>
|
||||||
<span>About Firefly Tools</span>
|
<span>About Firefly Tools</span>
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-3 text-blue-700">
|
<div className="space-y-3 text-blue-700">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-blue-600 text-lg">🏠</div>
|
<div className="text-blue-600 text-lg">🏠</div>
|
||||||
<p>
|
<p>
|
||||||
This site is a another version of {" "}
|
This site is a another version of {" "}
|
||||||
<span className="font-semibold text-success">Firefly Tools {" "}</span>
|
<span className="font-semibold text-success">Firefly Tools {" "}</span>
|
||||||
developed by {" "}
|
developed by {" "}
|
||||||
<span className="font-semibold text-warning">Me {"(Kain)"}</span>
|
<span className="font-semibold text-warning">Me {"(Kain)"}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid md:grid-cols-2 gap-4">
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
<div className="bg-white border border-blue-200 rounded-lg p-4">
|
<div className="bg-white border border-blue-200 rounded-lg p-4">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<span className="text-blue-600 text-lg">🏆</span>
|
<span className="text-blue-600 text-lg">🏆</span>
|
||||||
@@ -37,7 +37,7 @@ export default function FireflyToolsPage() {
|
|||||||
https://srtools.kain.id.vn/
|
https://srtools.kain.id.vn/
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-white border border-blue-200 rounded-lg p-4">
|
<div className="bg-white border border-blue-200 rounded-lg p-4">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<span className="text-blue-600 text-lg">🔄</span>
|
<span className="text-blue-600 text-lg">🔄</span>
|
||||||
@@ -53,106 +53,106 @@ export default function FireflyToolsPage() {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-blue-600 text-lg">👨💻</div>
|
<div className="text-blue-600 text-lg">👨💻</div>
|
||||||
<p>The original tool was created by a third-party developer named <span className="font-semibold text-warning">Amazing</span>. This version is directly based on that work, without modification to core logic.</p>
|
<p>The original tool was created by a third-party developer named <span className="font-semibold text-warning">Amazing</span>. This version is directly based on that work, without modification to core logic.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-blue-600 text-lg">🔗</div>
|
<div className="text-blue-600 text-lg">🔗</div>
|
||||||
<p>There is also a more modern version by the same author available at{" "}
|
<p>There is also a more modern version by the same author available at{" "}
|
||||||
<a
|
<a
|
||||||
href="https://srtools.neonteam.dev/"
|
href="https://srtools.neonteam.dev/"
|
||||||
className="link link-warning"
|
className="link link-warning"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
srtools.neonteam.dev
|
srtools.neonteam.dev
|
||||||
</a>
|
</a>
|
||||||
{" "}and the original version at{" "}
|
{" "}and the original version at{" "}
|
||||||
<a
|
<a
|
||||||
href="https://srtools.pages.dev/"
|
href="https://srtools.pages.dev/"
|
||||||
className="link link-warning"
|
className="link link-warning"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
srtools.pages.dev
|
srtools.pages.dev
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Section 2: Main Features */}
|
{/* Section 2: Main Features */}
|
||||||
<div className="bg-green-50 border-l-4 border-green-400 p-6 rounded-r-lg">
|
<div className="bg-green-50 border-l-4 border-green-400 p-6 rounded-r-lg">
|
||||||
<h2 className="text-2xl font-bold text-green-800 flex items-center gap-2 mb-4">
|
<h2 className="text-2xl font-bold text-green-800 flex items-center gap-2 mb-4">
|
||||||
<span>🔧</span>
|
<span>🔧</span>
|
||||||
<span>Main Features</span>
|
<span>Main Features</span>
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-3 text-green-700">
|
<div className="space-y-3 text-green-700">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-green-600 text-lg">⚙️</div>
|
<div className="text-green-600 text-lg">⚙️</div>
|
||||||
<p>Configure characters, light cones, relics, traces, and eidolons easily in your browser.</p>
|
<p>Configure characters, light cones, relics, traces, and eidolons easily in your browser.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-green-600 text-lg">🔌</div>
|
<div className="text-green-600 text-lg">🔌</div>
|
||||||
<p>Instantly apply setups to <span className="font-semibold text-warning">Firefly GO Server</span> using <span className="font-semibold">Connect PS</span> — no manual file uploads required.</p>
|
<p>Instantly apply setups to <span className="font-semibold text-warning">Firefly GO Server</span> using <span className="font-semibold">Connect PS</span> — no manual file uploads required.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-green-600 text-2xl">✨</div>
|
<div className="text-green-600 text-2xl">✨</div>
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-green-800 text-lg">Extra Settings</h4>
|
<h4 className="font-semibold text-green-800 text-lg">Extra Settings</h4>
|
||||||
<p className="text-green-700 mt-1">
|
<p className="text-green-700 mt-1">
|
||||||
Enhance your <span className="font-semibold text-warning">Firefly GO Server</span> experience with extra features:
|
Enhance your <span className="font-semibold text-warning">Firefly GO Server</span> experience with extra features:
|
||||||
</p>
|
</p>
|
||||||
<ul className="list-disc list-inside mt-2 space-y-1 text-green-700">
|
<ul className="list-disc list-inside mt-2 space-y-1 text-green-700">
|
||||||
<li>🎭 <span className="font-medium">Hidden Game UI</span> — remove the entire game interface.</li>
|
<li>🎭 <span className="font-medium">Hidden Game UI</span> — remove the entire game interface.</li>
|
||||||
<li>🚫 <span className="font-medium">Disable Censorship</span> — get rid of Lens Flare censor 💀.</li>
|
<li>🚫 <span className="font-medium">Disable Censorship</span> — get rid of Lens Flare censor 💀.</li>
|
||||||
<li>🧪 <span className="font-medium">Theorycraft Mode</span> — configure HP, cycles, and more via the web.</li>
|
<li>🧪 <span className="font-medium">Theorycraft Mode</span> — configure HP, cycles, and more via the web.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-green-600 text-lg">📂</div>
|
<div className="text-green-600 text-lg">📂</div>
|
||||||
<p>Export and import full builds using <code className="bg-gray-200 px-2 py-1 rounded text-sm">freesr-data.json</code>.</p>
|
<p>Export and import full builds using <code className="bg-gray-200 px-2 py-1 rounded text-sm">freesr-data.json</code>.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-green-600 text-lg">⚡</div>
|
<div className="text-green-600 text-lg">⚡</div>
|
||||||
<p>Fast testing workflow — no sync cooldowns, instant in-game updates.</p>
|
<p>Fast testing workflow — no sync cooldowns, instant in-game updates.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Section 3: Getting Started */}
|
{/* Section 3: Getting Started */}
|
||||||
<div className="bg-purple-50 border-l-4 border-purple-400 p-6 rounded-r-lg">
|
<div className="bg-purple-50 border-l-4 border-purple-400 p-6 rounded-r-lg">
|
||||||
<h2 className="text-2xl font-bold text-purple-800 flex items-center gap-2 mb-4">
|
<h2 className="text-2xl font-bold text-purple-800 flex items-center gap-2 mb-4">
|
||||||
<span>🚀</span>
|
<span>🚀</span>
|
||||||
<span>Getting Started</span>
|
<span>Getting Started</span>
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-3 text-purple-700">
|
<div className="space-y-3 text-purple-700">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-purple-600 text-lg">1️⃣</div>
|
<div className="text-purple-600 text-lg">1️⃣</div>
|
||||||
<p>Access the tool through your browser at the self-hosted instance.</p>
|
<p>Access the tool through your browser at the self-hosted instance.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-purple-600 text-lg">2️⃣</div>
|
<div className="text-purple-600 text-lg">2️⃣</div>
|
||||||
<p>Configure your character builds with the intuitive web interface.</p>
|
<p>Configure your character builds with the intuitive web interface.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-purple-600 text-lg">3️⃣</div>
|
<div className="text-purple-600 text-lg">3️⃣</div>
|
||||||
<p>Use <span className="font-semibold">Connect PS</span> feature to instantly sync with your private server.</p>
|
<p>Use <span className="font-semibold">Connect PS</span> feature to instantly sync with your private server.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-purple-600 text-lg">4️⃣</div>
|
<div className="text-purple-600 text-lg">4️⃣</div>
|
||||||
<p>Test your builds in-game with real-time updates and modifications.</p>
|
<p>Test your builds in-game with real-time updates and modifications.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-center pt-4">
|
<div className="text-center pt-4">
|
||||||
<Link to="/" className="btn btn-primary btn-wide">Back to Home</Link>
|
<Link to="/" className="btn btn-primary btn-wide">Back to Home</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ import useLauncherStore from '@/stores/launcherStore';
|
|||||||
import { motion } from 'motion/react';
|
import { motion } from 'motion/react';
|
||||||
import { Link } from '@tanstack/react-router';
|
import { Link } from '@tanstack/react-router';
|
||||||
import { CheckUpdateLauncher, CheckUpdateProxy, CheckUpdateServer, sleep, UpdateLauncher, UpdateProxy, UpdateServer } from '@/helper';
|
import { CheckUpdateLauncher, CheckUpdateProxy, CheckUpdateServer, sleep, UpdateLauncher, UpdateProxy, UpdateServer } from '@/helper';
|
||||||
|
import UpdateModal from '@/components/updateModal';
|
||||||
|
|
||||||
export default function LauncherPage() {
|
export default function LauncherPage() {
|
||||||
const { gamePath,
|
const { gamePath,
|
||||||
@@ -21,6 +22,7 @@ export default function LauncherPage() {
|
|||||||
serverVersion,
|
serverVersion,
|
||||||
proxyVersion,
|
proxyVersion,
|
||||||
} = useSettingStore()
|
} = useSettingStore()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpenDownloadDataModal,
|
isOpenDownloadDataModal,
|
||||||
isOpenUpdateDataModal,
|
isOpenUpdateDataModal,
|
||||||
@@ -29,6 +31,7 @@ export default function LauncherPage() {
|
|||||||
setIsOpenUpdateDataModal,
|
setIsOpenUpdateDataModal,
|
||||||
setIsOpenSelfUpdateModal
|
setIsOpenSelfUpdateModal
|
||||||
} = useModalStore()
|
} = useModalStore()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isLoading,
|
isLoading,
|
||||||
downloadType,
|
downloadType,
|
||||||
@@ -54,6 +57,33 @@ export default function LauncherPage() {
|
|||||||
setUpdateData,
|
setUpdateData,
|
||||||
} = useLauncherStore()
|
} = 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(() => {
|
useEffect(() => {
|
||||||
const check = async () => {
|
const check = async () => {
|
||||||
if (!serverVersion || !proxyVersion) {
|
if (!serverVersion || !proxyVersion) {
|
||||||
@@ -200,18 +230,18 @@ export default function LauncherPage() {
|
|||||||
setIsDownloading(true)
|
setIsDownloading(true)
|
||||||
if (updateData.launcher.isUpdate) {
|
if (updateData.launcher.isUpdate) {
|
||||||
await UpdateLauncher(updateData.launcher.version)
|
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)
|
setIsOpenSelfUpdateModal(true)
|
||||||
}
|
}
|
||||||
if (updateData.server.isUpdate || !updateData.server.isExists) {
|
if (updateData.server.isUpdate || !updateData.server.isExists) {
|
||||||
await UpdateServer(updateData.server.version)
|
await UpdateServer(updateData.server.version)
|
||||||
setServerReady(true)
|
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) {
|
if (updateData.proxy.isUpdate || !updateData.proxy.isExists) {
|
||||||
await UpdateProxy(updateData.proxy.version)
|
await UpdateProxy(updateData.proxy.version)
|
||||||
setProxyReady(true)
|
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("")
|
setDownloadType("")
|
||||||
@@ -251,60 +281,21 @@ export default function LauncherPage() {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="hidden sm:flex fixed top-1/4 left-4 z-10 flex-col space-y-3 bg-black/30 backdrop-blur-md rounded-xl p-3 shadow-lg">
|
<div className="hidden sm:flex fixed top-1/4 left-4 z-10 flex-col space-y-3 bg-black/30 backdrop-blur-md rounded-xl p-3 shadow-lg">
|
||||||
|
{widgetLinks.map((link, idx) => (
|
||||||
<div className="tooltip tooltip-right" data-tip="Firefly SRAnalysis">
|
<div key={idx} className="tooltip tooltip-right" data-tip={link.tooltip}>
|
||||||
<a
|
<a
|
||||||
className="btn btn-circle btn-primary"
|
className={`btn btn-circle ${link.btnClass}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://sranalysis.kain.id.vn/"
|
href={link.href}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="https://sranalysis.kain.id.vn/ff-sranalysis.png"
|
src={link.img}
|
||||||
alt="SRAnalysis Logo"
|
alt={link.tooltip}
|
||||||
className="w-8 h-8 rounded-full"
|
className="w-8 h-8 rounded-full"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
<div className="tooltip tooltip-right" data-tip="Firefly SRTools">
|
|
||||||
<a
|
|
||||||
className="btn btn-circle btn-secondary"
|
|
||||||
target="_blank"
|
|
||||||
href="https://srtools.kain.id.vn/"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="https://srtools.kain.id.vn/ff-srtool.png"
|
|
||||||
alt="SRTools Logo"
|
|
||||||
className="w-8 h-8 rounded-full"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="tooltip tooltip-right" data-tip="Amazing's SRTools (Original UI)">
|
|
||||||
<a
|
|
||||||
className="btn btn-circle btn-primary"
|
|
||||||
target="_blank"
|
|
||||||
href="https://srtools.pages.dev/"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="https://icons.duckduckgo.com/ip3/srtools.pages.dev.ico"
|
|
||||||
alt="SRTools Logo"
|
|
||||||
className="w-8 h-8 rounded-full"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="tooltip tooltip-right" data-tip="Amazing's SRTools (Modern UI)">
|
|
||||||
<a
|
|
||||||
className="btn btn-circle btn-secondary"
|
|
||||||
target="_blank"
|
|
||||||
href="https://srtools.neonteam.dev/"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="https://icons.duckduckgo.com/ip3/srtools.neonteam.dev.ico"
|
|
||||||
alt="SRTools Logo"
|
|
||||||
className="w-8 h-8 rounded-full"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="tooltip tooltip-right" data-tip="How to use all tools & commands">
|
<div className="tooltip tooltip-right" data-tip="How to use all tools & commands">
|
||||||
<Link
|
<Link
|
||||||
@@ -345,32 +336,33 @@ export default function LauncherPage() {
|
|||||||
</div>
|
</div>
|
||||||
<ul tabIndex={0} className="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm">
|
<ul tabIndex={0} className="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm">
|
||||||
<li><button onClick={handlePickFile}>Change Game Path</button></li>
|
<li><button onClick={handlePickFile}>Change Game Path</button></li>
|
||||||
<li><button
|
<li>
|
||||||
onClick={async () => {
|
<button
|
||||||
const serverData = await CheckUpdateServer(serverPath, serverVersion)
|
onClick={async () => {
|
||||||
const proxyData = await CheckUpdateProxy(proxyPath, proxyVersion)
|
const serverData = await CheckUpdateServer(serverPath, serverVersion)
|
||||||
const launcherData = await CheckUpdateLauncher()
|
const proxyData = await CheckUpdateProxy(proxyPath, proxyVersion)
|
||||||
setUpdateData({
|
setUpdateData({
|
||||||
server: serverData,
|
server: serverData,
|
||||||
proxy: proxyData,
|
proxy: proxyData,
|
||||||
launcher: launcherData
|
launcher: updateData.launcher
|
||||||
})
|
})
|
||||||
if (launcherData.isUpdate) {
|
|
||||||
setIsOpenSelfUpdateModal(true)
|
if (!serverData.isExists || !proxyData.isExists) {
|
||||||
return
|
setIsOpenDownloadDataModal(true)
|
||||||
}
|
return
|
||||||
if (!serverData.isExists || !proxyData.isExists) {
|
}
|
||||||
setIsOpenDownloadDataModal(true)
|
if (serverData.isUpdate || proxyData.isUpdate) {
|
||||||
return
|
setIsOpenUpdateDataModal(true)
|
||||||
}
|
return
|
||||||
if (serverData.isUpdate || proxyData.isUpdate) {
|
}
|
||||||
setIsOpenUpdateDataModal(true)
|
toast.success("No updates available")
|
||||||
return
|
}}>
|
||||||
}
|
Check for Updates Server & Proxy
|
||||||
toast.success("No updates available")
|
</button>
|
||||||
}}>
|
</li>
|
||||||
Check for Updates
|
<li>
|
||||||
</button></li>
|
|
||||||
|
</li>
|
||||||
<li><button disabled={!serverPath} onClick={() => {
|
<li><button disabled={!serverPath} onClick={() => {
|
||||||
if (serverPath) {
|
if (serverPath) {
|
||||||
FSService.OpenFolder("./server")
|
FSService.OpenFolder("./server")
|
||||||
@@ -395,34 +387,35 @@ export default function LauncherPage() {
|
|||||||
|
|
||||||
{/* Downloading */}
|
{/* Downloading */}
|
||||||
{isDownloading && (
|
{isDownloading && (
|
||||||
updateData.proxy.isUpdate
|
updateData.proxy.isUpdate
|
||||||
|| updateData.server.isUpdate
|
|| updateData.server.isUpdate
|
||||||
|| !updateData.proxy.isExists
|
|| !updateData.proxy.isExists
|
||||||
|| !updateData.server.isExists
|
|| !updateData.server.isExists
|
||||||
) && (
|
) && (
|
||||||
<div className="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-10 w-[60vw] bg-black/20 backdrop-blur-sm rounded-lg p-4 shadow-lg">
|
<div className="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-10 w-[60vw] bg-black/20 backdrop-blur-sm rounded-lg p-4 shadow-lg">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex justify-center items-center text-sm text-white/80">
|
<div className="flex justify-center items-center text-sm text-white/80">
|
||||||
<span>{downloadType}</span>
|
<span>{downloadType}</span>
|
||||||
<div className="flex items-center gap-4 ml-4">
|
<div className="flex items-center gap-4 ml-4">
|
||||||
<span className="text-cyan-400 font-semibold">{downloadSpeed}</span>
|
<span className="text-cyan-400 font-semibold">{downloadSpeed}</span>
|
||||||
<span className="text-white font-bold">{progressDownload.toFixed(1)}%</span>
|
<span className="text-white font-bold">{progressDownload.toFixed(1)}%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-white/20 rounded-full h-2 overflow-hidden">
|
||||||
|
<motion.div
|
||||||
|
className="h-full bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full"
|
||||||
|
initial={{ width: 0 }}
|
||||||
|
animate={{ width: `${progressDownload}%` }}
|
||||||
|
transition={{ type: "tween", ease: "linear", duration: 0.03 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="text-center text-xs text-white/60">
|
||||||
|
{progressDownload < 100 ? 'Please wait...' : 'Complete!'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full bg-white/20 rounded-full h-2 overflow-hidden">
|
|
||||||
<motion.div
|
|
||||||
className="h-full bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full"
|
|
||||||
initial={{ width: 0 }}
|
|
||||||
animate={{ width: `${progressDownload}%` }}
|
|
||||||
transition={{ type: "tween", ease: "linear", duration: 0.03 }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="text-center text-xs text-white/60">
|
|
||||||
{progressDownload < 100 ? 'Please wait...' : 'Complete!'}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
|
||||||
{isDownloading && updateData.launcher.isUpdate && (
|
{isDownloading && updateData.launcher.isUpdate && (
|
||||||
<div className="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-10 w-[60vw] bg-black/20 backdrop-blur-sm rounded-lg p-4 shadow-lg">
|
<div className="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-10 w-[60vw] bg-black/20 backdrop-blur-sm rounded-lg p-4 shadow-lg">
|
||||||
<div className="space-y-3 text-sm text-white/80 text-center">
|
<div className="space-y-3 text-sm text-white/80 text-center">
|
||||||
@@ -430,10 +423,10 @@ export default function LauncherPage() {
|
|||||||
<div className="flex justify-center items-center gap-4 ml-4">
|
<div className="flex justify-center items-center gap-4 ml-4">
|
||||||
<span
|
<span
|
||||||
className={`font-bold ${downloadType === "update:launcher:downloading"
|
className={`font-bold ${downloadType === "update:launcher:downloading"
|
||||||
? "text-yellow-200 text-2xl"
|
? "text-yellow-200 text-2xl"
|
||||||
: downloadType === "update:launcher:success"
|
: downloadType === "update:launcher:success"
|
||||||
? "text-emerald-200 text-xl"
|
? "text-emerald-200 text-xl"
|
||||||
: "text-red-200 text-xl"
|
: "text-red-200 text-xl"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{downloadType === "update:launcher:downloading" && "Updating launcher"}
|
{downloadType === "update:launcher:downloading" && "Updating launcher"}
|
||||||
@@ -476,141 +469,38 @@ export default function LauncherPage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
{isOpenUpdateDataModal && (
|
<UpdateModal
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
isOpen={isOpenUpdateDataModal}
|
||||||
<div className="relative w-[90%] max-w-5xl bg-base-100 text-base-content rounded-xl border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
onClose={() => setIsOpenUpdateDataModal(false)}
|
||||||
<motion.button
|
title="Update Data"
|
||||||
whileHover={{ scale: 1.1, rotate: 90 }}
|
message="Do you want to update data server and proxy?"
|
||||||
transition={{ duration: 0.2 }}
|
buttons={[
|
||||||
className="btn btn-circle btn-md btn-error absolute right-3 top-3"
|
{ text: "No", onClick: () => setIsOpenUpdateDataModal(false), variant: "outline" },
|
||||||
onClick={() => setIsOpenUpdateDataModal(false)}
|
{ text: "Yes", onClick: async () => { setIsOpenUpdateDataModal(false); await handlerUpdateData() }, variant: "primary" }
|
||||||
>
|
]}
|
||||||
✕
|
/>
|
||||||
</motion.button>
|
|
||||||
|
|
||||||
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
<UpdateModal
|
||||||
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-400">
|
isOpen={isOpenDownloadDataModal}
|
||||||
Update Data
|
onClose={() => setIsOpenDownloadDataModal(false)}
|
||||||
</h3>
|
title="Download Data"
|
||||||
</div>
|
message="Data server and proxy download required"
|
||||||
|
buttons={[
|
||||||
|
{ text: "Download", onClick: async () => { setIsOpenDownloadDataModal(false); await handlerUpdateData() }, variant: "primary" }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="px-6 pb-6">
|
<UpdateModal
|
||||||
<div className="mb-6">
|
isOpen={isOpenSelfUpdateModal}
|
||||||
<p className="text-warning text-lg">
|
onClose={() => setIsOpenSelfUpdateModal(false)}
|
||||||
Do you want to update data server and proxy?
|
title="Update Launcher"
|
||||||
</p>
|
message="Do you want to update launcher?"
|
||||||
</div>
|
buttons={[
|
||||||
|
{ text: "No", onClick: () => setIsOpenSelfUpdateModal(false), variant: "outline" },
|
||||||
|
{ text: "Yes", onClick: async () => { setIsOpenSelfUpdateModal(false); await handlerUpdateData() }, variant: "primary" }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end gap-3">
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.05 }}
|
|
||||||
whileTap={{ scale: 0.95 }}
|
|
||||||
className="btn btn-outline btn-error"
|
|
||||||
onClick={() => setIsOpenUpdateDataModal(false)}
|
|
||||||
>
|
|
||||||
No
|
|
||||||
</motion.button>
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.05 }}
|
|
||||||
whileTap={{ scale: 0.95 }}
|
|
||||||
className="btn btn-primary bg-gradient-to-r from-orange-200 to-red-400 border-none"
|
|
||||||
onClick={async () => {
|
|
||||||
setIsOpenUpdateDataModal(false)
|
|
||||||
await handlerUpdateData()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Yes
|
|
||||||
</motion.button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isOpenDownloadDataModal && (
|
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
|
||||||
<div className="relative w-[90%] max-w-5xl bg-base-100 text-base-content rounded-xl border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
|
||||||
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
|
||||||
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-400">
|
|
||||||
Download Data
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="px-6 pb-6">
|
|
||||||
<div className="mb-6">
|
|
||||||
<p className="text-warning text-lg">
|
|
||||||
Data server and proxy download required
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end gap-3">
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.05 }}
|
|
||||||
whileTap={{ scale: 0.95 }}
|
|
||||||
className="btn btn-primary bg-gradient-to-r from-orange-200 to-red-400 border-none"
|
|
||||||
onClick={async () => {
|
|
||||||
setIsOpenDownloadDataModal(false)
|
|
||||||
await handlerUpdateData()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Download
|
|
||||||
</motion.button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isOpenSelfUpdateModal && (
|
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
|
||||||
<div className="relative w-[90%] max-w-5xl bg-base-100 text-base-content rounded-xl border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.1, rotate: 90 }}
|
|
||||||
transition={{ duration: 0.2 }}
|
|
||||||
className="btn btn-circle btn-md btn-error absolute right-3 top-3"
|
|
||||||
onClick={() => setIsOpenSelfUpdateModal(false)}
|
|
||||||
>
|
|
||||||
✕
|
|
||||||
</motion.button>
|
|
||||||
|
|
||||||
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
|
||||||
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-400">
|
|
||||||
Update Launcher
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="px-6 pb-6">
|
|
||||||
<div className="mb-6">
|
|
||||||
<p className="text-warning text-lg">
|
|
||||||
Do you want to update launcher?
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end gap-3">
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.05 }}
|
|
||||||
whileTap={{ scale: 0.95 }}
|
|
||||||
className="btn btn-outline btn-error"
|
|
||||||
onClick={() => setIsOpenSelfUpdateModal(false)}
|
|
||||||
>
|
|
||||||
No
|
|
||||||
</motion.button>
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.05 }}
|
|
||||||
whileTap={{ scale: 0.95 }}
|
|
||||||
className="btn btn-primary bg-gradient-to-r from-orange-200 to-red-400 border-none"
|
|
||||||
onClick={async () => {
|
|
||||||
setIsOpenSelfUpdateModal(false)
|
|
||||||
await handlerUpdateData()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Yes
|
|
||||||
</motion.button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,107 +1,31 @@
|
|||||||
import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
|
import { createRootRoute, Outlet } from '@tanstack/react-router'
|
||||||
import ThemeController from '../components/themeController'
|
|
||||||
import { ToastContainer } from 'react-toastify'
|
import { ToastContainer } from 'react-toastify'
|
||||||
import { useGlobalEvents } from '@/hooks';
|
import { useGlobalEvents } from '@/hooks';
|
||||||
import useLauncherStore from '@/stores/launcherStore';
|
import useModalStore from '@/stores/modalStore';;
|
||||||
import useDiffStore from '@/stores/diffStore';
|
import SettingModal from '@/components/settingModal';
|
||||||
|
import CloseModal from '@/components/closeModal';
|
||||||
|
import Header from '@/components/header';
|
||||||
|
|
||||||
export const Route = createRootRoute({
|
export const Route = createRootRoute({
|
||||||
component: RootLayout
|
component: RootLayout
|
||||||
})
|
})
|
||||||
|
|
||||||
function RootLayout() {
|
function RootLayout() {
|
||||||
const { setGameRunning, setServerRunning, setProxyRunning, setProgressDownload, setDownloadSpeed } = useLauncherStore()
|
const { setIsOpenCloseModal, isOpenCloseModal, isOpenSettingModal, setIsOpenSettingModal } = useModalStore()
|
||||||
const { setProgressUpdate, setMaxProgressUpdate, setMessageUpdate, setStageType } = useDiffStore()
|
|
||||||
useGlobalEvents({
|
useGlobalEvents();
|
||||||
setGameRunning,
|
|
||||||
setServerRunning,
|
|
||||||
setProxyRunning,
|
|
||||||
setProgressUpdate,
|
|
||||||
setMaxProgressUpdate,
|
|
||||||
setProgressDownload,
|
|
||||||
setDownloadSpeed,
|
|
||||||
setMessageUpdate,
|
|
||||||
setStageType
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="navbar bg-base-100 shadow-sm sticky top-0 z-50 px-3">
|
<Header />
|
||||||
<div className="navbar-start">
|
|
||||||
<div className="dropdown">
|
|
||||||
<div tabIndex={0} role="button" className="btn btn-ghost md:hidden">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h8m-8 6h16" /> </svg>
|
|
||||||
</div>
|
|
||||||
<ul
|
|
||||||
tabIndex={0}
|
|
||||||
className="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow">
|
|
||||||
<li><Link to="/">Home</Link></li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a>Tools</a>
|
|
||||||
<ul className="p-2">
|
|
||||||
<li><Link to="/language">Language</Link></li>
|
|
||||||
<li><Link to="/diff">Diff</Link></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a>Plugins</a>
|
|
||||||
<ul className="p-2">
|
|
||||||
<li><Link to="/analysis">Analysis (Veritas)</Link></li>
|
|
||||||
<li><Link to="/srtools">SrTools</Link></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><Link to="/howto">How to?</Link></li>
|
|
||||||
<li><Link to="/about">About</Link></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<Link to="/" className="grid grid-cols-1 items-start text-left gap-0 hover:scale-105 px-2">
|
|
||||||
<div className="flex items-center justify-center">
|
|
||||||
<img src="/appicon.png" alt="Logo" className='w-13 h-13 rounded-lg mx-2' />
|
|
||||||
<div className="flex flex-col justify-center items-start">
|
|
||||||
<h1 className="text-xl font-bold">
|
|
||||||
<span className="text-emerald-500">Firefly </span>
|
|
||||||
<span className="bg-clip-text text-transparent bg-gradient-to-r from-emerald-400 via-orange-500 to-red-500">
|
|
||||||
Launcher
|
|
||||||
</span>
|
|
||||||
</h1>
|
|
||||||
<p className="text-sm text-gray-500">By Kain</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className="navbar-center hidden md:flex">
|
|
||||||
<ul className="menu menu-horizontal px-1 gap-4">
|
|
||||||
<li><Link to="/">Home</Link></li>
|
|
||||||
<li>
|
|
||||||
<details>
|
|
||||||
<summary>Tools</summary>
|
|
||||||
<ul className="p-2">
|
|
||||||
<li><Link to="/language">Language</Link></li>
|
|
||||||
<li><Link to="/diff">Diff</Link></li>
|
|
||||||
</ul>
|
|
||||||
</details>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<details>
|
|
||||||
<summary>Plugins</summary>
|
|
||||||
<ul className="p-2">
|
|
||||||
<li><Link to="/analysis">Analysis (Veritas)</Link></li>
|
|
||||||
<li><Link to="/srtools">Firefly Tools</Link></li>
|
|
||||||
</ul>
|
|
||||||
</details>
|
|
||||||
</li>
|
|
||||||
<li><Link to="/howto">How to?</Link></li>
|
|
||||||
<li><Link to="/about">About</Link></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="navbar-end">
|
|
||||||
<ThemeController />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="min-h-[78vh]">
|
<div className="min-h-[78vh]">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<CloseModal isOpen={isOpenCloseModal} onClose={() => setIsOpenCloseModal(false)} />
|
||||||
|
<SettingModal isOpen={isOpenSettingModal} onClose={() => setIsOpenSettingModal(false)} />
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,18 +5,26 @@ interface ModalState {
|
|||||||
isOpenDownloadDataModal: boolean;
|
isOpenDownloadDataModal: boolean;
|
||||||
isOpenUpdateDataModal: boolean;
|
isOpenUpdateDataModal: boolean;
|
||||||
isOpenSelfUpdateModal: boolean;
|
isOpenSelfUpdateModal: boolean;
|
||||||
|
isOpenCloseModal: boolean;
|
||||||
|
isOpenSettingModal: boolean;
|
||||||
setIsOpenDownloadDataModal: (modal: boolean) => void;
|
setIsOpenDownloadDataModal: (modal: boolean) => void;
|
||||||
setIsOpenUpdateDataModal: (modal: boolean) => void;
|
setIsOpenUpdateDataModal: (modal: boolean) => void;
|
||||||
setIsOpenSelfUpdateModal: (modal: boolean) => void;
|
setIsOpenSelfUpdateModal: (modal: boolean) => void;
|
||||||
|
setIsOpenCloseModal: (modal: boolean) => void;
|
||||||
|
setIsOpenSettingModal: (modal: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useModalStore = create<ModalState>((set, get) => ({
|
const useModalStore = create<ModalState>((set, get) => ({
|
||||||
isOpenDownloadDataModal: false,
|
isOpenDownloadDataModal: false,
|
||||||
isOpenUpdateDataModal: false,
|
isOpenUpdateDataModal: false,
|
||||||
isOpenSelfUpdateModal: false,
|
isOpenSelfUpdateModal: false,
|
||||||
|
isOpenCloseModal: false,
|
||||||
|
isOpenSettingModal: false,
|
||||||
setIsOpenDownloadDataModal: (modal: boolean) => set({ isOpenDownloadDataModal: modal }),
|
setIsOpenDownloadDataModal: (modal: boolean) => set({ isOpenDownloadDataModal: modal }),
|
||||||
setIsOpenUpdateDataModal: (modal: boolean) => set({ isOpenUpdateDataModal: modal }),
|
setIsOpenUpdateDataModal: (modal: boolean) => set({ isOpenUpdateDataModal: modal }),
|
||||||
setIsOpenSelfUpdateModal: (modal: boolean) => set({ isOpenSelfUpdateModal: modal }),
|
setIsOpenSelfUpdateModal: (modal: boolean) => set({ isOpenSelfUpdateModal: modal }),
|
||||||
|
setIsOpenCloseModal: (modal: boolean) => set({ isOpenCloseModal: modal }),
|
||||||
|
setIsOpenSettingModal: (modal: boolean) => set({ isOpenSettingModal: modal }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useModalStore;
|
export default useModalStore;
|
||||||
@@ -10,6 +10,11 @@ interface SettingState {
|
|||||||
proxyPath: string;
|
proxyPath: string;
|
||||||
serverVersion: string;
|
serverVersion: string;
|
||||||
proxyVersion: string;
|
proxyVersion: string;
|
||||||
|
closingOption: {
|
||||||
|
isMinimize: boolean;
|
||||||
|
isAsk: boolean;
|
||||||
|
}
|
||||||
|
setClosingOption: (newClosingOption: { isMinimize: boolean; isAsk: boolean }) => void;
|
||||||
setLocale: (newLocale: string) => void;
|
setLocale: (newLocale: string) => void;
|
||||||
setGamePath: (newGamePath: string) => void;
|
setGamePath: (newGamePath: string) => void;
|
||||||
setGameDir: (newGameDir: string) => void;
|
setGameDir: (newGameDir: string) => void;
|
||||||
@@ -29,6 +34,11 @@ const useSettingStore = create<SettingState>()(
|
|||||||
proxyPath: "",
|
proxyPath: "",
|
||||||
serverVersion: "",
|
serverVersion: "",
|
||||||
proxyVersion: "",
|
proxyVersion: "",
|
||||||
|
closingOption: {
|
||||||
|
isMinimize: false,
|
||||||
|
isAsk: true,
|
||||||
|
},
|
||||||
|
setClosingOption: (newClosingOption: { isMinimize: boolean; isAsk: boolean }) => set({ closingOption: newClosingOption }),
|
||||||
setLocale: (newLocale: string) => set({ locale: newLocale }),
|
setLocale: (newLocale: string) => set({ locale: newLocale }),
|
||||||
setGamePath: (newGamePath: string) => set({ gamePath: newGamePath }),
|
setGamePath: (newGamePath: string) => set({ gamePath: newGamePath }),
|
||||||
setGameDir: (newGameDir: string) => set({ gameDir: newGameDir }),
|
setGameDir: (newGameDir: string) => set({ gameDir: newGameDir }),
|
||||||
|
|||||||
@@ -19,4 +19,19 @@ func (a *AppService) CloseAppAfterTimeout(timeout int) (bool, string) {
|
|||||||
application.Get().Quit()
|
application.Get().Quit()
|
||||||
}()
|
}()
|
||||||
return true, ""
|
return true, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AppService) CloseApp() (bool, string) {
|
||||||
|
application.Get().Quit()
|
||||||
|
return true, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AppService) MinimizeApp() (bool, string) {
|
||||||
|
window := application.Get().Window.Current()
|
||||||
|
if window == nil {
|
||||||
|
return false, "not found window"
|
||||||
|
}
|
||||||
|
window.Hide()
|
||||||
|
return true, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
12
main.go
12
main.go
@@ -17,6 +17,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/events"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed all:frontend/dist
|
//go:embed all:frontend/dist
|
||||||
@@ -84,7 +85,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Create window
|
// Create window
|
||||||
app.Window.NewWithOptions(application.WebviewWindowOptions{
|
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
|
||||||
Title: "Firefly Launcher - Kain",
|
Title: "Firefly Launcher - Kain",
|
||||||
Mac: application.MacWindow{
|
Mac: application.MacWindow{
|
||||||
InvisibleTitleBarHeight: 50,
|
InvisibleTitleBarHeight: 50,
|
||||||
@@ -106,13 +107,20 @@ func main() {
|
|||||||
// Attach the window to the system tray
|
// Attach the window to the system tray
|
||||||
menu := application.NewMenu()
|
menu := application.NewMenu()
|
||||||
menu.Add("Open").OnClick(func(ctx *application.Context) {
|
menu.Add("Open").OnClick(func(ctx *application.Context) {
|
||||||
// Handle click
|
window.Show()
|
||||||
})
|
})
|
||||||
menu.Add("Quit").OnClick(func(ctx *application.Context) {
|
menu.Add("Quit").OnClick(func(ctx *application.Context) {
|
||||||
app.Quit()
|
app.Quit()
|
||||||
})
|
})
|
||||||
|
|
||||||
systemTray.SetMenu(menu)
|
systemTray.SetMenu(menu)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) {
|
||||||
|
app.Event.Emit("window:close")
|
||||||
|
e.Cancel()
|
||||||
|
})
|
||||||
|
|
||||||
err = app.Run()
|
err = app.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const ProxyZipFile = "64bit.zip"
|
|||||||
const LauncherFile = "firefly-launcher.exe"
|
const LauncherFile = "firefly-launcher.exe"
|
||||||
const TempUrl = "./temp"
|
const TempUrl = "./temp"
|
||||||
|
|
||||||
const CurrentLauncherVersion = "2.0.1"
|
const CurrentLauncherVersion = "2.1.0"
|
||||||
|
|
||||||
type ToolFile string
|
type ToolFile string
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user