import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation';
import AsyncSearch from '../../../util/AsyncSearch';
+import { addRecentEmoji, getRecentEmojis } from './recent';
import Text from '../../atoms/text/Text';
import RawIcon from '../../atoms/system-icons/RawIcon';
import ScrollView from '../../atoms/scroll/ScrollView';
import SearchIC from '../../../../public/res/ic/outlined/search.svg';
+import RecentClockIC from '../../../../public/res/ic/outlined/recent-clock.svg';
import EmojiIC from '../../../../public/res/ic/outlined/emoji.svg';
import DogIC from '../../../../public/res/ic/outlined/dog.svg';
import CupIC from '../../../../public/res/ic/outlined/cup.svg';
import PeaceIC from '../../../../public/res/ic/outlined/peace.svg';
import FlagIC from '../../../../public/res/ic/outlined/flag.svg';
+const ROW_EMOJIS_COUNT = 7;
+
const EmojiGroup = React.memo(({ name, groupEmojis }) => {
function getEmojiBoard() {
const emojiBoard = [];
- const ROW_EMOJIS_COUNT = 7;
const totalEmojis = groupEmojis.length;
for (let r = 0; r < totalEmojis; r += ROW_EMOJIS_COUNT) {
function selectEmoji(e) {
if (isTargetNotEmoji(e.target)) return;
- const emoji = e.target;
- onSelect(getEmojiDataFromTarget(emoji));
+ const emoji = getEmojiDataFromTarget(e.target);
+ onSelect(emoji);
+ if (emoji.hexcode) addRecentEmoji(emoji.unicode);
}
function setEmojiInfo(emoji) {
}
const [availableEmojis, setAvailableEmojis] = useState([]);
+ const [recentEmojis, setRecentEmojis] = useState([]);
+
+ const recentOffset = recentEmojis.length > 0 ? 1 : 0;
useEffect(() => {
const updateAvailableEmoji = (selectedRoomId) => {
const onOpen = () => {
searchRef.current.value = '';
handleSearchChange();
+
+ // only update when board is getting opened to prevent shifting UI
+ setRecentEmojis(getRecentEmojis(3 * ROW_EMOJIS_COUNT));
};
navigation.on(cons.events.navigation.ROOM_SELECTED, updateAvailableEmoji);
const $emojiContent = scrollEmojisRef.current.firstElementChild;
const groupCount = $emojiContent.childElementCount;
if (groupCount > emojiGroups.length) {
- tabIndex += groupCount - emojiGroups.length - availableEmojis.length;
+ tabIndex += groupCount - emojiGroups.length - availableEmojis.length - recentOffset;
}
$emojiContent.children[tabIndex].scrollIntoView();
}
<ScrollView ref={scrollEmojisRef} autoHide>
<div onMouseMove={hoverEmoji} onClick={selectEmoji}>
<SearchedEmoji />
+ {recentEmojis.length > 0 && <EmojiGroup name="Recently used" groupEmojis={recentEmojis} />}
{
availableEmojis.map((pack) => (
<EmojiGroup
</div>
<ScrollView invisible>
<div className="emoji-board__nav">
+ {recentEmojis.length > 0 && (
+ <IconButton
+ onClick={() => openGroup(0)}
+ src={RecentClockIC}
+ tooltip="Recent"
+ tooltipPlacement="right"
+ />
+ )}
<div className="emoji-board__nav-custom">
{
availableEmojis.map((pack) => {
const src = initMatrix.matrixClient.mxcUrlToHttp(pack.avatar ?? pack.images[0].mxc);
return (
<IconButton
- onClick={() => openGroup(pack.packIndex)}
+ onClick={() => openGroup(recentOffset + pack.packIndex)}
src={src}
key={pack.packIndex}
tooltip={pack.displayName}
[7, FlagIC, 'Flags'],
].map(([indx, ico, name]) => (
<IconButton
- onClick={() => openGroup(availableEmojis.length + indx)}
+ onClick={() => openGroup(recentOffset + availableEmojis.length + indx)}
key={indx}
src={ico}
tooltip={name}
--- /dev/null
+import initMatrix from '../../../client/initMatrix';
+import { emojis } from './emoji';
+
+const eventType = 'io.element.recent_emoji';
+
+function getRecentEmojisRaw() {
+ return initMatrix.matrixClient.getAccountData(eventType).getContent().recent_emoji ?? [];
+}
+
+export function getRecentEmojis(limit) {
+ const res = [];
+ getRecentEmojisRaw()
+ .sort((a, b) => b[1] - a[1])
+ .find(([unicode]) => {
+ const emoji = emojis.find((e) => e.unicode === unicode);
+ if (emoji) return res.push(emoji) >= limit;
+ return false;
+ });
+ return res;
+}
+
+export function addRecentEmoji(unicode) {
+ const recent = getRecentEmojisRaw();
+ const i = recent.findIndex(([u]) => u === unicode);
+ let entry;
+ if (i < 0) {
+ entry = [unicode, 1];
+ } else {
+ [entry] = recent.splice(i, 1);
+ entry[1] += 1;
+ }
+ recent.unshift(entry);
+ initMatrix.matrixClient.setAccountData(eventType, {
+ recent_emoji: recent.slice(0, 100),
+ });
+}