From 9e2b0e1754f87a0a0c05a42589583c830610fbcd Mon Sep 17 00:00:00 2001 From: Alexander Harding <2166114+aeharding@users.noreply.github.com> Date: Fri, 9 Aug 2024 19:19:29 -0500 Subject: [PATCH] Improve login instance selector ease of use for newcomers --- .../auth/login/login/PickLoginServer.tsx | 97 ++++++++++++------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/src/features/auth/login/login/PickLoginServer.tsx b/src/features/auth/login/login/PickLoginServer.tsx index 06a036a49b..edb68730d8 100644 --- a/src/features/auth/login/login/PickLoginServer.tsx +++ b/src/features/auth/login/login/PickLoginServer.tsx @@ -1,4 +1,10 @@ -import React, { useEffect, useMemo, useRef, useState } from "react"; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; import { IonBackButton, IonButton, @@ -12,7 +18,7 @@ import { IonTitle, IonToolbar, } from "@ionic/react"; -import { VList } from "virtua"; +import { VList, VListHandle } from "virtua"; import { styled } from "@linaria/react"; import { LOGIN_SERVERS } from "../data/servers"; import { getClient } from "../../../../services/lemmy"; @@ -44,7 +50,7 @@ const StyledIonList = styled(IonList)` export default function PickLoginServer() { const presentToast = useAppToast(); const [search, setSearch] = useState(""); - const [dirty, setDirty] = useState(false); + const [shouldSubmit, setShouldSubmit] = useState(false); const searchHostname = stripProtocol(search.trim()); const instances = useMemo( () => @@ -55,6 +61,8 @@ export default function PickLoginServer() { ); const [loading, setLoading] = useState(false); + const vHandle = useRef(null); + const ref = useRef(null); const searchbarRef = useRef(null); @@ -68,20 +76,22 @@ export default function PickLoginServer() { [searchHostname], ); + useEffect(() => { + vHandle.current?.scrollTo(0); + }, [search]); + useEffect(() => { setTimeout(() => { searchbarRef.current?.setFocus(); }, 300); }, []); - async function submit() { + const submit = useCallback(async () => { if (loading) return; const potentialServer = searchHostname.toLowerCase(); - // Dirty input with candidate if (instances[0] && search !== potentialServer) { - setDirty(false); setSearch(instances[0]); return; } @@ -120,7 +130,14 @@ export default function PickLoginServer() { ?.push(() => ( )); - } + }, [instances, loading, presentToast, search, searchHostname]); + + useEffect(() => { + if (!shouldSubmit) return; + + setShouldSubmit(false); + submit(); + }, [shouldSubmit, submit]); return ( <> @@ -141,7 +158,7 @@ export default function PickLoginServer() { - +
@@ -157,19 +174,26 @@ export default function PickLoginServer() { onKeyDown={(e) => { if (e.key !== "Enter") return; + // Invalid search and there is a candidate for autocomplete + if ( + searchInvalid && + instances[0] && + instances[0] !== searchHostname + ) { + setSearch(instances[0]); + return; + } + // Already selected a server - if (!dirty && search) return submit(); + if (search) return submit(); // Valid with TLD (for autocomplete search) if (!searchInvalid) { - setDirty(false); submit(); return; } - // Dirty input with candidate if (instances[0]) { - setDirty(false); setSearch(instances[0]); return; } @@ -182,33 +206,34 @@ export default function PickLoginServer() { }} value={search} onIonInput={(e) => { - setDirty(true); - setSearch(e.detail.value || ""); + setSearch(e.detail.value ?? ""); }} /> - {dirty && ( - - - {(i) => { - const instance = instances[i]!; - - return ( - { - setSearch(instance); - setDirty(false); - searchbarRef.current?.setFocus(); - }} - > - {instance} - - ); - }} - - - )} + + + {(i) => { + const instance = instances[i]!; + + return ( + { + setSearch(instance); + setShouldSubmit(true); + searchbarRef.current?.setFocus(); + }} + > + {instance} + + ); + }} + +