This commit is contained in:
Ajay Bura 2022-07-27 10:09:46 +05:30 committed by Ajay Bura
parent 5816ec7d2f
commit 7d8c4ce5f1
11 changed files with 211 additions and 55 deletions

View file

@ -6,9 +6,9 @@ import initMatrix from '../../../client/initMatrix';
import Button from '../../atoms/button/Button';
import Text from '../../atoms/text/Text';
import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
import ImagePackProfile from './ImagePackProfile';
import ImagePackItem from './ImagePackItem';
import Checkbox from '../../atoms/button/Checkbox';
import { ImagePack as ImagePackBuilder, getUserImagePack } from '../../organisms/emoji-board/custom-emoji';
function getUsage(usage) {
@ -19,6 +19,17 @@ function getUsage(usage) {
return 'both';
}
function isGlobalPack(roomId, stateKey) {
const mx = initMatrix.matrixClient;
const globalContent = mx.getAccountData('im.ponies.emote_rooms')?.getContent();
if (typeof globalContent !== 'object') return false;
const { rooms } = globalContent;
if (typeof rooms !== 'object') return false;
return rooms[roomId]?.[stateKey] !== undefined;
}
function ImagePack({ roomId, stateKey }) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
@ -33,32 +44,34 @@ function ImagePack({ roomId, stateKey }) {
return (
<div className="image-pack">
<MenuHeader>{pack.displayName}</MenuHeader>
<ImagePackProfile
avatarUrl={mx.mxcUrlToHttp(pack.avatarUrl ?? pack.getEmojis()[0].mxc)}
displayName={pack.displayName}
attribution={pack.attribution}
usage={getUsage(pack.usage)}
onUsage={() => false}
onUsageChange={(newUsage) => console.log(newUsage)}
onEdit={() => false}
/>
<div className="image-pack__header">
<Text variant="b3">Image</Text>
<Text variant="b3">Shortcode</Text>
<Text variant="b3">Usage</Text>
</div>
<div>
<div className="image-pack__header">
<Text variant="b3">Image</Text>
<Text variant="b3">Shortcode</Text>
<Text variant="b3">Usage</Text>
</div>
{([...pack.images].slice(0, viewMore ? pack.images.size : 2)).map(([shortcode, image]) => (
<ImagePackItem
key={shortcode}
url={mx.mxcUrlToHttp(image.mxc)}
shortcode={shortcode}
usage={getUsage(image.usage)}
onUsageChange={() => false}
onDelete={() => false}
onRename={() => false}
/>
))}
</div>
<div className="image-pack__footer">
{pack.images.size > 2 && (
{pack.images.size > 2 && (
<div className="image-pack__footer">
<Button onClick={() => setViewMore(!viewMore)}>
{
viewMore
@ -66,8 +79,17 @@ function ImagePack({ roomId, stateKey }) {
: `View ${pack.images.size - 2} more`
}
</Button>
)}
</div>
</div>
)}
{ roomId && (
<div className="image-pack__global">
<Checkbox variant="positive" isActive={isGlobalPack(roomId, stateKey)} />
<div>
<Text variant="b2">Use globally</Text>
<Text variant="b3">Add this pack to your account to use in all rooms.</Text>
</div>
</div>
)}
</div>
);
}

View file

@ -6,7 +6,6 @@
}
&__header {
margin-top: var(--sp-normal);
padding: var(--sp-extra-tight) var(--sp-normal);
display: flex;
align-items: center;
@ -19,5 +18,13 @@
&__footer {
padding: var(--sp-normal);
display: flex;
}
&__global {
padding: var(--sp-normal);
padding-top: var(--sp-tight);
display: flex;
align-items: center;
gap: var(--sp-normal);
}
}

View file

@ -2,14 +2,39 @@ import React from 'react';
import PropTypes from 'prop-types';
import './ImagePackItem.scss';
import { openReusableContextMenu } from '../../../client/action/navigation';
import { getEventCords } from '../../../util/common';
import Avatar from '../../atoms/avatar/Avatar';
import Text from '../../atoms/text/Text';
import Button from '../../atoms/button/Button';
import RawIcon from '../../atoms/system-icons/RawIcon';
import IconButton from '../../atoms/button/IconButton';
import ImagePackUsageSelector from './ImagePackUsageSelector';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
import PencilIC from '../../../../public/res/ic/outlined/pencil.svg';
import BinIC from '../../../../public/res/ic/outlined/bin.svg';
function ImagePackItem({
url, shortcode, usage, onUsageChange, onDelete, onRename,
}) {
const handleUsageSelect = (event) => {
openReusableContextMenu(
'bottom',
getEventCords(event, '.btn-surface'),
(closeMenu) => (
<ImagePackUsageSelector
usage={usage}
onSelect={(newUsage) => {
onUsageChange(newUsage);
closeMenu();
}}
/>
),
);
};
function ImagePackItem({ url, shortcode, usage }) {
return (
<div className="image-pack-item">
<Avatar imageSrc={url} size="extra-small" text={shortcode} bgColor="black" />
@ -17,8 +42,12 @@ function ImagePackItem({ url, shortcode, usage }) {
<Text>{shortcode}</Text>
</div>
<div className="image-pack-item__usage">
<Button>
<RawIcon src={ChevronBottomIC} size="extra-small" />
<div className="image-pack-item__btn">
{onRename && <IconButton tooltip="Rename" size="extra-small" src={PencilIC} onClick={onRename} />}
{onDelete && <IconButton tooltip="Delete" size="extra-small" src={BinIC} onClick={onDelete} />}
</div>
<Button onClick={onUsageChange ? handleUsageSelect : undefined}>
{onUsageChange && <RawIcon src={ChevronBottomIC} size="extra-small" />}
<Text variant="b2">
{usage === 'emoticon' && 'Emoji'}
{usage === 'sticker' && 'Sticker'}
@ -30,10 +59,18 @@ function ImagePackItem({ url, shortcode, usage }) {
);
}
ImagePackItem.defaultProps = {
onUsageChange: null,
onDelete: null,
onRename: null,
};
ImagePackItem.propTypes = {
url: PropTypes.string.isRequired,
shortcode: PropTypes.string.isRequired,
usage: PropTypes.oneOf(['emoticon', 'sticker', 'both']).isRequired,
onUsageChange: PropTypes.func,
onDelete: PropTypes.func,
onRename: PropTypes.func,
};
export default ImagePackItem;

View file

@ -1,4 +1,5 @@
@use '../../partials/flex';
@use '../../partials/dir';
.image-pack-item {
margin: 0 var(--sp-normal);
@ -11,7 +12,27 @@
@extend .cp-fx__item-one;
}
&__usage > button {
padding: 6px var(--sp-extra-tight);
&__usage {
display: flex;
gap: var(--sp-ultra-tight);
& button {
padding: 6px;
}
& > button.btn-surface {
padding: 6px var(--sp-tight);
min-width: 0;
@include dir.side(margin, var(--sp-ultra-tight), 0);
}
}
&__btn {
display: none;
}
&:hover,
&:focus-within {
.image-pack-item__btn {
display: flex;
gap: var(--sp-ultra-tight);
}
}
}

View file

@ -2,33 +2,58 @@ import React from 'react';
import PropTypes from 'prop-types';
import './ImagePackProfile.scss';
import { openReusableContextMenu } from '../../../client/action/navigation';
import { getEventCords } from '../../../util/common';
import Text from '../../atoms/text/Text';
import Avatar from '../../atoms/avatar/Avatar';
import Button from '../../atoms/button/Button';
import IconButton from '../../atoms/button/IconButton';
import ImagePackUsageSelector from './ImagePackUsageSelector';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
import PencilIC from '../../../../public/res/ic/outlined/pencil.svg';
function ImagePackProfile({
avatarUrl, displayName, attribution, usage, onUsage, onEdit,
avatarUrl, displayName, attribution, usage, onUsageChange, onEdit,
}) {
const handleUsageSelect = (event) => {
openReusableContextMenu(
'bottom',
getEventCords(event, '.btn-surface'),
(closeMenu) => (
<ImagePackUsageSelector
usage={usage}
onSelect={(newUsage) => {
onUsageChange(newUsage);
closeMenu();
}}
/>
),
);
};
return (
<div className="image-pack-profile">
<Avatar text={displayName} bgColor="blue" imageSrc={avatarUrl} size="normal" />
<div className="image-pack-profile__content">
<div>
<Text>{displayName}</Text>
{onEdit && <IconButton size="extra-small" onClick={onEdit} src={PencilIC} />}
{onEdit && <IconButton size="extra-small" onClick={onEdit} src={PencilIC} tooltip="Edit" />}
</div>
{attribution && <Text variant="b3">{attribution}</Text>}
</div>
<div className="image-pack-profile__usage">
<Text variant="b3">Pack usage</Text>
<Button iconSrc={onUsage ? ChevronBottomIC : null} onClick={onUsage}>
{usage === 'emoticon' && 'Emoji'}
{usage === 'sticker' && 'Sticker'}
{usage === 'both' && 'Emoji & Sticker'}
<Button
onClick={onUsageChange ? handleUsageSelect : undefined}
iconSrc={onUsageChange ? ChevronBottomIC : null}
>
<Text>
{usage === 'emoticon' && 'Emoji'}
{usage === 'sticker' && 'Sticker'}
{usage === 'both' && 'Both'}
</Text>
</Button>
</div>
</div>
@ -38,7 +63,7 @@ function ImagePackProfile({
ImagePackProfile.defaultProps = {
avatarUrl: null,
attribution: null,
onUsage: null,
onUsageChange: null,
onEdit: null,
};
ImagePackProfile.propTypes = {
@ -46,7 +71,7 @@ ImagePackProfile.propTypes = {
displayName: PropTypes.string.isRequired,
attribution: PropTypes.string,
usage: PropTypes.oneOf(['emoticon', 'sticker', 'both']).isRequired,
onUsage: PropTypes.func,
onUsageChange: PropTypes.func,
onEdit: PropTypes.func,
};

View file

@ -3,15 +3,20 @@
.image-pack-profile {
padding: var(--sp-normal);
display: flex;
align-items: flex-start;
gap: var(--sp-tight);
&__content {
padding: 0 var(--sp-tight);
@extend .cp-fx__item-one;
& div:first-child {
display: flex;
align-items: center;
gap: var(--sp-extra-tight);
& .ic-btn {
padding: var(--sp-ultra-tight);
}
}
}
&__usage {

View file

@ -2,14 +2,33 @@ import React from 'react';
import PropTypes from 'prop-types';
import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu';
import CheckIC from '../../../../public/res/ic/outlined/check.svg';
function ImagePackUsageSelector({ usage, onSelect }) {
return (
<div>
<MenuHeader>Usage</MenuHeader>
<MenuItem variant={usage === 'emoticon' ? 'positive' : 'surface'} onClick={() => onSelect('emoticon')}>Emoji</MenuItem>
<MenuItem variant={usage === 'sticker' ? 'positive' : 'surface'} onClick={() => onSelect('sticker')}>Sticker</MenuItem>
<MenuItem variant={usage === 'both' ? 'positive' : 'surface'} onClick={() => onSelect('both')}>Both</MenuItem>
<MenuItem
iconSrc={usage === 'emoticon' ? CheckIC : undefined}
variant={usage === 'emoticon' ? 'positive' : 'surface'}
onClick={() => onSelect('emoticon')}
>
Emoji
</MenuItem>
<MenuItem
iconSrc={usage === 'sticker' ? CheckIC : undefined}
variant={usage === 'sticker' ? 'positive' : 'surface'}
onClick={() => onSelect('sticker')}
>
Sticker
</MenuItem>
<MenuItem
iconSrc={usage === 'both' ? CheckIC : undefined}
variant={usage === 'both' ? 'positive' : 'surface'}
onClick={() => onSelect('both')}
>
Both
</MenuItem>
</div>
);
}

View file

@ -0,0 +1,34 @@
import React from 'react';
import PropTypes from 'prop-types';
import initMatrix from '../../../client/initMatrix';
import ImagePack from '../image-pack/ImagePack';
function RoomEmojis({ roomId }) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
const packEvents = room.currentState.getStateEvents('im.ponies.room_emotes');
const unUsablePacks = [];
const usablePacks = packEvents.filter((mEvent) => {
if (typeof mEvent.getContent()?.images !== 'object') {
unUsablePacks.push(mEvent);
return false;
}
return true;
});
return usablePacks.map((mEvent) => (
<ImagePack
key={mEvent.getId()}
roomId={roomId}
stateKey={mEvent.getStateKey()}
/>
));
}
RoomEmojis.propTypes = {
roomId: PropTypes.string.isRequired,
};
export default RoomEmojis;

View file

@ -25,7 +25,7 @@ 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 ImagePack from '../../molecules/image-pack/ImagePack';
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';
@ -128,29 +128,6 @@ GeneralSettings.propTypes = {
roomId: PropTypes.string.isRequired,
};
function RoomEmojis({ roomId }) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
const packEvents = room.currentState.getStateEvents('im.ponies.room_emotes');
const unUsablePacks = [];
const usablePacks = packEvents.filter((mEvent) => {
if (typeof mEvent.getContent()?.images !== 'object') {
unUsablePacks.push(mEvent);
return false;
}
return true;
});
return usablePacks.map((mEvent) => (
<ImagePack
key={mEvent.getId()}
roomId={roomId}
stateKey={mEvent.getStateKey()}
/>
));
}
function SecuritySettings({ roomId }) {
return (
<>

View file

@ -25,6 +25,7 @@ import RoomVisibility from '../../molecules/room-visibility/RoomVisibility';
import RoomAliases from '../../molecules/room-aliases/RoomAliases';
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 CrossIC from '../../../../public/res/ic/outlined/cross.svg';
@ -35,6 +36,7 @@ import PinIC from '../../../../public/res/ic/outlined/pin.svg';
import PinFilledIC from '../../../../public/res/ic/filled/pin.svg';
import CategoryIC from '../../../../public/res/ic/outlined/category.svg';
import CategoryFilledIC from '../../../../public/res/ic/filled/category.svg';
import EmojiIC from '../../../../public/res/ic/outlined/emoji.svg';
import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import { useForceUpdate } from '../../hooks/useForceUpdate';
@ -42,6 +44,7 @@ import { useForceUpdate } from '../../hooks/useForceUpdate';
const tabText = {
GENERAL: 'General',
MEMBERS: 'Members',
EMOJIS: 'Emojis',
PERMISSIONS: 'Permissions',
};
@ -53,6 +56,10 @@ const tabItems = [{
iconSrc: UserIC,
text: tabText.MEMBERS,
disabled: false,
}, {
iconSrc: EmojiIC,
text: tabText.EMOJIS,
disabled: false,
}, {
iconSrc: ShieldUserIC,
text: tabText.PERMISSIONS,
@ -178,6 +185,7 @@ function SpaceSettings() {
<div className="space-settings__cards-wrapper">
{selectedTab.text === tabText.GENERAL && <GeneralSettings roomId={roomId} />}
{selectedTab.text === tabText.MEMBERS && <RoomMembers roomId={roomId} />}
{selectedTab.text === tabText.EMOJIS && <RoomEmojis roomId={roomId} />}
{selectedTab.text === tabText.PERMISSIONS && <RoomPermissions roomId={roomId} />}
</div>
</div>

View file

@ -32,6 +32,7 @@
}
.space-settings .room-permissions__card,
.space-settings .room-members {
.space-settings .room-members,
.space-settings .image-pack {
@extend .space-settings__card;
}