summaryrefslogtreecommitdiff
path: root/packages/frontend/src/pages
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2024-03-01 21:17:01 +0900
committerGitHub <noreply@github.com>2024-03-01 21:17:01 +0900
commit7e706ea6693781fb8973800bb3d0c91b5ab91cdf (patch)
tree8abd91edd288284cf3cc47949e749f881cfaff8e /packages/frontend/src/pages
parentMerge pull request #13045 from misskey-dev/develop (diff)
parentNew translations ja-jp.yml (Chinese Traditional) (#13480) (diff)
downloadmisskey-7e706ea6693781fb8973800bb3d0c91b5ab91cdf.tar.gz
misskey-7e706ea6693781fb8973800bb3d0c91b5ab91cdf.tar.bz2
misskey-7e706ea6693781fb8973800bb3d0c91b5ab91cdf.zip
Merge pull request #13447 from misskey-dev/develop
Release: 2024.3.0
Diffstat (limited to 'packages/frontend/src/pages')
-rw-r--r--packages/frontend/src/pages/admin/RolesEditorFormula.vue9
-rw-r--r--packages/frontend/src/pages/admin/bot-protection.vue2
-rw-r--r--packages/frontend/src/pages/admin/branding.vue2
-rw-r--r--packages/frontend/src/pages/admin/email-settings.vue2
-rw-r--r--packages/frontend/src/pages/admin/external-services.vue2
-rw-r--r--packages/frontend/src/pages/admin/instance-block.vue2
-rw-r--r--packages/frontend/src/pages/admin/moderation.vue2
-rw-r--r--packages/frontend/src/pages/admin/modlog.ModLog.vue6
-rw-r--r--packages/frontend/src/pages/admin/modlog.vue2
-rw-r--r--packages/frontend/src/pages/admin/object-storage.vue2
-rw-r--r--packages/frontend/src/pages/admin/other-settings.vue2
-rw-r--r--packages/frontend/src/pages/admin/proxy-account.vue2
-rw-r--r--packages/frontend/src/pages/admin/roles.editor.vue19
-rw-r--r--packages/frontend/src/pages/admin/roles.vue7
-rw-r--r--packages/frontend/src/pages/admin/security.vue4
-rw-r--r--packages/frontend/src/pages/admin/server-rules.vue2
-rw-r--r--packages/frontend/src/pages/admin/settings.vue2
-rw-r--r--packages/frontend/src/pages/drop-and-fusion.game.vue120
-rw-r--r--packages/frontend/src/pages/drop-and-fusion.vue56
-rw-r--r--packages/frontend/src/pages/emoji-edit-dialog.vue2
-rw-r--r--packages/frontend/src/pages/instance-info.vue12
-rw-r--r--packages/frontend/src/pages/notifications.vue4
-rw-r--r--packages/frontend/src/pages/reversi/game.board.vue19
-rw-r--r--packages/frontend/src/pages/settings/drive.vue2
-rw-r--r--packages/frontend/src/pages/settings/emoji-picker.vue2
-rw-r--r--packages/frontend/src/pages/settings/notifications.notification-config.vue1
-rw-r--r--packages/frontend/src/pages/settings/notifications.vue13
-rw-r--r--packages/frontend/src/pages/settings/preferences-backups.vue2
28 files changed, 153 insertions, 149 deletions
diff --git a/packages/frontend/src/pages/admin/RolesEditorFormula.vue b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
index f4a8f44955..2f5b4c47d8 100644
--- a/packages/frontend/src/pages/admin/RolesEditorFormula.vue
+++ b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
@@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSelect v-model="type" :class="$style.typeSelect">
<option value="isLocal">{{ i18n.ts._role._condition.isLocal }}</option>
<option value="isRemote">{{ i18n.ts._role._condition.isRemote }}</option>
+ <option value="roleAssignedTo">{{ i18n.ts._role._condition.roleAssignedTo }}</option>
<option value="createdLessThan">{{ i18n.ts._role._condition.createdLessThan }}</option>
<option value="createdMoreThan">{{ i18n.ts._role._condition.createdMoreThan }}</option>
<option value="followersLessThanOrEq">{{ i18n.ts._role._condition.followersLessThanOrEq }}</option>
@@ -51,6 +52,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInput v-else-if="['followersLessThanOrEq', 'followersMoreThanOrEq', 'followingLessThanOrEq', 'followingMoreThanOrEq', 'notesLessThanOrEq', 'notesMoreThanOrEq'].includes(type)" v-model="v.value" type="number">
</MkInput>
+
+ <MkSelect v-else-if="type === 'roleAssignedTo'" v-model="v.roleId">
+ <option v-for="role in roles.filter(r => r.target === 'manual')" :key="role.id" :value="role.id">{{ role.name }}</option>
+ </MkSelect>
</div>
</template>
@@ -62,6 +67,7 @@ import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
import { deepClone } from '@/scripts/clone.js';
+import { rolesCache } from '@/cache.js';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
@@ -77,6 +83,8 @@ const props = defineProps<{
const v = ref(deepClone(props.modelValue));
+const roles = await rolesCache.fetch();
+
watch(() => props.modelValue, () => {
if (JSON.stringify(props.modelValue) === JSON.stringify(v.value)) return;
v.value = deepClone(props.modelValue);
@@ -92,6 +100,7 @@ const type = computed({
if (t === 'and') v.value.values = [];
if (t === 'or') v.value.values = [];
if (t === 'not') v.value.value = { id: uuid(), type: 'isRemote' };
+ if (t === 'roleAssignedTo') v.value.roleId = '';
if (t === 'createdLessThan') v.value.sec = 86400;
if (t === 'createdMoreThan') v.value.sec = 86400;
if (t === 'followersLessThanOrEq') v.value.value = 10;
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index e5e04fdeb8..73c5e1919f 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -142,7 +142,7 @@ function save() {
turnstileSiteKey: turnstileSiteKey.value,
turnstileSecretKey: turnstileSecretKey.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
</script>
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index 2b559f92c9..fe1b7c561d 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -169,7 +169,7 @@ function save() {
feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value,
manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)),
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index 839b9bee16..4a858887f3 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -124,7 +124,7 @@ function save() {
smtpUser: smtpUser.value,
smtpPass: smtpPass.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue
index ba3eb05e72..e0b82eb02e 100644
--- a/packages/frontend/src/pages/admin/external-services.vue
+++ b/packages/frontend/src/pages/admin/external-services.vue
@@ -61,7 +61,7 @@ function save() {
deeplAuthKey: deeplAuthKey.value,
deeplIsPro: deeplIsPro.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue
index 5167b2e6b2..6b14bd42c2 100644
--- a/packages/frontend/src/pages/admin/instance-block.vue
+++ b/packages/frontend/src/pages/admin/instance-block.vue
@@ -50,7 +50,7 @@ function save() {
silencedHosts: silencedHosts.value.split('\n') || [],
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index d6cb1e39a7..9efb34ac9a 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -110,7 +110,7 @@ function save() {
hiddenTags: hiddenTags.value.split('\n'),
preservedUsernames: preservedUsernames.value.split('\n'),
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index 21d68331cb..e33c882721 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -110,6 +110,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
</div>
</template>
+ <template v-else-if="log.type === 'updateRemoteInstanceNote'">
+ <div>{{ i18n.ts.user }}: {{ log.info.userId }}</div>
+ <div :class="$style.diff">
+ <CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
+ </div>
+ </template>
<details>
<summary>raw</summary>
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
index 5e251b8a6f..8590ee1651 100644
--- a/packages/frontend/src/pages/admin/modlog.vue
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -54,8 +54,6 @@ const pagination = {
})),
};
-console.log(Misskey);
-
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue
index 4ff5ab09ca..5fddb715cd 100644
--- a/packages/frontend/src/pages/admin/object-storage.vue
+++ b/packages/frontend/src/pages/admin/object-storage.vue
@@ -143,7 +143,7 @@ function save() {
objectStorageSetPublicRead: objectStorageSetPublicRead.value,
objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue
index 651f0ef936..345cf333b5 100644
--- a/packages/frontend/src/pages/admin/other-settings.vue
+++ b/packages/frontend/src/pages/admin/other-settings.vue
@@ -73,7 +73,7 @@ function save() {
enableChartsForRemoteUser: enableChartsForRemoteUser.value,
enableChartsForFederatedInstances: enableChartsForFederatedInstances.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue
index 02b506d13d..81db9f1da9 100644
--- a/packages/frontend/src/pages/admin/proxy-account.vue
+++ b/packages/frontend/src/pages/admin/proxy-account.vue
@@ -56,7 +56,7 @@ function save() {
os.apiWithDialog('admin/update-meta', {
proxyAccountId: proxyAccountId.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index ad9df35dbf..eb8a59b34f 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -160,6 +160,25 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])">
+ <template #label>{{ i18n.ts._role._options.mentionMax }}</template>
+ <template #suffix>
+ <span v-if="role.policies.mentionLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+ <span v-else>{{ role.policies.mentionLimit.value }}</span>
+ <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mentionLimit)"></i></span>
+ </template>
+ <div class="_gaps">
+ <MkSwitch v-model="role.policies.mentionLimit.useDefault" :readonly="readonly">
+ <template #label>{{ i18n.ts._role.useBaseValue }}</template>
+ </MkSwitch>
+ <MkInput v-model="role.policies.mentionLimit.value" :disabled="role.policies.mentionLimit.useDefault" type="number" :readonly="readonly">
+ </MkInput>
+ <MkRange v-model="role.policies.mentionLimit.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>
+
<MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])">
<template #label>{{ i18n.ts._role._options.canInvite }}</template>
<template #suffix>
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 496cb09664..9753d9f6cb 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -48,6 +48,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
</MkFolder>
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])">
+ <template #label>{{ i18n.ts._role._options.mentionMax }}</template>
+ <template #suffix>{{ policies.mentionLimit }}</template>
+ <MkInput v-model="policies.mentionLimit" type="number">
+ </MkInput>
+ </MkFolder>
+
<MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])">
<template #label>{{ i18n.ts._role._options.canInvite }}</template>
<template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template>
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index cadcf5a8cc..c4745978df 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -196,7 +196,7 @@ async function init() {
enableTruemailApi.value = meta.enableTruemailApi;
truemailInstance.value = meta.truemailInstance;
truemailAuthKey.value = meta.truemailAuthKey;
- bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || "";
+ bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || '';
}
function save() {
@@ -221,7 +221,7 @@ function save() {
truemailAuthKey: truemailAuthKey.value,
bannedEmailDomains: bannedEmailDomains.value.split('\n'),
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue
index 87318bccce..ff9b8d6299 100644
--- a/packages/frontend/src/pages/admin/server-rules.vue
+++ b/packages/frontend/src/pages/admin/server-rules.vue
@@ -58,7 +58,7 @@ const save = async () => {
await os.apiWithDialog('admin/update-meta', {
serverRules: serverRules.value,
});
- fetchInstance();
+ fetchInstance(true);
};
const remove = (index: number): void => {
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index c505d70aa9..9a198ee8a3 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -243,7 +243,7 @@ async function save(): void {
notesPerOneAd: notesPerOneAd.value,
});
- fetchInstance();
+ fetchInstance(true);
}
const headerTabs = computed(() => []);
diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue
index d9881cebbf..eba5b92154 100644
--- a/packages/frontend/src/pages/drop-and-fusion.game.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.game.vue
@@ -7,9 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer :contentMax="800">
<div :class="$style.root">
<div v-if="!gameLoaded" :class="$style.loadingScreen">
- <div>
- Loading...
- </div>
+ <div>{{ i18n.ts.loading }}<MkEllipsis/></div>
</div>
<!-- ↓に対してTransitionコンポーネントを使うと何故かkeyを指定していてもキャッシュが効かず様々なコンポーネントが都度再評価されてパフォーマンスが低下する -->
<div v-show="gameLoaded" class="_gaps_s">
@@ -32,18 +30,18 @@ SPDX-License-Identifier: AGPL-3.0-only
</Transition>
<div :class="$style.header">
- <div :class="[$style.frame, $style.headerTitle]">
- <div :class="$style.frameInner">
- <b>BUBBLE GAME</b>
- <div>- {{ gameMode }} -</div>
+ <div class="_woodenFrame" :class="[$style.headerTitle]">
+ <div class="_woodenFrameInner">
+ <b>{{ i18n.ts.bubbleGame }}</b>
+ <div>- {{ gameMode.toUpperCase() }} -</div>
</div>
</div>
- <div :class="[$style.frame, $style.frameH]">
- <div :class="$style.frameInner">
- <MkButton inline small @click="hold">HOLD</MkButton>
+ <div class="_woodenFrame _woodenFrameH">
+ <div class="_woodenFrameInner">
+ <MkButton inline small @click="hold">{{ i18n.ts._bubbleGame.hold }}</MkButton>
<img v-if="holdingStock" :src="getTextureImageUrl(holdingStock.mono)" style="width: 32px; margin-left: 8px; vertical-align: bottom;"/>
</div>
- <div :class="[$style.frameInner, $style.stock]" style="text-align: center;">
+ <div class="_woodenFrameInner" :class="$style.stock" style="text-align: center;">
<TransitionGroup
:enterActiveClass="$style.transition_stock_enterActive"
:leaveActiveClass="$style.transition_stock_leaveActive"
@@ -90,58 +88,74 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="isGameOver && !replaying" :class="$style.gameOverLabel">
<div class="_gaps_s">
<img src="/client-assets/drop-and-fusion/gameover.png" style="width: 200px; max-width: 100%; display: block; margin: auto; margin-bottom: -5px;"/>
- <div>SCORE: <MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</div>
- <div>MAX CHAIN: <MkNumber :value="maxCombo"/></div>
- <div v-if="gameMode === 'yen'">TOTAL EARNINGS: <b><MkNumber :value="yenTotal ?? score"/>円</b></div>
- <div v-if="gameMode === 'sweets'"><b>おにぎり<MkNumber :value="score / 130"/>個分</b></div>
+ <div>{{ i18n.ts._bubbleGame._score.score }}: <MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</div>
+ <div>{{ i18n.ts._bubbleGame._score.maxChain }}: <MkNumber :value="maxCombo"/></div>
+ <div v-if="gameMode === 'yen'">
+ {{ i18n.ts._bubbleGame._score.scoreYen }}:
+ <I18n :src="i18n.ts._bubbleGame._score.yen" tag="b">
+ <template #yen><MkNumber :value="yenTotal ?? score"/></template>
+ </I18n>
+ </div>
+ <I18n v-if="gameMode === 'sweets'" :src="i18n.ts._bubbleGame._score.scoreSweets" tag="div">
+ <template #onigiriQtyWithUnit>
+ <I18n :src="i18n.ts._bubbleGame._score.estimatedQty" tag="b">
+ <template #qty><MkNumber :value="score / 130"/></template>
+ </I18n>
+ </template>
+ </I18n>
</div>
</div>
<div v-if="replaying" :class="$style.replayIndicator"><span :class="$style.replayIndicatorText"><i class="ti ti-player-play"></i> {{ i18n.ts.replaying }}</span></div>
</div>
- <div v-if="replaying" :class="$style.frame">
- <div :class="$style.frameInner">
+ <div v-if="replaying" class="_woodenFrame">
+ <div class="_woodenFrameInner">
<div style="background: #0004;">
<div style="height: 10px; background: var(--accent); will-change: width;" :style="{ width: `${(currentFrame / endedAtFrame) * 100}%` }"></div>
</div>
</div>
- <div :class="$style.frameInner">
+ <div class="_woodenFrameInner">
<div class="_buttonsCenter">
- <MkButton @click="endReplay"><i class="ti ti-player-stop"></i> END</MkButton>
+ <MkButton @click="endReplay"><i class="ti ti-player-stop"></i> {{ i18n.ts.endReplay }}</MkButton>
<MkButton :primary="replayPlaybackRate === 4" @click="replayPlaybackRate = replayPlaybackRate === 4 ? 1 : 4"><i class="ti ti-player-track-next"></i> x4</MkButton>
<MkButton :primary="replayPlaybackRate === 16" @click="replayPlaybackRate = replayPlaybackRate === 16 ? 1 : 16"><i class="ti ti-player-track-next"></i> x16</MkButton>
</div>
</div>
</div>
- <div v-if="isGameOver" :class="$style.frame">
- <div :class="$style.frameInner">
+ <div v-if="isGameOver" class="_woodenFrame">
+ <div class="_woodenFrameInner">
<div class="_buttonsCenter">
<MkButton primary rounded @click="backToTitle">{{ i18n.ts.backToTitle }}</MkButton>
<MkButton primary rounded @click="replay">{{ i18n.ts.showReplay }}</MkButton>
<MkButton primary rounded @click="share">{{ i18n.ts.share }}</MkButton>
- <MkButton rounded @click="exportLog">Copy replay data</MkButton>
+ <MkButton rounded @click="exportLog">{{ i18n.ts.copyReplayData }}</MkButton>
</div>
</div>
</div>
<div style="display: flex;">
- <div :class="$style.frame" style="flex: 1; margin-right: 10px;">
- <div :class="$style.frameInner">
- <div>SCORE: <b><MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</b></div>
- <div>HIGH SCORE: <b v-if="highScore"><MkNumber :value="highScore"/>{{ getScoreUnit(gameMode) }}</b><b v-else>-</b></div>
- <div v-if="gameMode === 'yen'">TOTAL EARNINGS: <b v-if="yenTotal"><MkNumber :value="yenTotal"/>円</b><b v-else>-</b></div>
+ <div class="_woodenFrame" style="flex: 1; margin-right: 10px;">
+ <div class="_woodenFrameInner">
+ <div>{{ i18n.ts._bubbleGame._score.score }}: <MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</div>
+ <div>{{ i18n.ts._bubbleGame._score.highScore }}: <b v-if="highScore"><MkNumber :value="highScore"/>{{ getScoreUnit(gameMode) }}</b><b v-else>-</b></div>
+ <div v-if="gameMode === 'yen'">
+ {{ i18n.ts._bubbleGame._score.scoreYen }}:
+ <I18n :src="i18n.ts._bubbleGame._score.yen" tag="b">
+ <template #yen><MkNumber :value="yenTotal ?? score"/></template>
+ </I18n>
+ </div>
</div>
</div>
- <div :class="[$style.frame]" style="margin-left: auto;">
- <div :class="$style.frameInner" style="text-align: center;">
+ <div class="_woodenFrame" style="margin-left: auto;">
+ <div class="_woodenFrameInner" style="text-align: center;">
<div @click="showConfig = !showConfig"><i class="ti ti-settings"></i></div>
</div>
</div>
</div>
- <div v-if="showConfig" :class="$style.frame">
- <div :class="$style.frameInner">
+ <div v-if="showConfig" class="_woodenFrame">
+ <div class="_woodenFrameInner">
<div class="_gaps">
<MkRange v-model="bgmVolume" :min="0" :max="1" :step="0.01" :textConverter="(v) => `${Math.floor(v * 100)}%`" :continuousUpdate="true" @dragEnded="(v) => updateSettings('bgmVolume', v)">
<template #label>BGM {{ i18n.ts.volume }}</template>
@@ -153,8 +167,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
- <div :class="$style.frame">
- <div :class="$style.frameInner">
+ <div class="_woodenFrame">
+ <div class="_woodenFrameInner">
<div>FUSION RECIPE</div>
<div>
<div v-for="(mono, i) in game.monoDefinitions.sort((a, b) => a.level - b.level)" :key="mono.id" style="display: inline-block;">
@@ -165,10 +179,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
- <div :class="$style.frame">
- <div :class="$style.frameInner">
- <MkButton v-if="!isGameOver && !replaying" full danger @click="surrender">Surrender</MkButton>
- <MkButton v-else full @click="restart">Retry</MkButton>
+ <div class="_woodenFrame">
+ <div class="_woodenFrameInner">
+ <MkButton v-if="!isGameOver && !replaying" full danger @click="surrender">{{ i18n.ts.surrender }}</MkButton>
+ <MkButton v-else full @click="restart">{{ i18n.ts.gameRetry }}</MkButton>
</div>
</div>
</div>
@@ -1313,38 +1327,6 @@ definePageMetadata(() => ({
max-width: 100%;
}
-.frame {
- padding: 7px;
- background: #8C4F26;
- box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c;
- border-radius: 10px;
-}
-
-.frameH {
- display: flex;
- gap: 6px;
-}
-
-.frameInner {
- padding: 8px;
- margin-top: 8px;
- background: #F1E8DC;
- box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410;
- border-radius: 6px;
- color: #693410;
-
- &:first-child {
- margin-top: 0;
- }
-}
-
-.frameDivider {
- height: 0;
- border: none;
- border-top: 1px solid #693410;
- border-bottom: 1px solid #ce8a5c;
-}
-
.header {
position: relative;
z-index: 10;
diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue
index 1b11457988..54352c9b0d 100644
--- a/packages/frontend/src/pages/drop-and-fusion.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.vue
@@ -15,13 +15,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer v-if="!gameStarted" :contentMax="800">
<div :class="$style.root">
<div class="_gaps">
- <div :class="$style.frame" style="text-align: center;">
- <div :class="$style.frameInner">
+ <div class="_woodenFrame" style="text-align: center;">
+ <div class="_woodenFrameInner">
<img src="/client-assets/drop-and-fusion/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
</div>
</div>
- <div :class="$style.frame" style="text-align: center;">
- <div :class="$style.frameInner">
+ <div class="_woodenFrame" style="text-align: center;">
+ <div class="_woodenFrameInner">
<div class="_gaps" style="padding: 16px;">
<MkSelect v-model="gameMode">
<option value="normal">NORMAL</option>
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton>
</div>
</div>
- <div :class="$style.frameInner">
+ <div class="_woodenFrameInner">
<div class="_gaps" style="padding: 16px;">
<div style="font-size: 90%;"><i class="ti ti-music"></i> {{ i18n.ts.soundWillBePlayed }}</div>
<MkSwitch v-model="mute">
@@ -42,10 +42,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
- <div :class="$style.frame">
- <div :class="$style.frameInner">
+ <div class="_woodenFrame">
+ <div class="_woodenFrameInner">
<div class="_gaps_s" style="padding: 16px;">
- <div><b>{{ i18n.tsx.lastNDays({ n: 7 }) }} {{ i18n.ts.ranking }}</b> ({{ gameMode }})</div>
+ <div><b>{{ i18n.tsx.lastNDays({ n: 7 }) }} {{ i18n.ts.ranking }}</b> ({{ gameMode.toUpperCase() }})</div>
<div v-if="ranking" class="_gaps_s">
<div v-for="r in ranking" :key="r.id" :class="$style.rankingRecord">
<MkAvatar :link="true" style="width: 24px; height: 24px; margin-right: 4px;" :user="r.user"/>
@@ -57,8 +57,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
- <div :class="$style.frame">
- <div :class="$style.frameInner" style="padding: 16px;">
+ <div class="_woodenFrame">
+ <div class="_woodenFrameInner" style="padding: 16px;">
<div style="font-weight: bold;">{{ i18n.ts._bubbleGame.howToPlay }}</div>
<ol>
<li>{{ i18n.ts._bubbleGame._howToPlay.section1 }}</li>
@@ -67,8 +67,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</ol>
</div>
</div>
- <div :class="$style.frame">
- <div :class="$style.frameInner">
+ <div class="_woodenFrame">
+ <div class="_woodenFrameInner">
<div class="_gaps_s" style="padding: 16px;">
<div><b>Credit</b></div>
<div>
@@ -149,38 +149,6 @@ definePageMetadata(() => ({
}
}
-.frame {
- padding: 7px;
- background: #8C4F26;
- box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c;
- border-radius: 10px;
-}
-
-.frameH {
- display: flex;
- gap: 6px;
-}
-
-.frameInner {
- padding: 8px;
- margin-top: 8px;
- background: #F1E8DC;
- box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410;
- border-radius: 6px;
- color: #693410;
-
- &:first-child {
- margin-top: 0;
- }
-}
-
-.frameDivider {
- height: 0;
- border: none;
- border-top: 1px solid #693410;
- border-bottom: 1px solid #ce8a5c;
-}
-
.rankingRecord {
display: flex;
line-height: 24px;
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index 12e9416f72..16769ef360 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -135,7 +135,7 @@ async function addRole() {
const { canceled, result: role } = await os.select({
items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })),
});
- if (canceled) return;
+ if (canceled || role == null) return;
rolesThatCanBeUsedThisEmojiAsReaction.value.push(role);
}
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 2f1557182a..cb7fe2866c 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -39,6 +39,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
+ <MkTextarea v-model="moderationNote" manualSave>
+ <template #label>{{ i18n.ts.moderationNote }}</template>
+ </MkTextarea>
</div>
</FormSection>
@@ -119,7 +122,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { ref, computed } from 'vue';
+import { ref, computed, watch } from 'vue';
import * as Misskey from 'misskey-js';
import MkChart from '@/components/MkChart.vue';
import MkObjectView from '@/components/MkObjectView.vue';
@@ -141,6 +144,7 @@ import MkPagination from '@/components/MkPagination.vue';
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
import { dateString } from '@/filters/date.js';
+import MkTextarea from '@/components/MkTextarea.vue';
const props = defineProps<{
host: string;
@@ -155,6 +159,7 @@ const suspended = ref(false);
const isBlocked = ref(false);
const isSilenced = ref(false);
const faviconUrl = ref<string | null>(null);
+const moderationNote = ref('');
const usersPagination = {
endpoint: iAmModerator ? 'admin/show-users' : 'users' as const,
@@ -167,6 +172,10 @@ const usersPagination = {
offsetMode: true,
};
+watch(moderationNote, async () => {
+ await misskeyApi('admin/federation/update-instance', { host: instance.value.host, moderationNote: moderationNote.value });
+});
+
async function fetch(): Promise<void> {
if (iAmAdmin) {
meta.value = await misskeyApi('admin/meta');
@@ -178,6 +187,7 @@ async function fetch(): Promise<void> {
isBlocked.value = instance.value?.isBlocked ?? false;
isSilenced.value = instance.value?.isSilenced ?? false;
faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview');
+ moderationNote.value = instance.value?.moderationNote;
}
async function toggleBlock(): Promise<void> {
diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue
index 7db6fa5395..28f5838296 100644
--- a/packages/frontend/src/pages/notifications.vue
+++ b/packages/frontend/src/pages/notifications.vue
@@ -52,7 +52,7 @@ const directNotesPagination = {
function setFilter(ev) {
const typeItems = notificationTypes.map(t => ({
text: i18n.ts._notification._types[t],
- active: includeTypes.value && includeTypes.value.includes(t),
+ active: (includeTypes.value && includeTypes.value.includes(t)) ?? false,
action: () => {
includeTypes.value = [t];
},
@@ -63,7 +63,7 @@ function setFilter(ev) {
action: () => {
includeTypes.value = null;
},
- }, { type: 'divider' }, ...typeItems] : typeItems;
+ }, { type: 'divider' as const }, ...typeItems] : typeItems;
os.popupMenu(items, ev.currentTarget ?? ev.target);
}
diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue
index cf7cec6b5e..5259dfa29a 100644
--- a/packages/frontend/src/pages/reversi/game.board.vue
+++ b/packages/frontend/src/pages/reversi/game.board.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
- <div :class="$style.board">
+ <div class="_woodenFrame">
<div :class="$style.boardInner">
<div v-if="showBoardLabels" :class="$style.labelsX">
<span v-for="i in game.map[0].length" :key="i" :class="$style.labelsXLabel">{{ String.fromCharCode(64 + i) }}</span>
@@ -124,8 +124,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder>
<template #label>{{ i18n.ts.options }}</template>
<div class="_gaps_s" style="text-align: left;">
- <MkSwitch v-model="showBoardLabels">Show labels</MkSwitch>
- <MkSwitch v-model="useAvatarAsStone">useAvatarAsStone</MkSwitch>
+ <MkSwitch v-model="showBoardLabels">{{ i18n.ts._reversi.showBoardLabels }}</MkSwitch>
+ <MkSwitch v-model="useAvatarAsStone">{{ i18n.ts._reversi.useAvatarAsStone }}</MkSwitch>
</div>
</MkFolder>
@@ -248,7 +248,7 @@ if (game.value.isStarted && !game.value.isEnded) {
crc32: crc32.toString(),
}).then((res) => {
if (res.desynced) {
- console.log('resynced');
+ if (_DEV_) console.log('resynced');
restoreGame(res.game!);
}
});
@@ -500,17 +500,6 @@ $gap: 4px;
text-align: center;
}
-.board {
- width: 100%;
- box-sizing: border-box;
- margin: 0 auto;
-
- padding: 7px;
- background: #8C4F26;
- box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c;
- border-radius: 12px;
-}
-
.boardInner {
padding: 32px;
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index cd38f9850f..1919f80864 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -113,7 +113,7 @@ if (defaultStore.state.uploadFolder) {
function chooseUploadFolder() {
os.selectDriveFolder(false).then(async folder => {
- defaultStore.set('uploadFolder', folder ? folder.id : null);
+ defaultStore.set('uploadFolder', folder[0] ? folder[0].id : null);
os.success();
if (defaultStore.state.uploadFolder) {
uploadFolder.value = await misskeyApi('drive/folders/show', {
diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue
index 79969427ec..ce296ec183 100644
--- a/packages/frontend/src/pages/settings/emoji-picker.vue
+++ b/packages/frontend/src/pages/settings/emoji-picker.vue
@@ -213,7 +213,7 @@ async function pickEmoji(itemsRef: Ref<string[]>, ev: MouseEvent) {
os.pickEmoji(getHTMLElement(ev), {
showPinned: false,
}).then(it => {
- const emoji = it as string;
+ const emoji = it;
if (!itemsRef.value.includes(emoji)) {
itemsRef.value.push(emoji);
}
diff --git a/packages/frontend/src/pages/settings/notifications.notification-config.vue b/packages/frontend/src/pages/settings/notifications.notification-config.vue
index d6aac63674..a36f036303 100644
--- a/packages/frontend/src/pages/settings/notifications.notification-config.vue
+++ b/packages/frontend/src/pages/settings/notifications.notification-config.vue
@@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<option value="following">{{ i18n.ts.following }}</option>
<option value="follower">{{ i18n.ts.followers }}</option>
<option value="mutualFollow">{{ i18n.ts.mutualFollow }}</option>
+ <option value="followingOrFollower">{{ i18n.ts.followingOrFollower }}</option>
<option value="list">{{ i18n.ts.userList }}</option>
<option value="never">{{ i18n.ts.none }}</option>
</MkSelect>
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index febcfa32ed..70db6a5109 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
$i.notificationRecieveConfig[type]?.type === 'following' ? i18n.ts.following :
$i.notificationRecieveConfig[type]?.type === 'follower' ? i18n.ts.followers :
$i.notificationRecieveConfig[type]?.type === 'mutualFollow' ? i18n.ts.mutualFollow :
+ $i.notificationRecieveConfig[type]?.type === 'followingOrFollower' ? i18n.ts.followingOrFollower :
$i.notificationRecieveConfig[type]?.type === 'list' ? i18n.ts.userList :
i18n.ts.all
}}
@@ -34,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<FormSection>
<div class="_gaps_m">
<FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
+ <FormLink @click="flushNotification">{{ i18n.ts._notification.flushNotification }}</FormLink>
</div>
</FormSection>
<FormSection>
@@ -113,6 +115,17 @@ function testNotification(): void {
misskeyApi('notifications/test-notification');
}
+async function flushNotification() {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.ts.resetAreYouSure,
+ });
+
+ if (canceled) return;
+
+ os.apiWithDialog('notifications/flush');
+}
+
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index 676159d1b5..942de19d82 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -203,6 +203,7 @@ async function saveNew(): Promise<void> {
const { canceled, result: name } = await os.inputText({
title: ts._preferencesBackups.inputName,
+ default: '',
});
if (canceled) return;
@@ -371,6 +372,7 @@ async function rename(id: string): Promise<void> {
const { canceled: cancel1, result: name } = await os.inputText({
title: ts._preferencesBackups.inputName,
+ default: '',
});
if (cancel1 || profiles.value[id].name === name) return;