Merge branch 'refactor/emojis' into develop

This commit is contained in:
ThatOneCalculator 2023-06-22 22:09:25 -07:00
commit 05c75579d5
No known key found for this signature in database
GPG Key ID: 8703CACD01000000
12 changed files with 146 additions and 1823 deletions

View File

@ -1109,6 +1109,7 @@ isLocked: "This account has follow approvals"
isModerator: "Moderator" isModerator: "Moderator"
isAdmin: "Administrator" isAdmin: "Administrator"
isPatron: "Calckey Patron" isPatron: "Calckey Patron"
reactionPickerSkinTone: "Preferred emoji skin tone"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Reduces the effort of server moderation through automatically recognizing description: "Reduces the effort of server moderation through automatically recognizing

View File

@ -6,7 +6,7 @@
"type": "git", "type": "git",
"url": "https://codeberg.org/calckey/calckey.git" "url": "https://codeberg.org/calckey/calckey.git"
}, },
"packageManager": "pnpm@8.6.2", "packageManager": "pnpm@8.6.3",
"private": true, "private": true,
"scripts": { "scripts": {
"rebuild": "pnpm run clean && pnpm -r run build && pnpm run gulp", "rebuild": "pnpm run clean && pnpm -r run build && pnpm run gulp",

View File

@ -44,6 +44,7 @@
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "10.11.0", "cypress": "10.11.0",
"date-fns": "2.30.0", "date-fns": "2.30.0",
"emojilib": "^3.0.10",
"escape-regexp": "0.0.1", "escape-regexp": "0.0.1",
"eventemitter3": "4.0.7", "eventemitter3": "4.0.7",
"focus-trap": "^7.4.3", "focus-trap": "^7.4.3",
@ -79,6 +80,7 @@
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"twemoji-parser": "14.0.0", "twemoji-parser": "14.0.0",
"typescript": "5.1.3", "typescript": "5.1.3",
"unicode-emoji-json": "^0.4.0",
"uuid": "9.0.0", "uuid": "9.0.0",
"vanilla-tilt": "1.8.0", "vanilla-tilt": "1.8.0",
"vite": "4.3.9", "vite": "4.3.9",

View File

@ -114,19 +114,19 @@ type EmojiDef = {
const lib = emojilist.filter((x) => x.category !== "flags"); const lib = emojilist.filter((x) => x.category !== "flags");
const emjdb: EmojiDef[] = lib.map((x) => ({ const emjdb: EmojiDef[] = lib.map((x) => ({
emoji: x.char, emoji: x.emoji,
name: x.name, name: x.slug,
url: char2filePath(x.char), url: char2filePath(x.emoji),
})); }));
for (const x of lib) { for (const x of lib) {
if (x.keywords) { if (x.keywords) {
for (const k of x.keywords) { for (const k of x.keywords) {
emjdb.push({ emjdb.push({
emoji: x.char, emoji: x.emoji,
name: k, name: k,
aliasOf: x.name, aliasOf: x.slug,
url: char2filePath(x.char), url: char2filePath(x.emoji),
}); });
} }
} }

View File

@ -42,13 +42,13 @@
<div v-if="searchResultUnicode.length > 0" class="body"> <div v-if="searchResultUnicode.length > 0" class="body">
<button <button
v-for="emoji in searchResultUnicode" v-for="emoji in searchResultUnicode"
:key="emoji.name" :key="emoji.slug"
class="_button item" class="_button item"
:title="emoji.name" :title="emoji.slug"
tabindex="0" tabindex="0"
@click="chosen(emoji, $event)" @click="chosen(emoji, $event)"
> >
<MkEmoji class="emoji" :emoji="emoji.char" /> <MkEmoji class="emoji" :emoji="emoji.emoji" />
</button> </button>
</div> </div>
</section> </section>
@ -111,15 +111,17 @@
<div v-once class="group"> <div v-once class="group">
<header>{{ i18n.ts.emoji }}</header> <header>{{ i18n.ts.emoji }}</header>
<XSection <XSection
v-for="category in categories" v-for="category in unicodeEmojiCategories"
:key="category" :key="category"
:emojis=" :emojis="
emojilist emojilist
.filter((e) => e.category === category) .filter((e) => e.category === category)
.map((e) => e.char) .map((e) => e.emoji)
" "
@chosen="chosen" @chosen="chosen"
>{{ category }}</XSection >{{
getNicelyLabeledCategory(category) || category
}}</XSection
> >
</div> </div>
</div> </div>
@ -163,8 +165,9 @@ import * as Misskey from "calckey-js";
import XSection from "@/components/MkEmojiPicker.section.vue"; import XSection from "@/components/MkEmojiPicker.section.vue";
import { import {
emojilist, emojilist,
unicodeEmojiCategories,
UnicodeEmojiDef, UnicodeEmojiDef,
unicodeEmojiCategories as categories, getNicelyLabeledCategory,
} from "@/scripts/emojilist"; } from "@/scripts/emojilist";
import { getStaticImageUrl } from "@/scripts/get-static-image-url"; import { getStaticImageUrl } from "@/scripts/get-static-image-url";
import Ripple from "@/components/MkRipple.vue"; import Ripple from "@/components/MkRipple.vue";
@ -232,7 +235,7 @@ watch(q, () => {
const newQ = q.value.replace(/:/g, "").toLowerCase(); const newQ = q.value.replace(/:/g, "").toLowerCase();
const searchCustom = () => { const searchCustom = () => {
const max = 8; const max = 16;
const emojis = customEmojis; const emojis = customEmojis;
const matches = new Set<Misskey.entities.CustomEmoji>(); const matches = new Set<Misskey.entities.CustomEmoji>();
@ -304,11 +307,11 @@ watch(q, () => {
}; };
const searchUnicode = () => { const searchUnicode = () => {
const max = 8; const max = 32;
const emojis = emojilist; const emojis = emojilist;
const matches = new Set<UnicodeEmojiDef>(); const matches = new Set<UnicodeEmojiDef>();
const exactMatch = emojis.find((emoji) => emoji.name === newQ); const exactMatch = emojis.find((emoji) => emoji.slug === newQ);
if (exactMatch) matches.add(exactMatch); if (exactMatch) matches.add(exactMatch);
if (newQ.includes(" ")) { if (newQ.includes(" ")) {
@ -317,7 +320,7 @@ watch(q, () => {
// //
for (const emoji of emojis) { for (const emoji of emojis) {
if (keywords.every((keyword) => emoji.name.includes(keyword))) { if (keywords.every((keyword) => emoji.slug.includes(keyword))) {
matches.add(emoji); matches.add(emoji);
if (matches.size >= max) break; if (matches.size >= max) break;
} }
@ -329,8 +332,8 @@ watch(q, () => {
if ( if (
keywords.every( keywords.every(
(keyword) => (keyword) =>
emoji.name.includes(keyword) || emoji.slug.includes(keyword) ||
emoji.keywords.some((alias) => emoji.keywords?.some((alias) =>
alias.includes(keyword) alias.includes(keyword)
) )
) )
@ -341,7 +344,7 @@ watch(q, () => {
} }
} else { } else {
for (const emoji of emojis) { for (const emoji of emojis) {
if (emoji.name.startsWith(newQ)) { if (emoji.slug.startsWith(newQ)) {
matches.add(emoji); matches.add(emoji);
if (matches.size >= max) break; if (matches.size >= max) break;
} }
@ -350,7 +353,7 @@ watch(q, () => {
for (const emoji of emojis) { for (const emoji of emojis) {
if ( if (
emoji.keywords.some((keyword) => keyword.startsWith(newQ)) emoji.keywords?.some((keyword) => keyword.startsWith(newQ))
) { ) {
matches.add(emoji); matches.add(emoji);
if (matches.size >= max) break; if (matches.size >= max) break;
@ -359,7 +362,7 @@ watch(q, () => {
if (matches.size >= max) return matches; if (matches.size >= max) return matches;
for (const emoji of emojis) { for (const emoji of emojis) {
if (emoji.name.includes(newQ)) { if (emoji.slug.includes(newQ)) {
matches.add(emoji); matches.add(emoji);
if (matches.size >= max) break; if (matches.size >= max) break;
} }
@ -367,7 +370,7 @@ watch(q, () => {
if (matches.size >= max) return matches; if (matches.size >= max) return matches;
for (const emoji of emojis) { for (const emoji of emojis) {
if (emoji.keywords.some((keyword) => keyword.includes(newQ))) { if (emoji.keywords?.some((keyword) => keyword.includes(newQ))) {
matches.add(emoji); matches.add(emoji);
if (matches.size >= max) break; if (matches.size >= max) break;
} }
@ -397,7 +400,7 @@ function reset() {
function getKey( function getKey(
emoji: string | Misskey.entities.CustomEmoji | UnicodeEmojiDef emoji: string | Misskey.entities.CustomEmoji | UnicodeEmojiDef
): string { ): string {
return typeof emoji === "string" ? emoji : emoji.char || `:${emoji.name}:`; return typeof emoji === "string" ? emoji : emoji.emoji || `:${emoji.name}:`;
} }
function chosen(emoji: any, ev?: MouseEvent) { function chosen(emoji: any, ev?: MouseEvent) {
@ -441,7 +444,7 @@ function done(query?: any): boolean | void {
return true; return true;
} }
const exactMatchUnicode = emojilist.find( const exactMatchUnicode = emojilist.find(
(emoji) => emoji.char === q2 || emoji.name === q2 (emoji) => emoji.emoji === q2 || emoji.slug === q2
); );
if (exactMatchUnicode) { if (exactMatchUnicode) {
chosen(exactMatchUnicode); chosen(exactMatchUnicode);

File diff suppressed because it is too large Load Diff

View File

@ -131,21 +131,18 @@
}}</template> }}</template>
<option value="⭐"> <option value="⭐">
<MkEmoji <MkEmoji
class="emoji"
emoji="⭐" emoji="⭐"
style="height: 1.7em" style="height: 1.7em"
/> />
</option> </option>
<option value="👍"> <option value="👍">
<MkEmoji <MkEmoji
class="emoji"
emoji="👍" emoji="👍"
style="height: 1.7em" style="height: 1.7em"
/> />
</option> </option>
<option value="❤️"> <option value="❤️">
<MkEmoji <MkEmoji
class="emoji"
emoji="❤️" emoji="❤️"
style="height: 1.7em" style="height: 1.7em"
/> />

View File

@ -23,7 +23,11 @@
class="_button item" class="_button item"
@click="remove(element, $event)" @click="remove(element, $event)"
> >
<MkEmoji :emoji="element" :normal="true" /> <MkEmoji
:emoji="element"
style="height: 1.7em"
class="emoji"
/>
</button> </button>
</template> </template>
<template #footer> <template #footer>
@ -41,6 +45,27 @@
> >
</FromSlot> </FromSlot>
<FormRadios v-model="reactionPickerSkinTone" class="_formBlock">
<template #label>{{ i18n.ts.reactionPickerSkinTone }}</template>
<option :value="1">
<MkEmoji style="height: 1.7em" emoji="✌️" />
</option>
<option :value="6">
<MkEmoji style="height: 1.7em" emoji="✌🏿" />
</option>
<option :value="5">
<MkEmoji style="height: 1.7em" emoji="✌🏾" />
</option>
<option :value="4">
<MkEmoji style="height: 1.7em" emoji="✌🏽" />
</option>
<option :value="3">
<MkEmoji style="height: 1.7em" emoji="✌🏼" />
</option>
<option :value="2">
<MkEmoji style="height: 1.7em" emoji="✌🏻" />
</option>
</FormRadios>
<FormRadios v-model="reactionPickerSize" class="_formBlock"> <FormRadios v-model="reactionPickerSize" class="_formBlock">
<template #label>{{ i18n.ts.size }}</template> <template #label>{{ i18n.ts.size }}</template>
<option :value="1">{{ i18n.ts.small }}</option> <option :value="1">{{ i18n.ts.small }}</option>
@ -125,6 +150,9 @@ async function reloadAsk() {
let reactions = $ref(deepClone(defaultStore.state.reactions)); let reactions = $ref(deepClone(defaultStore.state.reactions));
const reactionPickerSkinTone = $computed(
defaultStore.makeGetterSetter("reactionPickerSkinTone")
);
const reactionPickerSize = $computed( const reactionPickerSize = $computed(
defaultStore.makeGetterSetter("reactionPickerSize") defaultStore.makeGetterSetter("reactionPickerSize")
); );

View File

@ -1,5 +1,10 @@
import data from "unicode-emoji-json/data-by-group.json";
import emojiComponents from "unicode-emoji-json/data-emoji-components.json";
import keywordSet from "emojilib";
import { defaultStore } from "@/store";
export const unicodeEmojiCategories = [ export const unicodeEmojiCategories = [
"face", "emotion",
"people", "people",
"animals_and_nature", "animals_and_nature",
"food_and_drink", "food_and_drink",
@ -10,14 +15,69 @@ export const unicodeEmojiCategories = [
"flags", "flags",
] as const; ] as const;
export const categoryMapping = {
"Smileys & Emotion": "emotion",
"People & Body": "people",
"Animals & Nature": "animals_and_nature",
"Food & Drink": "food_and_drink",
"Activities": "activity",
"Travel & Places": "travel_and_places",
"Objects": "objects",
"Symbols": "symbols",
"Flags": "flags",
} as const;
function addSkinTone(emoji: string) {
const skinTone = defaultStore.state.reactionPickerSkinTone;
if (skinTone === 1) return emoji;
if (skinTone === 2) return emoji + emojiComponents.light_skin_tone;
if (skinTone === 3) return emoji + emojiComponents.medium_light_skin_tone;
if (skinTone === 4) return emoji + emojiComponents.medium_skin_tone;
if (skinTone === 5) return emoji + emojiComponents.medium_dark_skin_tone;
if (skinTone === 6) return emoji + emojiComponents.dark_skin_tone;
return emoji;
}
const newData = {};
Object.keys(data).forEach((originalCategory) => {
const newCategory = categoryMapping[originalCategory];
if (newCategory) {
newData[newCategory] = newData[newCategory] || [];
Object.keys(data[originalCategory]).forEach((emojiIndex) => {
const emojiObj = { ...data[originalCategory][emojiIndex] };
if (emojiObj.skin_tone_support) {
emojiObj.emoji = addSkinTone(emojiObj.emoji);
}
emojiObj.category = newCategory;
emojiObj.keywords = keywordSet[emojiObj.emoji];
newData[newCategory].push(emojiObj);
});
}
});
export type UnicodeEmojiDef = { export type UnicodeEmojiDef = {
name: string; emoji: string;
keywords: string[];
char: string;
category: typeof unicodeEmojiCategories[number]; category: typeof unicodeEmojiCategories[number];
slug: string;
keywords?: string[];
}; };
// initial converted from https://github.com/muan/emojilib/commit/242fe68be86ed6536843b83f7e32f376468b38fb export const emojilist: UnicodeEmojiDef[] = Object.keys(newData).reduce((acc, category) => {
import _emojilist from "../emojilist.json"; const categoryItems = newData[category].map((item) => {
return {
emoji: item.emoji,
slug: item.slug,
category: item.category,
keywords: item.keywords || [],
};
});
return acc.concat(categoryItems);
}, []);
export const emojilist = _emojilist as UnicodeEmojiDef[];
export function getNicelyLabeledCategory(internalName) {
return Object.keys(categoryMapping).find(
(key) => categoryMapping[key] === internalName
) || internalName;
}

View File

@ -242,6 +242,10 @@ export const defaultStore = markRaw(
where: "device", where: "device",
default: "remote" as "none" | "remote" | "always", default: "remote" as "none" | "remote" | "always",
}, },
reactionPickerSkinTone: {
where: "account",
default: 1,
},
reactionPickerSize: { reactionPickerSize: {
where: "device", where: "device",
default: 3, default: 3,

View File

@ -777,6 +777,9 @@ importers:
date-fns: date-fns:
specifier: 2.30.0 specifier: 2.30.0
version: 2.30.0 version: 2.30.0
emojilib:
specifier: ^3.0.10
version: 3.0.10
escape-regexp: escape-regexp:
specifier: 0.0.1 specifier: 0.0.1
version: 0.0.1 version: 0.0.1
@ -882,6 +885,9 @@ importers:
typescript: typescript:
specifier: 5.1.3 specifier: 5.1.3
version: 5.1.3 version: 5.1.3
unicode-emoji-json:
specifier: ^0.4.0
version: 0.4.0
uuid: uuid:
specifier: 9.0.0 specifier: 9.0.0
version: 9.0.0 version: 9.0.0
@ -6777,6 +6783,10 @@ packages:
/emoji-regex@9.2.2: /emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
/emojilib@3.0.10:
resolution: {integrity: sha512-VQtCRroFykPTJaoEBEGFg5tI+rEluabjuaVDDbSftDtiRJ5GuqRG/LGV1mmDzkJP4bh5rzuEBOafMN68/YXQcQ==}
dev: true
/emojis-list@3.0.0: /emojis-list@3.0.0:
resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
engines: {node: '>= 4'} engines: {node: '>= 4'}
@ -14866,6 +14876,10 @@ packages:
busboy: 1.6.0 busboy: 1.6.0
dev: false dev: false
/unicode-emoji-json@0.4.0:
resolution: {integrity: sha512-lVNOwh2AnmbwqtSrEVjAWKQoVzWgyWmXVqPuPkPfKb0tnA0+uYN/4ILCTdy9IRj/+3drAVhmjwjNJQr2dhCwnA==}
dev: true
/union-value@1.0.1: /union-value@1.0.1:
resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}

View File

@ -14,7 +14,6 @@
"packages/backend/src/server/web/manifest.ts", "packages/backend/src/server/web/manifest.ts",
"packages/backend/built/", "packages/backend/built/",
"*/model.json", "*/model.json",
"packages/client/src/emojilist.json",
"*.md", "*.md",
"**/tsconfig.json", "**/tsconfig.json",
"*/.yml" "*/.yml"