Fixed permissions issue and modpack issue with environment overhaul

This commit is contained in:
Prospector
2025-08-28 18:40:05 -07:00
parent 697468e910
commit debaf1381c
9 changed files with 94 additions and 26 deletions

View File

@@ -764,7 +764,16 @@
> >
{{ formatMessage(messages.environmentMigrationMessage) }} {{ formatMessage(messages.environmentMigrationMessage) }}
<ButtonStyled color="orange"> <ButtonStyled color="orange">
<nuxt-link :to="`/project/${project.id}/settings/environment`" class="mt-3 w-fit"> <nuxt-link
v-tooltip="
hasEditDetailsPermission
? undefined
: formatMessage(commonProjectSettingsMessages.noPermissionDescription)
"
:to="`/project/${project.id}/settings/environment`"
class="mt-3 w-fit"
:disabled="!hasEditDetailsPermission"
>
<SettingsIcon /> {{ formatMessage(messages.reviewEnvironmentSettings) }} <SettingsIcon /> {{ formatMessage(messages.reviewEnvironmentSettings) }}
</nuxt-link> </nuxt-link>
</ButtonStyled> </ButtonStyled>
@@ -957,6 +966,7 @@ import {
ButtonStyled, ButtonStyled,
Checkbox, Checkbox,
commonMessages, commonMessages,
commonProjectSettingsMessages,
injectNotificationManager, injectNotificationManager,
NewModal, NewModal,
OverflowMenu, OverflowMenu,
@@ -1589,6 +1599,11 @@ const currentMember = computed(() => {
return val return val
}) })
const hasEditDetailsPermission = computed(() => {
const EDIT_DETAILS = 1 << 2
return (currentMember.value?.permissions & EDIT_DETAILS) === EDIT_DETAILS
})
versions.value = data.$computeVersions(versions.value, allMembers.value) versions.value = data.$computeVersions(versions.value, allMembers.value)
// Q: Why do this instead of computing the versions of featuredVersions? // Q: Why do this instead of computing the versions of featuredVersions?
@@ -1870,6 +1885,7 @@ provideProjectPageContext({
projectV2: project, projectV2: project,
projectV3, projectV3,
refreshProject: resetProject, refreshProject: resetProject,
currentMember,
}) })
</script> </script>

View File

