273 lines
7.5 KiB
TypeScript
273 lines
7.5 KiB
TypeScript
import {Href, Stack, useRouter} from "expo-router";
|
|
import {StyleSheet, Text, View, ColorValue, ListRenderItemInfo, TouchableOpacity, Platform, Modal} from "react-native";
|
|
import {RowMap, SwipeListView} from "react-native-swipe-list-view";
|
|
import {ChevronDown, Plus} from "lucide-react-native";
|
|
import {useEffect, useState} from "react";
|
|
import {MenuAction, MenuView, NativeActionEvent} from "@react-native-menu/menu";
|
|
import {SQLiteDatabase, SQLiteProvider, useSQLiteContext} from "expo-sqlite";
|
|
import CreateComputer from "@/app/createComputer";
|
|
|
|
export const iOS_HIGHLIGHT = "#007AFF";
|
|
|
|
export async function migrateDbIfNeeded(db: SQLiteDatabase) {
|
|
await db.execAsync(`
|
|
PRAGMA journal_mode = WAL;
|
|
CREATE TABLE IF NOT EXISTS servers (key INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, domain TEXT NOT NULL);
|
|
CREATE TABLE IF NOT EXISTS computers (key INTEGER PRIMARY KEY NOT NULL, server_key INTEGER NOT NULL, name, emoji, background_color, mac_address TEXT NOT NULL);
|
|
`);
|
|
}
|
|
|
|
export default function Index() {
|
|
const [ currentServerName, setCurrentServerName ] = useState<string>("None");
|
|
const [ servers, setServers ] = useState<Server[]>([]);
|
|
const [ computers, setComputers ] = useState<Computer[]>([]);
|
|
|
|
return (
|
|
<View>
|
|
<SQLiteProvider databaseName="selfstarter.db" onInit={migrateDbIfNeeded}>
|
|
<Header servers={servers} setServers={setServers} currentServerName={currentServerName} setCurrentServerName={setCurrentServerName} />
|
|
<ComputerList computers={computers} setComputers={setComputers}/>
|
|
</SQLiteProvider>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
function Header(props: { servers: Server[], setServers: (value: (((prevState: Server[]) => Server[]) | Server[])) => void, currentServerName: string, setCurrentServerName: (value: (((prevState: string) => string) | string)) => void }) {
|
|
const db = useSQLiteContext();
|
|
|
|
useEffect(() => {
|
|
async function setup() {
|
|
const result = await db.getFirstAsync<Server>(`SELECT * FROM servers`);
|
|
props.setCurrentServerName(result?.name || "None");
|
|
}
|
|
|
|
if (props.currentServerName === "None") {
|
|
setup();
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
async function setup() {
|
|
const result = await db.getAllAsync<Server>(`SELECT * FROM servers`);
|
|
props.setServers(result);
|
|
}
|
|
setup();
|
|
}, []);
|
|
|
|
const [ menuEvent, setMenuEvent ] = useState("");
|
|
const router = useRouter();
|
|
useEffect(() => {
|
|
if (menuEvent === "addComputer") {
|
|
router.navigate('/createComputer');
|
|
} else if (menuEvent === "addServer") {
|
|
router.navigate('/createServer');
|
|
}
|
|
setMenuEvent("");
|
|
}, [menuEvent]);
|
|
|
|
const selectActions: MenuAction[] = [];
|
|
for (const server of props.servers) {
|
|
if (server.name !== props.currentServerName) {
|
|
selectActions.push({
|
|
id: server.domain,
|
|
title: server.name,
|
|
titleColor: '#000000',
|
|
imageColor: '#000000',
|
|
})
|
|
}
|
|
}
|
|
selectActions.push({
|
|
id: 'deleteServer',
|
|
title: 'Delete Current Server',
|
|
attributes: {
|
|
destructive: true,
|
|
},
|
|
image: Platform.select({
|
|
ios: 'trash',
|
|
android: 'ic_menu_delete',
|
|
}),
|
|
imageColor: "red",
|
|
})
|
|
|
|
|
|
const handleServerSelect = ({nativeEvent}: NativeActionEvent) => {
|
|
const newName = props.servers.find((server) => {
|
|
return server.name === nativeEvent.event;
|
|
})
|
|
props.setCurrentServerName(newName?.name ?? "None");
|
|
};
|
|
|
|
const addActions: MenuAction[] = [
|
|
{
|
|
id: 'addServer',
|
|
title: 'Add WoL Server',
|
|
titleColor: '#000000',
|
|
image: 'globe',
|
|
imageColor: '#000000',
|
|
}
|
|
];
|
|
if (props.currentServerName !== "None") {
|
|
addActions.push({
|
|
id: 'addComputer',
|
|
title: 'Add Computer',
|
|
titleColor: '#000000',
|
|
image: 'desktopcomputer',
|
|
imageColor: '#000000',
|
|
});
|
|
}
|
|
|
|
return <Stack.Screen
|
|
options={{
|
|
title: 'Home',
|
|
headerTintColor: '#fff',
|
|
headerTitleStyle: {
|
|
fontWeight: 'bold',
|
|
},
|
|
headerTitle: _ =>
|
|
<View style={{
|
|
display: "flex",
|
|
flexDirection: "row",
|
|
gap: 10,
|
|
}}>
|
|
<Text style={{
|
|
fontSize: 20,
|
|
fontWeight: 'bold',
|
|
}}>{props.currentServerName}</Text>
|
|
{props.servers.length > 0 &&
|
|
<View style={{
|
|
backgroundColor: iOS_HIGHLIGHT,
|
|
borderRadius: 100,
|
|
}}>
|
|
<MenuView onPressAction={handleServerSelect} actions={selectActions}>
|
|
<ChevronDown color={"#FFFFFF"}/>
|
|
</MenuView>
|
|
</View>
|
|
}
|
|
</View>,
|
|
headerRight: props =>
|
|
<View>
|
|
<MenuView
|
|
onPressAction={({nativeEvent}) => { setMenuEvent(nativeEvent.event); }}
|
|
actions={addActions}
|
|
shouldOpenOnLongPress={false}
|
|
>
|
|
<Plus color={iOS_HIGHLIGHT}/>
|
|
</MenuView>
|
|
</View>
|
|
}}
|
|
/>
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
computerElement: {
|
|
display: "flex",
|
|
flexDirection: "row",
|
|
paddingHorizontal: 24,
|
|
paddingVertical: 14,
|
|
gap: 32,
|
|
backgroundColor: '#FFFFFF'
|
|
},
|
|
computerName: {
|
|
fontSize: 18,
|
|
justifyContent: "flex-start",
|
|
},
|
|
computerSubtitle: {
|
|
fontSize: 12,
|
|
justifyContent: "flex-start",
|
|
color: '#979797'
|
|
},
|
|
backTextWhite: {
|
|
color: '#FFF',
|
|
},
|
|
rowBack: {
|
|
alignItems: 'center',
|
|
backgroundColor: '#DDD',
|
|
flex: 1,
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
paddingLeft: 15,
|
|
},
|
|
backRightBtn: {
|
|
alignItems: 'center',
|
|
bottom: 0,
|
|
justifyContent: 'center',
|
|
position: 'absolute',
|
|
top: 0,
|
|
width: 75,
|
|
},
|
|
backRightBtnRight: {
|
|
backgroundColor: 'red',
|
|
right: 0,
|
|
},
|
|
});
|
|
|
|
function ComputerList(props: {computers: Computer[], setComputers: (value: (((prevState: Computer[]) => Computer[]) | Computer[])) => void }) {
|
|
const db = useSQLiteContext();
|
|
|
|
useEffect(() => {
|
|
async function setup() {
|
|
const result = await db.getAllAsync<Computer>(`SELECT * FROM computers`);
|
|
props.setComputers(result);
|
|
}
|
|
setup();
|
|
}, []);
|
|
|
|
const renderHiddenItem = (data: ListRenderItemInfo<Computer>, rowMap: RowMap<Computer>) => (
|
|
<View style={styles.rowBack}>
|
|
<TouchableOpacity
|
|
style={[styles.backRightBtn, styles.backRightBtnRight]}
|
|
onPress={() => {
|
|
//TODO: remove it from storage
|
|
}}>
|
|
<Text style={styles.backTextWhite}>Delete</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
);
|
|
|
|
return (
|
|
<SwipeListView
|
|
data={props.computers}
|
|
renderItem={({item}: ListRenderItemInfo<Computer>) =>
|
|
<View style={styles.computerElement}>
|
|
<View style={{
|
|
width: 65,
|
|
height: 65,
|
|
borderRadius: 100,
|
|
backgroundColor: item.background_color,
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
}}> //TODO: Make Button
|
|
<Text style={{
|
|
fontSize: 28
|
|
}}>{item.emoji}</Text>
|
|
</View>
|
|
<View style={{
|
|
flexDirection: "column",
|
|
gap: 3,
|
|
padding: 0,
|
|
justifyContent: "center",
|
|
}}>
|
|
<Text style={styles.computerName}>{item.name}</Text>
|
|
<Text style={styles.computerSubtitle}>{item.mac_address}</Text>
|
|
</View>
|
|
</View>
|
|
} rightOpenValue={-75} renderHiddenItem={renderHiddenItem}
|
|
/>
|
|
)
|
|
}
|
|
|
|
type Server = {
|
|
key: number,
|
|
name: string,
|
|
domain: string,
|
|
}
|
|
|
|
type Computer = {
|
|
key: number,
|
|
server_key: number,
|
|
name: string,
|
|
emoji: string,
|
|
background_color: ColorValue,
|
|
mac_address: string,
|
|
}
|