From aa6fb5940beeca301e7699c578067c9a0bcb2e7d Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Fri, 5 Aug 2022 17:10:58 +0530 Subject: [PATCH] Add support for sending stickers --- public/res/ic/outlined/sticker.svg | 4 + src/app/organisms/emoji-board/EmojiBoard.scss | 1 + src/app/organisms/room/RoomViewInput.jsx | 55 +++++++++++- .../organisms/sticker-board/StickerBoard.jsx | 88 +++++++++++++++++++ .../organisms/sticker-board/StickerBoard.scss | 60 +++++++++++++ 5 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 public/res/ic/outlined/sticker.svg create mode 100644 src/app/organisms/sticker-board/StickerBoard.jsx create mode 100644 src/app/organisms/sticker-board/StickerBoard.scss diff --git a/public/res/ic/outlined/sticker.svg b/public/res/ic/outlined/sticker.svg new file mode 100644 index 00000000..bc486e5e --- /dev/null +++ b/public/res/ic/outlined/sticker.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/app/organisms/emoji-board/EmojiBoard.scss b/src/app/organisms/emoji-board/EmojiBoard.scss index 256bdf16..6883e18e 100644 --- a/src/app/organisms/emoji-board/EmojiBoard.scss +++ b/src/app/organisms/emoji-board/EmojiBoard.scss @@ -84,6 +84,7 @@ .emoji { width: 32px; height: 32px; + object-fit: contain; } } & > p:last-child { diff --git a/src/app/organisms/room/RoomViewInput.jsx b/src/app/organisms/room/RoomViewInput.jsx index f4ba3f3f..a39f92b4 100644 --- a/src/app/organisms/room/RoomViewInput.jsx +++ b/src/app/organisms/room/RoomViewInput.jsx @@ -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'; @@ -203,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 === ''; @@ -342,6 +372,29 @@ function RoomViewInput({ {isMarkdown && }
+ { + openReusableContextMenu( + 'top', + (() => { + const cords = getEventCords(e); + cords.y -= 20; + return cords; + })(), + (closeMenu) => ( + { + handleSendSticker(data); + closeMenu(); + }} + /> + ), + ); + }} + tooltip="Sticker" + src={StickerIC} + /> { const cords = getEventCords(e); diff --git a/src/app/organisms/sticker-board/StickerBoard.jsx b/src/app/organisms/sticker-board/StickerBoard.jsx new file mode 100644 index 00000000..53b75635 --- /dev/null +++ b/src/app/organisms/sticker-board/StickerBoard.jsx @@ -0,0 +1,88 @@ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +import React from 'react'; +import PropTypes from 'prop-types'; +import './StickerBoard.scss'; + +import initMatrix from '../../../client/initMatrix'; +import { getRelevantPacks } from '../emoji-board/custom-emoji'; + +import Text from '../../atoms/text/Text'; +import ScrollView from '../../atoms/scroll/ScrollView'; + +function StickerBoard({ roomId, onSelect }) { + const mx = initMatrix.matrixClient; + const room = mx.getRoom(roomId); + + const parentIds = initMatrix.roomList.getAllParentSpaces(room.roomId); + const parentRooms = [...parentIds].map((id) => mx.getRoom(id)); + + const packs = getRelevantPacks( + mx, + [room, ...parentRooms], + ).filter((pack) => pack.getStickers().length !== 0); + + function isTargetNotSticker(target) { + return target.classList.contains('sticker-board__sticker') === false; + } + function getStickerData(target) { + const mxc = target.getAttribute('data-mx-sticker'); + const body = target.getAttribute('title'); + const httpUrl = target.getAttribute('src'); + return { mxc, body, httpUrl }; + } + const handleOnSelect = (e) => { + if (isTargetNotSticker(e.target)) return; + + const stickerData = getStickerData(e.target); + onSelect(stickerData); + }; + + const renderPack = (pack) => ( +
+ {pack.displayName ?? 'Unknown'} +
+ {pack.getStickers().map((sticker) => ( + {sticker.shortcode} + ))} +
+
+ ); + + return ( +
+
+ +
+ { + packs.length > 0 + ? packs.map(renderPack) + : ( +
+ There is no sticker pack. +
+ ) + } +
+
+
+
+
+ ); +} +StickerBoard.propTypes = { + roomId: PropTypes.string.isRequired, + onSelect: PropTypes.func.isRequired, +}; + +export default StickerBoard; diff --git a/src/app/organisms/sticker-board/StickerBoard.scss b/src/app/organisms/sticker-board/StickerBoard.scss new file mode 100644 index 00000000..be8ad35a --- /dev/null +++ b/src/app/organisms/sticker-board/StickerBoard.scss @@ -0,0 +1,60 @@ +@use '../../partials/dir'; + +.sticker-board { + --sticker-board-height: 390px; + --sticker-board-width: 286px; + display: flex; + height: var(--sticker-board-height); + + &__container { + flex-grow: 1; + min-width: 0; + width: var(--sticker-board-width); + display: flex; + } + + &__content { + min-height: 100%; + } + + &__pack { + margin-bottom: var(--sp-normal); + position: relative; + + &-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); + } + &-items { + margin: var(--sp-tight); + @include dir.side(margin, var(--sp-normal), var(--sp-extra-tight)); + display: flex; + flex-wrap: wrap; + gap: var(--sp-normal) var(--sp-tight); + + img { + width: 76px; + height: 76px; + object-fit: contain; + cursor: pointer; + } + } + } + + &__empty { + width: 100%; + height: var(--sticker-board-height); + display: flex; + justify-content: center; + align-items: center; + text-align: center; + } +} \ No newline at end of file