diff --git a/src/app/atoms/avatar/Avatar.jsx b/src/app/atoms/avatar/Avatar.jsx index 3a2206c..de1b3fa 100644 --- a/src/app/atoms/avatar/Avatar.jsx +++ b/src/app/atoms/avatar/Avatar.jsx @@ -16,7 +16,10 @@ function Avatar({ if (size === 'small') textSize = 'b1'; if (size === 'extra-small') textSize = 'b3'; - useEffect(() => updateImage(imageSrc), [imageSrc]); + useEffect(() => { + updateImage(imageSrc); + return () => updateImage(null); + }, [imageSrc]); return (
diff --git a/src/app/molecules/room-notification/RoomNotification.jsx b/src/app/molecules/room-notification/RoomNotification.jsx index 64e4eee..5d11863 100644 --- a/src/app/molecules/room-notification/RoomNotification.jsx +++ b/src/app/molecules/room-notification/RoomNotification.jsx @@ -129,9 +129,6 @@ function useNotifications(roomId) { function RoomNotification({ roomId }) { const [activeType, setNotification] = useNotifications(roomId); - console.log(roomId) - console.log(activeType) - return (
{ diff --git a/src/app/molecules/room-visibility/RoomVisibility.jsx b/src/app/molecules/room-visibility/RoomVisibility.jsx new file mode 100644 index 0000000..746a07a --- /dev/null +++ b/src/app/molecules/room-visibility/RoomVisibility.jsx @@ -0,0 +1,121 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import PropTypes from 'prop-types'; +import './RoomVisibility.scss'; + +import initMatrix from '../../../client/initMatrix'; + +import Text from '../../atoms/text/Text'; +import RadioButton from '../../atoms/button/RadioButton'; +import { MenuItem } from '../../atoms/context-menu/ContextMenu'; + +import HashIC from '../../../../public/res/ic/outlined/hash.svg'; +import HashLockIC from '../../../../public/res/ic/outlined/hash-lock.svg'; +import HashGlobeIC from '../../../../public/res/ic/outlined/hash-globe.svg'; +import SpaceIC from '../../../../public/res/ic/outlined/space.svg'; +import SpaceLockIC from '../../../../public/res/ic/outlined/space-lock.svg'; +import SpaceGlobeIC from '../../../../public/res/ic/outlined/space-globe.svg'; + +const visibility = { + INVITE: 'invite', + RESTRICTED: 'restricted', + PUBLIC: 'public', +}; + +function setJoinRule(roomId, type) { + const mx = initMatrix.matrixClient; + let allow; + if (type === visibility.RESTRICTED) { + const { currentState } = mx.getRoom(roomId); + const mEvent = currentState.getStateEvents('m.space.parent')[0]; + if (!mEvent) return Promise.resolve(undefined); + + allow = [{ + room_id: mEvent.getStateKey(), + type: 'm.room_membership', + }]; + } + + return mx.sendStateEvent( + roomId, + 'm.room.join_rules', + { + join_rule: type, + allow, + }, + ); +} + +function useVisibility(roomId) { + const mx = initMatrix.matrixClient; + const room = mx.getRoom(roomId); + + const [activeType, setActiveType] = useState(room.getJoinRule()); + useEffect(() => setActiveType(room.getJoinRule()), [roomId]); + + const setNotification = useCallback((item) => { + if (item.type === activeType.type) return; + setActiveType(item.type); + setJoinRule(roomId, item.type); + }, [activeType, roomId]); + + return [activeType, setNotification]; +} + +function RoomVisibility({ roomId }) { + const [activeType, setVisibility] = useVisibility(roomId); + const mx = initMatrix.matrixClient; + const room = mx.getRoom(roomId); + const isSpace = room.isSpaceRoom(); + const { currentState } = room; + + const noSpaceParent = currentState.getStateEvents('m.space.parent').length === 0; + const mCreate = currentState.getStateEvents('m.room.create')[0]?.getContent(); + const roomVersion = Number(mCreate.room_version); + + const myPowerlevel = room.getMember(mx.getUserId())?.powerLevel || 0; + const canChange = room.currentState.hasSufficientPowerLevelFor('state_default', myPowerlevel); + + const items = [{ + iconSrc: isSpace ? SpaceLockIC : HashLockIC, + text: 'Private (invite only)', + type: visibility.INVITE, + unsupported: false, + }, { + iconSrc: isSpace ? SpaceIC : HashIC, + text: roomVersion < 8 ? 'Restricted (unsupported: required room upgrade)' : 'Restricted (space member can join)', + type: visibility.RESTRICTED, + unsupported: roomVersion < 8 || noSpaceParent, + }, { + iconSrc: isSpace ? SpaceGlobeIC : HashGlobeIC, + text: 'Public (anyone can join)', + type: visibility.PUBLIC, + unsupported: false, + }]; + + return ( +
+ { + items.map((item) => ( + setVisibility(item)} + disabled={(!canChange || item.unsupported)} + > + + {item.text} + + + + )) + } +
+ ); +} + +RoomVisibility.propTypes = { + roomId: PropTypes.string.isRequired, +}; + +export default RoomVisibility; diff --git a/src/app/molecules/room-visibility/RoomVisibility.scss b/src/app/molecules/room-visibility/RoomVisibility.scss new file mode 100644 index 0000000..b3ad966 --- /dev/null +++ b/src/app/molecules/room-visibility/RoomVisibility.scss @@ -0,0 +1,19 @@ +@use '../../partials/flex'; +@use '../../partials/dir'; +@use '../../partials/text'; + +.room-visibility { + & .context-menu__item .text { + @extend .cp-fx__item-one; + @extend .cp-fx__row--s-c; + + & span:first-child { + @extend .cp-fx__item-one; + @extend .cp-txt__ellipsis; + } + + & .radio-btn { + @include dir.side(margin, var(--sp-tight), 0); + } + } +} \ No newline at end of file diff --git a/src/app/organisms/room/RoomSettings.jsx b/src/app/organisms/room/RoomSettings.jsx index ec3f7f5..0504f47 100644 --- a/src/app/organisms/room/RoomSettings.jsx +++ b/src/app/organisms/room/RoomSettings.jsx @@ -15,6 +15,7 @@ import Tabs from '../../atoms/tabs/Tabs'; import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu'; import RoomProfile from '../../molecules/room-profile/RoomProfile'; import RoomNotification from '../../molecules/room-notification/RoomNotification'; +import RoomVisibility from '../../molecules/room-visibility/RoomVisibility'; import SettingsIC from '../../../../public/res/ic/outlined/settings.svg'; import SearchIC from '../../../../public/res/ic/outlined/search.svg'; @@ -56,7 +57,7 @@ function GeneralSettings({ roomId }) { return ( <>
- Notification + Notification (Changing this will only affect you)
@@ -69,6 +70,10 @@ function GeneralSettings({ roomId }) { roomActions.leave(roomId)} iconSrc={LeaveArrowIC}>Leave
+
+ Visibility (who can join) + +
); } @@ -111,7 +116,11 @@ function RoomSettings({ roomId }) { - + tab.text === selectedTab.text)} + onSelect={handleTabChange} + />
{selectedTab.text === tabItems[0].text && }
diff --git a/src/client/state/navigation.js b/src/client/state/navigation.js index 8ebf404..fb8873e 100644 --- a/src/client/state/navigation.js +++ b/src/client/state/navigation.js @@ -78,7 +78,7 @@ class Navigation extends EventEmitter { this.removeRecentRoom(prevSelectedRoomId); this.addRecentRoom(prevSelectedRoomId); this.removeRecentRoom(this.selectedRoomId); - if (this.isRoomSettings) { + if (this.isRoomSettings && typeof this.selectedRoomId === 'string') { this.isRoomSettings = !this.isRoomSettings; this.emit(cons.events.navigation.ROOM_SETTINGS_TOGGLED, this.isRoomSettings); }