You've already forked AstralRinth
forked from didirus/AstralRinth
Versions environments updates (#4949)
* add environment to version page metadata card * remove environment migration warnings * show settings/environments in nav only for staff * use v2 versions route due to regressions * add modpack incorrect loaders migration * remove modpack migration step * remove unused var * run pnpm intl:extract * componentize environment migration page * rename environment selector * rename environment selector pt2 * add migration modal to admonition * hide environments in settings and show message * show environment in project versions table * pnpm fix * pnpm fix on ui package * intl:extract * fix: .value * lower case file * add icon to environment tags and use i18n * Update apps/frontend/src/pages/[type]/[id].vue Co-authored-by: Calum H. <contact@cal.engineer> Signed-off-by: Truman Gao <106889354+tdgao@users.noreply.github.com> * open migration modal from warning icon in project dashboard * fix settings side nav icon * use useRoute composable * pnpm fix * intl:extract * fix import * fix import again * run pnpm prepr * fix designMessage import * fix environment fetch * fix environment fetch properly without key conflict * fix environment refetching * fix not using current versions in table to check different environments * fix download tooltip --------- Signed-off-by: Truman Gao <106889354+tdgao@users.noreply.github.com> Co-authored-by: Calum H. (IMB11) <contact@cal.engineer>
This commit is contained in:
@@ -155,7 +155,14 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { EditIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, Chips, TagItem } from '@modrinth/ui'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Chips,
|
||||
defineMessages,
|
||||
ENVIRONMENTS_COPY,
|
||||
TagItem,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { formatCategory } from '@modrinth/utils'
|
||||
|
||||
import { useGeneratedState } from '~/composables/generated'
|
||||
@@ -215,61 +222,51 @@ const usingDetectedLoaders = computed(() => {
|
||||
return loadersMatch
|
||||
})
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const noEnvironmentMessage = defineMessages({
|
||||
title: {
|
||||
id: 'version.environment.none.title',
|
||||
defaultMessage: 'No environment set',
|
||||
},
|
||||
description: {
|
||||
id: 'version.environment.none.description',
|
||||
defaultMessage: 'The environment for this version has not been specified.',
|
||||
},
|
||||
})
|
||||
|
||||
const unknownEnvironmentMessage = defineMessages({
|
||||
title: {
|
||||
id: 'version.environment.unknown.title',
|
||||
defaultMessage: 'Unknown environment',
|
||||
},
|
||||
description: {
|
||||
id: 'version.environment.unknown.description',
|
||||
defaultMessage: 'The environment: "{environment}" is not recognized.',
|
||||
},
|
||||
})
|
||||
|
||||
const environmentCopy = computed(() => {
|
||||
const emptyMessage = {
|
||||
title: 'No environment set',
|
||||
description: 'The environment for this version has not been specified.',
|
||||
}
|
||||
if (!draftVersion.value.environment) return emptyMessage
|
||||
|
||||
const envCopy: Record<string, { title: string; description: string }> = {
|
||||
client_only: {
|
||||
title: 'Client-side only',
|
||||
description: 'All functionality is done client-side and is compatible with vanilla servers.',
|
||||
},
|
||||
server_only: {
|
||||
title: 'Server-side only',
|
||||
description: 'All functionality is done server-side and is compatible with vanilla clients.',
|
||||
},
|
||||
singleplayer_only: {
|
||||
title: 'Singleplayer only',
|
||||
description: 'Only functions in Singleplayer or when not connected to a Multiplayer server.',
|
||||
},
|
||||
dedicated_server_only: {
|
||||
title: 'Server-side only',
|
||||
description: 'All functionality is done server-side and is compatible with vanilla clients.',
|
||||
},
|
||||
client_and_server: {
|
||||
title: 'Client and server',
|
||||
description: 'Has some functionality on both the client and server, even if only partially.',
|
||||
},
|
||||
client_only_server_optional: {
|
||||
title: 'Client and server',
|
||||
description: 'Has some functionality on both the client and server, even if only partially.',
|
||||
},
|
||||
server_only_client_optional: {
|
||||
title: 'Client and server',
|
||||
description: 'Has some functionality on both the client and server, even if only partially.',
|
||||
},
|
||||
client_or_server: {
|
||||
title: 'Client and server',
|
||||
description: 'Has some functionality on both the client and server, even if only partially.',
|
||||
},
|
||||
client_or_server_prefers_both: {
|
||||
title: 'Client and server',
|
||||
description: 'Has some functionality on both the client and server, even if only partially.',
|
||||
},
|
||||
unknown: {
|
||||
title: 'Unknown environment',
|
||||
description: 'The environment for this version could not be determined.',
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
envCopy[draftVersion.value.environment] || {
|
||||
title: 'Unknown environment',
|
||||
description: `The environment: "${draftVersion.value.environment}" is not recognized.`,
|
||||
if (!draftVersion.value.environment) {
|
||||
return {
|
||||
title: formatMessage(noEnvironmentMessage.title),
|
||||
description: formatMessage(noEnvironmentMessage.description),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const envCopy = ENVIRONMENTS_COPY[draftVersion.value.environment]
|
||||
if (envCopy) {
|
||||
return {
|
||||
title: formatMessage(envCopy.title),
|
||||
description: formatMessage(envCopy.description),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
title: formatMessage(unknownEnvironmentMessage.title),
|
||||
description: formatMessage(unknownEnvironmentMessage.description, {
|
||||
environment: draftVersion.value.environment,
|
||||
}),
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="sm:w-[512px]">
|
||||
<ProjectSettingsEnvSelector v-model="draftVersion.environment" />
|
||||
<EnvironmentSelector v-model="draftVersion.environment" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ProjectSettingsEnvSelector } from '@modrinth/ui'
|
||||
import { EnvironmentSelector } from '@modrinth/ui'
|
||||
|
||||
import { injectManageVersionContext } from '~/providers/version/manage-version-modal'
|
||||
|
||||
|
||||
@@ -2130,7 +2130,7 @@
|
||||
"message": "Learn more about this change"
|
||||
},
|
||||
"project.environment.migration.message": {
|
||||
"message": "We've just overhauled the Environments system on Modrinth and new options are now available. Please visit your project's settings and verify that the metadata is correct."
|
||||
"message": "We've just overhauled the Environments system on Modrinth and new options are now available. Please verify that the metadata is correct."
|
||||
},
|
||||
"project.environment.migration.review-button": {
|
||||
"message": "Review environment settings"
|
||||
@@ -2177,36 +2177,6 @@
|
||||
"project.notification.updated.title": {
|
||||
"message": "Project updated"
|
||||
},
|
||||
"project.settings.environment.notice.missing-env.description": {
|
||||
"message": "Your project is missing environment metadata, please select the appropriate option below."
|
||||
},
|
||||
"project.settings.environment.notice.missing-env.title": {
|
||||
"message": "Please select an environment for your project"
|
||||
},
|
||||
"project.settings.environment.notice.multiple-environments.description": {
|
||||
"message": "Different versions of your project have different environments selected, so you can't edit them globally at this time."
|
||||
},
|
||||
"project.settings.environment.notice.multiple-environments.title": {
|
||||
"message": "Your project has multiple environments"
|
||||
},
|
||||
"project.settings.environment.notice.review-options.description": {
|
||||
"message": "We've just overhauled the Environments system on Modrinth and new options are now available. Please ensure the correct option is selected below and then click 'Verify' when you're done!"
|
||||
},
|
||||
"project.settings.environment.notice.review-options.title": {
|
||||
"message": "Please review the options below"
|
||||
},
|
||||
"project.settings.environment.notice.wrong-project-type.description": {
|
||||
"message": "Only mod or modpack projects can have environment metadata."
|
||||
},
|
||||
"project.settings.environment.notice.wrong-project-type.title": {
|
||||
"message": "This project type does not support environment metadata"
|
||||
},
|
||||
"project.settings.environment.verification.verify-button": {
|
||||
"message": "Verify"
|
||||
},
|
||||
"project.settings.environment.verification.verify-text": {
|
||||
"message": "Verify that this project's environment is set correctly."
|
||||
},
|
||||
"project.settings.general.name.description": {
|
||||
"message": "Avoid prefixes, suffixes, parentheticals, or added descriptions—just the project's actual name."
|
||||
},
|
||||
@@ -2917,5 +2887,17 @@
|
||||
},
|
||||
"ui.latest-news-row.view-all": {
|
||||
"message": "View all news"
|
||||
},
|
||||
"version.environment.none.description": {
|
||||
"message": "The environment for this version has not been specified."
|
||||
},
|
||||
"version.environment.none.title": {
|
||||
"message": "No environment set"
|
||||
},
|
||||
"version.environment.unknown.description": {
|
||||
"message": "The environment: \"{environment}\" is not recognized."
|
||||
},
|
||||
"version.environment.unknown.title": {
|
||||
"message": "Unknown environment"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,11 +43,11 @@
|
||||
<NuxtPage
|
||||
v-model:project="project"
|
||||
v-model:project-v3="projectV3"
|
||||
v-model:versions="versions"
|
||||
v-model:members="members"
|
||||
v-model:all-members="allMembers"
|
||||
v-model:dependencies="dependencies"
|
||||
v-model:organization="organization"
|
||||
v-model:versions="versions"
|
||||
:current-member="currentMember"
|
||||
:patch-project="patchProject"
|
||||
:patch-icon="patchIcon"
|
||||
@@ -457,9 +457,6 @@
|
||||
|
||||
<div class="hidden sm:contents">
|
||||
<ButtonStyled
|
||||
v-tooltip="
|
||||
auth.user && currentMember ? formatMessage(commonMessages.downloadButton) : ''
|
||||
"
|
||||
size="large"
|
||||
:color="
|
||||
(auth.user && currentMember) || route.name === 'type-id-version-version'
|
||||
@@ -468,7 +465,12 @@
|
||||
"
|
||||
:circular="auth.user && currentMember"
|
||||
>
|
||||
<button @click="(event) => downloadModal.show(event)">
|
||||
<button
|
||||
v-tooltip="
|
||||
auth.user && currentMember ? formatMessage(commonMessages.downloadButton) : ''
|
||||
"
|
||||
@click="(event) => downloadModal.show(event)"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
{{
|
||||
auth.user && currentMember ? '' : formatMessage(commonMessages.downloadButton)
|
||||
@@ -778,9 +780,9 @@
|
||||
{{ formatMessage(messages.environmentMigrationLink) }}
|
||||
</nuxt-link>
|
||||
<ButtonStyled v-if="hasEditDetailsPermission" color="orange">
|
||||
<nuxt-link :to="`/project/${project.id}/settings/environment`" class="mt-3 w-fit">
|
||||
<button class="mt-3 w-fit" @click="() => projectEnvironmentModal.show()">
|
||||
<SettingsIcon /> {{ formatMessage(messages.reviewEnvironmentSettings) }}
|
||||
</nuxt-link>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</Admonition>
|
||||
<MessageBanner v-if="project.status === 'archived'" message-type="warning" class="my-4">
|
||||
@@ -935,6 +937,10 @@
|
||||
@toggle-collapsed="collapsedModerationChecklist = !collapsedModerationChecklist"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template v-if="hasEditDetailsPermission">
|
||||
<ProjectEnvironmentModal ref="projectEnvironmentModal" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -977,6 +983,7 @@ import {
|
||||
OverflowMenu,
|
||||
PopoutMenu,
|
||||
ProjectBackgroundGradient,
|
||||
ProjectEnvironmentModal,
|
||||
ProjectHeader,
|
||||
ProjectSidebarCompatibility,
|
||||
ProjectSidebarCreators,
|
||||
@@ -994,6 +1001,7 @@ import { formatCategory, formatPrice, formatProjectType, renderString } from '@m
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import dayjs from 'dayjs'
|
||||
import { Tooltip } from 'floating-vue'
|
||||
import { useTemplateRef } from 'vue'
|
||||
|
||||
import { navigateTo } from '#app'
|
||||
import Accordion from '~/components/ui/Accordion.vue'
|
||||
@@ -1037,6 +1045,8 @@ const gameVersionFilterInput = ref()
|
||||
|
||||
const versionFilter = ref('')
|
||||
|
||||
const projectEnvironmentModal = useTemplateRef('projectEnvironmentModal')
|
||||
|
||||
const baseId = useId()
|
||||
|
||||
const currentGameVersion = computed(() => {
|
||||
@@ -1186,7 +1196,7 @@ const messages = defineMessages({
|
||||
environmentMigrationMessage: {
|
||||
id: 'project.environment.migration.message',
|
||||
defaultMessage:
|
||||
"We've just overhauled the Environments system on Modrinth and new options are now available. Please visit your project's settings and verify that the metadata is correct.",
|
||||
"We've just overhauled the Environments system on Modrinth and new options are now available. Please verify that the metadata is correct.",
|
||||
},
|
||||
environmentMigrationTitle: {
|
||||
id: 'project.environment.migration.title',
|
||||
@@ -1459,21 +1469,25 @@ let project,
|
||||
resetMembers,
|
||||
dependencies,
|
||||
versions,
|
||||
resetVersions,
|
||||
versionsV3,
|
||||
resetVersionsV2,
|
||||
organization,
|
||||
resetOrganization,
|
||||
projectV2Error,
|
||||
projectV3Error,
|
||||
membersError,
|
||||
dependenciesError,
|
||||
versionsError
|
||||
versionsError,
|
||||
versionsV3Error,
|
||||
resetVersionsV3
|
||||
try {
|
||||
;[
|
||||
{ data: project, error: projectV2Error, refresh: resetProjectV2 },
|
||||
{ data: projectV3, error: projectV3Error, refresh: resetProjectV3 },
|
||||
{ data: allMembers, error: membersError, refresh: resetMembers },
|
||||
{ data: dependencies, error: dependenciesError },
|
||||
{ data: versions, error: versionsError, refresh: resetVersions },
|
||||
{ data: versions, error: versionsError, refresh: resetVersionsV2 },
|
||||
{ data: versionsV3, error: versionsV3Error, refresh: resetVersionsV3 },
|
||||
{ data: organization, refresh: resetOrganization },
|
||||
] = await Promise.all([
|
||||
useAsyncData(`project/${projectId.value}`, () => useBaseFetch(`project/${projectId.value}`), {
|
||||
@@ -1515,6 +1529,9 @@ try {
|
||||
useAsyncData(`project/${projectId.value}/version`, () =>
|
||||
useBaseFetch(`project/${projectId.value}/version`),
|
||||
),
|
||||
useAsyncData(`project/${projectId.value}/version/v3`, () =>
|
||||
useBaseFetch(`project/${projectId.value}/version`, { apiVersion: 3 }),
|
||||
),
|
||||
useAsyncData(`project/${projectId.value}/organization`, () =>
|
||||
useBaseFetch(`project/${projectId.value}/organization`, { apiVersion: 3 }),
|
||||
),
|
||||
@@ -1523,6 +1540,11 @@ try {
|
||||
await updateProjectRoute()
|
||||
|
||||
versions = shallowRef(toRaw(versions))
|
||||
versionsV3 = shallowRef(toRaw(versionsV3))
|
||||
versions.value = versions.value.map((v) => ({
|
||||
...v,
|
||||
environment: versionsV3.value?.find((v3) => v3.id === v.id)?.environment,
|
||||
}))
|
||||
} catch (err) {
|
||||
throw createError({
|
||||
fatal: true,
|
||||
@@ -1555,6 +1577,16 @@ async function resetProject() {
|
||||
await resetProjectV3()
|
||||
}
|
||||
|
||||
async function resetVersions() {
|
||||
await resetVersionsV2()
|
||||
await resetVersionsV3()
|
||||
|
||||
versions.value = versions.value.map((v) => ({
|
||||
...v,
|
||||
environment: versionsV3.value?.find((v3) => v3.id === v.id)?.environment,
|
||||
}))
|
||||
}
|
||||
|
||||
function handleError(err, project = false) {
|
||||
if (err.value && err.value.statusCode) {
|
||||
throw createError({
|
||||
@@ -1573,6 +1605,7 @@ handleError(projectV3Error)
|
||||
handleError(membersError)
|
||||
handleError(dependenciesError)
|
||||
handleError(versionsError)
|
||||
handleError(versionsV3Error)
|
||||
|
||||
if (!project.value) {
|
||||
throw createError({
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
AlignLeftIcon,
|
||||
BookTextIcon,
|
||||
ChartIcon,
|
||||
GlobeIcon,
|
||||
ImageIcon,
|
||||
InfoIcon,
|
||||
LinkIcon,
|
||||
@@ -16,7 +17,7 @@ import {
|
||||
injectNotificationManager,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import type { Project, ProjectV3Partial } from '@modrinth/utils'
|
||||
import { isStaff, type Project, type ProjectV3Partial } from '@modrinth/utils'
|
||||
import { useLocalStorage, useScroll } from '@vueuse/core'
|
||||
import { computed } from 'vue'
|
||||
|
||||
@@ -25,7 +26,7 @@ import NavStack from '~/components/ui/NavStack.vue'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
currentMember: any
|
||||
patchProject: any
|
||||
patchIcon: any
|
||||
@@ -47,6 +48,11 @@ const organization = defineModel<any>('organization')
|
||||
|
||||
const navItems = computed(() => {
|
||||
const base = `${project.value.project_type}/${project.value.slug ? project.value.slug : project.value.id}`
|
||||
|
||||
const showEnvironment =
|
||||
projectV3.value.project_types.some((type) => ['mod', 'modpack'].includes(type)) &&
|
||||
isStaff(props.currentMember.user)
|
||||
|
||||
const items = [
|
||||
{
|
||||
link: `/${base}/settings`,
|
||||
@@ -101,6 +107,13 @@ const navItems = computed(() => {
|
||||
label: formatMessage(commonProjectSettingsMessages.analytics),
|
||||
icon: ChartIcon,
|
||||
},
|
||||
{ type: 'heading', label: 'moderation', shown: showEnvironment },
|
||||
{
|
||||
link: `/${base}/settings/environment`,
|
||||
label: formatMessage(commonProjectSettingsMessages.environment),
|
||||
icon: GlobeIcon,
|
||||
shown: showEnvironment,
|
||||
},
|
||||
]
|
||||
return items.filter(Boolean) as any[]
|
||||
})
|
||||
|
||||
@@ -1,177 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import { CheckIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
commonProjectSettingsMessages,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
ProjectSettingsEnvSelector,
|
||||
UnsavedChangesPopup,
|
||||
useSavable,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const { currentMember, projectV2, projectV3, refreshProject } = injectProjectPageContext()
|
||||
const { handleError } = injectNotificationManager()
|
||||
const client = injectModrinthClient()
|
||||
|
||||
const saving = ref(false)
|
||||
|
||||
const supportsEnvironment = computed(() =>
|
||||
projectV3.value.project_types.some((type) => ['mod', 'modpack'].includes(type)),
|
||||
)
|
||||
|
||||
const needsToVerify = computed(
|
||||
() =>
|
||||
projectV3.value.side_types_migration_review_status === 'pending' &&
|
||||
(projectV3.value.environment?.length ?? 0) > 0 &&
|
||||
projectV3.value.environment?.[0] !== 'unknown' &&
|
||||
supportsEnvironment.value,
|
||||
)
|
||||
|
||||
const hasPermission = computed(() => {
|
||||
const EDIT_DETAILS = 1 << 2
|
||||
return (currentMember.value?.permissions & EDIT_DETAILS) === EDIT_DETAILS
|
||||
})
|
||||
|
||||
function getInitialEnv() {
|
||||
return projectV3.value.environment?.length === 1 ? projectV3.value.environment[0] : undefined
|
||||
}
|
||||
|
||||
const { saved, current, reset, save } = useSavable(
|
||||
() => ({
|
||||
environment: getInitialEnv(),
|
||||
side_types_migration_review_status: projectV3.value.side_types_migration_review_status,
|
||||
}),
|
||||
({ environment, side_types_migration_review_status }) => {
|
||||
saving.value = true
|
||||
side_types_migration_review_status = 'reviewed'
|
||||
client.labrinth.projects_v3
|
||||
.edit(projectV2.value.id, { environment, side_types_migration_review_status })
|
||||
.then(() => refreshProject().then(reset))
|
||||
.catch(handleError)
|
||||
.finally(() => (saving.value = false))
|
||||
},
|
||||
)
|
||||
// Set current to reviewed, which will trigger unsaved changes popup.
|
||||
// It should not be possible to save without reviewing it.
|
||||
const originalEnv = getInitialEnv()
|
||||
if (originalEnv && originalEnv !== 'unknown') {
|
||||
current.value.side_types_migration_review_status = 'reviewed'
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
verifyButton: {
|
||||
id: 'project.settings.environment.verification.verify-button',
|
||||
defaultMessage: 'Verify',
|
||||
},
|
||||
verifyLabel: {
|
||||
id: 'project.settings.environment.verification.verify-text',
|
||||
defaultMessage: `Verify that this project's environment is set correctly.`,
|
||||
},
|
||||
wrongProjectTypeTitle: {
|
||||
id: 'project.settings.environment.notice.wrong-project-type.title',
|
||||
defaultMessage: `This project type does not support environment metadata`,
|
||||
},
|
||||
wrongProjectTypeDescription: {
|
||||
id: 'project.settings.environment.notice.wrong-project-type.description',
|
||||
defaultMessage: `Only mod or modpack projects can have environment metadata.`,
|
||||
},
|
||||
missingEnvTitle: {
|
||||
id: 'project.settings.environment.notice.missing-env.title',
|
||||
defaultMessage: `Please select an environment for your project`,
|
||||
},
|
||||
missingEnvDescription: {
|
||||
id: 'project.settings.environment.notice.missing-env.description',
|
||||
defaultMessage: `Your project is missing environment metadata, please select the appropriate option below.`,
|
||||
},
|
||||
multipleEnvironmentsTitle: {
|
||||
id: 'project.settings.environment.notice.multiple-environments.title',
|
||||
defaultMessage: 'Your project has multiple environments',
|
||||
},
|
||||
multipleEnvironmentsDescription: {
|
||||
id: 'project.settings.environment.notice.multiple-environments.description',
|
||||
defaultMessage:
|
||||
"Different versions of your project have different environments selected, so you can't edit them globally at this time.",
|
||||
},
|
||||
reviewOptionsTitle: {
|
||||
id: 'project.settings.environment.notice.review-options.title',
|
||||
defaultMessage: 'Please review the options below',
|
||||
},
|
||||
reviewOptionsDescription: {
|
||||
id: 'project.settings.environment.notice.review-options.description',
|
||||
defaultMessage:
|
||||
"We've just overhauled the Environments system on Modrinth and new options are now available. Please ensure the correct option is selected below and then click 'Verify' when you're done!",
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="card experimental-styles-within">
|
||||
<h2 class="m-0 mb-2 block text-lg font-extrabold text-contrast">
|
||||
{{ formatMessage(commonProjectSettingsMessages.environment) }}
|
||||
</h2>
|
||||
<Admonition
|
||||
v-if="!supportsEnvironment"
|
||||
type="critical"
|
||||
:header="formatMessage(messages.wrongProjectTypeTitle)"
|
||||
:body="formatMessage(messages.wrongProjectTypeDescription)"
|
||||
class="mb-3"
|
||||
/>
|
||||
<template v-else>
|
||||
<Admonition
|
||||
v-if="!hasPermission"
|
||||
type="critical"
|
||||
:header="formatMessage(commonProjectSettingsMessages.noPermissionTitle)"
|
||||
:body="formatMessage(commonProjectSettingsMessages.noPermissionDescription)"
|
||||
class="mb-3"
|
||||
/>
|
||||
<Admonition
|
||||
v-else-if="
|
||||
!projectV3.environment ||
|
||||
projectV3.environment.length === 0 ||
|
||||
(projectV3.environment.length === 1 && projectV3.environment[0] === 'unknown')
|
||||
"
|
||||
type="critical"
|
||||
:header="formatMessage(messages.missingEnvTitle)"
|
||||
:body="formatMessage(messages.missingEnvDescription)"
|
||||
class="mb-3"
|
||||
/>
|
||||
<Admonition
|
||||
v-else-if="projectV3.environment.length > 1"
|
||||
type="info"
|
||||
:header="formatMessage(messages.multipleEnvironmentsTitle)"
|
||||
:body="formatMessage(messages.multipleEnvironmentsDescription)"
|
||||
class="mb-3"
|
||||
/>
|
||||
<Admonition
|
||||
v-else-if="needsToVerify"
|
||||
type="warning"
|
||||
:header="formatMessage(messages.reviewOptionsTitle)"
|
||||
:body="formatMessage(messages.reviewOptionsDescription)"
|
||||
class="mb-3"
|
||||
/>
|
||||
<ProjectSettingsEnvSelector
|
||||
v-model="current.environment"
|
||||
:disabled="!hasPermission || (projectV3?.environment?.length ?? 0) > 1"
|
||||
/>
|
||||
</template>
|
||||
<div v-if="showEnvironmentMigration" class="card experimental-styles-within">
|
||||
<h2 class="m-0 mb-2 block text-lg font-extrabold text-contrast">Project environment</h2>
|
||||
<EnvironmentMigration />
|
||||
</div>
|
||||
<div v-else class="grid place-content-center py-32">
|
||||
<div class="flex flex-col items-center gap-5 text-center">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="text-xl font-semibold text-contrast">
|
||||
Environments are now managed per version.
|
||||
</div>
|
||||
<div>Visit Project Settings to manage environments for each version.</div>
|
||||
</div>
|
||||
<ButtonStyled color="green">
|
||||
<nuxt-link
|
||||
:to="`/${projectV2.project_type}/${projectV2.id}/settings/versions`"
|
||||
class="items flex"
|
||||
>
|
||||
<SettingsIcon /> Edit versions
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<UnsavedChangesPopup
|
||||
v-if="supportsEnvironment && hasPermission && (projectV3?.environment?.length ?? 0) <= 1"
|
||||
:original="saved"
|
||||
:modified="current"
|
||||
:saving="saving"
|
||||
:can-reset="!needsToVerify"
|
||||
:text="needsToVerify ? messages.verifyLabel : undefined"
|
||||
:save-label="needsToVerify ? messages.verifyButton : undefined"
|
||||
:save-icon="needsToVerify ? CheckIcon : undefined"
|
||||
@reset="reset"
|
||||
@save="save"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SettingsIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, EnvironmentMigration, injectProjectPageContext } from '@modrinth/ui'
|
||||
import { isStaff } from '@modrinth/utils'
|
||||
|
||||
const { currentMember, projectV2 } = injectProjectPageContext()
|
||||
|
||||
const showEnvironmentMigration = computed(() => {
|
||||
return isStaff(currentMember.value.user)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -560,6 +560,17 @@
|
||||
</template>
|
||||
<span v-else>{{ $formatVersion(version.game_versions) }}</span>
|
||||
</div>
|
||||
<div v-if="!isEditing && environment">
|
||||
<h4>Environment</h4>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<template v-if="environment.icon">
|
||||
<component :is="environment.icon" />
|
||||
</template>
|
||||
<span>
|
||||
{{ environment.title.defaultMessage }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!isEditing">
|
||||
<h4>Downloads</h4>
|
||||
<span>{{ version.downloads }}</span>
|
||||
@@ -635,6 +646,7 @@ import {
|
||||
Checkbox,
|
||||
ConfirmModal,
|
||||
CopyCode,
|
||||
ENVIRONMENTS_COPY,
|
||||
injectNotificationManager,
|
||||
MarkdownEditor,
|
||||
} from '@modrinth/ui'
|
||||
@@ -817,6 +829,12 @@ export default defineNuxtComponent({
|
||||
if (!version) {
|
||||
version = props.versions.find((x) => x.displayUrlEnding === route.params.version)
|
||||
}
|
||||
|
||||
const versionV3 = await useBaseFetch(
|
||||
`project/${props.project.id}/version/${route.params.version}`,
|
||||
{ apiVersion: 3 },
|
||||
)
|
||||
if (versionV3) version.environment = versionV3.environment
|
||||
}
|
||||
|
||||
if (!version) {
|
||||
@@ -933,6 +951,9 @@ export default defineNuxtComponent({
|
||||
(a, b) => order.indexOf(a.dependency_type) - order.indexOf(b.dependency_type),
|
||||
)
|
||||
},
|
||||
environment() {
|
||||
return ENVIRONMENTS_COPY[this.version.environment]
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$route.path'() {
|
||||
|
||||
@@ -290,7 +290,7 @@
|
||||
v-tooltip="'Please review environment metadata'"
|
||||
:to="`/${getProjectTypeForUrl(project.project_type, project.loaders)}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/settings/environment`"
|
||||
}?showEnvironmentMigrationWarning=true`"
|
||||
>
|
||||
<TriangleAlertIcon />
|
||||
</nuxt-link>
|
||||
|
||||
@@ -42,7 +42,12 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="versions.length > 0"
|
||||
class="flex flex-col gap-4 rounded-2xl bg-bg-raised px-6 pb-8 pt-4 supports-[grid-template-columns:subgrid]:grid supports-[grid-template-columns:subgrid]:grid-cols-[1fr_min-content] sm:px-8 supports-[grid-template-columns:subgrid]:sm:grid-cols-[min-content_auto_auto_auto_min-content] supports-[grid-template-columns:subgrid]:xl:grid-cols-[min-content_auto_auto_auto_auto_auto_min-content]"
|
||||
class="flex flex-col gap-4 rounded-2xl bg-bg-raised px-6 pb-8 pt-4 supports-[grid-template-columns:subgrid]:grid supports-[grid-template-columns:subgrid]:grid-cols-[1fr_min-content] sm:px-8 supports-[grid-template-columns:subgrid]:sm:grid-cols-[min-content_auto_auto_auto_min-content]"
|
||||
:class="[
|
||||
hasMultipleEnvironments
|
||||
? 'supports-[grid-template-columns:subgrid]:xl:grid-cols-[min-content_auto_auto_auto_auto_auto_auto_min-content] has-environment'
|
||||
: 'supports-[grid-template-columns:subgrid]:xl:grid-cols-[min-content_auto_auto_auto_auto_auto_min-content] no-environment',
|
||||
]"
|
||||
>
|
||||
<div class="versions-grid-row">
|
||||
<div class="w-9 max-sm:hidden"></div>
|
||||
@@ -57,6 +62,12 @@
|
||||
>
|
||||
Platforms
|
||||
</div>
|
||||
<div
|
||||
v-if="hasMultipleEnvironments"
|
||||
class="text-sm font-bold text-contrast max-sm:hidden sm:max-xl:collapse sm:max-xl:hidden"
|
||||
>
|
||||
Environment
|
||||
</div>
|
||||
<div
|
||||
class="text-sm font-bold text-contrast max-sm:hidden sm:max-xl:collapse sm:max-xl:hidden"
|
||||
>
|
||||
@@ -144,6 +155,24 @@
|
||||
</TagItem>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="hasMultipleEnvironments"
|
||||
v-tooltip="
|
||||
ENVIRONMENTS_COPY[version.environment || 'unknown']?.description
|
||||
? formatMessage(ENVIRONMENTS_COPY[version.environment || 'unknown'].description)
|
||||
: undefined
|
||||
"
|
||||
class="flex items-center"
|
||||
>
|
||||
<TagItem class="z-[1] text-center">
|
||||
<component :is="ENVIRONMENTS_COPY[version.environment || 'unknown']?.icon" />
|
||||
{{
|
||||
ENVIRONMENTS_COPY[version.environment || 'unknown']?.title
|
||||
? formatMessage(ENVIRONMENTS_COPY[version.environment || 'unknown'].title)
|
||||
: ''
|
||||
}}
|
||||
</TagItem>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col justify-center gap-1 max-sm:flex-row max-sm:justify-start max-sm:gap-3 xl:contents"
|
||||
@@ -215,12 +244,14 @@ import { commonMessages } from '../../utils/common-messages'
|
||||
import AutoLink from '../base/AutoLink.vue'
|
||||
import TagItem from '../base/TagItem.vue'
|
||||
import { Pagination, VersionChannelIndicator, VersionFilterControl } from '../index'
|
||||
import { ENVIRONMENTS_COPY } from './settings/environment/environments'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
|
||||
type VersionWithDisplayUrlEnding = Version & {
|
||||
displayUrlEnding: string
|
||||
environment?: Labrinth.Projects.v3.Environment
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
@@ -260,6 +291,11 @@ const selectedPlatforms: Ref<string[]> = computed(
|
||||
)
|
||||
const selectedChannels: Ref<string[]> = computed(() => versionFilters.value?.selectedChannels ?? [])
|
||||
|
||||
const hasMultipleEnvironments = computed(() => {
|
||||
const environments = new Set(currentVersions.value.map((v) => v.environment).filter(Boolean))
|
||||
return environments.size > 1
|
||||
})
|
||||
|
||||
const filteredVersions = computed(() => {
|
||||
return props.versions.filter(
|
||||
(version) =>
|
||||
@@ -321,6 +357,14 @@ function updateQuery(newQueries: Record<string, string | string[] | undefined |
|
||||
</script>
|
||||
<style scoped>
|
||||
.versions-grid-row {
|
||||
@apply grid grid-cols-[1fr_min-content] gap-4 supports-[grid-template-columns:subgrid]:col-span-full supports-[grid-template-columns:subgrid]:!grid-cols-subgrid sm:grid-cols-[min-content_1fr_1fr_1fr_min-content] xl:grid-cols-[min-content_1fr_1fr_1fr_1fr_1fr_min-content];
|
||||
@apply grid grid-cols-[1fr_min-content] gap-4 supports-[grid-template-columns:subgrid]:col-span-full supports-[grid-template-columns:subgrid]:!grid-cols-subgrid sm:grid-cols-[min-content_1fr_1fr_1fr_min-content];
|
||||
}
|
||||
|
||||
.has-environment .versions-grid-row {
|
||||
@apply xl:grid-cols-[min-content_1fr_1fr_1fr_1fr_1fr_1fr_min-content];
|
||||
}
|
||||
|
||||
.no-environment .versions-grid-row {
|
||||
@apply xl:grid-cols-[min-content_1fr_1fr_1fr_1fr_1fr_min-content];
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
:action="() => router.push(`/${project.project_type}s?g=categories:${platform}`)"
|
||||
:style="`--_color: var(--color-platform-${platform})`"
|
||||
>
|
||||
<svg v-html="tags.loaders.find((x) => x.name === platform).icon"></svg>
|
||||
<svg v-html="tags.loaders.find((x) => x.name === platform)?.icon"></svg>
|
||||
{{ formatCategory(platform) }}
|
||||
</TagItem>
|
||||
</div>
|
||||
@@ -69,6 +69,7 @@
|
||||
</TagItem>
|
||||
<TagItem
|
||||
v-if="
|
||||
// @ts-ignore
|
||||
project.project_type !== 'datapack' &&
|
||||
project.client_side !== 'unsupported' &&
|
||||
project.server_side !== 'unsupported' &&
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
<script setup lang="ts">
|
||||
import { CheckIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
commonProjectSettingsMessages,
|
||||
defineMessages,
|
||||
EnvironmentSelector,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
UnsavedChangesPopup,
|
||||
useSavable,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const { currentMember, projectV2, projectV3, refreshProject } = injectProjectPageContext()
|
||||
const { handleError } = injectNotificationManager()
|
||||
const client = injectModrinthClient()
|
||||
|
||||
const saving = ref(false)
|
||||
|
||||
const supportsEnvironment = computed(() =>
|
||||
projectV3.value.project_types.some((type) => ['mod', 'modpack'].includes(type)),
|
||||
)
|
||||
|
||||
const needsToVerify = computed(
|
||||
() =>
|
||||
projectV3.value.side_types_migration_review_status === 'pending' &&
|
||||
(projectV3.value.environment?.length ?? 0) > 0 &&
|
||||
projectV3.value.environment?.[0] !== 'unknown' &&
|
||||
supportsEnvironment.value,
|
||||
)
|
||||
|
||||
const hasPermission = computed(() => {
|
||||
const EDIT_DETAILS = 1 << 2
|
||||
return (currentMember.value?.permissions & EDIT_DETAILS) === EDIT_DETAILS
|
||||
})
|
||||
|
||||
function getInitialEnv() {
|
||||
return projectV3.value.environment?.length === 1 ? projectV3.value.environment[0] : undefined
|
||||
}
|
||||
|
||||
const { saved, current, reset, save } = useSavable(
|
||||
() => ({
|
||||
environment: getInitialEnv(),
|
||||
side_types_migration_review_status: projectV3.value.side_types_migration_review_status,
|
||||
}),
|
||||
({ environment, side_types_migration_review_status }) => {
|
||||
saving.value = true
|
||||
side_types_migration_review_status = 'reviewed'
|
||||
client.labrinth.projects_v3
|
||||
.edit(projectV2.value.id, { environment, side_types_migration_review_status })
|
||||
.then(() => refreshProject().then(reset))
|
||||
.catch(handleError)
|
||||
.finally(() => (saving.value = false))
|
||||
},
|
||||
)
|
||||
// Set current to reviewed, which will trigger unsaved changes popup.
|
||||
// It should not be possible to save without reviewing it.
|
||||
const originalEnv = getInitialEnv()
|
||||
if (originalEnv && originalEnv !== 'unknown') {
|
||||
current.value.side_types_migration_review_status = 'reviewed'
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
verifyButton: {
|
||||
id: 'project.settings.environment.verification.verify-button',
|
||||
defaultMessage: 'Verify',
|
||||
},
|
||||
verifyLabel: {
|
||||
id: 'project.settings.environment.verification.verify-text',
|
||||
defaultMessage: `Verify that this project's environment is set correctly.`,
|
||||
},
|
||||
wrongProjectTypeTitle: {
|
||||
id: 'project.settings.environment.notice.wrong-project-type.title',
|
||||
defaultMessage: `This project type does not support environment metadata`,
|
||||
},
|
||||
wrongProjectTypeDescription: {
|
||||
id: 'project.settings.environment.notice.wrong-project-type.description',
|
||||
defaultMessage: `Only mod or modpack projects can have environment metadata.`,
|
||||
},
|
||||
missingEnvTitle: {
|
||||
id: 'project.settings.environment.notice.missing-env.title',
|
||||
defaultMessage: `Please select an environment for your project`,
|
||||
},
|
||||
missingEnvDescription: {
|
||||
id: 'project.settings.environment.notice.missing-env.description',
|
||||
defaultMessage: `Your project is missing environment metadata, please select the appropriate option below.`,
|
||||
},
|
||||
multipleEnvironmentsTitle: {
|
||||
id: 'project.settings.environment.notice.multiple-environments.title',
|
||||
defaultMessage: 'Your project has multiple environments',
|
||||
},
|
||||
multipleEnvironmentsDescription: {
|
||||
id: 'project.settings.environment.notice.multiple-environments.description',
|
||||
defaultMessage:
|
||||
"Different versions of your project have different environments selected, so you can't edit them globally at this time.",
|
||||
},
|
||||
reviewOptionsTitle: {
|
||||
id: 'project.settings.environment.notice.review-options.title',
|
||||
defaultMessage: 'Please review the options below',
|
||||
},
|
||||
reviewOptionsDescription: {
|
||||
id: 'project.settings.environment.notice.review-options.description',
|
||||
defaultMessage:
|
||||
"We've just overhauled the Environments system on Modrinth and new options are now available. Please ensure the correct option is selected below and then click 'Verify' when you're done!",
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<Admonition
|
||||
v-if="!supportsEnvironment"
|
||||
type="critical"
|
||||
:header="formatMessage(messages.wrongProjectTypeTitle)"
|
||||
:body="formatMessage(messages.wrongProjectTypeDescription)"
|
||||
class="mb-3"
|
||||
/>
|
||||
<template v-else>
|
||||
<Admonition
|
||||
v-if="!hasPermission"
|
||||
type="critical"
|
||||
:header="formatMessage(commonProjectSettingsMessages.noPermissionTitle)"
|
||||
:body="formatMessage(commonProjectSettingsMessages.noPermissionDescription)"
|
||||
class="mb-3"
|
||||
/>
|
||||
<Admonition
|
||||
v-else-if="
|
||||
!projectV3.environment ||
|
||||
projectV3.environment.length === 0 ||
|
||||
(projectV3.environment.length === 1 && projectV3.environment[0] === 'unknown')
|
||||
"
|
||||
type="critical"
|
||||
:header="formatMessage(messages.missingEnvTitle)"
|
||||
:body="formatMessage(messages.missingEnvDescription)"
|
||||
class="mb-3"
|
||||
/>
|
||||
<Admonition
|
||||
v-else-if="projectV3.environment.length > 1"
|
||||
type="info"
|
||||
:header="formatMessage(messages.multipleEnvironmentsTitle)"
|
||||
:body="formatMessage(messages.multipleEnvironmentsDescription)"
|
||||
class="mb-3"
|
||||
/>
|
||||
<Admonition
|
||||
v-else-if="needsToVerify"
|
||||
type="warning"
|
||||
:header="formatMessage(messages.reviewOptionsTitle)"
|
||||
:body="formatMessage(messages.reviewOptionsDescription)"
|
||||
class="mb-3"
|
||||
/>
|
||||
<EnvironmentSelector
|
||||
v-model="current.environment"
|
||||
:disabled="!hasPermission || (projectV3?.environment?.length ?? 0) > 1"
|
||||
/>
|
||||
</template>
|
||||
<UnsavedChangesPopup
|
||||
v-if="supportsEnvironment && hasPermission && (projectV3?.environment?.length ?? 0) <= 1"
|
||||
:original="saved"
|
||||
:modified="current"
|
||||
:saving="saving"
|
||||
:can-reset="!needsToVerify"
|
||||
:text="needsToVerify ? messages.verifyLabel : undefined"
|
||||
:save-label="needsToVerify ? messages.verifyButton : undefined"
|
||||
:save-icon="needsToVerify ? CheckIcon : undefined"
|
||||
@reset="reset"
|
||||
@save="save"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -33,7 +33,10 @@ const optionLabelFormat = defineMessage({
|
||||
defaultMessage: '{title}: {description}',
|
||||
})
|
||||
|
||||
const OUTER_OPTIONS = {
|
||||
const OUTER_OPTIONS: Record<
|
||||
string,
|
||||
EnvironmentRadioOption & { suboptions: Record<string, EnvironmentRadioOption> }
|
||||
> = {
|
||||
client: {
|
||||
title: defineMessage({
|
||||
id: 'project.settings.environment.client_only.title',
|
||||
@@ -125,10 +128,8 @@ const OUTER_OPTIONS = {
|
||||
}),
|
||||
suboptions: {},
|
||||
},
|
||||
} as const satisfies Record<
|
||||
string,
|
||||
EnvironmentRadioOption & { suboptions: Record<string, EnvironmentRadioOption> }
|
||||
>
|
||||
} as const
|
||||
|
||||
type OuterOptionKey = keyof typeof OUTER_OPTIONS
|
||||
type SubOptionKey = ValidKeys<(typeof OUTER_OPTIONS)[keyof typeof OUTER_OPTIONS]['suboptions']>
|
||||
|
||||
@@ -248,7 +249,7 @@ const simulateSave = ref(false)
|
||||
:aria-label="
|
||||
formatMessage(optionLabelFormat, {
|
||||
title: formatMessage(title),
|
||||
description: formatMessage(description),
|
||||
description: description ? formatMessage(description) : '',
|
||||
})
|
||||
"
|
||||
@select="
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<NewModal ref="modal" :scrollable="true" max-content-height="82vh" :closable="true">
|
||||
<template #title>
|
||||
<span class="text-lg font-extrabold text-contrast">Edit project Environment</span>
|
||||
</template>
|
||||
<div class="mb-24 max-w-[600px]">
|
||||
<EnvironmentMigration />
|
||||
</div>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, useTemplateRef } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
import { NewModal } from '../../../modal'
|
||||
import EnvironmentMigration from './EnvironmentMigration.vue'
|
||||
|
||||
const modal = useTemplateRef<InstanceType<typeof NewModal>>('modal')
|
||||
|
||||
function show() {
|
||||
modal.value?.show()
|
||||
}
|
||||
|
||||
function hide() {
|
||||
modal.value?.hide()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const route = useRoute()
|
||||
if (route.query.showEnvironmentMigrationWarning === 'true') {
|
||||
show()
|
||||
}
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
show,
|
||||
hide,
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,129 @@
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { ClientIcon, MonitorSmartphoneIcon, ServerIcon, UserIcon } from '@modrinth/assets'
|
||||
import type { Component } from 'vue'
|
||||
|
||||
import { defineMessage, type MessageDescriptor } from '../../../../composables/i18n'
|
||||
|
||||
export const ENVIRONMENTS_COPY: Record<
|
||||
Labrinth.Projects.v3.Environment,
|
||||
{ title: MessageDescriptor; description: MessageDescriptor; icon?: Component }
|
||||
> = {
|
||||
client_only: {
|
||||
title: defineMessage({
|
||||
id: 'project.environment.client-only.title',
|
||||
defaultMessage: 'Client-side only',
|
||||
}),
|
||||
description: defineMessage({
|
||||
id: 'project.environment.client-only.description',
|
||||
defaultMessage:
|
||||
'All functionality is done client-side and is compatible with vanilla servers.',
|
||||
}),
|
||||
icon: ClientIcon,
|
||||
},
|
||||
server_only: {
|
||||
title: defineMessage({
|
||||
id: 'project.environment.server-only.title',
|
||||
defaultMessage: 'Server-side only',
|
||||
}),
|
||||
description: defineMessage({
|
||||
id: 'project.environment.server-only.description',
|
||||
defaultMessage:
|
||||
'All functionality is done server-side and is compatible with vanilla clients.',
|
||||
}),
|
||||
icon: ServerIcon,
|
||||
},
|
||||
singleplayer_only: {
|
||||
title: defineMessage({
|
||||
id: 'project.environment.singleplayer-only.title',
|
||||
defaultMessage: 'Singleplayer only',
|
||||
}),
|
||||
description: defineMessage({
|
||||
id: 'project.environment.singleplayer-only.description',
|
||||
defaultMessage:
|
||||
'Only functions in Singleplayer or when not connected to a Multiplayer server.',
|
||||
}),
|
||||
icon: UserIcon,
|
||||
},
|
||||
dedicated_server_only: {
|
||||
title: defineMessage({
|
||||
id: 'project.environment.dedicated-server-only.title',
|
||||
defaultMessage: 'Server-side only',
|
||||
}),
|
||||
description: defineMessage({
|
||||
id: 'project.environment.dedicated-server-only.description',
|
||||
defaultMessage:
|
||||
'All functionality is done server-side and is compatible with vanilla clients.',
|
||||
}),
|
||||
icon: ServerIcon,
|
||||
},
|
||||
client_and_server: {
|
||||
title: defineMessage({
|
||||
id: 'project.environment.client-and-server.title',
|
||||
defaultMessage: 'Client and server',
|
||||
}),
|
||||
description: defineMessage({
|
||||
id: 'project.environment.client-and-server.description',
|
||||
defaultMessage:
|
||||
'Has some functionality on both the client and server, even if only partially.',
|
||||
}),
|
||||
icon: MonitorSmartphoneIcon,
|
||||
},
|
||||
client_only_server_optional: {
|
||||
title: defineMessage({
|
||||
id: 'project.environment.client-only-server-optional.title',
|
||||
defaultMessage: 'Client and server',
|
||||
}),
|
||||
description: defineMessage({
|
||||
id: 'project.environment.client-only-server-optional.description',
|
||||
defaultMessage:
|
||||
'Has some functionality on both the client and server, even if only partially.',
|
||||
}),
|
||||
icon: MonitorSmartphoneIcon,
|
||||
},
|
||||
server_only_client_optional: {
|
||||
title: defineMessage({
|
||||
id: 'project.environment.server-only-client-optional.title',
|
||||
defaultMessage: 'Client and server',
|
||||
}),
|
||||
description: defineMessage({
|
||||
id: 'project.environment.server-only-client-optional.description',
|
||||
defaultMessage:
|
||||
'Has some functionality on both the client and server, even if only partially.',
|
||||
}),
|
||||
icon: MonitorSmartphoneIcon,
|
||||
},
|
||||
client_or_server: {
|
||||
title: defineMessage({
|
||||
id: 'project.environment.client-or-server.title',
|
||||
defaultMessage: 'Client and server',
|
||||
}),
|
||||
description: defineMessage({
|
||||
id: 'project.environment.client-or-server.description',
|
||||
defaultMessage:
|
||||
'Has some functionality on both the client and server, even if only partially.',
|
||||
}),
|
||||
icon: MonitorSmartphoneIcon,
|
||||
},
|
||||
client_or_server_prefers_both: {
|
||||
title: defineMessage({
|
||||
id: 'project.environment.client-or-server-prefers-both.title',
|
||||
defaultMessage: 'Client and server',
|
||||
}),
|
||||
description: defineMessage({
|
||||
id: 'project.environment.client-or-server-prefers-both.description',
|
||||
defaultMessage:
|
||||
'Has some functionality on both the client and server, even if only partially.',
|
||||
}),
|
||||
icon: MonitorSmartphoneIcon,
|
||||
},
|
||||
unknown: {
|
||||
title: defineMessage({
|
||||
id: 'project.environment.unknown.title',
|
||||
defaultMessage: 'Unknown environment',
|
||||
}),
|
||||
description: defineMessage({
|
||||
id: 'project.environment.unknown.description',
|
||||
defaultMessage: 'The environment for this version could not be determined.',
|
||||
}),
|
||||
},
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
// Environment
|
||||
export { default as ProjectSettingsEnvSelector } from './environment/ProjectSettingsEnvSelector.vue'
|
||||
export { default as EnvironmentMigration } from './environment/EnvironmentMigration.vue'
|
||||
export { ENVIRONMENTS_COPY } from './environment/environments'
|
||||
export { default as EnvironmentSelector } from './environment/EnvironmentSelector.vue'
|
||||
export { default as ProjectEnvironmentModal } from './environment/ProjectEnvironmentModal.vue'
|
||||
|
||||
@@ -677,6 +677,66 @@
|
||||
"project.about.links.wiki": {
|
||||
"defaultMessage": "Visit wiki"
|
||||
},
|
||||
"project.environment.client-and-server.description": {
|
||||
"defaultMessage": "Has some functionality on both the client and server, even if only partially."
|
||||
},
|
||||
"project.environment.client-and-server.title": {
|
||||
"defaultMessage": "Client and server"
|
||||
},
|
||||
"project.environment.client-only-server-optional.description": {
|
||||
"defaultMessage": "Has some functionality on both the client and server, even if only partially."
|
||||
},
|
||||
"project.environment.client-only-server-optional.title": {
|
||||
"defaultMessage": "Client and server"
|
||||
},
|
||||
"project.environment.client-only.description": {
|
||||
"defaultMessage": "All functionality is done client-side and is compatible with vanilla servers."
|
||||
},
|
||||
"project.environment.client-only.title": {
|
||||
"defaultMessage": "Client-side only"
|
||||
},
|
||||
"project.environment.client-or-server-prefers-both.description": {
|
||||
"defaultMessage": "Has some functionality on both the client and server, even if only partially."
|
||||
},
|
||||
"project.environment.client-or-server-prefers-both.title": {
|
||||
"defaultMessage": "Client and server"
|
||||
},
|
||||
"project.environment.client-or-server.description": {
|
||||
"defaultMessage": "Has some functionality on both the client and server, even if only partially."
|
||||
},
|
||||
"project.environment.client-or-server.title": {
|
||||
"defaultMessage": "Client and server"
|
||||
},
|
||||
"project.environment.dedicated-server-only.description": {
|
||||
"defaultMessage": "All functionality is done server-side and is compatible with vanilla clients."
|
||||
},
|
||||
"project.environment.dedicated-server-only.title": {
|
||||
"defaultMessage": "Server-side only"
|
||||
},
|
||||
"project.environment.server-only-client-optional.description": {
|
||||
"defaultMessage": "Has some functionality on both the client and server, even if only partially."
|
||||
},
|
||||
"project.environment.server-only-client-optional.title": {
|
||||
"defaultMessage": "Client and server"
|
||||
},
|
||||
"project.environment.server-only.description": {
|
||||
"defaultMessage": "All functionality is done server-side and is compatible with vanilla clients."
|
||||
},
|
||||
"project.environment.server-only.title": {
|
||||
"defaultMessage": "Server-side only"
|
||||
},
|
||||
"project.environment.singleplayer-only.description": {
|
||||
"defaultMessage": "Only functions in Singleplayer or when not connected to a Multiplayer server."
|
||||
},
|
||||
"project.environment.singleplayer-only.title": {
|
||||
"defaultMessage": "Singleplayer only"
|
||||
},
|
||||
"project.environment.unknown.description": {
|
||||
"defaultMessage": "The environment for this version could not be determined."
|
||||
},
|
||||
"project.environment.unknown.title": {
|
||||
"defaultMessage": "Unknown environment"
|
||||
},
|
||||
"project.settings.analytics.title": {
|
||||
"defaultMessage": "Analytics"
|
||||
},
|
||||
@@ -710,6 +770,30 @@
|
||||
"project.settings.environment.client_only.title": {
|
||||
"defaultMessage": "Client-side only"
|
||||
},
|
||||
"project.settings.environment.notice.missing-env.description": {
|
||||
"defaultMessage": "Your project is missing environment metadata, please select the appropriate option below."
|
||||
},
|
||||
"project.settings.environment.notice.missing-env.title": {
|
||||
"defaultMessage": "Please select an environment for your project"
|
||||
},
|
||||
"project.settings.environment.notice.multiple-environments.description": {
|
||||
"defaultMessage": "Different versions of your project have different environments selected, so you can't edit them globally at this time."
|
||||
},
|
||||
"project.settings.environment.notice.multiple-environments.title": {
|
||||
"defaultMessage": "Your project has multiple environments"
|
||||
},
|
||||
"project.settings.environment.notice.review-options.description": {
|
||||
"defaultMessage": "We've just overhauled the Environments system on Modrinth and new options are now available. Please ensure the correct option is selected below and then click 'Verify' when you're done!"
|
||||
},
|
||||
"project.settings.environment.notice.review-options.title": {
|
||||
"defaultMessage": "Please review the options below"
|
||||
},
|
||||
"project.settings.environment.notice.wrong-project-type.description": {
|
||||
"defaultMessage": "Only mod or modpack projects can have environment metadata."
|
||||
},
|
||||
"project.settings.environment.notice.wrong-project-type.title": {
|
||||
"defaultMessage": "This project type does not support environment metadata"
|
||||
},
|
||||
"project.settings.environment.server_only.dedicated_only.title": {
|
||||
"defaultMessage": "Dedicated server only"
|
||||
},
|
||||
@@ -737,6 +821,12 @@
|
||||
"project.settings.environment.title": {
|
||||
"defaultMessage": "Environment"
|
||||
},
|
||||
"project.settings.environment.verification.verify-button": {
|
||||
"defaultMessage": "Verify"
|
||||
},
|
||||
"project.settings.environment.verification.verify-text": {
|
||||
"defaultMessage": "Verify that this project's environment is set correctly."
|
||||
},
|
||||
"project.settings.gallery.title": {
|
||||
"defaultMessage": "Gallery"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user