Added option to fav spaces (#52)

This commit is contained in:
unknown 2021-09-05 18:56:34 +05:30
parent 2e58757bc9
commit cdf421f0f1
17 changed files with 269 additions and 112 deletions

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<polygon points="12,2 15.1,8.6 22,9.6 17,14.8 18.2,22 12,18.6 5.8,22 7,14.8 2,9.6 8.9,8.6 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 549 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path d="M12,6.7l1.7,3.7l4.1,0.6l-3,3.1l0.7,4.2l-3.5-2l-3.5,2l0.7-4.2l-3-3.1l4.1-0.6L12,6.7 M12,2L8.9,8.6L2,9.6l5,5.1L5.8,22
l6.2-3.4l6.2,3.4L17,14.8l5-5.1l-6.9-1.1L12,2L12,2z"/>
</svg>

After

Width:  |  Height:  |  Size: 624 B

View file

@ -83,7 +83,7 @@
margin: 0 !important; margin: 0 !important;
} }
& .ic-btn-surface { & .ic-btn {
padding: 6px; padding: 6px;
border-radius: calc(var(--bo-radius) / 2); border-radius: calc(var(--bo-radius) / 2);
} }

View file

@ -1,8 +1,10 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import './Drawer.scss'; import './Drawer.scss';
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation'; import navigation from '../../../client/state/navigation';
import { selectTab, selectSpace } from '../../../client/action/navigation';
import ScrollView from '../../atoms/scroll/ScrollView'; import ScrollView from '../../atoms/scroll/ScrollView';
@ -12,34 +14,42 @@ import Home from './Home';
import Directs from './Directs'; import Directs from './Directs';
function Drawer() { function Drawer() {
const [selectedTab, setSelectedTab] = useState('home'); const [selectedTab, setSelectedTab] = useState(navigation.selectedTab);
const [spaceId, setSpaceId] = useState(navigation.selectedSpaceId); const [spaceId, setSpaceId] = useState(navigation.selectedSpaceId);
function onTabChanged(tabId) { function onTabSelected(tabId) {
setSelectedTab(tabId); setSelectedTab(tabId);
} }
function onSpaceSelected(roomId) { function onSpaceSelected(roomId) {
setSpaceId(roomId); setSpaceId(roomId);
} }
function onRoomLeaved(roomId) {
const lRoomIndex = navigation.selectedSpacePath.indexOf(roomId);
if (lRoomIndex === -1) return;
if (lRoomIndex === 0) selectTab(cons.tabs.HOME);
else selectSpace(navigation.selectedSpacePath[lRoomIndex - 1]);
}
useEffect(() => { useEffect(() => {
navigation.on(cons.events.navigation.TAB_CHANGED, onTabChanged); navigation.on(cons.events.navigation.TAB_SELECTED, onTabSelected);
navigation.on(cons.events.navigation.SPACE_SELECTED, onSpaceSelected); navigation.on(cons.events.navigation.SPACE_SELECTED, onSpaceSelected);
initMatrix.roomList.on(cons.events.roomList.ROOM_LEAVED, onRoomLeaved);
return () => { return () => {
navigation.removeListener(cons.events.navigation.TAB_CHANGED, onTabChanged); navigation.removeListener(cons.events.navigation.TAB_SELECTED, onTabSelected);
navigation.removeListener(cons.events.navigation.SPACE_SELECTED, onSpaceSelected); navigation.removeListener(cons.events.navigation.SPACE_SELECTED, onSpaceSelected);
initMatrix.roomList.removeListener(cons.events.roomList.ROOM_LEAVED, onRoomLeaved);
}; };
}, []); }, []);
return ( return (
<div className="drawer"> <div className="drawer">
<DrawerHeader selectedTab={selectedTab} spaceId={spaceId} /> <DrawerHeader selectedTab={selectedTab} spaceId={spaceId} />
<div className="drawer__content-wrapper"> <div className="drawer__content-wrapper">
{selectedTab === 'home' && <DrawerBreadcrumb />} {selectedTab !== cons.tabs.DIRECTS && <DrawerBreadcrumb spaceId={spaceId} />}
<div className="rooms__wrapper"> <div className="rooms__wrapper">
<ScrollView autoHide> <ScrollView autoHide>
<div className="rooms-container"> <div className="rooms-container">
{ {
selectedTab === 'home' selectedTab !== cons.tabs.DIRECTS
? <Home spaceId={spaceId} /> ? <Home spaceId={spaceId} />
: <Directs /> : <Directs />
} }

View file

@ -18,6 +18,10 @@
border-left: 1px solid var(--bg-surface-border); border-left: 1px solid var(--bg-surface-border);
} }
& .header__title-wrapper .text {
font-weight: 500;
}
&__content-wrapper { &__content-wrapper {
@extend .drawer-flexItem; @extend .drawer-flexItem;
@extend .drawer-flexBox; @extend .drawer-flexBox;

View file

@ -1,4 +1,5 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import './DrawerBreadcrumb.scss'; import './DrawerBreadcrumb.scss';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -13,50 +14,47 @@ import ScrollView from '../../atoms/scroll/ScrollView';
import ChevronRightIC from '../../../../public/res/ic/outlined/chevron-right.svg'; import ChevronRightIC from '../../../../public/res/ic/outlined/chevron-right.svg';
function DrawerBreadcrumb() { function DrawerBreadcrumb({ spaceId }) {
const [, forceUpdate] = useState({});
const scrollRef = useRef(null); const scrollRef = useRef(null);
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
const spacePath = navigation.selectedSpacePath; const spacePath = navigation.selectedSpacePath;
function onSpaceSelected() { useEffect(() => {
forceUpdate({});
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (scrollRef?.current === null) return; if (scrollRef?.current === null) return;
scrollRef.current.scrollLeft = scrollRef.current.scrollWidth; scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;
}); });
} }, [spaceId]);
useEffect(() => { if (spacePath.length === 1) return null;
navigation.on(cons.events.navigation.SPACE_SELECTED, onSpaceSelected);
return () => {
navigation.removeListener(cons.events.navigation.SPACE_SELECTED, onSpaceSelected);
};
}, []);
if (spacePath.length === 0) return null;
return ( return (
<div className="breadcrumb__wrapper"> <div className="breadcrumb__wrapper">
<ScrollView ref={scrollRef} horizontal vertical={false} invisible> <ScrollView ref={scrollRef} horizontal vertical={false} invisible>
<div className="breadcrumb"> <div className="breadcrumb">
<Button onClick={() => selectSpace(null)}>
<Text variant="b2">Home</Text>
</Button>
{ {
spacePath.map((spaceId, index) => ( spacePath.map((id, index) => {
<React.Fragment if (index === 0) {
key={spaceId} return (
> <Button key={id} onClick={() => selectSpace(id)}>
<RawIcon size="extra-small" src={ChevronRightIC} /> <Text variant="b2">{id === cons.tabs.HOME ? 'Home' : mx.getRoom(id).name}</Text>
<Button </Button>
className={index === spacePath.length - 1 ? 'breadcrumb__btn--selected' : ''} );
onClick={() => selectSpace(spaceId)} }
return (
<React.Fragment
key={id}
> >
<Text variant="b2">{ mx.getRoom(spaceId).name }</Text> <RawIcon size="extra-small" src={ChevronRightIC} />
</Button> <Button
</React.Fragment> className={index === spacePath.length - 1 ? 'breadcrumb__btn--selected' : ''}
)) onClick={() => selectSpace(id)}
>
<Text variant="b2">{ mx.getRoom(id).name }</Text>
</Button>
</React.Fragment>
);
})
} }
<div style={{ width: 'var(--sp-extra-tight)', height: '100%' }} /> <div style={{ width: 'var(--sp-extra-tight)', height: '100%' }} />
</div> </div>
@ -65,4 +63,12 @@ function DrawerBreadcrumb() {
); );
} }
DrawerBreadcrumb.defaultProps = {
spaceId: null,
};
DrawerBreadcrumb.propTypes = {
spaceId: PropTypes.string,
};
export default DrawerBreadcrumb; export default DrawerBreadcrumb;

View file

@ -1,11 +1,12 @@
import React from 'react'; import React, { useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import { import {
selectSpace, openPublicRooms, openCreateRoom, openInviteUser, openPublicRooms, openCreateRoom, openInviteUser,
} from '../../../client/action/navigation'; } from '../../../client/action/navigation';
import navigation from '../../../client/state/navigation'; import { createSpaceShortcut, deleteSpaceShortcut } from '../../../client/action/room';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import Header, { TitleWrapper } from '../../atoms/header/Header'; import Header, { TitleWrapper } from '../../atoms/header/Header';
@ -15,32 +16,37 @@ import ContextMenu, { MenuItem, MenuHeader } from '../../atoms/context-menu/Cont
import PlusIC from '../../../../public/res/ic/outlined/plus.svg'; import PlusIC from '../../../../public/res/ic/outlined/plus.svg';
import HashPlusIC from '../../../../public/res/ic/outlined/hash-plus.svg'; import HashPlusIC from '../../../../public/res/ic/outlined/hash-plus.svg';
import HashSearchIC from '../../../../public/res/ic/outlined/hash-search.svg'; import HashSearchIC from '../../../../public/res/ic/outlined/hash-search.svg';
import ChevronLeftIC from '../../../../public/res/ic/outlined/chevron-left.svg'; import StarIC from '../../../../public/res/ic/outlined/star.svg';
import FilledStarIC from '../../../../public/res/ic/filled/star.svg';
function DrawerHeader({ selectedTab, spaceId }) { function DrawerHeader({ selectedTab, spaceId }) {
const [, forceUpdate] = useState({});
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
const tabName = selectedTab === 'home' ? 'Home' : 'Direct messages'; const tabName = selectedTab !== cons.tabs.DIRECTS ? 'Home' : 'Direct messages';
const room = mx.getRoom(spaceId); const room = mx.getRoom(spaceId);
const spaceName = selectedTab === 'dm' ? null : (room?.name || null); const spaceName = selectedTab === cons.tabs.DIRECTS ? null : (room?.name || null);
function handleBackClick() {
const spacePath = navigation.selectedSpacePath;
if (spacePath.length === 1) {
selectSpace(null);
return;
}
selectSpace(spacePath[spacePath.length - 2]);
}
return ( return (
<Header> <Header>
<TitleWrapper> <TitleWrapper>
<Text variant="s1">{spaceName || tabName}</Text> <Text variant="s1">{spaceName || tabName}</Text>
</TitleWrapper> </TitleWrapper>
{ spaceName && <IconButton onClick={handleBackClick} tooltip="Back" src={ChevronLeftIC} size="normal" /> } {spaceName && (
{ selectedTab === 'dm' && <IconButton onClick={() => openInviteUser()} tooltip="Start DM" src={PlusIC} size="normal" /> } <IconButton
{ selectSpace !== 'dm' && !spaceName && ( size="extra-small"
variant={initMatrix.roomList.spaceShortcut.has(spaceId) ? 'positive' : 'surface'}
tooltip={initMatrix.roomList.spaceShortcut.has(spaceId) ? 'Remove favourite' : 'Favourite'}
src={initMatrix.roomList.spaceShortcut.has(spaceId) ? FilledStarIC : StarIC}
onClick={() => {
if (initMatrix.roomList.spaceShortcut.has(spaceId)) deleteSpaceShortcut(spaceId);
else createSpaceShortcut(spaceId);
forceUpdate({});
}}
/>
)}
{ selectedTab === cons.tabs.DIRECTS && <IconButton onClick={() => openInviteUser()} tooltip="Start DM" src={PlusIC} size="normal" /> }
{ selectedTab !== cons.tabs.DIRECTS && !spaceName && (
<> <>
<ContextMenu <ContextMenu
content={(hideMenu) => ( content={(hideMenu) => (

View file

@ -5,20 +5,25 @@ import PropTypes from 'prop-types';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import { doesRoomHaveUnread } from '../../../util/matrixUtil'; import { doesRoomHaveUnread } from '../../../util/matrixUtil';
import navigation from '../../../client/state/navigation'; import navigation from '../../../client/state/navigation';
import { createSpaceShortcut, deleteSpaceShortcut } from '../../../client/action/room';
import IconButton from '../../atoms/button/IconButton';
import RoomSelector from '../../molecules/room-selector/RoomSelector'; import RoomSelector from '../../molecules/room-selector/RoomSelector';
import HashIC from '../../../../public/res/ic/outlined/hash.svg'; import HashIC from '../../../../public/res/ic/outlined/hash.svg';
import HashLockIC from '../../../../public/res/ic/outlined/hash-lock.svg'; import HashLockIC from '../../../../public/res/ic/outlined/hash-lock.svg';
import SpaceIC from '../../../../public/res/ic/outlined/space.svg'; import SpaceIC from '../../../../public/res/ic/outlined/space.svg';
import SpaceLockIC from '../../../../public/res/ic/outlined/space-lock.svg'; import SpaceLockIC from '../../../../public/res/ic/outlined/space-lock.svg';
import StarIC from '../../../../public/res/ic/outlined/star.svg';
import FilledStarIC from '../../../../public/res/ic/filled/star.svg';
function Selector({ function Selector({
roomId, isDM, drawerPostie, onClick, roomId, isDM, drawerPostie, onClick,
}) { }) {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId); const room = mx.getRoom(roomId);
const imageSrc = room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null; let imageSrc = room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
if (imageSrc === null) imageSrc = room.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
const [isSelected, setIsSelected] = useState(navigation.selectedRoomId === roomId); const [isSelected, setIsSelected] = useState(navigation.selectedRoomId === roomId);
const [, forceUpdate] = useState({}); const [, forceUpdate] = useState({});
@ -60,6 +65,23 @@ function Selector({
notificationCount={room.getUnreadNotificationCount('total') || 0} notificationCount={room.getUnreadNotificationCount('total') || 0}
isAlert={room.getUnreadNotificationCount('highlight') !== 0} isAlert={room.getUnreadNotificationCount('highlight') !== 0}
onClick={onClick} onClick={onClick}
options={(
!room.isSpaceRoom()
? null
: (
<IconButton
size="extra-small"
variant={initMatrix.roomList.spaceShortcut.has(roomId) ? 'positive' : 'surface'}
tooltip={initMatrix.roomList.spaceShortcut.has(roomId) ? 'Remove favourite' : 'Favourite'}
src={initMatrix.roomList.spaceShortcut.has(roomId) ? FilledStarIC : StarIC}
onClick={() => {
if (initMatrix.roomList.spaceShortcut.has(roomId)) deleteSpaceShortcut(roomId);
else createSpaceShortcut(roomId);
forceUpdate({});
}}
/>
)
)}
/> />
); );
} }

View file

@ -6,7 +6,7 @@ import cons from '../../../client/state/cons';
import colorMXID from '../../../util/colorMXID'; import colorMXID from '../../../util/colorMXID';
import logout from '../../../client/action/logout'; import logout from '../../../client/action/logout';
import { import {
changeTab, openInviteList, openPublicRooms, openSettings, selectTab, openInviteList, openPublicRooms, openSettings,
} from '../../../client/action/navigation'; } from '../../../client/action/navigation';
import navigation from '../../../client/state/navigation'; import navigation from '../../../client/state/navigation';
@ -55,29 +55,37 @@ function ProfileAvatarMenu() {
} }
function SideBar() { function SideBar() {
const totalInviteCount = () => initMatrix.roomList.inviteRooms.size const { roomList } = initMatrix;
+ initMatrix.roomList.inviteSpaces.size const mx = initMatrix.matrixClient;
+ initMatrix.roomList.inviteDirects.size; const totalInviteCount = () => roomList.inviteRooms.size
+ roomList.inviteSpaces.size
+ roomList.inviteDirects.size;
const [totalInvites, updateTotalInvites] = useState(totalInviteCount()); const [totalInvites, updateTotalInvites] = useState(totalInviteCount());
const [selectedTab, setSelectedTab] = useState('home'); const [selectedTab, setSelectedTab] = useState(navigation.selectedTab);
const [, forceUpdate] = useState({});
function onTabChanged(tabId) { function onTabSelected(tabId) {
setSelectedTab(tabId); setSelectedTab(tabId);
} }
function onInviteListChange() { function onInviteListChange() {
updateTotalInvites(totalInviteCount()); updateTotalInvites(totalInviteCount());
} }
function onSpaceShortcutUpdated() {
forceUpdate({});
}
useEffect(() => { useEffect(() => {
navigation.on(cons.events.navigation.TAB_CHANGED, onTabChanged); navigation.on(cons.events.navigation.TAB_SELECTED, onTabSelected);
roomList.on(cons.events.roomList.SPACE_SHORTCUT_UPDATED, onSpaceShortcutUpdated);
initMatrix.roomList.on( initMatrix.roomList.on(
cons.events.roomList.INVITELIST_UPDATED, cons.events.roomList.INVITELIST_UPDATED,
onInviteListChange, onInviteListChange,
); );
return () => { return () => {
navigation.removeListener(cons.events.navigation.TAB_CHANGED, onTabChanged); navigation.removeListener(cons.events.navigation.TAB_SELECTED, onTabSelected);
roomList.removeListener(cons.events.roomList.SPACE_SHORTCUT_UPDATED, onSpaceShortcutUpdated);
initMatrix.roomList.removeListener( initMatrix.roomList.removeListener(
cons.events.roomList.INVITELIST_UPDATED, cons.events.roomList.INVITELIST_UPDATED,
onInviteListChange, onInviteListChange,
@ -91,12 +99,30 @@ function SideBar() {
<ScrollView invisible> <ScrollView invisible>
<div className="scrollable-content"> <div className="scrollable-content">
<div className="featured-container"> <div className="featured-container">
<SidebarAvatar active={selectedTab === 'home'} onClick={() => changeTab('home')} tooltip="Home" iconSrc={HomeIC} /> <SidebarAvatar active={selectedTab === cons.tabs.HOME} onClick={() => selectTab(cons.tabs.HOME)} tooltip="Home" iconSrc={HomeIC} />
<SidebarAvatar active={selectedTab === 'dm'} onClick={() => changeTab('dm')} tooltip="People" iconSrc={UserIC} /> <SidebarAvatar active={selectedTab === cons.tabs.DIRECTS} onClick={() => selectTab(cons.tabs.DIRECTS)} tooltip="People" iconSrc={UserIC} />
<SidebarAvatar onClick={() => openPublicRooms()} tooltip="Public rooms" iconSrc={HashSearchIC} /> <SidebarAvatar onClick={() => openPublicRooms()} tooltip="Public rooms" iconSrc={HashSearchIC} />
</div> </div>
<div className="sidebar-divider" /> <div className="sidebar-divider" />
<div className="space-container" /> <div className="space-container">
{
[...roomList.spaceShortcut].map((shortcut) => {
const sRoomId = shortcut;
const room = mx.getRoom(sRoomId);
return (
<SidebarAvatar
active={selectedTab === sRoomId}
key={sRoomId}
tooltip={room.name}
bgColor={colorMXID(room.roomId)}
imageSrc={room.getAvatarUrl(initMatrix.matrixClient.baseUrl, 42, 42, 'crop') || null}
text={room.name.slice(0, 1)}
onClick={() => selectTab(shortcut)}
/>
);
})
}
</div>
</div> </div>
</ScrollView> </ScrollView>
</div> </div>

View file

@ -44,7 +44,7 @@
var(--bg-surface-low), var(--bg-surface-low),
var(--bg-surface-low-transparent)); var(--bg-surface-low-transparent));
position: sticky; position: sticky;
bottom: 0; bottom: -1px;
left: 0; left: 0;
} }
} }

View file

@ -18,23 +18,15 @@ import PeopleSelector from '../../molecules/people-selector/PeopleSelector';
import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg'; import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
function getPowerLabel(powerLevel) { function getPowerLabel(powerLevel) {
switch (powerLevel) { if (powerLevel > 9000) return 'Goku';
case 100: if (powerLevel > 100) return 'Founder';
return 'Admin'; if (powerLevel === 100) return 'Admin';
case 50: if (powerLevel >= 50) return 'Mod';
return 'Mod'; return null;
default:
return null;
}
} }
function compare(m1, m2) { function AtoZ(m1, m2) {
let aName = m1.name; const aName = m1.name;
let bName = m2.name; const bName = m2.name;
// remove "#" from the room name
// To ignore it in sorting
aName = aName.replaceAll('#', '');
bName = bName.replaceAll('#', '');
if (aName.toLowerCase() < bName.toLowerCase()) { if (aName.toLowerCase() < bName.toLowerCase()) {
return -1; return -1;
@ -45,25 +37,18 @@ function compare(m1, m2) {
return 0; return 0;
} }
function sortByPowerLevel(m1, m2) { function sortByPowerLevel(m1, m2) {
let pl1 = String(m1.powerLevel); const pl1 = m1.powerLevel;
let pl2 = String(m2.powerLevel); const pl2 = m2.powerLevel;
if (pl1 === '100') pl1 = '90.9'; if (pl1 > pl2) return -1;
if (pl2 === '100') pl2 = '90.9'; if (pl1 < pl2) return 1;
if (pl1.toLowerCase() > pl2.toLowerCase()) {
return -1;
}
if (pl1.toLowerCase() < pl2.toLowerCase()) {
return 1;
}
return 0; return 0;
} }
function PeopleDrawer({ roomId }) { function PeopleDrawer({ roomId }) {
const PER_PAGE_MEMBER = 50; const PER_PAGE_MEMBER = 50;
const room = initMatrix.matrixClient.getRoom(roomId); const room = initMatrix.matrixClient.getRoom(roomId);
const totalMemberList = room.getJoinedMembers().sort(compare).sort(sortByPowerLevel); const totalMemberList = room.getJoinedMembers().sort(AtoZ).sort(sortByPowerLevel);
const [memberList, updateMemberList] = useState([]); const [memberList, updateMemberList] = useState([]);
let isRoomChanged = false; let isRoomChanged = false;
@ -75,7 +60,7 @@ function PeopleDrawer({ roomId }) {
updateMemberList(totalMemberList.slice(0, PER_PAGE_MEMBER)); updateMemberList(totalMemberList.slice(0, PER_PAGE_MEMBER));
room.loadMembersIfNeeded().then(() => { room.loadMembersIfNeeded().then(() => {
if (isRoomChanged) return; if (isRoomChanged) return;
const newTotalMemberList = room.getJoinedMembers().sort(compare).sort(sortByPowerLevel); const newTotalMemberList = room.getJoinedMembers().sort(AtoZ).sort(sortByPowerLevel);
updateMemberList(newTotalMemberList.slice(0, PER_PAGE_MEMBER)); updateMemberList(newTotalMemberList.slice(0, PER_PAGE_MEMBER));
}); });

View file

@ -10,6 +10,7 @@ import cons from '../../../client/state/cons';
import { toggleMarkdown } from '../../../client/action/settings'; import { toggleMarkdown } from '../../../client/action/settings';
import * as roomActions from '../../../client/action/room'; import * as roomActions from '../../../client/action/room';
import { import {
selectTab,
selectRoom, selectRoom,
openCreateRoom, openCreateRoom,
openPublicRooms, openPublicRooms,
@ -357,7 +358,8 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
} }
function fireCmd(myCmd) { function fireCmd(myCmd) {
if (myCmd.prefix.match(/^>[*#@]$/)) { if (myCmd.prefix.match(/^>[*#@]$/)) {
selectRoom(myCmd.result.roomId); if (cmd.prefix === '>*') selectTab(myCmd.result.roomId);
else selectRoom(myCmd.result.roomId);
viewEvent.emit('cmd_fired'); viewEvent.emit('cmd_fired');
} }
if (myCmd.prefix === '/') { if (myCmd.prefix === '/') {

View file

@ -1,9 +1,9 @@
import appDispatcher from '../dispatcher'; import appDispatcher from '../dispatcher';
import cons from '../state/cons'; import cons from '../state/cons';
function changeTab(tabId) { function selectTab(tabId) {
appDispatcher.dispatch({ appDispatcher.dispatch({
type: cons.actions.navigation.CHANGE_TAB, type: cons.actions.navigation.SELECT_TAB,
tabId, tabId,
}); });
} }
@ -78,7 +78,7 @@ function openReadReceipts(roomId, eventId) {
} }
export { export {
changeTab, selectTab,
selectSpace, selectSpace,
selectRoom, selectRoom,
togglePeopleDrawer, togglePeopleDrawer,

View file

@ -190,7 +190,22 @@ async function invite(roomId, userId) {
} }
} }
function createSpaceShortcut(roomId) {
appDispatcher.dispatch({
type: cons.actions.room.CREATE_SPACE_SHORTCUT,
roomId,
});
}
function deleteSpaceShortcut(roomId) {
appDispatcher.dispatch({
type: cons.actions.room.DELETE_SPACE_SHORTCUT,
roomId,
});
}
export { export {
join, leave, join, leave,
create, invite, create, invite,
createSpaceShortcut, deleteSpaceShortcut,
}; };

View file

@ -9,6 +9,8 @@ class RoomList extends EventEmitter {
this.mDirects = this.getMDirects(); this.mDirects = this.getMDirects();
this.roomIdToParents = new Map(); this.roomIdToParents = new Map();
this.spaceShortcut = new Set();
this.inviteDirects = new Set(); this.inviteDirects = new Set();
this.inviteSpaces = new Set(); this.inviteSpaces = new Set();
this.inviteRooms = new Set(); this.inviteRooms = new Set();
@ -20,11 +22,18 @@ class RoomList extends EventEmitter {
this.processingRooms = new Map(); this.processingRooms = new Map();
this._populateRooms(); this._populateRooms();
this._populateSpaceShortcut();
this._listenEvents(); this._listenEvents();
appDispatcher.register(this.roomActions.bind(this)); appDispatcher.register(this.roomActions.bind(this));
} }
_updateSpaceShortcutData(shortcutList) {
const spaceContent = this.matrixClient.getAccountData(cons['in.cinny.spaces'])?.getContent() || {};
spaceContent.shortcut = shortcutList;
this.matrixClient.setAccountData(cons['in.cinny.spaces'], spaceContent);
}
getSpaceChildren(roomId) { getSpaceChildren(roomId) {
const space = this.matrixClient.getRoom(roomId); const space = this.matrixClient.getRoom(roomId);
const mSpaceChild = space?.currentState.getStateEvents('m.space.child'); const mSpaceChild = space?.currentState.getStateEvents('m.space.child');
@ -64,6 +73,12 @@ class RoomList extends EventEmitter {
spaceChildren?.forEach((childRoomId) => { spaceChildren?.forEach((childRoomId) => {
this.removeFromRoomIdToParents(childRoomId, roomId); this.removeFromRoomIdToParents(childRoomId, roomId);
}); });
if (this.spaceShortcut.has(roomId)) {
// if delete space has shortcut remove it.
this.spaceShortcut.delete(roomId);
this._updateSpaceShortcutData([...this.spaceShortcut]);
}
} }
roomActions(action) { roomActions(action) {
@ -106,6 +121,18 @@ class RoomList extends EventEmitter {
}); });
} }
}, },
[cons.actions.room.CREATE_SPACE_SHORTCUT]: () => {
if (this.spaceShortcut.has(action.roomId)) return;
this.spaceShortcut.add(action.roomId);
this._updateSpaceShortcutData([...this.spaceShortcut]);
this.emit(cons.events.roomList.SPACE_SHORTCUT_UPDATED, action.roomId);
},
[cons.actions.room.DELETE_SPACE_SHORTCUT]: () => {
if (!this.spaceShortcut.has(action.roomId)) return;
this.spaceShortcut.delete(action.roomId);
this._updateSpaceShortcutData([...this.spaceShortcut]);
this.emit(cons.events.roomList.SPACE_SHORTCUT_UPDATED, action.roomId);
},
}; };
actions[action.type]?.(); actions[action.type]?.();
} }
@ -125,6 +152,21 @@ class RoomList extends EventEmitter {
return mDirectsId; return mDirectsId;
} }
_populateSpaceShortcut() {
this.spaceShortcut.clear();
const spacesContent = this.matrixClient.getAccountData(cons['in.cinny.spaces'])?.getContent();
if (spacesContent && Array.isArray(spacesContent?.shortcut)) {
spacesContent.shortcut.forEach((shortcut) => {
if (this.spaces.has(shortcut)) this.spaceShortcut.add(shortcut);
});
if (spacesContent.shortcut.length !== this.spaceShortcut.size) {
// update shortcut list from account data if shortcut space doesn't exist.
this._updateSpaceShortcutData([...this.spaceShortcut]);
}
}
}
_populateRooms() { _populateRooms() {
this.directs.clear(); this.directs.clear();
this.roomIdToParents.clear(); this.roomIdToParents.clear();
@ -166,6 +208,12 @@ class RoomList extends EventEmitter {
_listenEvents() { _listenEvents() {
// Update roomList when m.direct changes // Update roomList when m.direct changes
this.matrixClient.on('accountData', (event) => { this.matrixClient.on('accountData', (event) => {
if (event.getType() === cons['in.cinny.spaces']) {
this._populateSpaceShortcut();
this.emit(cons.events.roomList.SPACE_SHORTCUT_UPDATED);
return;
}
if (event.getType() !== 'm.direct') return; if (event.getType() !== 'm.direct') return;
const latestMDirects = this.getMDirects(); const latestMDirects = this.getMDirects();

View file

@ -6,9 +6,14 @@ const cons = {
BASE_URL: 'cinny_hs_base_url', BASE_URL: 'cinny_hs_base_url',
}, },
DEVICE_DISPLAY_NAME: 'Cinny Web', DEVICE_DISPLAY_NAME: 'Cinny Web',
'in.cinny.spaces': 'in.cinny.spaces',
tabs: {
HOME: 'home',
DIRECTS: 'dm',
},
actions: { actions: {
navigation: { navigation: {
CHANGE_TAB: 'CHANGE_TAB', SELECT_TAB: 'SELECT_TAB',
SELECT_SPACE: 'SELECT_SPACE', SELECT_SPACE: 'SELECT_SPACE',
SELECT_ROOM: 'SELECT_ROOM', SELECT_ROOM: 'SELECT_ROOM',
TOGGLE_PEOPLE_DRAWER: 'TOGGLE_PEOPLE_DRAWER', TOGGLE_PEOPLE_DRAWER: 'TOGGLE_PEOPLE_DRAWER',
@ -24,6 +29,8 @@ const cons = {
JOIN: 'JOIN', JOIN: 'JOIN',
LEAVE: 'LEAVE', LEAVE: 'LEAVE',
CREATE: 'CREATE', CREATE: 'CREATE',
CREATE_SPACE_SHORTCUT: 'CREATE_SPACE_SHORTCUT',
DELETE_SPACE_SHORTCUT: 'DELETE_SPACE_SHORTCUT',
error: { error: {
CREATE: 'ERROR_CREATE', CREATE: 'ERROR_CREATE',
}, },
@ -34,7 +41,7 @@ const cons = {
}, },
events: { events: {
navigation: { navigation: {
TAB_CHANGED: 'TAB_CHANGED', TAB_SELECTED: 'TAB_SELECTED',
SPACE_SELECTED: 'SPACE_SELECTED', SPACE_SELECTED: 'SPACE_SELECTED',
ROOM_SELECTED: 'ROOM_SELECTED', ROOM_SELECTED: 'ROOM_SELECTED',
PEOPLE_DRAWER_TOGGLED: 'PEOPLE_DRAWER_TOGGLED', PEOPLE_DRAWER_TOGGLED: 'PEOPLE_DRAWER_TOGGLED',
@ -54,6 +61,7 @@ const cons = {
ROOM_CREATED: 'ROOM_CREATED', ROOM_CREATED: 'ROOM_CREATED',
MY_RECEIPT_ARRIVED: 'MY_RECEIPT_ARRIVED', MY_RECEIPT_ARRIVED: 'MY_RECEIPT_ARRIVED',
EVENT_ARRIVED: 'EVENT_ARRIVED', EVENT_ARRIVED: 'EVENT_ARRIVED',
SPACE_SHORTCUT_UPDATED: 'SPACE_SHORTCUT_UPDATED',
}, },
roomTimeline: { roomTimeline: {
EVENT: 'EVENT', EVENT: 'EVENT',

View file

@ -6,19 +6,17 @@ class Navigation extends EventEmitter {
constructor() { constructor() {
super(); super();
this.selectedTab = 'home'; this.selectedTab = cons.tabs.HOME;
this.selectedSpaceId = null; this.selectedSpaceId = null;
this.selectedSpacePath = []; this.selectedSpacePath = [cons.tabs.HOME];
this.selectedRoomId = null; this.selectedRoomId = null;
this.isPeopleDrawerVisible = true; this.isPeopleDrawerVisible = true;
// TODO:
window.navigation = this;
} }
_setSpacePath(roomId) { _setSpacePath(roomId) {
if (roomId === null) { if (roomId === null || roomId === cons.tabs.HOME) {
this.selectedSpacePath = []; this.selectedSpacePath = [cons.tabs.HOME];
return; return;
} }
if (this.selectedSpacePath.includes(roomId)) { if (this.selectedSpacePath.includes(roomId)) {
@ -31,14 +29,24 @@ class Navigation extends EventEmitter {
navigate(action) { navigate(action) {
const actions = { const actions = {
[cons.actions.navigation.CHANGE_TAB]: () => { [cons.actions.navigation.SELECT_TAB]: () => {
this.selectedTab = action.tabId; this.selectedTab = action.tabId;
this.emit(cons.events.navigation.TAB_CHANGED, this.selectedTab); if (this.selectedTab !== cons.tabs.DIRECTS) {
if (this.selectedTab === cons.tabs.HOME) {
this.selectedSpacePath = [cons.tabs.HOME];
this.selectedSpaceId = null;
} else {
this.selectedSpacePath = [this.selectedTab];
this.selectedSpaceId = this.selectedTab;
}
this.emit(cons.events.navigation.SPACE_SELECTED, this.selectedSpaceId);
} else this.selectedSpaceId = null;
this.emit(cons.events.navigation.TAB_SELECTED, this.selectedTab);
}, },
[cons.actions.navigation.SELECT_SPACE]: () => { [cons.actions.navigation.SELECT_SPACE]: () => {
this._setSpacePath(action.roomId); this._setSpacePath(action.roomId);
this.selectedSpaceId = action.roomId; this.selectedSpaceId = action.roomId;
this.emit(cons.events.navigation.SPACE_SELECTED, action.roomId); this.emit(cons.events.navigation.SPACE_SELECTED, this.selectedSpaceId);
}, },
[cons.actions.navigation.SELECT_ROOM]: () => { [cons.actions.navigation.SELECT_ROOM]: () => {
const prevSelectedRoomId = this.selectedRoomId; const prevSelectedRoomId = this.selectedRoomId;