feat(mobile): auth added in
Some checks failed
Build and Push LST Docker Image / docker (push) Has been cancelled
Some checks failed
Build and Push LST Docker Image / docker (push) Has been cancelled
This commit is contained in:
@@ -1,9 +1,32 @@
|
||||
import { Tabs } from "expo-router";
|
||||
import { Home, Settings } from "lucide-react-native";
|
||||
import { Redirect, Tabs } from "expo-router";
|
||||
import { Container, Home, Logs, Rows4, Settings } from "lucide-react-native";
|
||||
import { useAppStore } from "../../hooks/useAppStore";
|
||||
import { useMobileAuthStore } from "../../hooks/useMobileAuth";
|
||||
|
||||
// const roles = {
|
||||
// adminOnly: ["admin"],
|
||||
// management: ["admin", "manager"],
|
||||
// allStaff: ["admin", "manager", "driver", "lead", "user"],
|
||||
// };
|
||||
|
||||
export default function TabsLayout() {
|
||||
const serverPort = useAppStore((s) => s.serverPort);
|
||||
const user = useMobileAuthStore((s) => s.user);
|
||||
const isUnlocked = useMobileAuthStore((s) => s.isUnlocked);
|
||||
|
||||
const port = parseInt(serverPort || "0", 10) >= 50000;
|
||||
|
||||
if (!user || (!isUnlocked && !port)) {
|
||||
return <Redirect href="/login" />;
|
||||
}
|
||||
|
||||
const isNormalScanner = parseInt(serverPort || "0", 10) >= 50000;
|
||||
|
||||
const hasRole = (allowed: string[] = []) => {
|
||||
const role = user?.role?.toLowerCase();
|
||||
return role ? allowed.includes(role) : false;
|
||||
};
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
@@ -27,11 +50,24 @@ export default function TabsLayout() {
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="config"
|
||||
name="laneCheck"
|
||||
options={{
|
||||
title: "settings",
|
||||
title: "Lane Check",
|
||||
|
||||
href: isNormalScanner ? null : "/(tabs)/laneCheck",
|
||||
tabBarIcon: ({ color, size }) => <Rows4 size={size} color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="dockScan"
|
||||
options={{
|
||||
title: "Dock scan",
|
||||
href:
|
||||
isNormalScanner || !hasRole(["admin", "manager"])
|
||||
? null
|
||||
: "/(tabs)/dockScan",
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<Settings size={size} color={color} />
|
||||
<Container size={size} color={color} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
@@ -40,7 +76,10 @@ export default function TabsLayout() {
|
||||
options={{
|
||||
title: "Logs",
|
||||
href:
|
||||
parseInt(serverPort || "0", 10) >= 50000 ? null : "/(tabs)/logs",
|
||||
isNormalScanner || !hasRole(["admin", "manager"])
|
||||
? null
|
||||
: "/(tabs)/logs",
|
||||
tabBarIcon: ({ color, size }) => <Logs size={size} color={color} />,
|
||||
}}
|
||||
/>
|
||||
{/* <Tabs.Screen
|
||||
@@ -51,6 +90,15 @@ export default function TabsLayout() {
|
||||
parseInt(serverPort || "0", 10) >= 50000 ? null : "/(tabs)/logs",
|
||||
}}
|
||||
/> */}
|
||||
<Tabs.Screen
|
||||
name="config"
|
||||
options={{
|
||||
title: "settings",
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<Settings size={size} color={color} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Link } from "expo-router";
|
||||
import { Text, View } from "react-native";
|
||||
import Setup from "../setup";
|
||||
|
||||
export default function SettingsTab() {
|
||||
return <Setup />
|
||||
}
|
||||
return <Setup />;
|
||||
}
|
||||
|
||||
26
lstMobile/src/app/(tabs)/dockScan.tsx
Normal file
26
lstMobile/src/app/(tabs)/dockScan.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from "react";
|
||||
import { Text, View } from "react-native";
|
||||
import { Button } from "../../components/ui/button";
|
||||
|
||||
export default function LaneCheck() {
|
||||
const getInfo = async () => {
|
||||
const info = "ho";
|
||||
|
||||
console.log(info);
|
||||
};
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
//justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginTop: 50,
|
||||
}}
|
||||
>
|
||||
<Text>Dock Scanning</Text>
|
||||
<Button onPress={getInfo}>
|
||||
<Text>Check info</Text>
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
37
lstMobile/src/app/(tabs)/laneCheck.tsx
Normal file
37
lstMobile/src/app/(tabs)/laneCheck.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import { Text, View } from "react-native";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { type ZebraScanResult, zebraScanner } from "../../lib/ZebraScanner";
|
||||
|
||||
export default function LaneCheck() {
|
||||
const handleScan = useCallback(async (scan: ZebraScanResult) => {
|
||||
console.log(scan);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
zebraScanner.ensureProfile();
|
||||
zebraScanner.startListening();
|
||||
|
||||
const sub = zebraScanner.addScanListener((scan) => {
|
||||
//console.log("SCAN:", scan);
|
||||
handleScan(scan);
|
||||
});
|
||||
|
||||
return () => {
|
||||
sub.remove();
|
||||
zebraScanner.stopListening();
|
||||
};
|
||||
}, [handleScan]);
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
//justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginTop: 50,
|
||||
}}
|
||||
>
|
||||
<Text>LaneChecks</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,26 +1,21 @@
|
||||
import { PortalHost } from "@rn-primitives/portal";
|
||||
import { Stack } from "expo-router";
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import "../../global.css";
|
||||
import { PortalHost } from "@rn-primitives/portal";
|
||||
import { View } from "react-native";
|
||||
import useDeviceLock from "../hooks/useDeviceCheck";
|
||||
|
||||
export default function RootLayout() {
|
||||
useDeviceLock();
|
||||
|
||||
return (
|
||||
<>
|
||||
<StatusBar style="dark" />
|
||||
<Stack screenOptions={{ headerShown: false }}>
|
||||
<Stack.Screen name="index" />
|
||||
<View className="items-center">
|
||||
<Stack.Screen
|
||||
name="(tabs)"
|
||||
options={{
|
||||
title: "Pending update",
|
||||
headerStyle: {
|
||||
backgroundColor: "lightblue",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<Stack.Screen name="login" />
|
||||
<Stack.Screen name="setup" />
|
||||
<Stack.Screen name="updateScreen" />
|
||||
<Stack.Screen name="(tabs)" />
|
||||
</Stack>
|
||||
<PortalHost />
|
||||
</>
|
||||
|
||||
@@ -1,127 +1,31 @@
|
||||
import axios from "axios";
|
||||
import Constants from "expo-constants";
|
||||
import { Redirect, useRouter } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Redirect } from "expo-router";
|
||||
import { ActivityIndicator, Text, View } from "react-native";
|
||||
import { useAppStore } from "../hooks/useAppStore";
|
||||
import { useMobileAuthStore } from "../hooks/useMobileAuth";
|
||||
import { useServerStore } from "../hooks/useServerCheck";
|
||||
import { devDelay } from "../lib/devMode";
|
||||
import { useAppStartup } from "../hooks/useAppStartup";
|
||||
|
||||
const startupMessages = {
|
||||
loading: "Loading app...",
|
||||
validating: "Validating data...",
|
||||
scannerMode: "Checking scanner mode...",
|
||||
normalScanner: "Starting normal ALPLAprod scanner that has no LST rules",
|
||||
checkingUpdates: "Checking for updates...",
|
||||
opening: "Opening LST scan app...",
|
||||
error: "Something went wrong during startup.",
|
||||
};
|
||||
|
||||
export default function Index() {
|
||||
const router = useRouter();
|
||||
const [message, setMessage] = useState(<Text>Starting app...</Text>);
|
||||
const [ready, setReady] = useState(false);
|
||||
const setServerVersion = useServerStore((s) => s.setServerVersion);
|
||||
//const { isUnlocked } = useMobileAuthStore();
|
||||
const { ready, startupRoute, status } = useAppStartup();
|
||||
|
||||
const hasHydrated = useAppStore((s) => s.hasHydrated);
|
||||
const serverPort = useAppStore((s) => s.serverPort);
|
||||
const serverIp = useAppStore((s) => s.serverIp);
|
||||
const build = Constants.expoConfig?.android?.versionCode ?? 1;
|
||||
const hasValidSetup = useAppStore((s) => s.hasValidSetup);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasHydrated) {
|
||||
setMessage(<Text>Loading app...</Text>);
|
||||
return;
|
||||
}
|
||||
|
||||
const startup = async () => {
|
||||
try {
|
||||
await devDelay(1500);
|
||||
|
||||
setMessage(<Text>Validating data...</Text>);
|
||||
await devDelay(1500);
|
||||
|
||||
if (!hasValidSetup()) {
|
||||
router.replace("/setup");
|
||||
return;
|
||||
}
|
||||
|
||||
// checking for lst.
|
||||
console.log(
|
||||
`http://${serverIp}:${parseInt(serverPort || "0", 10) >= 50000 ? "3000" : serverPort}/lst/api/mobile/version`,
|
||||
);
|
||||
try {
|
||||
const res = await axios.get(
|
||||
`http://${serverIp}:${parseInt(serverPort || "0", 10) >= 50000 ? "3000" : serverPort}/lst/api/mobile/version`,
|
||||
{
|
||||
timeout: 5000,
|
||||
},
|
||||
);
|
||||
|
||||
console.log(res.data);
|
||||
|
||||
// if the build version dose not match the latest server version force update
|
||||
if (res.status === 200) {
|
||||
setServerVersion(res.data);
|
||||
}
|
||||
|
||||
// TODO: change the header to show orange and theres a new version
|
||||
// console.log(build < res.data.minSupportedVersionCode);
|
||||
// if (build < res.data.minSupportedVersionCode) {
|
||||
// router.replace("/updateScreen");
|
||||
// return;
|
||||
// }
|
||||
} catch (error) {
|
||||
console.log("Error: ", error);
|
||||
}
|
||||
|
||||
setMessage(<Text>Checking scanner mode...</Text>);
|
||||
await devDelay(1500);
|
||||
|
||||
if (parseInt(serverPort || "0", 10) >= 50000) {
|
||||
setMessage(
|
||||
<Text>
|
||||
Starting normal alplaprod scanner that has no LST rules
|
||||
</Text>,
|
||||
);
|
||||
await devDelay(1500);
|
||||
//router.replace("/scanner");
|
||||
setReady(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setMessage(<Text>Checking for updates</Text>);
|
||||
await devDelay(1500);
|
||||
// TODO if theres an update go to update screen message :D
|
||||
setMessage(<Text>Opening LST scan app</Text>);
|
||||
await devDelay(3250);
|
||||
|
||||
setReady(true);
|
||||
} catch (error) {
|
||||
console.log("Startup error", error);
|
||||
setMessage(<Text>Something went wrong during startup.</Text>);
|
||||
}
|
||||
};
|
||||
|
||||
startup();
|
||||
}, [
|
||||
hasHydrated,
|
||||
hasValidSetup,
|
||||
serverPort,
|
||||
serverIp,
|
||||
router,
|
||||
setServerVersion,
|
||||
]);
|
||||
|
||||
// if (ready && !isUnlocked) {
|
||||
// return <Redirect href={"/login"} />;
|
||||
// }
|
||||
if (ready) {
|
||||
return <Redirect href="/(tabs)/scanner" />;
|
||||
if (ready && startupRoute) {
|
||||
return <Redirect href={startupRoute as any} />;
|
||||
}
|
||||
|
||||
if (ready) {
|
||||
return <Redirect href="/login" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginTop: 12,
|
||||
}}
|
||||
>
|
||||
{message}
|
||||
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
|
||||
<Text>{startupMessages[status]}</Text>
|
||||
<ActivityIndicator size="large" />
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -1,27 +1,49 @@
|
||||
import axios from "axios";
|
||||
import Constants from "expo-constants";
|
||||
|
||||
import { useRouter } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { Alert, Button, Text, View } from "react-native";
|
||||
import { Button, Text, View } from "react-native";
|
||||
import { Input } from "../components/ui/input";
|
||||
import { useAppStore } from "../hooks/useAppStore";
|
||||
import { useMobileAuthStore } from "../hooks/useMobileAuth";
|
||||
|
||||
export default function Login() {
|
||||
const { setUser } = useMobileAuthStore();
|
||||
// doing this causes rerender and sub
|
||||
//const { setUser } = useMobileAuthStore();
|
||||
const [pin, setPin] = useState("");
|
||||
const serverPort = useAppStore((s) => s.serverPort);
|
||||
const serverIp = useAppStore((s) => s.serverIp);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const onLogin = async () => {
|
||||
if (pin.length < 6) {
|
||||
console.log("pin must be min 6 ");
|
||||
}
|
||||
console.log(pin);
|
||||
try {
|
||||
const res = await axios.get(
|
||||
`http://${serverIp}:${parseInt(serverPort || "0", 10) >= 50000 ? "3000" : serverPort}/lst/api/mobile/version`,
|
||||
const res = await axios.post(
|
||||
`http://${serverIp}:${parseInt(serverPort || "0", 10) >= 50000 ? "3000" : serverPort}/lst/api/mobile/auth/pin`,
|
||||
{ pin },
|
||||
|
||||
{
|
||||
timeout: 5000,
|
||||
},
|
||||
);
|
||||
|
||||
console.log(res.data);
|
||||
} catch (error) {}
|
||||
if (res.status === 200) {
|
||||
// this way to set the user is direct and basically a 1 shot
|
||||
useMobileAuthStore.getState().setUser(res.data.data);
|
||||
return router.replace("/(tabs)/scanner");
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const config = () => {
|
||||
console.log("config");
|
||||
return router.replace("/setup");
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -43,10 +65,21 @@ export default function Login() {
|
||||
keyboardType="number-pad"
|
||||
textContentType="oneTimeCode"
|
||||
placeholder="Pin number"
|
||||
onChangeText={setPin}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<Button title="Login" onPress={onLogin} />
|
||||
<View>
|
||||
<Text>
|
||||
Warning: If you are logged into another scanner you will encounter
|
||||
scan errors, please do not try to log into more than 1 scanner at a
|
||||
time.
|
||||
</Text>
|
||||
</View>
|
||||
<View className="flex gap-2 flex-row">
|
||||
<Button title="Login" onPress={onLogin} />
|
||||
<Button title="Config" onPress={config} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ export default function Setup() {
|
||||
marginTop: "auto",
|
||||
alignItems: "center",
|
||||
padding: 10,
|
||||
marginBottom: 12,
|
||||
marginBottom: 50,
|
||||
}}
|
||||
>
|
||||
<Text className="text-sm color-[#312f2f]">
|
||||
|
||||
Reference in New Issue
Block a user