Skip to content

Commit 037cc86

Browse files
authored
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
1 parent 1e8d550 commit 037cc86

File tree

10 files changed

+909
-164
lines changed

10 files changed

+909
-164
lines changed

Diff for: apps/frontend/src/components/ui/servers/ContentVersionEditModal.vue

+530
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<template>
2+
<div class="experimental-styles-within flex w-full flex-col items-center gap-2">
3+
<ManySelect
4+
v-model="selectedPlatforms"
5+
:tooltip="
6+
filterOptions.platform.length < 2 && !disabled ? 'No other platforms available' : undefined
7+
"
8+
:options="filterOptions.platform"
9+
:dropdown-id="`${baseId}-platform`"
10+
search
11+
show-always
12+
class="w-full"
13+
:disabled="disabled || filterOptions.platform.length < 2"
14+
:dropdown-class="'w-full'"
15+
@change="updateFilters"
16+
>
17+
<slot name="platform">
18+
<FilterIcon class="h-5 w-5 text-secondary" />
19+
Platform
20+
</slot>
21+
<template #option="{ option }">
22+
{{ formatCategory(option) }}
23+
</template>
24+
<template v-if="hasAnyUnsupportedPlatforms" #footer>
25+
<Checkbox
26+
v-model="showSupportedPlatformsOnly"
27+
class="mx-1"
28+
:label="`Show ${type?.toLowerCase()} platforms only`"
29+
/>
30+
</template>
31+
</ManySelect>
32+
<ManySelect
33+
v-model="selectedGameVersions"
34+
:tooltip="
35+
filterOptions.gameVersion.length < 2 && !disabled
36+
? 'No other game versions available'
37+
: undefined
38+
"
39+
:options="filterOptions.gameVersion"
40+
:dropdown-id="`${baseId}-game-version`"
41+
search
42+
show-always
43+
class="w-full"
44+
:disabled="disabled || filterOptions.gameVersion.length < 2"
45+
:dropdown-class="'w-full'"
46+
@change="updateFilters"
47+
>
48+
<slot name="game-versions">
49+
<FilterIcon class="h-5 w-5 text-secondary" />
50+
Game versions
51+
</slot>
52+
<template v-if="hasAnySnapshots" #footer>
53+
<Checkbox v-model="showSnapshots" class="mx-1" :label="`Show all versions`" />
54+
</template>
55+
</ManySelect>
56+
</div>
57+
</template>
58+
59+
<script setup lang="ts">
60+
import { FilterIcon } from "@modrinth/assets";
61+
import { type Version, formatCategory, type GameVersionTag } from "@modrinth/utils";
62+
import { ref, computed } from "vue";
63+
import { useRoute } from "vue-router";
64+
import ManySelect from "@modrinth/ui/src/components/base/ManySelect.vue";
65+
import Checkbox from "@modrinth/ui/src/components/base/Checkbox.vue";
66+
67+
export type ListedGameVersion = {
68+
name: string;
69+
release: boolean;
70+
};
71+
72+
export type ListedPlatform = {
73+
name: string;
74+
isType: boolean;
75+
};
76+
77+
const props = defineProps<{
78+
versions: Version[];
79+
gameVersions: GameVersionTag[];
80+
listedGameVersions: ListedGameVersion[];
81+
listedPlatforms: ListedPlatform[];
82+
baseId?: string;
83+
type: "Mod" | "Plugin";
84+
platformTags: {
85+
name: string;
86+
supported_project_types: string[];
87+
}[];
88+
disabled?: boolean;
89+
}>();
90+
91+
const emit = defineEmits(["update:query"]);
92+
const route = useRoute();
93+
94+
const showSnapshots = ref(false);
95+
const hasAnySnapshots = computed(() => {
96+
return props.versions.some((x) =>
97+
props.gameVersions.some(
98+
(y) => y.version_type !== "release" && x.game_versions.includes(y.version),
99+
),
100+
);
101+
});
102+
103+
const hasOnlySnapshots = computed(() => {
104+
return props.versions.every((version) => {
105+
return version.game_versions.every((gv) => {
106+
const matched = props.gameVersions.find((tag) => tag.version === gv);
107+
return matched && matched.version_type !== "release";
108+
});
109+
});
110+
});
111+
112+
const hasAnyUnsupportedPlatforms = computed(() => {
113+
return props.listedPlatforms.some((x) => !x.isType);
114+
});
115+
116+
const hasOnlyUnsupportedPlatforms = computed(() => {
117+
return props.listedPlatforms.every((x) => !x.isType);
118+
});
119+
120+
const showSupportedPlatformsOnly = ref(true);
121+
122+
const filterOptions = computed(() => {
123+
const filters: Record<"gameVersion" | "platform", string[]> = {
124+
gameVersion: [],
125+
platform: [],
126+
};
127+
128+
filters.gameVersion = props.listedGameVersions
129+
.filter((x) => {
130+
return showSnapshots.value || hasOnlySnapshots.value ? true : x.release;
131+
})
132+
.map((x) => x.name);
133+
134+
filters.platform = props.listedPlatforms
135+
.filter((x) => {
136+
return !showSupportedPlatformsOnly.value || hasOnlyUnsupportedPlatforms.value
137+
? true
138+
: x.isType;
139+
})
140+
.map((x) => x.name);
141+
142+
return filters;
143+
});
144+
145+
const selectedGameVersions = ref<string[]>([]);
146+
const selectedPlatforms = ref<string[]>([]);
147+
148+
selectedGameVersions.value = route.query.g ? getArrayOrString(route.query.g) : [];
149+
selectedPlatforms.value = route.query.l ? getArrayOrString(route.query.l) : [];
150+
151+
function updateFilters() {
152+
emit("update:query", {
153+
g: selectedGameVersions.value,
154+
l: selectedPlatforms.value,
155+
});
156+
}
157+
158+
defineExpose({
159+
selectedGameVersions,
160+
selectedPlatforms,
161+
});
162+
163+
function getArrayOrString(x: string | (string | null)[]): string[] {
164+
if (typeof x === "string") {
165+
return [x];
166+
} else {
167+
return x.filter((item): item is string => item !== null);
168+
}
169+
}
170+
</script>
171+
172+
<style></style>

