Skip to content

Commit

Permalink
feat: アンテナからノートを削除
Browse files Browse the repository at this point in the history
  • Loading branch information
tai-cha committed Feb 4, 2025
1 parent 0c634c9 commit e29dbca
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 3 deletions.
4 changes: 4 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5254,6 +5254,10 @@ export interface Locale extends ILocale {
* このサーバーは連合が無効化されています。他のサーバーのユーザーとやり取りすることはできません。
*/
"federationDisabled": string;
/**
* アンテナからノートを消す
*/
"removeNoteFromAntenna": string;
"_accountSettings": {
/**
* コンテンツの表示にログインを必須にする
Expand Down
1 change: 1 addition & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,7 @@ availableRoles: "利用可能なロール"
acknowledgeNotesAndEnable: "注意事項を理解した上でオンにします。"
federationSpecified: "このサーバーはホワイトリスト連合で運用されています。管理者が指定したサーバー以外とやり取りすることはできません。"
federationDisabled: "このサーバーは連合が無効化されています。他のサーバーのユーザーとやり取りすることはできません。"
removeNoteFromAntenna: "アンテナからノートを消す"

_accountSettings:
requireSigninToViewContents: "コンテンツの表示にログインを必須にする"
Expand Down
4 changes: 4 additions & 0 deletions packages/backend/src/core/AntennaService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ export class AntennaService implements OnApplicationShutdown {
redisPipeline.exec();
}

@bindThis async removeNoteFromAntenna(antena: MiAntenna, note: MiNote): Promise<void> {
this.fanoutTimelineService.remove(`antennaTimeline:${antena.id}`, note.id);
}

// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている

@bindThis
Expand Down
5 changes: 5 additions & 0 deletions packages/backend/src/core/FanoutTimelineService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,9 @@ export class FanoutTimelineService {
public purge(name: FanoutTimelineName) {
return this.redisForTimelines.del('list:' + name);
}

@bindThis
public remove(name: FanoutTimelineName, id: string) {
return this.redisForTimelines.lrem('list:' + name, 0, id);
}
}
1 change: 1 addition & 0 deletions packages/backend/src/server/api/endpoint-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export * as 'antennas/list' from './endpoints/antennas/list.js';
export * as 'antennas/notes' from './endpoints/antennas/notes.js';
export * as 'antennas/show' from './endpoints/antennas/show.js';
export * as 'antennas/update' from './endpoints/antennas/update.js';
export * as 'antennas/delete-note' from './endpoints/antennas/delete-note.js';
export * as 'ap/get' from './endpoints/ap/get.js';
export * as 'ap/show' from './endpoints/ap/show.js';
export * as 'app/create' from './endpoints/app/create.js';
Expand Down
76 changes: 76 additions & 0 deletions packages/backend/src/server/api/endpoints/antennas/delete-note.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { AntennasRepository, NotesRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { AntennaService } from '@/core/AntennaService.js';
import { ApiError } from '../../error.js';

export const meta = {
tags: ['antennas'],

requireCredential: true,

kind: 'write:account',

errors: {
noSuchAntenna: {
message: 'No such antenna.',
code: 'NO_SUCH_ANTENNA',
id: 'b34dcf9d-348f-44bb-99d0-6c9314cfe2df',
},

noSuchNote: {
message: 'No such note.',
code: 'NO_SUCH_NOTE',
id: '490be23f-8c1f-4796-819f-94cb4f9d1630',
},
},
} as const;

export const paramDef = {
type: 'object',
properties: {
antennaId: { type: 'string', format: 'misskey:id' },
noteId: { type: 'string', format: 'misskey:id' },
},
required: ['antennaId', 'noteId'],
} as const;

@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.antennasRepository)
private antennasRepository: AntennasRepository,

@Inject(DI.notesRepository)
private notesRepository: NotesRepository,

private antennaService: AntennaService,
) {
super(meta, paramDef, async (ps, me) => {
const antenna = await this.antennasRepository.findOneBy({
id: ps.antennaId,
userId: me.id,
});

if (antenna == null) {
throw new ApiError(meta.errors.noSuchAntenna);
}

const note = await this.notesRepository.findOneBy({
id: ps.noteId,
});

if (note == null) {
throw new ApiError(meta.errors.noSuchNote);
}

await this.antennaService.removeNoteFromAntenna(antenna, note);
});
}
}
5 changes: 3 additions & 2 deletions packages/frontend/src/components/MkNote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ const inTimeline = inject<boolean>('inTimeline', false);
const tl_withSensitive = inject<Ref<boolean>>('tl_withSensitive', ref(true));
const inChannel = inject('inChannel', null);
const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
const currentAntenna = inject<Ref<Misskey.entities.Antenna> | null>('currentAntenna', null);

const note = ref(deepClone(props.note));

Expand Down Expand Up @@ -542,7 +543,7 @@ function onContextmenu(ev: MouseEvent): void {
ev.preventDefault();
react();
} else {
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value });
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value, currentAntenna: currentAntenna?.value });
os.contextMenu(menu, ev).then(focus).finally(cleanup);
}
}
Expand All @@ -552,7 +553,7 @@ function showMenu(): void {
return;
}

const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value });
const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value, currentAntenna: currentAntenna?.value });
os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup);
}

Expand Down
4 changes: 3 additions & 1 deletion packages/frontend/src/pages/antenna-timeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>

<script lang="ts" setup>
import { computed, watch, ref, shallowRef } from 'vue';
import { computed, watch, ref, shallowRef, provide } from 'vue';
import * as Misskey from 'misskey-js';
import MkTimeline from '@/components/MkTimeline.vue';
import { scroll } from '@@/js/scroll.js';
Expand Down Expand Up @@ -70,6 +70,8 @@ function focus() {
tlEl.value.focus();
}

provide('currentAntenna', antenna);

watch(() => props.antennaId, async () => {
antenna.value = await misskeyApi('antennas/show', {
antennaId: props.antennaId,
Expand Down
19 changes: 19 additions & 0 deletions packages/frontend/src/scripts/get-note-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export function getNoteMenu(props: {
translating: Ref<boolean>;
isDeleted: Ref<boolean>;
currentClip?: Misskey.entities.Clip;
currentAntenna?: Misskey.entities.Antenna;
}) {
const appearNote = getAppearNote(props.note);

Expand Down Expand Up @@ -253,6 +254,15 @@ export function getNoteMenu(props: {
props.isDeleted.value = true;
}

async function removeNoteFromAntenna(): Promise<void> {
if (props.currentAntenna == null) return;
os.apiWithDialog('antennas/delete-note', {
antennaId: props.currentAntenna.id,
noteId: appearNote.id,
});
props.isDeleted.value = true;
}

async function promote(): Promise<void> {
const { canceled, result: days } = await os.inputNumber({
title: i18n.ts.numberOfDays,
Expand Down Expand Up @@ -305,6 +315,15 @@ export function getNoteMenu(props: {
}, { type: 'divider' });
}

if (props.currentAntenna != null) {
menuItems.push({
icon: 'ti ti-backspace',
text: i18n.ts.removeNoteFromAntenna,
danger: true,
action: removeNoteFromAntenna,
}, { type: 'divider' });
}

menuItems.push({
icon: 'ti ti-info-circle',
text: i18n.ts.details,
Expand Down

0 comments on commit e29dbca

Please sign in to comment.