Add ability to explore and join space rooms
Signed-off-by: ajbura <ajbura@gmail.com>
This commit is contained in:
parent
a55f1df17f
commit
1f6e5e71ef
8 changed files with 538 additions and 7 deletions
|
@ -4,13 +4,14 @@ import PropTypes from 'prop-types';
|
||||||
import { twemojify } from '../../../util/twemojify';
|
import { twemojify } from '../../../util/twemojify';
|
||||||
|
|
||||||
import initMatrix from '../../../client/initMatrix';
|
import initMatrix from '../../../client/initMatrix';
|
||||||
import { openSpaceSettings, openInviteUser } from '../../../client/action/navigation';
|
import { openSpaceSettings, openSpaceManage, openInviteUser } from '../../../client/action/navigation';
|
||||||
import { leave, createSpaceShortcut, deleteSpaceShortcut } from '../../../client/action/room';
|
import { leave, createSpaceShortcut, deleteSpaceShortcut } from '../../../client/action/room';
|
||||||
|
|
||||||
import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu';
|
import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu';
|
||||||
|
|
||||||
import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
|
import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
|
||||||
import SettingsIC from '../../../../public/res/ic/outlined/settings.svg';
|
import SettingsIC from '../../../../public/res/ic/outlined/settings.svg';
|
||||||
|
import HashSearchIC from '../../../../public/res/ic/outlined/hash-search.svg';
|
||||||
import LeaveArrowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
|
import LeaveArrowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
|
||||||
import PinIC from '../../../../public/res/ic/outlined/pin.svg';
|
import PinIC from '../../../../public/res/ic/outlined/pin.svg';
|
||||||
import PinFilledIC from '../../../../public/res/ic/filled/pin.svg';
|
import PinFilledIC from '../../../../public/res/ic/filled/pin.svg';
|
||||||
|
@ -35,6 +36,10 @@ function SpaceOptions({ roomId, afterOptionSelect }) {
|
||||||
openSpaceSettings(roomId);
|
openSpaceSettings(roomId);
|
||||||
afterOptionSelect();
|
afterOptionSelect();
|
||||||
};
|
};
|
||||||
|
const handleManageRoom = () => {
|
||||||
|
openSpaceManage(roomId);
|
||||||
|
afterOptionSelect();
|
||||||
|
};
|
||||||
|
|
||||||
const handleLeaveClick = () => {
|
const handleLeaveClick = () => {
|
||||||
if (confirm('Are you really want to leave this space?')) {
|
if (confirm('Are you really want to leave this space?')) {
|
||||||
|
@ -59,6 +64,7 @@ function SpaceOptions({ roomId, afterOptionSelect }) {
|
||||||
>
|
>
|
||||||
Invite
|
Invite
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleManageRoom} iconSrc={HashSearchIC}>Manage rooms</MenuItem>
|
||||||
<MenuItem onClick={handleSettingsClick} iconSrc={SettingsIC}>Settings</MenuItem>
|
<MenuItem onClick={handleSettingsClick} iconSrc={SettingsIC}>Settings</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
variant="danger"
|
variant="danger"
|
||||||
|
|
|
@ -9,6 +9,7 @@ import CreateRoom from '../create-room/CreateRoom';
|
||||||
import InviteUser from '../invite-user/InviteUser';
|
import InviteUser from '../invite-user/InviteUser';
|
||||||
import Settings from '../settings/Settings';
|
import Settings from '../settings/Settings';
|
||||||
import SpaceSettings from '../space-settings/SpaceSettings';
|
import SpaceSettings from '../space-settings/SpaceSettings';
|
||||||
|
import SpaceManage from '../space-manage/SpaceManage';
|
||||||
|
|
||||||
function Windows() {
|
function Windows() {
|
||||||
const [isInviteList, changeInviteList] = useState(false);
|
const [isInviteList, changeInviteList] = useState(false);
|
||||||
|
@ -85,6 +86,7 @@ function Windows() {
|
||||||
onRequestClose={() => changeSettings(false)}
|
onRequestClose={() => changeSettings(false)}
|
||||||
/>
|
/>
|
||||||
<SpaceSettings />
|
<SpaceSettings />
|
||||||
|
<SpaceManage />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
352
src/app/organisms/space-manage/SpaceManage.jsx
Normal file
352
src/app/organisms/space-manage/SpaceManage.jsx
Normal file
|
@ -0,0 +1,352 @@
|
||||||
|
/* eslint-disable react/prop-types */
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import './SpaceManage.scss';
|
||||||
|
|
||||||
|
import { twemojify } from '../../../util/twemojify';
|
||||||
|
|
||||||
|
import initMatrix from '../../../client/initMatrix';
|
||||||
|
import cons from '../../../client/state/cons';
|
||||||
|
import navigation from '../../../client/state/navigation';
|
||||||
|
import colorMXID from '../../../util/colorMXID';
|
||||||
|
import { selectRoom, selectTab } from '../../../client/action/navigation';
|
||||||
|
import RoomsHierarchy from '../../../client/state/RoomsHierarchy';
|
||||||
|
import { joinRuleToIconSrc } from '../../../util/matrixUtil';
|
||||||
|
import { join } from '../../../client/action/room';
|
||||||
|
|
||||||
|
import Text from '../../atoms/text/Text';
|
||||||
|
import RawIcon from '../../atoms/system-icons/RawIcon';
|
||||||
|
import Button from '../../atoms/button/Button';
|
||||||
|
import IconButton from '../../atoms/button/IconButton';
|
||||||
|
import Checkbox from '../../atoms/button/Checkbox';
|
||||||
|
import Avatar from '../../atoms/avatar/Avatar';
|
||||||
|
import Spinner from '../../atoms/spinner/Spinner';
|
||||||
|
import ScrollView from '../../atoms/scroll/ScrollView';
|
||||||
|
import PopupWindow from '../../molecules/popup-window/PopupWindow';
|
||||||
|
|
||||||
|
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
|
||||||
|
import ChevronRightIC from '../../../../public/res/ic/outlined/chevron-right.svg';
|
||||||
|
import InfoIC from '../../../../public/res/ic/outlined/info.svg';
|
||||||
|
|
||||||
|
import { useForceUpdate } from '../../hooks/useForceUpdate';
|
||||||
|
import { useStore } from '../../hooks/useStore';
|
||||||
|
|
||||||
|
function SpaceManageBreadcrumb({ path, onSelect }) {
|
||||||
|
return (
|
||||||
|
<div className="space-manage-breadcrumb__wrapper">
|
||||||
|
<ScrollView horizontal vertical={false} invisible>
|
||||||
|
<div className="space-manage-breadcrumb">
|
||||||
|
{
|
||||||
|
path.map((item, index) => (
|
||||||
|
<React.Fragment key={item.roomId}>
|
||||||
|
{index > 0 && <RawIcon size="extra-small" src={ChevronRightIC} />}
|
||||||
|
<Button onClick={() => onSelect(item.roomId, item.name)}>
|
||||||
|
<Text variant="b2">{twemojify(item.name)}</Text>
|
||||||
|
</Button>
|
||||||
|
</React.Fragment>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</ScrollView>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SpaceManageBreadcrumb.propTypes = {
|
||||||
|
path: PropTypes.arrayOf(PropTypes.exact({
|
||||||
|
roomId: PropTypes.string,
|
||||||
|
name: PropTypes.string,
|
||||||
|
})).isRequired,
|
||||||
|
onSelect: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
function SpaceManageItem({
|
||||||
|
parentId, roomInfo, onSpaceClick, requestClose,
|
||||||
|
isSelected, onSelect, roomHierarchy,
|
||||||
|
}) {
|
||||||
|
const [isExpand, setIsExpand] = useState(false);
|
||||||
|
const [isJoining, setIsJoining] = useState(false);
|
||||||
|
|
||||||
|
const { directs } = initMatrix.roomList;
|
||||||
|
const mx = initMatrix.matrixClient;
|
||||||
|
const parentRoom = mx.getRoom(parentId);
|
||||||
|
const canManage = parentRoom?.currentState.maySendStateEvent('m.space.child', mx.getUserId()) || false;
|
||||||
|
|
||||||
|
const isSpace = roomInfo.room_type === 'm.space';
|
||||||
|
const roomId = roomInfo.room_id;
|
||||||
|
const room = mx.getRoom(roomId);
|
||||||
|
const isJoined = !!(room?.getMyMembership() === 'join' || null);
|
||||||
|
const name = room?.name || roomInfo.name || roomInfo.canonical_alias || roomId;
|
||||||
|
let imageSrc = mx.mxcUrlToHttp(roomInfo.avatar_url, 24, 24, 'crop') || null;
|
||||||
|
if (!imageSrc && room) {
|
||||||
|
imageSrc = room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
|
||||||
|
if (imageSrc === null) imageSrc = room.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOpen = () => {
|
||||||
|
if (isSpace) selectTab(roomId);
|
||||||
|
else selectRoom(roomId);
|
||||||
|
requestClose();
|
||||||
|
};
|
||||||
|
const handleJoin = () => {
|
||||||
|
const viaSet = roomHierarchy.viaMap.get(roomId);
|
||||||
|
const via = viaSet ? [...viaSet] : undefined;
|
||||||
|
join(roomId, false, via);
|
||||||
|
setIsJoining(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const roomAvatarJSX = (
|
||||||
|
<Avatar
|
||||||
|
text={name}
|
||||||
|
bgColor={colorMXID(roomId)}
|
||||||
|
imageSrc={directs.has(roomId) ? imageSrc : null}
|
||||||
|
iconColor="var(--ic-surface-low)"
|
||||||
|
iconSrc={joinRuleToIconSrc(roomInfo.join_rule, isSpace)}
|
||||||
|
size="extra-small"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const roomNameJSX = (
|
||||||
|
<Text>
|
||||||
|
{twemojify(name)}
|
||||||
|
<Text variant="b3" span>{` • ${roomInfo.num_joined_members} members`}</Text>
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
|
||||||
|
const expandBtnJsx = (
|
||||||
|
<IconButton
|
||||||
|
variant={isExpand ? 'primary' : 'surface'}
|
||||||
|
size="extra-small"
|
||||||
|
src={InfoIC}
|
||||||
|
tooltip="Topic"
|
||||||
|
tooltipPlacement="top"
|
||||||
|
onClick={() => setIsExpand(!isExpand)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`space-manage-item${isSpace ? '--space' : ''}`}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{canManage && <Checkbox isActive={isSelected} onToggle={() => onSelect(roomId)} variant="positive" />}
|
||||||
|
<button
|
||||||
|
className="space-manage-item__btn"
|
||||||
|
onClick={isSpace ? () => onSpaceClick(roomId, name) : null}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{roomAvatarJSX}
|
||||||
|
{roomNameJSX}
|
||||||
|
</button>
|
||||||
|
{roomInfo.topic && expandBtnJsx}
|
||||||
|
{
|
||||||
|
isJoined
|
||||||
|
? <Button onClick={handleOpen}>Open</Button>
|
||||||
|
: <Button variant="primary" onClick={handleJoin} disabled={isJoining}>{isJoining ? 'Joining...' : 'Join'}</Button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
{isExpand && roomInfo.topic && <Text variant="b2">{twemojify(roomInfo.topic, undefined, true)}</Text>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SpaceManageItem.propTypes = {
|
||||||
|
parentId: PropTypes.string.isRequired,
|
||||||
|
roomHierarchy: PropTypes.shape({}).isRequired,
|
||||||
|
roomInfo: PropTypes.shape({}).isRequired,
|
||||||
|
onSpaceClick: PropTypes.func.isRequired,
|
||||||
|
requestClose: PropTypes.func.isRequired,
|
||||||
|
isSelected: PropTypes.bool.isRequired,
|
||||||
|
onSelect: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
function SpaceManageFooter({ roomId, selected }) {
|
||||||
|
return (
|
||||||
|
<div className="space-manage__footer">
|
||||||
|
<Text weight="medium">{`${selected.length} item selected`}</Text>
|
||||||
|
<Button variant="danger">Remove</Button>
|
||||||
|
<Button variant="primary">Mark as suggested</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SpaceManageFooter.propTypes = {
|
||||||
|
roomId: PropTypes.string.isRequired,
|
||||||
|
selected: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
function useSpacePath(roomId) {
|
||||||
|
const mx = initMatrix.matrixClient;
|
||||||
|
const room = mx.getRoom(roomId);
|
||||||
|
const [spacePath, setSpacePath] = useState([{ roomId, name: room.name }]);
|
||||||
|
|
||||||
|
const addPathItem = (rId, name) => {
|
||||||
|
const newPath = [...spacePath];
|
||||||
|
const itemIndex = newPath.findIndex((item) => item.roomId === rId);
|
||||||
|
if (itemIndex < 0) {
|
||||||
|
newPath.push({ roomId: rId, name });
|
||||||
|
setSpacePath(newPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newPath.splice(itemIndex + 1);
|
||||||
|
setSpacePath(newPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
return [spacePath, addPathItem];
|
||||||
|
}
|
||||||
|
|
||||||
|
function useUpdateOnJoin(roomId) {
|
||||||
|
const [, forceUpdate] = useForceUpdate();
|
||||||
|
const { roomList } = initMatrix;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleRoomList = () => forceUpdate();
|
||||||
|
|
||||||
|
roomList.on(cons.events.roomList.ROOM_JOINED, handleRoomList);
|
||||||
|
roomList.on(cons.events.roomList.ROOM_LEAVED, handleRoomList);
|
||||||
|
return () => {
|
||||||
|
roomList.removeListener(cons.events.roomList.ROOM_JOINED, handleRoomList);
|
||||||
|
roomList.removeListener(cons.events.roomList.ROOM_LEAVED, handleRoomList);
|
||||||
|
};
|
||||||
|
}, [roomId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SpaceManageContent({ roomId, requestClose }) {
|
||||||
|
const mx = initMatrix.matrixClient;
|
||||||
|
useUpdateOnJoin(roomId);
|
||||||
|
const [roomsHierarchy] = useState(new RoomsHierarchy(mx, 30));
|
||||||
|
const [spacePath, addPathItem] = useSpacePath(roomId);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [selected, setSelected] = useState([]);
|
||||||
|
const mountStore = useStore();
|
||||||
|
|
||||||
|
const currentPath = spacePath[spacePath.length - 1];
|
||||||
|
const currentHierarchy = roomsHierarchy.getHierarchy(currentPath.roomId);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
mountStore.setItem(true);
|
||||||
|
return () => {
|
||||||
|
mountStore.setItem(false);
|
||||||
|
};
|
||||||
|
}, [roomId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelected([]);
|
||||||
|
}, [spacePath]);
|
||||||
|
|
||||||
|
const handleSelected = (selectedRoomId) => {
|
||||||
|
const newSelected = [...selected];
|
||||||
|
const selectedIndex = newSelected.indexOf(selectedRoomId);
|
||||||
|
|
||||||
|
if (selectedIndex > -1) {
|
||||||
|
newSelected.splice(selectedIndex, 1);
|
||||||
|
setSelected(newSelected);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newSelected.push(selectedRoomId);
|
||||||
|
setSelected(newSelected);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadRoomHierarchy = async () => {
|
||||||
|
if (!roomsHierarchy.canLoadMore(currentPath.roomId)) return;
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
await roomsHierarchy.load(currentPath.roomId);
|
||||||
|
if (!mountStore.getItem()) return;
|
||||||
|
setIsLoading(false);
|
||||||
|
} catch {
|
||||||
|
if (!mountStore.getItem()) return;
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!currentHierarchy) loadRoomHierarchy();
|
||||||
|
return (
|
||||||
|
<div className="space-manage__content">
|
||||||
|
{spacePath.length > 1 && (
|
||||||
|
<SpaceManageBreadcrumb path={spacePath} onSelect={addPathItem} />
|
||||||
|
)}
|
||||||
|
<Text variant="b3" weight="bold">Rooms and spaces</Text>
|
||||||
|
<div className="space-manage__content-items">
|
||||||
|
{!isLoading && currentHierarchy?.rooms?.length === 1 && (
|
||||||
|
<Text>
|
||||||
|
Either the space contains private rooms or you need to join space to view it's rooms.
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{currentHierarchy && (currentHierarchy.rooms?.map((roomInfo) => (
|
||||||
|
roomInfo.room_id === currentPath.roomId
|
||||||
|
? null
|
||||||
|
: (
|
||||||
|
<SpaceManageItem
|
||||||
|
key={roomInfo.room_id}
|
||||||
|
isSelected={selected.includes(roomInfo.room_id)}
|
||||||
|
roomHierarchy={currentHierarchy}
|
||||||
|
parentId={currentPath.roomId}
|
||||||
|
roomInfo={roomInfo}
|
||||||
|
onSpaceClick={addPathItem}
|
||||||
|
requestClose={requestClose}
|
||||||
|
onSelect={handleSelected}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)))}
|
||||||
|
{!currentHierarchy && <Text>loading...</Text>}
|
||||||
|
</div>
|
||||||
|
{currentHierarchy?.canLoadMore && !isLoading && (
|
||||||
|
<Button onClick={loadRoomHierarchy}>Load more</Button>
|
||||||
|
)}
|
||||||
|
{isLoading && (
|
||||||
|
<div className="space-manage__content-loading">
|
||||||
|
<Spinner size="small" />
|
||||||
|
<Text>Loading rooms</Text>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{selected.length > 0 && <SpaceManageFooter roomId={roomId} selected={selected} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SpaceManageContent.propTypes = {
|
||||||
|
roomId: PropTypes.string.isRequired,
|
||||||
|
requestClose: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
function useWindowToggle() {
|
||||||
|
const [roomId, setRoomId] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const openSpaceManage = (rId) => {
|
||||||
|
setRoomId(rId);
|
||||||
|
};
|
||||||
|
navigation.on(cons.events.navigation.SPACE_MANAGE_OPENED, openSpaceManage);
|
||||||
|
return () => {
|
||||||
|
navigation.removeListener(cons.events.navigation.SPACE_MANAGE_OPENED, openSpaceManage);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const requestClose = () => setRoomId(null);
|
||||||
|
|
||||||
|
return [roomId, requestClose];
|
||||||
|
}
|
||||||
|
function SpaceManage() {
|
||||||
|
const mx = initMatrix.matrixClient;
|
||||||
|
const [roomId, requestClose] = useWindowToggle();
|
||||||
|
const room = mx.getRoom(roomId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopupWindow
|
||||||
|
isOpen={roomId !== null}
|
||||||
|
className="space-manage"
|
||||||
|
title={(
|
||||||
|
<Text variant="s1" weight="medium" primary>
|
||||||
|
{roomId && twemojify(room.name)}
|
||||||
|
<span style={{ color: 'var(--tc-surface-low)' }}> — manage rooms</span>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip="Close" />}
|
||||||
|
onRequestClose={requestClose}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
roomId
|
||||||
|
? <SpaceManageContent roomId={roomId} requestClose={requestClose} />
|
||||||
|
: <div />
|
||||||
|
}
|
||||||
|
</PopupWindow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SpaceManage;
|
159
src/app/organisms/space-manage/SpaceManage.scss
Normal file
159
src/app/organisms/space-manage/SpaceManage.scss
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
@use '../../partials/text';
|
||||||
|
@use '../../partials/dir';
|
||||||
|
@use '../../partials/flex';
|
||||||
|
|
||||||
|
.space-manage {
|
||||||
|
& .pw__content-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
& .pw__content-container {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 73px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.space-manage__content {
|
||||||
|
margin-bottom: var(--sp-extra-loose);
|
||||||
|
|
||||||
|
& > .text {
|
||||||
|
margin-top: var(--sp-extra-tight);
|
||||||
|
padding: var(--sp-extra-tight) var(--sp-normal);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-items {
|
||||||
|
@include dir.side(padding, var(--sp-extra-tight), 0);
|
||||||
|
& > .text:first-child {
|
||||||
|
padding: var(--sp-extra-tight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
margin: var(--sp-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-loading {
|
||||||
|
padding: var(--sp-loose);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
& .text {
|
||||||
|
margin: 0 var(--sp-normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.space-manage-breadcrumb {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 var(--sp-extra-tight);
|
||||||
|
|
||||||
|
&__wrapper {
|
||||||
|
height: var(--header-height);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 99;
|
||||||
|
background-color: var(--bg-surface);
|
||||||
|
}
|
||||||
|
& > * {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .btn-surface {
|
||||||
|
min-width: 0;
|
||||||
|
padding: var(--sp-extra-tight) 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
box-shadow: none;
|
||||||
|
& p {
|
||||||
|
@extend .cp-txt__ellipsis;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
box-shadow: var(--bs-surface-border) !important;
|
||||||
|
background-color: var(--bg-surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.space-manage-item {
|
||||||
|
margin: var(--sp-ultra-tight) 0;
|
||||||
|
padding: 0 var(--sp-extra-tight);
|
||||||
|
border-radius: var(--bo-radius);
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
min-height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--space {
|
||||||
|
@extend .space-manage-item;
|
||||||
|
& .space-manage-item__btn {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--bg-surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
& .checkbox {
|
||||||
|
@include dir.side(margin, 0, var(--sp-tight));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&__btn {
|
||||||
|
@extend .cp-fx__item-one;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
& .avatar__border--active {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
& .text {
|
||||||
|
@extend .cp-txt__ellipsis;
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0 var(--sp-extra-tight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .ic-btn {
|
||||||
|
padding: 7px;
|
||||||
|
@include dir.side(margin, 0, var(--sp-extra-tight));
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .btn-surface,
|
||||||
|
& .btn-primary {
|
||||||
|
padding: var(--sp-ultra-tight) var(--sp-extra-tight);
|
||||||
|
min-width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .text {
|
||||||
|
padding: 32px;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: var(--sp-normal);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.space-manage__footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--sp-normal);
|
||||||
|
background-color: var(--bg-surface);
|
||||||
|
border-top: 1px solid var(--bg-surface-border);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& > .text {
|
||||||
|
@extend .cp-fx__item-one;
|
||||||
|
padding: 0 var(--sp-tight);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
@include dir.side(margin, var(--sp-normal), 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -100,8 +100,8 @@ function useWindowToggle(setSelectedTab) {
|
||||||
const [window, setWindow] = useState(null);
|
const [window, setWindow] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const openSpaceSettings = (spaceId, tab) => {
|
const openSpaceSettings = (roomId, tab) => {
|
||||||
setWindow({ spaceId, tabText });
|
setWindow({ roomId, tabText });
|
||||||
const tabItem = tabItems.find((item) => item.text === tab);
|
const tabItem = tabItems.find((item) => item.text === tab);
|
||||||
if (tabItem) setSelectedTab(tabItem);
|
if (tabItem) setSelectedTab(tabItem);
|
||||||
};
|
};
|
||||||
|
@ -120,7 +120,7 @@ function SpaceSettings() {
|
||||||
const [selectedTab, setSelectedTab] = useState(tabItems[0]);
|
const [selectedTab, setSelectedTab] = useState(tabItems[0]);
|
||||||
const [window, requestClose] = useWindowToggle(setSelectedTab);
|
const [window, requestClose] = useWindowToggle(setSelectedTab);
|
||||||
const isOpen = window !== null;
|
const isOpen = window !== null;
|
||||||
const roomId = window?.spaceId;
|
const roomId = window?.roomId;
|
||||||
|
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
const room = mx.getRoom(roomId);
|
const room = mx.getRoom(roomId);
|
||||||
|
|
|
@ -23,14 +23,21 @@ export function selectRoom(roomId, eventId) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openSpaceSettings(spaceId, tabText) {
|
export function openSpaceSettings(roomId, tabText) {
|
||||||
appDispatcher.dispatch({
|
appDispatcher.dispatch({
|
||||||
type: cons.actions.navigation.OPEN_SPACE_SETTINGS,
|
type: cons.actions.navigation.OPEN_SPACE_SETTINGS,
|
||||||
spaceId,
|
roomId,
|
||||||
tabText,
|
tabText,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function openSpaceManage(roomId) {
|
||||||
|
appDispatcher.dispatch({
|
||||||
|
type: cons.actions.navigation.OPEN_SPACE_MANAGE,
|
||||||
|
roomId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function toggleRoomSettings(tabText) {
|
export function toggleRoomSettings(tabText) {
|
||||||
appDispatcher.dispatch({
|
appDispatcher.dispatch({
|
||||||
type: cons.actions.navigation.TOGGLE_ROOM_SETTINGS,
|
type: cons.actions.navigation.TOGGLE_ROOM_SETTINGS,
|
||||||
|
|
|
@ -31,6 +31,7 @@ const cons = {
|
||||||
SELECT_SPACE: 'SELECT_SPACE',
|
SELECT_SPACE: 'SELECT_SPACE',
|
||||||
SELECT_ROOM: 'SELECT_ROOM',
|
SELECT_ROOM: 'SELECT_ROOM',
|
||||||
OPEN_SPACE_SETTINGS: 'OPEN_SPACE_SETTINGS',
|
OPEN_SPACE_SETTINGS: 'OPEN_SPACE_SETTINGS',
|
||||||
|
OPEN_SPACE_MANAGE: 'OPEN_SPACE_MANAGE',
|
||||||
TOGGLE_ROOM_SETTINGS: 'TOGGLE_ROOM_SETTINGS',
|
TOGGLE_ROOM_SETTINGS: 'TOGGLE_ROOM_SETTINGS',
|
||||||
OPEN_INVITE_LIST: 'OPEN_INVITE_LIST',
|
OPEN_INVITE_LIST: 'OPEN_INVITE_LIST',
|
||||||
OPEN_PUBLIC_ROOMS: 'OPEN_PUBLIC_ROOMS',
|
OPEN_PUBLIC_ROOMS: 'OPEN_PUBLIC_ROOMS',
|
||||||
|
@ -69,6 +70,7 @@ const cons = {
|
||||||
SPACE_SELECTED: 'SPACE_SELECTED',
|
SPACE_SELECTED: 'SPACE_SELECTED',
|
||||||
ROOM_SELECTED: 'ROOM_SELECTED',
|
ROOM_SELECTED: 'ROOM_SELECTED',
|
||||||
SPACE_SETTINGS_OPENED: 'SPACE_SETTINGS_OPENED',
|
SPACE_SETTINGS_OPENED: 'SPACE_SETTINGS_OPENED',
|
||||||
|
SPACE_MANAGE_OPENED: 'SPACE_MANAGE_OPENED',
|
||||||
ROOM_SETTINGS_TOGGLED: 'ROOM_SETTINGS_TOGGLED',
|
ROOM_SETTINGS_TOGGLED: 'ROOM_SETTINGS_TOGGLED',
|
||||||
INVITE_LIST_OPENED: 'INVITE_LIST_OPENED',
|
INVITE_LIST_OPENED: 'INVITE_LIST_OPENED',
|
||||||
PUBLIC_ROOMS_OPENED: 'PUBLIC_ROOMS_OPENED',
|
PUBLIC_ROOMS_OPENED: 'PUBLIC_ROOMS_OPENED',
|
||||||
|
|
|
@ -90,7 +90,10 @@ class Navigation extends EventEmitter {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[cons.actions.navigation.OPEN_SPACE_SETTINGS]: () => {
|
[cons.actions.navigation.OPEN_SPACE_SETTINGS]: () => {
|
||||||
this.emit(cons.events.navigation.SPACE_SETTINGS_OPENED, action.spaceId, action.tabText);
|
this.emit(cons.events.navigation.SPACE_SETTINGS_OPENED, action.roomId, action.tabText);
|
||||||
|
},
|
||||||
|
[cons.actions.navigation.OPEN_SPACE_MANAGE]: () => {
|
||||||
|
this.emit(cons.events.navigation.SPACE_MANAGE_OPENED, action.roomId);
|
||||||
},
|
},
|
||||||
[cons.actions.navigation.TOGGLE_ROOM_SETTINGS]: () => {
|
[cons.actions.navigation.TOGGLE_ROOM_SETTINGS]: () => {
|
||||||
this.isRoomSettings = !this.isRoomSettings;
|
this.isRoomSettings = !this.isRoomSettings;
|
||||||
|
|
Loading…
Reference in a new issue