Custom emoji & Sticker support (#686)
* Remove comments * Show custom emoji first in suggestions * Show global image packs in emoji picker * Display emoji and sticker in room settings * Fix some pack not visible in emojiboard * WIP * Add/delete/rename images to exisitng packs * Change pack avatar, name & attribution * Add checkbox to make pack global * Bug fix * Create or delete pack * Add personal emoji in settings * Show global pack selector in settings * Show space emoji in emojiboard * Send custom emoji reaction as mxc * Render stickers as stickers * Fix sticker jump bug * Fix reaction width * Fix stretched custom emoji * Fix sending space emoji in message * Remove unnessesary comments * Send user pills * Fix pill generating regex * Add support for sending stickers
This commit is contained in:
@@ -25,9 +25,11 @@ import RoomHistoryVisibility from '../../molecules/room-history-visibility/RoomH
|
||||
import RoomEncryption from '../../molecules/room-encryption/RoomEncryption';
|
||||
import RoomPermissions from '../../molecules/room-permissions/RoomPermissions';
|
||||
import RoomMembers from '../../molecules/room-members/RoomMembers';
|
||||
import RoomEmojis from '../../molecules/room-emojis/RoomEmojis';
|
||||
|
||||
import UserIC from '../../../../public/res/ic/outlined/user.svg';
|
||||
import SettingsIC from '../../../../public/res/ic/outlined/settings.svg';
|
||||
import EmojiIC from '../../../../public/res/ic/outlined/emoji.svg';
|
||||
import SearchIC from '../../../../public/res/ic/outlined/search.svg';
|
||||
import ShieldUserIC from '../../../../public/res/ic/outlined/shield-user.svg';
|
||||
import LockIC from '../../../../public/res/ic/outlined/lock.svg';
|
||||
@@ -42,6 +44,7 @@ const tabText = {
|
||||
GENERAL: 'General',
|
||||
SEARCH: 'Search',
|
||||
MEMBERS: 'Members',
|
||||
EMOJIS: 'Emojis',
|
||||
PERMISSIONS: 'Permissions',
|
||||
SECURITY: 'Security',
|
||||
};
|
||||
@@ -58,6 +61,10 @@ const tabItems = [{
|
||||
iconSrc: UserIC,
|
||||
text: tabText.MEMBERS,
|
||||
disabled: false,
|
||||
}, {
|
||||
iconSrc: EmojiIC,
|
||||
text: tabText.EMOJIS,
|
||||
disabled: false,
|
||||
}, {
|
||||
iconSrc: ShieldUserIC,
|
||||
text: tabText.PERMISSIONS,
|
||||
@@ -197,6 +204,7 @@ function RoomSettings({ roomId }) {
|
||||
{selectedTab.text === tabText.GENERAL && <GeneralSettings roomId={roomId} />}
|
||||
{selectedTab.text === tabText.SEARCH && <RoomSearch roomId={roomId} />}
|
||||
{selectedTab.text === tabText.MEMBERS && <RoomMembers roomId={roomId} />}
|
||||
{selectedTab.text === tabText.EMOJIS && <RoomEmojis roomId={roomId} />}
|
||||
{selectedTab.text === tabText.PERMISSIONS && <RoomPermissions roomId={roomId} />}
|
||||
{selectedTab.text === tabText.SECURITY && <SecuritySettings roomId={roomId} />}
|
||||
</div>
|
||||
@@ -210,7 +218,5 @@ RoomSettings.propTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export {
|
||||
RoomSettings as default,
|
||||
tabText,
|
||||
};
|
||||
export default RoomSettings;
|
||||
export { tabText };
|
||||
|
||||
@@ -21,7 +21,7 @@ import AsyncSearch from '../../../util/AsyncSearch';
|
||||
import Text from '../../atoms/text/Text';
|
||||
import ScrollView from '../../atoms/scroll/ScrollView';
|
||||
import FollowingMembers from '../../molecules/following-members/FollowingMembers';
|
||||
import { addRecentEmoji } from '../emoji-board/recent';
|
||||
import { addRecentEmoji, getRecentEmojis } from '../emoji-board/recent';
|
||||
|
||||
const commands = [{
|
||||
name: 'markdown',
|
||||
@@ -213,9 +213,15 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
|
||||
setCmd({ prefix, suggestions: commands });
|
||||
},
|
||||
':': () => {
|
||||
const emojis = getEmojiForCompletion(mx.getRoom(roomId));
|
||||
const parentIds = initMatrix.roomList.getAllParentSpaces(roomId);
|
||||
const parentRooms = [...parentIds].map((id) => mx.getRoom(id));
|
||||
const emojis = getEmojiForCompletion(mx, [mx.getRoom(roomId), ...parentRooms]);
|
||||
const recentEmoji = getRecentEmojis(20);
|
||||
asyncSearch.setup(emojis, { keys: ['shortcode'], isContain: true, limit: 20 });
|
||||
setCmd({ prefix, suggestions: emojis.slice(26, 46) });
|
||||
setCmd({
|
||||
prefix,
|
||||
suggestions: recentEmoji.length > 0 ? recentEmoji : emojis.slice(26, 46),
|
||||
});
|
||||
},
|
||||
'@': () => {
|
||||
const members = mx.getRoom(roomId).getJoinedMembers().map((member) => ({
|
||||
@@ -247,7 +253,7 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
|
||||
}
|
||||
if (myCmd.prefix === '@') {
|
||||
viewEvent.emit('cmd_fired', {
|
||||
replace: myCmd.result.name,
|
||||
replace: `@${myCmd.result.userId}`,
|
||||
});
|
||||
}
|
||||
deactivateCmd();
|
||||
|
||||
@@ -8,7 +8,7 @@ import TextareaAutosize from 'react-autosize-textarea';
|
||||
import initMatrix from '../../../client/initMatrix';
|
||||
import cons from '../../../client/state/cons';
|
||||
import settings from '../../../client/state/settings';
|
||||
import { openEmojiBoard } from '../../../client/action/navigation';
|
||||
import { openEmojiBoard, openReusableContextMenu } from '../../../client/action/navigation';
|
||||
import navigation from '../../../client/state/navigation';
|
||||
import { bytesToSize, getEventCords } from '../../../util/common';
|
||||
import { getUsername } from '../../../util/matrixUtil';
|
||||
@@ -20,9 +20,12 @@ import IconButton from '../../atoms/button/IconButton';
|
||||
import ScrollView from '../../atoms/scroll/ScrollView';
|
||||
import { MessageReply } from '../../molecules/message/Message';
|
||||
|
||||
import StickerBoard from '../sticker-board/StickerBoard';
|
||||
|
||||
import CirclePlusIC from '../../../../public/res/ic/outlined/circle-plus.svg';
|
||||
import EmojiIC from '../../../../public/res/ic/outlined/emoji.svg';
|
||||
import SendIC from '../../../../public/res/ic/outlined/send.svg';
|
||||
import StickerIC from '../../../../public/res/ic/outlined/sticker.svg';
|
||||
import ShieldIC from '../../../../public/res/ic/outlined/shield.svg';
|
||||
import VLCIC from '../../../../public/res/ic/outlined/vlc.svg';
|
||||
import VolumeFullIC from '../../../../public/res/ic/outlined/volume-full.svg';
|
||||
@@ -128,7 +131,11 @@ function RoomViewInput({
|
||||
}
|
||||
function firedCmd(cmdData) {
|
||||
const msg = textAreaRef.current.value;
|
||||
textAreaRef.current.value = replaceCmdWith(msg, cmdCursorPos, typeof cmdData?.replace !== 'undefined' ? cmdData.replace : '');
|
||||
textAreaRef.current.value = replaceCmdWith(
|
||||
msg,
|
||||
cmdCursorPos,
|
||||
typeof cmdData?.replace !== 'undefined' ? cmdData.replace : '',
|
||||
);
|
||||
deactivateCmd();
|
||||
}
|
||||
|
||||
@@ -199,6 +206,33 @@ function RoomViewInput({
|
||||
if (replyTo !== null) setReplyTo(null);
|
||||
};
|
||||
|
||||
const handleSendSticker = async (data) => {
|
||||
const { mxc: url, body, httpUrl } = data;
|
||||
const info = {};
|
||||
|
||||
const img = new Image();
|
||||
img.src = httpUrl;
|
||||
|
||||
try {
|
||||
const res = await fetch(httpUrl);
|
||||
const blob = await res.blob();
|
||||
info.w = img.width;
|
||||
info.h = img.height;
|
||||
info.mimetype = blob.type;
|
||||
info.size = blob.size;
|
||||
info.thumbnail_info = { ...info };
|
||||
info.thumbnail_url = url;
|
||||
} catch {
|
||||
// send sticker without info
|
||||
}
|
||||
|
||||
mx.sendEvent(roomId, 'm.sticker', {
|
||||
body,
|
||||
url,
|
||||
info,
|
||||
});
|
||||
};
|
||||
|
||||
function processTyping(msg) {
|
||||
const isEmptyMsg = msg === '';
|
||||
|
||||
@@ -338,6 +372,29 @@ function RoomViewInput({
|
||||
{isMarkdown && <RawIcon size="extra-small" src={MarkdownIC} />}
|
||||
</div>
|
||||
<div ref={rightOptionsRef} className="room-input__option-container">
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
openReusableContextMenu(
|
||||
'top',
|
||||
(() => {
|
||||
const cords = getEventCords(e);
|
||||
cords.y -= 20;
|
||||
return cords;
|
||||
})(),
|
||||
(closeMenu) => (
|
||||
<StickerBoard
|
||||
roomId={roomId}
|
||||
onSelect={(data) => {
|
||||
handleSendSticker(data);
|
||||
closeMenu();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
);
|
||||
}}
|
||||
tooltip="Sticker"
|
||||
src={StickerIC}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
const cords = getEventCords(e);
|
||||
|
||||
Reference in New Issue
Block a user