diff options
| author | futchitwo <74236683+futchitwo@users.noreply.github.com> | 2022-06-18 18:27:09 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-06-18 18:27:09 +0900 |
| commit | 5b7595d9d7e313271c692a849fc915e73fcea108 (patch) | |
| tree | 2e20578ce771986d813bba100004469f37c65b24 /packages | |
| parent | Refactor clip page to Composition API (#8822) (diff) | |
| download | misskey-5b7595d9d7e313271c692a849fc915e73fcea108.tar.gz misskey-5b7595d9d7e313271c692a849fc915e73fcea108.tar.bz2 misskey-5b7595d9d7e313271c692a849fc915e73fcea108.zip | |
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>
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/backend/src/server/api/endpoints.ts | 2 | ||||
| -rw-r--r-- | packages/backend/src/server/api/endpoints/clips/remove-note.ts | 57 | ||||
| -rw-r--r-- | packages/client/src/components/note-detailed.vue | 4 | ||||
| -rw-r--r-- | packages/client/src/components/note.vue | 8 | ||||
| -rw-r--r-- | packages/client/src/pages/clip.vue | 4 | ||||
| -rw-r--r-- | packages/client/src/scripts/get-note-menu.ts | 42 |
6 files changed, 108 insertions, 9 deletions
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 1e7afd8cdd..5fac7df239 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -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], diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts new file mode 100644 index 0000000000..8b90e31f65 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -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,
+ });
+});
diff --git a/packages/client/src/components/note-detailed.vue b/packages/client/src/components/note-detailed.vue index 6234b710d2..ba47bfcd4a 100644 --- a/packages/client/src/components/note-detailed.vue +++ b/packages/client/src/components/note-detailed.vue @@ -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); } diff --git a/packages/client/src/components/note.vue b/packages/client/src/components/note.vue index e5744d1ce9..b06f4edd0a 100644 --- a/packages/client/src/components/note.vue +++ b/packages/client/src/components/note.vue @@ -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); } diff --git a/packages/client/src/pages/clip.vue b/packages/client/src/pages/clip.vue index cfe11efd1c..bfe1578b13 100644 --- a/packages/client/src/pages/clip.vue +++ b/packages/client/src/pages/clip.vue @@ -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, diff --git a/packages/client/src/scripts/get-note-menu.ts b/packages/client/src/scripts/get-note-menu.ts index 78749ad6bb..e56d519493 100644 --- a/packages/client/src/scripts/get-note-menu.ts +++ b/packages/client/src/scripts/get-note-menu.ts @@ -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 |