From 69cb1c522089d24a6131d869f09ad161fbf9518b Mon Sep 17 00:00:00 2001 From: Natty Date: Wed, 27 Dec 2023 03:50:56 +0100 Subject: [PATCH] Render all notes via Magnetar --- .../client/src/components/MagNote.vue | 36 +- .../client/src/components/MagNoteDetailed.vue | 113 ++- .../{MkNoteHeader.vue => MagNoteHeader.vue} | 46 +- .../src/components/MagNoteResolvingProxy.vue | 44 + .../{MkNoteSimple.vue => MagNoteSimple.vue} | 9 +- .../{MkNoteSub.vue => MagNoteSub.vue} | 92 +- .../components/{MkPoll.vue => MagPoll.vue} | 82 +- .../{MkQuoteButton.vue => MagQuoteButton.vue} | 3 +- ...MkRenoteButton.vue => MagRenoteButton.vue} | 45 +- ...bNoteContent.vue => MagSubNoteContent.vue} | 96 +- .../src/components/MkFollowApproveButton.vue | 7 +- .../frontend/client/src/components/MkNote.vue | 941 ------------------ .../client/src/components/MkNotes.vue | 8 +- .../client/src/components/MkNotification.vue | 4 +- .../client/src/components/MkNotifications.vue | 20 +- .../client/src/components/MkPostForm.vue | 55 +- .../src/components/MkReactionsViewer.vue | 42 +- fe_calckey/frontend/client/src/i18n.ts | 5 +- .../client/src/pages/about-magnetar.vue | 8 - .../frontend/client/src/pages/favorites.vue | 4 +- .../page-editor/els/page-editor.el.note.vue | 2 +- .../frontend/client/src/pages/user/clips.vue | 4 +- .../client/src/pages/user/follow-list.vue | 4 +- .../client/src/pages/user/followers.vue | 23 +- .../client/src/pages/user/following.vue | 23 +- .../client/src/pages/user/gallery.vue | 4 +- .../frontend/client/src/pages/user/home.vue | 71 +- .../client/src/pages/user/index.activity.vue | 5 +- .../client/src/pages/user/index.photos.vue | 3 +- .../client/src/pages/user/index.timeline.vue | 6 +- .../frontend/client/src/pages/user/index.vue | 78 +- .../frontend/client/src/pages/user/pages.vue | 4 +- .../client/src/pages/user/reactions.vue | 4 +- .../client/src/pages/welcome.timeline.vue | 27 +- .../client/src/scripts-mag/mag-util.ts | 10 +- .../client/src/scripts/get-note-menu.ts | 91 +- .../client/src/scripts/get-user-menu.ts | 118 ++- .../client/src/scripts/page-metadata.ts | 2 +- .../client/src/scripts/reaction-picker.ts | 2 +- .../client/src/scripts/use-note-capture.ts | 241 ++--- .../frontend/magnetar-common/src/types.ts | 1 + .../magnetar-common/src/types/MovedTo.ts | 3 + .../src/types/UserProfileExt.ts | 3 +- magnetar_sdk/src/types/user.rs | 10 +- src/model/data/user.rs | 6 +- src/model/processing/user.rs | 26 +- 46 files changed, 675 insertions(+), 1756 deletions(-) rename fe_calckey/frontend/client/src/components/{MkNoteHeader.vue => MagNoteHeader.vue} (72%) create mode 100644 fe_calckey/frontend/client/src/components/MagNoteResolvingProxy.vue rename fe_calckey/frontend/client/src/components/{MkNoteSimple.vue => MagNoteSimple.vue} (80%) rename fe_calckey/frontend/client/src/components/{MkNoteSub.vue => MagNoteSub.vue} (90%) rename fe_calckey/frontend/client/src/components/{MkPoll.vue => MagPoll.vue} (70%) rename fe_calckey/frontend/client/src/components/{MkQuoteButton.vue => MagQuoteButton.vue} (92%) rename fe_calckey/frontend/client/src/components/{MkRenoteButton.vue => MagRenoteButton.vue} (86%) rename fe_calckey/frontend/client/src/components/{MkSubNoteContent.vue => MagSubNoteContent.vue} (78%) delete mode 100644 fe_calckey/frontend/client/src/components/MkNote.vue create mode 100644 fe_calckey/frontend/magnetar-common/src/types/MovedTo.ts diff --git a/fe_calckey/frontend/client/src/components/MagNote.vue b/fe_calckey/frontend/client/src/components/MagNote.vue index 66c6c54..93bf6c3 100644 --- a/fe_calckey/frontend/client/src/components/MagNote.vue +++ b/fe_calckey/frontend/client/src/components/MagNote.vue @@ -11,7 +11,7 @@ :class="{ renote: isRenote }" :id="appearNote.id" > -
- + >
@@ -229,14 +229,14 @@ import type { Ref } from "vue"; import { computed, inject, onMounted, ref, toRaw } from "vue"; import * as mfm from "mfm-js"; import type * as misskey from "calckey-js"; -import MkNoteSub from "@/components/MkNoteSub.vue"; -import MkSubNoteContent from "./MkSubNoteContent.vue"; -import XNoteHeader from "@/components/MkNoteHeader.vue"; -import XRenoteButton from "@/components/MkRenoteButton.vue"; +import XNoteSub from "@/components/MagNoteSub.vue"; +import XSubNoteContent from "./MagSubNoteContent.vue"; +import XNoteHeader from "@/components/MagNoteHeader.vue"; +import XRenoteButton from "@/components/MagRenoteButton.vue"; import XReactionsViewer from "@/components/MkReactionsViewer.vue"; import XStarButton from "@/components/MkStarButton.vue"; import XStarButtonNoEmoji from "@/components/MkStarButtonNoEmoji.vue"; -import XQuoteButton from "@/components/MkQuoteButton.vue"; +import XQuoteButton from "@/components/MagQuoteButton.vue"; import MkVisibility from "@/components/MkVisibility.vue"; import copyToClipboard from "@/scripts/copy-to-clipboard"; import { url } from "@/config"; @@ -298,13 +298,13 @@ if (noteViewInterruptors.length > 0) { const isRenote = magIsRenote(note); -const el = ref(); -const footerEl = ref(); -const menuButton = ref(); +const el = ref(null); +const footerEl = ref(null); +const menuButton = ref(null); const starButton = ref>(); const renoteButton = ref>(); -const renoteTime = ref(); -const reactButton = ref(); +const renoteTime = ref(null); +const reactButton = ref(null); let appearNote = $computed( () => magEffectiveNote(note) as packed.PackNoteMaybeFull ); @@ -386,7 +386,7 @@ function onContextmenu(ev: MouseEvent): void { return isLink(el.parentElement); } }; - if (isLink(ev.target)) return; + if (ev.target && isLink(ev.target as HTMLElement)) return; if (window.getSelection()?.toString() !== "") return; if (defaultStore.state.useReactionPickerForContextMenu) { @@ -451,11 +451,11 @@ function menu(viaKeyboard = false): void { note: note, translating, translation, - menuButton, + menuButton: menuButton.value, isDeleted, - currentClipPage, + currentClipPage: currentClipPage?.value, }), - menuButton.value, + menuButton.value ?? undefined, { viaKeyboard, } @@ -478,7 +478,7 @@ function showRenoteMenu(viaKeyboard = false): void { }, }, ], - renoteTime.value, + renoteTime.value ?? undefined, { viaKeyboard: viaKeyboard, } diff --git a/fe_calckey/frontend/client/src/components/MagNoteDetailed.vue b/fe_calckey/frontend/client/src/components/MagNoteDetailed.vue index 56e1269..4da7c78 100644 --- a/fe_calckey/frontend/client/src/components/MagNoteDetailed.vue +++ b/fe_calckey/frontend/client/src/components/MagNoteDetailed.vue @@ -9,7 +9,7 @@ :tabindex="!isDeleted ? '-1' : null" :class="{ renote: magIsRenote(note) }" > - - - + > - - - +
-
- +
+
@@ -40,10 +43,18 @@ import { defineComponent } from "vue"; import XReactionsViewer from "@/components/MkReactionsViewer.vue"; import XMediaList from "@/components/MkMediaList.vue"; -import XPoll from "@/components/MkPoll.vue"; +import XPoll from "@/components/MagPoll.vue"; import * as os from "@/os"; +import { packed } from "magnetar-common"; +import { resolveNote } from "@/scripts-mag/mag-util"; +import { $i } from "@/account"; export default defineComponent({ + computed: { + $i() { + return $i; + }, + }, components: { XReactionsViewer, XMediaList, @@ -52,14 +63,14 @@ export default defineComponent({ data() { return { - notes: [], + notes: [] as packed.PackNoteMaybeFull[], isScrolling: false, }; }, created() { - os.api("notes/featured").then((notes) => { - this.notes = notes; + os.api("notes/featured").then(async (notes) => { + this.notes = await Promise.all(notes.map(resolveNote)); }); }, diff --git a/fe_calckey/frontend/client/src/scripts-mag/mag-util.ts b/fe_calckey/frontend/client/src/scripts-mag/mag-util.ts index 226ec91..1e2e38e 100644 --- a/fe_calckey/frontend/client/src/scripts-mag/mag-util.ts +++ b/fe_calckey/frontend/client/src/scripts-mag/mag-util.ts @@ -1,6 +1,7 @@ import * as Misskey from "calckey-js"; -import { packed, types } from "magnetar-common"; +import { endpoints, packed, types } from "magnetar-common"; import { UnicodeEmojiDef } from "@/scripts/emojilist"; +import * as os from "@/os"; // https://stackoverflow.com/a/50375286 Evil magic type Dist = U extends any ? (k: U) => void : never; @@ -331,3 +332,10 @@ export function magReactionIndex( return magReactionEquals(r, reactionType); }); } + +export const resolveNote = async ({ id }: { id: string }) => + os.magApi( + endpoints.GetNoteById, + { attachments: true, context: true }, + { id } + ); diff --git a/fe_calckey/frontend/client/src/scripts/get-note-menu.ts b/fe_calckey/frontend/client/src/scripts/get-note-menu.ts index 5fe1489..f68c56a 100644 --- a/fe_calckey/frontend/client/src/scripts/get-note-menu.ts +++ b/fe_calckey/frontend/client/src/scripts/get-note-menu.ts @@ -9,23 +9,18 @@ import { url } from "@/config"; import { noteActions } from "@/store"; import { shareAvailable } from "@/scripts/share-available"; import { getUserMenu } from "@/scripts/get-user-menu"; -import { - magEffectiveNote, - magTransMap, - magTransProperty, - magTransUsername, -} from "@/scripts-mag/mag-util"; +import { magEffectiveNote, magTransUsername } from "@/scripts-mag/mag-util"; import { packed } from "magnetar-common"; export function getNoteMenu(props: { - note: packed.PackNoteMaybeFull | misskey.entities.Note; - menuButton: Ref; + note: packed.PackNoteMaybeFull; + menuButton: HTMLElement | null; translation: Ref; translating: Ref; isDeleted: Ref; - currentClipPage?: Ref; + currentClipPage?: misskey.entities.Clip; }) { - const appearNote = magEffectiveNote(props.note); + const appearNote = magEffectiveNote(props.note) as packed.PackNoteMaybeFull; function del(): void { os.confirm({ @@ -36,7 +31,7 @@ export function getNoteMenu(props: { os.api("notes/delete", { noteId: appearNote.id, - }); + }).then(); }); } @@ -49,23 +44,23 @@ export function getNoteMenu(props: { os.api("notes/delete", { noteId: appearNote.id, - }); + }).then(); os.post({ initialNote: appearNote, - renote: magTransProperty(appearNote, "renoted_note", "renote"), - reply: magTransProperty(appearNote, "parent_note", "reply"), - }); + renote: appearNote.renoted_note, + reply: appearNote.parent_note, + }).then(); }); } function edit(): void { os.post({ initialNote: appearNote, - renote: magTransProperty(appearNote, "renoted_note", "renote"), - reply: magTransProperty(appearNote, "parent_note", "reply"), + renote: appearNote.renoted_note, + reply: appearNote.parent_note, editId: appearNote.id, - }); + }).then(); } function toggleFavorite(favorite: boolean): void { @@ -74,7 +69,7 @@ export function getNoteMenu(props: { { noteId: appearNote.id, } - ); + ).then(); } function toggleWatch(watch: boolean): void { @@ -83,7 +78,7 @@ export function getNoteMenu(props: { { noteId: appearNote.id, } - ); + ).then(); } function toggleThreadMute(mute: boolean): void { @@ -92,22 +87,22 @@ export function getNoteMenu(props: { { noteId: appearNote.id, } - ); + ).then(); } function copyContent(): void { copyToClipboard(appearNote.text); - os.success(); + os.success().then(); } function copyLink(): void { copyToClipboard(`${url}/notes/${appearNote.id}`); - os.success(); + os.success().then(); } function copyOriginal(): void { copyToClipboard(appearNote.url ?? appearNote.uri); - os.success(); + os.success().then(); } function togglePin(pin: boolean): void { @@ -122,7 +117,7 @@ export function getNoteMenu(props: { os.alert({ type: "error", text: i18n.ts.pinLimitExceeded, - }); + }).then(); } }); } @@ -162,14 +157,15 @@ export function getNoteMenu(props: { result ); - os.apiWithDialog("clips/add-note", { - clipId: clip.id, - noteId: appearNote.id, - }); + if (clip) + os.apiWithDialog("clips/add-note", { + clipId: clip.id, + noteId: appearNote.id, + }).then(); }, }, null, - ...clips.map((clip) => ({ + ...clips?.map((clip) => ({ text: clip.name, action: () => { os.promiseDialog( @@ -196,9 +192,9 @@ export function getNoteMenu(props: { os.apiWithDialog("clips/remove-note", { clipId: clip.id, noteId: appearNote.id, - }); + }).then(); if ( - props.currentClipPage?.value.id === + props.currentClipPage?.id === clip.id ) props.isDeleted.value = true; @@ -207,34 +203,36 @@ export function getNoteMenu(props: { os.alert({ type: "error", text: err.message + "\n" + err.id, - }); + }).then(); } } ); }, })), ], - props.menuButton.value, + props.menuButton ?? undefined, {} ).then(focus); } async function unclip(): Promise { os.apiWithDialog("clips/remove-note", { - clipId: props.currentClipPage.value.id, + clipId: props.currentClipPage?.id, noteId: appearNote.id, - }); + }).then(); props.isDeleted.value = true; } function share(): void { - navigator.share({ - title: i18n.t("noteOf", { - user: magTransUsername(appearNote.user), - }), - text: appearNote.text ?? undefined, - url: `${url}/notes/${appearNote.id}`, - }); + navigator + .share({ + title: i18n.t("noteOf", { + user: magTransUsername(appearNote.user), + }), + text: appearNote.text ?? undefined, + url: `${url}/notes/${appearNote.id}`, + }) + .then(); } async function translate(): Promise { @@ -254,12 +252,11 @@ export function getNoteMenu(props: { noteId: appearNote.id, }); - const isAppearAuthor = - magTransMap(appearNote, "user", "userId", (u) => u.id) === $i.id; + const isAppearAuthor = appearNote.user.id === $i.id; const isModerator = $i.isAdmin || $i.isModerator; menu = [ - ...(props.currentClipPage?.value.userId === $i.id + ...(props.currentClipPage?.value?.userId === $i.id ? [ { icon: "ph-minus-circle ph-bold ph-lg", @@ -412,7 +409,7 @@ export function getNoteMenu(props: { }, {}, "closed" - ); + ).then(); }, } : undefined, diff --git a/fe_calckey/frontend/client/src/scripts/get-user-menu.ts b/fe_calckey/frontend/client/src/scripts/get-user-menu.ts index c2c9189..4ab994d 100644 --- a/fe_calckey/frontend/client/src/scripts/get-user-menu.ts +++ b/fe_calckey/frontend/client/src/scripts/get-user-menu.ts @@ -1,4 +1,3 @@ -import * as Acct from "calckey-js/built/acct"; import { defineAsyncComponent } from "vue"; import { i18n } from "@/i18n"; import copyToClipboard from "@/scripts/copy-to-clipboard"; @@ -8,8 +7,14 @@ import { userActions } from "@/store"; import { $i, iAmModerator } from "@/account"; import { mainRouter } from "@/router"; import { Router } from "@/nirax"; +import * as Misskey from "calckey-js"; +import { packed } from "magnetar-common"; +import { magTransProperty } from "@/scripts-mag/mag-util"; -export function getUserMenu(user, router: Router = mainRouter) { +export function getUserMenu( + user: packed.PackUserMaybeAll | Misskey.entities.UserDetailed, + router: Router = mainRouter +) { const meId = $i ? $i.id : null; async function pushList() { @@ -60,11 +65,12 @@ export function getUserMenu(user, router: Router = mainRouter) { } async function toggleMute() { - if (user.isMuted) { + if (magTransProperty(user, "mute", "isMuted")) { os.apiWithDialog("mute/delete", { userId: user.id, }).then(() => { - user.isMuted = false; + if ("isMuted" in user) user.isMuted = false; + else if ("mute" in user) user.mute = false; }); } else { const { canceled, result: period } = await os.select({ @@ -112,82 +118,121 @@ export function getUserMenu(user, router: Router = mainRouter) { userId: user.id, expiresAt, }).then(() => { - user.isMuted = true; + if ("isMuted" in user) user.isMuted = true; + else if ("mute" in user) user.mute = true; }); } } async function toggleRenoteMute(): Promise { os.apiWithDialog( - user.isRenoteMuted ? "renote-mute/delete" : "renote-mute/create", + magTransProperty(user, "mute_renotes", "isRenoteMuted") + ? "renote-mute/delete" + : "renote-mute/create", { userId: user.id, } ).then(() => { - user.isRenoteMuted = !user.isRenoteMuted; + if ("isRenoteMuted" in user) + user.isRenoteMuted = !user.isRenoteMuted; + else if ("mute_renotes" in user) + user.mute_renotes = !user.mute_renotes; }); } async function toggleBlock(): Promise { if ( !(await getConfirmed( - user.isBlocking ? i18n.ts.unblockConfirm : i18n.ts.blockConfirm + magTransProperty(user, "you_block", "isBlocking") + ? i18n.ts.unblockConfirm + : i18n.ts.blockConfirm )) ) return; await os.apiWithDialog( - user.isBlocking ? "blocking/delete" : "blocking/create", + magTransProperty(user, "you_block", "isBlocking") + ? "blocking/delete" + : "blocking/create", { userId: user.id, } ); - user.isBlocking = !user.isBlocking; - await os.api(user.isBlocking ? "mute/create" : "mute/delete", { - userId: user.id, - }); - user.isMuted = user.isBlocking; - if (user.isBlocking) { + + if ("isBlocking" in user) user.isBlocking = !user.isBlocking; + else if ("you_block" in user) user.you_block = !user.you_block; + + await os.api( + magTransProperty(user, "you_block", "isBlocking") + ? "mute/create" + : "mute/delete", + { + userId: user.id, + } + ); + + if ("isMuted" in user) user.isMuted = user.isBlocking; + else if ("mute" in user) user.mute = !user.mute; + + if (magTransProperty(user, "isBlocking", "you_block")) { await os.api("following/delete", { userId: user.id, }); - user.isFollowing = false; + + if ("isFollowing" in user) user.isFollowing = false; + else if ("you_follow" in user) user.you_follow = false; } } async function toggleSilence() { if ( !(await getConfirmed( - i18n.t(user.isSilenced ? "unsilenceConfirm" : "silenceConfirm") + i18n.t( + magTransProperty(user, "is_silenced", "isSilenced") + ? "unsilenceConfirm" + : "silenceConfirm" + ) )) ) return; os.apiWithDialog( - user.isSilenced ? "admin/unsilence-user" : "admin/silence-user", + magTransProperty(user, "isSilenced", "is_silenced") + ? "admin/unsilence-user" + : "admin/silence-user", { userId: user.id, } ).then(() => { - user.isSilenced = !user.isSilenced; + if ("isSilenced" in user) user.isSilenced = !user.isSilenced; + else if ("is_silenced" in user) + user.is_silenced = !user.is_silenced; }); } async function toggleSuspend() { if ( !(await getConfirmed( - i18n.t(user.isSuspended ? "unsuspendConfirm" : "suspendConfirm") + i18n.t( + magTransProperty(user, "is_suspended", "isSuspended") + ? "unsuspendConfirm" + : "suspendConfirm" + ) )) ) return; os.apiWithDialog( - user.isSuspended ? "admin/unsuspend-user" : "admin/suspend-user", + magTransProperty(user, "is_suspended", "isSuspended") + ? "admin/unsuspend-user" + : "admin/suspend-user", { userId: user.id, } ).then(() => { - user.isSuspended = !user.isSuspended; + if ("isSuspended" in user) user.isSuspended = !user.isSuspended; + else if ("is_suspended" in user) + user.is_suspended = !user.is_suspended; }); } @@ -220,7 +265,9 @@ export function getUserMenu(user, router: Router = mainRouter) { os.apiWithDialog("following/invalidate", { userId: user.id, }).then(() => { - user.isFollowed = !user.isFollowed; + if ("isFollowed" in user) user.isFollowed = !user.isFollowed; + else if ("follows_you" in user) + user.follows_you = !user.follows_you; }); } @@ -266,10 +313,10 @@ export function getUserMenu(user, router: Router = mainRouter) { : undefined, null, { - icon: user.isRenoteMuted + icon: magTransProperty(user, "mute_renotes", "isRenoteMuted") ? "ph-eye ph-bold ph-lg" : "ph-eye-slash ph-bold ph-lg", - text: user.isRenoteMuted + text: magTransProperty(user, "mute_renotes", "isRenoteMuted") ? i18n.ts.renoteUnmute : i18n.ts.renoteMute, action: toggleRenoteMute, @@ -279,21 +326,26 @@ export function getUserMenu(user, router: Router = mainRouter) { if ($i && meId !== user.id) { menu = menu.concat([ { - icon: user.isMuted + icon: magTransProperty(user, "mute", "isMuted") ? "ph-eye ph-bold ph-lg" : "ph-eye-slash ph-bold ph-lg", - text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute, - hidden: user.isBlocking === true, + text: magTransProperty(user, "mute", "isMuted") + ? i18n.ts.unmute + : i18n.ts.mute, + hidden: + magTransProperty(user, "you_block", "isBlocking") === true, action: toggleMute, }, { icon: "ph-prohibit ph-bold ph-lg", - text: user.isBlocking ? i18n.ts.unblock : i18n.ts.block, + text: magTransProperty(user, "you_block", "isBlocking") + ? i18n.ts.unblock + : i18n.ts.block, action: toggleBlock, }, ]); - if (user.isFollowed) { + if (magTransProperty(user, "follows_you", "isFollowed")) { menu = menu.concat([ { icon: "ph-link-break ph-bold ph-lg", @@ -317,12 +369,14 @@ export function getUserMenu(user, router: Router = mainRouter) { null, { icon: "ph-microphone-slash ph-bold ph-lg", - text: user.isSilenced ? i18n.ts.unsilence : i18n.ts.silence, + text: magTransProperty(user, "is_silenced", "isSilenced") + ? i18n.ts.unsilence + : i18n.ts.silence, action: toggleSilence, }, { icon: "ph-snowflake ph-bold ph-lg", - text: user.isSuspended + text: magTransProperty(user, "is_suspended", "isSuspended") ? i18n.ts.unsuspend : i18n.ts.suspend, action: toggleSuspend, diff --git a/fe_calckey/frontend/client/src/scripts/page-metadata.ts b/fe_calckey/frontend/client/src/scripts/page-metadata.ts index debd54f..39335f7 100644 --- a/fe_calckey/frontend/client/src/scripts/page-metadata.ts +++ b/fe_calckey/frontend/client/src/scripts/page-metadata.ts @@ -19,7 +19,7 @@ export type PageMetadata = { subtitle?: string; icon?: string | null; avatar?: packed.PackUserBase | misskey.entities.User | null; - userName?: misskey.entities.User | null; + userName?: packed.PackUserBase | misskey.entities.User | null; bg?: string; }; diff --git a/fe_calckey/frontend/client/src/scripts/reaction-picker.ts b/fe_calckey/frontend/client/src/scripts/reaction-picker.ts index 0f657b4..6bdf0fa 100644 --- a/fe_calckey/frontend/client/src/scripts/reaction-picker.ts +++ b/fe_calckey/frontend/client/src/scripts/reaction-picker.ts @@ -38,7 +38,7 @@ class ReactionPicker { } public show( - src: HTMLElement, + src: HTMLElement | null, onChosen: ReactionPicker["onChosen"], onClosed: ReactionPicker["onClosed"] ) { diff --git a/fe_calckey/frontend/client/src/scripts/use-note-capture.ts b/fe_calckey/frontend/client/src/scripts/use-note-capture.ts index 535ff2c..e387928 100644 --- a/fe_calckey/frontend/client/src/scripts/use-note-capture.ts +++ b/fe_calckey/frontend/client/src/scripts/use-note-capture.ts @@ -1,18 +1,13 @@ import { onUnmounted, Ref, toRaw } from "vue"; -import * as misskey from "calckey-js"; import { stream } from "@/stream"; import { $i } from "@/account"; import * as os from "@/os"; import { endpoints, packed } from "magnetar-common"; -import { - magConvertReaction, - magReactionIndex, - noteIsMag, -} from "@/scripts-mag/mag-util"; +import { magConvertReaction, magReactionIndex } from "@/scripts-mag/mag-util"; export function useNoteCapture(props: { rootEl: Ref; - note: Ref; + note: Ref; isDeletedRef: Ref; }) { const note = props.note; @@ -23,180 +18,98 @@ export function useNoteCapture(props: { if (id !== note.value.id) return; - if (noteIsMag(note.value)) { - switch (type) { - case "reacted": { - const reaction = body.reaction as string; + switch (type) { + case "reacted": { + const reaction = body.reaction as string; - const reactionType = magConvertReaction( + const reactionType = magConvertReaction( + reaction, + body?.emoji?.url + ); + const foundReaction = magReactionIndex( + note.value.reactions, + reactionType + ); + + const selfReact = ($i && body.userId === $i.id) || false; + if (foundReaction >= 0) { + const [reaction, cnt, selfReactPrev] = toRaw( + note.value.reactions[foundReaction] + ); + note.value.reactions[foundReaction] = [ reaction, - body?.emoji?.url - ); - const foundReaction = magReactionIndex( - note.value.reactions, - reactionType - ); - - const selfReact = ($i && body.userId === $i.id) || false; - if (foundReaction >= 0) { - const [reaction, cnt, selfReactPrev] = toRaw( - note.value.reactions[foundReaction] - ); - note.value.reactions[foundReaction] = [ - reaction, - cnt + 1, - selfReactPrev || selfReact, - ]; - } else { - note.value.reactions.push([reactionType, 1, selfReact]); - } - break; - } - - case "unreacted": { - const reaction = body.reaction; - - const reactionType = magConvertReaction(reaction); - const foundReaction = magReactionIndex( - note.value.reactions, - reactionType - ); - - const selfUnReact = ($i && body.userId === $i.id) || false; - if (foundReaction >= 0) { - const [name, cnt, selfReact] = - note.value.reactions[foundReaction]; - - note.value.reactions[foundReaction] = [ - name, - Math.max(0, cnt - 1), - !selfUnReact && (selfReact ?? false), - ]; - } - - break; - } - - case "pollVoted": { - const choice = body.choice; - - if (note.value.poll) { - const options = [...note.value.poll.options]; - options[choice] = { - ...options[choice], - votes_count: options[choice].votes_count + 1, - voted: ($i && body.userId === $i.id) || null, - }; - note.value.poll.options = options; - } - - break; - } - - case "deleted": { - props.isDeletedRef.value = true; - break; - } - - case "updated": { - const editedNote = await os.magApi( - endpoints.GetNoteById, - { - attachments: true, - context: true, - }, - { id } - ); - - const keys = new Set(); - Object.keys(editedNote) - .concat(Object.keys(note.value)) - .forEach((key) => keys.add(key)); - keys.forEach((key) => { - note.value[key] = editedNote[key]; - }); - break; + cnt + 1, + selfReactPrev || selfReact, + ]; + } else { + note.value.reactions.push([reactionType, 1, selfReact]); } + break; } - } else { - switch (type) { - case "reacted": { - const reaction = body.reaction; - if (body.emoji) { - const emojis = note.value.emojis || []; - if (!emojis.includes(body.emoji)) { - note.value.emojis = [...emojis, body.emoji]; - } - } + case "unreacted": { + const reaction = body.reaction; - // TODO: reactionsプロパティがない場合ってあったっけ? なければ || {} は消せる - const currentCount = note.value.reactions?.[reaction] || 0; + const reactionType = magConvertReaction(reaction); + const foundReaction = magReactionIndex( + note.value.reactions, + reactionType + ); - note.value.reactions[reaction] = currentCount + 1; + const selfUnReact = ($i && body.userId === $i.id) || false; + if (foundReaction >= 0) { + const [name, cnt, selfReact] = + note.value.reactions[foundReaction]; - if ($i && body.userId === $i.id) { - note.value.myReaction = reaction; - } - break; + note.value.reactions[foundReaction] = [ + name, + Math.max(0, cnt - 1), + !selfUnReact && (selfReact ?? false), + ]; } - case "unreacted": { - const reaction = body.reaction; + break; + } - // TODO: reactionsプロパティがない場合ってあったっけ? なければ || {} は消せる - const currentCount = note.value.reactions?.[reaction] || 0; + case "pollVoted": { + const choice = body.choice; - note.value.reactions[reaction] = Math.max( - 0, - currentCount - 1 - ); - - if ($i && body.userId === $i.id) { - note.value.myReaction = undefined; - } - break; + if (note.value.poll) { + const options = [...note.value.poll.options]; + options[choice] = { + ...options[choice], + votes_count: options[choice].votes_count + 1, + voted: ($i && body.userId === $i.id) || null, + }; + note.value.poll.options = options; } - case "pollVoted": { - const choice = body.choice; + break; + } - if (note.value.poll) { - const choices = [...note.value.poll.choices]; - choices[choice] = { - ...choices[choice], - votes: choices[choice].votes + 1, - ...($i && body.userId === $i.id - ? { - isVoted: true, - } - : {}), - }; - note.value.poll.choices = choices; - } + case "deleted": { + props.isDeletedRef.value = true; + break; + } - break; - } + case "updated": { + const editedNote = await os.magApi( + endpoints.GetNoteById, + { + attachments: true, + context: true, + }, + { id } + ); - case "deleted": { - props.isDeletedRef.value = true; - break; - } - - case "updated": { - const editedNote = await os.api("notes/show", { - noteId: id, - }); - - const keys = new Set(); - Object.keys(editedNote) - .concat(Object.keys(note.value)) - .forEach((key) => keys.add(key)); - keys.forEach((key) => { - note.value[key] = editedNote[key]; - }); - break; - } + const keys = new Set(); + Object.keys(editedNote) + .concat(Object.keys(note.value)) + .forEach((key) => keys.add(key)); + keys.forEach((key) => { + note.value[key] = editedNote[key]; + }); + break; } } } diff --git a/fe_calckey/frontend/magnetar-common/src/types.ts b/fe_calckey/frontend/magnetar-common/src/types.ts index 6692a64..4a2340d 100644 --- a/fe_calckey/frontend/magnetar-common/src/types.ts +++ b/fe_calckey/frontend/magnetar-common/src/types.ts @@ -41,3 +41,4 @@ export { DriveFolderBase } from "./types/DriveFolderBase"; export { DriveFolderParentExt } from "./types/DriveFolderParentExt"; export { ImageMeta } from "./types/ImageMeta"; export { InstanceTicker } from "./types/InstanceTicker"; +export { MovedTo } from "./types/MovedTo"; diff --git a/fe_calckey/frontend/magnetar-common/src/types/MovedTo.ts b/fe_calckey/frontend/magnetar-common/src/types/MovedTo.ts new file mode 100644 index 0000000..1f1d60f --- /dev/null +++ b/fe_calckey/frontend/magnetar-common/src/types/MovedTo.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export interface MovedTo { moved_to_uri: string, username: string, host: string, } \ No newline at end of file diff --git a/fe_calckey/frontend/magnetar-common/src/types/UserProfileExt.ts b/fe_calckey/frontend/magnetar-common/src/types/UserProfileExt.ts index 52080d7..784d137 100644 --- a/fe_calckey/frontend/magnetar-common/src/types/UserProfileExt.ts +++ b/fe_calckey/frontend/magnetar-common/src/types/UserProfileExt.ts @@ -1,5 +1,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { MmXml } from "./MmXml"; +import type { MovedTo } from "./MovedTo"; import type { ProfileField } from "./ProfileField"; -export interface UserProfileExt { is_locked: boolean, is_silenced: boolean, is_suspended: boolean, description: string | null, description_mm: MmXml | null, location: string | null, birthday: string | null, fields: Array, follower_count: number | null, following_count: number | null, note_count: number | null, url: string | null, moved_to_uri: string | null, also_known_as: string | null, banner_url: string | null, banner_blurhash: string | null, has_public_reactions: boolean, } \ No newline at end of file +export interface UserProfileExt { is_locked: boolean, is_silenced: boolean, is_suspended: boolean, description: string | null, description_mm: MmXml | null, location: string | null, birthday: string | null, fields: Array, follower_count: number | null, following_count: number | null, note_count: number | null, url: string | null, moved_to: MovedTo | null, also_known_as: string | null, banner_url: string | null, banner_blurhash: string | null, has_public_reactions: boolean, } \ No newline at end of file diff --git a/magnetar_sdk/src/types/user.rs b/magnetar_sdk/src/types/user.rs index 9a647ec..c6fdc9b 100644 --- a/magnetar_sdk/src/types/user.rs +++ b/magnetar_sdk/src/types/user.rs @@ -56,6 +56,14 @@ pub struct UserBase { pack!(PackUserBase, Required as id & Required as user); +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[ts(export)] +pub struct MovedTo { + pub moved_to_uri: String, + pub username: String, + pub host: String, +} + #[derive(Clone, Debug, Deserialize, Serialize, TS)] #[ts(export)] pub struct UserProfileExt { @@ -75,7 +83,7 @@ pub struct UserProfileExt { pub url: Option, - pub moved_to_uri: Option, + pub moved_to: Option, pub also_known_as: Option, pub banner_url: Option, diff --git a/src/model/data/user.rs b/src/model/data/user.rs index d3a21ff..e1070c6 100644 --- a/src/model/data/user.rs +++ b/src/model/data/user.rs @@ -5,7 +5,7 @@ use magnetar_sdk::types::emoji::{EmojiContext, PackEmojiBase}; use magnetar_sdk::types::instance::InstanceTicker; use magnetar_sdk::types::note::PackNoteMaybeFull; use magnetar_sdk::types::user::{ - AvatarDecoration, PackSecurityKeyBase, ProfileField, SecurityKeyBase, SpeechTransform, + AvatarDecoration, MovedTo, PackSecurityKeyBase, ProfileField, SecurityKeyBase, SpeechTransform, UserAuthOverviewExt, UserBase, UserDetailExt, UserProfileExt, UserProfilePinsEx, UserRelationExt, UserSecretsExt, }; @@ -80,6 +80,7 @@ pub struct UserProfileExtSource<'a> { pub banner: Option<&'a PackDriveFileBase>, pub description_mm: Option<&'a MmXml>, pub relation: Option<&'a UserRelationExt>, + pub moved_to: Option<&'a MovedTo>, } impl PackType> for UserProfileExt { @@ -92,6 +93,7 @@ impl PackType> for UserProfileExt { banner, description_mm, relation, + moved_to, }: UserProfileExtSource, ) -> Self { let follow_visibility = match profile.ff_visibility { @@ -118,7 +120,7 @@ impl PackType> for UserProfileExt { following_count: follow_visibility.then_some(user.following_count as usize), note_count: Some(user.notes_count as usize), url: profile.url.clone(), - moved_to_uri: user.moved_to_uri.clone(), + moved_to: moved_to.cloned(), also_known_as: user.also_known_as.clone(), banner_url: banner.and_then(|b| b.file.0.url.to_owned()), banner_blurhash: banner.and_then(|b| b.file.0.blurhash.clone()), diff --git a/src/model/processing/user.rs b/src/model/processing/user.rs index 4df5536..7d9e748 100644 --- a/src/model/processing/user.rs +++ b/src/model/processing/user.rs @@ -13,8 +13,8 @@ use magnetar_sdk::types::drive::PackDriveFileBase; use magnetar_sdk::types::emoji::EmojiContext; use magnetar_sdk::types::instance::InstanceTicker; use magnetar_sdk::types::user::{ - PackSecurityKeyBase, PackUserBase, PackUserMaybeAll, PackUserSelfMaybeAll, ProfileField, - SecurityKeyBase, UserAuthOverviewExt, UserBase, UserDetailExt, UserProfileExt, + MovedTo, PackSecurityKeyBase, PackUserBase, PackUserMaybeAll, PackUserSelfMaybeAll, + ProfileField, SecurityKeyBase, UserAuthOverviewExt, UserBase, UserDetailExt, UserProfileExt, UserProfilePinsEx, UserRelationExt, UserSecretsExt, }; use magnetar_sdk::types::{Id, MmXml}; @@ -153,6 +153,27 @@ impl UserModel { _ => None, }; + let moved = match &user.moved_to_uri { + Some(uri) => { + let moved = ctx.service.db.get_user_by_uri(uri).await?; + + moved.and_then(|m| { + let Some(uri) = m.uri else { + return None; + }; + + Some(MovedTo { + moved_to_uri: uri, + username: m.username, + host: m + .host + .unwrap_or_else(|| ctx.service.config.networking.host.to_string()), + }) + }) + } + None => None, + }; + let description_mm = self.tokenize_description(&profile); let fields = Vec::::deserialize(&profile.fields)?; @@ -192,6 +213,7 @@ impl UserModel { .and_then(|desc_mm| mmm::to_xml_string(&desc_mm).map(MmXml).ok()) .as_ref(), relation, + moved_to: moved.as_ref(), }, )) }