Merge pull request #23 from team-shahu/feat/muted-reaction
feat: リアクションした人一覧がブロック・ミュートを考慮するようにする設定
This commit is contained in:
parent
759109e7a1
commit
a313fc6366
@ -2824,3 +2824,6 @@ _contextMenu:
|
|||||||
app: "Application"
|
app: "Application"
|
||||||
appWithShift: "Application with shift key"
|
appWithShift: "Application with shift key"
|
||||||
native: "Native"
|
native: "Native"
|
||||||
|
_reactionChecksMuting:
|
||||||
|
title: "Check mutings when get reactions"
|
||||||
|
caption: "Check mutings when get reactions, but cache does not work and may increase traffic"
|
||||||
|
10
locales/index.d.ts
vendored
10
locales/index.d.ts
vendored
@ -10895,6 +10895,16 @@ export interface Locale extends ILocale {
|
|||||||
*/
|
*/
|
||||||
"native": string;
|
"native": string;
|
||||||
};
|
};
|
||||||
|
"_reactionChecksMuting": {
|
||||||
|
/**
|
||||||
|
* リアクションでミュートを考慮する
|
||||||
|
*/
|
||||||
|
"title": string;
|
||||||
|
/**
|
||||||
|
* リアクションがミュートを考慮しますが、キャッシュが効かず通信量が増えることがあります。
|
||||||
|
*/
|
||||||
|
"caption": string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
declare const locales: {
|
declare const locales: {
|
||||||
[lang: string]: Locale;
|
[lang: string]: Locale;
|
||||||
|
@ -2899,3 +2899,7 @@ _contextMenu:
|
|||||||
app: "アプリケーション"
|
app: "アプリケーション"
|
||||||
appWithShift: "Shiftキーでアプリケーション"
|
appWithShift: "Shiftキーでアプリケーション"
|
||||||
native: "ブラウザのUI"
|
native: "ブラウザのUI"
|
||||||
|
|
||||||
|
_reactionChecksMuting:
|
||||||
|
title: "リアクションでミュートを考慮する"
|
||||||
|
caption: "リアクションがミュートを考慮しますが、キャッシュが効かず通信量が増えることがあります。"
|
||||||
|
@ -4,13 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Brackets, type FindOptionsWhere } from 'typeorm';
|
|
||||||
import type { NoteReactionsRepository } from '@/models/_.js';
|
import type { NoteReactionsRepository } from '@/models/_.js';
|
||||||
import type { MiNoteReaction } from '@/models/NoteReaction.js';
|
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js';
|
import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { QueryService } from '@/core/QueryService.js';
|
import { QueryService } from '@/core/QueryService.js';
|
||||||
|
import { CacheService } from '@/core/CacheService.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['notes', 'reactions'],
|
tags: ['notes', 'reactions'],
|
||||||
@ -59,6 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
|
|
||||||
private noteReactionEntityService: NoteReactionEntityService,
|
private noteReactionEntityService: NoteReactionEntityService,
|
||||||
private queryService: QueryService,
|
private queryService: QueryService,
|
||||||
|
private cacheService: CacheService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'), ps.sinceId, ps.untilId)
|
const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'), ps.sinceId, ps.untilId)
|
||||||
@ -66,6 +66,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
.leftJoinAndSelect('reaction.user', 'user')
|
.leftJoinAndSelect('reaction.user', 'user')
|
||||||
.leftJoinAndSelect('reaction.note', 'note');
|
.leftJoinAndSelect('reaction.note', 'note');
|
||||||
|
|
||||||
|
if (me != null) {
|
||||||
|
const [userIdsWhoMeMuting, userIdsWhoBlockingMe] = await Promise.all([
|
||||||
|
this.cacheService.userMutingsCache.get(me.id),
|
||||||
|
this.cacheService.userBlockedCache.get(me.id),
|
||||||
|
]);
|
||||||
|
|
||||||
|
query.andWhere('reaction.userId NOT IN (:...userIds)', { userIds: Array.from(userIdsWhoMeMuting ?? []).concat(Array.from(userIdsWhoBlockingMe ?? [])) });
|
||||||
|
}
|
||||||
|
|
||||||
if (ps.type) {
|
if (ps.type) {
|
||||||
// ローカルリアクションはホスト名が . とされているが
|
// ローカルリアクションはホスト名が . とされているが
|
||||||
// DB 上ではそうではないので、必要に応じて変換
|
// DB 上ではそうではないので、必要に応じて変換
|
||||||
|
@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkAvatar :class="$style.avatar" :user="u"/>
|
<MkAvatar :class="$style.avatar" :user="u"/>
|
||||||
<MkUserName :user="u" :nowrap="true"/>
|
<MkUserName :user="u" :nowrap="true"/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="count <= 0" :class="$style.user"> {{ i18n.ts.noUsers }} </div>
|
||||||
<div v-if="count > 10" :class="$style.more">+{{ count - 10 }}</div>
|
<div v-if="count > 10" :class="$style.more">+{{ count - 10 }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -26,6 +27,7 @@ import { } from 'vue';
|
|||||||
import MkTooltip from './MkTooltip.vue';
|
import MkTooltip from './MkTooltip.vue';
|
||||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||||
import { getEmojiName } from '@/scripts/emojilist.js';
|
import { getEmojiName } from '@/scripts/emojilist.js';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
showing: boolean;
|
showing: boolean;
|
||||||
|
@ -36,6 +36,8 @@ import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.j
|
|||||||
import { customEmojisMap } from '@/custom-emojis.js';
|
import { customEmojisMap } from '@/custom-emojis.js';
|
||||||
import { getUnicodeEmoji } from '@/scripts/emojilist.js';
|
import { getUnicodeEmoji } from '@/scripts/emojilist.js';
|
||||||
|
|
||||||
|
const reactionChecksMuting = computed(defaultStore.makeGetterSetter('reactionChecksMuting'));
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
reaction: string;
|
reaction: string;
|
||||||
count: number;
|
count: number;
|
||||||
@ -146,7 +148,9 @@ onMounted(() => {
|
|||||||
|
|
||||||
if (!mock) {
|
if (!mock) {
|
||||||
useTooltip(buttonEl, async (showing) => {
|
useTooltip(buttonEl, async (showing) => {
|
||||||
const reactions = await misskeyApiGet('notes/reactions', {
|
const useGet = !reactionChecksMuting.value;
|
||||||
|
const apiCall = useGet ? misskeyApiGet : misskeyApi;
|
||||||
|
const reactions = await apiCall('notes/reactions', {
|
||||||
noteId: props.note.id,
|
noteId: props.note.id,
|
||||||
type: props.reaction,
|
type: props.reaction,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
@ -154,12 +158,13 @@ if (!mock) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const users = reactions.map(x => x.user);
|
const users = reactions.map(x => x.user);
|
||||||
|
const count = users.length;
|
||||||
|
|
||||||
const { dispose } = os.popup(XDetails, {
|
const { dispose } = os.popup(XDetails, {
|
||||||
showing,
|
showing,
|
||||||
reaction: props.reaction,
|
reaction: props.reaction,
|
||||||
users,
|
users,
|
||||||
count: props.count,
|
count,
|
||||||
targetElement: buttonEl.value,
|
targetElement: buttonEl.value,
|
||||||
}, {
|
}, {
|
||||||
closed: () => dispose(),
|
closed: () => dispose(),
|
||||||
|
@ -235,6 +235,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkSwitch v-model="enableHorizontalSwipe">{{ i18n.ts.enableHorizontalSwipe }}</MkSwitch>
|
<MkSwitch v-model="enableHorizontalSwipe">{{ i18n.ts.enableHorizontalSwipe }}</MkSwitch>
|
||||||
<MkSwitch v-model="alwaysConfirmFollow">{{ i18n.ts.alwaysConfirmFollow }}</MkSwitch>
|
<MkSwitch v-model="alwaysConfirmFollow">{{ i18n.ts.alwaysConfirmFollow }}</MkSwitch>
|
||||||
<MkSwitch v-model="confirmWhenRevealingSensitiveMedia">{{ i18n.ts.confirmWhenRevealingSensitiveMedia }}</MkSwitch>
|
<MkSwitch v-model="confirmWhenRevealingSensitiveMedia">{{ i18n.ts.confirmWhenRevealingSensitiveMedia }}</MkSwitch>
|
||||||
|
|
||||||
|
<MkSwitch v-model="reactionChecksMuting">
|
||||||
|
{{ i18n.ts._reactionChecksMuting.title }}<span class="_beta">{{ i18n.ts.originalFeature }}</span>
|
||||||
|
<template #caption>{{ i18n.ts._reactionChecksMuting.caption }}</template>
|
||||||
|
</MkSwitch>
|
||||||
</div>
|
</div>
|
||||||
<MkSelect v-model="serverDisconnectedBehavior">
|
<MkSelect v-model="serverDisconnectedBehavior">
|
||||||
<template #label>{{ i18n.ts.whenServerDisconnected }}</template>
|
<template #label>{{ i18n.ts.whenServerDisconnected }}</template>
|
||||||
@ -435,6 +440,8 @@ const useNativeUIForVideoAudioPlayer = computed(defaultStore.makeGetterSetter('u
|
|||||||
const alwaysConfirmFollow = computed(defaultStore.makeGetterSetter('alwaysConfirmFollow'));
|
const alwaysConfirmFollow = computed(defaultStore.makeGetterSetter('alwaysConfirmFollow'));
|
||||||
const confirmWhenRevealingSensitiveMedia = computed(defaultStore.makeGetterSetter('confirmWhenRevealingSensitiveMedia'));
|
const confirmWhenRevealingSensitiveMedia = computed(defaultStore.makeGetterSetter('confirmWhenRevealingSensitiveMedia'));
|
||||||
const contextMenu = computed(defaultStore.makeGetterSetter('contextMenu'));
|
const contextMenu = computed(defaultStore.makeGetterSetter('contextMenu'));
|
||||||
|
const searchEngine = computed(defaultStore.makeGetterSetter('searchEngine'));
|
||||||
|
const reactionChecksMuting = computed(defaultStore.makeGetterSetter('reactionChecksMuting'));
|
||||||
|
|
||||||
watch(lang, () => {
|
watch(lang, () => {
|
||||||
miLocalStorage.setItem('lang', lang.value as string);
|
miLocalStorage.setItem('lang', lang.value as string);
|
||||||
|
@ -532,10 +532,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
|||||||
where: 'device',
|
where: 'device',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
contextMenu: {
|
contextMenu: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: 'app' as 'app' | 'appWithShift' | 'native',
|
default: 'app' as 'app' | 'appWithShift' | 'native',
|
||||||
},
|
},
|
||||||
|
|
||||||
sound_masterVolume: {
|
sound_masterVolume: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
@ -565,6 +565,14 @@ export const defaultStore = markRaw(new Storage('base', {
|
|||||||
where: 'device',
|
where: 'device',
|
||||||
default: { type: 'syuilo/bubble2', volume: 1 } as SoundStore,
|
default: { type: 'syuilo/bubble2', volume: 1 } as SoundStore,
|
||||||
},
|
},
|
||||||
|
searchEngine: {
|
||||||
|
where: 'device',
|
||||||
|
default: 'https://google.com/search?q=',
|
||||||
|
},
|
||||||
|
reactionChecksMuting: {
|
||||||
|
where: 'device',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// TODO: 他のタブと永続化されたstateを同期
|
// TODO: 他のタブと永続化されたstateを同期
|
||||||
|
Loading…
Reference in New Issue
Block a user