- changeTab('home')} tooltip="Home" iconSrc={HomeIC} />
- changeTab('dm')} tooltip="People" iconSrc={UserIC} />
+ changeTab('home')} tooltip="Home" iconSrc={HomeIC} />
+ changeTab('dm')} tooltip="People" iconSrc={UserIC} />
openPublicRooms()} tooltip="Public rooms" iconSrc={HashSearchIC} />
diff --git a/src/app/organisms/navigation/SideBar.scss b/src/app/organisms/navigation/SideBar.scss
index 0f4e6773..09641fcc 100644
--- a/src/app/organisms/navigation/SideBar.scss
+++ b/src/app/organisms/navigation/SideBar.scss
@@ -39,11 +39,10 @@
height: 8px;
background: transparent;
- // background-image: linear-gradient(to top, var(--bg-surface-low), transparent);
- // It produce bug in safari
- // To fix it, we have to set the color as a fully transparent version of that exact color. like:
- // background-image: linear-gradient(to top, rgb(255, 255, 255), rgba(255, 255, 255, 0));
- // TODO: fix this bug while implementing spaces
+ background-image: linear-gradient(
+ to top,
+ var(--bg-surface-low),
+ var(--bg-surface-low-transparent));
position: sticky;
bottom: 0;
left: 0;
diff --git a/src/app/templates/auth/Auth.jsx b/src/app/templates/auth/Auth.jsx
index a8837ab4..74c4fd1d 100644
--- a/src/app/templates/auth/Auth.jsx
+++ b/src/app/templates/auth/Auth.jsx
@@ -13,7 +13,7 @@ import Spinner from '../../atoms/spinner/Spinner';
import CinnySvg from '../../../../public/res/svg/cinny.svg';
-// This regex validates historical usernames, which don't satisy today's username requirements.
+// This regex validates historical usernames, which don't satisfy today's username requirements.
// See https://matrix.org/docs/spec/appendices#id13 for more info.
const LOCALPART_LOGIN_REGEX = /.*/;
const LOCALPART_SIGNUP_REGEX = /^[a-z0-9_\-.=/]+$/;
diff --git a/src/client/action/navigation.js b/src/client/action/navigation.js
index cf40b4ae..bf560d87 100644
--- a/src/client/action/navigation.js
+++ b/src/client/action/navigation.js
@@ -8,6 +8,13 @@ function changeTab(tabId) {
});
}
+function selectSpace(roomId) {
+ appDispatcher.dispatch({
+ type: cons.actions.navigation.SELECT_SPACE,
+ roomId,
+ });
+}
+
function selectRoom(roomId) {
appDispatcher.dispatch({
type: cons.actions.navigation.SELECT_ROOM,
@@ -72,6 +79,7 @@ function openReadReceipts(roomId, eventId) {
export {
changeTab,
+ selectSpace,
selectRoom,
togglePeopleDrawer,
openInviteList,
diff --git a/src/client/state/RoomList.js b/src/client/state/RoomList.js
index 428d1040..4e88adc7 100644
--- a/src/client/state/RoomList.js
+++ b/src/client/state/RoomList.js
@@ -7,6 +7,7 @@ class RoomList extends EventEmitter {
super();
this.matrixClient = matrixClient;
this.mDirects = this.getMDirects();
+ this.roomIdToParents = new Map();
this.inviteDirects = new Set();
this.inviteSpaces = new Set();
@@ -24,13 +25,54 @@ class RoomList extends EventEmitter {
appDispatcher.register(this.roomActions.bind(this));
}
+ getSpaceChildren(roomId) {
+ const space = this.matrixClient.getRoom(roomId);
+ const mSpaceChild = space?.currentState.getStateEvents('m.space.child');
+ const children = mSpaceChild?.map((mEvent) => {
+ if (Object.keys(mEvent.event.content).length === 0) return null;
+ return mEvent.event.state_key;
+ });
+ return children?.filter((child) => child !== null);
+ }
+
+ addToRoomIdToParents(roomId, parentRoomId) {
+ if (!this.roomIdToParents.has(roomId)) {
+ this.roomIdToParents.set(roomId, new Set());
+ }
+ const parents = this.roomIdToParents.get(roomId);
+ parents.add(parentRoomId);
+ }
+
+ removeFromRoomIdToParents(roomId, parentRoomId) {
+ if (!this.roomIdToParents.has(roomId)) return;
+ const parents = this.roomIdToParents.get(roomId);
+ parents.delete(parentRoomId);
+ if (parents.size === 0) this.roomIdToParents.delete(roomId);
+ }
+
+ addToSpaces(roomId) {
+ this.spaces.add(roomId);
+ const spaceChildren = this.getSpaceChildren(roomId);
+ spaceChildren?.forEach((childRoomId) => {
+ this.addToRoomIdToParents(childRoomId, roomId);
+ });
+ }
+
+ deleteFromSpaces(roomId) {
+ this.spaces.delete(roomId);
+ const spaceChildren = this.getSpaceChildren(roomId);
+ spaceChildren?.forEach((childRoomId) => {
+ this.removeFromRoomIdToParents(childRoomId, roomId);
+ });
+ }
+
roomActions(action) {
const addRoom = (roomId, isDM) => {
const myRoom = this.matrixClient.getRoom(roomId);
if (myRoom === null) return false;
if (isDM) this.directs.add(roomId);
- else if (myRoom.isSpaceRoom()) this.spaces.add(roomId);
+ else if (myRoom.isSpaceRoom()) this.addToSpaces(roomId);
else this.rooms.add(roomId);
return true;
};
@@ -85,6 +127,7 @@ class RoomList extends EventEmitter {
_populateRooms() {
this.directs.clear();
+ this.roomIdToParents.clear();
this.spaces.clear();
this.rooms.clear();
this.inviteDirects.clear();
@@ -109,7 +152,7 @@ class RoomList extends EventEmitter {
if (room.getMyMembership() !== 'join') return;
if (this.mDirects.has(roomId)) this.directs.add(roomId);
- else if (room.isSpaceRoom()) this.spaces.add(roomId);
+ else if (room.isSpaceRoom()) this.addToSpaces(roomId);
else this.rooms.add(roomId);
});
}
@@ -165,8 +208,16 @@ class RoomList extends EventEmitter {
}
});
- this.matrixClient.on('RoomState.events', (event) => {
- if (event.getType() !== 'm.room.join_rules') return;
+ this.matrixClient.on('RoomState.events', (mEvent) => {
+ if (mEvent.getType() === 'm.space.child') {
+ const { event } = mEvent;
+ const isRoomAdded = Object.keys(event.content).length > 0;
+ if (isRoomAdded) this.addToRoomIdToParents(event.state_key, event.room_id);
+ else this.removeFromRoomIdToParents(event.state_key, event.room_id);
+ this.emit(cons.events.roomList.ROOMLIST_UPDATED);
+ return;
+ }
+ if (mEvent.getType() !== 'm.room.join_rules') return;
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
});
@@ -207,7 +258,7 @@ class RoomList extends EventEmitter {
const procRoomInfo = this.processingRooms.get(roomId);
if (procRoomInfo.isDM) this.directs.add(roomId);
- else if (room.isSpaceRoom()) this.spaces.add(roomId);
+ else if (room.isSpaceRoom()) this.addToSpaces(roomId);
else this.rooms.add(roomId);
if (procRoomInfo.task === 'CREATE') this.emit(cons.events.roomList.ROOM_CREATED, roomId);
@@ -218,7 +269,7 @@ class RoomList extends EventEmitter {
return;
}
if (room.isSpaceRoom()) {
- this.spaces.add(roomId);
+ this.addToSpaces(roomId);
this.emit(cons.events.roomList.ROOM_JOINED, roomId);
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
@@ -269,12 +320,12 @@ class RoomList extends EventEmitter {
}
// when room is not a DM add/remove it from rooms.
if (membership === 'leave' || membership === 'kick' || membership === 'ban') {
- if (room.isSpaceRoom()) this.spaces.delete(roomId);
+ if (room.isSpaceRoom()) this.deleteFromSpaces(roomId);
else this.rooms.delete(roomId);
this.emit(cons.events.roomList.ROOM_LEAVED, roomId);
}
if (membership === 'join') {
- if (room.isSpaceRoom()) this.spaces.add(roomId);
+ if (room.isSpaceRoom()) this.addToSpaces(roomId);
else this.rooms.add(roomId);
this.emit(cons.events.roomList.ROOM_JOINED, roomId);
}
diff --git a/src/client/state/cons.js b/src/client/state/cons.js
index f5e92b0f..e8b8d158 100644
--- a/src/client/state/cons.js
+++ b/src/client/state/cons.js
@@ -9,6 +9,7 @@ const cons = {
actions: {
navigation: {
CHANGE_TAB: 'CHANGE_TAB',
+ SELECT_SPACE: 'SELECT_SPACE',
SELECT_ROOM: 'SELECT_ROOM',
TOGGLE_PEOPLE_DRAWER: 'TOGGLE_PEOPLE_DRAWER',
OPEN_INVITE_LIST: 'OPEN_INVITE_LIST',
@@ -34,6 +35,7 @@ const cons = {
events: {
navigation: {
TAB_CHANGED: 'TAB_CHANGED',
+ SPACE_SELECTED: 'SPACE_SELECTED',
ROOM_SELECTED: 'ROOM_SELECTED',
PEOPLE_DRAWER_TOGGLED: 'PEOPLE_DRAWER_TOGGLED',
INVITE_LIST_OPENED: 'INVITE_LIST_OPENED',
diff --git a/src/client/state/navigation.js b/src/client/state/navigation.js
index 5c108af9..084af25a 100644
--- a/src/client/state/navigation.js
+++ b/src/client/state/navigation.js
@@ -6,29 +6,44 @@ class Navigation extends EventEmitter {
constructor() {
super();
- this.activeTab = 'home';
- this.activeRoomId = null;
+ this.selectedTab = 'home';
+ this.selectedSpaceId = null;
+ this.selectedSpacePath = [];
+ this.selectedRoomId = null;
this.isPeopleDrawerVisible = true;
+
+ // TODO:
+ window.navigation = this;
}
- getActiveTab() {
- return this.activeTab;
- }
-
- getActiveRoomId() {
- return this.activeRoomId;
+ _setSpacePath(roomId) {
+ if (roomId === null) {
+ this.selectedSpacePath = [];
+ return;
+ }
+ if (this.selectedSpacePath.includes(roomId)) {
+ const spIndex = this.selectedSpacePath.indexOf(roomId);
+ this.selectedSpacePath = this.selectedSpacePath.slice(0, spIndex + 1);
+ return;
+ }
+ this.selectedSpacePath.push(roomId);
}
navigate(action) {
const actions = {
[cons.actions.navigation.CHANGE_TAB]: () => {
- this.activeTab = action.tabId;
- this.emit(cons.events.navigation.TAB_CHANGED, this.activeTab);
+ this.selectedTab = action.tabId;
+ this.emit(cons.events.navigation.TAB_CHANGED, this.selectedTab);
+ },
+ [cons.actions.navigation.SELECT_SPACE]: () => {
+ this._setSpacePath(action.roomId);
+ this.selectedSpaceId = action.roomId;
+ this.emit(cons.events.navigation.SPACE_SELECTED, action.roomId);
},
[cons.actions.navigation.SELECT_ROOM]: () => {
- const prevActiveRoomId = this.activeRoomId;
- this.activeRoomId = action.roomId;
- this.emit(cons.events.navigation.ROOM_SELECTED, this.activeRoomId, prevActiveRoomId);
+ const prevSelectedRoomId = this.selectedRoomId;
+ this.selectedRoomId = action.roomId;
+ this.emit(cons.events.navigation.ROOM_SELECTED, this.selectedRoomId, prevSelectedRoomId);
},
[cons.actions.navigation.TOGGLE_PEOPLE_DRAWER]: () => {
this.isPeopleDrawerVisible = !this.isPeopleDrawerVisible;
diff --git a/src/index.scss b/src/index.scss
index a3819a95..678bb65a 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -4,7 +4,9 @@
/* background color | --bg-[background type]: value */
--bg-surface: #FFFFFF;
+ --bg-surface-transparent: #FFFFFF00;
--bg-surface-low: #F6F6F6;
+ --bg-surface-low-transparent: #F6F6F600;
--bg-surface-hover: rgba(0, 0, 0, 3%);
--bg-surface-active: rgba(0, 0, 0, 5%);
--bg-surface-border: rgba(0, 0, 0, 6%);
@@ -155,14 +157,18 @@
.silver-theme {
/* background color | --bg-[background type]: value */
--bg-surface: hsl(0, 0%, 95%);
+ --bg-surface-transparent: hsla(0, 0%, 95%, 0);
--bg-surface-low: hsl(0, 0%, 91%);
+ --bg-surface-low-transparent: hsla(0, 0%, 91%, 0);
}
.dark-theme,
.butter-theme {
/* background color | --bg-[background type]: value */
--bg-surface: hsl(208, 8%, 20%);
+ --bg-surface-transparent: hsla(208, 8%, 20%, 0);
--bg-surface-low: hsl(208, 8%, 16%);
+ --bg-surface-low-transparent: hsla(208, 8%, 16%, 0);
--bg-surface-hover: rgba(255, 255, 255, 3%);
--bg-surface-active: rgba(255, 255, 255, 5%);
--bg-surface-border: rgba(0, 0, 0, 20%);
@@ -206,7 +212,9 @@
.butter-theme {
/* background color | --bg-[background type]: value */
--bg-surface: hsl(64, 6%, 14%);
+ --bg-surface-transparent: hsla(64, 6%, 14%, 0);
--bg-surface-low: hsl(64, 6%, 10%);
+ --bg-surface-low-transparent: hsla(64, 6%, 14%, 0);
/* text color | --tc-[background type]-[priority]: value */