From c46a94dcd6be6d46bf9566120c9535bc51184814 Mon Sep 17 00:00:00 2001 From: Prospector <6166773+Prospector@users.noreply.github.com> Date: Thu, 30 Apr 2026 18:41:55 -0700 Subject: [PATCH 1/3] Add monetization toggle for projects --- .../src/pages/[type]/[id]/settings/index.vue | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/apps/frontend/src/pages/[type]/[id]/settings/index.vue b/apps/frontend/src/pages/[type]/[id]/settings/index.vue index e78a94564d..7ef1319512 100644 --- a/apps/frontend/src/pages/[type]/[id]/settings/index.vue +++ b/apps/frontend/src/pages/[type]/[id]/settings/index.vue @@ -273,6 +273,60 @@ + +
+
+ + +
+
+
+
+ + + + +
+
@@ -311,6 +365,7 @@ import { CheckIcon, ImageIcon, IssuesIcon, + ScaleIcon, TrashIcon, TriangleAlertIcon, UploadIcon, @@ -319,6 +374,7 @@ import { import { MIN_SUMMARY_CHARS } from '@modrinth/moderation' import { Avatar, + ButtonStyled, Combobox, ConfirmLeaveModal, ConfirmModal, @@ -326,14 +382,18 @@ import { injectNotificationManager, injectProjectPageContext, StyledInput, + Toggle, UnsavedChangesPopup, usePageLeaveSafety, } from '@modrinth/ui' import { fileIsValid, formatProjectStatus, formatProjectType } from '@modrinth/utils' import FileInput from '~/components/ui/FileInput.vue' +import { useAuth } from '~/composables/auth.js' import { useFeatureFlags } from '~/composables/featureFlags.ts' +const auth = await useAuth() + const { addNotification } = injectNotificationManager() const { projectV2: project, @@ -364,6 +424,22 @@ const visibility = ref( : project.value.requested_status, ) +const monetizationEnabled = ref(project.value.monetization_status === 'monetized') +const loadingModeratorMonetization = ref(false) + +watch( + () => project.value.monetization_status, + () => { + monetizationEnabled.value = project.value.monetization_status === 'monetized' + }, +) + +const isStaff = computed( + () => !!auth.value.user && tags.value.staffRoles.includes(auth.value.user.role), +) + +const isForceDemonetized = computed(() => project.value.monetization_status === 'force-demonetized') + // Server project specific refs const MC_SERVER_BANNER_NAME = '__mc_server_banner__' const isServerProject = computed(() => projectV3.value?.minecraft_server != null) @@ -378,6 +454,8 @@ const hasPermission = computed(() => { return ((currentMember.value?.permissions ?? 0) & EDIT_DETAILS) === EDIT_DETAILS }) +const monetizationToggleDisabled = computed(() => !hasPermission.value || isForceDemonetized.value) + const hasDeletePermission = computed(() => { const DELETE_PROJECT = 1 << 7 return ((currentMember.value?.permissions ?? 0) & DELETE_PROJECT) === DELETE_PROJECT @@ -450,6 +528,13 @@ const basePatchData = computed(() => { data.requested_status = visibility.value } + if (project.value.monetization_status !== 'force-demonetized') { + const wasMonetized = project.value.monetization_status === 'monetized' + if (monetizationEnabled.value !== wasMonetized) { + data.monetization_status = monetizationEnabled.value ? 'monetized' : 'demonetized' + } + } + return data }) @@ -468,6 +553,7 @@ const original = computed(() => ({ deletedIcon: false, bannerFile: null, deletedBanner: false, + monetizationEnabled: project.value.monetization_status === 'monetized', })) const modified = computed(() => ({ @@ -481,6 +567,7 @@ const modified = computed(() => ({ deletedIcon: deletedIcon.value, bannerFile: bannerFile.value, deletedBanner: deletedBanner.value, + monetizationEnabled: monetizationEnabled.value, })) const hasChanges = computed(() => @@ -504,6 +591,16 @@ function resetChanges() { bannerFile.value = null bannerPreview.value = null deletedBanner.value = false + monetizationEnabled.value = project.value.monetization_status === 'monetized' +} + +async function updateMonetizationStatus(status) { + loadingModeratorMonetization.value = true + try { + await patchProject({ monetization_status: status }) + } finally { + loadingModeratorMonetization.value = false + } } const hasModifiedVisibility = () => { From 9b0782d59a747bae8827d8f28ee2d359f1687562 Mon Sep 17 00:00:00 2001 From: Prospector <6166773+Prospector@users.noreply.github.com> Date: Fri, 1 May 2026 11:52:31 -0700 Subject: [PATCH 2/3] add flag for monetization toggle --- apps/frontend/src/composables/featureFlags.ts | 1 + apps/frontend/src/pages/[type]/[id]/settings/index.vue | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/composables/featureFlags.ts b/apps/frontend/src/composables/featureFlags.ts index be15100be2..8de3e8c149 100644 --- a/apps/frontend/src/composables/featureFlags.ts +++ b/apps/frontend/src/composables/featureFlags.ts @@ -47,6 +47,7 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({ showDiscoverProjectButtons: false, useV1ContentTabAPI: true, labrinthApiCanary: false, + projectMonetizationToggle: false, } as const) export type FeatureFlag = keyof typeof DEFAULT_FEATURE_FLAGS diff --git a/apps/frontend/src/pages/[type]/[id]/settings/index.vue b/apps/frontend/src/pages/[type]/[id]/settings/index.vue index 7ef1319512..e300a87479 100644 --- a/apps/frontend/src/pages/[type]/[id]/settings/index.vue +++ b/apps/frontend/src/pages/[type]/[id]/settings/index.vue @@ -274,7 +274,10 @@ -
+
-
+