Improve: unclip (#8823)
* Refactor clip page to use Composition API * Refactor clip page * Refactor clip page * Refactor clip page * Improve: unclip * Fix unclip * Fix unclip * chore: better type and name * Fix * Fix clipPage vue provider Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
		@@ -99,6 +99,7 @@ import * as ep___charts_user_notes from './endpoints/charts/user/notes.js';
 | 
			
		||||
import * as ep___charts_user_reactions from './endpoints/charts/user/reactions.js';
 | 
			
		||||
import * as ep___charts_users from './endpoints/charts/users.js';
 | 
			
		||||
import * as ep___clips_addNote from './endpoints/clips/add-note.js';
 | 
			
		||||
import * as ep___clips_removeNote from './endpoints/clips/remove-note.js';
 | 
			
		||||
import * as ep___clips_create from './endpoints/clips/create.js';
 | 
			
		||||
import * as ep___clips_delete from './endpoints/clips/delete.js';
 | 
			
		||||
import * as ep___clips_list from './endpoints/clips/list.js';
 | 
			
		||||
@@ -409,6 +410,7 @@ const eps = [
 | 
			
		||||
	['charts/user/reactions', ep___charts_user_reactions],
 | 
			
		||||
	['charts/users', ep___charts_users],
 | 
			
		||||
	['clips/add-note', ep___clips_addNote],
 | 
			
		||||
	['clips/remove-note', ep___clips_removeNote],
 | 
			
		||||
	['clips/create', ep___clips_create],
 | 
			
		||||
	['clips/delete', ep___clips_delete],
 | 
			
		||||
	['clips/list', ep___clips_list],
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,57 @@
 | 
			
		||||
import define from '../../define.js';
 | 
			
		||||
import { ClipNotes, Clips } from '@/models/index.js';
 | 
			
		||||
import { ApiError } from '../../error.js';
 | 
			
		||||
import { getNote } from '../../common/getters.js';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	tags: ['account', 'notes', 'clips'],
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
 | 
			
		||||
	kind: 'write:account',
 | 
			
		||||
 | 
			
		||||
	errors: {
 | 
			
		||||
		noSuchClip: {
 | 
			
		||||
			message: 'No such clip.',
 | 
			
		||||
			code: 'NO_SUCH_CLIP',
 | 
			
		||||
			id: 'b80525c6-97f7-49d7-a42d-ebccd49cfd52',
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		noSuchNote: {
 | 
			
		||||
			message: 'No such note.',
 | 
			
		||||
			code: 'NO_SUCH_NOTE',
 | 
			
		||||
			id: 'aff017de-190e-434b-893e-33a9ff5049d8',
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
export const paramDef = {
 | 
			
		||||
	type: 'object',
 | 
			
		||||
	properties: {
 | 
			
		||||
		clipId: { type: 'string', format: 'misskey:id' },
 | 
			
		||||
		noteId: { type: 'string', format: 'misskey:id' },
 | 
			
		||||
	},
 | 
			
		||||
	required: ['clipId', 'noteId'],
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line import/no-default-export
 | 
			
		||||
export default define(meta, paramDef, async (ps, user) => {
 | 
			
		||||
	const clip = await Clips.findOneBy({
 | 
			
		||||
		id: ps.clipId,
 | 
			
		||||
		userId: user.id,
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (clip == null) {
 | 
			
		||||
		throw new ApiError(meta.errors.noSuchClip);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const note = await getNote(ps.noteId).catch(e => {
 | 
			
		||||
		if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
 | 
			
		||||
		throw e;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	await ClipNotes.delete({
 | 
			
		||||
		noteId: note.id,
 | 
			
		||||
		clipId: clip.id,
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
@@ -251,12 +251,12 @@ function onContextmenu(ev: MouseEvent): void {
 | 
			
		||||
		ev.preventDefault();
 | 
			
		||||
		react();
 | 
			
		||||
	} else {
 | 
			
		||||
		os.contextMenu(getNoteMenu({ note: note, translating, translation, menuButton }), ev).then(focus);
 | 
			
		||||
		os.contextMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted }), ev).then(focus);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function menu(viaKeyboard = false): void {
 | 
			
		||||
	os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton }), menuButton.value, {
 | 
			
		||||
	os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted }), menuButton.value, {
 | 
			
		||||
		viaKeyboard,
 | 
			
		||||
	}).then(focus);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -105,7 +105,7 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed, inject, onMounted, onUnmounted, reactive, ref } from 'vue';
 | 
			
		||||
import { computed, inject, onMounted, onUnmounted, reactive, ref, Ref } from 'vue';
 | 
			
		||||
import * as mfm from 'mfm-js';
 | 
			
		||||
import * as misskey from 'misskey-js';
 | 
			
		||||
import MkNoteSub from './MkNoteSub.vue';
 | 
			
		||||
@@ -225,6 +225,8 @@ function undoReact(note): void {
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const cullentClipPage = inject<Ref<misskey.entities.Clip>>('cullentClipPage');
 | 
			
		||||
 | 
			
		||||
function onContextmenu(ev: MouseEvent): void {
 | 
			
		||||
	const isLink = (el: HTMLElement) => {
 | 
			
		||||
		if (el.tagName === 'A') return true;
 | 
			
		||||
@@ -239,12 +241,12 @@ function onContextmenu(ev: MouseEvent): void {
 | 
			
		||||
		ev.preventDefault();
 | 
			
		||||
		react();
 | 
			
		||||
	} else {
 | 
			
		||||
		os.contextMenu(getNoteMenu({ note: note, translating, translation, menuButton }), ev).then(focus);
 | 
			
		||||
		os.contextMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, cullentClipPage }), ev).then(focus);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function menu(viaKeyboard = false): void {
 | 
			
		||||
	os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton }), menuButton.value, {
 | 
			
		||||
	os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, cullentClipPage }), menuButton.value, {
 | 
			
		||||
		viaKeyboard,
 | 
			
		||||
	}).then(focus);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed, watch } from 'vue';
 | 
			
		||||
import { computed, watch, provide } from 'vue';
 | 
			
		||||
import * as misskey from 'misskey-js';
 | 
			
		||||
import XNotes from '@/components/notes.vue';
 | 
			
		||||
import { $i } from '@/account';
 | 
			
		||||
@@ -47,6 +47,8 @@ watch(() => props.clipId, async () => {
 | 
			
		||||
	immediate: true,
 | 
			
		||||
}); 
 | 
			
		||||
 | 
			
		||||
provide('cullentClipPage', $$(clip));
 | 
			
		||||
 | 
			
		||||
defineExpose({
 | 
			
		||||
	[symbols.PAGE_INFO]: computed(() => clip ? {
 | 
			
		||||
		title: clip.name,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { defineAsyncComponent, Ref } from 'vue';
 | 
			
		||||
import { defineAsyncComponent, Ref, inject } from 'vue';
 | 
			
		||||
import * as misskey from 'misskey-js';
 | 
			
		||||
import { $i } from '@/account';
 | 
			
		||||
import { i18n } from '@/i18n';
 | 
			
		||||
@@ -14,6 +14,8 @@ export function getNoteMenu(props: {
 | 
			
		||||
	menuButton: Ref<HTMLElement>;
 | 
			
		||||
	translation: Ref<any>;
 | 
			
		||||
	translating: Ref<boolean>;
 | 
			
		||||
	isDeleted: Ref<boolean>;
 | 
			
		||||
	cullentClipPage?: Ref<misskey.entities.Clip>;
 | 
			
		||||
}) {
 | 
			
		||||
	const isRenote = (
 | 
			
		||||
		props.note.renote != null &&
 | 
			
		||||
@@ -125,12 +127,37 @@ export function getNoteMenu(props: {
 | 
			
		||||
		}, null, ...clips.map(clip => ({
 | 
			
		||||
			text: clip.name,
 | 
			
		||||
			action: () => {
 | 
			
		||||
				os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id });
 | 
			
		||||
				os.promiseDialog(
 | 
			
		||||
					os.api('clips/add-note', { clipId: clip.id, noteId: appearNote.id }),
 | 
			
		||||
					null,
 | 
			
		||||
					async (err) => {
 | 
			
		||||
						if (err.id === '734806c4-542c-463a-9311-15c512803965') {
 | 
			
		||||
							const confirm = await os.confirm({
 | 
			
		||||
								type: 'warning',
 | 
			
		||||
								text: i18n.t('confirmToUnclipAlreadyClippedNote', { name: clip.name }),
 | 
			
		||||
							});
 | 
			
		||||
							if (!confirm.canceled) {
 | 
			
		||||
								os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id });
 | 
			
		||||
								if (props.cullentClipPage?.value.id === clip.id) props.isDeleted.value = true;
 | 
			
		||||
							}
 | 
			
		||||
						} else {
 | 
			
		||||
							os.alert({
 | 
			
		||||
								type: 'error',
 | 
			
		||||
								text: err.message + '\n' + err.id,
 | 
			
		||||
							});
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		}))], props.menuButton.value, {
 | 
			
		||||
		}).then(focus);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	async function unclip(): Promise<void> {
 | 
			
		||||
		os.apiWithDialog('clips/remove-note', { clipId: props.cullentClipPage.value.id, noteId: appearNote.id });
 | 
			
		||||
		props.isDeleted.value = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	async function promote(): Promise<void> {
 | 
			
		||||
		const { canceled, result: days } = await os.inputNumber({
 | 
			
		||||
			title: i18n.ts.numberOfDays,
 | 
			
		||||
@@ -169,7 +196,16 @@ export function getNoteMenu(props: {
 | 
			
		||||
			noteId: appearNote.id
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		menu = [{
 | 
			
		||||
		menu = [
 | 
			
		||||
		...(
 | 
			
		||||
			props.cullentClipPage?.value.userId === $i.id ? [{
 | 
			
		||||
				icon: 'fas fa-circle-minus',
 | 
			
		||||
				text: i18n.ts.unclip,
 | 
			
		||||
				danger: true,
 | 
			
		||||
				action: unclip,
 | 
			
		||||
			}, null] : []
 | 
			
		||||
		),
 | 
			
		||||
		{
 | 
			
		||||
			icon: 'fas fa-copy',
 | 
			
		||||
			text: i18n.ts.copyContent,
 | 
			
		||||
			action: copyContent
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user