From c21caad1c505dda039817844681327b24e8a4e8f Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 1 Nov 2018 11:51:49 +0900 Subject: [PATCH] Custom emoji (#3061) * wip * wip * wip --- .../common/views/components/autocomplete.vue | 40 ++++++++++++++++--- .../components/misskey-flavored-markdown.ts | 16 +++++++- .../common/views/directives/autocomplete.ts | 4 +- src/client/app/mios.ts | 8 ++++ src/models/meta.ts | 20 ++++++++++ src/server/api/endpoints/admin/update-meta.ts | 10 +++++ src/server/api/endpoints/meta.ts | 1 + 7 files changed, 91 insertions(+), 8 deletions(-) diff --git a/src/client/app/common/views/components/autocomplete.vue b/src/client/app/common/views/components/autocomplete.vue index bc0120c9a..d46d53665 100644 --- a/src/client/app/common/views/components/autocomplete.vue +++ b/src/client/app/common/views/components/autocomplete.vue @@ -14,7 +14,8 @@
  1. - {{ emoji.emoji }} + + {{ emoji.emoji }} ({{ emoji.alias }})
  2. @@ -169,22 +170,45 @@ export default Vue.extend({ } } else if (this.type == 'emoji') { const matched = []; + const max = 30; + + const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis; + customEmojis.some(x => { + if (x.name.startsWith(this.q)) matched.push({ + name: x.name, + emoji: `:${x.name}:`, + url: x.url + }); + return matched.length == max; + }); + customEmojis.some(x => { + const alias = (x.aliases || []).find(a => a.startsWith(this.q)); + if (alias) matched.push({ + alias: x.name, + name: alias, + emoji: `:${x.name}:`, + url: x.url + }); + return matched.length == max; + }); + emjdb.some(x => { if (x.name.indexOf(this.q) == 0 && !x.alias && !matched.some(y => y.emoji == x.emoji)) matched.push(x); - return matched.length == 30; + return matched.length == max; }); - if (matched.length < 30) { + if (matched.length < max) { emjdb.some(x => { if (x.name.indexOf(this.q) == 0 && !matched.some(y => y.emoji == x.emoji)) matched.push(x); - return matched.length == 30; + return matched.length == max; }); } - if (matched.length < 30) { + if (matched.length < max) { emjdb.some(x => { if (x.name.indexOf(this.q) > -1 && !matched.some(y => y.emoji == x.emoji)) matched.push(x); - return matched.length == 30; + return matched.length == max; }); } + this.emojis = matched; } }, @@ -340,6 +364,10 @@ export default Vue.extend({ margin 0 4px 0 0 width 24px + > img + width 24px + vertical-align bottom + .name color var(--autocompleteItemText) diff --git a/src/client/app/common/views/components/misskey-flavored-markdown.ts b/src/client/app/common/views/components/misskey-flavored-markdown.ts index d36a0a371..c45bb1022 100644 --- a/src/client/app/common/views/components/misskey-flavored-markdown.ts +++ b/src/client/app/common/views/components/misskey-flavored-markdown.ts @@ -3,7 +3,6 @@ import * as emojilib from 'emojilib'; import { length } from 'stringz'; import parse from '../../../../../mfm/parse'; import getAcct from '../../../../../misc/acct/render'; -import { url } from '../../../config'; import MkUrl from './url.vue'; import MkGoogle from './google.vue'; import { concat } from '../../../../../prelude/array'; @@ -186,6 +185,21 @@ export default Vue.component('misskey-flavored-markdown', { } case 'emoji': { + //#region カスタム絵文字 + const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis; + const customEmoji = customEmojis.find(e => e.name == token.emoji || (e.aliases || []).includes(token.emoji)); + if (customEmoji) { + return [createElement('img', { + attrs: { + src: customEmoji.url, + alt: token.emoji, + title: token.emoji, + style: 'height: 2.5em; vertical-align: middle;' + } + })]; + } + //#endregion + const emoji = emojilib.lib[token.emoji]; return [createElement('span', emoji ? emoji.char : token.content)]; } diff --git a/src/client/app/common/views/directives/autocomplete.ts b/src/client/app/common/views/directives/autocomplete.ts index e2cc64d79..1a5c5daed 100644 --- a/src/client/app/common/views/directives/autocomplete.ts +++ b/src/client/app/common/views/directives/autocomplete.ts @@ -222,13 +222,15 @@ class Autocomplete { const trimmedBefore = before.substring(0, before.lastIndexOf(':')); const after = source.substr(caret); + if (value.startsWith(':')) value = value + ' '; + // 挿入 this.text = trimmedBefore + value + after; // キャレットを戻す this.vm.$nextTick(() => { this.textarea.focus(); - const pos = trimmedBefore.length + 1; + const pos = trimmedBefore.length + (value.startsWith(':') ? value.length : 1); this.textarea.setSelectionRange(pos, pos); }); } diff --git a/src/client/app/mios.ts b/src/client/app/mios.ts index d50c4e15b..9c351e904 100644 --- a/src/client/app/mios.ts +++ b/src/client/app/mios.ts @@ -510,6 +510,14 @@ export default class MiOS extends EventEmitter { return promise; } + /** + * Misskeyのメタ情報を取得します + */ + @autobind + public getMetaSync() { + return this.meta ? this.meta.data : null; + } + /** * Misskeyのメタ情報を取得します * @param force キャッシュを無視するか否か diff --git a/src/models/meta.ts b/src/models/meta.ts index 3c0347485..7ecb4990d 100644 --- a/src/models/meta.ts +++ b/src/models/meta.ts @@ -15,4 +15,24 @@ export type IMeta = { disableLocalTimeline?: boolean; hidedTags?: string[]; bannerUrl?: string; + + /** + * カスタム絵文字定義 + */ + emojis?: { + /** + * 絵文字名 (例: thinking_ai) + */ + name: string; + + /** + * エイリアス + */ + aliases?: string[]; + + /** + * 絵文字画像のURL + */ + url: string; + }[]; }; diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts index f0ebfbe93..f45efd7fe 100644 --- a/src/server/api/endpoints/admin/update-meta.ts +++ b/src/server/api/endpoints/admin/update-meta.ts @@ -17,6 +17,12 @@ export const meta = { } }), + emojis: $.arr($.obj()).optional.note({ + desc: { + 'ja-JP': 'カスタム絵文字定義' + } + }), + disableRegistration: $.bool.optional.nullable.note({ desc: { 'ja-JP': '招待制か否か' @@ -53,6 +59,10 @@ export default (params: any) => new Promise(async (res, rej) => { set.broadcasts = ps.broadcasts; } + if (ps.emojis) { + set.emojis = ps.emojis; + } + if (typeof ps.disableRegistration === 'boolean') { set.disableRegistration = ps.disableRegistration; } diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts index 0cd584231..8bc029383 100644 --- a/src/server/api/endpoints/meta.ts +++ b/src/server/api/endpoints/meta.ts @@ -50,6 +50,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => hidedTags: (me && me.isAdmin) ? meta.hidedTags : undefined, bannerUrl: meta.bannerUrl, maxNoteTextLength: config.maxNoteTextLength, + emojis: meta.emojis, features: { registration: !meta.disableRegistration,