(chore) remove outdated code (#1765)

* optimize room typing members hook

* remove unused code - WIP

* remove old code from initMatrix

* remove twemojify function

* remove old sanitize util

* delete old markdown util

* delete Math atom component

* uninstall unused dependencies

* remove old notification system

* decrypt message in inbox notification center and fix refresh in background

* improve notification

---------

Co-authored-by: Krishan <33421343+kfiven@users.noreply.github.com>
This commit is contained in:
Ajay Bura
2024-07-08 16:57:10 +05:30
committed by GitHub
parent 60e022035f
commit 4f09e6bbb5
147 changed files with 1164 additions and 15330 deletions

View File

@@ -1,356 +0,0 @@
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import './EmojiBoard.scss';
import parse from 'html-react-parser';
import twemoji from 'twemoji';
import { emojiGroups, emojis } from './emoji';
import { getRelevantPacks } from './custom-emoji';
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation';
import AsyncSearch from '../../../util/AsyncSearch';
import { addRecentEmoji, getRecentEmojis } from './recent';
import { TWEMOJI_BASE_URL } from '../../../util/twemojify';
import Text from '../../atoms/text/Text';
import RawIcon from '../../atoms/system-icons/RawIcon';
import IconButton from '../../atoms/button/IconButton';
import Input from '../../atoms/input/Input';
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 BallIC from '../../../../public/res/ic/outlined/ball.svg';
import PhotoIC from '../../../../public/res/ic/outlined/photo.svg';
import BulbIC from '../../../../public/res/ic/outlined/bulb.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 totalEmojis = groupEmojis.length;
for (let r = 0; r < totalEmojis; r += ROW_EMOJIS_COUNT) {
const emojiRow = [];
for (let c = r; c < r + ROW_EMOJIS_COUNT; c += 1) {
const emojiIndex = c;
if (emojiIndex >= totalEmojis) break;
const emoji = groupEmojis[emojiIndex];
emojiRow.push(
<span key={emojiIndex}>
{emoji.hexcode ? (
// This is a unicode emoji, and should be rendered with twemoji
parse(
twemoji.parse(emoji.unicode, {
attributes: () => ({
unicode: emoji.unicode,
shortcodes: emoji.shortcodes?.toString(),
hexcode: emoji.hexcode,
loading: 'lazy',
}),
base: TWEMOJI_BASE_URL,
})
)
) : (
// This is a custom emoji, and should be render as an mxc
<img
className="emoji"
draggable="false"
loading="lazy"
alt={emoji.shortcode}
unicode={`:${emoji.shortcode}:`}
shortcodes={emoji.shortcode}
src={initMatrix.matrixClient.mxcUrlToHttp(emoji.mxc)}
data-mx-emoticon={emoji.mxc}
/>
)}
</span>
);
}
emojiBoard.push(
<div key={r} className="emoji-row">
{emojiRow}
</div>
);
}
return emojiBoard;
}
return (
<div className="emoji-group">
<Text className="emoji-group__header" variant="b2" weight="bold">
{name}
</Text>
{groupEmojis.length !== 0 && <div className="emoji-set noselect">{getEmojiBoard()}</div>}
</div>
);
});
EmojiGroup.propTypes = {
name: PropTypes.string.isRequired,
groupEmojis: PropTypes.arrayOf(
PropTypes.shape({
length: PropTypes.number,
unicode: PropTypes.string,
hexcode: PropTypes.string,
mxc: PropTypes.string,
shortcode: PropTypes.string,
shortcodes: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
})
).isRequired,
};
const asyncSearch = new AsyncSearch();
asyncSearch.setup(emojis, { keys: ['shortcode'], isContain: true, limit: 40 });
function SearchedEmoji() {
const [searchedEmojis, setSearchedEmojis] = useState(null);
function handleSearchEmoji(resultEmojis, term) {
if (term === '' || resultEmojis.length === 0) {
if (term === '') setSearchedEmojis(null);
else setSearchedEmojis({ emojis: [] });
return;
}
setSearchedEmojis({ emojis: resultEmojis });
}
useEffect(() => {
asyncSearch.on(asyncSearch.RESULT_SENT, handleSearchEmoji);
return () => {
asyncSearch.removeListener(asyncSearch.RESULT_SENT, handleSearchEmoji);
};
}, []);
if (searchedEmojis === null) return false;
return (
<EmojiGroup
key="-1"
name={searchedEmojis.emojis.length === 0 ? 'No search result found' : 'Search results'}
groupEmojis={searchedEmojis.emojis}
/>
);
}
function EmojiBoard({ onSelect, searchRef }) {
const scrollEmojisRef = useRef(null);
const emojiInfo = useRef(null);
function isTargetNotEmoji(target) {
return target.classList.contains('emoji') === false;
}
function getEmojiDataFromTarget(target) {
const unicode = target.getAttribute('unicode');
const hexcode = target.getAttribute('hexcode');
const mxc = target.getAttribute('data-mx-emoticon');
let shortcodes = target.getAttribute('shortcodes');
if (typeof shortcodes === 'undefined') shortcodes = undefined;
else shortcodes = shortcodes.split(',');
return {
unicode,
hexcode,
shortcodes,
mxc,
};
}
function selectEmoji(e) {
if (isTargetNotEmoji(e.target)) return;
const emoji = getEmojiDataFromTarget(e.target);
onSelect(emoji);
if (emoji.hexcode) addRecentEmoji(emoji.unicode);
}
function setEmojiInfo(emoji) {
const infoEmoji = emojiInfo.current.firstElementChild.firstElementChild;
const infoShortcode = emojiInfo.current.lastElementChild;
infoEmoji.src = emoji.src;
infoEmoji.alt = emoji.unicode;
infoShortcode.textContent = `:${emoji.shortcode}:`;
}
function hoverEmoji(e) {
if (isTargetNotEmoji(e.target)) return;
const emoji = e.target;
const { shortcodes, unicode } = getEmojiDataFromTarget(emoji);
const { src } = e.target;
if (typeof shortcodes === 'undefined') {
searchRef.current.placeholder = 'Search';
setEmojiInfo({
unicode: '🙂',
shortcode: 'slight_smile',
src: 'https://twemoji.maxcdn.com/v/13.1.0/72x72/1f642.png',
});
return;
}
if (searchRef.current.placeholder === shortcodes[0]) return;
searchRef.current.setAttribute('placeholder', shortcodes[0]);
setEmojiInfo({ shortcode: shortcodes[0], src, unicode });
}
function handleSearchChange() {
const term = searchRef.current.value;
asyncSearch.search(term);
scrollEmojisRef.current.scrollTop = 0;
}
const [availableEmojis, setAvailableEmojis] = useState([]);
const [recentEmojis, setRecentEmojis] = useState([]);
const recentOffset = recentEmojis.length > 0 ? 1 : 0;
useEffect(() => {
const updateAvailableEmoji = (selectedRoomId) => {
if (!selectedRoomId) {
setAvailableEmojis([]);
return;
}
const mx = initMatrix.matrixClient;
const room = mx.getRoom(selectedRoomId);
const parentIds = initMatrix.roomList.getAllParentSpaces(room.roomId);
const parentRooms = [...parentIds].map((id) => mx.getRoom(id));
if (room) {
const packs = getRelevantPacks(room.client, [room, ...parentRooms]).filter(
(pack) => pack.getEmojis().length !== 0
);
// Set an index for each pack so that we know where to jump when the user uses the nav
for (let i = 0; i < packs.length; i += 1) {
packs[i].packIndex = i;
}
setAvailableEmojis(packs);
}
};
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);
navigation.on(cons.events.navigation.EMOJIBOARD_OPENED, onOpen);
return () => {
navigation.removeListener(cons.events.navigation.ROOM_SELECTED, updateAvailableEmoji);
navigation.removeListener(cons.events.navigation.EMOJIBOARD_OPENED, onOpen);
};
}, []);
function openGroup(groupOrder) {
let tabIndex = groupOrder;
const $emojiContent = scrollEmojisRef.current.firstElementChild;
const groupCount = $emojiContent.childElementCount;
if (groupCount > emojiGroups.length) {
tabIndex += groupCount - emojiGroups.length - availableEmojis.length - recentOffset;
}
$emojiContent.children[tabIndex].scrollIntoView();
}
return (
<div id="emoji-board" className="emoji-board">
<ScrollView invisible>
<div className="emoji-board__nav">
{recentEmojis.length > 0 && (
<IconButton
onClick={() => openGroup(0)}
src={RecentClockIC}
tooltip="Recent"
tooltipPlacement="left"
/>
)}
<div className="emoji-board__nav-custom">
{availableEmojis.map((pack) => {
const src = initMatrix.matrixClient.mxcUrlToHttp(
pack.avatarUrl ?? pack.getEmojis()[0].mxc
);
return (
<IconButton
onClick={() => openGroup(recentOffset + pack.packIndex)}
src={src}
key={pack.packIndex}
tooltip={pack.displayName ?? 'Unknown'}
tooltipPlacement="left"
isImage
/>
);
})}
</div>
<div className="emoji-board__nav-twemoji">
{[
[0, EmojiIC, 'Smilies'],
[1, DogIC, 'Animals'],
[2, CupIC, 'Food'],
[3, BallIC, 'Activities'],
[4, PhotoIC, 'Travel'],
[5, BulbIC, 'Objects'],
[6, PeaceIC, 'Symbols'],
[7, FlagIC, 'Flags'],
].map(([indx, ico, name]) => (
<IconButton
onClick={() => openGroup(recentOffset + availableEmojis.length + indx)}
key={indx}
src={ico}
tooltip={name}
tooltipPlacement="left"
/>
))}
</div>
</div>
</ScrollView>
<div className="emoji-board__content">
<div className="emoji-board__content__search">
<RawIcon size="small" src={SearchIC} />
<Input onChange={handleSearchChange} forwardRef={searchRef} placeholder="Search" />
</div>
<div className="emoji-board__content__emojis">
<ScrollView ref={scrollEmojisRef} autoHide>
<div onMouseMove={hoverEmoji} onClick={selectEmoji}>
<SearchedEmoji />
{recentEmojis.length > 0 && (
<EmojiGroup name="Recently used" groupEmojis={recentEmojis} />
)}
{availableEmojis.map((pack) => (
<EmojiGroup
name={pack.displayName ?? 'Unknown'}
key={pack.packIndex}
groupEmojis={pack.getEmojis()}
className="custom-emoji-group"
/>
))}
{emojiGroups.map((group) => (
<EmojiGroup key={group.name} name={group.name} groupEmojis={group.emojis} />
))}
</div>
</ScrollView>
</div>
<div ref={emojiInfo} className="emoji-board__content__info">
<div>{parse(twemoji.parse('🙂', { base: TWEMOJI_BASE_URL }))}</div>
<Text>:slight_smile:</Text>
</div>
</div>
</div>
);
}
EmojiBoard.propTypes = {
onSelect: PropTypes.func.isRequired,
searchRef: PropTypes.shape({}).isRequired,
};
export default EmojiBoard;

