wip: feat: reaction import contextmenu
This commit is contained in:
parent
3bd7f3d301
commit
356d28928a
@ -6,6 +6,7 @@
|
|||||||
class="hkzvhatu _button"
|
class="hkzvhatu _button"
|
||||||
:class="{ reacted: note.myReaction == reaction, canToggle }"
|
:class="{ reacted: note.myReaction == reaction, canToggle }"
|
||||||
@click="toggleReaction()"
|
@click="toggleReaction()"
|
||||||
|
@contextmenu.stop="onContextmenu"
|
||||||
>
|
>
|
||||||
<XReactionIcon class="icon" :reaction="reaction" :custom-emojis="note.emojis"/>
|
<XReactionIcon class="icon" :reaction="reaction" :custom-emojis="note.emojis"/>
|
||||||
<span class="count">{{ count }}</span>
|
<span class="count">{{ count }}</span>
|
||||||
@ -13,13 +14,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 * as misskey from 'misskey-js';
|
||||||
import XDetails from '@/components/MkReactionsViewer.details.vue';
|
import XDetails from '@/components/MkReactionsViewer.details.vue';
|
||||||
import XReactionIcon from '@/components/MkReactionIcon.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 { openReactionImportMenu } from '@/scripts/reactionImportMenu';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
reaction: string;
|
reaction: string;
|
||||||
@ -88,6 +90,12 @@ useTooltip(buttonRef, async (showing) => {
|
|||||||
targetElement: buttonRef.value,
|
targetElement: buttonRef.value,
|
||||||
}, {}, 'closed');
|
}, {}, 'closed');
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
|
const onContextmenu = (e: MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log('contextmenu');
|
||||||
|
openReactionImportMenu(e, props.reaction);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<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"/>
|
<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-if="char && useOsNativeEmojis">{{ char }}</span>
|
||||||
<span v-else>{{ emoji }}</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 { char2filePath } from '@/scripts/twemoji-base';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
import { instance } from '@/instance';
|
import { instance } from '@/instance';
|
||||||
|
import { openReactionImportMenu } from '@/scripts/reactionImportMenu';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
emoji: string;
|
emoji: string;
|
||||||
@ -36,6 +37,11 @@ const url = computed(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const alt = computed(() => customEmoji.value ? `:${customEmoji.value.name}:` : char.value);
|
const alt = computed(() => customEmoji.value ? `:${customEmoji.value.name}:` : char.value);
|
||||||
|
|
||||||
|
const onContextmenu = (e: MouseEvent) => {
|
||||||
|
openReactionImportMenu(e, props.emoji);
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
121
packages/client/src/scripts/reactionImportMenu.ts
Normal file
121
packages/client/src/scripts/reactionImportMenu.ts
Normal 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);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user