1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-22 15:14:16 +02:00

enhance(frontend): チャンネル指定リノートでリノート先のチャンネルに移動できるように (#17280)

* enhance(frontend): チャンネル指定リノートでリノート先のチャンネルに移動できるように

* Update Changelog

* fix condition

* refactor
This commit is contained in:
かっこかり
2026-04-05 17:22:17 +09:00
committed by GitHub
parent 8169c57bd1
commit 0b7b59f1e2
7 changed files with 59 additions and 21 deletions

View File

@@ -4,7 +4,7 @@
- -
### Client ### Client
- - Enhance: チャンネル指定リノートでリノート先のチャンネルに移動できるように
### Server ### Server
- Fix: `/api-doc` にアクセスできない問題を修正 - Fix: `/api-doc` にアクセスできない問題を修正

View File

@@ -1408,6 +1408,7 @@ frame: "フレーム"
presets: "プリセット" presets: "プリセット"
zeroPadding: "ゼロ埋め" zeroPadding: "ゼロ埋め"
nothingToConfigure: "設定項目はありません" nothingToConfigure: "設定項目はありません"
viewRenotedChannel: "リノート先のチャンネルを見る"
_imageEditing: _imageEditing:
_vars: _vars:

View File

@@ -263,7 +263,7 @@ const emit = defineEmits<{
const inTimeline = inject<boolean>('inTimeline', false); const inTimeline = inject<boolean>('inTimeline', false);
const tl_withSensitive = inject<Ref<boolean>>('tl_withSensitive', ref(true)); const tl_withSensitive = inject<Ref<boolean>>('tl_withSensitive', ref(true));
const inChannel = inject('inChannel', null); const inChannel = inject(DI.inChannel, null);
const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null); const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
let note = deepClone(props.note); let note = deepClone(props.note);
@@ -650,23 +650,35 @@ async function showRenoteMenu() {
}; };
} }
const renoteDetailsMenu: MenuItem = { const renoteDetailsMenu: MenuItem[] = [{
type: 'link', type: 'link',
text: i18n.ts.renoteDetails, text: i18n.ts.renoteDetails,
icon: 'ti ti-info-circle', icon: 'ti ti-info-circle',
to: notePage(note), to: notePage(note),
}; }];
if (
props.note.channelId != null &&
(inChannel == null || props.note.channelId !== inChannel.value)
) {
renoteDetailsMenu.push({
type: 'link',
text: i18n.ts.viewRenotedChannel,
icon: 'ti ti-device-tv',
to: `/channels/${props.note.channelId}`,
});
}
if (isMyRenote) { if (isMyRenote) {
os.popupMenu([ os.popupMenu([
renoteDetailsMenu, ...renoteDetailsMenu,
getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote), getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
{ type: 'divider' }, { type: 'divider' },
getUnrenote(), getUnrenote(),
], renoteTime.value); ], renoteTime.value);
} else { } else {
os.popupMenu([ os.popupMenu([
renoteDetailsMenu, ...renoteDetailsMenu,
getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote), getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
{ type: 'divider' }, { type: 'divider' },
getAbuseNoteMenu(note, i18n.ts.reportAbuseRenote), getAbuseNoteMenu(note, i18n.ts.reportAbuseRenote),

View File

@@ -238,6 +238,7 @@ import { isLink } from '@@/js/is-link.js';
import { host } from '@@/js/config.js'; import { host } from '@@/js/config.js';
import type { OpenOnRemoteOptions } from '@/utility/please-login.js'; import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
import type { Keymap } from '@/utility/hotkey.js'; import type { Keymap } from '@/utility/hotkey.js';
import type { MenuItem } from '@/types/menu.js';
import MkNoteSub from '@/components/MkNoteSub.vue'; import MkNoteSub from '@/components/MkNoteSub.vue';
import MkNoteSimple from '@/components/MkNoteSimple.vue'; import MkNoteSimple from '@/components/MkNoteSimple.vue';
import MkReactionsViewer from '@/components/MkReactionsViewer.vue'; import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
@@ -286,7 +287,7 @@ const props = withDefaults(defineProps<{
initialTab: 'replies', initialTab: 'replies',
}); });
const inChannel = inject('inChannel', null); const inChannel = inject(DI.inChannel, null);
let note = deepClone(props.note); let note = deepClone(props.note);
@@ -581,18 +582,36 @@ async function showRenoteMenu() {
const isLoggedIn = await pleaseLogin({ openOnRemote: pleaseLoginContext.value }); const isLoggedIn = await pleaseLogin({ openOnRemote: pleaseLoginContext.value });
if (!isLoggedIn) return; if (!isLoggedIn) return;
os.popupMenu([{ const menu: MenuItem[] = [];
text: i18n.ts.unrenote,
icon: 'ti ti-trash', if (isMyRenote) {
danger: true, menu.push({
action: () => { text: i18n.ts.unrenote,
misskeyApi('notes/delete', { icon: 'ti ti-trash',
noteId: note.id, danger: true,
}).then(() => { action: () => {
globalEvents.emit('noteDeleted', note.id); misskeyApi('notes/delete', {
}); noteId: note.id,
}, }).then(() => {
}], renoteTime.value); globalEvents.emit('noteDeleted', note.id);
});
},
});
}
if (
props.note.channelId != null &&
(inChannel == null || props.note.channelId !== inChannel.value)
) {
menu.push({
type: 'link',
text: i18n.ts.viewRenotedChannel,
icon: 'ti ti-device-tv',
to: `/channels/${props.note.channelId}`,
});
}
os.popupMenu(menu, renoteTime.value);
} }
function focus() { function focus() {

View File

@@ -74,6 +74,7 @@ import { store } from '@/store.js';
import MkNote from '@/components/MkNote.vue'; import MkNote from '@/components/MkNote.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { DI } from '@/di.js';
import { globalEvents, useGlobalEvent } from '@/events.js'; import { globalEvents, useGlobalEvent } from '@/events.js';
import { isSeparatorNeeded, getSeparatorInfo } from '@/utility/timeline-date-separate.js'; import { isSeparatorNeeded, getSeparatorInfo } from '@/utility/timeline-date-separate.js';
import { Paginator } from '@/utility/paginator.js'; import { Paginator } from '@/utility/paginator.js';
@@ -101,7 +102,7 @@ const props = withDefaults(defineProps<{
provide('inTimeline', true); provide('inTimeline', true);
provide('tl_withSensitive', computed(() => props.withSensitive)); provide('tl_withSensitive', computed(() => props.withSensitive));
provide('inChannel', computed(() => props.src === 'channel')); provide(DI.inChannel, computed(() => props.src === 'channel' ? props.channel ?? null : null));
let paginator: IPaginator<Misskey.entities.Note>; let paginator: IPaginator<Misskey.entities.Note>;

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { InjectionKey, Ref } from 'vue'; import type { InjectionKey, Ref, ComputedRef } from 'vue';
import type { PageMetadata } from '@/page.js'; import type { PageMetadata } from '@/page.js';
import type { Router } from '@/router.js'; import type { Router } from '@/router.js';
@@ -18,4 +18,5 @@ export const DI = {
mfmEmojiReactCallback: Symbol() as InjectionKey<(emoji: string) => void>, mfmEmojiReactCallback: Symbol() as InjectionKey<(emoji: string) => void>,
inModal: Symbol() as InjectionKey<boolean>, inModal: Symbol() as InjectionKey<boolean>,
inAppSearchMarkerId: Symbol() as InjectionKey<Ref<string | null>>, inAppSearchMarkerId: Symbol() as InjectionKey<Ref<string | null>>,
inChannel: Symbol() as InjectionKey<ComputedRef<string | null> | null>, // 現在開いているチャンネルのID
}; };

View File

@@ -5647,6 +5647,10 @@ export interface Locale extends ILocale {
* 設定項目はありません * 設定項目はありません
*/ */
"nothingToConfigure": string; "nothingToConfigure": string;
/**
* リノート先のチャンネルを見る
*/
"viewRenotedChannel": string;
"_imageEditing": { "_imageEditing": {
"_vars": { "_vars": {
/** /**