View File

@@ -1,137 +0,0 @@
@use '../../partials/flex';
@use '../../partials/text';
@use '../../partials/dir';
.emoji-board {
--emoji-board-height: 390px;
--emoji-board-width: 286px;
display: flex;
max-width: 90vw;
max-height: 90vh;
&__content {
@extend .cp-fx__item-one;
@extend .cp-fx__column;
height: var(--emoji-board-height);
width: var(--emoji-board-width);
}
& > .scrollbar {
width: initial;
height: var(--emoji-board-height);
}
&__nav {
@extend .cp-fx__column;
justify-content: center;
min-height: 100%;
padding: 4px 6px;
@include dir.side(border, none, 1px solid var(--bg-surface-border));
position: relative;
& .ic-btn-surface {
opacity: 0.8;
}
}
&__nav-custom,
&__nav-twemoji {
@extend .cp-fx__column;
}
&__nav-twemoji {
background-color: var(--bg-surface);
position: sticky;
bottom: -70%;
z-index: 999;
}
}
.emoji-board__content__search {
padding: var(--sp-extra-tight);
position: relative;
& .ic-raw {
position: absolute;
@include dir.prop(left, var(--sp-normal), unset);
@include dir.prop(right, unset, var(--sp-normal));
top: var(--sp-normal);
transform: translateY(1px);
}
& .input-container {
& .input {
min-width: 100%;
width: 0;
padding: var(--sp-extra-tight) 36px;
border-radius: calc(var(--bo-radius) / 2);
}
}
}
.emoji-board__content__emojis {
@extend .cp-fx__item-one;
@extend .cp-fx__column;
}
.emoji-board__content__info {
margin: 0 var(--sp-extra-tight);
padding: var(--sp-tight) var(--sp-extra-tight);
border-top: 1px solid var(--bg-surface-border);
display: flex;
align-items: center;
& > div:first-child {
line-height: 0;
.emoji {
width: 32px;
height: 32px;
object-fit: contain;
}
}
& > p:last-child {
@extend .cp-fx__item-one;
@extend .cp-txt__ellipsis;
margin: 0 var(--sp-tight);
}
}
.emoji-row {
display: flex;
}
.emoji-group {
--emoji-padding: 6px;
position: relative;
margin-bottom: var(--sp-normal);
&__header {
position: sticky;
top: 0;
z-index: 99;
background-color: var(--bg-surface);
@include dir.side(margin, var(--sp-extra-tight), 0);
padding: var(--sp-extra-tight) var(--sp-ultra-tight);
text-transform: uppercase;
box-shadow: 0 -4px 0 0 var(--bg-surface);
border-bottom: 1px solid var(--bg-surface-border);
}
& .emoji-set {
--left-margin: calc(var(--sp-normal) - var(--emoji-padding));
--right-margin: calc(var(--sp-extra-tight) - var(--emoji-padding));
margin: var(--sp-extra-tight);
@include dir.side(margin, var(--left-margin), var(--right-margin));
}
& .emoji {
max-width: 38px;
max-height: 38px;
width: 100%;
height: 100%;
overflow: hidden;
object-fit: contain;
padding: var(--emoji-padding);
cursor: pointer;
&:hover {
background-color: var(--bg-surface-hover);
border-radius: var(--bo-radius);
}
}
}

