Compare commits
2 Commits
e53e274d1d
...
0bb0c775dc
Author | SHA1 | Date |
---|---|---|
Natty | 0bb0c775dc | |
Natty | 4cb7431681 |
|
@ -27,6 +27,7 @@
|
|||
"@types/uuid": "8.3.4",
|
||||
"@vitejs/plugin-vue": "4.2.3",
|
||||
"@vue/compiler-sfc": "3.3.4",
|
||||
"@vue/runtime-core": "3.3.4",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "5.0.2",
|
||||
"blurhash": "1.1.5",
|
||||
|
|
|
@ -260,6 +260,8 @@ import {
|
|||
magHasReacted,
|
||||
magIsRenote,
|
||||
magReactionCount,
|
||||
magReactionSelf,
|
||||
magReactionToLegacy,
|
||||
} from "@/scripts-mag/mag-util";
|
||||
|
||||
const router = useRouter();
|
||||
|
@ -353,7 +355,7 @@ function react(viaKeyboard = false): void {
|
|||
(reaction) => {
|
||||
os.api("notes/reactions/create", {
|
||||
noteId: appearNote.id,
|
||||
reaction: reaction,
|
||||
reaction: magReactionToLegacy(reaction),
|
||||
});
|
||||
},
|
||||
() => {
|
||||
|
@ -362,8 +364,8 @@ function react(viaKeyboard = false): void {
|
|||
);
|
||||
}
|
||||
|
||||
function undoReact(note): void {
|
||||
const oldReaction = note.myReaction;
|
||||
function undoReact(note: packed.PackNoteMaybeFull): void {
|
||||
const oldReaction = magReactionSelf(note);
|
||||
if (!oldReaction) return;
|
||||
os.api("notes/reactions/delete", {
|
||||
noteId: note.id,
|
||||
|
|
|
@ -170,7 +170,11 @@ import { useNoteCapture } from "@/scripts/use-note-capture";
|
|||
import { stream } from "@/stream";
|
||||
import { NoteUpdatedEvent } from "calckey-js/built/streaming.types";
|
||||
import { packed } from "magnetar-common";
|
||||
import { magIsRenote, magReactionCount } from "@/scripts-mag/mag-util";
|
||||
import {
|
||||
magIsRenote,
|
||||
magReactionCount,
|
||||
magReactionToLegacy,
|
||||
} from "@/scripts-mag/mag-util";
|
||||
import MagNote from "@/components/MagNote.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -254,7 +258,7 @@ function react(viaKeyboard = false): void {
|
|||
(reaction) => {
|
||||
os.api("notes/reactions/create", {
|
||||
noteId: note.id,
|
||||
reaction: reaction,
|
||||
reaction: magReactionToLegacy(reaction),
|
||||
});
|
||||
},
|
||||
() => {
|
||||
|
|
|
@ -34,16 +34,19 @@
|
|||
class="_button item"
|
||||
@click="emit('chosen', emoji, $event)"
|
||||
>
|
||||
<MkEmoji class="emoji" :emoji="emoji" :normal="true" />
|
||||
<MagEmoji class="emoji" :emoji="emoji" :normal="true" />
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import { ref, watch, onMounted, toRaw } from "vue";
|
||||
import { addSkinTone } from "@/scripts/emojilist";
|
||||
import emojiComponents from "unicode-emoji-json/data-emoji-components.json";
|
||||
import { types } from "magnetar-common";
|
||||
import { magConvertReaction, magIsUnicodeEmoji } from "@/scripts-mag/mag-util";
|
||||
import { instance } from "@/instance";
|
||||
|
||||
const props = defineProps<{
|
||||
emojis: string[];
|
||||
|
@ -52,10 +55,19 @@ const props = defineProps<{
|
|||
skinTones?: string[];
|
||||
}>();
|
||||
|
||||
const localEmojis = ref([...props.emojis]);
|
||||
const resolveEmojis = (e: string) =>
|
||||
magConvertReaction(e, (name) => {
|
||||
return instance.emojis.find((e) => e.name === name)?.url!;
|
||||
});
|
||||
|
||||
const localEmojis = ref<types.Reaction[]>(props.emojis.map(resolveEmojis));
|
||||
|
||||
function applyUnicodeSkinTone(custom?: number) {
|
||||
for (let i = 0; i < localEmojis.value.length; i++) {
|
||||
let rawEmoji: types.ReactionUnicode = toRaw(localEmojis[i]);
|
||||
|
||||
if (!magIsUnicodeEmoji(rawEmoji)) continue;
|
||||
|
||||
if (
|
||||
[
|
||||
emojiComponents.light_skin_tone,
|
||||
|
@ -63,16 +75,16 @@ function applyUnicodeSkinTone(custom?: number) {
|
|||
emojiComponents.medium_skin_tone,
|
||||
emojiComponents.medium_dark_skin_tone,
|
||||
emojiComponents.dark_skin_tone,
|
||||
].some((v) => localEmojis.value[i].endsWith(v))
|
||||
].some((v) => rawEmoji.endsWith(v))
|
||||
) {
|
||||
localEmojis.value[i] = localEmojis.value[i].slice(0, -1);
|
||||
rawEmoji = rawEmoji.slice(0, -1);
|
||||
}
|
||||
localEmojis.value[i] = addSkinTone(localEmojis.value[i], custom);
|
||||
localEmojis.value[i] = addSkinTone(rawEmoji, custom);
|
||||
}
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: "chosen", v: string, event: MouseEvent): void;
|
||||
(ev: "chosen", v: types.Reaction, event: MouseEvent): void;
|
||||
}>();
|
||||
|
||||
const shown = ref(!!props.initialShown);
|
||||
|
@ -86,7 +98,7 @@ onMounted(() => {
|
|||
watch(
|
||||
() => props.emojis,
|
||||
(newVal) => {
|
||||
localEmojis.value = [...newVal];
|
||||
localEmojis.value = newVal.map(resolveEmojis);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
class="_button item"
|
||||
:title="emoji.name"
|
||||
tabindex="0"
|
||||
@click="chosen(emoji, $event)"
|
||||
@click="chosen(magCustomEmoji(emoji), $event)"
|
||||
>
|
||||
<!--<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>-->
|
||||
<!--<MagEmoji v-if="emoji.char != null" :emoji="emoji.char"/>-->
|
||||
<img
|
||||
class="emoji"
|
||||
:src="
|
||||
|
@ -46,9 +46,9 @@
|
|||
class="_button item"
|
||||
:title="emoji.slug"
|
||||
tabindex="0"
|
||||
@click="chosen(emoji, $event)"
|
||||
@click="chosen(magUnicodeEmoji(emoji), $event)"
|
||||
>
|
||||
<MkEmoji class="emoji" :emoji="emoji.emoji" />
|
||||
<MagEmoji class="emoji" :emoji="emoji.emoji" />
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -57,13 +57,13 @@
|
|||
<section v-if="showPinned">
|
||||
<div class="body">
|
||||
<button
|
||||
v-for="emoji in pinned"
|
||||
v-for="emoji in resolvedPinned"
|
||||
:key="emoji"
|
||||
class="_button item"
|
||||
tabindex="0"
|
||||
@click="chosen(emoji, $event)"
|
||||
>
|
||||
<MkEmoji
|
||||
<MagEmoji
|
||||
class="emoji"
|
||||
:emoji="emoji"
|
||||
:normal="true"
|
||||
|
@ -79,12 +79,12 @@
|
|||
</header>
|
||||
<div class="body">
|
||||
<button
|
||||
v-for="emoji in recentlyUsedEmojis"
|
||||
v-for="emoji in resolvedRecent"
|
||||
:key="emoji"
|
||||
class="_button item"
|
||||
@click="chosen(emoji, $event)"
|
||||
>
|
||||
<MkEmoji
|
||||
<MagEmoji
|
||||
class="emoji"
|
||||
:emoji="emoji"
|
||||
:normal="true"
|
||||
|
@ -181,6 +181,13 @@ import { emojiCategories, instance } from "@/instance";
|
|||
import { i18n } from "@/i18n";
|
||||
import { defaultStore } from "@/store";
|
||||
import { FocusTrap } from "focus-trap-vue";
|
||||
import { types } from "magnetar-common";
|
||||
import {
|
||||
magConvertReaction,
|
||||
magCustomEmoji,
|
||||
magReactionToLegacy,
|
||||
magUnicodeEmoji,
|
||||
} from "@/scripts-mag/mag-util";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
@ -195,7 +202,7 @@ const props = withDefaults(
|
|||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: "chosen", v: string): void;
|
||||
(ev: "chosen", v: types.Reaction): void;
|
||||
}>();
|
||||
|
||||
const search = ref<HTMLInputElement>();
|
||||
|
@ -226,6 +233,16 @@ const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]);
|
|||
const searchResultUnicode = ref<UnicodeEmojiDef[]>([]);
|
||||
const tab = ref<"index" | "custom" | "unicode" | "tags">("index");
|
||||
|
||||
const resolveEmojis = (e: string) =>
|
||||
magConvertReaction(e, (name) => {
|
||||
return customEmojis.find((e) => e.name === name)?.url!;
|
||||
});
|
||||
|
||||
const resolvedPinned = computed(() => pinned.value.map(resolveEmojis));
|
||||
const resolvedRecent = computed(() =>
|
||||
recentlyUsedEmojis.value.map(resolveEmojis)
|
||||
);
|
||||
|
||||
watch(q, () => {
|
||||
if (emojis.value) emojis.value.scrollTop = 0;
|
||||
|
||||
|
@ -400,13 +417,7 @@ function reset() {
|
|||
q.value = "";
|
||||
}
|
||||
|
||||
function getKey(
|
||||
emoji: string | Misskey.entities.CustomEmoji | UnicodeEmojiDef
|
||||
): string {
|
||||
return typeof emoji === "string" ? emoji : emoji.emoji || `:${emoji.name}:`;
|
||||
}
|
||||
|
||||
function chosen(emoji: any, ev?: MouseEvent) {
|
||||
function chosen(emoji: types.Reaction, ev?: MouseEvent) {
|
||||
const el =
|
||||
ev &&
|
||||
((ev.currentTarget ?? ev.target) as HTMLElement | null | undefined);
|
||||
|
@ -417,8 +428,9 @@ function chosen(emoji: any, ev?: MouseEvent) {
|
|||
os.popup(Ripple, { x, y }, {}, "end");
|
||||
}
|
||||
|
||||
const key = getKey(emoji);
|
||||
emit("chosen", key);
|
||||
emit("chosen", emoji);
|
||||
|
||||
const key = magReactionToLegacy(emoji);
|
||||
|
||||
// 最近使った絵文字更新
|
||||
if (!pinned.value.includes(key)) {
|
||||
|
@ -443,22 +455,22 @@ function done(query?: any): boolean | void {
|
|||
const q2 = query.replaceAll(":", "");
|
||||
const exactMatchCustom = customEmojis.find((emoji) => emoji.name === q2);
|
||||
if (exactMatchCustom) {
|
||||
chosen(exactMatchCustom);
|
||||
chosen(magCustomEmoji(exactMatchCustom));
|
||||
return true;
|
||||
}
|
||||
const exactMatchUnicode = emojilist.find(
|
||||
(emoji) => emoji.emoji === q2 || emoji.slug === q2
|
||||
);
|
||||
if (exactMatchUnicode) {
|
||||
chosen(exactMatchUnicode);
|
||||
chosen(magUnicodeEmoji(exactMatchUnicode));
|
||||
return true;
|
||||
}
|
||||
if (searchResultCustom.value.length > 0) {
|
||||
chosen(searchResultCustom.value[0]);
|
||||
chosen(magCustomEmoji(searchResultCustom.value[0]));
|
||||
return true;
|
||||
}
|
||||
if (searchResultUnicode.value.length > 0) {
|
||||
chosen(searchResultUnicode.value[0]);
|
||||
chosen(magUnicodeEmoji(searchResultUnicode.value[0]));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import { ref } from "vue";
|
|||
import MkModal from "@/components/MkModal.vue";
|
||||
import MkEmojiPicker from "@/components/MkEmojiPicker.vue";
|
||||
import { defaultStore } from "@/store";
|
||||
import { types } from "magnetar-common";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
|
@ -51,7 +52,7 @@ withDefaults(
|
|||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: "done", v: any): void;
|
||||
(ev: "done", v: types.Reaction): void;
|
||||
(ev: "close"): void;
|
||||
(ev: "closed"): void;
|
||||
}>();
|
||||
|
@ -59,7 +60,7 @@ const emit = defineEmits<{
|
|||
const modal = ref<InstanceType<typeof MkModal>>();
|
||||
const picker = ref<InstanceType<typeof MkEmojiPicker>>();
|
||||
|
||||
function chosen(emoji: any) {
|
||||
function chosen(emoji: types.Reaction) {
|
||||
emit("done", emoji);
|
||||
modal.value?.close();
|
||||
}
|
||||
|
|
|
@ -264,6 +264,7 @@ import { getNoteMenu } from "@/scripts/get-note-menu";
|
|||
import { useNoteCapture } from "@/scripts/use-note-capture";
|
||||
import { notePage } from "@/filters/note";
|
||||
import { getNoteSummary } from "@/scripts/get-note-summary";
|
||||
import { magReactionToLegacy } from "@/scripts-mag/mag-util";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
|
@ -360,7 +361,7 @@ function react(viaKeyboard = false): void {
|
|||
(reaction) => {
|
||||
os.api("notes/reactions/create", {
|
||||
noteId: appearNote.id,
|
||||
reaction: reaction,
|
||||
reaction: magReactionToLegacy(reaction),
|
||||
});
|
||||
},
|
||||
() => {
|
||||
|
|
|
@ -222,6 +222,7 @@ import {
|
|||
magHasReacted,
|
||||
magIsRenote,
|
||||
magReactionCount,
|
||||
magReactionToLegacy,
|
||||
magTransMap,
|
||||
magTransProperty,
|
||||
} from "@/scripts-mag/mag-util";
|
||||
|
@ -312,7 +313,7 @@ function react(viaKeyboard = false): void {
|
|||
(reaction) => {
|
||||
os.api("notes/reactions/create", {
|
||||
noteId: appearNote.id,
|
||||
reaction: reaction,
|
||||
reaction: magReactionToLegacy(reaction),
|
||||
});
|
||||
},
|
||||
() => {
|
||||
|
|
|
@ -64,27 +64,23 @@
|
|||
class="ph-microphone-stage ph-bold"
|
||||
></i>
|
||||
<!-- notification.reaction が null になることはまずないが、ここでoptional chaining使うと一部ブラウザで刺さるので念の為 -->
|
||||
<XReactionIcon
|
||||
<MagEmoji
|
||||
v-else-if="
|
||||
showEmojiReactions && notification.type === 'reaction'
|
||||
"
|
||||
ref="reactionRef"
|
||||
:reaction="
|
||||
notification.reaction
|
||||
? notification.reaction.replace(
|
||||
/^:(\w+):$/,
|
||||
':$1@.:'
|
||||
)
|
||||
: notification.reaction
|
||||
"
|
||||
:custom-emojis="notification.note.emojis"
|
||||
:emoji="normalizeNotifReaction(notification)"
|
||||
:is-reaction="true"
|
||||
:normal="true"
|
||||
:no-style="true"
|
||||
/>
|
||||
<XReactionIcon
|
||||
<MagEmoji
|
||||
v-else-if="
|
||||
!showEmojiReactions && notification.type === 'reaction'
|
||||
"
|
||||
:reaction="defaultReaction"
|
||||
:emoji="defaultReaction"
|
||||
:is-reaction="true"
|
||||
:normal="true"
|
||||
:no-style="true"
|
||||
/>
|
||||
</div>
|
||||
|
@ -273,7 +269,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import * as misskey from "calckey-js";
|
||||
import XReactionIcon from "@/components/MkReactionIcon.vue";
|
||||
import MkFollowButton from "@/components/MkFollowButton.vue";
|
||||
import XReactionTooltip from "@/components/MkReactionTooltip.vue";
|
||||
import { getNoteSummary } from "@/scripts/get-note-summary";
|
||||
|
@ -286,6 +281,8 @@ import { useTooltip } from "@/scripts/use-tooltip";
|
|||
import { defaultStore } from "@/store";
|
||||
import { instance } from "@/instance";
|
||||
import MkFollowApproveButton from "@/components/MkFollowApproveButton.vue";
|
||||
import { magConvertReaction, magIsCustomEmoji } from "@/scripts-mag/mag-util";
|
||||
import { types } from "magnetar-common";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
@ -310,6 +307,27 @@ const defaultReaction = ["⭐", "👍", "❤️"].includes(instance.defaultReact
|
|||
? instance.defaultReaction
|
||||
: "⭐";
|
||||
|
||||
function normalizeNotifReaction(
|
||||
notification: misskey.entities.Notification & { type: "reaction" }
|
||||
): types.Reaction {
|
||||
return notification.reaction
|
||||
? magConvertReaction(
|
||||
notification.reaction,
|
||||
(name, host) =>
|
||||
notification.note.emojis.find((e) => {
|
||||
const parsed = magConvertReaction(`:${e.name}:`, e.url);
|
||||
|
||||
if (!magIsCustomEmoji(parsed)) return false;
|
||||
|
||||
return (
|
||||
parsed.name === name &&
|
||||
(parsed.host ?? null) === (host ?? null)
|
||||
);
|
||||
})?.url!
|
||||
)
|
||||
: notification.reaction;
|
||||
}
|
||||
|
||||
let readObserver: IntersectionObserver | undefined;
|
||||
let connection;
|
||||
|
||||
|
@ -357,14 +375,13 @@ const rejectGroupInvitation = () => {
|
|||
};
|
||||
|
||||
useTooltip(reactionRef, (showing) => {
|
||||
if (props.notification.type !== "reaction") return;
|
||||
|
||||
os.popup(
|
||||
XReactionTooltip,
|
||||
{
|
||||
showing,
|
||||
reaction: props.notification.reaction
|
||||
? props.notification.reaction.replace(/^:(\w+):$/, ":$1@.:")
|
||||
: props.notification.reaction,
|
||||
emojis: props.notification.note.emojis,
|
||||
reaction: normalizeNotifReaction(props.notification),
|
||||
targetElement: reactionRef.value.$el,
|
||||
},
|
||||
{},
|
||||
|
|
|
@ -3,23 +3,25 @@
|
|||
<div :class="$style.tabs">
|
||||
<button
|
||||
v-for="reaction in reactions"
|
||||
:key="reaction"
|
||||
:class="[$style.tab, { [$style.tabActive]: tab === reaction }]"
|
||||
:key="reaction[0]"
|
||||
:class="[
|
||||
$style.tab,
|
||||
{
|
||||
[$style.tabActive]:
|
||||
tab === magReactionToLegacy(reaction[0]),
|
||||
},
|
||||
]"
|
||||
class="_button"
|
||||
@click="tab = reaction"
|
||||
@click="tab = magReactionToLegacy(reaction[0])"
|
||||
>
|
||||
<MkReactionIcon
|
||||
<MagEmoji
|
||||
ref="reactionRef"
|
||||
:reaction="
|
||||
reaction
|
||||
? reaction.replace(/^:(\w+):$/, ':$1@.:')
|
||||
: reaction
|
||||
"
|
||||
:custom-emojis="note.emojis"
|
||||
:emoji="reaction[0]"
|
||||
:is-reaction="true"
|
||||
:normal="true"
|
||||
/>
|
||||
<span style="margin-left: 4px">{{
|
||||
note.reactions[reaction]
|
||||
}}</span>
|
||||
|
||||
<span style="margin-left: 4px">{{ reaction[1] }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<MkUserCardMini
|
||||
|
@ -37,19 +39,19 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, watch } from "vue";
|
||||
import * as misskey from "calckey-js";
|
||||
import MkReactionIcon from "@/components/MkReactionIcon.vue";
|
||||
import MkUserCardMini from "@/components/MkUserCardMini.vue";
|
||||
import { i18n } from "@/i18n";
|
||||
import * as os from "@/os";
|
||||
import { endpoints, types, packed } from "magnetar-common";
|
||||
import { magReactionToLegacy } from "@/scripts-mag/mag-util";
|
||||
|
||||
const props = defineProps<{
|
||||
noteId: misskey.entities.Note["id"];
|
||||
noteId: string;
|
||||
}>();
|
||||
|
||||
let note = $ref<misskey.entities.Note>();
|
||||
let tab = $ref<string>();
|
||||
let reactions = $ref<string[]>();
|
||||
let users = $ref();
|
||||
let note = $ref<packed.PackNoteBase>();
|
||||
let reactions = $ref<types.ReactionPair[]>();
|
||||
let users = $ref<misskey.entities.UserLite[]>();
|
||||
|
||||
watch($$(tab), async () => {
|
||||
const res = await os.api("notes/reactions", {
|
||||
|
@ -62,11 +64,9 @@ watch($$(tab), async () => {
|
|||
});
|
||||
|
||||
onMounted(() => {
|
||||
os.api("notes/show", {
|
||||
noteId: props.noteId,
|
||||
}).then((res) => {
|
||||
reactions = Object.keys(res.reactions);
|
||||
tab = reactions[0];
|
||||
os.magApi(endpoints.GetNoteById, {}, { id: props.noteId }).then((res) => {
|
||||
reactions = res.reactions;
|
||||
tab = magReactionToLegacy(reactions.map((r) => r[0])[0]);
|
||||
note = res;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<template>
|
||||
<MkEmoji
|
||||
<MagEmoji
|
||||
:emoji="reaction"
|
||||
:custom-emojis="customEmojis || []"
|
||||
:is-reaction="true"
|
||||
:normal="true"
|
||||
:no-style="noStyle"
|
||||
|
@ -9,11 +8,10 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as Misskey from "calckey-js";
|
||||
import { types } from "magnetar-common";
|
||||
|
||||
const props = defineProps<{
|
||||
reaction: string;
|
||||
customEmojis?: Pick<Misskey.entities.CustomEmoji, "name" | "url">[];
|
||||
reaction: types.Reaction;
|
||||
noStyle?: boolean;
|
||||
}>();
|
||||
</script>
|
||||
|
|
|
@ -6,13 +6,17 @@
|
|||
@closed="emit('closed')"
|
||||
>
|
||||
<div class="beeadbfb">
|
||||
<XReactionIcon
|
||||
:reaction="reaction"
|
||||
:custom-emojis="emojis"
|
||||
<MagEmoji
|
||||
class="icon"
|
||||
:class="{
|
||||
unicodeReaction: magIsUnicodeEmoji(reaction),
|
||||
}"
|
||||
:emoji="reaction"
|
||||
:is-reaction="true"
|
||||
:normal="true"
|
||||
:no-style="true"
|
||||
/>
|
||||
<div class="name">{{ reaction.replace("@.", "") }}</div>
|
||||
<div class="name">{{ magReactionToLegacy(reaction) }}</div>
|
||||
</div>
|
||||
</MkTooltip>
|
||||
</template>
|
||||
|
@ -20,11 +24,11 @@
|
|||
<script lang="ts" setup>
|
||||
import {} from "vue";
|
||||
import MkTooltip from "./MkTooltip.vue";
|
||||
import XReactionIcon from "@/components/MkReactionIcon.vue";
|
||||
import { magIsUnicodeEmoji, magReactionToLegacy } from "@/scripts-mag/mag-util";
|
||||
import { types } from "magnetar-common";
|
||||
|
||||
const props = defineProps<{
|
||||
reaction: string;
|
||||
emojis: any[]; // TODO
|
||||
reaction: types.Reaction;
|
||||
targetElement: HTMLElement;
|
||||
}>();
|
||||
|
||||
|
@ -40,8 +44,11 @@ const emit = defineEmits<{
|
|||
> .icon {
|
||||
display: block;
|
||||
width: 60px;
|
||||
font-size: 60px; // unicodeな絵文字についてはwidthが効かないため
|
||||
margin: 0 auto;
|
||||
|
||||
&.unicodeReaction {
|
||||
font-size: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
> .name {
|
||||
|
|
|
@ -7,13 +7,17 @@
|
|||
>
|
||||
<div class="bqxuuuey">
|
||||
<div class="reaction">
|
||||
<XReactionIcon
|
||||
:reaction="reaction"
|
||||
:custom-emojis="emojis"
|
||||
<MagEmoji
|
||||
class="icon"
|
||||
:class="{
|
||||
unicodeReaction: magIsUnicodeEmoji(reaction),
|
||||
}"
|
||||
:emoji="reaction"
|
||||
:is-reaction="true"
|
||||
:normal="true"
|
||||
:no-style="true"
|
||||
/>
|
||||
<div class="name">{{ reaction.replace("@.", "") }}</div>
|
||||
<div class="name">{{ magReactionToLegacy(reaction) }}</div>
|
||||
</div>
|
||||
<div class="users">
|
||||
<div v-for="u in users" :key="u.id" class="user">
|
||||
|
@ -31,13 +35,13 @@
|
|||
<script lang="ts" setup>
|
||||
import {} from "vue";
|
||||
import MkTooltip from "./MkTooltip.vue";
|
||||
import XReactionIcon from "@/components/MkReactionIcon.vue";
|
||||
import { magIsUnicodeEmoji, magReactionToLegacy } from "@/scripts-mag/mag-util";
|
||||
import { types } from "magnetar-common";
|
||||
|
||||
const props = defineProps<{
|
||||
reaction: string;
|
||||
reaction: types.Reaction;
|
||||
users: any[]; // TODO
|
||||
count: number;
|
||||
emojis: any[]; // TODO
|
||||
targetElement: HTMLElement;
|
||||
}>();
|
||||
|
||||
|
@ -57,8 +61,11 @@ const emit = defineEmits<{
|
|||
> .icon {
|
||||
display: block;
|
||||
width: 60px;
|
||||
font-size: 60px; // unicodeな絵文字についてはwidthが効かないため
|
||||
margin: 0 auto;
|
||||
|
||||
&.unicodeReaction {
|
||||
font-size: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
> .name {
|
||||
|
|
|
@ -5,24 +5,17 @@
|
|||
v-ripple="canToggle"
|
||||
class="hkzvhatu _button"
|
||||
:class="{
|
||||
reacted: magReactionSelf(note) === reaction,
|
||||
reacted: magReactionSelf(note) === magReactionToLegacy(reaction),
|
||||
canToggle,
|
||||
newlyAdded: !isInitial,
|
||||
}"
|
||||
@click="toggleReaction()"
|
||||
>
|
||||
<XReactionIcon
|
||||
<MagEmoji
|
||||
class="icon"
|
||||
:reaction="reaction"
|
||||
:custom-emojis="[
|
||||
...note.emojis,
|
||||
typeof url !== 'undefined'
|
||||
? {
|
||||
name: reaction.substring(1, reaction.length - 1),
|
||||
url: url,
|
||||
}
|
||||
: undefined,
|
||||
]"
|
||||
:emoji="reaction"
|
||||
:is-reaction="true"
|
||||
:normal="true"
|
||||
/>
|
||||
<span class="count">{{ count }}</span>
|
||||
</button>
|
||||
|
@ -32,44 +25,45 @@
|
|||
import { computed, ref } from "vue";
|
||||
import * as misskey from "calckey-js";
|
||||
import XDetails from "@/components/MkReactionsViewer.details.vue";
|
||||
import XReactionIcon from "@/components/MkReactionIcon.vue";
|
||||
import * as os from "@/os";
|
||||
import { useTooltip } from "@/scripts/use-tooltip";
|
||||
import { $i } from "@/account";
|
||||
import { packed } from "magnetar-common";
|
||||
import { magReactionSelf } from "@/scripts-mag/mag-util";
|
||||
import { packed, types } from "magnetar-common";
|
||||
import { magReactionSelf, magReactionToLegacy } from "@/scripts-mag/mag-util";
|
||||
|
||||
const props = defineProps<{
|
||||
reaction: string;
|
||||
reaction: types.Reaction;
|
||||
count: number;
|
||||
url?: string;
|
||||
isInitial: boolean;
|
||||
note: packed.PackNoteMaybeFull | misskey.entities.Note;
|
||||
}>();
|
||||
|
||||
const buttonRef = ref<HTMLElement>();
|
||||
|
||||
const canToggle = computed(() => !props.reaction.match(/@\w/) && $i);
|
||||
const canToggle = computed(
|
||||
() => !magReactionToLegacy(props.reaction).match(/@\w/) && $i
|
||||
);
|
||||
|
||||
const toggleReaction = () => {
|
||||
if (!canToggle.value) return;
|
||||
|
||||
const oldReaction = magReactionSelf(props.note);
|
||||
const newReaction = magReactionToLegacy(props.reaction);
|
||||
if (oldReaction) {
|
||||
os.api("notes/reactions/delete", {
|
||||
noteId: props.note.id,
|
||||
}).then(() => {
|
||||
if (oldReaction !== props.reaction) {
|
||||
if (oldReaction !== newReaction) {
|
||||
os.api("notes/reactions/create", {
|
||||
noteId: props.note.id,
|
||||
reaction: props.reaction,
|
||||
reaction: magReactionToLegacy(props.reaction),
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
os.api("notes/reactions/create", {
|
||||
noteId: props.note.id,
|
||||
reaction: props.reaction,
|
||||
reaction: magReactionToLegacy(props.reaction),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -79,7 +73,7 @@ useTooltip(
|
|||
async (showing) => {
|
||||
const reactions = await os.apiGet("notes/reactions", {
|
||||
noteId: props.note.id,
|
||||
type: props.reaction,
|
||||
type: magReactionToLegacy(props.reaction),
|
||||
limit: 11,
|
||||
_cacheKey_: props.count,
|
||||
});
|
||||
|
@ -91,7 +85,6 @@ useTooltip(
|
|||
{
|
||||
showing,
|
||||
reaction: props.reaction,
|
||||
emojis: props.note.emojis,
|
||||
users,
|
||||
count: props.count,
|
||||
targetElement: buttonRef.value,
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
<template>
|
||||
<div class="tdflqwzn" :class="{ isMe }">
|
||||
<XReaction
|
||||
v-for="r in Array.isArray(note.reactions)
|
||||
? note.reactions
|
||||
: Object.entries(note.reactions)"
|
||||
:key="magReactionPairToLegacy(r)[0]"
|
||||
:reaction="magReactionPairToLegacy(r)[0]"
|
||||
:url="typeof r[0]['url'] !== 'undefined' ? r[0]['url'] : undefined"
|
||||
:count="magReactionPairToLegacy(r)[1]"
|
||||
:is-initial="initialReactions.has(magReactionPairToLegacy(r)[0])"
|
||||
v-for="r in reactions"
|
||||
:key="magReactionToLegacy(r[0])"
|
||||
:reaction="r[0]"
|
||||
:count="r[1]"
|
||||
:is-initial="initialReactions.has(magReactionToLegacy(r[0]))"
|
||||
:note="note"
|
||||
/>
|
||||
</div>
|
||||
|
@ -19,14 +16,58 @@ import { computed } from "vue";
|
|||
import * as misskey from "calckey-js";
|
||||
import { $i } from "@/account";
|
||||
import XReaction from "@/components/MkReactionsViewer.reaction.vue";
|
||||
import { packed } from "magnetar-common";
|
||||
import { magReactionPairToLegacy } from "@/scripts-mag/mag-util";
|
||||
import { packed, types } from "magnetar-common";
|
||||
import {
|
||||
magConvertReaction,
|
||||
magReactionEquals,
|
||||
magReactionToLegacy,
|
||||
} from "@/scripts-mag/mag-util";
|
||||
|
||||
const props = defineProps<{
|
||||
note: packed.PackNoteMaybeFull | misskey.entities.Note;
|
||||
}>();
|
||||
|
||||
const initialReactions = new Set(Object.keys(props.note.reactions));
|
||||
const reactions = computed(() => {
|
||||
const myReactionMk = (props.note as misskey.entities.Note).myReaction
|
||||
? magConvertReaction((props.note as misskey.entities.Note).myReaction!)
|
||||
: null;
|
||||
|
||||
return Array.isArray(props.note.reactions)
|
||||
? props.note.reactions
|
||||
: Object.entries(props.note.reactions)
|
||||
.filter(([e]) => e)
|
||||
.map(([e, cnt]) => {
|
||||
const parsed = magConvertReaction(
|
||||
e,
|
||||
(name, host) =>
|
||||
(
|
||||
props.note
|
||||
.emojis as misskey.entities.Note["emojis"]
|
||||
).find((e) =>
|
||||
magReactionEquals(
|
||||
magConvertReaction(`:${e.name}:`),
|
||||
{ name, host, url: null! }
|
||||
)
|
||||
)?.url ?? null!
|
||||
);
|
||||
|
||||
return [
|
||||
parsed,
|
||||
cnt,
|
||||
myReactionMk
|
||||
? magReactionEquals(myReactionMk, parsed)
|
||||
: undefined,
|
||||
] as types.ReactionPair;
|
||||
});
|
||||
});
|
||||
|
||||
const initialReactions = new Set(
|
||||
Array.isArray(props.note.reactions)
|
||||
? props.note.reactions.map((v) => magReactionToLegacy(v[0]))
|
||||
: Object.keys(props.note.reactions)
|
||||
.map((v) => magConvertReaction(v))
|
||||
.map(magReactionToLegacy)
|
||||
);
|
||||
|
||||
const isMe = computed(() => $i && $i.id === props.note.user.id);
|
||||
</script>
|
||||
|
|
|
@ -118,7 +118,7 @@ const renote = async (viaKeyboard = false, ev?: MouseEvent) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (["public", "home"].includes(props.note.visibility)) {
|
||||
if (["public", "home", "Public", "home"].includes(props.note.visibility)) {
|
||||
buttonActions.push({
|
||||
text: `${i18n.ts.renote} (${i18n.ts._visibility.home})`,
|
||||
icon: "ph-house ph-bold ph-lg",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<img
|
||||
v-if="customEmoji"
|
||||
v-if="isCustom"
|
||||
class="mk-emoji custom"
|
||||
:class="{ normal, noStyle }"
|
||||
:src="url"
|
||||
|
@ -22,56 +22,43 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import * as Misskey from "calckey-js";
|
||||
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
|
||||
import { char2filePath } from "@/scripts/twemoji-base";
|
||||
import { defaultStore } from "@/store";
|
||||
import { instance } from "@/instance";
|
||||
import { packed } from "magnetar-common";
|
||||
import { magTransProperty } from "@/scripts-mag/mag-util";
|
||||
import { types } from "magnetar-common";
|
||||
import {
|
||||
magIsCustomEmoji,
|
||||
magIsUnicodeEmoji,
|
||||
magReactionToLegacy,
|
||||
} from "@/scripts-mag/mag-util";
|
||||
|
||||
const props = defineProps<{
|
||||
emoji: string;
|
||||
emoji: types.Reaction;
|
||||
normal?: boolean;
|
||||
noStyle?: boolean;
|
||||
customEmojis?: (
|
||||
| packed.PackEmojiBase
|
||||
| Pick<Misskey.entities.CustomEmoji, "name" | "url">
|
||||
)[];
|
||||
isReaction?: boolean;
|
||||
}>();
|
||||
|
||||
const isCustom = computed(() => props.emoji.startsWith(":"));
|
||||
const char = computed(() => (isCustom.value ? null : props.emoji));
|
||||
const isCustom = computed(() => magIsCustomEmoji(props.emoji));
|
||||
|
||||
const char = computed(() =>
|
||||
magIsUnicodeEmoji(props.emoji) ? props.emoji : null
|
||||
);
|
||||
const useOsNativeEmojis = computed(
|
||||
() => defaultStore.state.useOsNativeEmojis && !props.isReaction
|
||||
);
|
||||
const ce = computed(() => props.customEmojis ?? instance.emojis ?? []);
|
||||
const customEmoji = computed(() =>
|
||||
isCustom.value
|
||||
? ce.value.find(
|
||||
(x) =>
|
||||
magTransProperty(x, "shortcode", "name") ===
|
||||
props.emoji.substring(1, props.emoji.length - 1)
|
||||
)
|
||||
: null
|
||||
);
|
||||
const url = computed(() => {
|
||||
if (char.value) {
|
||||
return char2filePath(char.value);
|
||||
} else if (customEmoji?.value?.url) {
|
||||
} else if (magIsCustomEmoji(props.emoji)) {
|
||||
return defaultStore.state.disableShowingAnimatedImages
|
||||
? getStaticImageUrl(customEmoji.value.url)
|
||||
: customEmoji.value.url;
|
||||
? getStaticImageUrl(props.emoji.url)
|
||||
: props.emoji.url;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
const alt = computed(() =>
|
||||
customEmoji.value
|
||||
? `:${magTransProperty(customEmoji.value, "shortcode", "name")}:`
|
||||
: char.value
|
||||
);
|
||||
const alt = computed(() => magReactionToLegacy(props.emoji));
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
|
@ -1,61 +0,0 @@
|
|||
import { App } from "vue";
|
||||
|
||||
import Mfm from "./global/MkMisskeyFlavoredMarkdown.vue";
|
||||
import MkA from "./global/MkA.vue";
|
||||
import MkAcct from "./global/MkAcct.vue";
|
||||
import MkAvatar from "./global/MkAvatar.vue";
|
||||
import MkEmoji from "./global/MkEmoji.vue";
|
||||
import MkUserName from "./global/MkUserName.vue";
|
||||
import MkEllipsis from "./global/MkEllipsis.vue";
|
||||
import MkTime from "./global/MkTime.vue";
|
||||
import MkUrl from "./global/MkUrl.vue";
|
||||
import I18n from "./global/i18n";
|
||||
import RouterView from "./global/RouterView.vue";
|
||||
import MkLoading from "./global/MkLoading.vue";
|
||||
import MkError from "./global/MkError.vue";
|
||||
import MkAd from "./global/MkAd.vue";
|
||||
import MkPageHeader from "./global/MkPageHeader.vue";
|
||||
import MkSpacer from "./global/MkSpacer.vue";
|
||||
import MkStickyContainer from "./global/MkStickyContainer.vue";
|
||||
|
||||
export default function (app: App) {
|
||||
app.component("I18n", I18n);
|
||||
app.component("RouterView", RouterView);
|
||||
app.component("Mfm", Mfm);
|
||||
app.component("MkA", MkA);
|
||||
app.component("MkAcct", MkAcct);
|
||||
app.component("MkAvatar", MkAvatar);
|
||||
app.component("MkEmoji", MkEmoji);
|
||||
app.component("MkUserName", MkUserName);
|
||||
app.component("MkEllipsis", MkEllipsis);
|
||||
app.component("MkTime", MkTime);
|
||||
app.component("MkUrl", MkUrl);
|
||||
app.component("MkLoading", MkLoading);
|
||||
app.component("MkError", MkError);
|
||||
app.component("MkAd", MkAd);
|
||||
app.component("MkPageHeader", MkPageHeader);
|
||||
app.component("MkSpacer", MkSpacer);
|
||||
app.component("MkStickyContainer", MkStickyContainer);
|
||||
}
|
||||
|
||||
declare module "@vue/runtime-core" {
|
||||
export interface GlobalComponents {
|
||||
I18n: typeof I18n;
|
||||
RouterView: typeof RouterView;
|
||||
Mfm: typeof Mfm;
|
||||
MkA: typeof MkA;
|
||||
MkAcct: typeof MkAcct;
|
||||
MkAvatar: typeof MkAvatar;
|
||||
MkEmoji: typeof MkEmoji;
|
||||
MkUserName: typeof MkUserName;
|
||||
MkEllipsis: typeof MkEllipsis;
|
||||
MkTime: typeof MkTime;
|
||||
MkUrl: typeof MkUrl;
|
||||
MkLoading: typeof MkLoading;
|
||||
MkError: typeof MkError;
|
||||
MkAd: typeof MkAd;
|
||||
MkPageHeader: typeof MkPageHeader;
|
||||
MkSpacer: typeof MkSpacer;
|
||||
MkStickyContainer: typeof MkStickyContainer;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ import type { VNode } from "vue";
|
|||
import MkUrl from "@/components/global/MkUrl.vue";
|
||||
import MkLink from "@/components/MkLink.vue";
|
||||
import MkMention from "@/components/MkMention.vue";
|
||||
import MkEmoji from "@/components/global/MkEmoji.vue";
|
||||
import { concat } from "@/scripts/array";
|
||||
import MkFormula from "@/components/MkFormula.vue";
|
||||
import MkCode from "@/components/MkCode.vue";
|
||||
|
@ -13,6 +12,12 @@ import MkA from "@/components/global/MkA.vue";
|
|||
import { host } from "@/config";
|
||||
import { reducedMotion } from "@/scripts/reduced-motion";
|
||||
import { defaultStore } from "@/store";
|
||||
import MagEmoji from "@/components/global/MagEmoji.vue";
|
||||
import {
|
||||
magConvertReaction,
|
||||
magReactionEquals,
|
||||
magReactionToLegacy,
|
||||
} from "@/scripts-mag/mag-util";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -537,10 +542,20 @@ export default defineComponent({
|
|||
|
||||
case "emojiCode": {
|
||||
return [
|
||||
h(MkEmoji, {
|
||||
h(MagEmoji, {
|
||||
key: Math.random(),
|
||||
emoji: `:${token.props.name}:`,
|
||||
customEmojis: this.customEmojis,
|
||||
emoji: magConvertReaction(
|
||||
`:${token.props.name}:`,
|
||||
(name, host) =>
|
||||
this.customEmojis.find((e) =>
|
||||
magReactionEquals(
|
||||
magConvertReaction(
|
||||
`:${e.name}:`
|
||||
),
|
||||
{ name, host, url: null! }
|
||||
)
|
||||
)?.url ?? null
|
||||
),
|
||||
normal: this.plain,
|
||||
}),
|
||||
];
|
||||
|
@ -548,10 +563,9 @@ export default defineComponent({
|
|||
|
||||
case "unicodeEmoji": {
|
||||
return [
|
||||
h(MkEmoji, {
|
||||
h(MagEmoji, {
|
||||
key: Math.random(),
|
||||
emoji: token.props.emoji,
|
||||
customEmojis: this.customEmojis,
|
||||
normal: this.plain,
|
||||
}),
|
||||
];
|
||||
|
|
|
@ -23,7 +23,6 @@ import { compareVersions } from "compare-versions";
|
|||
|
||||
import widgets from "@/widgets";
|
||||
import directives from "@/directives";
|
||||
import components from "@/components";
|
||||
import { lang, ui, version } from "@/config";
|
||||
import { applyTheme } from "@/scripts/theme";
|
||||
import { isDeviceDarkmode } from "@/scripts/is-device-darkmode";
|
||||
|
@ -43,6 +42,68 @@ import { reactionPicker } from "@/scripts/reaction-picker";
|
|||
import { getUrlWithoutLoginId } from "@/scripts/login-id";
|
||||
import { getAccountFromId } from "@/scripts/get-account-from-id";
|
||||
|
||||
import { App } from "vue";
|
||||
|
||||
import Mfm from "./components/global/MkMisskeyFlavoredMarkdown.vue";
|
||||
import MkA from "./components/global/MkA.vue";
|
||||
import MkAcct from "./components/global/MkAcct.vue";
|
||||
import MkAvatar from "./components/global/MkAvatar.vue";
|
||||
import MagEmoji from "./components/global/MagEmoji.vue";
|
||||
import MkUserName from "./components/global/MkUserName.vue";
|
||||
import MkEllipsis from "./components/global/MkEllipsis.vue";
|
||||
import MkTime from "./components/global/MkTime.vue";
|
||||
import MkUrl from "./components/global/MkUrl.vue";
|
||||
import I18n from "./components/global/i18n";
|
||||
import RouterView from "./components/global/RouterView.vue";
|
||||
import MkLoading from "./components/global/MkLoading.vue";
|
||||
import MkError from "./components/global/MkError.vue";
|
||||
import MkAd from "./components/global/MkAd.vue";
|
||||
import MkPageHeader from "./components/global/MkPageHeader.vue";
|
||||
import MkSpacer from "./components/global/MkSpacer.vue";
|
||||
import MkStickyContainer from "./components/global/MkStickyContainer.vue";
|
||||
|
||||
function globalComponents(app: App) {
|
||||
app.component("I18n", I18n);
|
||||
app.component("RouterView", RouterView);
|
||||
app.component("Mfm", Mfm);
|
||||
app.component("MkA", MkA);
|
||||
app.component("MkAcct", MkAcct);
|
||||
app.component("MkAvatar", MkAvatar);
|
||||
app.component("MagEmoji", MagEmoji);
|
||||
app.component("MkUserName", MkUserName);
|
||||
app.component("MkEllipsis", MkEllipsis);
|
||||
app.component("MkTime", MkTime);
|
||||
app.component("MkUrl", MkUrl);
|
||||
app.component("MkLoading", MkLoading);
|
||||
app.component("MkError", MkError);
|
||||
app.component("MkAd", MkAd);
|
||||
app.component("MkPageHeader", MkPageHeader);
|
||||
app.component("MkSpacer", MkSpacer);
|
||||
app.component("MkStickyContainer", MkStickyContainer);
|
||||
}
|
||||
|
||||
declare module "@vue/runtime-core" {
|
||||
export interface GlobalComponents {
|
||||
I18n: typeof I18n;
|
||||
RouterView: typeof RouterView;
|
||||
Mfm: typeof Mfm;
|
||||
MkA: typeof MkA;
|
||||
MkAcct: typeof MkAcct;
|
||||
MkAvatar: typeof MkAvatar;
|
||||
MagEmoji: typeof MagEmoji;
|
||||
MkUserName: typeof MkUserName;
|
||||
MkEllipsis: typeof MkEllipsis;
|
||||
MkTime: typeof MkTime;
|
||||
MkUrl: typeof MkUrl;
|
||||
MkLoading: typeof MkLoading;
|
||||
MkError: typeof MkError;
|
||||
MkAd: typeof MkAd;
|
||||
MkPageHeader: typeof MkPageHeader;
|
||||
MkSpacer: typeof MkSpacer;
|
||||
MkStickyContainer: typeof MkStickyContainer;
|
||||
}
|
||||
}
|
||||
|
||||
const accounts = localStorage.getItem("accounts");
|
||||
if (accounts) {
|
||||
set("accounts", JSON.parse(accounts));
|
||||
|
@ -208,7 +269,7 @@ function checkForSplash() {
|
|||
|
||||
widgets(app);
|
||||
directives(app);
|
||||
components(app);
|
||||
globalComponents(app);
|
||||
|
||||
checkForSplash();
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ import {
|
|||
MagApiClient,
|
||||
Method,
|
||||
} from "magnetar-common";
|
||||
import { magReactionToLegacy } from "@/scripts-mag/mag-util";
|
||||
import { types } from "magnetar-common";
|
||||
|
||||
export const pendingApiRequestsCount = ref(0);
|
||||
|
||||
|
@ -814,8 +816,8 @@ export async function pickEmoji(src: HTMLElement | null, opts) {
|
|||
...opts,
|
||||
},
|
||||
{
|
||||
done: (emoji) => {
|
||||
resolve(emoji);
|
||||
done: (emoji: types.Reaction) => {
|
||||
resolve(magReactionToLegacy(emoji));
|
||||
},
|
||||
},
|
||||
"closed"
|
||||
|
@ -910,11 +912,11 @@ export async function openEmojiPicker(
|
|||
...opts,
|
||||
},
|
||||
{
|
||||
chosen: (emoji) => {
|
||||
insertTextAtCursor(activeTextarea, emoji);
|
||||
chosen: (emoji: types.Reaction) => {
|
||||
insertTextAtCursor(activeTextarea, magReactionToLegacy(emoji));
|
||||
},
|
||||
done: (emoji) => {
|
||||
insertTextAtCursor(activeTextarea, emoji);
|
||||
done: (emoji: types.Reaction) => {
|
||||
insertTextAtCursor(activeTextarea, magReactionToLegacy(emoji));
|
||||
},
|
||||
closed: () => {
|
||||
openingEmojiPicker!.dispose();
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
:class="{
|
||||
_physics_circle_: !emoji.emoji.startsWith(':'),
|
||||
}"
|
||||
><MkEmoji
|
||||
><MagEmoji
|
||||
class="emoji"
|
||||
:emoji="emoji.emoji"
|
||||
:custom-emojis="$instance.emojis"
|
||||
|
|
|
@ -130,13 +130,22 @@
|
|||
i18n.ts.defaultReaction
|
||||
}}</template>
|
||||
<option value="⭐">
|
||||
<MkEmoji emoji="⭐" style="height: 1.7em" />
|
||||
<MagEmoji
|
||||
emoji="⭐"
|
||||
style="height: 1.7em"
|
||||
/>
|
||||
</option>
|
||||
<option value="👍">
|
||||
<MkEmoji emoji="👍" style="height: 1.7em" />
|
||||
<MagEmoji
|
||||
emoji="👍"
|
||||
style="height: 1.7em"
|
||||
/>
|
||||
</option>
|
||||
<option value="❤️">
|
||||
<MkEmoji emoji="❤️" style="height: 1.7em" />
|
||||
<MagEmoji
|
||||
emoji="❤️"
|
||||
style="height: 1.7em"
|
||||
/>
|
||||
</option>
|
||||
<option value="custom">
|
||||
<FormInput
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
}}</template>
|
||||
<div v-panel style="border-radius: 6px">
|
||||
<XDraggable
|
||||
v-model="reactions"
|
||||
v-model="reactionsResolved"
|
||||
class="zoaiodol"
|
||||
:item-key="(item) => item"
|
||||
:item-key="(item) => magReactionToLegacy(item)"
|
||||
animation="150"
|
||||
delay="100"
|
||||
delay-on-touch-only="true"
|
||||
|
@ -23,7 +23,7 @@
|
|||
class="_button item"
|
||||
@click="remove(element, $event)"
|
||||
>
|
||||
<MkEmoji
|
||||
<MagEmoji
|
||||
:emoji="element"
|
||||
style="height: 1.7em"
|
||||
class="emoji"
|
||||
|
@ -48,22 +48,22 @@
|
|||
<FormRadios v-model="reactionPickerSkinTone" class="_formBlock">
|
||||
<template #label>{{ i18n.ts.reactionPickerSkinTone }}</template>
|
||||
<option :value="1">
|
||||
<MkEmoji style="height: 1.7em" emoji="✌️" />
|
||||
<MagEmoji style="height: 1.7em" emoji="✌️" />
|
||||
</option>
|
||||
<option :value="6">
|
||||
<MkEmoji style="height: 1.7em" emoji="✌🏿" />
|
||||
<MagEmoji style="height: 1.7em" emoji="✌🏿" />
|
||||
</option>
|
||||
<option :value="5">
|
||||
<MkEmoji style="height: 1.7em" emoji="✌🏾" />
|
||||
<MagEmoji style="height: 1.7em" emoji="✌🏾" />
|
||||
</option>
|
||||
<option :value="4">
|
||||
<MkEmoji style="height: 1.7em" emoji="✌🏽" />
|
||||
<MagEmoji style="height: 1.7em" emoji="✌🏽" />
|
||||
</option>
|
||||
<option :value="3">
|
||||
<MkEmoji style="height: 1.7em" emoji="✌🏼" />
|
||||
<MagEmoji style="height: 1.7em" emoji="✌🏼" />
|
||||
</option>
|
||||
<option :value="2">
|
||||
<MkEmoji style="height: 1.7em" emoji="✌🏻" />
|
||||
<MagEmoji style="height: 1.7em" emoji="✌🏻" />
|
||||
</option>
|
||||
</FormRadios>
|
||||
<FormRadios v-model="reactionPickerSize" class="_formBlock">
|
||||
|
@ -123,7 +123,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent, watch } from "vue";
|
||||
import { computed, defineAsyncComponent, watch } from "vue";
|
||||
import XDraggable from "vuedraggable";
|
||||
import FormRadios from "@/components/form/radios.vue";
|
||||
import FromSlot from "@/components/form/slot.vue";
|
||||
|
@ -135,6 +135,13 @@ import { defaultStore } from "@/store";
|
|||
import { i18n } from "@/i18n";
|
||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||
import { unisonReload } from "@/scripts/unison-reload";
|
||||
import MagEmoji from "@/components/global/MagEmoji.vue";
|
||||
import {
|
||||
magConvertReaction,
|
||||
magReactionToLegacy,
|
||||
} from "@/scripts-mag/mag-util";
|
||||
import { instance } from "@/instance";
|
||||
import { types } from "magnetar-common";
|
||||
|
||||
async function reloadAsk() {
|
||||
const { canceled } = await os.confirm({
|
||||
|
@ -146,8 +153,15 @@ async function reloadAsk() {
|
|||
unisonReload();
|
||||
}
|
||||
|
||||
const resolveEmojis = (e: string) =>
|
||||
magConvertReaction(e, (name) => {
|
||||
return instance.emojis.find((e) => e.name === name)?.url!;
|
||||
});
|
||||
|
||||
let reactions = $ref(structuredClone(defaultStore.state.reactions));
|
||||
|
||||
let reactionsResolved = computed(() => reactions.map(resolveEmojis));
|
||||
|
||||
const reactionPickerSkinTone = $computed(
|
||||
defaultStore.makeGetterSetter("reactionPickerSkinTone")
|
||||
);
|
||||
|
@ -174,13 +188,15 @@ function save() {
|
|||
defaultStore.set("reactions", reactions);
|
||||
}
|
||||
|
||||
function remove(reaction, ev: MouseEvent) {
|
||||
function remove(reaction: types.Reaction, ev: MouseEvent) {
|
||||
os.popupMenu(
|
||||
[
|
||||
{
|
||||
text: i18n.ts.remove,
|
||||
action: () => {
|
||||
reactions = reactions.filter((x) => x !== reaction);
|
||||
reactions = reactions.filter(
|
||||
(x) => x !== magReactionToLegacy(reaction)
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -7,17 +7,17 @@
|
|||
<div class="shape2"></div>
|
||||
<img src="/client-assets/misskey.svg" class="misskey" />
|
||||
<div class="emojis">
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="⭐" />
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="❤️" />
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="😆" />
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="🤔" />
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="😮" />
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="🎉" />
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="💢" />
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="😥" />
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="😇" />
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="🦊" />
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="🦋" />
|
||||
<MagEmoji :normal="true" :no-style="true" emoji="⭐" />
|
||||
<MagEmoji :normal="true" :no-style="true" emoji="❤️" />
|
||||
<MagEmoji :normal="true" :no-style="true" emoji="😆" />
|
||||
<MagEmoji :normal="true" :no-style="true" emoji="🤔" />
|
||||
<MagEmoji :normal="true" :no-style="true" emoji="😮" />
|
||||
<MagEmoji :normal="true" :no-style="true" emoji="🎉" />
|
||||
<MagEmoji :normal="true" :no-style="true" emoji="💢" />
|
||||
<MagEmoji :normal="true" :no-style="true" emoji="😥" />
|
||||
<MagEmoji :normal="true" :no-style="true" emoji="😇" />
|
||||
<MagEmoji :normal="true" :no-style="true" emoji="🦊" />
|
||||
<MagEmoji :normal="true" :no-style="true" emoji="🦋" />
|
||||
</div>
|
||||
<div class="main">
|
||||
<img
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import * as Misskey from "calckey-js";
|
||||
import { packed, types } from "magnetar-common";
|
||||
import { UnicodeEmojiDef } from "@/scripts/emojilist";
|
||||
|
||||
// https://stackoverflow.com/a/50375286 Evil magic
|
||||
type Dist<U> = U extends any ? (k: U) => void : never;
|
||||
|
@ -101,8 +102,16 @@ export function magReactionSelf(
|
|||
([, , reacted]) => reacted === true
|
||||
)?.[0];
|
||||
return typeof found !== "undefined" ? magReactionToLegacy(found) : null;
|
||||
} else if ((note as Misskey.entities.Note).myReaction !== "undefined") {
|
||||
return (note as Misskey.entities.Note).myReaction ?? null;
|
||||
} else if (
|
||||
typeof (note as Misskey.entities.Note).myReaction !== "undefined"
|
||||
) {
|
||||
return (note as Misskey.entities.Note).myReaction
|
||||
? magReactionToLegacy(
|
||||
magConvertReaction(
|
||||
(note as Misskey.entities.Note).myReaction!
|
||||
)
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -164,22 +173,54 @@ export function magLegacyVisibility(
|
|||
}
|
||||
}
|
||||
|
||||
export function magCustomEmoji(
|
||||
emoji: Misskey.entities.CustomEmoji
|
||||
): types.ReactionShortcode {
|
||||
return {
|
||||
name: emoji.name,
|
||||
host: null,
|
||||
url: emoji.url,
|
||||
};
|
||||
}
|
||||
|
||||
export function magUnicodeEmoji(emoji: UnicodeEmojiDef): types.ReactionUnicode {
|
||||
return emoji.emoji;
|
||||
}
|
||||
|
||||
export function magIsCustomEmoji(
|
||||
emoji: types.Reaction
|
||||
): emoji is types.ReactionShortcode {
|
||||
return (
|
||||
typeof emoji === "object" &&
|
||||
emoji !== null &&
|
||||
typeof emoji["name"] !== "undefined"
|
||||
);
|
||||
}
|
||||
|
||||
export function magIsUnicodeEmoji(
|
||||
emoji: types.Reaction
|
||||
): emoji is types.ReactionUnicode {
|
||||
return typeof emoji === "string";
|
||||
}
|
||||
|
||||
export function magConvertReaction(
|
||||
reaction: string,
|
||||
urlHint?: string | null
|
||||
urlHint?: ((name: string, host: string | null) => string) | string | null
|
||||
): types.Reaction {
|
||||
if (reaction.endsWith("@.:")) {
|
||||
reaction = reaction.replaceAll(/@\.:$/, ":");
|
||||
}
|
||||
|
||||
if (reaction.match(/^:.+:$/)) {
|
||||
reaction = reaction.replaceAll(/(^:) | (:$)/, "");
|
||||
reaction = reaction.replaceAll(":", "");
|
||||
|
||||
const [name, maybeHost] = reaction.split("@");
|
||||
|
||||
const host = (maybeHost || ".") === "." ? null : maybeHost;
|
||||
|
||||
const [name, host] = reaction.split("@");
|
||||
return {
|
||||
name,
|
||||
host: host || null,
|
||||
url: urlHint!,
|
||||
host,
|
||||
url:
|
||||
typeof urlHint === "function"
|
||||
? urlHint(name, host || null)
|
||||
: urlHint!,
|
||||
};
|
||||
} else {
|
||||
return reaction;
|
||||
|
@ -213,30 +254,31 @@ export function magReactionPairToLegacy(
|
|||
return [legacy, reaction[1]];
|
||||
}
|
||||
|
||||
export function magReactionEquals(a: types.Reaction, b: types.Reaction) {
|
||||
if (typeof a !== typeof b) return false;
|
||||
|
||||
if (magIsUnicodeEmoji(a)) {
|
||||
return a === b;
|
||||
} else if (magIsCustomEmoji(a)) {
|
||||
const { name, host } = b as {
|
||||
name: string;
|
||||
host: string | null;
|
||||
};
|
||||
const { name: rName, host: rHost } = a;
|
||||
|
||||
return name === rName && (host ?? null) === (rHost ?? null);
|
||||
} else if ("raw" in a && "raw" in (b as { raw: string })) {
|
||||
return a.raw === (b as { raw: string }).raw;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function magReactionIndex(
|
||||
reactions: types.ReactionPair[],
|
||||
reactionType: types.Reaction
|
||||
) {
|
||||
return reactions.findIndex(([r, ,]) => {
|
||||
if (typeof r !== typeof reactionType) return false;
|
||||
|
||||
if (typeof r === "string") {
|
||||
return r === reactionType;
|
||||
} else if (
|
||||
"name" in r &&
|
||||
"name" in (reactionType as { name: string; host: string | null })
|
||||
) {
|
||||
const { name, host } = reactionType as {
|
||||
name: string;
|
||||
host: string | null;
|
||||
};
|
||||
const { name: rName, host: rHost } = r;
|
||||
|
||||
return name === rName && (host ?? null) === (rHost ?? null);
|
||||
} else if ("raw" in r && "raw" in (reactionType as { raw: string })) {
|
||||
return r.raw === (reactionType as { raw: string }).raw;
|
||||
}
|
||||
|
||||
return false;
|
||||
return magReactionEquals(r, reactionType);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { defineAsyncComponent, Ref, ref } from "vue";
|
||||
import { popup } from "@/os";
|
||||
import { types } from "magnetar-common";
|
||||
|
||||
class ReactionPicker {
|
||||
private src: Ref<HTMLElement | null> = ref(null);
|
||||
private manualShowing = ref(false);
|
||||
private onChosen?: (reaction: string) => void;
|
||||
private onChosen?: (reaction: types.Reaction) => void;
|
||||
private onClosed?: () => void;
|
||||
|
||||
constructor() {
|
||||
|
@ -22,7 +23,7 @@ class ReactionPicker {
|
|||
manualShowing: this.manualShowing,
|
||||
},
|
||||
{
|
||||
done: (reaction) => {
|
||||
done: (reaction: types.Reaction) => {
|
||||
this.onChosen!(reaction);
|
||||
},
|
||||
close: () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { onUnmounted, Ref } from "vue";
|
||||
import { onUnmounted, Ref, toRaw } from "vue";
|
||||
import * as misskey from "calckey-js";
|
||||
import { stream } from "@/stream";
|
||||
import { $i } from "@/account";
|
||||
|
@ -28,23 +28,6 @@ export function useNoteCapture(props: {
|
|||
case "reacted": {
|
||||
const reaction = body.reaction as string;
|
||||
|
||||
if (body.emoji) {
|
||||
const emojis = note.value.emojis || [];
|
||||
if (!emojis.includes(body.emoji)) {
|
||||
note.value.emojis = [
|
||||
...emojis,
|
||||
{
|
||||
id: body.emoji.id,
|
||||
shortcode: body.emoji.name,
|
||||
url: body.emoji.url,
|
||||
width: body.emoji.width ?? null,
|
||||
height: body.emoji.height ?? null,
|
||||
category: null,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const reactionType = magConvertReaction(
|
||||
reaction,
|
||||
body?.emoji?.url
|
||||
|
@ -57,7 +40,7 @@ export function useNoteCapture(props: {
|
|||
const selfReact = ($i && body.userId === $i.id) || false;
|
||||
if (foundReaction >= 0) {
|
||||
note.value.reactions[foundReaction] = [
|
||||
note.value.reactions[foundReaction][0],
|
||||
toRaw(note.value.reactions[foundReaction][0]),
|
||||
note.value.reactions[foundReaction][1] + 1,
|
||||
selfReact,
|
||||
];
|
||||
|
@ -76,12 +59,15 @@ export function useNoteCapture(props: {
|
|||
reactionType
|
||||
);
|
||||
|
||||
const selfUnReact = ($i && body.userId === $i.id) || false;
|
||||
if (foundReaction >= 0) {
|
||||
const cnt = note.value.reactions[foundReaction][1];
|
||||
const [name, cnt, selfReact] =
|
||||
note.value.reactions[foundReaction];
|
||||
|
||||
note.value.reactions[foundReaction] = [
|
||||
note.value.reactions[foundReaction][0],
|
||||
cnt === 0 ? 0 : cnt - 1,
|
||||
false,
|
||||
name,
|
||||
Math.max(0, cnt - 1),
|
||||
!selfUnReact && (selfReact ?? false),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@ export { PollBase } from "./types/PollBase";
|
|||
export { PollChoice } from "./types/PollChoice";
|
||||
export { Reaction } from "./types/Reaction";
|
||||
export { ReactionPair } from "./types/ReactionPair";
|
||||
export { ReactionShortcode } from "./types/ReactionShortcode";
|
||||
export { ReactionUnicode } from "./types/ReactionUnicode";
|
||||
export { ReactionUnknown } from "./types/ReactionUnknown";
|
||||
export { AvatarDecoration } from "./types/AvatarDecoration";
|
||||
export { ProfileField } from "./types/ProfileField";
|
||||
export { SecurityKeyBase } from "./types/SecurityKeyBase";
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ReactionShortcode } from "./ReactionShortcode";
|
||||
import type { ReactionUnicode } from "./ReactionUnicode";
|
||||
import type { ReactionUnknown } from "./ReactionUnknown";
|
||||
|
||||
export type Reaction = string | { name: string, host: string | null, url: string, } | { raw: string, };
|
||||
export type Reaction = ReactionUnicode | ReactionShortcode | ReactionUnknown;
|
|
@ -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 ReactionShortcode { name: string, host: string | null, url: string, }
|
|
@ -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 type ReactionUnicode = string;
|
|
@ -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 ReactionUnknown { raw: string, }
|
|
@ -164,6 +164,9 @@ importers:
|
|||
'@vue/compiler-sfc':
|
||||
specifier: 3.3.4
|
||||
version: 3.3.4
|
||||
'@vue/runtime-core':
|
||||
specifier: 3.3.4
|
||||
version: 3.3.4
|
||||
autobind-decorator:
|
||||
specifier: 2.4.0
|
||||
version: 2.4.0
|
||||
|
@ -1007,7 +1010,7 @@ packages:
|
|||
hasBin: true
|
||||
peerDependencies:
|
||||
'@swc/core': ^1.2.66
|
||||
chokidar: ^3.5.1
|
||||
chokidar: ^3.3.1
|
||||
peerDependenciesMeta:
|
||||
chokidar:
|
||||
optional: true
|
||||
|
@ -1440,7 +1443,7 @@ packages:
|
|||
resolution: {integrity: sha512-Km7XAtUIduROw7QPgvcft0lIupeG8a8rdKL8RiSyKvlE7dYY31fEn41HVuQsRFDuROA8tA4K2UVL+WdfFmErBA==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@types/node': 20.8.10
|
||||
'@types/node': 14.18.63
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
|
@ -1982,8 +1985,8 @@ packages:
|
|||
async-done: 1.3.2
|
||||
dev: true
|
||||
|
||||
/async@3.2.4:
|
||||
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
|
||||
/async@3.2.5:
|
||||
resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==}
|
||||
dev: true
|
||||
|
||||
/asynckit@0.4.0:
|
||||
|
@ -2432,8 +2435,8 @@ packages:
|
|||
engines: {node: '>=6.0'}
|
||||
dev: true
|
||||
|
||||
/ci-info@3.8.0:
|
||||
resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==}
|
||||
/ci-info@3.9.0:
|
||||
resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
|
@ -3793,7 +3796,7 @@ packages:
|
|||
/getos@3.2.1:
|
||||
resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==}
|
||||
dependencies:
|
||||
async: 3.2.4
|
||||
async: 3.2.5
|
||||
dev: true
|
||||
|
||||
/getpass@0.1.7:
|
||||
|
@ -4298,7 +4301,7 @@ packages:
|
|||
resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
ci-info: 3.8.0
|
||||
ci-info: 3.9.0
|
||||
dev: true
|
||||
|
||||
/is-core-module@2.12.1:
|
||||
|
@ -4541,8 +4544,8 @@ packages:
|
|||
supports-color: 8.1.1
|
||||
dev: true
|
||||
|
||||
/joi@17.9.2:
|
||||
resolution: {integrity: sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==}
|
||||
/joi@17.11.0:
|
||||
resolution: {integrity: sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==}
|
||||
dependencies:
|
||||
'@hapi/hoek': 9.3.0
|
||||
'@hapi/topo': 5.1.0
|
||||
|
@ -7509,7 +7512,7 @@ packages:
|
|||
hasBin: true
|
||||
dependencies:
|
||||
axios: 0.25.0(debug@4.3.4)
|
||||
joi: 17.9.2
|
||||
joi: 17.11.0
|
||||
lodash: 4.17.21
|
||||
minimist: 1.2.8
|
||||
rxjs: 7.8.1
|
||||
|
|
|
@ -106,19 +106,32 @@ pack!(
|
|||
Required<Id> as id & Required<NoteBase> as note & Optional<NoteSelfContextExt> as user_context & Optional<NoteAttachmentExt> as attachment & Optional<NoteDetailExt> as detail
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
#[repr(transparent)]
|
||||
pub struct ReactionUnicode(pub String);
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
pub struct ReactionShortcode {
|
||||
pub name: String,
|
||||
pub host: Option<String>,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
pub struct ReactionUnknown {
|
||||
pub raw: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(untagged)]
|
||||
pub enum Reaction {
|
||||
Unicode(String),
|
||||
Shortcode {
|
||||
name: String,
|
||||
host: Option<String>,
|
||||
url: String,
|
||||
},
|
||||
Unknown {
|
||||
raw: String,
|
||||
},
|
||||
Unicode(ReactionUnicode),
|
||||
Shortcode(ReactionShortcode),
|
||||
Unknown(ReactionUnknown),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
|
|
|
@ -25,6 +25,7 @@ use magnetar_sdk::types::emoji::EmojiContext;
|
|||
use magnetar_sdk::types::note::{
|
||||
NoteAttachmentExt, NoteBase, NoteDetailExt, NoteSelfContextExt, PackNoteBase,
|
||||
PackNoteMaybeAttachments, PackNoteMaybeFull, PackPollBase, PollBase, Reaction, ReactionPair,
|
||||
ReactionShortcode, ReactionUnicode, ReactionUnknown,
|
||||
};
|
||||
use magnetar_sdk::types::{Id, MmXml};
|
||||
use magnetar_sdk::{mmm, Optional, Packed, Required};
|
||||
|
@ -243,6 +244,7 @@ impl NoteModel {
|
|||
.map(|(code, count, self_reacted)| {
|
||||
Ok((code, usize::deserialize(count)?, self_reacted))
|
||||
})
|
||||
.filter(|v| !v.as_ref().is_ok_and(|(_, count, _)| *count == 0))
|
||||
.collect::<Result<Vec<_>, serde_json::Error>>()?;
|
||||
// Pick out all successfully-parsed shortcode emojis
|
||||
let reactions_to_resolve = reactions_raw
|
||||
|
@ -274,25 +276,29 @@ impl NoteModel {
|
|||
.into_iter()
|
||||
.map(|(raw, count, self_reaction)| {
|
||||
let reaction = raw.either(
|
||||
|raw| Reaction::Unknown { raw },
|
||||
|raw| Reaction::Unknown(ReactionUnknown { raw }),
|
||||
|raw| match raw {
|
||||
RawReaction::Unicode(text) => Reaction::Unicode(text),
|
||||
RawReaction::Unicode(text) => Reaction::Unicode(ReactionUnicode(text)),
|
||||
RawReaction::Shortcode { shortcode, host } => reactions_fetched
|
||||
.iter()
|
||||
.find(|e| e.host == host && e.name == shortcode)
|
||||
.map_or_else(
|
||||
|| Reaction::Unknown {
|
||||
|| {
|
||||
Reaction::Unknown(ReactionUnknown {
|
||||
raw: format!(
|
||||
":{shortcode}{}:",
|
||||
host.as_deref()
|
||||
.map(|h| format!("@{h}"))
|
||||
.unwrap_or_default()
|
||||
),
|
||||
})
|
||||
},
|
||||
|e| Reaction::Shortcode {
|
||||
|e| {
|
||||
Reaction::Shortcode(ReactionShortcode {
|
||||
name: shortcode.clone(),
|
||||
host: host.clone(),
|
||||
url: e.public_url.clone(),
|
||||
})
|
||||
},
|
||||
),
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue