2026-06-08 23:11:09 +07:00
|
|
|
import React, { useEffect, useState } from "react";
|
2026-02-04 12:37:40 +07:00
|
|
|
import { Box, Text, useApp } from "ink";
|
2026-02-02 18:23:31 +07:00
|
|
|
import Spinner from "ink-spinner";
|
2026-06-08 23:11:09 +07:00
|
|
|
import {
|
|
|
|
|
Header,
|
|
|
|
|
UploadProgress,
|
|
|
|
|
ConnectionStatus,
|
|
|
|
|
ConfirmAnimatedUpload,
|
|
|
|
|
} from "./index.ts";
|
2026-02-02 18:23:31 +07:00
|
|
|
import { useUpload } from "../hooks/useUpload.ts";
|
|
|
|
|
import type { UploadOptions } from "../cli/types.ts";
|
|
|
|
|
|
|
|
|
|
export interface AppProps {
|
|
|
|
|
options: UploadOptions;
|
|
|
|
|
verbose: boolean;
|
2026-06-08 23:11:09 +07:00
|
|
|
/** Set when the upload contains a GIF/video and requires user confirmation */
|
|
|
|
|
confirmMediaType?: "gif" | "video" | null;
|
2026-02-02 18:23:31 +07:00
|
|
|
}
|
|
|
|
|
|
2026-06-08 23:11:09 +07:00
|
|
|
export const App: React.FC<AppProps> = ({
|
|
|
|
|
options,
|
|
|
|
|
verbose,
|
|
|
|
|
confirmMediaType,
|
|
|
|
|
}) => {
|
|
|
|
|
const [confirmed, setConfirmed] = useState(!confirmMediaType);
|
|
|
|
|
|
|
|
|
|
if (!confirmed && confirmMediaType) {
|
|
|
|
|
return (
|
|
|
|
|
<ConfirmAnimatedUpload
|
|
|
|
|
mediaType={confirmMediaType}
|
|
|
|
|
onConfirm={() => setConfirmed(true)}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return <UploadFlow options={options} verbose={verbose} />;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const UploadFlow: React.FC<{ options: UploadOptions; verbose: boolean }> = ({
|
|
|
|
|
options,
|
|
|
|
|
verbose,
|
|
|
|
|
}) => {
|
2026-02-04 12:37:40 +07:00
|
|
|
const { exit } = useApp();
|
2026-02-02 18:23:31 +07:00
|
|
|
const {
|
|
|
|
|
status,
|
|
|
|
|
message,
|
|
|
|
|
progress,
|
|
|
|
|
currentFileIndex,
|
|
|
|
|
totalFiles,
|
|
|
|
|
uploadSteps,
|
|
|
|
|
connectionSteps,
|
|
|
|
|
} = useUpload(options, verbose);
|
|
|
|
|
|
2026-02-04 12:37:40 +07:00
|
|
|
// Exit the app when done
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (status === "success" || status === "error") {
|
|
|
|
|
// Give time for the final render, then exit
|
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
|
exit();
|
|
|
|
|
// Force exit since noble keeps handles open
|
|
|
|
|
process.exit(status === "error" ? 1 : 0);
|
|
|
|
|
}, 100);
|
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
|
}
|
|
|
|
|
}, [status, exit]);
|
|
|
|
|
|
2026-02-02 18:23:31 +07:00
|
|
|
return (
|
|
|
|
|
<Box flexDirection="column" padding={1}>
|
|
|
|
|
<Header />
|
|
|
|
|
|
|
|
|
|
{options.isBulk && totalFiles > 1 && (
|
|
|
|
|
<Box marginBottom={1}>
|
|
|
|
|
<Text color="blue" bold>
|
|
|
|
|
Bulk Upload: {currentFileIndex}/{totalFiles} files
|
|
|
|
|
</Text>
|
|
|
|
|
</Box>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{status === "connecting" && (
|
|
|
|
|
<Box flexDirection="column">
|
|
|
|
|
<Box>
|
|
|
|
|
<Text color="cyan">
|
|
|
|
|
<Spinner type="dots" />
|
|
|
|
|
</Text>
|
|
|
|
|
<Text color="cyan" bold>
|
|
|
|
|
{" "}
|
|
|
|
|
{message}
|
|
|
|
|
</Text>
|
|
|
|
|
</Box>
|
|
|
|
|
<ConnectionStatus steps={connectionSteps} />
|
|
|
|
|
</Box>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{status === "uploading" && (
|
|
|
|
|
<Box flexDirection="column">
|
|
|
|
|
<UploadProgress
|
|
|
|
|
status={status}
|
|
|
|
|
message={message}
|
|
|
|
|
progress={progress}
|
|
|
|
|
/>
|
|
|
|
|
<ConnectionStatus steps={uploadSteps} />
|
|
|
|
|
</Box>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{status === "success" && (
|
|
|
|
|
<>
|
|
|
|
|
<UploadProgress
|
|
|
|
|
status={status}
|
|
|
|
|
message={message}
|
|
|
|
|
progress={progress}
|
|
|
|
|
/>
|
|
|
|
|
<Box marginTop={1}>
|
|
|
|
|
<Text color="green">Device is ready to use!</Text>
|
|
|
|
|
</Box>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{status === "error" && (
|
|
|
|
|
<>
|
|
|
|
|
<UploadProgress
|
|
|
|
|
status={status}
|
|
|
|
|
message={message}
|
|
|
|
|
progress={progress}
|
|
|
|
|
/>
|
|
|
|
|
<Box marginTop={1}>
|
|
|
|
|
<Text color="red">Please check your device and try again.</Text>
|
|
|
|
|
</Box>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
};
|