diff --git a/package.json b/package.json index 97a9214..b101275 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "react-router": "6", "react-router-dom": "^5.2.0", "react-use-clipboard": "^1.0.7", - "react-use-measure": "^2.1.1" + "react-use-measure": "^2.1.1", + "unique-names-generator": "^4.6.0" }, "devDependencies": { "@babel/core": "^7.16.5", diff --git a/src/ClientContext.jsx b/src/ClientContext.jsx index 20ccd89..91d756a 100644 --- a/src/ClientContext.jsx +++ b/src/ClientContext.jsx @@ -145,29 +145,36 @@ export function ClientProvider({ children }) { [client] ); - const setClient = useCallback((client, session) => { - if (client) { - localStorage.setItem("matrix-auth-store", JSON.stringify(session)); + const setClient = useCallback( + (newClient, session) => { + if (client && client !== newClient) { + client.stopClient(); + } - setState({ - client, - loading: false, - isAuthenticated: true, - isPasswordlessUser: !!session.passwordlessUser, - userName: client.getUserIdLocalpart(), - }); - } else { - localStorage.removeItem("matrix-auth-store"); + if (newClient) { + localStorage.setItem("matrix-auth-store", JSON.stringify(session)); - setState({ - client: undefined, - loading: false, - isAuthenticated: false, - isPasswordlessUser: false, - userName: null, - }); - } - }, []); + setState({ + client: newClient, + loading: false, + isAuthenticated: true, + isPasswordlessUser: !!session.passwordlessUser, + userName: newClient.getUserIdLocalpart(), + }); + } else { + localStorage.removeItem("matrix-auth-store"); + + setState({ + client: undefined, + loading: false, + isAuthenticated: false, + isPasswordlessUser: false, + userName: null, + }); + } + }, + [client] + ); const logout = useCallback(() => { localStorage.removeItem("matrix-auth-store"); @@ -226,6 +233,10 @@ export function ClientProvider({ children }) { ] ); + useEffect(() => { + window.matrixclient = client; + }, [client]); + if (error) { return ; } diff --git a/src/auth/RegisterPage.jsx b/src/auth/RegisterPage.jsx index 70eeaaf..6f08acb 100644 --- a/src/auth/RegisterPage.jsx +++ b/src/auth/RegisterPage.jsx @@ -31,13 +31,7 @@ import { usePageTitle } from "../usePageTitle"; export function RegisterPage() { usePageTitle("Register"); - const { - loading, - client, - changePassword, - isAuthenticated, - isPasswordlessUser, - } = useClient(); + const { loading, isAuthenticated, isPasswordlessUser, client } = useClient(); const confirmPasswordRef = useRef(); const history = useHistory(); const location = useLocation(); @@ -64,11 +58,31 @@ export function RegisterPage() { async function submit() { setRegistering(true); - if (isPasswordlessUser) { - await changePassword(password); - } else { - const recaptchaResponse = await execute(); - await register(userName, password, recaptchaResponse); + let roomIds; + + if (client && isPasswordlessUser) { + const groupCalls = client.groupCallEventHandler.groupCalls.values(); + roomIds = Array.from(groupCalls).map( + (groupCall) => groupCall.room.roomId + ); + } + + const recaptchaResponse = await execute(); + const newClient = await register( + userName, + password, + userName, + recaptchaResponse + ); + + if (roomIds) { + for (const roomId of roomIds) { + try { + await newClient.joinRoom(roomId); + } catch (error) { + console.warn(`Couldn't join room ${roomId}`, error); + } + } } } @@ -86,15 +100,7 @@ export function RegisterPage() { reset(); }); }, - [ - register, - changePassword, - location, - history, - isPasswordlessUser, - reset, - execute, - ] + [register, location, history, isPasswordlessUser, reset, execute, client] ); useEffect(() => { @@ -110,10 +116,10 @@ export function RegisterPage() { }, [password, passwordConfirmation]); useEffect(() => { - if (!loading && isAuthenticated && !isPasswordlessUser) { + if (!loading && isAuthenticated && !isPasswordlessUser && !registering) { history.push("/"); } - }, [history, isAuthenticated, isPasswordlessUser]); + }, [history, isAuthenticated, isPasswordlessUser, registering]); if (loading) { return ; @@ -137,12 +143,6 @@ export function RegisterPage() { autoCapitalize="none" prefix="@" suffix={`:${defaultHomeserverHost}`} - value={ - isAuthenticated && isPasswordlessUser - ? client.getUserIdLocalpart() - : undefined - } - disabled={isAuthenticated && isPasswordlessUser} /> @@ -168,22 +168,20 @@ export function RegisterPage() { ref={confirmPasswordRef} /> - {!isPasswordlessUser && ( - - This site is protected by ReCAPTCHA and the Google{" "} - - Privacy Policy - {" "} - and{" "} - - Terms of Service - {" "} - apply. -
- By clicking "Log in", you agree to our{" "} - Terms and conditions - - )} + + This site is protected by ReCAPTCHA and the Google{" "} + + Privacy Policy + {" "} + and{" "} + + Terms of Service + {" "} + apply. +
+ By clicking "Register", you agree to our{" "} + Terms and conditions + {error && ( {error.message} diff --git a/src/auth/generateRandomName.js b/src/auth/generateRandomName.js new file mode 100644 index 0000000..7e6ff93 --- /dev/null +++ b/src/auth/generateRandomName.js @@ -0,0 +1,137 @@ +import { + uniqueNamesGenerator, + adjectives, + colors, + animals, +} from "unique-names-generator"; + +const elements = [ + "hydrogen", + "helium", + "lithium", + "beryllium", + "boron", + "carbon", + "nitrogen", + "oxygen", + "fluorine", + "neon", + "sodium", + "magnesium", + "aluminum", + "silicon", + "phosphorus", + "sulfur", + "chlorine", + "argon", + "potassium", + "calcium", + "scandium", + "titanium", + "vanadium", + "chromium", + "manganese", + "iron", + "cobalt", + "nickel", + "copper", + "zinc", + "gallium", + "germanium", + "arsenic", + "selenium", + "bromine", + "krypton", + "rubidium", + "strontium", + "yttrium", + "zirconium", + "niobium", + "molybdenum", + "technetium", + "ruthenium", + "rhodium", + "palladium", + "silver", + "cadmium", + "indium", + "tin", + "antimony", + "tellurium", + "iodine", + "xenon", + "cesium", + "barium", + "lanthanum", + "cerium", + "praseodymium", + "neodymium", + "promethium", + "samarium", + "europium", + "gadolinium", + "terbium", + "dysprosium", + "holmium", + "erbium", + "thulium", + "ytterbium", + "lutetium", + "hafnium", + "tantalum", + "wolfram", + "rhenium", + "osmium", + "iridium", + "platinum", + "gold", + "mercury", + "thallium", + "lead", + "bismuth", + "polonium", + "astatine", + "radon", + "francium", + "radium", + "actinium", + "thorium", + "protactinium", + "uranium", + "neptunium", + "plutonium", + "americium", + "curium", + "berkelium", + "californium", + "einsteinium", + "fermium", + "mendelevium", + "nobelium", + "lawrencium", + "rutherfordium", + "dubnium", + "seaborgium", + "bohrium", + "hassium", + "meitnerium", + "darmstadtium", + "roentgenium", + "copernicium", + "nihonium", + "flerovium", + "moscovium", + "livermorium", + "tennessine", + "oganesson", +]; + +export function generateRandomName(config) { + return uniqueNamesGenerator({ + dictionaries: [colors, adjectives, animals, elements], + style: "lowerCase", + length: 3, + separator: "-", + ...config, + }); +} diff --git a/src/auth/useInteractiveRegistration.js b/src/auth/useInteractiveRegistration.js index fc398a1..dbddbb0 100644 --- a/src/auth/useInteractiveRegistration.js +++ b/src/auth/useInteractiveRegistration.js @@ -25,7 +25,13 @@ export function useInteractiveRegistration() { }, []); const register = useCallback( - async (username, password, recaptchaResponse, passwordlessUser) => { + async ( + username, + password, + displayName, + recaptchaResponse, + passwordlessUser + ) => { const interactiveAuth = new InteractiveAuth({ matrixClient: authClientRef.current, busyChanged(loading) { @@ -66,7 +72,7 @@ export function useInteractiveRegistration() { deviceId: device_id, }); - await client.setDisplayName(username); + await client.setDisplayName(displayName); const session = { user_id, device_id, access_token, passwordlessUser }; diff --git a/src/home/UnauthenticatedView.jsx b/src/home/UnauthenticatedView.jsx index fb6fe42..21db6ac 100644 --- a/src/home/UnauthenticatedView.jsx +++ b/src/home/UnauthenticatedView.jsx @@ -14,6 +14,7 @@ import { Body, Caption, Link, Headline } from "../typography/Typography"; import { Form } from "../form/Form"; import styles from "./UnauthenticatedView.module.css"; import commonStyles from "./common.module.css"; +import { generateRandomName } from "../auth/generateRandomName"; export function UnauthenticatedView() { const [loading, setLoading] = useState(false); @@ -26,19 +27,20 @@ export function UnauthenticatedView() { e.preventDefault(); const data = new FormData(e.target); const roomName = data.get("callName"); - const userName = data.get("userName"); + const displayName = data.get("displayName"); async function submit() { setError(undefined); setLoading(true); const recaptchaResponse = await execute(); + const userName = generateRandomName(); const client = await register( userName, randomString(16), + displayName, recaptchaResponse, true ); - const roomIdOrAlias = await createRoom(client, roomName); if (roomIdOrAlias) { @@ -100,10 +102,10 @@ export function UnauthenticatedView() { { e.preventDefault(); const data = new FormData(e.target); - const userName = data.get("userName"); + const displayName = data.get("displayName"); async function submit() { setError(undefined); setLoading(true); const recaptchaResponse = await execute(); - await register(userName, randomString(16), recaptchaResponse, true); + const userName = generateRandomName(); + await register( + userName, + randomString(16), + displayName, + recaptchaResponse, + true + ); } submit().catch((error) => { @@ -58,10 +66,10 @@ export function RoomAuthView() {