diff --git a/src/app/organisms/navigation/Drawer.scss b/src/app/organisms/navigation/Drawer.scss index 48b2855..81e9c8c 100644 --- a/src/app/organisms/navigation/Drawer.scss +++ b/src/app/organisms/navigation/Drawer.scss @@ -10,6 +10,13 @@ 1px solid var(--bg-surface-border), ); + & .header { + padding: var(--sp-extra-tight); + & > .header__title-wrapper { + @include dir.side(margin, var(--sp-ultra-tight), 0); + } + } + &__content-wrapper { @extend .cp-fx__item-one; @extend .cp-fx__column; diff --git a/src/app/organisms/navigation/DrawerHeader.jsx b/src/app/organisms/navigation/DrawerHeader.jsx index 51d2bed..acdd48b 100644 --- a/src/app/organisms/navigation/DrawerHeader.jsx +++ b/src/app/organisms/navigation/DrawerHeader.jsx @@ -1,16 +1,20 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; +import './DrawerHeader.scss'; import { twemojify } from '../../../util/twemojify'; import initMatrix from '../../../client/initMatrix'; import cons from '../../../client/state/cons'; import { - openPublicRooms, openCreateRoom, openInviteUser, + openSpaceSettings, openPublicRooms, openCreateRoom, openInviteUser, } from '../../../client/action/navigation'; import { createSpaceShortcut, deleteSpaceShortcut } from '../../../client/action/room'; +import { blurOnBubbling } from '../../atoms/button/script'; + import Text from '../../atoms/text/Text'; +import RawIcon from '../../atoms/system-icons/RawIcon'; import Header, { TitleWrapper } from '../../atoms/header/Header'; import IconButton from '../../atoms/button/IconButton'; import ContextMenu, { MenuItem, MenuHeader } from '../../atoms/context-menu/ContextMenu'; @@ -18,12 +22,14 @@ import ContextMenu, { MenuItem, MenuHeader } from '../../atoms/context-menu/Cont import PlusIC from '../../../../public/res/ic/outlined/plus.svg'; import HashPlusIC from '../../../../public/res/ic/outlined/hash-plus.svg'; import HashSearchIC from '../../../../public/res/ic/outlined/hash-search.svg'; +import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg'; import PinIC from '../../../../public/res/ic/outlined/pin.svg'; import PinFilledIC from '../../../../public/res/ic/filled/pin.svg'; function DrawerHeader({ selectedTab, spaceId }) { const [, forceUpdate] = useState({}); const mx = initMatrix.matrixClient; + const { spaceShortcut } = initMatrix.roomList; const tabName = selectedTab !== cons.tabs.DIRECTS ? 'Home' : 'Direct messages'; const room = mx.getRoom(spaceId); @@ -31,17 +37,30 @@ function DrawerHeader({ selectedTab, spaceId }) { return (
- - {twemojify(spaceName) || tabName} - + {spaceName ? ( + + ) : ( + + {tabName} + + )} {spaceName && ( { - if (initMatrix.roomList.spaceShortcut.has(spaceId)) deleteSpaceShortcut(spaceId); + if (spaceShortcut.has(spaceId)) deleteSpaceShortcut(spaceId); else createSpaceShortcut(spaceId); forceUpdate({}); }} @@ -72,7 +91,6 @@ function DrawerHeader({ selectedTab, spaceId }) { /> )} - {/* ''} tooltip="Menu" src={VerticalMenuIC} size="normal" /> */}
); } diff --git a/src/app/organisms/navigation/DrawerHeader.scss b/src/app/organisms/navigation/DrawerHeader.scss new file mode 100644 index 0000000..9ed17e4 --- /dev/null +++ b/src/app/organisms/navigation/DrawerHeader.scss @@ -0,0 +1,28 @@ +@use '../../partials/flex'; +@use '../../partials/dir'; + +.drawer-header__btn { + min-width: 0; + @extend .cp-fx__row--s-c; + @include dir.side(margin, 0, auto); + padding: var(--sp-ultra-tight); + border-radius: calc(var(--bo-radius) / 2); + cursor: pointer; + + & .header__title-wrapper { + @include dir.side(margin, 0, var(--sp-extra-tight)); + } + + @media (hover:hover) { + &:hover { + background-color: var(--bg-surface-hover); + box-shadow: var(--bs-surface-outline); + } + } + &:focus, + &:active { + background-color: var(--bg-surface-active); + box-shadow: var(--bs-surface-outline); + outline: none; + } +} \ No newline at end of file diff --git a/src/app/organisms/pw/Windows.jsx b/src/app/organisms/pw/Windows.jsx index 32a0ee1..692f2e1 100644 --- a/src/app/organisms/pw/Windows.jsx +++ b/src/app/organisms/pw/Windows.jsx @@ -8,6 +8,7 @@ import PublicRooms from '../public-rooms/PublicRooms'; import CreateRoom from '../create-room/CreateRoom'; import InviteUser from '../invite-user/InviteUser'; import Settings from '../settings/Settings'; +import SpaceSettings from '../space-settings/SpaceSettings'; function Windows() { const [isInviteList, changeInviteList] = useState(false); @@ -83,6 +84,7 @@ function Windows() { isOpen={settings} onRequestClose={() => changeSettings(false)} /> + ); } diff --git a/src/app/organisms/space-settings/SpaceSettings.jsx b/src/app/organisms/space-settings/SpaceSettings.jsx new file mode 100644 index 0000000..87ef4b5 --- /dev/null +++ b/src/app/organisms/space-settings/SpaceSettings.jsx @@ -0,0 +1,154 @@ +import React, { useState, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import './SpaceSettings.scss'; + +import { twemojify } from '../../../util/twemojify'; + +import initMatrix from '../../../client/initMatrix'; +import cons from '../../../client/state/cons'; +import navigation from '../../../client/state/navigation'; +import { leave, createSpaceShortcut, deleteSpaceShortcut } from '../../../client/action/room'; + +import Text from '../../atoms/text/Text'; +import IconButton from '../../atoms/button/IconButton'; +import Tabs from '../../atoms/tabs/Tabs'; +import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu'; +import PopupWindow from '../../molecules/popup-window/PopupWindow'; +import RoomProfile from '../../molecules/room-profile/RoomProfile'; +import RoomVisibility from '../../molecules/room-visibility/RoomVisibility'; +import RoomAliases from '../../molecules/room-aliases/RoomAliases'; +import RoomPermissions from '../../molecules/room-permissions/RoomPermissions'; + +import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; +import SettingsIC from '../../../../public/res/ic/outlined/settings.svg'; +import ShieldUserIC from '../../../../public/res/ic/outlined/shield-user.svg'; +import LeaveArrowIC from '../../../../public/res/ic/outlined/leave-arrow.svg'; +import PinIC from '../../../../public/res/ic/outlined/pin.svg'; +import PinFilledIC from '../../../../public/res/ic/filled/pin.svg'; + +import { useForceUpdate } from '../../hooks/useForceUpdate'; + +const tabText = { + GENERAL: 'General', + PERMISSIONS: 'Permissions', +}; + +const tabItems = [{ + iconSrc: SettingsIC, + text: tabText.GENERAL, + disabled: false, +}, { + iconSrc: ShieldUserIC, + text: tabText.PERMISSIONS, + disabled: false, +}]; + +function GeneralSettings({ roomId }) { + const isPinned = initMatrix.roomList.spaceShortcut.has(roomId); + const [, forceUpdate] = useForceUpdate(); + + return ( + <> +
+ { + if (isPinned) deleteSpaceShortcut(roomId); + else createSpaceShortcut(roomId); + forceUpdate(); + }} + iconSrc={isPinned ? PinFilledIC : PinIC} + > + {isPinned ? 'Unpin from sidebar' : 'Pin to sidebar'} + + { + if (confirm('Are you really want to leave this space?')) { + leave(roomId); + } + }} + iconSrc={LeaveArrowIC} + > + Leave + +
+
+ Space visibility (who can join) + +
+
+ Space addresses + +
+ + ); +} + +GeneralSettings.propTypes = { + roomId: PropTypes.string.isRequired, +}; + +function useWindowToggle(setSelectedTab) { + const [window, setWindow] = useState(null); + + useEffect(() => { + const openSpaceSettings = (spaceId, tab) => { + setWindow({ spaceId, tabText }); + const tabItem = tabItems.find((item) => item.text === tab); + if (tabItem) setSelectedTab(tabItem); + }; + navigation.on(cons.events.navigation.SPACE_SETTINGS_OPENED, openSpaceSettings); + return () => { + navigation.removeListener(cons.events.navigation.SPACE_SETTINGS_OPENED, openSpaceSettings); + }; + }, []); + + const requestClose = () => setWindow(null); + + return [window, requestClose]; +} + +function SpaceSettings() { + const [selectedTab, setSelectedTab] = useState(tabItems[0]); + const [window, requestClose] = useWindowToggle(setSelectedTab); + const isOpen = window !== null; + const roomId = window?.spaceId; + + const mx = initMatrix.matrixClient; + const room = mx.getRoom(roomId); + + const handleTabChange = (tabItem) => { + setSelectedTab(tabItem); + }; + + return ( + + {twemojify(isOpen ? `${room.name} - space settings` : 'Space settings')} + + )} + contentOptions={} + onRequestClose={requestClose} + > + {isOpen && ( +
+ + tab.text === selectedTab.text)} + onSelect={handleTabChange} + /> +
+ {selectedTab.text === tabText.GENERAL && } + {selectedTab.text === tabText.PERMISSIONS && } +
+
+ )} +
+ ); +} + +export default SpaceSettings; diff --git a/src/app/organisms/space-settings/SpaceSettings.scss b/src/app/organisms/space-settings/SpaceSettings.scss new file mode 100644 index 0000000..d695dac --- /dev/null +++ b/src/app/organisms/space-settings/SpaceSettings.scss @@ -0,0 +1,40 @@ +@use '../../partials/dir.scss'; + +.space-settings { + & .pw { + background-color: var(--bg-surface-low); + } + + & .room-profile { + padding: var(--sp-loose) var(--sp-extra-loose); + } + + & .tabs { + box-shadow: inset 0 -1px 0 var(--bg-surface-border); + + &__content { + padding: 0 var(--sp-normal); + } + } + + &__cards-wrapper { + padding: 0 var(--sp-normal); + @include dir.side(padding, var(--sp-normal), var(--sp-extra-tight)); + } +} + +.space-settings__card { + margin: var(--sp-normal) 0; + background-color: var(--bg-surface); + border-radius: var(--bo-radius); + box-shadow: var(--bs-surface-border); + overflow: hidden; + + & > .context-menu__header:first-child { + margin-top: 2px; + } +} + +.space-settings .room-permissions__card { + @extend .space-settings__card; +} \ No newline at end of file diff --git a/src/client/action/navigation.js b/src/client/action/navigation.js index d62ef1e..23420b8 100644 --- a/src/client/action/navigation.js +++ b/src/client/action/navigation.js @@ -23,6 +23,14 @@ export function selectRoom(roomId, eventId) { }); } +export function openSpaceSettings(spaceId, tabText) { + appDispatcher.dispatch({ + type: cons.actions.navigation.OPEN_SPACE_SETTINGS, + spaceId, + tabText, + }); +} + export function toggleRoomSettings(tabText) { appDispatcher.dispatch({ type: cons.actions.navigation.TOGGLE_ROOM_SETTINGS, diff --git a/src/client/state/cons.js b/src/client/state/cons.js index 9fc72cc..c0f8687 100644 --- a/src/client/state/cons.js +++ b/src/client/state/cons.js @@ -30,6 +30,7 @@ const cons = { SELECT_TAB: 'SELECT_TAB', SELECT_SPACE: 'SELECT_SPACE', SELECT_ROOM: 'SELECT_ROOM', + OPEN_SPACE_SETTINGS: 'OPEN_SPACE_SETTINGS', TOGGLE_ROOM_SETTINGS: 'TOGGLE_ROOM_SETTINGS', OPEN_INVITE_LIST: 'OPEN_INVITE_LIST', OPEN_PUBLIC_ROOMS: 'OPEN_PUBLIC_ROOMS', @@ -66,6 +67,7 @@ const cons = { TAB_SELECTED: 'TAB_SELECTED', SPACE_SELECTED: 'SPACE_SELECTED', ROOM_SELECTED: 'ROOM_SELECTED', + SPACE_SETTINGS_OPENED: 'SPACE_SETTINGS_OPENED', ROOM_SETTINGS_TOGGLED: 'ROOM_SETTINGS_TOGGLED', INVITE_LIST_OPENED: 'INVITE_LIST_OPENED', PUBLIC_ROOMS_OPENED: 'PUBLIC_ROOMS_OPENED', diff --git a/src/client/state/navigation.js b/src/client/state/navigation.js index fc9d41a..ff1d065 100644 --- a/src/client/state/navigation.js +++ b/src/client/state/navigation.js @@ -89,6 +89,9 @@ class Navigation extends EventEmitter { action.eventId, ); }, + [cons.actions.navigation.OPEN_SPACE_SETTINGS]: () => { + this.emit(cons.events.navigation.SPACE_SETTINGS_OPENED, action.spaceId, action.tabText); + }, [cons.actions.navigation.TOGGLE_ROOM_SETTINGS]: () => { this.isRoomSettings = !this.isRoomSettings; this.emit(