Merge: enhance privacy of notes
https://akkoma.dev/FoundKeyGang/FoundKey/pulls/14
This commit is contained in:
parent
3b40c7d081
commit
20763a84ee
|
@ -9,10 +9,6 @@ export const getNoteSummary = (note: Packed<'Note'>): string => {
|
||||||
return `(❌⛔)`;
|
return `(❌⛔)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.isHidden) {
|
|
||||||
return `(⛔)`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let summary = '';
|
let summary = '';
|
||||||
|
|
||||||
// 本文
|
// 本文
|
||||||
|
|
|
@ -14,6 +14,7 @@ export const NoteFavoriteRepository = db.getRepository(NoteFavorite).extend({
|
||||||
id: favorite.id,
|
id: favorite.id,
|
||||||
createdAt: favorite.createdAt.toISOString(),
|
createdAt: favorite.createdAt.toISOString(),
|
||||||
noteId: favorite.noteId,
|
noteId: favorite.noteId,
|
||||||
|
// may throw error
|
||||||
note: await Notes.pack(favorite.note || favorite.noteId, me),
|
note: await Notes.pack(favorite.note || favorite.noteId, me),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -22,6 +23,7 @@ export const NoteFavoriteRepository = db.getRepository(NoteFavorite).extend({
|
||||||
favorites: any[],
|
favorites: any[],
|
||||||
me: { id: User['id'] }
|
me: { id: User['id'] }
|
||||||
) {
|
) {
|
||||||
return Promise.all(favorites.map(x => this.pack(x, me)));
|
return Promise.allSettled(favorites.map(x => this.pack(x, me)))
|
||||||
|
.then(promises => promises.flatMap(result => result.status === 'fulfilled' ? [result.value] : []));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,8 +25,22 @@ export const NoteReactionRepository = db.getRepository(NoteReaction).extend({
|
||||||
user: await Users.pack(reaction.user ?? reaction.userId, me),
|
user: await Users.pack(reaction.user ?? reaction.userId, me),
|
||||||
type: convertLegacyReaction(reaction.reaction),
|
type: convertLegacyReaction(reaction.reaction),
|
||||||
...(opts.withNote ? {
|
...(opts.withNote ? {
|
||||||
|
// may throw error
|
||||||
note: await Notes.pack(reaction.note ?? reaction.noteId, me),
|
note: await Notes.pack(reaction.note ?? reaction.noteId, me),
|
||||||
} : {}),
|
} : {}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async packMany(
|
||||||
|
src: NoteReaction[],
|
||||||
|
me?: { id: User['id'] } | null | undefined,
|
||||||
|
options?: {
|
||||||
|
withNote: booleam;
|
||||||
|
},
|
||||||
|
): Promise<Packed<'NoteReaction'>[]> {
|
||||||
|
const reactions = await Promise.allSettled(src.map(reaction => this.pack(reaction, me, options)));
|
||||||
|
|
||||||
|
// filter out rejected promises, only keep fulfilled values
|
||||||
|
return reactions.flatMap(result => result.status === 'fulfilled' ? [result.value] : []);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,66 +10,7 @@ import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@
|
||||||
import { NoteReaction } from '@/models/entities/note-reaction.js';
|
import { NoteReaction } from '@/models/entities/note-reaction.js';
|
||||||
import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis.js';
|
import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis.js';
|
||||||
import { db } from '@/db/postgre.js';
|
import { db } from '@/db/postgre.js';
|
||||||
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
async function hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) {
|
|
||||||
// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
|
|
||||||
let hide = false;
|
|
||||||
|
|
||||||
// visibility が specified かつ自分が指定されていなかったら非表示
|
|
||||||
if (packedNote.visibility === 'specified') {
|
|
||||||
if (meId == null) {
|
|
||||||
hide = true;
|
|
||||||
} else if (meId === packedNote.userId) {
|
|
||||||
hide = false;
|
|
||||||
} else {
|
|
||||||
// 指定されているかどうか
|
|
||||||
const specified = packedNote.visibleUserIds!.some((id: any) => meId === id);
|
|
||||||
|
|
||||||
if (specified) {
|
|
||||||
hide = false;
|
|
||||||
} else {
|
|
||||||
hide = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
|
|
||||||
if (packedNote.visibility === 'followers') {
|
|
||||||
if (meId == null) {
|
|
||||||
hide = true;
|
|
||||||
} else if (meId === packedNote.userId) {
|
|
||||||
hide = false;
|
|
||||||
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
|
|
||||||
// 自分の投稿に対するリプライ
|
|
||||||
hide = false;
|
|
||||||
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
|
|
||||||
// 自分へのメンション
|
|
||||||
hide = false;
|
|
||||||
} else {
|
|
||||||
// フォロワーかどうか
|
|
||||||
const following = await Followings.findOneBy({
|
|
||||||
followeeId: packedNote.userId,
|
|
||||||
followerId: meId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (following == null) {
|
|
||||||
hide = true;
|
|
||||||
} else {
|
|
||||||
hide = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hide) {
|
|
||||||
packedNote.visibleUserIds = undefined;
|
|
||||||
packedNote.fileIds = [];
|
|
||||||
packedNote.files = [];
|
|
||||||
packedNote.text = null;
|
|
||||||
packedNote.poll = undefined;
|
|
||||||
packedNote.cw = null;
|
|
||||||
packedNote.isHidden = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function populatePoll(note: Note, meId: User['id'] | null) {
|
async function populatePoll(note: Note, meId: User['id'] | null) {
|
||||||
const poll = await Polls.findOneByOrFail({ noteId: note.id });
|
const poll = await Polls.findOneByOrFail({ noteId: note.id });
|
||||||
|
@ -193,7 +134,6 @@ export const NoteRepository = db.getRepository(Note).extend({
|
||||||
me?: { id: User['id'] } | null | undefined,
|
me?: { id: User['id'] } | null | undefined,
|
||||||
options?: {
|
options?: {
|
||||||
detail?: boolean;
|
detail?: boolean;
|
||||||
skipHide?: boolean;
|
|
||||||
_hint_?: {
|
_hint_?: {
|
||||||
myReactions: Map<Note['id'], NoteReaction | null>;
|
myReactions: Map<Note['id'], NoteReaction | null>;
|
||||||
};
|
};
|
||||||
|
@ -201,13 +141,16 @@ export const NoteRepository = db.getRepository(Note).extend({
|
||||||
): Promise<Packed<'Note'>> {
|
): Promise<Packed<'Note'>> {
|
||||||
const opts = Object.assign({
|
const opts = Object.assign({
|
||||||
detail: true,
|
detail: true,
|
||||||
skipHide: false,
|
|
||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
const meId = me ? me.id : null;
|
const meId = me ? me.id : null;
|
||||||
const note = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
|
const note = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
|
||||||
const host = note.userHost;
|
const host = note.userHost;
|
||||||
|
|
||||||
|
if (!await this.isVisibleForMe(note, meId)) {
|
||||||
|
throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.');
|
||||||
|
}
|
||||||
|
|
||||||
let text = note.text;
|
let text = note.text;
|
||||||
|
|
||||||
if (note.name && (note.url ?? note.uri)) {
|
if (note.name && (note.url ?? note.uri)) {
|
||||||
|
@ -282,10 +225,6 @@ export const NoteRepository = db.getRepository(Note).extend({
|
||||||
packed.text = mfm.toString(tokens);
|
packed.text = mfm.toString(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opts.skipHide) {
|
|
||||||
await hideNote(packed, meId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return packed;
|
return packed;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -294,7 +233,6 @@ export const NoteRepository = db.getRepository(Note).extend({
|
||||||
me?: { id: User['id'] } | null | undefined,
|
me?: { id: User['id'] } | null | undefined,
|
||||||
options?: {
|
options?: {
|
||||||
detail?: boolean;
|
detail?: boolean;
|
||||||
skipHide?: boolean;
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (notes.length === 0) return [];
|
if (notes.length === 0) return [];
|
||||||
|
@ -316,11 +254,14 @@ export const NoteRepository = db.getRepository(Note).extend({
|
||||||
|
|
||||||
await prefetchEmojis(aggregateNoteEmojis(notes));
|
await prefetchEmojis(aggregateNoteEmojis(notes));
|
||||||
|
|
||||||
return await Promise.all(notes.map(n => this.pack(n, me, {
|
const promises = await Promise.allSettled(notes.map(n => this.pack(n, me, {
|
||||||
...options,
|
...options,
|
||||||
_hint_: {
|
_hint_: {
|
||||||
myReactions: myReactionsMap,
|
myReactions: myReactionsMap,
|
||||||
},
|
},
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
// filter out rejected promises, only keep fulfilled values
|
||||||
|
return promises.flatMap(result => result.status === 'fulfilled' ? [result.value] : []);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,10 +52,6 @@ export const packedNoteSchema = {
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
ref: 'Note',
|
ref: 'Note',
|
||||||
},
|
},
|
||||||
isHidden: {
|
|
||||||
type: 'boolean',
|
|
||||||
optional: true, nullable: false,
|
|
||||||
},
|
|
||||||
visibility: {
|
visibility: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
|
|
@ -2,12 +2,20 @@ import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
import { User } from '@/models/entities/user.js';
|
import { User } from '@/models/entities/user.js';
|
||||||
import { Note } from '@/models/entities/note.js';
|
import { Note } from '@/models/entities/note.js';
|
||||||
import { Notes, Users } from '@/models/index.js';
|
import { Notes, Users } from '@/models/index.js';
|
||||||
|
import { generateVisibilityQuery } from './generate-visibility-query.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get note for API processing
|
* Get note for API processing, taking into account visibility.
|
||||||
*/
|
*/
|
||||||
export async function getNote(noteId: Note['id']) {
|
export async function getNote(noteId: Note['id'], me: { id: User['id'] } | null) {
|
||||||
const note = await Notes.findOneBy({ id: noteId });
|
const query = Notes.createQueryBuilder('note')
|
||||||
|
.where("note.id = :id", {
|
||||||
|
id: noteId,
|
||||||
|
});
|
||||||
|
|
||||||
|
generateVisibilityQuery(query, me);
|
||||||
|
|
||||||
|
const note = await query.getOne();
|
||||||
|
|
||||||
if (note == null) {
|
if (note == null) {
|
||||||
throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.');
|
throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.');
|
||||||
|
|
|
@ -35,9 +35,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const exist = await PromoNotes.findOneBy({ noteId: note.id });
|
const exist = await PromoNotes.findOneBy({ noteId: note.id });
|
||||||
|
|
|
@ -52,9 +52,9 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
throw new ApiError(meta.errors.noSuchClip);
|
throw new ApiError(meta.errors.noSuchClip);
|
||||||
}
|
}
|
||||||
|
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const exist = await ClipNotes.findOneBy({
|
const exist = await ClipNotes.findOneBy({
|
||||||
|
|
|
@ -39,9 +39,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, me) => {
|
export default define(meta, paramDef, async (ps, me) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, me).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const clipNotes = await ClipNotes.findBy({
|
const clipNotes = await ClipNotes.findBy({
|
||||||
|
|
|
@ -41,9 +41,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const conversation: Note[] = [];
|
const conversation: Note[] = [];
|
||||||
|
@ -51,7 +51,11 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
|
|
||||||
async function get(id: any) {
|
async function get(id: any) {
|
||||||
i++;
|
i++;
|
||||||
const p = await Notes.findOneBy({ id });
|
const p = await getNote(id, user).catch(e => {
|
||||||
|
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') return null;
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
|
||||||
if (p == null) return;
|
if (p == null) return;
|
||||||
|
|
||||||
if (i > ps.offset!) {
|
if (i > ps.offset!) {
|
||||||
|
|
|
@ -7,9 +7,11 @@ import { DriveFile } from '@/models/entities/drive-file.js';
|
||||||
import { Note } from '@/models/entities/note.js';
|
import { Note } from '@/models/entities/note.js';
|
||||||
import { Channel } from '@/models/entities/channel.js';
|
import { Channel } from '@/models/entities/channel.js';
|
||||||
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
|
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
|
||||||
|
import { HOUR } from '@/const.js';
|
||||||
import { noteVisibilities } from '../../../../types.js';
|
import { noteVisibilities } from '../../../../types.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
|
import { getNote } from '../../common/getters.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['notes'],
|
tags: ['notes'],
|
||||||
|
@ -185,11 +187,12 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
let renote: Note | null = null;
|
let renote: Note | null = null;
|
||||||
if (ps.renoteId != null) {
|
if (ps.renoteId != null) {
|
||||||
// Fetch renote to note
|
// Fetch renote to note
|
||||||
renote = await Notes.findOneBy({ id: ps.renoteId });
|
renote = await getNote(ps.renoteId, user).catch(e => {
|
||||||
|
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchRenoteTarget);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
|
||||||
if (renote == null) {
|
if (renote.renoteId && !renote.text && !renote.fileIds && !renote.hasPoll) {
|
||||||
throw new ApiError(meta.errors.noSuchRenoteTarget);
|
|
||||||
} else if (renote.renoteId && !renote.text && !renote.fileIds && !renote.hasPoll) {
|
|
||||||
throw new ApiError(meta.errors.cannotReRenote);
|
throw new ApiError(meta.errors.cannotReRenote);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,11 +211,12 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
let reply: Note | null = null;
|
let reply: Note | null = null;
|
||||||
if (ps.replyId != null) {
|
if (ps.replyId != null) {
|
||||||
// Fetch reply
|
// Fetch reply
|
||||||
reply = await Notes.findOneBy({ id: ps.replyId });
|
reply = await getNote(ps.replyId, user).catch(e => {
|
||||||
|
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchReplyTarget);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
|
||||||
if (reply == null) {
|
if (reply.renoteId && !reply.text && !reply.fileIds && !reply.hasPoll) {
|
||||||
throw new ApiError(meta.errors.noSuchReplyTarget);
|
|
||||||
} else if (reply.renoteId && !reply.text && !reply.fileIds && !reply.hasPoll) {
|
|
||||||
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,9 +43,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((!user.isAdmin && !user.isModerator) && (note.userId !== user.id)) {
|
if ((!user.isAdmin && !user.isModerator) && (note.userId !== user.id)) {
|
||||||
|
|
|
@ -37,9 +37,9 @@ export const paramDef = {
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
// Get favoritee
|
// Get favoritee
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
// if already favorited
|
// if already favorited
|
||||||
|
|
|
@ -36,9 +36,9 @@ export const paramDef = {
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
// Get favoritee
|
// Get favoritee
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
// if already favorited
|
// if already favorited
|
||||||
|
|
|
@ -72,9 +72,9 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const createdAt = new Date();
|
const createdAt = new Date();
|
||||||
|
|
||||||
// Get votee
|
// Get votee
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!note.hasPoll) {
|
if (!note.hasPoll) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { NoteReactions } from '@/models/index.js';
|
||||||
import { NoteReaction } from '@/models/entities/note-reaction.js';
|
import { NoteReaction } from '@/models/entities/note-reaction.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
|
import { getNote } from '../../common/getters.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['notes', 'reactions'],
|
tags: ['notes', 'reactions'],
|
||||||
|
@ -47,6 +48,12 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
|
// check note visibility
|
||||||
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
noteId: ps.noteId,
|
noteId: ps.noteId,
|
||||||
} as FindOptionsWhere<NoteReaction>;
|
} as FindOptionsWhere<NoteReaction>;
|
||||||
|
@ -69,5 +76,5 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
relations: ['user', 'user.avatar', 'user.banner', 'note'],
|
relations: ['user', 'user.avatar', 'user.banner', 'note'],
|
||||||
});
|
});
|
||||||
|
|
||||||
return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, user)));
|
return await NoteReactions.packMany(reactions, user);
|
||||||
});
|
});
|
||||||
|
|
|
@ -42,9 +42,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
await createReaction(user, note, ps.reaction).catch(e => {
|
await createReaction(user, note, ps.reaction).catch(e => {
|
||||||
if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') throw new ApiError(meta.errors.alreadyReacted);
|
if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') throw new ApiError(meta.errors.alreadyReacted);
|
||||||
|
|
|
@ -42,9 +42,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
await deleteReaction(user, note).catch(e => {
|
await deleteReaction(user, note).catch(e => {
|
||||||
if (e.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') throw new ApiError(meta.errors.notReacted);
|
if (e.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') throw new ApiError(meta.errors.notReacted);
|
||||||
|
|
|
@ -45,9 +45,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
||||||
|
|
|
@ -34,12 +34,16 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
return await Notes.pack(note, user, {
|
return await Notes.pack(note, user, {
|
||||||
|
// FIXME: packing with detail may throw an error if the reply or renote is not visible (#8774)
|
||||||
detail: true,
|
detail: true,
|
||||||
|
}).catch(err => {
|
||||||
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
|
throw err;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { NoteFavorites, Notes, NoteThreadMutings, NoteWatchings } from '@/models/index.js';
|
import { NoteFavorites, Notes, NoteThreadMutings, NoteWatchings } from '@/models/index.js';
|
||||||
|
import { getNote } from '../../common/getters.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -36,7 +37,7 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await Notes.findOneByOrFail({ id: ps.noteId });
|
const note = await getNote(ps.noteId, user);
|
||||||
|
|
||||||
const [favorite, watching, threadMuting] = await Promise.all([
|
const [favorite, watching, threadMuting] = await Promise.all([
|
||||||
NoteFavorites.count({
|
NoteFavorites.count({
|
||||||
|
|
|
@ -31,9 +31,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const mutedNotes = await Notes.find({
|
const mutedNotes = await Notes.find({
|
||||||
|
|
|
@ -29,9 +29,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
await NoteThreadMutings.delete({
|
await NoteThreadMutings.delete({
|
||||||
|
|
|
@ -39,15 +39,11 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!(await Notes.isVisibleForMe(note, user ? user.id : null))) {
|
|
||||||
return 204; // TODO: 良い感じのエラー返す
|
|
||||||
}
|
|
||||||
|
|
||||||
if (note.text == null) {
|
if (note.text == null) {
|
||||||
return 204;
|
return 204;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const renotes = await Notes.findBy({
|
const renotes = await Notes.findBy({
|
||||||
|
|
|
@ -29,9 +29,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
await watch(user.id, note);
|
await watch(user.id, note);
|
||||||
|
|
|
@ -29,9 +29,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
await unwatch(user.id, note);
|
await unwatch(user.id, note);
|
||||||
|
|
|
@ -28,9 +28,9 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
const note = await getNote(ps.noteId).catch(e => {
|
const note = await getNote(ps.noteId, user).catch(err => {
|
||||||
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
throw e;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const exist = await PromoReads.findOneBy({
|
const exist = await PromoReads.findOneBy({
|
||||||
|
|
|
@ -63,5 +63,5 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
.take(ps.limit)
|
.take(ps.limit)
|
||||||
.getMany();
|
.getMany();
|
||||||
|
|
||||||
return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, me, { withNote: true })));
|
return await NoteReactions.packMany(reactions, me, { withNote: true });
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import Connection from '.';
|
import Connection from '.';
|
||||||
|
import { Note } from '@/models/entities/note.js';
|
||||||
|
import { Notes } from '@/models/index.js';
|
||||||
|
import { Packed } from '@/misc/schema.js';
|
||||||
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream channel
|
* Stream channel
|
||||||
|
@ -54,6 +58,32 @@ export default abstract class Channel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected withPackedNote(callback: (note: Packed<'Note'>) => void): (Note) => void {
|
||||||
|
return async (note: Note) => {
|
||||||
|
try {
|
||||||
|
// because `note` was previously JSON.stringify'ed, the fields that
|
||||||
|
// were objects before are now strings and have to be restored or
|
||||||
|
// removed from the object
|
||||||
|
note.createdAt = new Date(note.createdAt);
|
||||||
|
delete note.reply;
|
||||||
|
delete note.renote;
|
||||||
|
delete note.user;
|
||||||
|
delete note.channel;
|
||||||
|
|
||||||
|
const packed = await Notes.pack(note, this.user, { detail: true });
|
||||||
|
|
||||||
|
callback(packed);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof IdentifiableError && err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') {
|
||||||
|
// skip: note not visible to user
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public abstract init(params: any): void;
|
public abstract init(params: any): void;
|
||||||
public dispose?(): void;
|
public dispose?(): void;
|
||||||
public onMessage?(type: string, body: any): void;
|
public onMessage?(type: string, body: any): void;
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Channel from '../channel.js';
|
||||||
import { Notes } from '@/models/index.js';
|
import { Notes } from '@/models/index.js';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||||
import { StreamMessages } from '../types.js';
|
import { StreamMessages } from '../types.js';
|
||||||
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
|
|
||||||
export default class extends Channel {
|
export default class extends Channel {
|
||||||
public readonly chName = 'antenna';
|
public readonly chName = 'antenna';
|
||||||
|
@ -23,6 +24,7 @@ export default class extends Channel {
|
||||||
|
|
||||||
private async onEvent(data: StreamMessages['antenna']['payload']) {
|
private async onEvent(data: StreamMessages['antenna']['payload']) {
|
||||||
if (data.type === 'note') {
|
if (data.type === 'note') {
|
||||||
|
try {
|
||||||
const note = await Notes.pack(data.body.id, this.user, { detail: true });
|
const note = await Notes.pack(data.body.id, this.user, { detail: true });
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
||||||
|
@ -33,6 +35,14 @@ export default class extends Channel {
|
||||||
this.connection.cacheNote(note);
|
this.connection.cacheNote(note);
|
||||||
|
|
||||||
this.send('note', note);
|
this.send('note', note);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof IdentifiableError && e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') {
|
||||||
|
// skip: note not visible to user
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.send(data.type, data.body);
|
this.send(data.type, data.body);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Channel from '../channel.js';
|
import Channel from '../channel.js';
|
||||||
import { Notes, Users } from '@/models/index.js';
|
import { Users } from '@/models/index.js';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||||
import { User } from '@/models/entities/user.js';
|
import { User } from '@/models/entities/user.js';
|
||||||
import { StreamMessages } from '../types.js';
|
import { StreamMessages } from '../types.js';
|
||||||
|
@ -31,19 +31,6 @@ export default class extends Channel {
|
||||||
private async onNote(note: Packed<'Note'>) {
|
private async onNote(note: Packed<'Note'>) {
|
||||||
if (note.channelId !== this.channelId) return;
|
if (note.channelId !== this.channelId) return;
|
||||||
|
|
||||||
// リプライなら再pack
|
|
||||||
if (note.replyId != null) {
|
|
||||||
note.reply = await Notes.pack(note.replyId, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Renoteなら再pack
|
|
||||||
if (note.renoteId != null) {
|
|
||||||
note.renote = await Notes.pack(note.renoteId, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
||||||
if (isUserRelated(note, this.muting)) return;
|
if (isUserRelated(note, this.muting)) return;
|
||||||
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
|
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import Channel from '../channel.js';
|
import Channel from '../channel.js';
|
||||||
import { fetchMeta } from '@/misc/fetch-meta.js';
|
import { fetchMeta } from '@/misc/fetch-meta.js';
|
||||||
import { Notes } from '@/models/index.js';
|
|
||||||
import { checkWordMute } from '@/misc/check-word-mute.js';
|
import { checkWordMute } from '@/misc/check-word-mute.js';
|
||||||
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
|
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||||
|
@ -13,7 +12,7 @@ export default class extends Channel {
|
||||||
|
|
||||||
constructor(id: string, connection: Channel['connection']) {
|
constructor(id: string, connection: Channel['connection']) {
|
||||||
super(id, connection);
|
super(id, connection);
|
||||||
this.onNote = this.onNote.bind(this);
|
this.onNote = this.withPackedNote(this.onNote.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(params: any) {
|
public async init(params: any) {
|
||||||
|
@ -30,19 +29,6 @@ export default class extends Channel {
|
||||||
if (note.visibility !== 'public') return;
|
if (note.visibility !== 'public') return;
|
||||||
if (note.channelId != null) return;
|
if (note.channelId != null) return;
|
||||||
|
|
||||||
// リプライなら再pack
|
|
||||||
if (note.replyId != null) {
|
|
||||||
note.reply = await Notes.pack(note.replyId, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Renoteなら再pack
|
|
||||||
if (note.renoteId != null) {
|
|
||||||
note.renote = await Notes.pack(note.renoteId, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 関係ない返信は除外
|
// 関係ない返信は除外
|
||||||
if (note.reply && !this.user!.showTimelineReplies) {
|
if (note.reply && !this.user!.showTimelineReplies) {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import Channel from '../channel.js';
|
import Channel from '../channel.js';
|
||||||
import { Notes } from '@/models/index.js';
|
|
||||||
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||||
import { Packed } from '@/misc/schema.js';
|
import { Packed } from '@/misc/schema.js';
|
||||||
|
@ -12,7 +11,7 @@ export default class extends Channel {
|
||||||
|
|
||||||
constructor(id: string, connection: Channel['connection']) {
|
constructor(id: string, connection: Channel['connection']) {
|
||||||
super(id, connection);
|
super(id, connection);
|
||||||
this.onNote = this.onNote.bind(this);
|
this.onNote = this.withPackedNote(this.onNote.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(params: any) {
|
public async init(params: any) {
|
||||||
|
@ -29,13 +28,6 @@ export default class extends Channel {
|
||||||
const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag))));
|
const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag))));
|
||||||
if (!matched) return;
|
if (!matched) return;
|
||||||
|
|
||||||
// Renoteなら再pack
|
|
||||||
if (note.renoteId != null) {
|
|
||||||
note.renote = await Notes.pack(note.renoteId, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
||||||
if (isUserRelated(note, this.muting)) return;
|
if (isUserRelated(note, this.muting)) return;
|
||||||
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
|
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import Channel from '../channel.js';
|
import Channel from '../channel.js';
|
||||||
import { Notes } from '@/models/index.js';
|
|
||||||
import { checkWordMute } from '@/misc/check-word-mute.js';
|
import { checkWordMute } from '@/misc/check-word-mute.js';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||||
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
|
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
|
||||||
|
@ -12,7 +11,7 @@ export default class extends Channel {
|
||||||
|
|
||||||
constructor(id: string, connection: Channel['connection']) {
|
constructor(id: string, connection: Channel['connection']) {
|
||||||
super(id, connection);
|
super(id, connection);
|
||||||
this.onNote = this.onNote.bind(this);
|
this.onNote = this.withPackedNote(this.onNote.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(params: any) {
|
public async init(params: any) {
|
||||||
|
@ -31,29 +30,6 @@ export default class extends Channel {
|
||||||
// Ignore notes from instances the user has muted
|
// Ignore notes from instances the user has muted
|
||||||
if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
|
if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
|
||||||
|
|
||||||
if (['followers', 'specified'].includes(note.visibility)) {
|
|
||||||
note = await Notes.pack(note.id, this.user!, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (note.isHidden) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// リプライなら再pack
|
|
||||||
if (note.replyId != null) {
|
|
||||||
note.reply = await Notes.pack(note.replyId, this.user!, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Renoteなら再pack
|
|
||||||
if (note.renoteId != null) {
|
|
||||||
note.renote = await Notes.pack(note.renoteId, this.user!, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 関係ない返信は除外
|
// 関係ない返信は除外
|
||||||
if (note.reply && !this.user!.showTimelineReplies) {
|
if (note.reply && !this.user!.showTimelineReplies) {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import Channel from '../channel.js';
|
import Channel from '../channel.js';
|
||||||
import { fetchMeta } from '@/misc/fetch-meta.js';
|
import { fetchMeta } from '@/misc/fetch-meta.js';
|
||||||
import { Notes } from '@/models/index.js';
|
|
||||||
import { checkWordMute } from '@/misc/check-word-mute.js';
|
import { checkWordMute } from '@/misc/check-word-mute.js';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||||
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
|
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
|
||||||
|
@ -13,7 +12,7 @@ export default class extends Channel {
|
||||||
|
|
||||||
constructor(id: string, connection: Channel['connection']) {
|
constructor(id: string, connection: Channel['connection']) {
|
||||||
super(id, connection);
|
super(id, connection);
|
||||||
this.onNote = this.onNote.bind(this);
|
this.onNote = this.withPackedNote(this.onNote.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(params: any) {
|
public async init(params: any) {
|
||||||
|
@ -36,29 +35,6 @@ export default class extends Channel {
|
||||||
(note.channelId != null && this.followingChannels.has(note.channelId))
|
(note.channelId != null && this.followingChannels.has(note.channelId))
|
||||||
)) return;
|
)) return;
|
||||||
|
|
||||||
if (['followers', 'specified'].includes(note.visibility)) {
|
|
||||||
note = await Notes.pack(note.id, this.user!, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (note.isHidden) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// リプライなら再pack
|
|
||||||
if (note.replyId != null) {
|
|
||||||
note.reply = await Notes.pack(note.replyId, this.user!, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Renoteなら再pack
|
|
||||||
if (note.renoteId != null) {
|
|
||||||
note.renote = await Notes.pack(note.renoteId, this.user!, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore notes from instances the user has muted
|
// Ignore notes from instances the user has muted
|
||||||
if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
|
if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import Channel from '../channel.js';
|
import Channel from '../channel.js';
|
||||||
import { fetchMeta } from '@/misc/fetch-meta.js';
|
import { fetchMeta } from '@/misc/fetch-meta.js';
|
||||||
import { Notes } from '@/models/index.js';
|
|
||||||
import { checkWordMute } from '@/misc/check-word-mute.js';
|
import { checkWordMute } from '@/misc/check-word-mute.js';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||||
import { Packed } from '@/misc/schema.js';
|
import { Packed } from '@/misc/schema.js';
|
||||||
|
@ -12,7 +11,7 @@ export default class extends Channel {
|
||||||
|
|
||||||
constructor(id: string, connection: Channel['connection']) {
|
constructor(id: string, connection: Channel['connection']) {
|
||||||
super(id, connection);
|
super(id, connection);
|
||||||
this.onNote = this.onNote.bind(this);
|
this.onNote = this.withPackedNote(this.onNote.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(params: any) {
|
public async init(params: any) {
|
||||||
|
@ -30,19 +29,6 @@ export default class extends Channel {
|
||||||
if (note.visibility !== 'public') return;
|
if (note.visibility !== 'public') return;
|
||||||
if (note.channelId != null && !this.followingChannels.has(note.channelId)) return;
|
if (note.channelId != null && !this.followingChannels.has(note.channelId)) return;
|
||||||
|
|
||||||
// リプライなら再pack
|
|
||||||
if (note.replyId != null) {
|
|
||||||
note.reply = await Notes.pack(note.replyId, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Renoteなら再pack
|
|
||||||
if (note.renoteId != null) {
|
|
||||||
note.renote = await Notes.pack(note.renoteId, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 関係ない返信は除外
|
// 関係ない返信は除外
|
||||||
if (note.reply && !this.user!.showTimelineReplies) {
|
if (note.reply && !this.user!.showTimelineReplies) {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import Channel from '../channel.js';
|
import Channel from '../channel.js';
|
||||||
import { Notes } from '@/models/index.js';
|
|
||||||
import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js';
|
import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js';
|
||||||
|
|
||||||
export default class extends Channel {
|
export default class extends Channel {
|
||||||
|
@ -16,26 +15,12 @@ export default class extends Channel {
|
||||||
if (isUserFromMutedInstance(data.body, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
|
if (isUserFromMutedInstance(data.body, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
|
||||||
if (data.body.userId && this.muting.has(data.body.userId)) return;
|
if (data.body.userId && this.muting.has(data.body.userId)) return;
|
||||||
|
|
||||||
if (data.body.note && data.body.note.isHidden) {
|
|
||||||
const note = await Notes.pack(data.body.note.id, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
this.connection.cacheNote(note);
|
|
||||||
data.body.note = note;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'mention': {
|
case 'mention': {
|
||||||
if (isInstanceMuted(data.body, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
|
if (isInstanceMuted(data.body, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
|
||||||
|
|
||||||
if (this.muting.has(data.body.userId)) return;
|
if (this.muting.has(data.body.userId)) return;
|
||||||
if (data.body.isHidden) {
|
|
||||||
const note = await Notes.pack(data.body.id, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
this.connection.cacheNote(note);
|
|
||||||
data.body = note;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Channel from '../channel.js';
|
import Channel from '../channel.js';
|
||||||
import { Notes, UserListJoinings, UserLists } from '@/models/index.js';
|
import { UserListJoinings, UserLists } from '@/models/index.js';
|
||||||
import { User } from '@/models/entities/user.js';
|
import { User } from '@/models/entities/user.js';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||||
import { Packed } from '@/misc/schema.js';
|
import { Packed } from '@/misc/schema.js';
|
||||||
|
@ -15,7 +15,7 @@ export default class extends Channel {
|
||||||
constructor(id: string, connection: Channel['connection']) {
|
constructor(id: string, connection: Channel['connection']) {
|
||||||
super(id, connection);
|
super(id, connection);
|
||||||
this.updateListUsers = this.updateListUsers.bind(this);
|
this.updateListUsers = this.updateListUsers.bind(this);
|
||||||
this.onNote = this.onNote.bind(this);
|
this.onNote = this.withPackedNote(this.onNote.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(params: any) {
|
public async init(params: any) {
|
||||||
|
@ -51,29 +51,6 @@ export default class extends Channel {
|
||||||
private async onNote(note: Packed<'Note'>) {
|
private async onNote(note: Packed<'Note'>) {
|
||||||
if (!this.listUsers.includes(note.userId)) return;
|
if (!this.listUsers.includes(note.userId)) return;
|
||||||
|
|
||||||
if (['followers', 'specified'].includes(note.visibility)) {
|
|
||||||
note = await Notes.pack(note.id, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (note.isHidden) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// リプライなら再pack
|
|
||||||
if (note.replyId != null) {
|
|
||||||
note.reply = await Notes.pack(note.replyId, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Renoteなら再pack
|
|
||||||
if (note.renoteId != null) {
|
|
||||||
note.renote = await Notes.pack(note.renoteId, this.user, {
|
|
||||||
detail: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
||||||
if (isUserRelated(note, this.muting)) return;
|
if (isUserRelated(note, this.muting)) return;
|
||||||
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
|
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
|
||||||
|
|
|
@ -243,7 +243,7 @@ export type StreamMessages = {
|
||||||
};
|
};
|
||||||
notes: {
|
notes: {
|
||||||
name: 'notesStream';
|
name: 'notesStream';
|
||||||
payload: Packed<'Note'>;
|
payload: Note;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -344,6 +344,8 @@ router.get('/notes/:note', async (ctx, next) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (note) {
|
if (note) {
|
||||||
|
try {
|
||||||
|
// FIXME: packing with detail may throw an error if the reply or renote is not visible (#8774)
|
||||||
const _note = await Notes.pack(note);
|
const _note = await Notes.pack(note);
|
||||||
const profile = await UserProfiles.findOneByOrFail({ userId: note.userId });
|
const profile = await UserProfiles.findOneByOrFail({ userId: note.userId });
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta();
|
||||||
|
@ -353,15 +355,21 @@ router.get('/notes/:note', async (ctx, next) => {
|
||||||
avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: note.userId })),
|
avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: note.userId })),
|
||||||
// TODO: Let locale changeable by instance setting
|
// TODO: Let locale changeable by instance setting
|
||||||
summary: getNoteSummary(_note),
|
summary: getNoteSummary(_note),
|
||||||
instanceName: meta.name || 'Calckey',
|
instanceName: meta.name || 'Misskey',
|
||||||
icon: meta.iconUrl,
|
icon: meta.iconUrl,
|
||||||
privateMode: meta.privateMode,
|
|
||||||
themeColor: meta.themeColor,
|
themeColor: meta.themeColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.set('Cache-Control', 'public, max-age=15');
|
ctx.set('Cache-Control', 'public, max-age=15');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') {
|
||||||
|
// note not visible to user
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
|
|
|
@ -345,19 +345,15 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pack the note
|
publishNotesStream(note);
|
||||||
const noteObj = await Notes.pack(note);
|
|
||||||
|
|
||||||
publishNotesStream(noteObj);
|
const webhooks = await getActiveWebhooks().then(webhooks => webhooks.filter(x => x.userId === user.id && x.on.includes('note')));
|
||||||
|
|
||||||
getActiveWebhooks().then(webhooks => {
|
|
||||||
webhooks = webhooks.filter(x => x.userId === user.id && x.on.includes('note'));
|
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, 'note', {
|
webhookDeliver(webhook, 'note', {
|
||||||
note: noteObj,
|
note: await Notes.pack(note, user),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const nm = new NotificationManager(user, note);
|
const nm = new NotificationManager(user, note);
|
||||||
const nmRelatedPromises = [];
|
const nmRelatedPromises = [];
|
||||||
|
@ -378,12 +374,14 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
|
|
||||||
if (!threadMuted) {
|
if (!threadMuted) {
|
||||||
nm.push(data.reply.userId, 'reply');
|
nm.push(data.reply.userId, 'reply');
|
||||||
publishMainStream(data.reply.userId, 'reply', noteObj);
|
|
||||||
|
const packedReply = await Notes.pack(note, { id: data.reply.userId });
|
||||||
|
publishMainStream(data.reply.userId, 'reply', packedReply);
|
||||||
|
|
||||||
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply'));
|
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, 'reply', {
|
webhookDeliver(webhook, 'reply', {
|
||||||
note: noteObj,
|
note: packedReply,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -404,12 +402,13 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
|
|
||||||
// Publish event
|
// Publish event
|
||||||
if ((user.id !== data.renote.userId) && data.renote.userHost === null) {
|
if ((user.id !== data.renote.userId) && data.renote.userHost === null) {
|
||||||
publishMainStream(data.renote.userId, 'renote', noteObj);
|
const packedRenote = await Notes.pack(note, { id: data.renote.userId });
|
||||||
|
publishMainStream(data.renote.userId, 'renote', packedRenote);
|
||||||
|
|
||||||
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote'));
|
const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
webhookDeliver(webhook, 'renote', {
|
webhookDeliver(webhook, 'renote', {
|
||||||
note: noteObj,
|
note: packedRenote,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -642,6 +641,8 @@ async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// note with "specified" visibility might not be visible to mentioned users
|
||||||
|
try {
|
||||||
const detailPackedNote = await Notes.pack(note, u, {
|
const detailPackedNote = await Notes.pack(note, u, {
|
||||||
detail: true,
|
detail: true,
|
||||||
});
|
});
|
||||||
|
@ -654,6 +655,10 @@ async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note,
|
||||||
note: detailPackedNote,
|
note: detailPackedNote,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') continue;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
// Create notification
|
// Create notification
|
||||||
nm.push(u.id, 'mention');
|
nm.push(u.id, 'mention');
|
||||||
|
|
|
@ -22,7 +22,6 @@ import {
|
||||||
UserListStreamTypes,
|
UserListStreamTypes,
|
||||||
UserStreamTypes,
|
UserStreamTypes,
|
||||||
} from '@/server/api/stream/types.js';
|
} from '@/server/api/stream/types.js';
|
||||||
import { Packed } from '@/misc/schema.js';
|
|
||||||
|
|
||||||
class Publisher {
|
class Publisher {
|
||||||
private publish = (channel: StreamChannels, type: string | null, value?: any): void => {
|
private publish = (channel: StreamChannels, type: string | null, value?: any): void => {
|
||||||
|
@ -87,7 +86,7 @@ class Publisher {
|
||||||
this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
||||||
};
|
};
|
||||||
|
|
||||||
public publishNotesStream = (note: Packed<'Note'>): void => {
|
public publishNotesStream = (note: Note): void => {
|
||||||
this.publish('notesStream', null, note);
|
this.publish('notesStream', null, note);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -154,18 +154,18 @@ describe('API visibility', () => {
|
||||||
|
|
||||||
it('[show] followers-postを非フォロワーが見れない', async(async () => {
|
it('[show] followers-postを非フォロワーが見れない', async(async () => {
|
||||||
const res = await show(fol.id, other);
|
const res = await show(fol.id, other);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('[show] followers-postを未認証が見れない', async(async () => {
|
it('[show] followers-postを未認証が見れない', async(async () => {
|
||||||
const res = await show(fol.id, null);
|
const res = await show(fol.id, null);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// specified
|
// specified
|
||||||
it('[show] specified-postを自分が見れる', async(async () => {
|
it('[show] specified-postを自分が見れる', async(async () => {
|
||||||
const res = await show(spe.id, alice);
|
const res = await show(spe.id, alice);
|
||||||
assert.strictEqual(res.body.text, 'x');
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('[show] specified-postを指定ユーザーが見れる', async(async () => {
|
it('[show] specified-postを指定ユーザーが見れる', async(async () => {
|
||||||
|
@ -175,17 +175,17 @@ describe('API visibility', () => {
|
||||||
|
|
||||||
it('[show] specified-postをフォロワーが見れない', async(async () => {
|
it('[show] specified-postをフォロワーが見れない', async(async () => {
|
||||||
const res = await show(spe.id, follower);
|
const res = await show(spe.id, follower);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('[show] specified-postを非フォロワーが見れない', async(async () => {
|
it('[show] specified-postを非フォロワーが見れない', async(async () => {
|
||||||
const res = await show(spe.id, other);
|
const res = await show(spe.id, other);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('[show] specified-postを未認証が見れない', async(async () => {
|
it('[show] specified-postを未認証が見れない', async(async () => {
|
||||||
const res = await show(spe.id, null);
|
const res = await show(spe.id, null);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -260,12 +260,12 @@ describe('API visibility', () => {
|
||||||
|
|
||||||
it('[show] followers-replyを非フォロワーが見れない', async(async () => {
|
it('[show] followers-replyを非フォロワーが見れない', async(async () => {
|
||||||
const res = await show(folR.id, other);
|
const res = await show(folR.id, other);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('[show] followers-replyを未認証が見れない', async(async () => {
|
it('[show] followers-replyを未認証が見れない', async(async () => {
|
||||||
const res = await show(folR.id, null);
|
const res = await show(folR.id, null);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// specified
|
// specified
|
||||||
|
@ -286,17 +286,17 @@ describe('API visibility', () => {
|
||||||
|
|
||||||
it('[show] specified-replyをフォロワーが見れない', async(async () => {
|
it('[show] specified-replyをフォロワーが見れない', async(async () => {
|
||||||
const res = await show(speR.id, follower);
|
const res = await show(speR.id, follower);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('[show] specified-replyを非フォロワーが見れない', async(async () => {
|
it('[show] specified-replyを非フォロワーが見れない', async(async () => {
|
||||||
const res = await show(speR.id, other);
|
const res = await show(speR.id, other);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('[show] specified-replyを未認証が見れない', async(async () => {
|
it('[show] specified-replyを未認証が見れない', async(async () => {
|
||||||
const res = await show(speR.id, null);
|
const res = await show(speR.id, null);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -371,12 +371,12 @@ describe('API visibility', () => {
|
||||||
|
|
||||||
it('[show] followers-mentionを非フォロワーが見れない', async(async () => {
|
it('[show] followers-mentionを非フォロワーが見れない', async(async () => {
|
||||||
const res = await show(folM.id, other);
|
const res = await show(folM.id, other);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('[show] followers-mentionを未認証が見れない', async(async () => {
|
it('[show] followers-mentionを未認証が見れない', async(async () => {
|
||||||
const res = await show(folM.id, null);
|
const res = await show(folM.id, null);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// specified
|
// specified
|
||||||
|
@ -392,22 +392,22 @@ describe('API visibility', () => {
|
||||||
|
|
||||||
it('[show] specified-mentionをされた人が指定されてなかったら見れない', async(async () => {
|
it('[show] specified-mentionをされた人が指定されてなかったら見れない', async(async () => {
|
||||||
const res = await show(speM.id, target2);
|
const res = await show(speM.id, target2);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('[show] specified-mentionをフォロワーが見れない', async(async () => {
|
it('[show] specified-mentionをフォロワーが見れない', async(async () => {
|
||||||
const res = await show(speM.id, follower);
|
const res = await show(speM.id, follower);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('[show] specified-mentionを非フォロワーが見れない', async(async () => {
|
it('[show] specified-mentionを非フォロワーが見れない', async(async () => {
|
||||||
const res = await show(speM.id, other);
|
const res = await show(speM.id, other);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('[show] specified-mentionを未認証が見れない', async(async () => {
|
it('[show] specified-mentionを未認証が見れない', async(async () => {
|
||||||
const res = await show(speM.id, null);
|
const res = await show(speM.id, null);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.status, 404);
|
||||||
}));
|
}));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,6 @@
|
||||||
</p>
|
</p>
|
||||||
<div v-show="appearNote.cw == null || showContent" class="content">
|
<div v-show="appearNote.cw == null || showContent" class="content">
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
|
||||||
<MkA v-if="appearNote.replyId" class="reply" :to="`/notes/${appearNote.replyId}`"><i class="fas fa-reply"></i></MkA>
|
<MkA v-if="appearNote.replyId" class="reply" :to="`/notes/${appearNote.replyId}`"><i class="fas fa-reply"></i></MkA>
|
||||||
<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
|
<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
|
||||||
<a v-if="appearNote.renote != null" class="rp">RN:</a>
|
<a v-if="appearNote.renote != null" class="rp">RN:</a>
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
</p>
|
</p>
|
||||||
<div v-show="appearNote.cw == null || showContent" class="content" :class="{ collapsed }">
|
<div v-show="appearNote.cw == null || showContent" class="content" :class="{ collapsed }">
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
|
||||||
<MkA v-if="appearNote.replyId" class="reply" :to="`/notes/${appearNote.replyId}`"><i class="fas fa-reply"></i></MkA>
|
<MkA v-if="appearNote.replyId" class="reply" :to="`/notes/${appearNote.replyId}`"><i class="fas fa-reply"></i></MkA>
|
||||||
<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
|
<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
|
||||||
<a v-if="appearNote.renote != null" class="rp">RN:</a>
|
<a v-if="appearNote.renote != null" class="rp">RN:</a>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="wrmlmaau" :class="{ collapsed }">
|
<div class="wrmlmaau" :class="{ collapsed }">
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<span v-if="note.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
|
||||||
<span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deleted }})</span>
|
<span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deleted }})</span>
|
||||||
<MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="fas fa-reply"></i></MkA>
|
<MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="fas fa-reply"></i></MkA>
|
||||||
<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i" :custom-emojis="note.emojis"/>
|
<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i" :custom-emojis="note.emojis"/>
|
||||||
|
|
|
@ -10,10 +10,6 @@ export const getNoteSummary = (note: misskey.entities.Note): string => {
|
||||||
return `(${i18n.ts.deletedNote})`;
|
return `(${i18n.ts.deletedNote})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.isHidden) {
|
|
||||||
return `(${i18n.ts.invisibleNote})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let summary = '';
|
let summary = '';
|
||||||
|
|
||||||
// 本文
|
// 本文
|
||||||
|
|
Loading…
Reference in New Issue