mirror of
https://github.com/open-goal/launcher.git
synced 2024-10-19 14:47:36 -04:00
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:
parent
e4b11d867c
commit
e4ea352530
|
@ -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",
|
||||
|
|
|
@ -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 /> {$_("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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Reference in a new issue