wip: feat: reaction import contextmenu

This commit is contained in:
こけっち 2022-09-18 01:20:12 +09:00
parent 3bd7f3d301
commit 356d28928a
No known key found for this signature in database
GPG Key ID: 21460619C5FC4DD1
3 changed files with 137 additions and 2 deletions

View File

@ -6,6 +6,7 @@
class="hkzvhatu _button"
:class="{ reacted: note.myReaction == reaction, canToggle }"
@click="toggleReaction()"
@contextmenu.stop="onContextmenu"
>
<XReactionIcon class="icon" :reaction="reaction" :custom-emojis="note.emojis"/>
<span class="count">{{ count }}</span>
@ -13,13 +14,14 @@
</template>
<script lang="ts" setup>
import { computed, onMounted, ref, watch } from 'vue';
import { computed, defineAsyncComponent, onMounted, ref, watch } from 'vue';
import * as misskey from 'misskey-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 { openReactionImportMenu } from '@/scripts/reactionImportMenu';
const props = defineProps<{
reaction: string;
@ -88,6 +90,12 @@ useTooltip(buttonRef, async (showing) => {
targetElement: buttonRef.value,
}, {}, 'closed');
}, 100);
const onContextmenu = (e: MouseEvent) => {
e.preventDefault();
console.log('contextmenu');
openReactionImportMenu(e, props.reaction);
};
</script>
<style lang="scss" scoped>

View File

@ -1,5 +1,5 @@
<template>
<img v-if="customEmoji" class="mk-emoji custom" :class="{ normal, noStyle }" :src="url" :alt="alt" :title="alt" decoding="async"/>
<img v-if="customEmoji" class="mk-emoji custom" :class="{ normal, noStyle }" :src="url" :alt="alt" :title="alt" @contextmenu.stop="onContextmenu" decoding="async"/>
<img v-else-if="char && !useOsNativeEmojis" class="mk-emoji" :src="url" :alt="alt" :title="alt" decoding="async"/>
<span v-else-if="char && useOsNativeEmojis">{{ char }}</span>
<span v-else>{{ emoji }}</span>
@ -12,6 +12,7 @@ import { getStaticImageUrl } from '@/scripts/get-static-image-url';
import { char2filePath } from '@/scripts/twemoji-base';
import { defaultStore } from '@/store';
import { instance } from '@/instance';
import { openReactionImportMenu } from '@/scripts/reactionImportMenu';
const props = defineProps<{
emoji: string;
@ -36,6 +37,11 @@ const url = computed(() => {
}
});
const alt = computed(() => customEmoji.value ? `:${customEmoji.value.name}:` : char.value);
const onContextmenu = (e: MouseEvent) => {
openReactionImportMenu(e, props.emoji);
};
</script>
<style lang="scss" scoped>

View File

@ -0,0 +1,121 @@
import { $i } from '@/account';
import { i18n } from '@/i18n';
import * as os from '@/os';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import { defineAsyncComponent } from 'vue';
import { entities } from 'misskey-js';
import { MenuItem } from '@/types/menu';
import { CustomEmoji } from 'misskey-js/built/entities';
export async function openReactionImportMenu(ev: MouseEvent, reaction: string) {
console.log('openReactionImportMenu', reaction);
const getEmojiObject = emojiId => new Promise<Record<string, any> | null>(async resolve => {
const sinceId = await os.api('admin/emoji/list', {
limit: 1,
untilId: emojiId.id,
});
if (!sinceId || !sinceId[0] || !sinceId[0].id) {
resolve(null);
return;
}
const id = await os.api('admin/emoji/list', {
limit: 1,
sinceId: sinceId[0].id,
});
if (!id || !id[0]) {
resolve(null);
return;
}
resolve(id[0]);
});
const getEmojiId = async (emojiName: string) => {
const isLocal = (emojiName.match(/(?<=@).*\.*(?=:)/g) == null || emojiName.match(/(?<=@).*\.*(?=:)/g)[0] == '.');
if (isLocal) return null;
const host = emojiName.match(/(?<=@).*\.*(?=:)/g)[0];
console.log(host)
const name = emojiName.match(/(?<=:).*(?=@.*\.*(?=:))/g)[0];
console.log(name)
if (!host || !name) return;
const resList: Record<string, any>[] = await os.api('admin/emoji/list-remote', {
host,
query: name,
limit: 100,
});
const emojiId = await resList.find(emoji => emoji.name == name && emoji.host == host)?.id;
console.log(emojiId);
return emojiId;
}
const importEmoji = async (emojiName: string) => {
const emojiId = await getEmojiId(emojiName);
if (!emojiId) return;
os.api('admin/emoji/copy', {
emojiId: emojiId,
}).then(emoji => os.popup(defineAsyncComponent(() => import('@/pages/admin/emoji-edit-dialog.vue')), {
emoji: getEmojiObject(emoji),
}));
};
if (!($i?.isAdmin || $i?.isModerator)) return;
if (!reaction) return;
console.log('passed');
const menuItems: MenuItem[] = [{
type: 'label',
text: reaction,
}, {
type: 'button',
icon: 'fas fa-copy',
text: i18n.ts.copy,
action: () => {
copyToClipboard(reaction);
},
}];
const emojiId = await getEmojiId(reaction);
if (reaction.startsWith(':') && emojiId) {
menuItems.push({
type: 'button',
icon: 'fas fa-download',
text: i18n.ts.import,
action: async () => {
const duplication: boolean = await os.api('meta').then(meta => {
const emojis = meta.emojis;
return emojis.some(emoji => {
return (emoji.name === reaction.replace(/@.*\.*:/, ""));
});
});
console.log(await duplication);
if (await duplication) {
os.confirm({
type: 'warning',
text: i18n.ts.emojiAlreadyExists,
}).then(canceled => {
if (canceled) return;
importEmoji(reaction);
});
} else {
importEmoji(reaction);
}
},
});
}
os.contextMenu(menuItems, ev);
}