Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(frontend): リノートの判定が甘いのを修正 #14396

Merged
merged 12 commits into from
Aug 17, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Fix: iOSでユーザー名などがリンクとして誤検知される現象を抑制
- Fix: mCaptchaを使用していてもbotプロテクションに関する警告が消えないのを修正
- Fix: ユーザーのモデレーションページにおいてユーザー名にドットが入っているとシステムアカウントとして表示されてしまう問題を修正
- Fix: 特定の条件下でノートの削除ボタンが出ないのを修正

### Server
- Fix: WSの`readAllNotifications` メッセージが `body` を持たない場合に動作しない問題 #14374
Expand Down
12 changes: 3 additions & 9 deletions packages/frontend/src/components/MkNote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ import { host } from '@/config.js';
import { isEnabledUrlPreview } from '@/instance.js';
import { type Keymap } from '@/scripts/hotkey.js';
import { focusPrev, focusNext } from '@/scripts/focus.js';
import { getAppearNote } from '@/scripts/get-appear-note.js';

const props = withDefaults(defineProps<{
note: Misskey.entities.Note;
Expand Down Expand Up @@ -242,22 +243,15 @@ if (noteViewInterruptors.length > 0) {
});
}

const isRenote = (
note.value.renote != null &&
note.value.reply == null &&
note.value.text == null &&
note.value.cw == null &&
note.value.fileIds && note.value.fileIds.length === 0 &&
note.value.poll == null
);
const isRenote = Misskey.note.isPureRenote(note.value);

const rootEl = shallowRef<HTMLElement>();
const menuButton = shallowRef<HTMLElement>();
const renoteButton = shallowRef<HTMLElement>();
const renoteTime = shallowRef<HTMLElement>();
const reactButton = shallowRef<HTMLElement>();
const clipButton = shallowRef<HTMLElement>();
const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
const appearNote = computed(() => getAppearNote(note.value));
const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
const isMyRenote = $i && ($i.id === note.value.userId);
const showContent = ref(false);
Expand Down
12 changes: 3 additions & 9 deletions packages/frontend/src/components/MkNoteDetailed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ import MkPagination, { type Paging } from '@/components/MkPagination.vue';
import MkReactionIcon from '@/components/MkReactionIcon.vue';
import MkButton from '@/components/MkButton.vue';
import { isEnabledUrlPreview } from '@/instance.js';
import { getAppearNote } from '@/scripts/get-appear-note.js';
import { type Keymap } from '@/scripts/hotkey.js';

const props = withDefaults(defineProps<{
Expand Down Expand Up @@ -267,22 +268,15 @@ if (noteViewInterruptors.length > 0) {
});
}

const isRenote = (
note.value.renote != null &&
note.value.reply == null &&
note.value.text == null &&
note.value.cw == null &&
note.value.fileIds && note.value.fileIds.length === 0 &&
note.value.poll == null
);
const isRenote = Misskey.note.isPureRenote(note.value);

const rootEl = shallowRef<HTMLElement>();
const menuButton = shallowRef<HTMLElement>();
const renoteButton = shallowRef<HTMLElement>();
const renoteTime = shallowRef<HTMLElement>();
const reactButton = shallowRef<HTMLElement>();
const clipButton = shallowRef<HTMLElement>();
const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
const appearNote = computed(() => getAppearNote(note.value));
const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
const isMyRenote = $i && ($i.id === note.value.userId);
const showContent = ref(false);
Expand Down
10 changes: 10 additions & 0 deletions packages/frontend/src/scripts/get-appear-note.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/

import * as Misskey from 'misskey-js';

export function getAppearNote(note: Misskey.entities.Note) {
return Misskey.note.isPureRenote(note) ? note.renote : note;
}
33 changes: 7 additions & 26 deletions packages/frontend/src/scripts/get-note-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { clipsCache, favoritedChannelsCache } from '@/cache.js';
import { MenuItem } from '@/types/menu.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { isSupportShare } from '@/scripts/navigator.js';
import { getAppearNote } from '@/scripts/get-appear-note.js';

