Skip to content

Commit 8fe8f9a

Browse files
fix(mobile): defer filesystem navigation (pingdotgg#4799)
Co-authored-by: codex <codex@users.noreply.github.com>
1 parent 936593c commit 8fe8f9a

1 file changed

Lines changed: 86 additions & 25 deletions

File tree

apps/mobile/src/features/projects/AddProjectScreen.tsx

Lines changed: 86 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
type AddProjectRemoteSource,
1212
} from "@t3tools/client-runtime/operations/projects";
1313
import {
14+
canPreloadBrowsePath,
15+
createBrowseNavigationCoordinator,
1416
filterFilesystemBrowseEntries,
1517
getFilesystemBrowsePath,
1618
} from "@t3tools/client-runtime/state/filesystem";
@@ -43,7 +45,10 @@ import { useThemeColor } from "../../lib/useThemeColor";
4345
import { uuidv4 } from "../../lib/uuid";
4446
import { useAtomCommand } from "../../state/use-atom-command";
4547
import { useAtomQueryRunner } from "../../state/use-atom-query-runner";
46-
import { useSavedRemoteConnections } from "../../state/use-remote-environment-registry";
48+
import {
49+
useRemoteEnvironmentRuntime,
50+
useSavedRemoteConnections,
51+
} from "../../state/use-remote-environment-registry";
4752

