Skip to content

Commit

Permalink
feat: complete :emoji: to unicode emoji
Browse files Browse the repository at this point in the history
  • Loading branch information
anatawa12 committed Feb 18, 2024
1 parent e8ce14e commit 94069e7
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 6 deletions.
31 changes: 25 additions & 6 deletions packages/frontend/src/components/MkAutocomplete.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span class="name">{{ hashtag }}</span>
</li>
</ol>
<ol v-else-if="type === 'emoji' && emojis.length > 0" ref="suggests" :class="$style.list">
<ol v-else-if="type === 'emoji' || type === 'emojiComplete' && emojis.length > 0" ref="suggests" :class="$style.list">
<li v-for="emoji in emojis" :key="emoji.emoji" :class="$style.item" tabindex="-1" @click="complete(type, emoji.emoji)" @keydown="onKeydown">
<MkCustomEmoji v-if="'isCustomEmoji' in emoji && emoji.isCustomEmoji" :name="emoji.emoji" :class="$style.emoji"/>
<MkEmoji v-else :emoji="emoji.emoji" :class="$style.emoji"/>
Expand Down Expand Up @@ -67,10 +67,16 @@ export type CompleteInfo = {
payload: string;
query: string;
},
// `:emo` -> `:emoji:` or some unicode emoji
emoji: {
payload: string;
query: string;
},
// like emoji but for `:emoji:` -> unicode emoji
emojiComplete: {
payload: string;
query: string;
},
mfmTag: {
payload: string;
query: string;
Expand Down Expand Up @@ -98,8 +104,7 @@ type EmojiDef = {

const lib = emojilist.filter(x => x.category !== 'flags');

const emojiDb = computed(() => {
//#region Unicode Emoji
const unicodeEmojiDB = computed(() => {
const char2path = defaultStore.reactiveState.emojiStyle.value === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath;

const unicodeEmojiDB: EmojiDef[] = lib.map(x => ({
Expand All @@ -122,6 +127,12 @@ const emojiDb = computed(() => {
}

unicodeEmojiDB.sort((a, b) => a.name.length - b.name.length);

return unicodeEmojiDB;
});

const emojiDb = computed(() => {
//#region Unicode Emoji
//#endregion

//#region Custom Emoji
Expand Down Expand Up @@ -149,7 +160,7 @@ const emojiDb = computed(() => {
customEmojiDB.sort((a, b) => a.name.length - b.name.length);
//#endregion

return markRaw([...customEmojiDB, ...unicodeEmojiDB]);
return markRaw([...customEmojiDB, ...unicodeEmojiDB.value]);
});

export default {
Expand All @@ -171,7 +182,7 @@ type PropsType<T extends keyof CompleteInfo> = {
//const props = defineProps<PropsType<keyof CompleteInfo>>();
// ↑と同じだけど↓にしないとdiscriminated unionにならない。
// https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions
const props = defineProps<PropsType<'user'> | PropsType<'hashtag'> | PropsType<'emoji'> | PropsType<'mfmTag'> | PropsType<'mfmParam'>>();
const props = defineProps<PropsType<'user'> | PropsType<'hashtag'> | PropsType<'emoji'> | PropsType<'emojiComplete'> | PropsType<'mfmTag'> | PropsType<'mfmParam'>>();

const emit = defineEmits<{
<T extends keyof CompleteInfo>(event: 'done', value: { type: T; value: CompleteInfo[T]['payload'] }): void;
Expand All @@ -194,7 +205,7 @@ const zIndex = os.claimZIndex('high');
function complete<T extends keyof CompleteInfo>(type: T, value: CompleteInfo[T]['payload']) {
emit('done', { type, value });
emit('closed');
if (type === 'emoji') {
if (type === 'emoji' || type === 'emojiComplete') {
let recents = defaultStore.state.recentlyUsedEmojis;
recents = recents.filter((emoji: any) => emoji !== value);
recents.unshift(value);
Expand Down Expand Up @@ -281,6 +292,14 @@ function exec() {
}

emojis.value = emojiAutoComplete(props.q, emojiDb.value);
} else if (props.type === 'emojiComplete') {
if (!props.q || props.q === '') {
// 最近使った絵文字をサジェスト
emojis.value = defaultStore.state.recentlyUsedEmojis.map(emoji => unicodeEmojiDB.value.find(dbEmoji => dbEmoji.emoji === emoji)).filter(x => x) as EmojiDef[];
return;
}

emojis.value = emojiAutoComplete(props.q, unicodeEmojiDB.value);
} else if (props.type === 'mfmTag') {
if (!props.q || props.q === '') {
mfmTags.value = MFM_TAGS;
Expand Down
26 changes: 26 additions & 0 deletions packages/frontend/src/scripts/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ export class Autocomplete {
const isMfmParam = mfmParamIndex !== -1 && text.split(/\$\[[a-zA-Z]+/).pop()?.includes('.');
const isMfmTag = mfmTagIndex !== -1 && !isMfmParam;
const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
// :ok:などを🆗にするたいおぷ
const isEmojiCompleteToUnicode = !isEmoji && emojiIndex === text.length - 1;

let opened = false;

Expand Down Expand Up @@ -129,6 +131,14 @@ export class Autocomplete {
}
}

if (isEmojiCompleteToUnicode && !opened && this.onlyType.includes('emoji')) {
const emoji = text.substring(text.lastIndexOf(':', text.length - 2) + 1, text.length - 1);
if (!emoji.includes(' ')) {
this.open('emojiComplete', emoji);
opened = true;
}
}

if (isMfmTag && !opened && this.onlyType.includes('mfmTag')) {
const mfmTag = text.substring(mfmTagIndex + 1);
if (!mfmTag.includes(' ')) {
Expand Down Expand Up @@ -272,6 +282,22 @@ export class Autocomplete {
// 挿入
this.text = trimmedBefore + value + after;

// キャレットを戻す
nextTick(() => {
this.textarea.focus();
const pos = trimmedBefore.length + value.length;
this.textarea.setSelectionRange(pos, pos);
});
} else if (type === 'emojiComplete') {
const source = this.text;

const before = source.substring(0, caret);
const trimmedBefore = before.substring(0, before.lastIndexOf(':', before.length - 2));
const after = source.substring(caret);

// 挿入
this.text = trimmedBefore + value + after;

// キャレットを戻す
nextTick(() => {
this.textarea.focus();
Expand Down

0 comments on commit 94069e7

Please sign in to comment.