Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 86 additions & 25 deletions apps/mobile/src/features/projects/AddProjectScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
type AddProjectRemoteSource,
} from "@t3tools/client-runtime/operations/projects";
import {
canPreloadBrowsePath,
createBrowseNavigationCoordinator,
filterFilesystemBrowseEntries,
getFilesystemBrowsePath,
} from "@t3tools/client-runtime/state/filesystem";
Expand Down Expand Up @@ -43,7 +45,10 @@ import { useThemeColor } from "../../lib/useThemeColor";
import { uuidv4 } from "../../lib/uuid";
import { useAtomCommand } from "../../state/use-atom-command";
import { useAtomQueryRunner } from "../../state/use-atom-query-runner";
import { useSavedRemoteConnections } from "../../state/use-remote-environment-registry";
import {
useRemoteEnvironmentRuntime,
useSavedRemoteConnections,
} from "../../state/use-remote-environment-registry";

interface EnvironmentOption {
readonly environmentId: EnvironmentId;
Expand Down Expand Up @@ -221,6 +226,63 @@ function ProjectPathInput(props: {
);
}

function useBrowsePathInput(environment: EnvironmentOption | null) {
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
const [pathInput, commitPathInput] = useState(() =>
getAddProjectInitialQuery(environment?.baseDirectory),
);
const environmentRuntime = useRemoteEnvironmentRuntime(environment?.environmentId ?? null);
const loadBrowsePath = useAtomQueryRunner(filesystemEnvironment.browse, {
reportFailure: false,
reportDefect: false,
});
const [browseNavigation] = useState(createBrowseNavigationCoordinator);
const [isBrowseNavigating, setIsBrowseNavigating] = useState(false);
const setPathInput = useCallback(
(path: string) => {
browseNavigation.invalidate();
setIsBrowseNavigating(false);
commitPathInput(path);
},
[browseNavigation],
);
const navigateToBrowsePath = useCallback(
async (path: string) => {
setIsBrowseNavigating(true);
const committed = await browseNavigation.run(
async () => {
if (environment && canPreloadBrowsePath(environmentRuntime?.connectionState)) {
await loadBrowsePath({
environmentId: environment.environmentId,
input: { partialPath: path },
});
}
},
() => commitPathInput(path),
);
if (committed) {
setIsBrowseNavigating(false);
}
return committed;
},
[browseNavigation, environment, environmentRuntime?.connectionState, loadBrowsePath],
);

useEffect(() => {
if (environment) {
setPathInput(getAddProjectInitialQuery(environment.baseDirectory));
}
}, [environment, setPathInput]);

useEffect(
() => () => {
browseNavigation.invalidate();
},
[browseNavigation],
);

return { isBrowseNavigating, pathInput, setPathInput, navigateToBrowsePath };
}

function useEnvironmentOptions(): ReadonlyArray<EnvironmentOption> {
const serverConfigByEnvironmentId = useServerConfigs();
const { savedConnectionsById } = useSavedRemoteConnections();
Expand Down Expand Up @@ -585,6 +647,7 @@ function FolderBrowser(props: {
readonly environment: EnvironmentOption;
readonly pathInput: string;
readonly setPathInput: (path: string) => void;
readonly navigateToBrowsePath: (path: string) => Promise<boolean>;
}) {
const accentColor = useThemeColor("--color-icon-muted");
const browsePath = useMemo(
Expand Down Expand Up @@ -633,7 +696,7 @@ function FolderBrowser(props: {
right={null}
onPress={() => {
if (browsePath.parentPath) {
props.setPathInput(browsePath.parentPath);
void props.navigateToBrowsePath(browsePath.parentPath);
}
}}
/>
Expand All @@ -650,7 +713,7 @@ function FolderBrowser(props: {
browsePath.directoryPath.length > 0
? appendBrowsePathSegment(browsePath.directoryPath, entry.name)
: ensureBrowseDirectoryPath(entry.fullPath);
props.setPathInput(nextPath);
void props.navigateToBrowsePath(nextPath);
}}
/>
))}
Expand All @@ -662,19 +725,13 @@ function FolderBrowser(props: {
export function AddProjectLocalFolderScreen(props: { readonly environmentId?: string | string[] }) {
const environment = useEnvironmentFromParam(props.environmentId);
const createProject = useCreateProject(environment);
const [pathInput, setPathInput] = useState(() =>
getAddProjectInitialQuery(environment?.baseDirectory),
);
const { isBrowseNavigating, navigateToBrowsePath, pathInput, setPathInput } =
useBrowsePathInput(environment);
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
if (!environment) return;
setPathInput(getAddProjectInitialQuery(environment.baseDirectory));
}, [environment]);

const submitPath = useCallback(async () => {
if (!environment || isSubmitting) return;
if (!environment || isBrowseNavigating || isSubmitting) return;
setError(null);
const resolved = resolveAddProjectPath({
rawPath: pathInput,
Expand All @@ -692,7 +749,7 @@ export function AddProjectLocalFolderScreen(props: { readonly environmentId?: st
setError(errorMessage(Cause.squash(result.cause)));
}
setIsSubmitting(false);
}, [createProject, environment, isSubmitting, pathInput]);
}, [createProject, environment, isBrowseNavigating, isSubmitting, pathInput]);

return (
<AddProjectShell>
Expand All @@ -706,12 +763,13 @@ export function AddProjectLocalFolderScreen(props: { readonly environmentId?: st
/>
<PrimaryActionButton
label="Add project"
disabled={isSubmitting}
disabled={isBrowseNavigating || isSubmitting}
onPress={() => void submitPath()}
loading={isSubmitting}
/>
<FolderBrowser
environment={environment}
navigateToBrowsePath={navigateToBrowsePath}
pathInput={pathInput}
setPathInput={setPathInput}
/>
Expand All @@ -735,19 +793,13 @@ export function AddProjectDestinationScreen(props: {
const createProject = useCreateProject(environment);
const remoteUrl = stringParam(props.remoteUrl);
const repositoryTitle = stringParam(props.repositoryTitle);
const [pathInput, setPathInput] = useState(() =>
getAddProjectInitialQuery(environment?.baseDirectory),
);
const { isBrowseNavigating, navigateToBrowsePath, pathInput, setPathInput } =
useBrowsePathInput(environment);
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
if (!environment) return;
setPathInput(getAddProjectInitialQuery(environment.baseDirectory));
}, [environment]);

const submitPath = useCallback(async () => {
if (!environment || !remoteUrl || isSubmitting) return;
if (!environment || !remoteUrl || isBrowseNavigating || isSubmitting) return;
setError(null);
const resolved = resolveAddProjectPath({
rawPath: pathInput,
Expand Down Expand Up @@ -776,7 +828,15 @@ export function AddProjectDestinationScreen(props: {
}
}
setIsSubmitting(false);
}, [cloneRepository, createProject, environment, isSubmitting, pathInput, remoteUrl]);
}, [
cloneRepository,
createProject,
environment,
isBrowseNavigating,
isSubmitting,
pathInput,
remoteUrl,
]);

return (
<AddProjectShell>
Expand All @@ -798,12 +858,13 @@ export function AddProjectDestinationScreen(props: {
/>
<PrimaryActionButton
label="Clone project"
disabled={isSubmitting || !remoteUrl}
disabled={isBrowseNavigating || isSubmitting || !remoteUrl}
onPress={() => void submitPath()}
loading={isSubmitting}
/>
<FolderBrowser
environment={environment}
navigateToBrowsePath={navigateToBrowsePath}
pathInput={pathInput}
setPathInput={setPathInput}
/>
Expand Down
Loading