export async function getNoteClipMenu(props: {
note: Misskey.entities.Note;
Expand All @@ -34,14 +35,7 @@ export async function getNoteClipMenu(props: {
}
}

const isRenote = (
props.note.renote != null &&
props.note.text == null &&
props.note.fileIds.length === 0 &&
props.note.poll == null
);

const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note;
const appearNote = getAppearNote(props.note);

const clips = await clipsCache.fetch();
const menu: MenuItem[] = [...clips.map(clip => ({
Expand Down Expand Up @@ -164,14 +158,7 @@ export function getNoteMenu(props: {
isDeleted: Ref<boolean>;
currentClip?: Misskey.entities.Clip;
}) {
const isRenote = (
props.note.renote != null &&
props.note.text == null &&
props.note.fileIds.length === 0 &&
props.note.poll == null
);

const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note;
const appearNote = getAppearNote(props.note);

const cleanups = [] as (() => void)[];

Expand Down Expand Up @@ -248,6 +235,7 @@ export function getNoteMenu(props: {
}

async function unclip(): Promise<void> {
if (!props.currentClip) return;
os.apiWithDialog('clips/remove-note', { clipId: props.currentClip.id, noteId: appearNote.id });
props.isDeleted.value = true;
}
Expand All @@ -267,8 +255,8 @@ export function getNoteMenu(props: {

function share(): void {
navigator.share({
title: i18n.tsx.noteOf({ user: appearNote.user.name }),
text: appearNote.text,
title: i18n.tsx.noteOf({ user: appearNote.user.name ?? appearNote.user.username }),
text: appearNote.text ?? '',
url: `${url}/notes/${appearNote.id}`,
});
}
Expand Down Expand Up @@ -509,14 +497,7 @@ export function getRenoteMenu(props: {
renoteButton: ShallowRef<HTMLElement | undefined>;
mock?: boolean;
}) {
const isRenote = (
props.note.renote != null &&
props.note.text == null &&
props.note.fileIds.length === 0 &&
props.note.poll == null
);

const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note;
const appearNote = getAppearNote(props.note);

const channelRenoteItems: MenuItem[] = [];
const normalRenoteItems: MenuItem[] = [];
Expand Down
22 changes: 21 additions & 1 deletion packages/misskey-js/etc/misskey-js.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,7 @@ declare namespace entities {
export {
ID,
DateString,
PureRenote,
PageEvent,
ModerationLog,
ServerStats,
Expand Down Expand Up @@ -2277,6 +2278,9 @@ type ISigninHistoryRequest = operations['i___signin-history']['requestBody']['co
// @public (undocumented)
type ISigninHistoryResponse = operations['i___signin-history']['responses']['200']['content']['application/json'];

// @public (undocumented)
function isPureRenote(note: Note): note is PureRenote;

// @public (undocumented)
type IUnpinRequest = operations['i___unpin']['requestBody']['content']['application/json'];

Expand Down Expand Up @@ -2510,6 +2514,13 @@ type MyAppsResponse = operations['my___apps']['responses']['200']['content']['ap
// @public (undocumented)
type Note = components['schemas']['Note'];

declare namespace note {
export {
isPureRenote
}
}
export { note }

// @public (undocumented)
type NoteFavorite = components['schemas']['NoteFavorite'];

Expand Down Expand Up @@ -2750,6 +2761,15 @@ type PinnedUsersResponse = operations['pinned-users']['responses']['200']['conte
// @public (undocumented)
type PromoReadRequest = operations['promo___read']['requestBody']['content']['application/json'];

// Warning: (ae-forgotten-export) The symbol "AllNullRecord" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "NonNullableRecord" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type PureRenote = Omit<Note, 'renote' | 'renoteId' | 'reply' | 'replyId' | 'text' | 'cw' | 'files' | 'fileIds' | 'poll'> & AllNullRecord<Pick<Note, 'reply' | 'replyId' | 'text' | 'cw' | 'poll'>> & {
files: [];
fileIds: [];
} & NonNullableRecord<Pick<Note, 'renote' | 'renoteId'>>;

// @public (undocumented)
type QueueCount = components['schemas']['QueueCount'];

Expand Down Expand Up @@ -3229,7 +3249,7 @@ type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['

// Warnings were encountered during analysis:
//
// src/entities.ts:35:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
// src/entities.ts:49:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
// src/streaming.types.ts:220:4 - (ae-forgotten-export) The symbol "ReversiUpdateKey" needs to be exported by the entry point index.d.ts
// src/streaming.types.ts:230:4 - (ae-forgotten-export) The symbol "ReversiUpdateSettings" needs to be exported by the entry point index.d.ts

Expand Down
14 changes: 14 additions & 0 deletions packages/misskey-js/src/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Announcement,
EmojiDetailed,
MeDetailed,
Note,
Page,
Role,
RolePolicies,
Expand All @@ -16,6 +17,19 @@ export * from './autogen/models.js';
export type ID = string;
export type DateString = string;

type NonNullableRecord<T> = {
[P in keyof T]-?: NonNullable<T[P]>;
};
type AllNullRecord<T> = {
[P in keyof T]: null;
};

export type PureRenote =
kakkokari-gtyih marked this conversation as resolved.
Show resolved Hide resolved
Omit<Note, 'renote' | 'renoteId' | 'reply' | 'replyId' | 'text' | 'cw' | 'files' | 'fileIds' | 'poll'>
& AllNullRecord<Pick<Note, 'reply' | 'replyId' | 'text' | 'cw' | 'poll'>>
& { files: []; fileIds: []; }
& NonNullableRecord<Pick<Note, 'renote' | 'renoteId'>>;

export type PageEvent = {
pageId: Page['id'];
event: string;
Expand Down
3 changes: 2 additions & 1 deletion packages/misskey-js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ export const reversiUpdateKeys = consts.reversiUpdateKeys;
import * as api from './api.js';
import * as entities from './entities.js';
import * as acct from './acct.js';
export { api, entities, acct };
import * as note from './note.js';
export { api, entities, acct, note };
12 changes: 12 additions & 0 deletions packages/misskey-js/src/note.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Note, PureRenote } from './entities.js';

export function isPureRenote(note: Note): note is PureRenote {
return (
note.renote != null &&
note.reply == null &&
note.text == null &&
note.cw == null &&
(note.fileIds == null || note.fileIds.length === 0) &&
note.poll == null
);
}
Loading