mirror of
https://github.com/EthanShoeDev/fressh.git
synced 2026-01-11 14:22:51 +00:00
commit
This commit is contained in:
8
.vscode/extensions.json
vendored
8
.vscode/extensions.json
vendored
@@ -1 +1,7 @@
|
||||
{ "recommendations": ["expo.vscode-expo-tools"] }
|
||||
{
|
||||
"recommendations": [
|
||||
"expo.vscode-expo-tools",
|
||||
"gruntfuggly.todo-tree",
|
||||
"eamodio.gitlens"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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<typeof TextInput> & {
|
||||
label?: string
|
||||
field: AnyFieldApi
|
||||
},
|
||||
) {
|
||||
const { label, field, style, ...rest } = props
|
||||
const meta = field.state.meta
|
||||
const errorMessage = meta?.errors?.[0] // TODO: typesafe errors
|
||||
|
||||
return (
|
||||
<View style={styles.inputGroup}>
|
||||
{label ? <Text style={styles.label}>{label}</Text> : null}
|
||||
<TextInput
|
||||
{...rest}
|
||||
style={[styles.input, style]}
|
||||
placeholderTextColor="#9AA0A6"
|
||||
/>
|
||||
{errorMessage ? (
|
||||
<Text style={styles.errorText}>{String(errorMessage)}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
function NumberField(
|
||||
props: React.ComponentProps<typeof TextInput> & {
|
||||
label?: string
|
||||
field: AnyFieldApi
|
||||
},
|
||||
) {
|
||||
const { label, field, style, keyboardType, onChangeText, ...rest } = props
|
||||
const meta = field.state.meta
|
||||
const errorMessage = meta?.errors?.[0]
|
||||
|
||||
return (
|
||||
<View style={styles.inputGroup}>
|
||||
{label ? <Text style={styles.label}>{label}</Text> : null}
|
||||
<TextInput
|
||||
{...rest}
|
||||
keyboardType={keyboardType ?? 'numeric'}
|
||||
style={[styles.input, style]}
|
||||
placeholderTextColor="#9AA0A6"
|
||||
onChangeText={(text) => {
|
||||
if (onChangeText) onChangeText(text)
|
||||
}}
|
||||
/>
|
||||
{errorMessage ? (
|
||||
<Text style={styles.errorText}>{String(errorMessage)}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
function SubmitButton(props: {
|
||||
onPress?: () => void
|
||||
title?: string
|
||||
disabled?: boolean
|
||||
}) {
|
||||
const { onPress, title = 'Connect', disabled } = props
|
||||
return (
|
||||
<Pressable
|
||||
style={[
|
||||
styles.submitButton,
|
||||
disabled ? styles.buttonDisabled : undefined,
|
||||
]}
|
||||
onPress={onPress}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Text style={styles.submitButtonText}>{title}</Text>
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
||||
// #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)
|
||||
},
|
||||
},
|
||||
})
|
||||
12
src/app/shell.tsx
Normal file
12
src/app/shell.tsx
Normal file
@@ -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 (
|
||||
<View>
|
||||
<Text>Shell</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
119
src/components/form-components.tsx
Normal file
119
src/components/form-components.tsx
Normal file
@@ -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<typeof TextInput> & {
|
||||
label?: string
|
||||
field: AnyFieldApi
|
||||
},
|
||||
) {
|
||||
const { label, field, style, ...rest } = props
|
||||
const meta = field.state.meta
|
||||
const errorMessage = meta?.errors?.[0] // TODO: typesafe errors
|
||||
|
||||
return (
|
||||
<View style={styles.inputGroup}>
|
||||
{label ? <Text style={styles.label}>{label}</Text> : null}
|
||||
<TextInput
|
||||
{...rest}
|
||||
style={[styles.input, style]}
|
||||
placeholderTextColor="#9AA0A6"
|
||||
/>
|
||||
{errorMessage ? (
|
||||
<Text style={styles.errorText}>{String(errorMessage)}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export function NumberField(
|
||||
props: React.ComponentProps<typeof TextInput> & {
|
||||
label?: string
|
||||
field: AnyFieldApi
|
||||
},
|
||||
) {
|
||||
const { label, field, style, keyboardType, onChangeText, ...rest } = props
|
||||
const meta = field.state.meta
|
||||
const errorMessage = meta?.errors?.[0]
|
||||
|
||||
return (
|
||||
<View style={styles.inputGroup}>
|
||||
{label ? <Text style={styles.label}>{label}</Text> : null}
|
||||
<TextInput
|
||||
{...rest}
|
||||
keyboardType={keyboardType ?? 'numeric'}
|
||||
style={[styles.input, style]}
|
||||
placeholderTextColor="#9AA0A6"
|
||||
onChangeText={(text) => {
|
||||
if (onChangeText) onChangeText(text)
|
||||
}}
|
||||
/>
|
||||
{errorMessage ? (
|
||||
<Text style={styles.errorText}>{String(errorMessage)}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export function SubmitButton(props: {
|
||||
onPress?: () => void
|
||||
title?: string
|
||||
disabled?: boolean
|
||||
}) {
|
||||
const { onPress, title = 'Connect', disabled } = props
|
||||
return (
|
||||
<Pressable
|
||||
style={[
|
||||
styles.submitButton,
|
||||
disabled ? styles.buttonDisabled : undefined,
|
||||
]}
|
||||
onPress={onPress}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Text style={styles.submitButtonText}>{title}</Text>
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
})
|
||||
20
src/lib/app-form.ts
Normal file
20
src/lib/app-form.ts
Normal file
@@ -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,
|
||||
})
|
||||
Reference in New Issue
Block a user