Merge pull request MisskeyIO#265 from MisskeyIO/merge-upstream
Merge misskey-dev/develop
This commit is contained in:
commit
77e676e121
|
@ -5,7 +5,7 @@
|
||||||
"workspaceFolder": "/workspace",
|
"workspaceFolder": "/workspace",
|
||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers-contrib/features/pnpm:2": {
|
"ghcr.io/devcontainers-contrib/features/pnpm:2": {
|
||||||
"version": "8.9.2"
|
"version": "latest"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": "20"
|
"version": "20"
|
||||||
|
|
|
@ -17,10 +17,12 @@
|
||||||
### General
|
### General
|
||||||
- Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
|
- Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
|
||||||
- Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
|
- Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
|
||||||
|
- Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加
|
||||||
- Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
|
- Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- Enhance: 絵文字のオートコンプリート機能強化 #12364
|
- Enhance: 絵文字のオートコンプリート機能強化 #12364
|
||||||
|
- Enhance: ユーザーのRawデータを表示するページが復活
|
||||||
- fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
|
- fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
|
||||||
- Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
|
- Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
|
||||||
- Fix: コードエディタが正しく表示されない問題を修正
|
- Fix: コードエディタが正しく表示されない問題を修正
|
||||||
|
@ -32,6 +34,7 @@
|
||||||
- Fix: ロールタイムラインが保存されない問題を修正
|
- Fix: ロールタイムラインが保存されない問題を修正
|
||||||
- Fix: api.jsonの生成ロジックを改善 #12402
|
- Fix: api.jsonの生成ロジックを改善 #12402
|
||||||
- Fix: 招待コードが使い回せる問題を修正
|
- Fix: 招待コードが使い回せる問題を修正
|
||||||
|
- Fix: 特定の条件下でチャンネルやユーザーのノート一覧に最新のノートが表示されなくなる問題を修正
|
||||||
|
|
||||||
## 2023.11.1
|
## 2023.11.1
|
||||||
|
|
||||||
|
|
3
locales/index.d.ts
vendored
3
locales/index.d.ts
vendored
|
@ -1042,6 +1042,7 @@ export interface Locale {
|
||||||
"enableChartsForFederatedInstances": string;
|
"enableChartsForFederatedInstances": string;
|
||||||
"showClipButtonInNoteFooter": string;
|
"showClipButtonInNoteFooter": string;
|
||||||
"reactionsDisplaySize": string;
|
"reactionsDisplaySize": string;
|
||||||
|
"limitWidthOfReaction": string;
|
||||||
"noteIdOrUrl": string;
|
"noteIdOrUrl": string;
|
||||||
"video": string;
|
"video": string;
|
||||||
"videos": string;
|
"videos": string;
|
||||||
|
@ -1648,7 +1649,9 @@ export interface Locale {
|
||||||
"assignTarget": string;
|
"assignTarget": string;
|
||||||
"descriptionOfAssignTarget": string;
|
"descriptionOfAssignTarget": string;
|
||||||
"manual": string;
|
"manual": string;
|
||||||
|
"manualRoles": string;
|
||||||
"conditional": string;
|
"conditional": string;
|
||||||
|
"conditionalRoles": string;
|
||||||
"condition": string;
|
"condition": string;
|
||||||
"isConditionalRole": string;
|
"isConditionalRole": string;
|
||||||
"isPublic": string;
|
"isPublic": string;
|
||||||
|
|
|
@ -1039,6 +1039,7 @@ enableChartsForRemoteUser: "リモートユーザーのチャートを生成"
|
||||||
enableChartsForFederatedInstances: "リモートサーバーのチャートを生成"
|
enableChartsForFederatedInstances: "リモートサーバーのチャートを生成"
|
||||||
showClipButtonInNoteFooter: "ノートのアクションにクリップを追加"
|
showClipButtonInNoteFooter: "ノートのアクションにクリップを追加"
|
||||||
reactionsDisplaySize: "リアクションの表示サイズ"
|
reactionsDisplaySize: "リアクションの表示サイズ"
|
||||||
|
limitWidthOfReaction: "リアクションの最大横幅を制限し、縮小して表示する"
|
||||||
noteIdOrUrl: "ノートIDまたはURL"
|
noteIdOrUrl: "ノートIDまたはURL"
|
||||||
video: "動画"
|
video: "動画"
|
||||||
videos: "動画"
|
videos: "動画"
|
||||||
|
@ -1558,7 +1559,9 @@ _role:
|
||||||
assignTarget: "アサイン"
|
assignTarget: "アサイン"
|
||||||
descriptionOfAssignTarget: "<b>マニュアル</b>は誰がこのロールに含まれるかを手動で管理します。\n<b>コンディショナル</b>は条件を設定し、それに合致するユーザーが自動で含まれるようになります。"
|
descriptionOfAssignTarget: "<b>マニュアル</b>は誰がこのロールに含まれるかを手動で管理します。\n<b>コンディショナル</b>は条件を設定し、それに合致するユーザーが自動で含まれるようになります。"
|
||||||
manual: "マニュアル"
|
manual: "マニュアル"
|
||||||
|
manualRoles: "マニュアルロール"
|
||||||
conditional: "コンディショナル"
|
conditional: "コンディショナル"
|
||||||
|
conditionalRoles: "コンディショナルロール"
|
||||||
condition: "条件"
|
condition: "条件"
|
||||||
isConditionalRole: "これはコンディショナルロールです。"
|
isConditionalRole: "これはコンディショナルロールです。"
|
||||||
isPublic: "公開ロール"
|
isPublic: "公開ロール"
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { IdService } from '@/core/IdService.js';
|
||||||
import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
|
import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||||
import { CacheService } from '@/core/CacheService.js';
|
import { CacheService } from '@/core/CacheService.js';
|
||||||
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -72,12 +73,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
private funoutTimelineService: FunoutTimelineService,
|
private funoutTimelineService: FunoutTimelineService,
|
||||||
private cacheService: CacheService,
|
private cacheService: CacheService,
|
||||||
private activeUsersChart: ActiveUsersChart,
|
private activeUsersChart: ActiveUsersChart,
|
||||||
|
private metaService: MetaService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
|
const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
|
||||||
const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null);
|
const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null);
|
||||||
const isRangeSpecified = untilId != null && sinceId != null;
|
const isRangeSpecified = untilId != null && sinceId != null;
|
||||||
|
|
||||||
|
const serverSettings = await this.metaService.fetch();
|
||||||
|
|
||||||
const channel = await this.channelsRepository.findOneBy({
|
const channel = await this.channelsRepository.findOneBy({
|
||||||
id: ps.channelId,
|
id: ps.channelId,
|
||||||
});
|
});
|
||||||
|
@ -88,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
if (me) this.activeUsersChart.read(me);
|
if (me) this.activeUsersChart.read(me);
|
||||||
|
|
||||||
if (isRangeSpecified || sinceId == null) {
|
if (serverSettings.enableFanoutTimeline && (isRangeSpecified || sinceId == null)) {
|
||||||
const [
|
const [
|
||||||
userIdsWhoMeMuting,
|
userIdsWhoMeMuting,
|
||||||
] = me ? await Promise.all([
|
] = me ? await Promise.all([
|
||||||
|
|
|
@ -263,7 +263,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
if (renote.channelId && renote.channelId !== ps.channelId) {
|
if (renote.channelId && renote.channelId !== ps.channelId) {
|
||||||
// チャンネルのノートに対しリノート要求がきたとき、チャンネル外へのリノート可否をチェック
|
// チャンネルのノートに対しリノート要求がきたとき、チャンネル外へのリノート可否をチェック
|
||||||
// リノートのユースケースのうち、チャンネル内→チャンネル外は少数だと考えられるため、JOINはせず必要な時に都度取得する
|
// リノートのユースケースのうち、チャンネル内→チャンネル外は少数だと考えられるため、JOINはせず必要な時に都度取得する
|
||||||
const renoteChannel = await this.channelsRepository.findOneById(renote.channelId);
|
const renoteChannel = await this.channelsRepository.findOneBy({ id: renote.channelId });
|
||||||
if (renoteChannel == null) {
|
if (renoteChannel == null) {
|
||||||
// リノートしたいノートが書き込まれているチャンネルが無い
|
// リノートしたいノートが書き込まれているチャンネルが無い
|
||||||
throw new ApiError(meta.errors.noSuchChannel);
|
throw new ApiError(meta.errors.noSuchChannel);
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { IdService } from '@/core/IdService.js';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||||
import { QueryService } from '@/core/QueryService.js';
|
import { QueryService } from '@/core/QueryService.js';
|
||||||
import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
|
import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
|
||||||
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -71,6 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
private cacheService: CacheService,
|
private cacheService: CacheService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private funoutTimelineService: FunoutTimelineService,
|
private funoutTimelineService: FunoutTimelineService,
|
||||||
|
private metaService: MetaService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
|
const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
|
||||||
|
@ -78,7 +80,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
const isRangeSpecified = untilId != null && sinceId != null;
|
const isRangeSpecified = untilId != null && sinceId != null;
|
||||||
const isSelf = me && (me.id === ps.userId);
|
const isSelf = me && (me.id === ps.userId);
|
||||||
|
|
||||||
if (isRangeSpecified || sinceId == null) {
|
const serverSettings = await this.metaService.fetch();
|
||||||
|
|
||||||
|
if (serverSettings.enableFanoutTimeline && (isRangeSpecified || sinceId == null)) {
|
||||||
const [
|
const [
|
||||||
userIdsWhoMeMuting,
|
userIdsWhoMeMuting,
|
||||||
] = me ? await Promise.all([
|
] = me ? await Promise.all([
|
||||||
|
|
|
@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<Mfm :text="report.comment"/>
|
<Mfm :text="report.comment"/>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div>{{ i18n.ts.reporter }}: <MkAcct :user="report.reporter"/></div>
|
<div>{{ i18n.ts.reporter }}: <MkA :to="`/admin/user/${report.reporter.id}`" class="_link">@{{ report.reporter.username }}</MkA></div>
|
||||||
<div v-if="report.assignee">
|
<div v-if="report.assignee">
|
||||||
{{ i18n.ts.moderator }}:
|
{{ i18n.ts.moderator }}:
|
||||||
<MkAcct :user="report.assignee"/>
|
<MkAcct :user="report.assignee"/>
|
||||||
|
|
|
@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]"
|
:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]"
|
||||||
@click="toggleReaction()"
|
@click="toggleReaction()"
|
||||||
>
|
>
|
||||||
<MkReactionIcon :class="$style.icon" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/>
|
<MkReactionIcon :class="defaultStore.state.limitWidthOfReaction ? $style.limitWidth : ''" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/>
|
||||||
<span :class="$style.count">{{ count }}</span>
|
<span :class="$style.count">{{ count }}</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
@ -188,7 +188,7 @@ if (!mock) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.limitWidth {
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -222,13 +222,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkButton primary rounded @click="create"><i class="ti ti-plus"></i> {{ i18n.ts._role.new }}</MkButton>
|
<MkButton primary rounded @click="create"><i class="ti ti-plus"></i> {{ i18n.ts._role.new }}</MkButton>
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<MkFoldableSection>
|
<MkFoldableSection>
|
||||||
<template #header>Manual roles</template>
|
<template #header>{{ i18n.ts._role.manualRoles }}</template>
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<MkRolePreview v-for="role in roles.filter(x => x.target === 'manual')" :key="role.id" :role="role" :forModeration="true"/>
|
<MkRolePreview v-for="role in roles.filter(x => x.target === 'manual')" :key="role.id" :role="role" :forModeration="true"/>
|
||||||
</div>
|
</div>
|
||||||
</MkFoldableSection>
|
</MkFoldableSection>
|
||||||
<MkFoldableSection>
|
<MkFoldableSection>
|
||||||
<template #header>Conditional roles</template>
|
<template #header>{{ i18n.ts._role.conditionalRoles }}</template>
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<MkRolePreview v-for="role in roles.filter(x => x.target === 'conditional')" :key="role.id" :role="role" :forModeration="true"/>
|
<MkRolePreview v-for="role in roles.filter(x => x.target === 'conditional')" :key="role.id" :role="role" :forModeration="true"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -56,6 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<option value="medium">{{ i18n.ts.medium }}</option>
|
<option value="medium">{{ i18n.ts.medium }}</option>
|
||||||
<option value="large">{{ i18n.ts.large }}</option>
|
<option value="large">{{ i18n.ts.large }}</option>
|
||||||
</MkRadios>
|
</MkRadios>
|
||||||
|
<MkSwitch v-model="limitWidthOfReaction">{{ i18n.ts.limitWidthOfReaction }}</MkSwitch>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MkSelect v-model="instanceTicker">
|
<MkSelect v-model="instanceTicker">
|
||||||
|
@ -226,6 +227,7 @@ const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serve
|
||||||
const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNoteActionsOnlyHover'));
|
const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNoteActionsOnlyHover'));
|
||||||
const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showClipButtonInNoteFooter'));
|
const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showClipButtonInNoteFooter'));
|
||||||
const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize'));
|
const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize'));
|
||||||
|
const limitWidthOfReaction = computed(defaultStore.makeGetterSetter('limitWidthOfReaction'));
|
||||||
const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes'));
|
const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes'));
|
||||||
const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v));
|
const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v));
|
||||||
const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal'));
|
const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal'));
|
||||||
|
@ -290,6 +292,7 @@ watch([
|
||||||
overridedDeviceKind,
|
overridedDeviceKind,
|
||||||
mediaListWithOneImageAppearance,
|
mediaListWithOneImageAppearance,
|
||||||
reactionsDisplaySize,
|
reactionsDisplaySize,
|
||||||
|
limitWidthOfReaction,
|
||||||
highlightSensitiveMedia,
|
highlightSensitiveMedia,
|
||||||
keepScreenOn,
|
keepScreenOn,
|
||||||
disableStreamingTimeline,
|
disableStreamingTimeline,
|
||||||
|
|
|
@ -18,6 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<XPages v-else-if="tab === 'pages'" :user="user"/>
|
<XPages v-else-if="tab === 'pages'" :user="user"/>
|
||||||
<XFlashs v-else-if="tab === 'flashs'" :user="user"/>
|
<XFlashs v-else-if="tab === 'flashs'" :user="user"/>
|
||||||
<XGallery v-else-if="tab === 'gallery'" :user="user"/>
|
<XGallery v-else-if="tab === 'gallery'" :user="user"/>
|
||||||
|
<XRaw v-else-if="tab === 'raw'" :user="user"/>
|
||||||
</div>
|
</div>
|
||||||
<MkError v-else-if="error" @retry="fetchUser()"/>
|
<MkError v-else-if="error" @retry="fetchUser()"/>
|
||||||
<MkLoading v-else/>
|
<MkLoading v-else/>
|
||||||
|
@ -44,6 +45,7 @@ const XLists = defineAsyncComponent(() => import('./lists.vue'));
|
||||||
const XPages = defineAsyncComponent(() => import('./pages.vue'));
|
const XPages = defineAsyncComponent(() => import('./pages.vue'));
|
||||||
const XFlashs = defineAsyncComponent(() => import('./flashs.vue'));
|
const XFlashs = defineAsyncComponent(() => import('./flashs.vue'));
|
||||||
const XGallery = defineAsyncComponent(() => import('./gallery.vue'));
|
const XGallery = defineAsyncComponent(() => import('./gallery.vue'));
|
||||||
|
const XRaw = defineAsyncComponent(() => import('./raw.vue'));
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
acct: string;
|
acct: string;
|
||||||
|
@ -112,6 +114,10 @@ const headerTabs = $computed(() => user ? [{
|
||||||
key: 'gallery',
|
key: 'gallery',
|
||||||
title: i18n.ts.gallery,
|
title: i18n.ts.gallery,
|
||||||
icon: 'ti ti-icons',
|
icon: 'ti ti-icons',
|
||||||
|
}, {
|
||||||
|
key: 'raw',
|
||||||
|
title: 'Raw',
|
||||||
|
icon: 'ti ti-code',
|
||||||
}] : []);
|
}] : []);
|
||||||
|
|
||||||
definePageMetadata(computed(() => user ? {
|
definePageMetadata(computed(() => user ? {
|
||||||
|
|
130
packages/frontend/src/pages/user/raw.vue
Normal file
130
packages/frontend/src/pages/user/raw.vue
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<MkSpacer :contentMax="600" :marginMin="16" :marginMax="32">
|
||||||
|
<div class="_gaps_m">
|
||||||
|
<div :class="$style.userMInfoRoot">
|
||||||
|
<MkAvatar :class="$style.userMInfoAvatar" :user="user" indicator link preview/>
|
||||||
|
<div :class="$style.userMInfoMetaRoot">
|
||||||
|
<span :class="$style.userMInfoMetaName"><MkUserName :class="$style.userMInfoMetaName" :user="user"/></span>
|
||||||
|
<span :class="$style.userMInfoMetaSub"><span class="acct _monospace">@{{ acct(user) }}</span></span>
|
||||||
|
<span :class="$style.userMInfoMetaState">
|
||||||
|
<span v-if="suspended" :class="$style.suspended">Suspended</span>
|
||||||
|
<span v-if="silenced" :class="$style.silenced">Silenced</span>
|
||||||
|
<span v-if="moderator" :class="$style.moderator">Moderator</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 1em;">
|
||||||
|
<MkKeyValue :copy="user.id" oneline>
|
||||||
|
<template #key>ID</template>
|
||||||
|
<template #value><span class="_monospace">{{ user.id }}</span></template>
|
||||||
|
</MkKeyValue>
|
||||||
|
<MkKeyValue oneline>
|
||||||
|
<template #key>{{ i18n.ts.createdAt }}</template>
|
||||||
|
<template #value><span class="_monospace"><MkTime :time="user.createdAt" :mode="'detail'"/></span></template>
|
||||||
|
</MkKeyValue>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormSection>
|
||||||
|
<template #label>Raw</template>
|
||||||
|
<MkObjectView tall :value="user"></MkObjectView>
|
||||||
|
</FormSection>
|
||||||
|
</div>
|
||||||
|
</MkSpacer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import { acct } from '@/filters/user.js';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||||
|
import FormSection from '@/components/form/section.vue';
|
||||||
|
import MkObjectView from '@/components/MkObjectView.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
user: Misskey.entities.User;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const moderator = computed(() => props.user.isModerator ?? false);
|
||||||
|
const silenced = computed(() => props.user.isSilenced ?? false);
|
||||||
|
const suspended = computed(() => props.user.isSuspended ?? false);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.userMInfoRoot {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userMInfoAvatar {
|
||||||
|
display: block;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userMInfoMetaRoot {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userMInfoMetaName {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userMInfoMetaSub {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 85%;
|
||||||
|
opacity: 0.7;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userMInfoMetaState {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 4px;
|
||||||
|
|
||||||
|
&:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .suspended,
|
||||||
|
> .silenced,
|
||||||
|
> .moderator {
|
||||||
|
display: inline-block;
|
||||||
|
border: solid 1px;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .suspended {
|
||||||
|
color: var(--error);
|
||||||
|
border-color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .silenced {
|
||||||
|
color: var(--warn);
|
||||||
|
border-color: var(--warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .moderator {
|
||||||
|
color: var(--success);
|
||||||
|
border-color: var(--success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -330,6 +330,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: 'medium' as 'small' | 'medium' | 'large',
|
default: 'medium' as 'small' | 'medium' | 'large',
|
||||||
},
|
},
|
||||||
|
limitWidthOfReaction: {
|
||||||
|
where: 'device',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
forceShowAds: {
|
forceShowAds: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: false,
|
default: false,
|
||||||
|
|
Loading…
Reference in a new issue