diff --git a/src/app/organisms/emoji-verification/EmojiVerification.jsx b/src/app/organisms/emoji-verification/EmojiVerification.jsx index 7d67e09..3d63b3c 100644 --- a/src/app/organisms/emoji-verification/EmojiVerification.jsx +++ b/src/app/organisms/emoji-verification/EmojiVerification.jsx @@ -125,7 +125,7 @@ function EmojiVerificationContent({ data, requestClose }) { return (
- Click accept to start the verification process + Click accept to start the verification process.
{ process diff --git a/src/app/organisms/join-alias/JoinAlias.jsx b/src/app/organisms/join-alias/JoinAlias.jsx new file mode 100644 index 0000000..0078d76 --- /dev/null +++ b/src/app/organisms/join-alias/JoinAlias.jsx @@ -0,0 +1,155 @@ +import React, { useState, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import './JoinAlias.scss'; + +import initMatrix from '../../../client/initMatrix'; +import cons from '../../../client/state/cons'; +import navigation from '../../../client/state/navigation'; +import { join } from '../../../client/action/room'; +import { selectRoom, selectSpace } from '../../../client/action/navigation'; + +import Text from '../../atoms/text/Text'; +import IconButton from '../../atoms/button/IconButton'; +import Button from '../../atoms/button/Button'; +import Input from '../../atoms/input/Input'; +import Spinner from '../../atoms/spinner/Spinner'; +import Dialog from '../../molecules/dialog/Dialog'; + +import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; + +import { useStore } from '../../hooks/useStore'; + +const ALIAS_OR_ID_REG = /^[#|!].+:.+\..+$/; + +function JoinAliasContent({ term, requestClose }) { + const [process, setProcess] = useState(false); + const [error, setError] = useState(undefined); + const [lastJoinId, setLastJoinId] = useState(undefined); + + const mx = initMatrix.matrixClient; + const mountStore = useStore(); + + const openRoom = (roomId) => { + const room = mx.getRoom(roomId); + if (!room) return; + if (room.isSpaceRoom()) selectSpace(roomId); + else selectRoom(roomId); + requestClose(); + }; + + useEffect(() => { + const handleJoin = (roomId) => { + if (lastJoinId !== roomId) return; + openRoom(roomId); + }; + initMatrix.roomList.on(cons.events.roomList.ROOM_JOINED, handleJoin); + return () => { + initMatrix.roomList.removeListener(cons.events.roomList.ROOM_JOINED, handleJoin); + }; + }, [lastJoinId]); + + const handleSubmit = async (e) => { + e.preventDefault(); + mountStore.setItem(true); + const alias = e.target.alias.value; + if (alias?.trim() === '') return; + if (alias.match(ALIAS_OR_ID_REG) === null) { + setError('Invalid address.'); + return; + } + setProcess('Looking for address...'); + setError(undefined); + let via; + if (alias.startsWith('#')) { + try { + const aliasData = await mx.resolveRoomAlias(alias); + via = aliasData?.servers || []; + if (mountStore.getItem()) { + setProcess(`Joining ${alias}...`); + } + } catch (err) { + if (!mountStore.getItem()) return; + setProcess(false); + setError(`Unable to find room/space with ${alias}. Either room/space is private or doesn't exist.`); + } + } + try { + const roomId = await join(alias, false, via); + if (!mountStore.getItem()) return; + setLastJoinId(roomId); + openRoom(roomId); + } catch { + if (!mountStore.getItem()) return; + setProcess(false); + setError(`Unable to join ${alias}. Either room/space is private or doesn't exist.`); + } + }; + + return ( +
+ + {error && {error}} +
+ { + process + ? ( + <> + + {process} + + ) + : + } +
+
+ ); +} +JoinAliasContent.defaultProps = { + term: undefined, +}; +JoinAliasContent.propTypes = { + term: PropTypes.string, + requestClose: PropTypes.func.isRequired, +}; + +function useWindowToggle() { + const [data, setData] = useState(null); + + useEffect(() => { + const handleOpen = (term) => { + setData({ term }); + }; + navigation.on(cons.events.navigation.JOIN_ALIAS_OPENED, handleOpen); + return () => { + navigation.removeListener(cons.events.navigation.JOIN_ALIAS_OPENED, handleOpen); + }; + }, []); + + const onRequestClose = () => setData(null); + + return [data, onRequestClose]; +} + +function JoinAlias() { + const [data, requestClose] = useWindowToggle(); + + return ( + Join with address + )} + contentOptions={} + onRequestClose={requestClose} + > + { data ? :
} +
+ ); +} + +export default JoinAlias; diff --git a/src/app/organisms/join-alias/JoinAlias.scss b/src/app/organisms/join-alias/JoinAlias.scss new file mode 100644 index 0000000..b3684b0 --- /dev/null +++ b/src/app/organisms/join-alias/JoinAlias.scss @@ -0,0 +1,20 @@ +@use '../../partials/dir'; + +.join-alias { + padding: var(--sp-normal); + @include dir.side(padding, var(--sp-normal), var(--sp-extra-tight)); + + & > *:not(:first-child) { + margin-top: var(--sp-normal); + } + + &__error { + color: var(--tc-danger-high); + margin-top: var(--sp-extra-tight) !important; + } + + &__btn { + display: flex; + gap: var(--sp-normal); + } +} \ No newline at end of file diff --git a/src/app/organisms/navigation/DrawerHeader.jsx b/src/app/organisms/navigation/DrawerHeader.jsx index 3613c64..ba1882b 100644 --- a/src/app/organisms/navigation/DrawerHeader.jsx +++ b/src/app/organisms/navigation/DrawerHeader.jsx @@ -7,7 +7,7 @@ import { twemojify } from '../../../util/twemojify'; import initMatrix from '../../../client/initMatrix'; import cons from '../../../client/state/cons'; import { - openPublicRooms, openCreateRoom, openSpaceManage, + openPublicRooms, openCreateRoom, openSpaceManage, openJoinAlias, openSpaceAddExisting, openInviteUser, openReusableContextMenu, } from '../../../client/action/navigation'; import { getEventCords } from '../../../util/common'; @@ -60,6 +60,14 @@ export function HomeSpaceOptions({ spaceId, afterOptionSelect }) { Join public room )} + { !spaceId && ( + { afterOptionSelect(); openJoinAlias(); }} + > + Join with address + + )} { spaceId && ( + diff --git a/src/client/action/navigation.js b/src/client/action/navigation.js index 70f270b..1292d56 100644 --- a/src/client/action/navigation.js +++ b/src/client/action/navigation.js @@ -86,6 +86,13 @@ export function openCreateRoom(isSpace = false, parentId = null) { }); } +export function openJoinAlias(term) { + appDispatcher.dispatch({ + type: cons.actions.navigation.OPEN_JOIN_ALIAS, + term, + }); +} + export function openInviteUser(roomId, searchTerm) { appDispatcher.dispatch({ type: cons.actions.navigation.OPEN_INVITE_USER, diff --git a/src/client/state/cons.js b/src/client/state/cons.js index 789ed58..ec8ba26 100644 --- a/src/client/state/cons.js +++ b/src/client/state/cons.js @@ -38,6 +38,7 @@ const cons = { OPEN_INVITE_LIST: 'OPEN_INVITE_LIST', OPEN_PUBLIC_ROOMS: 'OPEN_PUBLIC_ROOMS', OPEN_CREATE_ROOM: 'OPEN_CREATE_ROOM', + OPEN_JOIN_ALIAS: 'OPEN_JOIN_ALIAS', OPEN_INVITE_USER: 'OPEN_INVITE_USER', OPEN_PROFILE_VIEWER: 'OPEN_PROFILE_VIEWER', OPEN_SETTINGS: 'OPEN_SETTINGS', @@ -86,6 +87,7 @@ const cons = { INVITE_LIST_OPENED: 'INVITE_LIST_OPENED', PUBLIC_ROOMS_OPENED: 'PUBLIC_ROOMS_OPENED', CREATE_ROOM_OPENED: 'CREATE_ROOM_OPENED', + JOIN_ALIAS_OPENED: 'JOIN_ALIAS_OPENED', INVITE_USER_OPENED: 'INVITE_USER_OPENED', SETTINGS_OPENED: 'SETTINGS_OPENED', PROFILE_VIEWER_OPENED: 'PROFILE_VIEWER_OPENED', diff --git a/src/client/state/navigation.js b/src/client/state/navigation.js index 7e7ab69..cc1e173 100644 --- a/src/client/state/navigation.js +++ b/src/client/state/navigation.js @@ -122,6 +122,12 @@ class Navigation extends EventEmitter { action.parentId, ); }, + [cons.actions.navigation.OPEN_JOIN_ALIAS]: () => { + this.emit( + cons.events.navigation.JOIN_ALIAS_OPENED, + action.term, + ); + }, [cons.actions.navigation.OPEN_INVITE_USER]: () => { this.emit(cons.events.navigation.INVITE_USER_OPENED, action.roomId, action.searchTerm); },