You've already forked AstralRinth
forked from didirus/AstralRinth
Server Content Tab Fixes & Improvements (#3230)
* fix cancel button on edit modal * make hardcoded mod text dynamic for plugins * fix files path when clicking an external plugin * fix plugins path for file uploads * improve friendly mod name logic * fix toggling plugins * update pyroServers content definitions * install then remove for changing version Reinstall isn't currently implemented properly * make the edit dialog pretty * make new admonition component * fix warning admonition colour * new edit version modal * cleanup * make latest version default * final touches * lint
This commit is contained in:
@@ -1,57 +1,14 @@
|
||||
<template>
|
||||
<NewModal ref="modModal" header="Editing mod version">
|
||||
<div>
|
||||
<div class="mb-4 flex flex-col gap-4">
|
||||
<div class="inline-flex flex-wrap items-center">
|
||||
You're changing the version of
|
||||
<div class="inline-flex flex-wrap items-center gap-1 text-nowrap pl-2">
|
||||
<UiAvatar
|
||||
:src="currentMod?.icon_url"
|
||||
size="24px"
|
||||
class="inline-block"
|
||||
alt="Server Icon"
|
||||
/>
|
||||
<strong>{{ currentMod?.name + "." }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="props.server.general?.upstream" class="flex gap-2">
|
||||
<InfoIcon class="hidden sm:block" />
|
||||
<span class="text-sm text-secondary">
|
||||
Changing the mod version may cause unexpected issues. Because your server was created
|
||||
from a modpack, it is recommended to use the modpack's version of the mod.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<UiServersTeleportDropdownMenu
|
||||
v-model="currentVersion"
|
||||
name="Project"
|
||||
:options="currentVersions"
|
||||
placeholder="Select project..."
|
||||
class="!w-full"
|
||||
:display-name="
|
||||
(version) => (typeof version === 'object' ? version?.version_number : version)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4 flex flex-row items-center gap-4">
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="currentMod.changing" @click="changeModVersion">
|
||||
<PlusIcon />
|
||||
Install
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="modModal.value.hide()">
|
||||
<XIcon />
|
||||
Cancel
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
</NewModal>
|
||||
<UiServersContentVersionEditModal
|
||||
v-if="!invalidModal"
|
||||
ref="versionEditModal"
|
||||
:type="type"
|
||||
:mod-pack="Boolean(props.server.general?.upstream)"
|
||||
:game-version="props.server.general?.mc_version ?? ''"
|
||||
:loader="props.server.general?.loader?.toLowerCase() ?? ''"
|
||||
:server-id="props.server.serverId"
|
||||
@change-version="changeModVersion($event)"
|
||||
/>
|
||||
|
||||
<div v-if="server.general && localMods" class="relative isolate flex h-full w-full flex-col">
|
||||
<div ref="pyroContentSentinel" class="sentinel" data-pyro-content-sentinel />
|
||||
@@ -123,7 +80,7 @@
|
||||
class="rounded-xl bg-bg-raised"
|
||||
:margin-bottom="16"
|
||||
:file-type="type"
|
||||
:current-path="'/mods'"
|
||||
:current-path="`/${type.toLocaleLowerCase()}s`"
|
||||
:fs="props.server.fs"
|
||||
:accepted-types="acceptFileFromProjectType(type.toLocaleLowerCase()).split(',')"
|
||||
@upload-complete="() => props.server.refresh(['content'])"
|
||||
@@ -149,7 +106,7 @@
|
||||
:to="
|
||||
mod.project_id
|
||||
? `/project/${mod.project_id}/version/${mod.version_id}`
|
||||
: `files?path=mods`
|
||||
: `files?path=${type.toLocaleLowerCase()}s`
|
||||
"
|
||||
class="flex min-w-0 flex-1 items-center gap-2 rounded-xl p-2"
|
||||
draggable="false"
|
||||
@@ -162,9 +119,7 @@
|
||||
/>
|
||||
<div class="flex min-w-0 flex-col gap-1">
|
||||
<span class="text-md flex min-w-0 items-center gap-2 font-bold">
|
||||
<span class="truncate text-contrast">{{
|
||||
mod.name || mod.filename.replace(".disabled", "")
|
||||
}}</span>
|
||||
<span class="truncate text-contrast">{{ friendlyModName(mod) }}</span>
|
||||
<span
|
||||
v-if="mod.disabled"
|
||||
class="hidden rounded-full bg-button-bg p-1 px-2 text-xs text-contrast sm:block"
|
||||
@@ -174,19 +129,21 @@
|
||||
<div class="min-w-0 text-xs text-secondary">
|
||||
<span v-if="mod.owner" class="hidden sm:block"> by {{ mod.owner }} </span>
|
||||
<span class="block font-semibold sm:hidden">
|
||||
{{ mod.version_number || "External mod" }}
|
||||
{{ mod.version_number || `External ${type.toLocaleLowerCase()}` }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
<div class="ml-2 hidden min-w-0 flex-1 flex-col text-sm sm:flex">
|
||||
<div class="truncate font-semibold text-contrast">
|
||||
<span v-tooltip="'Mod version'">{{
|
||||
mod.version_number || "External mod"
|
||||
<span v-tooltip="`${type} version`">{{
|
||||
mod.version_number || `External ${type.toLocaleLowerCase()}`
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="truncate">
|
||||
<span v-tooltip="'Mod file name'">{{ mod.filename }}</span>
|
||||
<span v-tooltip="`${type} file name`">
|
||||
{{ mod.filename }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -194,7 +151,7 @@
|
||||
>
|
||||
<ButtonStyled color="red" type="transparent">
|
||||
<button
|
||||
v-tooltip="'Delete mod'"
|
||||
v-tooltip="`Delete ${type.toLocaleLowerCase()}`"
|
||||
:disabled="mod.changing"
|
||||
class="!hidden sm:!block"
|
||||
@click="removeMod(mod)"
|
||||
@@ -205,14 +162,16 @@
|
||||
<ButtonStyled type="transparent">
|
||||
<button
|
||||
v-tooltip="
|
||||
mod.project_id ? 'Edit mod version' : 'External mods cannot be edited'
|
||||
mod.project_id
|
||||
? `Edit ${type.toLocaleLowerCase()} version`
|
||||
: `External ${type.toLocaleLowerCase()}s cannot be edited`
|
||||
"
|
||||
:disabled="mod.changing || !mod.project_id"
|
||||
class="!hidden sm:!block"
|
||||
@click="beginChangeModVersion(mod)"
|
||||
@click="showVersionModal(mod)"
|
||||
>
|
||||
<template v-if="mod.changing">
|
||||
<UiServersIconsLoadingIcon />
|
||||
<UiServersIconsLoadingIcon class="animate-spin" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<EditIcon />
|
||||
@@ -232,7 +191,7 @@
|
||||
:options="[
|
||||
{
|
||||
id: 'edit',
|
||||
action: () => beginChangeModVersion(mod),
|
||||
action: () => showVersionModal(mod),
|
||||
shown: !!(mod.project_id && !mod.changing),
|
||||
},
|
||||
{
|
||||
@@ -357,8 +316,6 @@ import {
|
||||
PackageClosedIcon,
|
||||
FilterIcon,
|
||||
DropdownIcon,
|
||||
InfoIcon,
|
||||
XIcon,
|
||||
PlusIcon,
|
||||
MoreVerticalIcon,
|
||||
CompassIcon,
|
||||
@@ -366,7 +323,7 @@ import {
|
||||
ListIcon,
|
||||
FileIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { ButtonStyled, NewModal } from "@modrinth/ui";
|
||||
import { ButtonStyled } from "@modrinth/ui";
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from "vue";
|
||||
import FilesUploadDragAndDrop from "~/components/ui/servers/FilesUploadDragAndDrop.vue";
|
||||
import FilesUploadDropdown from "~/components/ui/servers/FilesUploadDropdown.vue";
|
||||
@@ -401,6 +358,64 @@ const filterMethod = ref("all");
|
||||
|
||||
const uploadDropdownRef = ref();
|
||||
|
||||
const versionEditModal = ref();
|
||||
const currentEditMod = ref<ContentItem | null>(null);
|
||||
const invalidModal = computed(
|
||||
() => !props.server.general?.mc_version || !props.server.general?.loader,
|
||||
);
|
||||
async function changeModVersion(event: string) {
|
||||
const mod = currentEditMod.value;
|
||||
|
||||
if (mod) mod.changing = true;
|
||||
|
||||
try {
|
||||
versionEditModal.value.hide();
|
||||
|
||||
// This will be used instead once backend implementation is done
|
||||
// await props.server.content?.reinstall(
|
||||
// `/${type.value.toLowerCase()}s/${event.fileName}`,
|
||||
// currentMod.value.project_id,
|
||||
// currentVersion.value.id,
|
||||
// );
|
||||
|
||||
await props.server.content?.install(
|
||||
type.value.toLowerCase() as "mod" | "plugin",
|
||||
mod?.project_id || "",
|
||||
event,
|
||||
);
|
||||
|
||||
await props.server.content?.remove(`/${type.value.toLowerCase()}s/${mod?.filename}`);
|
||||
|
||||
await props.server.refresh(["general", "content"]);
|
||||
} catch (error) {
|
||||
const errmsg = `Error changing mod version: ${error}`;
|
||||
console.error(errmsg);
|
||||
addNotification({
|
||||
text: errmsg,
|
||||
type: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (mod) mod.changing = false;
|
||||
}
|
||||
|
||||
function showVersionModal(mod: ContentItem) {
|
||||
if (invalidModal.value || !mod?.project_id || !mod?.filename) {
|
||||
const errmsg = invalidModal.value
|
||||
? "Data required for changing mod version was not found."
|
||||
: `${!mod?.project_id ? "No mod project ID found" : "No mod filename found"} for ${friendlyModName(mod!)}`;
|
||||
console.error(errmsg);
|
||||
addNotification({
|
||||
text: errmsg,
|
||||
type: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
currentEditMod.value = mod;
|
||||
versionEditModal.value.show(mod);
|
||||
}
|
||||
|
||||
const handleDroppedFiles = (files: File[]) => {
|
||||
files.forEach((file) => {
|
||||
uploadDropdownRef.value?.uploadFile(file);
|
||||
@@ -529,17 +544,30 @@ const debouncedSearch = debounce(() => {
|
||||
}
|
||||
}, 300);
|
||||
|
||||
function friendlyModName(mod: ContentItem) {
|
||||
if (mod.name) return mod.name;
|
||||
|
||||
// remove .disabled if at the end of the filename
|
||||
let cleanName = mod.filename.endsWith(".disabled") ? mod.filename.slice(0, -9) : mod.filename;
|
||||
|
||||
// remove everything after the last dot
|
||||
const lastDotIndex = cleanName.lastIndexOf(".");
|
||||
if (lastDotIndex !== -1) cleanName = cleanName.substring(0, lastDotIndex);
|
||||
return cleanName;
|
||||
}
|
||||
|
||||
async function toggleMod(mod: ContentItem) {
|
||||
mod.changing = true;
|
||||
|
||||
const originalFilename = mod.filename;
|
||||
try {
|
||||
const newFilename = mod.filename.endsWith(".disabled")
|
||||
? mod.filename.replace(".disabled", "")
|
||||
? mod.filename.slice(0, -9)
|
||||
: `${mod.filename}.disabled`;
|
||||
|
||||
const sourcePath = `/mods/${mod.filename}`;
|
||||
const destinationPath = `/mods/${newFilename}`;
|
||||
const folder = `${type.value.toLocaleLowerCase()}s`;
|
||||
const sourcePath = `/${folder}/${mod.filename}`;
|
||||
const destinationPath = `/${folder}/${newFilename}`;
|
||||
|
||||
mod.disabled = newFilename.endsWith(".disabled");
|
||||
mod.filename = newFilename;
|
||||
@@ -553,7 +581,7 @@ async function toggleMod(mod: ContentItem) {
|
||||
|
||||
console.error("Error toggling mod:", error);
|
||||
addNotification({
|
||||
text: `Something went wrong toggling ${mod.name || mod.filename.replace(".disabled", "")}`,
|
||||
text: `Something went wrong toggling ${friendlyModName(mod)}`,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
@@ -565,10 +593,7 @@ async function removeMod(mod: ContentItem) {
|
||||
mod.changing = true;
|
||||
|
||||
try {
|
||||
await props.server.content?.remove(
|
||||
type.value as "Mod" | "Plugin",
|
||||
`/${type.value.toLowerCase()}s/${mod.filename}`,
|
||||
);
|
||||
await props.server.content?.remove(`/${type.value.toLowerCase()}s/${mod.filename}`);
|
||||
await props.server.refresh(["general", "content"]);
|
||||
} catch (error) {
|
||||
console.error("Error removing mod:", error);
|
||||
@@ -582,41 +607,6 @@ async function removeMod(mod: ContentItem) {
|
||||
mod.changing = false;
|
||||
}
|
||||
|
||||
const modModal = ref();
|
||||
const currentMod = ref();
|
||||
const currentVersions = ref();
|
||||
const currentVersion = ref();
|
||||
|
||||
async function beginChangeModVersion(mod: Mod) {
|
||||
currentMod.value = mod;
|
||||
currentVersions.value = await useBaseFetch(`project/${mod.project_id}/version`, {}, false);
|
||||
|
||||
currentVersions.value = currentVersions.value.filter((version: any) =>
|
||||
version.loaders.includes(props.server.general?.loader?.toLowerCase()),
|
||||
);
|
||||
|
||||
currentVersion.value = currentVersions.value.find(
|
||||
(version: any) => version.id === mod.version_id,
|
||||
);
|
||||
modModal.value.show();
|
||||
}
|
||||
|
||||
async function changeModVersion() {
|
||||
currentMod.value.changing = true;
|
||||
try {
|
||||
modModal.value.hide();
|
||||
await props.server.content?.reinstall(
|
||||
type.value,
|
||||
currentMod.value.version_id,
|
||||
currentVersion.value.id,
|
||||
);
|
||||
await props.server.refresh(["general", "content"]);
|
||||
} catch (error) {
|
||||
console.error("Error changing mod version:", error);
|
||||
}
|
||||
currentMod.value.changing = false;
|
||||
}
|
||||
|
||||
const hasMods = computed(() => {
|
||||
return localMods.value?.length > 0;
|
||||
});
|
||||
@@ -646,9 +636,7 @@ const filteredMods = computed(() => {
|
||||
})();
|
||||
|
||||
return statusFilteredMods.sort((a, b) => {
|
||||
const aName = a.name || a.filename.replace(".disabled", "");
|
||||
const bName = b.name || b.filename.replace(".disabled", "");
|
||||
return aName.localeCompare(bName);
|
||||
return friendlyModName(a).localeCompare(friendlyModName(b));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user