add role policy "canViewFederation"

This commit is contained in:
Hazelnoot 2025-08-18 16:52:06 -04:00
parent aff1603788
commit 479e6daa1d
22 changed files with 84 additions and 13 deletions

View file

@ -93,7 +93,7 @@ import { useChartTooltip } from '@/use/use-chart-tooltip.js';
import { $i } from '@/i.js';
import * as os from '@/os.js';
import { misskeyApiGet } from '@/utility/misskey-api.js';
import { instance } from '@/instance.js';
import { instance, policies } from '@/instance.js';
import { i18n } from '@/i18n.js';
import MkHeatmap from '@/components/MkHeatmap.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
@ -103,7 +103,7 @@ import { initChart } from '@/utility/init-chart.js';
initChart();
const shouldShowFederation = computed(() => instance.federation !== 'none' || $i?.isModerator);
const shouldShowFederation = computed(() => (instance.federation !== 'none' || $i?.isModerator) && policies.canViewFederation);
const chartLimit = 500;
const chartSpan = ref<'hour' | 'day'>('hour');

View file

@ -58,7 +58,7 @@ import MkButton from '@/components/MkButton.vue';
import { widgets as widgetDefs, federationWidgets } from '@/widgets/index.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { instance, policies } from '@/instance.js';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
@ -68,7 +68,7 @@ const props = defineProps<{
}>();
const _widgetDefs = computed(() => {
if (instance.federation === 'none') {
if (instance.federation === 'none' || !policies.value.canViewFederation) {
return widgetDefs.filter(x => !federationWidgets.includes(x));
} else {
return widgetDefs;

View file

@ -22,10 +22,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, defineAsyncComponent, ref, watch } from 'vue';
import { instance } from '@/instance.js';
import { instance, policies } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { claimAchievement } from '@/utility/achievements.js';
import { definePage } from '@/page.js';
import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
const XOverview = defineAsyncComponent(() => import('@/pages/about.overview.vue'));
const XEmojis = defineAsyncComponent(() => import('@/pages/about.emojis.vue'));
@ -49,7 +50,7 @@ watch(tab, () => {
const headerActions = computed(() => []);
const headerTabs = computed(() => {
const items = [];
const items: Tab[] = [];
items.push({
key: 'overview',
@ -60,7 +61,7 @@ const headerTabs = computed(() => {
icon: 'ph-smiley ph-bold ph-lg',
});
if (instance.federation !== 'none') {
if (instance.federation !== 'none' && policies.value.canViewFederation) {
items.push({
key: 'federation',
title: i18n.ts.federation,

View file

@ -817,6 +817,26 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkRange>
</div>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canViewFederation, 'canViewFederation'])">
<template #label>{{ i18n.ts._role._options.canViewFederation }}</template>
<template #suffix>
<span v-if="role.policies.canViewFederation.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
<span v-else>{{ role.policies.canViewFederation.value ? i18n.ts.yes : i18n.ts.no }}</span>
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canViewFederation)"></i></span>
</template>
<div class="_gaps">
<MkSwitch v-model="role.policies.canViewFederation.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
</MkSwitch>
<MkSwitch v-model="role.policies.canViewFederation.value" :disabled="role.policies.canViewFederation.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
<MkRange v-model="role.policies.canViewFederation.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
<template #label>{{ i18n.ts._role.priority }}</template>
</MkRange>
</div>
</MkFolder>
</div>
</FormSlot>
</div>

View file

@ -308,6 +308,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canViewFederation, 'canViewFederation'])">
<template #label>{{ i18n.ts._role._options.canViewFederation }}</template>
<template #suffix>{{ policies.canViewFederation ? i18n.ts.yes : i18n.ts.no }}</template>
<MkSwitch v-model="policies.canViewFederation">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
</MkFolder>
</div>
</MkFolder>
<MkButton primary rounded @click="create"><i class="ti ti-plus"></i> {{ i18n.ts._role.new }}</MkButton>

View file

@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSelect v-model="statusbar.type" placeholder="Please select">
<template #label>{{ i18n.ts.type }}</template>
<option value="rss">RSS</option>
<option v-if="instance.federation !== 'none'" value="federation">Federation</option>
<option v-if="instance.federation !== 'none' && policies.canViewFederation" value="federation">Federation</option>
<option value="userList">User list timeline</option>
</MkSelect>
@ -95,7 +95,7 @@ import MkRadios from '@/components/MkRadios.vue';
import MkButton from '@/components/MkButton.vue';
import MkRange from '@/components/MkRange.vue';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { instance, policies } from '@/instance.js';
import { deepClone } from '@/utility/clone.js';
import { prefer } from '@/preferences.js';

View file

@ -6,7 +6,7 @@
import { defineAsyncComponent } from 'vue';
import type { MenuItem } from '@/types/menu.js';
import * as os from '@/os.js';
import { instance } from '@/instance.js';
import { instance, policies } from '@/instance.js';
import { host } from '@@/js/config.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/i.js';
@ -58,7 +58,7 @@ export function openInstanceMenu(ev: MouseEvent) {
to: '/about#emojis',
});
if (instance.federation !== 'none') {
if (instance.federation !== 'none' && policies.value.canViewFederation) {
menuItems.push({
type: 'link',
text: i18n.ts.federation,

View file

@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<span :class="$style.name">{{ x.name }}</span>
<XRss v-if="x.type === 'rss'" :class="$style.body" :refreshIntervalSec="x.props.refreshIntervalSec" :marqueeDuration="x.props.marqueeDuration" :marqueeReverse="x.props.marqueeReverse" :display="x.props.display" :url="x.props.url" :shuffle="x.props.shuffle"/>
<XFederation v-else-if="x.type === 'federation' && instance.federation !== 'none'" :class="$style.body" :refreshIntervalSec="x.props.refreshIntervalSec" :marqueeDuration="x.props.marqueeDuration" :marqueeReverse="x.props.marqueeReverse" :display="x.props.display" :colored="x.props.colored"/>
<XFederation v-else-if="x.type === 'federation' && instance.federation !== 'none' && policies.canViewFederation" :class="$style.body" :refreshIntervalSec="x.props.refreshIntervalSec" :marqueeDuration="x.props.marqueeDuration" :marqueeReverse="x.props.marqueeReverse" :display="x.props.display" :colored="x.props.colored"/>
<XUserList v-else-if="x.type === 'userList'" :class="$style.body" :refreshIntervalSec="x.props.refreshIntervalSec" :marqueeDuration="x.props.marqueeDuration" :marqueeReverse="x.props.marqueeReverse" :display="x.props.display" :userListId="x.props.userListId"/>
</div>
</div>
@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent } from 'vue';
import { instance } from '@/instance.js';
import { instance, policies } from '@/instance.js';
import { prefer } from '@/preferences.js';
const XRss = defineAsyncComponent(() => import('./statusbar-rss.vue'));
const XFederation = defineAsyncComponent(() => import('./statusbar-federation.vue'));