add server listing, create modal

This commit is contained in:
ppc
2024-08-13 18:32:33 +01:00
parent d5fee8f4f1
commit 837b686753
4 changed files with 193 additions and 6 deletions
+84
View File
@@ -0,0 +1,84 @@
import {useCallback, useEffect, useState} from "react";
import {Card, Paragraph, useTheme} from "react-native-paper";
import {TextInput, TouchableOpacity, View} from "react-native";
import {useModalStyles} from "../utils/ModalStyles";
import RealmContext from "../models/RealmContext";
import {AMServer} from "../models/AMServer";
const {useRealm} = RealmContext;
export default function ServerAddModal({navigation}) {
const realm = useRealm();
const theme = useTheme();
const modalStyles = useModalStyles();
const [serverAddress, setServerAddress] = useState('');
useEffect(() => {
navigation.setOptions({
headerLeft: () => <TouchableOpacity onPress={() => navigation.goBack()}>
<Paragraph style={modalStyles.headerText}>Cancel</Paragraph>
</TouchableOpacity>,
});
}, [navigation, modalStyles]);
useEffect(() => {
let urlValid = false;
try {
new URL(serverAddress);
urlValid = true;
} catch {
// do nothing
}
const themeOptions = urlValid ? modalStyles.headerText : {color: theme.colors.outline};
navigation.setOptions({
headerRight: () => <TouchableOpacity disabled={!urlValid} onPress={() => saveServer()}>
<Paragraph style={{...themeOptions, fontWeight: "600"}}>Save</Paragraph>
</TouchableOpacity>
})
}, [navigation, modalStyles, serverAddress]);
const saveServer = useCallback(() => {
let serverObject = realm.objects<AMServer>("server").find(x => x.serverAddress == serverAddress);
if (!serverObject) {
realm.write(() => {
serverObject = realm.create<AMServer>("server", AMServer.generate(serverAddress));
});
}
navigation.goBack();
setTimeout(() => navigation.navigate('server-card-in', {
serverId: serverObject._id.toHexString()
}), 500);
}, [serverAddress, realm]);
return (<View style={{display: "flex", gap: 10, justifyContent: "center", paddingHorizontal: 15, paddingTop: 25}}>
<Card>
<Card.Content>
<View style={{...modalStyles.formItemRow, gap: 10}}>
<Paragraph style={{...modalStyles.formItemName, minWidth: 0}}>
Address
</Paragraph>
<TextInput style={modalStyles.formItemInput}
autoFocus
numberOfLines={1}
autoComplete="off"
textContentType="URL"
autoCapitalize="none"
value={serverAddress}
onChangeText={setServerAddress}
placeholder="http://192.168.1.2:6070"
placeholderTextColor={theme.colors.outline}/>
</View>
</Card.Content>
</Card>
<Paragraph style={modalStyles.formFootnote}>
If you're using HTTPS, ensure a trusted certificate has been configured via HTTP.SYS
</Paragraph>
</View>)
}
+9
View File
@@ -0,0 +1,9 @@
import {ScrollView} from "react-native";
export default function ServerCardIn({navigation}) {
return (
<ScrollView contentInsetAdjustmentBehavior="always">
</ScrollView>
)
}
+58 -6
View File
@@ -1,9 +1,61 @@
import {Text, View} from "react-native";
import {Paragraph} from "react-native-paper";
import React, {useCallback, useMemo} from "react";
import {FlatList, Text, View} from "react-native";
import {Globe, History, ServerOff} from "lucide-react-native";
import {EmptyStatePlaceholder} from "../components/EmptyStatePlaceholder";
import {AMUICard, AMUICardDetail} from "../components/AMUICard";
import TimeAgo from "../components/TimeAgo";
import RealmContext from "../models/RealmContext";
import {AMServer} from "../models/AMServer";
const {useQuery} = RealmContext;
function ServerCard(props: { server: AMServer, navigation: any }) {
const pageCallback = useCallback(() => {
props.navigation.navigate('server-card-in', {
serverId: props.server._id.toHexString()
});
}, [props.navigation, props.server]);
export default function ServerManager() {
return (
<View>
<Text>Server Manager</Text>
</View>
);
<AMUICard title={props.server.serverName} onPress={pageCallback}>
<AMUICardDetail>
<Globe size={18} color="gray"/>
<Text style={{color: "gray", fontFamily: "JetBrainsMono_400Regular"}}>
{props.server.serverAddress}
</Text>
</AMUICardDetail>
<AMUICardDetail>
<History size={18} color="gray"/>
{props.server.lastUsed
? (<TimeAgo style={{color: "gray"}} dateTo={props.server.lastUsed}/>)
: (<Paragraph style={{color: "gray"}}>never</Paragraph>)}
</AMUICardDetail>
</AMUICard>
)
}
export default function ServerManager({navigation}) {
const query = useQuery(AMServer);
const servers = useMemo(() => query.sorted("lastUsed"), [query]);
if (!servers.length) {
return (
<View style={{display: "flex", flex: 1, gap: 15, justifyContent: "center", alignItems: "center"}}>
<EmptyStatePlaceholder message="No servers added yet" icon={<ServerOff color="gray"/>}/>
</View>
);
}
return (
<FlatList contentInsetAdjustmentBehavior="always"
style={{padding: 16, paddingTop: 24}}
data={servers}
keyExtractor={i => i._id.toString()}
contentContainerStyle={{paddingBottom: 40}}
ItemSeparatorComponent={() => <View style={{height: 15}}/>}
renderItem={i => <ServerCard navigation={navigation} server={i.item}/>}/>
)
}
+42
View File
@@ -0,0 +1,42 @@
import {Plus} from "lucide-react-native";
import {TouchableOpacity} from "react-native";
import {useTheme} from "@react-navigation/native";
import {createNativeStackNavigator} from "@react-navigation/native-stack";
import ServerCardIn from "./ServerCardIn";
import ServerManager from "./ServerManager";
import ServerAddModal from "./ServerAddModal";
const Stack = createNativeStackNavigator();
export default function ServerManagerStack({navigation}) {
const theme = useTheme();
return (
<Stack.Navigator>
<Stack.Group screenOptions={{
headerLargeTitle: true,
headerBlurEffect: "regular",
headerStyle: {
backgroundColor: theme.colors.background
}
}}>
<Stack.Screen name="server-listing"
component={ServerManager}
options={{
title: "Servers",
headerRight: () => <TouchableOpacity
onPress={() => navigation.navigate('add-server')}>
<Plus/>
</TouchableOpacity>
}}/>
<Stack.Screen name="server-card-in" options={{title: "Server"}} component={ServerCardIn}/>
</Stack.Group>
<Stack.Group screenOptions={{presentation: "modal"}}>
<Stack.Screen name="add-server" component={ServerAddModal} options={{title: "Add Server"}}/>
</Stack.Group>
</Stack.Navigator>
);
}