4853
interface EnvironmentOption {
4954
readonly environmentId: EnvironmentId;
@@ -221,6 +226,63 @@ function ProjectPathInput(props: {
221226
);
222227
}
223228

229+
function useBrowsePathInput(environment: EnvironmentOption | null) {
230+
const [pathInput, commitPathInput] = useState(() =>
231+
getAddProjectInitialQuery(environment?.baseDirectory),
232+
);
233+
const environmentRuntime = useRemoteEnvironmentRuntime(environment?.environmentId ?? null);
234+
const loadBrowsePath = useAtomQueryRunner(filesystemEnvironment.browse, {
235+
reportFailure: false,
236+
reportDefect: false,
237+
});
238+
const [browseNavigation] = useState(createBrowseNavigationCoordinator);
239+
const [isBrowseNavigating, setIsBrowseNavigating] = useState(false);
240+
const setPathInput = useCallback(
241+
(path: string) => {
242+
browseNavigation.invalidate();
243+
setIsBrowseNavigating(false);
244+
commitPathInput(path);
245+
},
246+
[browseNavigation],
247+
);
248+
const navigateToBrowsePath = useCallback(
249+
async (path: string) => {
250+
setIsBrowseNavigating(true);
251+
const committed = await browseNavigation.run(
252+
async () => {
253+
if (environment && canPreloadBrowsePath(environmentRuntime?.connectionState)) {
254+
await loadBrowsePath({
255+
environmentId: environment.environmentId,
256+
input: { partialPath: path },
257+
});
258+
}
259+
},
260+
() => commitPathInput(path),
261+
);
262+
if (committed) {
263+
setIsBrowseNavigating(false);
264+
}
265+
return committed;
266+
},
267+
[browseNavigation, environment, environmentRuntime?.connectionState, loadBrowsePath],
268+
);
269+
270+
useEffect(() => {
271+
if (environment) {
272+
setPathInput(getAddProjectInitialQuery(environment.baseDirectory));
273+
}
274+
}, [environment, setPathInput]);
275+
276+
useEffect(
277+
() => () => {
278+
browseNavigation.invalidate();
279+
},
280+
[browseNavigation],
281+
);
282+
283+
return { isBrowseNavigating, pathInput, setPathInput, navigateToBrowsePath };
284+
}
285+
224286
function useEnvironmentOptions(): ReadonlyArray<EnvironmentOption> {
225287
const serverConfigByEnvironmentId = useServerConfigs();
226288
const { savedConnectionsById } = useSavedRemoteConnections();
@@ -585,6 +647,7 @@ function FolderBrowser(props: {
585647
readonly environment: EnvironmentOption;
586648
readonly pathInput: string;
587649
readonly setPathInput: (path: string) => void;
650+
readonly navigateToBrowsePath: (path: string) => Promise<boolean>;
588651
}) {
589652
const accentColor = useThemeColor("--color-icon-muted");
590653
const browsePath = useMemo(
@@ -633,7 +696,7 @@ function FolderBrowser(props: {
633696
right={null}
634697
onPress={() => {
635698
if (browsePath.parentPath) {
636-
props.setPathInput(browsePath.parentPath);
699+
void props.navigateToBrowsePath(browsePath.parentPath);
637700
}
638701
}}
639702
/>
@@ -650,7 +713,7 @@ function FolderBrowser(props: {
650713
browsePath.directoryPath.length > 0
651714
? appendBrowsePathSegment(browsePath.directoryPath, entry.name)
652715
: ensureBrowseDirectoryPath(entry.fullPath);
653-
props.setPathInput(nextPath);
716+
void props.navigateToBrowsePath(nextPath);
654717
}}
655718
/>
656719
))}
@@ -662,19 +725,13 @@ function FolderBrowser(props: {
662725
export function AddProjectLocalFolderScreen(props: { readonly environmentId?: string | string[] }) {
663726
const environment = useEnvironmentFromParam(props.environmentId);
664727
const createProject = useCreateProject(environment);
665-
const [pathInput, setPathInput] = useState(() =>
666-
getAddProjectInitialQuery(environment?.baseDirectory),
667-
);
728+
const { isBrowseNavigating, navigateToBrowsePath, pathInput, setPathInput } =
729+
useBrowsePathInput(environment);
668730
const [isSubmitting, setIsSubmitting] = useState(false);
669731
const [error, setError] = useState<string | null>(null);
670732

671-
useEffect(() => {
672-
if (!environment) return;
673-
setPathInput(getAddProjectInitialQuery(environment.baseDirectory));
674-
}, [environment]);
675-
676733
const submitPath = useCallback(async () => {
677-
if (!environment || isSubmitting) return;
734+
if (!environment || isBrowseNavigating || isSubmitting) return;
678735
setError(null);
679736
const resolved = resolveAddProjectPath({
680737
rawPath: pathInput,
@@ -692,7 +749,7 @@ export function AddProjectLocalFolderScreen(props: { readonly environmentId?: st
692749
setError(errorMessage(Cause.squash(result.cause)));
693750
}
694751
setIsSubmitting(false);
695-
}, [createProject, environment, isSubmitting, pathInput]);
752+
}, [createProject, environment, isBrowseNavigating, isSubmitting, pathInput]);
696753

697754
return (
698755
<AddProjectShell>
@@ -706,12 +763,13 @@ export function AddProjectLocalFolderScreen(props: { readonly environmentId?: st
706763
/>
707764
<PrimaryActionButton
708765
label="Add project"
709-
disabled={isSubmitting}
766+
disabled={isBrowseNavigating || isSubmitting}
710767
onPress={() => void submitPath()}
711768
loading={isSubmitting}
712769
/>
713770
<FolderBrowser
714771
environment={environment}
772+
navigateToBrowsePath={navigateToBrowsePath}
715773
pathInput={pathInput}
716774
setPathInput={setPathInput}
717775
/>
@@ -735,19 +793,13 @@ export function AddProjectDestinationScreen(props: {
735793
const createProject = useCreateProject(environment);
736794
const remoteUrl = stringParam(props.remoteUrl);
737795
const repositoryTitle = stringParam(props.repositoryTitle);
738-
const [pathInput, setPathInput] = useState(() =>
739-
getAddProjectInitialQuery(environment?.baseDirectory),
740-
);
796+
const { isBrowseNavigating, navigateToBrowsePath, pathInput, setPathInput } =
797+
useBrowsePathInput(environment);
741798
const [isSubmitting, setIsSubmitting] = useState(false);
742799
const [error, setError] = useState<string | null>(null);
743800

744-
useEffect(() => {
745-
if (!environment) return;
746-
setPathInput(getAddProjectInitialQuery(environment.baseDirectory));
747-
}, [environment]);
748-
749801
const submitPath = useCallback(async () => {
750-
if (!environment || !remoteUrl || isSubmitting) return;
802+
if (!environment || !remoteUrl || isBrowseNavigating || isSubmitting) return;
751803
setError(null);
752804
const resolved = resolveAddProjectPath({
753805
rawPath: pathInput,
@@ -776,7 +828,15 @@ export function AddProjectDestinationScreen(props: {
776828
}
777829
}
778830
setIsSubmitting(false);
779-
}, [cloneRepository, createProject, environment, isSubmitting, pathInput, remoteUrl]);
831+
}, [
832+
cloneRepository,
833+
createProject,
834+
environment,
835+
isBrowseNavigating,
836+
isSubmitting,
837+
pathInput,
838+
remoteUrl,
839+
]);
780840

781841
return (
782842
<AddProjectShell>
@@ -798,12 +858,13 @@ export function AddProjectDestinationScreen(props: {
798858
/>
799859
<PrimaryActionButton
800860
label="Clone project"
801-
disabled={isSubmitting || !remoteUrl}
861+
disabled={isBrowseNavigating || isSubmitting || !remoteUrl}
802862
onPress={() => void submitPath()}
803863
loading={isSubmitting}
804864
/>
805865
<FolderBrowser
806866
environment={environment}
867+
navigateToBrowsePath={navigateToBrowsePath}
807868
pathInput={pathInput}
808869
setPathInput={setPathInput}
809870
/>

0 commit comments

Comments
 (0)