feat: Toggle showing calckey updates as admin

This commit is contained in:
ThatOneCalculator 2022-10-25 22:31:19 -07:00
parent 07c6472ba6
commit 7559a0d81a
8 changed files with 54 additions and 13 deletions

View File

@ -917,6 +917,7 @@ splash: "Splash Screen"
updateAvailable: "There might be an update available!" updateAvailable: "There might be an update available!"
swipeOnDesktop: "Allow mobile-style swiping on desktop" swipeOnDesktop: "Allow mobile-style swiping on desktop"
logoImageUrl: "Logo image URL" logoImageUrl: "Logo image URL"
showAdminUpdates: "Indicate a new Calckey version is avaliable (admin only)"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server." description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server."

View File

@ -905,18 +905,19 @@ shuffle: "シャッフル"
account: "アカウント" account: "アカウント"
move: "移動" move: "移動"
adminCustomCssWarn: "この設定は、それが何をするものであるかを知っている場合のみ使用してください。不適切な値を入力すると、クライアントが正常に動作しなくなる可能性があります。ユーザー設定でCSSをテストし、正しく動作することを確認してください。" adminCustomCssWarn: "この設定は、それが何をするものであるかを知っている場合のみ使用してください。不適切な値を入力すると、クライアントが正常に動作しなくなる可能性があります。ユーザー設定でCSSをテストし、正しく動作することを確認してください。"
customMOTD: "カスタムMOTD(スプラッシュスクリーンメッセージ)" customMOTD: "カスタムMOTD(スプラッシュスクリーンメッセージ)"
customMOTDDescription: "ユーザがページをロード/リロードするたびにランダムに表示される、改行で区切られたMOTD(スプラッシュスクリーン)用のカスタムメッセージ" customMOTDDescription: "ユーザがページをロード/リロードするたびにランダムに表示される、改行で区切られたMOTD(スプラッシュスクリーン)用のカスタムメッセージ"
customSplashIcons: "カスタムスプラッシュスクリーンアイコン" customSplashIcons: "カスタムスプラッシュスクリーンアイコン"
customSplashIconsDescription: "ユーザがページをロード/リロードするたびにランダムに表示される、改行で区切られたカスタムスプラッシュスクリーンアイコンの URL。画像は静的なURLで、できればすべて192x192にリサイズしてください。" customSplashIconsDescription: "ユーザがページをロード/リロードするたびにランダムに表示される、改行で区切られたカスタムスプラッシュスクリーンアイコンの URL。画像は静的なURLで、できればすべて192x192にリサイズしてください。"
showUpdates: "Calckeyの更新時にポップアップを表示する" showUpdates: "Calckeyの更新時にポップアップを表示する"
recommendedInstances: "推奨インスタンス" recommendedInstances: "推奨インスタンス"
recommendedInstancesDescription: "推奨タイムラインに表示するために改行で区切られた推奨インスタンス。`https//`を追加しないでください。ドメインのみを追加してください。" recommendedInstancesDescription: "推奨タイムラインに表示するために改行で区切られた推奨インスタンス。`https://`を追加しないでください。ドメインのみを追加してください。"
caption: "自動キャプション" caption: "自動キャプション"
splash: "スプラッシュスクリーン" splash: "スプラッシュスクリーン"
updateAvailable: "アップデートがありますよ" updateAvailable: "アップデートがありますよ"
swipeOnDesktop: "デスクトップでモバイルスタイルのスワイプを可能にする" swipeOnDesktop: "デスクトップでモバイルスタイルのスワイプを可能にする"
logoImageUrl: "ロゴのURL" logoImageUrl: "ロゴのURL"
showAdminUpdates: "新しいCalckeyのバージョンが利用可能であることを示す(管理者のみ)"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。"

View File

