feat: list custom emojis in autocomplete

This commit is contained in:
Jan 2022-10-26 08:22:39 +02:00
parent 9f3e47d327
commit 35fb6b4381

View file

@ -1,4 +1,7 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { Link } from "react-router-dom";
import { Channel, User } from "revolt.js"; import { Channel, User } from "revolt.js";
import { Emoji as CustomEmoji } from "revolt.js/esm/maps/Emojis";
import styled, { css } from "styled-components/macro"; import styled, { css } from "styled-components/macro";
import { StateUpdater, useState } from "preact/hooks"; import { StateUpdater, useState } from "preact/hooks";
@ -7,6 +10,8 @@ import { emojiDictionary } from "../../assets/emojis";
import { useClient } from "../../controllers/client/ClientController"; import { useClient } from "../../controllers/client/ClientController";
import ChannelIcon from "./ChannelIcon"; import ChannelIcon from "./ChannelIcon";
import Emoji from "./Emoji"; import Emoji from "./Emoji";
import ServerIcon from "./ServerIcon";
import Tooltip from "./Tooltip";
import UserIcon from "./user/UserIcon"; import UserIcon from "./user/UserIcon";
export type AutoCompleteState = export type AutoCompleteState =
@ -14,7 +19,7 @@ export type AutoCompleteState =
| ({ selected: number; within: boolean } & ( | ({ selected: number; within: boolean } & (
| { | {
type: "emoji"; type: "emoji";
matches: string[]; matches: (string | CustomEmoji)[];
} }
| { | {
type: "user"; type: "user";
@ -104,16 +109,23 @@ export function useAutoComplete(
if (type === "emoji") { if (type === "emoji") {
// ! TODO: we should convert it to a Binary Search Tree and use that // ! TODO: we should convert it to a Binary Search Tree and use that
const matches = Object.keys(emojiDictionary) const matches = [
.filter((emoji: string) => emoji.match(regex)) ...Object.keys(emojiDictionary).filter((emoji: string) =>
.splice(0, 5); emoji.match(regex),
),
...Array.from(client.emojis.values()).filter((emoji) =>
emoji.name.match(regex),
),
].splice(0, 5);
if (matches.length > 0) { if (matches.length > 0) {
const currentPosition = const currentPosition =
state.type !== "none" ? state.selected : 0; state.type !== "none" ? state.selected : 0;
setState({ setState({
// @ts-ignore-next-line are you high
type: "emoji", type: "emoji",
// @ts-ignore-next-line
matches, matches,
selected: Math.min(currentPosition, matches.length - 1), selected: Math.min(currentPosition, matches.length - 1),
within: false, within: false,
@ -233,10 +245,13 @@ export function useAutoComplete(
const content = el.value.split(""); const content = el.value.split("");
if (state.type === "emoji") { if (state.type === "emoji") {
const selected = state.matches[state.selected];
content.splice( content.splice(
index, index,
search.length, search.length,
state.matches[state.selected], selected instanceof CustomEmoji
? selected._id
: selected,
": ", ": ",
); );
} else if (state.type === "user") { } else if (state.type === "user") {
@ -388,12 +403,17 @@ export default function AutoComplete({
setState, setState,
onClick, onClick,
}: Pick<AutoCompleteProps, "detached" | "state" | "setState" | "onClick">) { }: Pick<AutoCompleteProps, "detached" | "state" | "setState" | "onClick">) {
const client = useClient();
return ( return (
<Base detached={detached}> <Base detached={detached}>
<div> <div>
{state.type === "emoji" && {state.type === "emoji" &&
state.matches.map((match, i) => ( state.matches.map((match, i) => (
<button <button
style={{
display: "flex",
justifyContent: "space-between",
}}
key={match} key={match}
className={i === state.selected ? "active" : ""} className={i === state.selected ? "active" : ""}
onMouseEnter={() => onMouseEnter={() =>
@ -412,15 +432,56 @@ export default function AutoComplete({
}) })
} }
onClick={onClick}> onClick={onClick}>
<Emoji <div>
emoji={ {match instanceof CustomEmoji ? (
(emojiDictionary as Record<string, string>)[ <img
match loading="lazy"
] src={match.imageURL}
} style={{
size={20} width: `20px`,
/> height: `20px`,
:{match}: }}
/>
) : (
<Emoji
emoji={
(
emojiDictionary as Record<
string,
string
>
)[match]
}
size={20}
/>
)}
{`:${
match instanceof CustomEmoji
? match.name
: match
}:`}
</div>
{match instanceof CustomEmoji &&
match.parent.type == "Server" && (
<>
<Tooltip
content={
client.servers.get(
match.parent.id,
)?.name
}>
<Link
to={`/server/${match.parent.id}`}>
<ServerIcon
target={client.servers.get(
match.parent.id,
)}
size={20}
/>
</Link>
</Tooltip>
</>
)}
</button> </button>
))} ))}
{state.type === "user" && {state.type === "user" &&