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