Files
ppc_amnet/amnet-native/app/ServerManager.tsx
T
2024-09-04 12:12:58 +01:00

127 lines
4.9 KiB
TypeScript

import {Button, Paragraph, useTheme} from "react-native-paper";
import {FlatList, NativeSyntheticEvent, Share, View} from "react-native";
import React, {useCallback, useLayoutEffect, useMemo, useState} from "react";
import {Globe, History, Joystick, Plus, ServerOff} from "lucide-react-native";
import ContextMenu, {ContextMenuOnPressNativeEvent} from "react-native-context-menu-view";
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";
import {getGameName} from "amnet-shared";
const {useRealm, useQuery} = RealmContext;
function ServerCard(props: { server: AMServer, navigation: any }) {
const realm = useRealm();
const gameName = useMemo(() => getGameName(props.server.gameId), [props.server.gameId]);
const pageCallback = useCallback(() => {
props.navigation.navigate('server', {
serverId: props.server._id.toHexString()
});
}, [props.navigation, props.server]);
const pageContextMenuCallback = useCallback((action: NativeSyntheticEvent<ContextMenuOnPressNativeEvent>) => {
switch (action.nativeEvent.indexPath.toString()) {
case "0":
pageCallback();
return;
case "1,0":
Share.share({url: props.server.shareUrl});
return;
case "1,1":
realm.write(() => realm.delete(props.server));
return;
}
}, [realm, props.server]);
return (
<ContextMenu onPress={pageContextMenuCallback} actions={[
{title: `Select ${props.server.serverName}`, systemIcon: "chevron.right"},
{
title: "",
inlineChildren: true,
actions: [
{title: "Share Server", systemIcon: "square.and.arrow.up"},
{title: "Delete Server", systemIcon: "trash", destructive: true}
]
}
]}>
<AMUICard title={props.server.serverName} onPress={pageCallback}>
<AMUICardDetail>
<Globe size={18} color="gray"/>
<Paragraph style={{color: "gray", fontFamily: "JetBrainsMono_400Regular"}}>
{props.server.serverAddress}
</Paragraph>
</AMUICardDetail>
{gameName && (
<AMUICardDetail>
<Joystick size={18} color="gray"/>
<Paragraph style={{color: "gray"}}>{gameName}</Paragraph>
</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>
</ContextMenu>
)
}
export default function ServerManager({navigation}) {
const theme = useTheme();
const [searchQuery, setSearchQuery] = useState('');
const query = useQuery(AMServer);
const servers = useMemo(() => {
let q = query;
if (searchQuery !== '') {
q = q.filtered("serverName CONTAINS[c] $0 OR serverAddress CONTAINS[c] $0", searchQuery);
}
return q.sorted("lastUsed");
}, [query, searchQuery]);
// enable searchbar
useLayoutEffect(() => {
navigation.setOptions({
headerSearchBarOptions: {
type: "text",
value: searchQuery,
autoCorrect: "none",
autoCapitalize: "none",
onChangeText: e => setSearchQuery(e.nativeEvent.text)
},
})
}, [navigation, setSearchQuery]);
if (!servers.length) {
return (
<View style={{gap: 10, flex: 1, justifyContent: "center", alignItems: "center"}}>
<EmptyStatePlaceholder message="No servers found" icon={<ServerOff color="gray"/>}/>
{(!searchQuery || !query.length) && (
<Button mode="text"
onPress={() => navigation.navigate('add-server')}
icon={() => <Plus size={18} color={theme.colors.primary}/>}>Add Server</Button>)}
</View>
);
}
return (
<FlatList contentInsetAdjustmentBehavior="always"
data={servers}
keyExtractor={i => i._id.toString()}
style={{padding: 16, paddingTop: 24}}
contentContainerStyle={{paddingBottom: 80}}
ItemSeparatorComponent={() => <View style={{height: 15}}/>}
renderItem={i => <ServerCard navigation={navigation} server={i.item}/>}/>
)
}