add experimental feature gate
This commit is contained in:
parent
f0a0a657b9
commit
59307392d4
10 changed files with 150 additions and 7 deletions
|
@ -2008,3 +2008,10 @@ _deck:
|
|||
list: "List"
|
||||
mentions: "Mentions"
|
||||
direct: "Direct messages"
|
||||
_experiments:
|
||||
title: "Experiments"
|
||||
alpha: "Alpha"
|
||||
beta: "Beta"
|
||||
release: "Release"
|
||||
enableExperimentalPostEditing: "Enable experimental post editing"
|
||||
experimentalPostEditingCaption: "Enables the option for users to edit their posts in post options"
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
export class ExperimentalFeatures1683980686995 {
|
||||
name = "ExperimentalFeatures1683980686995";
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "meta"
|
||||
ADD "experimentalFeatures" jsonb NOT NULL DEFAULT '{}'
|
||||
`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "meta" DROP COLUMN "experimentalFeatures"
|
||||
`);
|
||||
}
|
||||
}
|
|
@ -516,4 +516,9 @@ export class Meta {
|
|||
default: true,
|
||||
})
|
||||
public enableActiveEmailValidation: boolean;
|
||||
|
||||
@Column('jsonb', {
|
||||
default: {},
|
||||
})
|
||||
public experimentalFeatures: Record<string, unknown>;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import config from "@/config/index.js";
|
|||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||
import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js";
|
||||
import define from "../../define.js";
|
||||
import { Exp } from "@tensorflow/tfjs";
|
||||
|
||||
export const meta = {
|
||||
tags: ["meta"],
|
||||
|
@ -470,10 +471,20 @@ export const meta = {
|
|||
optional: false,
|
||||
nullable: false,
|
||||
},
|
||||
experimentalFeatures: {
|
||||
type: "object",
|
||||
optional: true,
|
||||
nullable: true,
|
||||
ref: "MetaExperimentalFeatures",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type MetaExperimentalFeatures = {
|
||||
postEditing: boolean;
|
||||
};
|
||||
|
||||
export const paramDef = {
|
||||
type: "object",
|
||||
properties: {},
|
||||
|
|
|
@ -170,6 +170,13 @@ export const paramDef = {
|
|||
objectStorageS3ForcePathStyle: { type: "boolean" },
|
||||
enableIpLogging: { type: "boolean" },
|
||||
enableActiveEmailValidation: { type: "boolean" },
|
||||
experimentalFeatures: {
|
||||
type: "object",
|
||||
nullable: true,
|
||||
properties: {
|
||||
postEditing: { type: "boolean" },
|
||||
},
|
||||
},
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
|
|
@ -304,6 +304,7 @@ export type LiteInstanceMetadata = {
|
|||
url: string;
|
||||
imageUrl: string;
|
||||
}[];
|
||||
experimentalFeatures?: Record<string, any>;
|
||||
};
|
||||
|
||||
export type DetailedInstanceMetadata = LiteInstanceMetadata & {
|
||||
|
|
83
packages/client/src/pages/admin/experiments.vue
Normal file
83
packages/client/src/pages/admin/experiments.vue
Normal file
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<MkStickyContainer>
|
||||
<FormSuspense :p="init">
|
||||
<FormSwitch
|
||||
v-model="enableExperimentalPostEditing"
|
||||
@update:modelValue="save"
|
||||
class="_formBlock"
|
||||
>
|
||||
<template #label
|
||||
><i class="ph-pencil-line ph-bold ph-lg"></i
|
||||
>{{ i18n.ts._experiments.enableExperimentalPostEditing
|
||||
}}<span class="level alpha"
|
||||
>({{ i18n.ts._experiments.alpha }})</span
|
||||
></template
|
||||
>
|
||||
<template #caption>{{
|
||||
i18n.ts._experiments.experimentalPostEditingCaption
|
||||
}}</template>
|
||||
</FormSwitch>
|
||||
</FormSuspense>
|
||||
</MkStickyContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {} from "vue";
|
||||
import MkStickyContainer from "@/components/global/MkStickyContainer.vue";
|
||||
import FormSuspense from "@/components/form/suspense.vue";
|
||||
import FormSwitch from "@/components/form/switch.vue";
|
||||
import * as os from "@/os";
|
||||
import { fetchInstance } from "@/instance";
|
||||
import { i18n } from "@/i18n";
|
||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||
|
||||
let enableExperimentalPostEditing = $ref(false);
|
||||
let meta = $ref<MetaExperiments | null>(null);
|
||||
|
||||
type MetaExperiments = {
|
||||
experimentalFeatures?: {
|
||||
postEditing?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
async function init() {
|
||||
meta = (await os.api("admin/meta")) as MetaExperiments;
|
||||
if (!meta) return;
|
||||
|
||||
enableExperimentalPostEditing =
|
||||
meta.experimentalFeatures?.postEditing ?? false;
|
||||
}
|
||||
|
||||
function save() {
|
||||
const experiments: MetaExperiments = {
|
||||
experimentalFeatures: {
|
||||
postEditing: enableExperimentalPostEditing,
|
||||
},
|
||||
};
|
||||
os.apiWithDialog("admin/update-meta", experiments).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
}
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts._experiments.title,
|
||||
icon: "ph-flask ph-bold ph-lg",
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.level {
|
||||
font-size: 0.8em;
|
||||
color: var(--color-gray-500);
|
||||
text-transform: uppercase;
|
||||
&.alpha {
|
||||
color: #f59e0b;
|
||||
}
|
||||
&.beta {
|
||||
color: #0ea5e9;
|
||||
}
|
||||
&.release {
|
||||
color: #10b981;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -300,6 +300,12 @@ const menuDef = $computed(() => [
|
|||
to: "/admin/database",
|
||||
active: currentPage?.route.name === "database",
|
||||
},
|
||||
{
|
||||
icon: "ph-flask ph-bold ph-lg",
|
||||
text: i18n.ts._experiments.title,
|
||||
to: "/admin/experiments",
|
||||
active: currentPage?.route.name === "experiments",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
|
|
@ -540,6 +540,11 @@ export const routes = [
|
|||
name: "other-settings",
|
||||
component: page(() => import("./pages/admin/custom-css.vue")),
|
||||
},
|
||||
{
|
||||
path: "/experiments",
|
||||
name: "experiments",
|
||||
component: page(() => import("./pages/admin/experiments.vue")),
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
component: page(() => import("./pages/_empty_.vue")),
|
||||
|
|
|
@ -421,7 +421,7 @@ export function getNoteMenu(props: {
|
|||
|
||||
null,
|
||||
|
||||
isAppearAuthor
|
||||
instance.experimentalFeatures?.postEditing && isAppearAuthor
|
||||
? {
|
||||
icon: "ph-pencil-line ph-bold ph-lg",
|
||||
text: i18n.ts.edit,
|
||||
|
@ -430,12 +430,14 @@ export function getNoteMenu(props: {
|
|||
}
|
||||
: undefined,
|
||||
|
||||
{
|
||||
icon: "ph-copy ph-bold ph-lg",
|
||||
text: i18n.ts.duplicate,
|
||||
textStyle: "color: var(--accent)",
|
||||
action: duplicate,
|
||||
},
|
||||
instance.experimentalFeatures?.postEditing
|
||||
? {
|
||||
icon: "ph-copy ph-bold ph-lg",
|
||||
text: i18n.ts.duplicate,
|
||||
textStyle: "color: var(--accent)",
|
||||
action: duplicate,
|
||||
}
|
||||
: undefined,
|
||||
|
||||
isAppearAuthor || isModerator
|
||||
? {
|
||||
|
|
Loading…
Reference in a new issue