View File

@@ -1,78 +0,0 @@
import React, { useEffect, useRef } from 'react';
import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation';
import settings from '../../../client/state/settings';
import ContextMenu from '../../atoms/context-menu/ContextMenu';
import EmojiBoard from './EmojiBoard';
let requestCallback = null;
let isEmojiBoardVisible = false;
function EmojiBoardOpener() {
const openerRef = useRef(null);
const searchRef = useRef(null);
function openEmojiBoard(cords, requestEmojiCallback) {
if (requestCallback !== null || isEmojiBoardVisible) {
requestCallback = null;
if (cords.detail === 0) openerRef.current.click();
return;
}
openerRef.current.style.transform = `translate(${cords.x}px, ${cords.y}px)`;
requestCallback = requestEmojiCallback;
openerRef.current.click();
}
function afterEmojiBoardToggle(isVisible) {
isEmojiBoardVisible = isVisible;
if (isVisible) {
if (!settings.isTouchScreenDevice) searchRef.current.focus();
} else {
setTimeout(() => {
if (!isEmojiBoardVisible) requestCallback = null;
}, 500);
}
}
function addEmoji(emoji) {
requestCallback(emoji);
}
useEffect(() => {
navigation.on(cons.events.navigation.EMOJIBOARD_OPENED, openEmojiBoard);
return () => {
navigation.removeListener(cons.events.navigation.EMOJIBOARD_OPENED, openEmojiBoard);
};
}, []);
return (
<ContextMenu
content={(
<EmojiBoard onSelect={addEmoji} searchRef={searchRef} />
)}
afterToggle={afterEmojiBoardToggle}
render={(toggleMenu) => (
<input
ref={openerRef}
onClick={toggleMenu}
type="button"
style={{
width: '32px',
height: '32px',
backgroundColor: 'transparent',
position: 'absolute',
top: 0,
left: 0,
padding: 0,
border: 'none',
visibility: 'hidden',
}}
/>
)}
/>
);
}
export default EmojiBoardOpener;