Diff for: apps/frontend/src/components/ui/servers/TeleportDropdownMenu.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ const radioValue = computed<OptionValue>({
227227
});
228228
229229
const triggerClasses = computed(() => ({
230-
"cursor-not-allowed opacity-50 grayscale": props.disabled,
230+
"!cursor-not-allowed opacity-50 grayscale": props.disabled,
231231
"rounded-b-none": dropdownVisible.value && !isRenderingUp.value && !props.disabled,
232232
"rounded-t-none": dropdownVisible.value && isRenderingUp.value && !props.disabled,
233233
}));

Diff for: apps/frontend/src/composables/pyroServers.ts

+14-20
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ export interface DirectoryResponse {
252252
current?: number;
253253
}
254254

255-
type ContentType = "Mod" | "Plugin";
255+
type ContentType = "mod" | "plugin";
256256

257257
const constructServerProperties = (properties: any): string => {
258258
let fileContent = `#Minecraft server properties\n#${new Date().toUTCString()}\n`;
@@ -519,8 +519,8 @@ const installContent = async (contentType: ContentType, projectId: string, versi
519519
await PyroFetch(`servers/${internalServerRefrence.value.serverId}/mods`, {
520520
method: "POST",
521521
body: {
522-
install_as: contentType,
523522
rinth_ids: { project_id: projectId, version_id: versionId },
523+
install_as: contentType,
524524
},
525525
});
526526
} catch (error) {
@@ -529,13 +529,12 @@ const installContent = async (contentType: ContentType, projectId: string, versi
529529
}
530530
};
531531

532-
const removeContent = async (contentType: ContentType, contentId: string) => {
532+
const removeContent = async (path: string) => {
533533
try {
534534
await PyroFetch(`servers/${internalServerRefrence.value.serverId}/deleteMod`, {
535535
method: "POST",
536536
body: {
537-
install_as: contentType,
538-
path: contentId,
537+
path,
539538
},
540539
});
541540
} catch (error) {
@@ -544,15 +543,11 @@ const removeContent = async (contentType: ContentType, contentId: string) => {
544543
}
545544
};
546545

547-
const reinstallContent = async (
548-
contentType: ContentType,
549-
contentId: string,
550-
newContentId: string,
551-
) => {
546+
const reinstallContent = async (replace: string, projectId: string, versionId: string) => {
552547
try {
553-
await PyroFetch(`servers/${internalServerRefrence.value.serverId}/mods/${contentId}`, {
554-
method: "PUT",
555-
body: { install_as: contentType, version_id: newContentId },
548+
await PyroFetch(`servers/${internalServerRefrence.value.serverId}/mods/update`, {
549+
method: "POST",
550+
body: { replace, project_id: projectId, version_id: versionId },
556551
});
557552
} catch (error) {
558553
console.error("Error reinstalling mod:", error);
@@ -1160,18 +1155,17 @@ type ContentFunctions = {
11601155

11611156
/**
11621157
* Removes a mod from a server.
1163-
* @param contentType - The type of content to remove.
1164-
* @param contentId - The ID of the content.
1158+
* @param path - The path of the mod file.
11651159
*/
1166-
remove: (contentType: ContentType, contentId: string) => Promise<void>;
1160+
remove: (path: string) => Promise<void>;
11671161

11681162
/**
11691163
* Reinstalls a mod to a server.
1170-
* @param contentType - The type of content to reinstall.
1171-
* @param contentId - The ID of the content.
1172-
* @param newContentId - The ID of the new version.
1164+
* @param replace - The path of the mod to replace.
1165+
* @param projectId - The ID of the content.
1166+
* @param versionId - The ID of the new version.
11731167
*/
1174-
reinstall: (contentType: ContentType, contentId: string, newContentId: string) => Promise<void>;
1168+
reinstall: (replace: string, projectId: string, versionId: string) => Promise<void>;
11751169
};
11761170

11771171
type BackupFunctions = {

0 commit comments

Comments
 (0)