diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index d37b0b8225..97344d9451 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -157,6 +157,11 @@ provideModrinthClient(tauriApiClient) providePageContext({ hierarchicalSidebarAvailable: ref(true), showAds: ref(false), + featureFlags: { + serverRamAsBytesAlwaysOn: computed(() => + themeStore.getFeatureFlag('server_ram_as_bytes_always_on'), + ), + }, openExternalUrl: (url) => openUrl(url), }) provideModalBehavior({ diff --git a/apps/app-frontend/src/store/theme.ts b/apps/app-frontend/src/store/theme.ts index dbdd5a1196..ccf143a78f 100644 --- a/apps/app-frontend/src/store/theme.ts +++ b/apps/app-frontend/src/store/theme.ts @@ -6,6 +6,7 @@ export const DEFAULT_FEATURE_FLAGS = { worlds_tab: false, worlds_in_home: true, server_project_qa: false, + server_ram_as_bytes_always_on: false, i18n_debug: false, } diff --git a/apps/frontend/src/composables/featureFlags.ts b/apps/frontend/src/composables/featureFlags.ts index a1acb5878c..be15100be2 100644 --- a/apps/frontend/src/composables/featureFlags.ts +++ b/apps/frontend/src/composables/featureFlags.ts @@ -38,6 +38,7 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({ showProjectPageQuickServerButton: false, newProjectGeneralSettings: false, newProjectEnvironmentSettings: true, + serverRamAsBytesAlwaysOn: false, archonSentryCapture: false, hideRussiaCensorshipBanner: false, disablePrettyProjectUrlRedirects: false, diff --git a/apps/frontend/src/providers/setup/page-context.ts b/apps/frontend/src/providers/setup/page-context.ts index 919a146c62..4ace7cffa1 100644 --- a/apps/frontend/src/providers/setup/page-context.ts +++ b/apps/frontend/src/providers/setup/page-context.ts @@ -1,12 +1,18 @@ import { provideModalBehavior, providePageContext } from '@modrinth/ui' import { computed, ref } from 'vue' +import { useFeatureFlags } from '~/composables/featureFlags.ts' + export function setupPageContextProvider() { const cosmetics = useCosmetics() + const featureFlags = useFeatureFlags() providePageContext({ hierarchicalSidebarAvailable: ref(false), showAds: ref(false), + featureFlags: { + serverRamAsBytesAlwaysOn: computed(() => featureFlags.value.serverRamAsBytesAlwaysOn), + }, openExternalUrl: (url) => window.open(url, '_blank'), }) provideModalBehavior({ diff --git a/packages/app-lib/src/state/settings.rs b/packages/app-lib/src/state/settings.rs index 4b555bdeb9..6345e4c6a1 100644 --- a/packages/app-lib/src/state/settings.rs +++ b/packages/app-lib/src/state/settings.rs @@ -55,6 +55,7 @@ pub enum FeatureFlag { ProjectBackground, WorldsTab, WorldsInHome, + ServerRamAsBytesAlwaysOn, ServersInApp, ServerProjectQa, I18nDebug, diff --git a/packages/ui/src/layouts/shared/server-settings/pages/general.vue b/packages/ui/src/layouts/shared/server-settings/pages/general.vue index 756da0fbe6..7bedf006c3 100644 --- a/packages/ui/src/layouts/shared/server-settings/pages/general.vue +++ b/packages/ui/src/layouts/shared/server-settings/pages/general.vue @@ -89,12 +89,15 @@ {{ prefConfig.description }} - +
+ +
@@ -138,11 +141,13 @@ import { injectModrinthClient, injectModrinthServerContext, injectNotificationManager, + injectPageContext, } from '#ui/providers' const { addNotification } = injectNotificationManager() const client = injectModrinthClient() const { server: data, serverId, busyReasons } = injectModrinthServerContext() +const { featureFlags } = injectPageContext() const queryClient = useQueryClient() const serverName = ref(data.value?.name) @@ -207,6 +212,28 @@ const userPreferences = useStorage( const newUserPreferences = ref(JSON.parse(JSON.stringify(userPreferences.value))) +const isRamAsBytesForcedByFeatureFlag = computed( + () => featureFlags?.serverRamAsBytesAlwaysOn?.value ?? false, +) + +const isPreferenceForcedByFeatureFlag = (key: string) => + key === 'ramAsNumber' && isRamAsBytesForcedByFeatureFlag.value + +const getPreferenceTooltip = (key: string) => + isPreferenceForcedByFeatureFlag(key) + ? 'Feature flag enabled to always show RAM as bytes.' + : undefined + +const getPreferenceValue = (key: string) => + isPreferenceForcedByFeatureFlag(key) ? true : newUserPreferences.value[key as PreferenceKeys] + +const setPreferenceValue = (key: string, value: boolean) => { + if (isPreferenceForcedByFeatureFlag(key)) { + return + } + newUserPreferences.value[key as PreferenceKeys] = value +} + // Info properties const infoProperties = [ { name: 'Server ID', value: serverId ?? 'Unknown' }, diff --git a/packages/ui/src/layouts/wrapped/hosting/manage/components/ServerManageStats.vue b/packages/ui/src/layouts/wrapped/hosting/manage/components/ServerManageStats.vue index 97b57d73ac..d27f8aece4 100644 --- a/packages/ui/src/layouts/wrapped/hosting/manage/components/ServerManageStats.vue +++ b/packages/ui/src/layouts/wrapped/hosting/manage/components/ServerManageStats.vue @@ -60,11 +60,12 @@ import { useStorage } from '@vueuse/core' import { computed, defineAsyncComponent, ref, shallowRef, watch } from 'vue' import { RouterLink } from 'vue-router' -import { injectModrinthServerContext } from '#ui/providers' +import { injectModrinthServerContext, injectPageContext } from '#ui/providers' const VueApexCharts = defineAsyncComponent(() => import('vue3-apexcharts')) const { serverId } = injectModrinthServerContext() +const { featureFlags } = injectPageContext() const props = withDefaults( defineProps<{ @@ -83,6 +84,9 @@ const chartsReady = ref(new Set()) const userPreferences = useStorage(`pyro-server-${serverId || 'unknown'}-preferences`, { ramAsNumber: false, }) +const isRamAsBytesForcedByFeatureFlag = computed( + () => featureFlags?.serverRamAsBytesAlwaysOn?.value ?? false, +) const stats = shallowRef( props.data?.current || { @@ -214,7 +218,9 @@ const metrics = computed(() => { { title: 'Memory', value: - props.showMemoryAsBytes || userPreferences.value.ramAsNumber + props.showMemoryAsBytes || + isRamAsBytesForcedByFeatureFlag.value || + userPreferences.value.ramAsNumber ? formatBytes(stats.value.ram_usage_bytes ?? 0) : `${ramPercent.value.toFixed(2)}%`, icon: DatabaseIcon, diff --git a/packages/ui/src/providers/page-context.ts b/packages/ui/src/providers/page-context.ts index ddac323294..6dfd9156d8 100644 --- a/packages/ui/src/providers/page-context.ts +++ b/packages/ui/src/providers/page-context.ts @@ -6,6 +6,9 @@ export interface PageContext { // pages may render sidebar content in #sidebar-teleport-target instead of in the main layout when true hierarchicalSidebarAvailable: Ref showAds: Ref + featureFlags?: { + serverRamAsBytesAlwaysOn?: Ref + } openExternalUrl: (url: string) => void }