View File

@@ -1,8 +1,6 @@
import { emojis } from './emoji';
// https://github.com/Sorunome/matrix-doc/blob/soru/emotes/proposals/2545-emotes.md
class ImagePack {
export class ImagePack {
static parsePack(eventId, packContent) {
if (!eventId || typeof packContent?.images !== 'object') {
return null;
@@ -141,127 +139,4 @@ class ImagePack {
}
}
function getGlobalImagePacks(mx) {
const globalContent = mx.getAccountData('im.ponies.emote_rooms')?.getContent();
if (typeof globalContent !== 'object') return [];
const { rooms } = globalContent;
if (typeof rooms !== 'object') return [];
const roomIds = Object.keys(rooms);
const packs = roomIds.flatMap((roomId) => {
if (typeof rooms[roomId] !== 'object') return [];
const room = mx.getRoom(roomId);
if (!room) return [];
const stateKeys = Object.keys(rooms[roomId]);
return stateKeys.map((stateKey) => {
const data = room.currentState.getStateEvents('im.ponies.room_emotes', stateKey);
const pack = ImagePack.parsePack(data?.getId(), data?.getContent());
if (pack) {
pack.displayName ??= room.name;
pack.avatarUrl ??= room.getMxcAvatarUrl();
}
return pack;
}).filter((pack) => pack !== null);
});
return packs;
}
function getUserImagePack(mx) {
const accountDataEmoji = mx.getAccountData('im.ponies.user_emotes');
if (!accountDataEmoji) {
return null;
}
const userImagePack = ImagePack.parsePack(mx.getUserId(), accountDataEmoji.event.content);
if (userImagePack) userImagePack.displayName ??= 'Personal Emoji';
return userImagePack;
}
function getRoomImagePacks(room) {
const dataEvents = room.currentState.getStateEvents('im.ponies.room_emotes');
return dataEvents
.map((data) => {
const pack = ImagePack.parsePack(data?.getId(), data?.getContent());
if (pack) {
pack.displayName ??= room.name;
pack.avatarUrl ??= room.getMxcAvatarUrl();
}
return pack;
})
.filter((pack) => pack !== null);
}
/**
* @param {MatrixClient} mx Provide if you want to include user personal/global pack
* @param {Room[]} rooms Provide rooms if you want to include rooms pack
* @returns {ImagePack[]} packs
*/
function getRelevantPacks(mx, rooms) {
const userPack = mx ? getUserImagePack(mx) : [];
const globalPacks = mx ? getGlobalImagePacks(mx) : [];
const globalPackIds = new Set(globalPacks.map((pack) => pack.id));
const roomsPack = rooms?.flatMap(getRoomImagePacks) ?? [];
return [].concat(
userPack ?? [],
globalPacks,
roomsPack.filter((pack) => !globalPackIds.has(pack.id)),
);
}
function getShortcodeToEmoji(mx, rooms) {
const allEmoji = new Map();
emojis.forEach((emoji) => {
if (Array.isArray(emoji.shortcodes)) {
emoji.shortcodes.forEach((shortcode) => {
allEmoji.set(shortcode, emoji);
});
} else {
allEmoji.set(emoji.shortcodes, emoji);
}
});
getRelevantPacks(mx, rooms)
.flatMap((pack) => pack.getEmojis())
.forEach((emoji) => {
allEmoji.set(emoji.shortcode, emoji);
});
return allEmoji;
}
function getShortcodeToCustomEmoji(room) {
const allEmoji = new Map();
getRelevantPacks(room.client, [room])
.flatMap((pack) => pack.getEmojis())
.forEach((emoji) => {
allEmoji.set(emoji.shortcode, emoji);
});
return allEmoji;
}
function getEmojiForCompletion(mx, rooms) {
const allEmoji = new Map();
getRelevantPacks(mx, rooms)
.flatMap((pack) => pack.getEmojis())
.forEach((emoji) => {
allEmoji.set(emoji.shortcode, emoji);
});
return Array.from(allEmoji.values()).concat(emojis.filter((e) => !allEmoji.has(e.shortcode)));
}
export {
ImagePack,
getUserImagePack, getGlobalImagePacks, getRoomImagePacks,
getShortcodeToEmoji, getShortcodeToCustomEmoji,
getRelevantPacks, getEmojiForCompletion,
};

View File

@@ -1,69 +0,0 @@
import emojisData from 'emojibase-data/en/compact.json';
import joypixels from 'emojibase-data/en/shortcodes/joypixels.json';
import emojibase from 'emojibase-data/en/shortcodes/emojibase.json';
const emojiGroups = [{
name: 'Smileys & people',
order: 0,
emojis: [],
}, {
name: 'Animals & nature',
order: 1,
emojis: [],
}, {
name: 'Food & drinks',
order: 2,
emojis: [],
}, {
name: 'Activity',
order: 3,
emojis: [],
}, {
name: 'Travel & places',
order: 4,
emojis: [],
}, {
name: 'Objects',
order: 5,
emojis: [],
}, {
name: 'Symbols',
order: 6,
emojis: [],
}, {
name: 'Flags',
order: 7,
emojis: [],
}];
Object.freeze(emojiGroups);
function addEmoji(emoji, order) {
emojiGroups[order].emojis.push(emoji);
}
function addToGroup(emoji) {
if (emoji.group === 0 || emoji.group === 1) addEmoji(emoji, 0);
else if (emoji.group === 3) addEmoji(emoji, 1);
else if (emoji.group === 4) addEmoji(emoji, 2);
else if (emoji.group === 6) addEmoji(emoji, 3);
else if (emoji.group === 5) addEmoji(emoji, 4);
else if (emoji.group === 7) addEmoji(emoji, 5);
else if (emoji.group === 8 || typeof emoji.group === 'undefined') addEmoji(emoji, 6);
else if (emoji.group === 9) addEmoji(emoji, 7);
}
const emojis = [];
emojisData.forEach((emoji) => {
const myShortCodes = joypixels[emoji.hexcode] || emojibase[emoji.hexcode];
if (!myShortCodes) return;
const em = {
...emoji,
shortcode: Array.isArray(myShortCodes) ? myShortCodes[0] : myShortCodes,
shortcodes: myShortCodes,
};
addToGroup(em);
emojis.push(em);
});
export {
emojis, emojiGroups,
};

View File

@@ -1,36 +0,0 @@
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),
});
}