@ -1,6 +1,6 @@
{ {
"name": "calckey", "name": "calckey",
"version": "12.119.0-calc.4.1", "version": "12.119.0-calc.4.2",
"codename": "aqua", "codename": "aqua",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -33,6 +33,7 @@ import { instance } from '@/instance';
import { version } from '@/config'; import { version } from '@/config';
import * as os from '@/os'; import * as os from '@/os';
import { lookupUser } from '@/scripts/lookup-user'; import { lookupUser } from '@/scripts/lookup-user';
import { defaultStore } from '@/store';
import { useRouter } from '@/router'; import { useRouter } from '@/router';
import { definePageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata';
@ -68,6 +69,7 @@ os.api('admin/abuse-user-reports', {
if (reports?.length > 0) thereIsUnresolvedAbuseReport = true; if (reports?.length > 0) thereIsUnresolvedAbuseReport = true;
}); });
if (defaultStore.state.showAdminUpdates) {
os.api('latest-version').then(res => { os.api('latest-version').then(res => {
const cleanRes = res?.tag_name.replace(/[^0-9]/g, ''); const cleanRes = res?.tag_name.replace(/[^0-9]/g, '');
const cleanVersion = version.replace(/[^0-9]/g, ''); const cleanVersion = version.replace(/[^0-9]/g, '');
@ -75,6 +77,7 @@ os.api('latest-version').then(res => {
updateAvailable = true; updateAvailable = true;
} }
}); });
}
const NARROW_THRESHOLD = 600; const NARROW_THRESHOLD = 600;
const ro = new ResizeObserver((entries, observer) => { const ro = new ResizeObserver((entries, observer) => {

View File

@ -59,6 +59,7 @@
</FormSwitch> </FormSwitch>
<FormSwitch v-model="disableDrawer" class="_formBlock">{{ i18n.ts.disableDrawer }}</FormSwitch> <FormSwitch v-model="disableDrawer" class="_formBlock">{{ i18n.ts.disableDrawer }}</FormSwitch>
<FormSwitch v-model="showUpdates" class="_formBlock">{{ i18n.ts.showUpdates }}</FormSwitch> <FormSwitch v-model="showUpdates" class="_formBlock">{{ i18n.ts.showUpdates }}</FormSwitch>
<FormSwitch v-if="$i?.isAdmin" v-model="showAdminUpdates" class="_formBlock">{{ i18n.ts.showAdminUpdates }}</FormSwitch>
<FormRadios v-model="fontSize" class="_formBlock"> <FormRadios v-model="fontSize" class="_formBlock">
<template #label>{{ i18n.ts.fontSize }}</template> <template #label>{{ i18n.ts.fontSize }}</template>
@ -96,6 +97,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { $i } from '@/account';
import FormSwitch from '@/components/form/switch.vue'; import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue'; import FormSelect from '@/components/form/select.vue';
import FormRadios from '@/components/form/radios.vue'; import FormRadios from '@/components/form/radios.vue';
@ -149,6 +151,7 @@ const seperateRenoteQuote = computed(defaultStore.makeGetterSetter('seperateReno
const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars')); const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars'));
const showUpdates = computed(defaultStore.makeGetterSetter('showUpdates')); const showUpdates = computed(defaultStore.makeGetterSetter('showUpdates'));
const swipeOnDesktop = computed(defaultStore.makeGetterSetter('swipeOnDesktop')); const swipeOnDesktop = computed(defaultStore.makeGetterSetter('swipeOnDesktop'));
const showAdminUpdates = computed(defaultStore.makeGetterSetter('showAdminUpdates'));
watch(lang, () => { watch(lang, () => {
localStorage.setItem('lang', lang.value as string); localStorage.setItem('lang', lang.value as string);
@ -184,6 +187,7 @@ watch([
showUpdates, showUpdates,
swipeOnDesktop, swipeOnDesktop,
seperateRenoteQuote, seperateRenoteQuote,
showAdminUpdates,
], async () => { ], async () => {
await reloadAsk(); await reloadAsk();
}); });

View File

@ -85,6 +85,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
'numberOfPageCache', 'numberOfPageCache',
'showUpdates', 'showUpdates',
'swipeOnDesktop', 'swipeOnDesktop',
'showAdminUpdates',
]; ];
const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [ const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
'lightTheme', 'lightTheme',

View File

@ -263,6 +263,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device', where: 'device',
default: false, default: false,
}, },
showAdminUpdates: {
where: 'account',
default: false,
}
})); }));
// TODO: 他のタブと永続化されたstateを同期 // TODO: 他のタブと永続化されたstateを同期

View File

@ -30,7 +30,7 @@
</template> </template>
<div class="divider"></div> <div class="divider"></div>
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip.noDelay.right="i18n.ts.controlPanel" class="item" active-class="active" to="/admin"> <MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip.noDelay.right="i18n.ts.controlPanel" class="item" active-class="active" to="/admin">
<i class="icon fas fa-door-open fa-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span> <span v-if="thereIsUnresolvedAbuseReport || noMaintainerInformation || noBotProtection || noEmailServer || updateAvailable" class="indicator"></span><i class="icon fas fa-door-open fa-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
</MkA> </MkA>
<button v-click-anime class="item _button" @click="more"> <button v-click-anime class="item _button" @click="more">
<i class="icon fa fa-ellipsis-h fa-fw"></i><span class="text">{{ i18n.ts.more }}</span> <i class="icon fa fa-ellipsis-h fa-fw"></i><span class="text">{{ i18n.ts.more }}</span>
@ -63,7 +63,9 @@ import { $i, openAccountMenu as openAccountMenu_ } from '@/account';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { instance } from '@/instance'; import { instance } from '@/instance';
import { host } from '@/config'; import { host, version } from '@/config';
const isEmpty = (x: string | null) => x == null || x === '';
const iconOnly = ref(false); const iconOnly = ref(false);
@ -88,6 +90,31 @@ watch(defaultStore.reactiveState.menuDisplay, () => {
calcViewState(); calcViewState();
}); });
let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha;
let noEmailServer = !instance.enableEmail;
let thereIsUnresolvedAbuseReport = $ref(false);
let updateAvailable = $ref(false);
if ($i?.isAdmin) {
os.api('admin/abuse-user-reports', {
state: 'unresolved',
limit: 1,
}).then(reports => {
if (reports?.length > 0) thereIsUnresolvedAbuseReport = true;
});
}
if (defaultStore.state.showAdminUpdates) {
os.api('latest-version').then(res => {
const cleanRes = res?.tag_name.replace(/[^0-9]/g, '');
const cleanVersion = version.replace(/[^0-9]/g, '');
if (cleanRes !== cleanVersion) {
updateAvailable = true;
}
});
}
function openAccountMenu(ev: MouseEvent) { function openAccountMenu(ev: MouseEvent) {
openAccountMenu_({ openAccountMenu_({
withExtraOperation: true, withExtraOperation: true,