From 5ce8aa76ab1adc4f038a29ecbe4037df9a6e8d38 Mon Sep 17 00:00:00 2001 From: Lncvrt Date: Wed, 29 Jul 2026 06:26:52 -0700 Subject: [PATCH] WIP home page & rework some stuff --- bun.lock | 12 +++- package.json | 4 +- src/app/main/layout.tsx | 65 ++++++++++++---------- src/app/main/page.tsx | 95 ++++++++++++++++++++++++++++++++ src/popups/ManageVersion.tsx | 76 ++++--------------------- src/providers/GlobalProvider.tsx | 1 + 6 files changed, 154 insertions(+), 99 deletions(-) create mode 100644 src/app/main/page.tsx diff --git a/bun.lock b/bun.lock index 587f213..c847594 100644 --- a/bun.lock +++ b/bun.lock @@ -22,12 +22,14 @@ "@tauri-apps/plugin-store": "2.4.4", "@tauri-apps/plugin-updater": "2.10.1", "@tauri-apps/plugin-window-state": "2.4.1", + "date-fns": "4.4.0", "dompurify": "3.4.12", "next": "16.2.12", "pretty-bytes": "7.1.1", "react": "19.2.8", "react-dom": "19.2.8", "semver": "7.8.5", + "swiper": "14.0.6", }, "devDependencies": { "@eslint/eslintrc": "3.3.6", @@ -425,7 +427,7 @@ "baseline-browser-mapping": ["baseline-browser-mapping@2.11.6", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-69D/imtToCsIcAl8WBS2YaRwA4jO/j0HhU+hELqMEu9f54MoUtI6+XH5mrKU8rEFNEk/Ui1I2MK4/JkWacclGw=="], - "brace-expansion": ["brace-expansion@1.1.16", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw=="], + "brace-expansion": ["brace-expansion@1.1.17", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], @@ -467,6 +469,8 @@ "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], + "date-fns": ["date-fns@4.4.0", "", {}, "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], @@ -483,7 +487,7 @@ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - "electron-to-chromium": ["electron-to-chromium@1.5.397", "", {}, "sha512-khGTy9U9x02KEtsKM8vx5A62BsRmcOsIgDpWr1ImE32Ax8GxHGPHZf+Eu9H8zOOyHJnB0jTbseyTHbq2XCT8yw=="], + "electron-to-chromium": ["electron-to-chromium@1.5.398", "", {}, "sha512-AsvhAxopJGh6museTDMIjn6JpDYOfgu4RLlygomt87MUwBUqTfd/1EiPtx10/LZE8xpTvkP2E9Gafq7lkLtodQ=="], "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], @@ -803,7 +807,7 @@ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - "postcss": ["postcss@8.5.24", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-8RyVklq0owXUTa4xlpzu4l9AaVKIdQvAcOHZWaMh98HgySsUtxRVf/chRe3dsSLqb6i40BzGRzEUddRaI+9TSw=="], + "postcss": ["postcss@8.5.25", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw=="], "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], @@ -893,6 +897,8 @@ "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + "swiper": ["swiper@14.0.6", "", {}, "sha512-ArptjmcZMBvpLCbvzSKuqvQpjjITCbZdymiGOrbZQffQ3ZSjgpvMByXX0WZBUt+FV4PPIumdYfZ9ZdBbFsdH6g=="], + "tailwindcss": ["tailwindcss@4.3.3", "", {}, "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ=="], "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], diff --git a/package.json b/package.json index fda43d8..2c654dc 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,14 @@ "@tauri-apps/plugin-store": "2.4.4", "@tauri-apps/plugin-updater": "2.10.1", "@tauri-apps/plugin-window-state": "2.4.1", + "date-fns": "4.4.0", "dompurify": "3.4.12", "next": "16.2.12", "pretty-bytes": "7.1.1", "react": "19.2.8", "react-dom": "19.2.8", - "semver": "7.8.5" + "semver": "7.8.5", + "swiper": "14.0.6" }, "devDependencies": { "@eslint/eslintrc": "3.3.6", diff --git a/src/app/main/layout.tsx b/src/app/main/layout.tsx index 74df8f1..80273d7 100644 --- a/src/app/main/layout.tsx +++ b/src/app/main/layout.tsx @@ -384,6 +384,39 @@ export default function RootLayout ({ ) } + const uninstallVersion = async ( + version: string, + updateList: boolean = true + ) => { + if (updateList) + await versions?.set( + 'list', + Object.fromEntries( + Object.entries(versionsList).filter(([k]) => k !== version) + ) + ) + + if ( + await exists( + (customDataLocation ? customDataLocation + '/' : null) + + 'game/' + + version, + { + baseDir: customDataLocation ? undefined : BaseDirectory.AppLocalData + } + ) + ) + await remove( + (customDataLocation ? customDataLocation + '/' : null) + + 'game/' + + version, + { + baseDir: customDataLocation ? undefined : BaseDirectory.AppLocalData, + recursive: true + } + ) + } + const launchGame = async (versionInfo: GameVersion) => { if (needsRevisionUpdate(versionInfo.lastRevision, versionInfo.id)) { const answer = await ask( @@ -409,36 +442,7 @@ export default function RootLayout ({ setFadeOut(false) //uninstall - await versions?.set( - 'list', - Object.fromEntries( - Object.entries(versionsList).filter(([k]) => k !== versionInfo.id) - ) - ) - - if ( - await exists( - customDataLocation - ? customDataLocation + '/' - : null + 'game/' + versionInfo.id, - { - baseDir: customDataLocation - ? undefined - : BaseDirectory.AppLocalData - } - ) - ) - await remove( - customDataLocation - ? customDataLocation + '/' - : null + 'game/' + versionInfo.id, - { - baseDir: customDataLocation - ? undefined - : BaseDirectory.AppLocalData, - recursive: true - } - ) + await uninstallVersion(versionInfo.id) //reinstall setSelectedVersionList([versionInfo.id]) @@ -912,6 +916,7 @@ export default function RootLayout ({ managingGame, setManagingGame, needsRevisionUpdate, + uninstallVersion, launchGame }} > diff --git a/src/app/main/page.tsx b/src/app/main/page.tsx new file mode 100644 index 0000000..1c3c71c --- /dev/null +++ b/src/app/main/page.tsx @@ -0,0 +1,95 @@ +'use client' + +import { useGlobal } from '@/providers/GlobalProvider' +import 'swiper/css' +import { Swiper, SwiperSlide } from 'swiper/react' +import { formatDistanceToNow } from 'date-fns' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faEllipsis, faPlay } from '@fortawesome/free-solid-svg-icons' + +export default function HomePage () { + const { + launchesList, + serverVersionList, + accountName, + launchGame, + setManagingVersion, + setPopupMode, + setShowPopup, + setFadeOut + } = useGlobal() + + const filteredLaunchesList = Object.fromEntries( + Object.entries(launchesList) + .flatMap(([gameId, launches]) => { + const entries = Object.entries(launches) + + if (entries.length === 0) return [] + + const [version, timestamp] = entries.reduce((latest, current) => + current[1] > latest[1] ? current : latest + ) + + return [[`${gameId}-${version}`, timestamp] as const] + }) + .sort((a, b) => b[1] - a[1]) + ) + + return ( +
+
+

Home

+

Welcome back, {accountName ?? 'User'}!

+
+
+

Jump back in

+ {Object.entries(filteredLaunchesList).length == 0 ? ( +

You have no recently launched games

+ ) : ( + + {Object.entries(filteredLaunchesList).map(([key, timestamp]) => { + const versionInfo = serverVersionList?.versions.find( + v => v.id == key.split('-').slice(1).join('-') + ) + if (!versionInfo) return + + return ( + +

+ {versionInfo?.displayName} +

+

+ Last played:{' '} + {formatDistanceToNow(new Date(timestamp), { + addSuffix: true + })} +

+
+ await launchGame(versionInfo)} + /> + { + setManagingVersion(versionInfo.id) + setPopupMode(2) + setShowPopup(true) + setFadeOut(false) + }} + /> +
+
+ ) + })} +
+ )} +
+
+ ) +} diff --git a/src/popups/ManageVersion.tsx b/src/popups/ManageVersion.tsx index da2b41b..0ea6b02 100644 --- a/src/popups/ManageVersion.tsx +++ b/src/popups/ManageVersion.tsx @@ -9,7 +9,6 @@ import { useGlobal } from '@/providers/GlobalProvider' import { invoke } from '@tauri-apps/api/core' import { useEffect, useState } from 'react' import prettyBytes from 'pretty-bytes' -import { BaseDirectory, exists, remove } from '@tauri-apps/plugin-fs' import { openFolder } from '@/lib/util' export default function ManageVersionPopup () { @@ -23,8 +22,9 @@ export default function ManageVersionPopup () { setPopupMode, setSelectedVersionList, downloadVersions, + uninstallVersion, versions, - customDataLocation + launchesList } = useGlobal() const [versionSize, setVersionSize] = useState(0) @@ -114,39 +114,16 @@ export default function ManageVersionPopup () { className='entry-info-item btntheme2' onClick={async () => { closePopup() + uninstallVersion(managingVersion) + if (!versionInfo) return - await versions?.set( - 'list', - Object.fromEntries( - Object.entries(versionsList).filter( - ([k]) => k !== managingVersion - ) - ) - ) + const gameLaunches = { ...launchesList[versionInfo.game] } + delete gameLaunches[versionInfo.id] - if ( - await exists( - (customDataLocation ? customDataLocation + '/' : null) + - 'game/' + - managingVersion, - { - baseDir: customDataLocation - ? undefined - : BaseDirectory.AppLocalData - } - ) - ) - await remove( - (customDataLocation ? customDataLocation + '/' : null) + - 'game/' + - managingVersion, - { - baseDir: customDataLocation - ? undefined - : BaseDirectory.AppLocalData, - recursive: true - } - ) + await versions?.set('launches', { + ...launchesList, + [versionInfo.game]: gameLaunches + }) }} title='Click to uninstall this game. This will NOT remove any progress or any save files.' > @@ -160,38 +137,7 @@ export default function ManageVersionPopup () { setPopupMode(1) //uninstall - await versions?.set( - 'list', - Object.fromEntries( - Object.entries(versionsList).filter( - ([k]) => k !== managingVersion - ) - ) - ) - - if ( - await exists( - (customDataLocation ? customDataLocation + '/' : null) + - 'game/' + - managingVersion, - { - baseDir: customDataLocation - ? undefined - : BaseDirectory.AppLocalData - } - ) - ) - await remove( - (customDataLocation ? customDataLocation + '/' : null) + - 'game/' + - managingVersion, - { - baseDir: customDataLocation - ? undefined - : BaseDirectory.AppLocalData, - recursive: true - } - ) + uninstallVersion(managingVersion) //reinstall setSelectedVersionList([managingVersion]) diff --git a/src/providers/GlobalProvider.tsx b/src/providers/GlobalProvider.tsx index 4d22151..ce1d529 100644 --- a/src/providers/GlobalProvider.tsx +++ b/src/providers/GlobalProvider.tsx @@ -73,6 +73,7 @@ type GlobalCtxType = { lastRevision: number | undefined, version: string ) => boolean + uninstallVersion: (version: string, updateList?: boolean) => Promise launchGame: (versionInfo: GameVersion) => Promise }