import cons from '../../../client/state/cons';
import colorMXID from '../../../util/colorMXID';
import {
- selectTab, openInviteList, openSearch,
- openSettings, openReusableContextMenu,
+ selectTab, openShortcutSpaces, openInviteList,
+ openSearch, openSettings, openReusableContextMenu,
} from '../../../client/action/navigation';
import { abbreviateNumber, getEventCords } from '../../../util/common';
import HomeIC from '../../../../public/res/ic/outlined/home.svg';
import UserIC from '../../../../public/res/ic/outlined/user.svg';
+import AddPinIC from '../../../../public/res/ic/outlined/add-pin.svg';
import SearchIC from '../../../../public/res/ic/outlined/search.svg';
import InviteIC from '../../../../public/res/ic/outlined/invite.svg';
);
})
}
+ <SidebarAvatar
+ onClick={() => openShortcutSpaces()}
+ tooltip="Pin spaces"
+ iconSrc={AddPinIC}
+ />
</div>
</div>
</ScrollView>
--- /dev/null
+import React, { useState, useEffect } from 'react';
+import './ShortcutSpaces.scss';
+
+import initMatrix from '../../../client/initMatrix';
+import cons from '../../../client/state/cons';
+import navigation from '../../../client/state/navigation';
+import { createSpaceShortcut, deleteSpaceShortcut } from '../../../client/action/accountData';
+import { joinRuleToIconSrc } from '../../../util/matrixUtil';
+
+import Text from '../../atoms/text/Text';
+import Button from '../../atoms/button/Button';
+import IconButton from '../../atoms/button/IconButton';
+import Checkbox from '../../atoms/button/Checkbox';
+import Spinner from '../../atoms/spinner/Spinner';
+import RoomSelector from '../../molecules/room-selector/RoomSelector';
+import Dialog from '../../molecules/dialog/Dialog';
+
+import PinIC from '../../../../public/res/ic/outlined/pin.svg';
+import PinFilledIC from '../../../../public/res/ic/filled/pin.svg';
+import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
+
+import { useSpaceShortcut } from '../../hooks/useSpaceShortcut';
+import { AtoZ } from '../navigation/common';
+
+function ShortcutSpacesContent() {
+ const mx = initMatrix.matrixClient;
+ const { spaces, roomIdToParents } = initMatrix.roomList;
+
+ const [spaceShortcut] = useSpaceShortcut();
+ const spaceWithoutShortcut = [...spaces].filter(
+ (spaceId) => !spaceShortcut.includes(spaceId),
+ ).sort(AtoZ);
+
+ const [process, setProcess] = useState(null);
+ const [selected, setSelected] = useState([]);
+
+ useEffect(() => {
+ if (process !== null) {
+ setProcess(null);
+ setSelected([]);
+ }
+ }, [spaceShortcut]);
+
+ const toggleSelection = (sId) => {
+ if (process !== null) return;
+ const newSelected = [...selected];
+ const selectedIndex = newSelected.indexOf(sId);
+
+ if (selectedIndex > -1) {
+ newSelected.splice(selectedIndex, 1);
+ setSelected(newSelected);
+ return;
+ }
+ newSelected.push(sId);
+ setSelected(newSelected);
+ };
+
+ const handleAdd = () => {
+ setProcess(`Pinning ${selected.length} spaces...`);
+ createSpaceShortcut(selected);
+ };
+
+ const renderSpace = (spaceId, isShortcut) => {
+ const room = mx.getRoom(spaceId);
+ if (!room) return null;
+
+ const parentSet = roomIdToParents.get(spaceId);
+ const parentNames = parentSet
+ ? [...parentSet].map((parentId) => mx.getRoom(parentId).name)
+ : undefined;
+ const parents = parentNames ? parentNames.join(', ') : null;
+
+ const toggleSelected = () => toggleSelection(spaceId);
+ const deleteShortcut = () => deleteSpaceShortcut(spaceId);
+
+ return (
+ <RoomSelector
+ key={spaceId}
+ name={room.name}
+ parentName={parents}
+ roomId={spaceId}
+ imageSrc={null}
+ iconSrc={joinRuleToIconSrc(room.getJoinRule(), true)}
+ isUnread={false}
+ notificationCount={0}
+ isAlert={false}
+ onClick={isShortcut ? deleteShortcut : toggleSelected}
+ options={isShortcut ? (
+ <IconButton
+ src={isShortcut ? PinFilledIC : PinIC}
+ size="small"
+ onClick={deleteShortcut}
+ disabled={process !== null}
+ />
+ ) : (
+ <Checkbox
+ isActive={selected.includes(spaceId)}
+ variant="positive"
+ onToggle={toggleSelected}
+ tabIndex={-1}
+ disabled={process !== null}
+ />
+ )}
+ />
+ );
+ };
+
+ return (
+ <>
+ <Text className="shortcut-spaces__header" variant="b3" weight="bold">Pinned spaces</Text>
+ {spaceShortcut.length === 0 && <Text>No pinned spaces</Text>}
+ {spaceShortcut.map((spaceId) => renderSpace(spaceId, true))}
+ <Text className="shortcut-spaces__header" variant="b3" weight="bold">Unpinned spaces</Text>
+ {spaceWithoutShortcut.length === 0 && <Text>No unpinned spaces</Text>}
+ {spaceWithoutShortcut.map((spaceId) => renderSpace(spaceId, false))}
+ {selected.length !== 0 && (
+ <div className="shortcut-spaces__footer">
+ {process && <Spinner size="small" />}
+ <Text weight="medium">{process || `${selected.length} spaces selected`}</Text>
+ { !process && (
+ <Button onClick={handleAdd} variant="primary">Pin</Button>
+ )}
+ </div>
+ )}
+ </>
+ );
+}
+
+function useVisibilityToggle() {
+ const [isOpen, setIsOpen] = useState(false);
+
+ useEffect(() => {
+ const handleOpen = () => setIsOpen(true);
+ navigation.on(cons.events.navigation.SHORTCUT_SPACES_OPENED, handleOpen);
+ return () => {
+ navigation.removeListener(cons.events.navigation.SHORTCUT_SPACES_OPENED, handleOpen);
+ };
+ }, []);
+
+ const requestClose = () => setIsOpen(false);
+
+ return [isOpen, requestClose];
+}
+
+function ShortcutSpaces() {
+ const [isOpen, requestClose] = useVisibilityToggle();
+
+ return (
+ <Dialog
+ isOpen={isOpen}
+ className="shortcut-spaces"
+ title={(
+ <Text variant="s1" weight="medium" primary>
+ Pin spaces
+ </Text>
+ )}
+ contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip="Close" />}
+ onRequestClose={requestClose}
+ >
+ {
+ isOpen
+ ? <ShortcutSpacesContent />
+ : <div />
+ }
+ </Dialog>
+ );
+}
+
+export default ShortcutSpaces;
OPEN_SPACE_MANAGE: 'OPEN_SPACE_MANAGE',
OPEN_SPACE_ADDEXISTING: 'OPEN_SPACE_ADDEXISTING',
TOGGLE_ROOM_SETTINGS: 'TOGGLE_ROOM_SETTINGS',
+ OPEN_SHORTCUT_SPACES: 'OPEN_SHORTCUT_SPACES',
OPEN_INVITE_LIST: 'OPEN_INVITE_LIST',
OPEN_PUBLIC_ROOMS: 'OPEN_PUBLIC_ROOMS',
OPEN_CREATE_ROOM: 'OPEN_CREATE_ROOM',
SPACE_MANAGE_OPENED: 'SPACE_MANAGE_OPENED',
SPACE_ADDEXISTING_OPENED: 'SPACE_ADDEXISTING_OPENED',
ROOM_SETTINGS_TOGGLED: 'ROOM_SETTINGS_TOGGLED',
+ SHORTCUT_SPACES_OPENED: 'SHORTCUT_SPACES_OPENED',
INVITE_LIST_OPENED: 'INVITE_LIST_OPENED',
PUBLIC_ROOMS_OPENED: 'PUBLIC_ROOMS_OPENED',
CREATE_ROOM_OPENED: 'CREATE_ROOM_OPENED',