@@ -14,7 +14,7 @@ import { defineMessages, useVIntl } from '@vintl/vintl'
const { formatMessage } = useVIntl() const { formatMessage } = useVIntl()
const { projectV2, projectV3, refreshProject } = injectProjectPageContext() const { currentMember, projectV2, projectV3, refreshProject } = injectProjectPageContext()
const { handleError } = injectNotificationManager() const { handleError } = injectNotificationManager()
const api = injectApi() const api = injectApi()
@@ -32,6 +32,11 @@ const needsToVerify = computed(
supportsEnvironment.value, supportsEnvironment.value,
) )
const hasPermission = computed(() => {
const EDIT_DETAILS = 1 << 2
return (currentMember.value?.permissions & EDIT_DETAILS) === EDIT_DETAILS
})
function getInitialEnv() { function getInitialEnv() {
return projectV3.value.environment?.length === 1 ? projectV3.value.environment[0] : undefined return projectV3.value.environment?.length === 1 ? projectV3.value.environment[0] : undefined
} }
@@ -106,7 +111,7 @@ const messages = defineMessages({
<template> <template>
<div> <div>
<UnsavedChangesPopup <UnsavedChangesPopup
v-if="supportsEnvironment" v-if="supportsEnvironment && hasPermission"
:original="saved" :original="saved"
:modified="current" :modified="current"
:saving="saving" :saving="saving"
@@ -125,40 +130,43 @@ const messages = defineMessages({
v-if="!supportsEnvironment" v-if="!supportsEnvironment"
type="critical" type="critical"
:header="formatMessage(messages.wrongProjectTypeTitle)" :header="formatMessage(messages.wrongProjectTypeTitle)"
:body="formatMessage(messages.wrongProjectTypeDescription)"
class="mb-3" class="mb-3"
> />
{{ formatMessage(messages.wrongProjectTypeDescription) }}
</Admonition>
<template v-else> <template v-else>
<Admonition <Admonition
v-if=" v-if="!hasPermission"
type="critical"
:header="formatMessage(commonProjectSettingsMessages.noPermissionTitle)"
:body="formatMessage(commonProjectSettingsMessages.noPermissionDescription)"
class="mb-3"
/>
<Admonition
v-else-if="
!projectV3.environment || !projectV3.environment ||
projectV3.environment.length === 0 || projectV3.environment.length === 0 ||
projectV3.environment[0] === 'unknown' projectV3.environment[0] === 'unknown'
" "
type="critical" type="critical"
:header="formatMessage(messages.missingEnvTitle)" :header="formatMessage(messages.missingEnvTitle)"
:body="formatMessage(messages.missingEnvDescription)"
class="mb-3" class="mb-3"
> />
{{ formatMessage(messages.missingEnvDescription) }}
</Admonition>
<Admonition <Admonition
v-else-if="projectV3.environment.length > 1" v-else-if="projectV3.environment.length > 1"
type="info" type="info"
:header="formatMessage(messages.multipleEnvironmentsTitle)" :header="formatMessage(messages.multipleEnvironmentsTitle)"
:body="formatMessage(messages.multipleEnvironmentsDescription)"
class="mb-3" class="mb-3"
> />
{{ formatMessage(messages.multipleEnvironmentsDescription) }}
</Admonition>
<Admonition <Admonition
v-else-if="needsToVerify" v-else-if="needsToVerify"
type="warning" type="warning"
:header="formatMessage(messages.reviewOptionsTitle)" :header="formatMessage(messages.reviewOptionsTitle)"
:body="formatMessage(messages.reviewOptionsDescription)"
class="mb-3" class="mb-3"
> />
{{ formatMessage(messages.reviewOptionsDescription) }} <ProjectSettingsEnvSelector v-model="current.environment" :disabled="!hasPermission" />
</Admonition>
<ProjectSettingsEnvSelector v-model="current.environment" />
</template> </template>
</div> </div>
</div> </div>

View File

@@ -456,9 +456,8 @@ if (user.value?.projects) {
projects.value = updateSort(user.value.projects, 'Name', false) projects.value = updateSort(user.value.projects, 'Name', false)
user.value?.projectsV3?.forEach((project) => { user.value?.projectsV3?.forEach((project) => {
if ( if (
(project.side_types_migration_review_status === 'pending' && project.side_types_migration_review_status === 'pending' &&
project.project_types.includes('mod')) || (project.project_types.includes('mod') || project.project_types.includes('modpack'))
project.project_types.includes('modpack')
) { ) {
projectsWithMigrationWarning.value.push(project.id) projectsWithMigrationWarning.value.push(project.id)
} }

View File

@@ -1,7 +1,13 @@
<template> <template>
<button <button
class="px-4 py-3 text-left border-0 font-medium border-2 border-button-bg border-solid flex gap-2 transition-all cursor-pointer active:scale-[0.98] hover:bg-button-bg hover:brightness-[--hover-brightness] rounded-xl" class="px-4 py-3 text-left border-0 font-medium border-2 border-button-bg border-solid flex gap-2 transition-all cursor-pointer rounded-xl"
:class="selected ? 'text-contrast bg-button-bg' : 'text-primary bg-transparent'" :class="
(selected ? 'text-contrast bg-button-bg' : 'text-primary bg-transparent') +
(disabled
? ' opacity-50'
: ' active:scale-[0.98] hover:bg-button-bg hover:brightness-[--hover-brightness]')
"
:disabled="disabled"
@click="emit('select')" @click="emit('select')"
> >
<RadioButtonCheckedIcon v-if="selected" class="text-brand h-5 w-5 shrink-0" /> <RadioButtonCheckedIcon v-if="selected" class="text-brand h-5 w-5 shrink-0" />
@@ -16,7 +22,13 @@ const emit = defineEmits<{
(e: 'select'): void (e: 'select'): void
}>() }>()
defineProps<{ withDefaults(
selected: boolean defineProps<{
}>() selected: boolean
disabled?: boolean
}>(),
{
disabled: false,
},
)
</script> </script>

View File

@@ -9,6 +9,15 @@ const { formatMessage } = useVIntl()
const value = defineModel<EnvironmentV3 | undefined>({ required: true }) const value = defineModel<EnvironmentV3 | undefined>({ required: true })
withDefaults(
defineProps<{
disabled?: boolean
}>(),
{
disabled: false,
},
)
type EnvironmentRadioOption = { type EnvironmentRadioOption = {
title: MessageDescriptor title: MessageDescriptor
description?: MessageDescriptor description?: MessageDescriptor
@@ -224,6 +233,7 @@ const simulateSave = ref(false)
class="!w-full" class="!w-full"
:class="{ 'mt-2': index > 0 }" :class="{ 'mt-2': index > 0 }"
:selected="currentOuterOption === key" :selected="currentOuterOption === key"
:disabled="disabled"
@select=" @select="
() => { () => {
if (currentOuterOption !== key) { if (currentOuterOption !== key) {
@@ -252,6 +262,7 @@ const simulateSave = ref(false)
'opacity-50': currentOuterOption !== key, 'opacity-50': currentOuterOption !== key,
}" }"
:selected="currentSubOption === suboptionKey" :selected="currentSubOption === suboptionKey"
:disabled="disabled"
@select=" @select="
() => { () => {
currentOuterOption = key currentOuterOption = key

View File

@@ -506,6 +506,12 @@
"project.settings.members.title": { "project.settings.members.title": {
"defaultMessage": "Members" "defaultMessage": "Members"
}, },
"project.settings.notice.no-permission.description": {
"defaultMessage": "You don't have permission to edit this."
},
"project.settings.notice.no-permission.title": {
"defaultMessage": "No permission"
},
"project.settings.tags.title": { "project.settings.tags.title": {
"defaultMessage": "Tags" "defaultMessage": "Tags"
}, },

View File

@@ -1,4 +1,4 @@
import type { Project, ProjectV3Partial } from '@modrinth/utils' import type { Project, ProjectV3Partial, TeamMember } from '@modrinth/utils'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { createContext } from '.' import { createContext } from '.'
@@ -7,6 +7,7 @@ export interface ProjectPageContext {
projectV2: Ref<Project> projectV2: Ref<Project>
projectV3: Ref<ProjectV3Partial> projectV3: Ref<ProjectV3Partial>
refreshProject: () => Promise<void> refreshProject: () => Promise<void>
currentMember: Ref<TeamMember>
} }
export const [injectProjectPageContext, provideProjectPageContext] = export const [injectProjectPageContext, provideProjectPageContext] =

View File

@@ -331,6 +331,14 @@ export const commonProjectSettingsMessages = defineMessages({
id: 'project.settings.members.title', id: 'project.settings.members.title',
defaultMessage: 'Members', defaultMessage: 'Members',
}, },
noPermissionDescription: {
id: 'project.settings.notice.no-permission.description',
defaultMessage: `You don't have permission to edit this.`,
},
noPermissionTitle: {
id: 'project.settings.notice.no-permission.title',
defaultMessage: 'No permission',
},
tags: { tags: {
id: 'project.settings.tags.title', id: 'project.settings.tags.title',
defaultMessage: 'Tags', defaultMessage: 'Tags',

View File

@@ -10,6 +10,13 @@ export type VersionEntry = {
} }
const VERSIONS: VersionEntry[] = [ const VERSIONS: VersionEntry[] = [
{
date: `2025-08-28T18:45:00-07:00`,
product: 'web',
body: `### Improvements
- Fixed modpacks showing an environment migration warning perpetually.
- Fixed environment settings being unclear about permissions.`,
},
{ {
date: `2025-08-28T16:50:00-07:00`, date: `2025-08-28T16:50:00-07:00`,
product: 'web', product: 'web',