Show mod description/tags/author. Allow mod preview before installing (#549)

![image](https://github.com/user-attachments/assets/64d03e0b-6fd0-4f44-9871-bf4e8b78a1e7)

![image](https://github.com/user-attachments/assets/d976a588-4aca-4a0d-8859-3b9ad87d613e)

When not yet installed, clicking a mod now brings you to the mod page
and requires Install / Version selection before you can launch the game.
Advanced/settings options are disabled.

![image](https://github.com/user-attachments/assets/477f7db0-366e-4bb4-82ed-f427c6edbd66)

If you somehow get here and we don't have a list of versions (e.g.
you're offline), the Install button is also disabled

![image](https://github.com/user-attachments/assets/56dd85b5-af37-4104-9398-5954923e512b)
This commit is contained in:
Matt Dallmeyer 2024-09-02 13:13:08 -07:00 committed by GitHub
parent e4b11d867c
commit e4ea352530
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 202 additions and 142 deletions

View file

@ -33,6 +33,7 @@
"gameControls_button_openGameFolder": "Open Game Data Folder",
"gameControls_button_openSavesFolder": "Open Saves Folder",
"gameControls_button_openSettingsFolder": "Open Settings Folder",
"gameControls_button_install": "Install",
"gameControls_button_play": "Play",
"gameControls_button_playInDebug": "Play in Debug Mode",
"gameControls_button_resetSettings": "Reset Settings",
@ -211,6 +212,8 @@
"features_mods_go_back": "Back",
"features_mods_versions": "Versions",
"features_mods_filter_placeholder": "Filter Mods..",
"features_mods_authors": "Author(s)",
"features_mods_tags": "Tag(s)",
"toasts_copiedToClipboard": "Copied to clipboard",
"toasts_savedToolingVersion": "Saved tooling version",
"toasts_modSourceUnreachable": "Mod source unreachable",

View file

@ -44,6 +44,9 @@
export let activeGame: SupportedGame;
export let modName: string = "";
export let modDisplayName: string = "";
export let modDescription: string = "";
export let modTags: string = "";
export let modAuthors: string = "";
export let modSource: string = "";
const dispatch = createEventDispatcher();
@ -180,12 +183,27 @@
}
</script>
<div class="flex flex-col justify-end items-end mt-auto">
<div
class="[margin-left:35%] p-3 rounded-lg flex flex-col justify-end items-end mt-auto [background-color:rgba(0,0,0,.5)]"
>
<h1
class="tracking-tighter text-2xl font-bold pb-3 text-orange-500 text-outline pointer-events-none"
class="tracking-tighter text-2xl font-bold pb-2 text-orange-500 text-outline pointer-events-none"
>
{modDisplayName}
</h1>
<h1
class="tracking-tighter pb-2 font-bold text-outline text-justify [text-align-last:right]"
>
{modDescription}
</h1>
<p class="pb-2 text-outline">
{$_("features_mods_tags")}: {modTags}
</p>
<p class="text-outline">
{$_("features_mods_authors")}: {modAuthors}
</p>
</div>
<div class="flex flex-col justify-end items-end mt-3">
<div class="flex flex-row gap-2">
<Button
class="border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
@ -195,16 +213,35 @@
});
}}><IconArrowLeft />&nbsp;{$_("features_mods_go_back")}</Button
>
<!-- show Play button if we have no version list (offline), if we're up to date, or we dont want forced updates -->
{#if modVersionListSorted.length == 0 || modVersionListSorted[0] === currentlyInstalledVersion || !checkForLatestModVersionChecked}
{#if currentlyInstalledVersion == "" && modVersionListSorted.length == 0}
<!-- show disabled Install button if no version installed and we have no version list (offline) -->
<Button
class="border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
disabled>{$_("gameControls_button_install")}</Button
>
{:else if currentlyInstalledVersion == ""}
<!-- show Install button if no version installed but we're online -->
<Button
class="border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
on:click={async () => {
await addModFromUrl(
modAssetUrlsSorted[0],
modName,
modSource,
modVersionListSorted[0],
);
}}>{$_("gameControls_button_install")}</Button
>
{:else if modVersionListSorted.length == 0 || modVersionListSorted[0] === currentlyInstalledVersion || !checkForLatestModVersionChecked}
<!-- show Play button if we have no version list (offline), if we're up to date, or we dont want forced updates -->
<Button
class="border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
on:click={async () => {
launchMod(getInternalName(activeGame), false, modName, modSource);
}}>{$_("gameControls_button_play")}</Button
>
<!-- otherwise show Update button -->
{:else}
<!-- otherwise show Update button -->
<Button
class="border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800 text-sm text-white font-semibold px-5 py-2"
on:click={async () => {
@ -273,132 +310,158 @@
{/each}
</Dropdown>
{/if}
<Button
class="text-center font-semibold focus:ring-0 focus:outline-none inline-flex items-center justify-center px-2 py-2 text-sm text-white border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800"
>
{$_("gameControls_button_advanced")}
</Button>
<Dropdown placement="top-end" class="!bg-slate-900">
<DropdownItem
on:click={async () => {
launchMod(getInternalName(activeGame), true, modName, modSource);
}}>{$_("gameControls_button_playInDebug")}</DropdownItem
{#if currentlyInstalledVersion == ""}
<!-- Disabled "advanced" button if not installed -->
<Button
class="text-center font-semibold focus:ring-0 focus:outline-none inline-flex items-center justify-center px-2 py-2 text-sm text-white border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800"
disabled
>
{#if !isLinux}
{$_("gameControls_button_advanced")}
</Button>
{:else}
<Button
class="text-center font-semibold focus:ring-0 focus:outline-none inline-flex items-center justify-center px-2 py-2 text-sm text-white border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800"
>
{$_("gameControls_button_advanced")}
</Button>
<Dropdown placement="top-end" class="!bg-slate-900">
<DropdownItem
on:click={async () => {
openREPLForMod(getInternalName(activeGame), modName, modSource);
}}>{$_("gameControls_button_openREPL")}</DropdownItem
launchMod(getInternalName(activeGame), true, modName, modSource);
}}>{$_("gameControls_button_playInDebug")}</DropdownItem
>
{/if}
<DropdownDivider />
<DropdownItem
on:click={async () => {
dispatch("job", {
type: "decompileMod",
});
}}
>{$_("gameControls_button_decompile")}
<!-- NOTE - this is a bug in flowbite-svelte, it's not replacing the default class but just appending -->
<Helper helperClass="!text-neutral-400 !text-xs"
>{$_("gameControls_button_decompile_helpText")}</Helper
></DropdownItem
>
<DropdownItem
on:click={async () => {
dispatch("job", {
type: "compileMod",
});
}}
>{$_("gameControls_button_compile")}
<!-- NOTE - this is a bug in flowbite-svelte, it's not replacing the default class but just appending -->
<Helper helperClass="!text-neutral-400 !text-xs"
>{$_("gameControls_button_compile_helpText")}
</Helper></DropdownItem
>
<DropdownDivider />
<DropdownItem
on:click={async () => {
if (gameDataDir) {
await openDir(gameDataDir);
}
}}>{$_("gameControls_button_openGameFolder")}</DropdownItem
>
</Dropdown>
<Button
class="text-center font-semibold focus:ring-0 focus:outline-none inline-flex items-center justify-center px-2 py-2 text-sm text-white border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800"
>
<IconCog />
</Button>
<Dropdown placement="top-end" class="!bg-slate-900">
<!-- TODO - screenshot folder? how do we even configure where those go? -->
{#if settingsDir}
<DropdownItem
on:click={async () => {
if (settingsDir) {
await openDir(settingsDir);
}
}}>{$_("gameControls_button_openSettingsFolder")}</DropdownItem
>
{/if}
{#if savesDir}
<DropdownItem
on:click={async () => {
if (savesDir) {
await openDir(savesDir);
}
}}>{$_("gameControls_button_openSavesFolder")}</DropdownItem
>
{/if}
{#if settingsDir || savesDir}
{#if !isLinux}
<DropdownItem
on:click={async () => {
openREPLForMod(getInternalName(activeGame), modName, modSource);
}}>{$_("gameControls_button_openREPL")}</DropdownItem
>
{/if}
<DropdownDivider />
{/if}
<DropdownItem
on:click={async () => {
const launchString = await getLaunchModString(
getInternalName(activeGame),
modName,
modSource,
);
await writeText(launchString);
toastStore.makeToast($_("toasts_copiedToClipboard"), "info");
}}
>{$_("gameControls_button_copyExecutableCommand")}<Helper
helperClass="!text-neutral-400 !text-xs"
>{$_("gameControls_button_copyExecutableCommand_helpText_1")}<br
/>{$_("gameControls_button_copyExecutableCommand_helpText_2")}</Helper
></DropdownItem
>
<DropdownDivider />
<DropdownItem
on:click={async () => {
await resetModSettings(
getInternalName(activeGame),
modName,
modSource,
);
}}>{$_("gameControls_button_resetSettings")}</DropdownItem
>
<DropdownItem
on:click={async () => {
// Get confirmation
// TODO - probably move these confirms into the actual launcher itself
const confirmed = await confirm(
$_("gameControls_button_uninstall_confirmation"),
{ title: "OpenGOAL Launcher", type: "warning" },
);
if (confirmed) {
await uninstallMod(getInternalName(activeGame), modName, modSource);
navigate(`/${getInternalName(activeGame)}/features/mods`, {
replace: true,
<DropdownItem
on:click={async () => {
dispatch("job", {
type: "decompileMod",
});
}
}}
>{$_("gameControls_button_uninstall")}<Helper
helperClass="!text-neutral-400 !text-xs"
>{$_("gameControls_button_uninstall_helpText")}</Helper
></DropdownItem
}}
>{$_("gameControls_button_decompile")}
<!-- NOTE - this is a bug in flowbite-svelte, it's not replacing the default class but just appending -->
<Helper helperClass="!text-neutral-400 !text-xs"
>{$_("gameControls_button_decompile_helpText")}</Helper
></DropdownItem
>
<DropdownItem
on:click={async () => {
dispatch("job", {
type: "compileMod",
});
}}
>{$_("gameControls_button_compile")}
<!-- NOTE - this is a bug in flowbite-svelte, it's not replacing the default class but just appending -->
<Helper helperClass="!text-neutral-400 !text-xs"
>{$_("gameControls_button_compile_helpText")}
</Helper></DropdownItem
>
<DropdownDivider />
<DropdownItem
on:click={async () => {
if (gameDataDir) {
await openDir(gameDataDir);
}
}}>{$_("gameControls_button_openGameFolder")}</DropdownItem
>
</Dropdown>
{/if}
{#if currentlyInstalledVersion == ""}
<!-- Disabled cog/settings button if not installed -->
<Button
class="text-center font-semibold focus:ring-0 focus:outline-none inline-flex items-center justify-center px-2 py-2 text-sm text-white border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800"
disabled
>
</Dropdown>
<IconCog />
</Button>
{:else}
<Button
class="text-center font-semibold focus:ring-0 focus:outline-none inline-flex items-center justify-center px-2 py-2 text-sm text-white border-solid border-2 border-slate-900 rounded bg-slate-900 hover:bg-slate-800"
>
<IconCog />
</Button>
<Dropdown placement="top-end" class="!bg-slate-900">
<!-- TODO - screenshot folder? how do we even configure where those go? -->
{#if settingsDir}
<DropdownItem
on:click={async () => {
if (settingsDir) {
await openDir(settingsDir);
}
}}>{$_("gameControls_button_openSettingsFolder")}</DropdownItem
>
{/if}
{#if savesDir}
<DropdownItem
on:click={async () => {
if (savesDir) {
await openDir(savesDir);
}
}}>{$_("gameControls_button_openSavesFolder")}</DropdownItem
>
{/if}
{#if settingsDir || savesDir}
<DropdownDivider />
{/if}
<DropdownItem
on:click={async () => {
const launchString = await getLaunchModString(
getInternalName(activeGame),
modName,
modSource,
);
await writeText(launchString);
toastStore.makeToast($_("toasts_copiedToClipboard"), "info");
}}
>{$_("gameControls_button_copyExecutableCommand")}<Helper
helperClass="!text-neutral-400 !text-xs"
>{$_("gameControls_button_copyExecutableCommand_helpText_1")}<br
/>{$_(
"gameControls_button_copyExecutableCommand_helpText_2",
)}</Helper
></DropdownItem
>
<DropdownDivider />
<DropdownItem
on:click={async () => {
await resetModSettings(
getInternalName(activeGame),
modName,
modSource,
);
}}>{$_("gameControls_button_resetSettings")}</DropdownItem
>
<DropdownItem
on:click={async () => {
// Get confirmation
// TODO - probably move these confirms into the actual launcher itself
const confirmed = await confirm(
$_("gameControls_button_uninstall_confirmation"),
{ title: "OpenGOAL Launcher", type: "warning" },
);
if (confirmed) {
await uninstallMod(
getInternalName(activeGame),
modName,
modSource,
);
navigate(`/${getInternalName(activeGame)}/features/mods`, {
replace: true,
});
}
}}
>{$_("gameControls_button_uninstall")}<Helper
helperClass="!text-neutral-400 !text-xs"
>{$_("gameControls_button_uninstall_helpText")}</Helper
></DropdownItem
>
</Dropdown>
{/if}
</div>
</div>

View file

@ -339,24 +339,9 @@
modInfo,
)}'); background-size: cover;"
on:click={async () => {
// Install the mod
const assetUrl = getModAssetUrlFromLatestVersion(
userPlatform,
modInfo,
navigate(
`/${getInternalName(activeGame)}/features/mods/${encodeURI(sourceInfo.sourceName)}/${encodeURI(modName)}`,
);
if (assetUrl !== undefined) {
await addModFromUrl(
assetUrl,
modName,
sourceInfo.sourceName,
modInfo.versions[0].version,
);
} else {
toastStore.makeToast(
$_("toasts_unableToRetrieveModDownloadURL"),
"error",
);
}
}}
>
<h3 class="pointer-events-none select-none text-outline">

View file

@ -39,6 +39,9 @@
let activeGame = SupportedGame.Jak1;
let modDisplayName: string | undefined = undefined;
let modDescription: string | undefined = undefined;
let modTags: string | undefined = undefined;
let modAuthors: string | undefined = undefined;
let componentLoaded = false;
let gameInstalled = false;
@ -97,6 +100,9 @@
// Prefer pre-game-config if available
if (foundMod !== undefined) {
modDisplayName = foundMod.displayName;
modDescription = foundMod.description;
modTags = foundMod.tags.join(", ");
modAuthors = foundMod.authors.join(", ");
} else {
modDisplayName = modName;
}
@ -245,6 +251,9 @@
{activeGame}
{modName}
{modDisplayName}
{modDescription}
{modTags}
{modAuthors}
{modSource}
on:change={updateGameState}
on:job={runGameJob}