From efebb08ce4f4bcefde844205713107021a406088 Mon Sep 17 00:00:00 2001 From: EthanShoeDev <13422990+EthanShoeDev@users.noreply.github.com> Date: Sun, 7 Sep 2025 23:47:02 -0400 Subject: [PATCH] commit --- .vscode/extensions.json | 8 +- {app => src/app}/_layout.tsx | 0 {app => src/app}/index.tsx | 141 +++++------------------------ src/app/shell.tsx | 12 +++ src/components/form-components.tsx | 119 ++++++++++++++++++++++++ src/lib/app-form.ts | 20 ++++ 6 files changed, 180 insertions(+), 120 deletions(-) rename {app => src/app}/_layout.tsx (100%) rename {app => src/app}/index.tsx (51%) create mode 100644 src/app/shell.tsx create mode 100644 src/components/form-components.tsx create mode 100644 src/lib/app-form.ts diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b7ed837..02450af 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1 +1,7 @@ -{ "recommendations": ["expo.vscode-expo-tools"] } +{ + "recommendations": [ + "expo.vscode-expo-tools", + "gruntfuggly.todo-tree", + "eamodio.gitlens" + ] +} diff --git a/app/_layout.tsx b/src/app/_layout.tsx similarity index 100% rename from app/_layout.tsx rename to src/app/_layout.tsx diff --git a/app/index.tsx b/src/app/index.tsx similarity index 51% rename from app/index.tsx rename to src/app/index.tsx index 6fef9c8..ba54e1b 100644 --- a/app/index.tsx +++ b/src/app/index.tsx @@ -1,136 +1,39 @@ import SSHClient, { PtyType } from '@dylankenneally/react-native-ssh-sftp' -import { - AnyFieldApi, - createFormHook, - createFormHookContexts, -} from '@tanstack/react-form' -import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native' - -const { fieldContext, formContext } = createFormHookContexts() - -// #region: UI Components - -// https://tanstack.com/form/latest/docs/framework/react/quick-start -function TextField( - props: React.ComponentProps & { - label?: string - field: AnyFieldApi - }, -) { - const { label, field, style, ...rest } = props - const meta = field.state.meta - const errorMessage = meta?.errors?.[0] // TODO: typesafe errors - - return ( - - {label ? {label} : null} - - {errorMessage ? ( - {String(errorMessage)} - ) : null} - - ) -} - -function NumberField( - props: React.ComponentProps & { - label?: string - field: AnyFieldApi - }, -) { - const { label, field, style, keyboardType, onChangeText, ...rest } = props - const meta = field.state.meta - const errorMessage = meta?.errors?.[0] - - return ( - - {label ? {label} : null} - { - if (onChangeText) onChangeText(text) - }} - /> - {errorMessage ? ( - {String(errorMessage)} - ) : null} - - ) -} - -function SubmitButton(props: { - onPress?: () => void - title?: string - disabled?: boolean -}) { - const { onPress, title = 'Connect', disabled } = props - return ( - - {title} - - ) -} - -// #endregion - -// https://tanstack.com/form/latest/docs/framework/react/quick-start -const { useAppForm } = createFormHook({ - fieldComponents: { - TextField, - NumberField, - }, - formComponents: { - SubmitButton, - }, - fieldContext, - formContext, -}) +import { StyleSheet, Text, View } from 'react-native' +import { useFresshAppForm } from '../lib/app-form' export default function Index() { - const connectionForm = useAppForm({ + const connectionForm = useFresshAppForm({ defaultValues: { - host: '', + // host: '', + // port: 22, + // username: '', + // password: '', + // TODO: Remove this weird default + host: 'test.rebex.net', port: 22, - username: '', - password: '', + username: 'demo', + password: 'password', }, validators: { + // TODO: Add a zod validator here + // onChange: z.object({ + // email: z.email(), + // }), onSubmitAsync: async ({ value }) => { - // we will connect here. - // if connection fails, tanstack form will know the form is in an error state. - - // we can read that state from the field.state.meta.errors (or errorMap) - // - SSHClient.connectWithPassword( + console.log('Connecting to SSH server...') + const sshClientConnection = await SSHClient.connectWithPassword( value.host, value.port, value.username, value.password, - ).then(async (client) => { - alert('Connected') - client.on('Shell', (data) => { - console.log(data) - }) - await client.startShell(PtyType.XTERM) + ) + console.log('Connected to SSH server') - setTimeout(() => { - client.disconnect() - }, 5_000) + sshClientConnection.on('Shell', (data) => { + console.log(data) }) + await sshClientConnection.startShell(PtyType.XTERM) }, }, }) diff --git a/src/app/shell.tsx b/src/app/shell.tsx new file mode 100644 index 0000000..953803d --- /dev/null +++ b/src/app/shell.tsx @@ -0,0 +1,12 @@ +/** + * This is the page that is shown after an ssh connection + */ +import { Text, View } from 'react-native' + +export default function Shell() { + return ( + + Shell + + ) +} diff --git a/src/components/form-components.tsx b/src/components/form-components.tsx new file mode 100644 index 0000000..6d712e1 --- /dev/null +++ b/src/components/form-components.tsx @@ -0,0 +1,119 @@ +import { AnyFieldApi } from '@tanstack/react-form' +import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native' + +// https://tanstack.com/form/latest/docs/framework/react/quick-start +export function TextField( + props: React.ComponentProps & { + label?: string + field: AnyFieldApi + }, +) { + const { label, field, style, ...rest } = props + const meta = field.state.meta + const errorMessage = meta?.errors?.[0] // TODO: typesafe errors + + return ( + + {label ? {label} : null} + + {errorMessage ? ( + {String(errorMessage)} + ) : null} + + ) +} + +export function NumberField( + props: React.ComponentProps & { + label?: string + field: AnyFieldApi + }, +) { + const { label, field, style, keyboardType, onChangeText, ...rest } = props + const meta = field.state.meta + const errorMessage = meta?.errors?.[0] + + return ( + + {label ? {label} : null} + { + if (onChangeText) onChangeText(text) + }} + /> + {errorMessage ? ( + {String(errorMessage)} + ) : null} + + ) +} + +export function SubmitButton(props: { + onPress?: () => void + title?: string + disabled?: boolean +}) { + const { onPress, title = 'Connect', disabled } = props + return ( + + {title} + + ) +} + +const styles = StyleSheet.create({ + inputGroup: { + marginBottom: 12, + }, + label: { + marginBottom: 6, + fontSize: 14, + color: '#C6CBD3', + fontWeight: '600', + }, + input: { + borderWidth: 1, + borderColor: '#2A3655', + backgroundColor: '#0E172B', + color: '#E5E7EB', + borderRadius: 10, + paddingHorizontal: 12, + paddingVertical: 12, + fontSize: 16, + }, + errorText: { + marginTop: 6, + color: '#FCA5A5', + fontSize: 12, + }, + submitButton: { + backgroundColor: '#2563EB', + borderRadius: 10, + paddingVertical: 14, + alignItems: 'center', + }, + submitButtonText: { + color: '#FFFFFF', + fontWeight: '700', + fontSize: 16, + }, + buttonDisabled: { + backgroundColor: '#3B82F6', + opacity: 0.6, + }, +}) diff --git a/src/lib/app-form.ts b/src/lib/app-form.ts new file mode 100644 index 0000000..4ff7f2b --- /dev/null +++ b/src/lib/app-form.ts @@ -0,0 +1,20 @@ +import { createFormHook, createFormHookContexts } from '@tanstack/react-form' +import { + NumberField, + SubmitButton, + TextField, +} from '../components/form-components' +const { fieldContext, formContext } = createFormHookContexts() + +// https://tanstack.com/form/latest/docs/framework/react/quick-start +export const { useAppForm: useFresshAppForm } = createFormHook({ + fieldComponents: { + TextField, + NumberField, + }, + formComponents: { + SubmitButton, + }, + fieldContext, + formContext, +})