feat: add vnc app

This commit is contained in:
2024-03-18 02:33:04 +07:00
parent d1ecd6d00b
commit d3b89113ae
15 changed files with 187 additions and 31 deletions
+5 -1
View File
@@ -39,7 +39,11 @@ const RootLayout = () => {
return (
<QueryClientProvider client={queryClient}>
<StatusBar style="auto" />
<View style={cn("flex-1 bg-[#f2f7fb]", { paddingTop: insets.top })}>
<View
style={cn("flex-1 bg-[#f2f7fb] overflow-hidden", {
paddingTop: insets.top,
})}
>
<Stack
screenOptions={{ contentStyle: { backgroundColor: "#f2f7fb" } }}
/>
@@ -4,7 +4,7 @@ import api from "@/lib/api";
import { useAuth } from "@/stores/authStore";
import BackButton from "@ui/BackButton";
import Input from "@ui/Input";
import { Stack, router, useLocalSearchParams } from "expo-router";
import { Stack, useLocalSearchParams } from "expo-router";
import React, { useState } from "react";
import { useMutation, useQuery } from "react-query";
import FileDrop from "@/components/pages/files/FileDrop";
@@ -13,7 +13,6 @@ import { HStack } from "@ui/Stack";
import Button from "@ui/Button";
import { Ionicons } from "@ui/Icons";
import FileInlineViewer from "@/components/pages/files/FileInlineViewer";
import { decodeUrl, encodeUrl } from "@/lib/utils";
import { FilesContext } from "@/components/pages/files/FilesContext";
import { FileItem } from "@/types/files";
@@ -23,7 +22,6 @@ const FilesPage = () => {
path: "",
});
const [viewFile, setViewFile] = useState<FileItem | null>(null);
const searchParams = useLocalSearchParams();
const parentPath =
params.path.length > 0
? params.path.split("/").slice(0, -1).join("/")
-16
View File
@@ -1,16 +0,0 @@
import { API_BASEURL } from "@/lib/constants";
import authStore from "@/stores/authStore";
import { FileItem } from "@/types/files";
export function openFile(file: FileItem | string, dl = false) {
const url = getFileUrl(file, dl);
window.open(url, "_blank");
}
export function getFileUrl(file: FileItem | string, dl = false) {
const filepath = typeof file === "string" ? file : file.path;
const url = new URL(API_BASEURL + "/files/download" + filepath);
url.searchParams.set("token", authStore.getState().token);
dl && url.searchParams.set("dl", "true");
return url.toString();
}
+41
View File
@@ -2,6 +2,9 @@
import api from "@/lib/api";
import { showToast } from "@/stores/toastStore";
import { API_BASEURL } from "@/lib/constants";
import authStore from "@/stores/authStore";
import { FileItem } from "@/types/files";
export const wakePcUp = async () => {
try {
@@ -11,3 +14,41 @@ export const wakePcUp = async () => {
showToast("Cannot wake up the PC!", { type: "danger" });
}
};
export function openFile(file: FileItem | string, dl = false) {
const url = getFileUrl(file, dl);
window.open(url, "_blank");
}
export function getFileUrl(file: FileItem | string, dl = false) {
const filepath = typeof file === "string" ? file : file.path;
const url = new URL(API_BASEURL + "/files/download" + filepath);
url.searchParams.set("token", authStore.getState().token);
dl && url.searchParams.set("dl", "true");
return url.toString();
}
export function openFullscreen(elem: any) {
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.webkitRequestFullscreen) {
/* Safari */
elem.webkitRequestFullscreen();
} else if (elem.msRequestFullscreen) {
/* IE11 */
elem.msRequestFullscreen();
}
}
/* Close fullscreen */
export function closeFullscreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if ((document as any).webkitExitFullscreen) {
/* Safari */
(document as any).webkitExitFullscreen();
} else if ((document as any).msExitFullscreen) {
/* IE11 */
(document as any).msExitFullscreen();
}
}
+2 -6
View File
@@ -7,7 +7,7 @@ import Box from "@ui/Box";
import Text from "@ui/Text";
import React, { useEffect, useRef } from "react";
import "xterm/css/xterm.css";
import { BASEURL } from "@/lib/constants";
import { BASEURL, WS_BASEURL } from "@/lib/constants";
import { useAuth } from "@/stores/authStore";
import { Stack } from "expo-router";
import BackButton from "@ui/BackButton";
@@ -28,11 +28,7 @@ const TerminalPage = () => {
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
const baseUrl = BASEURL.replace("https://", "wss://").replace(
"http://",
"ws://"
);
const socket = new WebSocket(baseUrl + "/terminal?token=" + token);
const socket = new WebSocket(WS_BASEURL + "/terminal?token=" + token);
const attachAddon = new AttachAddon(socket);
// Attach the socket to term
+61
View File
@@ -0,0 +1,61 @@
import { WS_BASEURL } from "@/lib/constants";
import { cn } from "@/lib/utils";
import { useAuth } from "@/stores/authStore";
import BackButton from "@ui/BackButton";
import Box from "@ui/Box";
import Button from "@ui/Button";
import { Ionicons } from "@ui/Icons";
import { Stack } from "expo-router";
import React, { useEffect, useRef } from "react";
import { Platform } from "react-native";
import { VncScreen, VncScreenHandle } from "react-vnc";
import { openFullscreen } from "./lib";
const VncPage = () => {
const containerRef = useRef<HTMLDivElement>(null!);
const vncRef = useRef<VncScreenHandle>(null!);
const { token } = useAuth();
const onFullscreen = () => {
openFullscreen(containerRef.current);
};
if (Platform.OS !== "web") {
return null;
}
return (
<>
<Stack.Screen
options={{
title: "VNC",
headerLeft: () => <BackButton />,
headerRight: () => (
<Button
icon={<Ionicons name="expand-outline" />}
variant="ghost"
className="h-14 w-14"
iconClassName="text-black text-2xl"
onPress={onFullscreen}
/>
),
}}
/>
{token ? (
<div ref={containerRef} style={cn("w-full h-full")}>
<VncScreen
url={WS_BASEURL + "/vnc?token=" + token}
resizeSession
scaleViewport
background="#000000"
style={cn("w-full h-full")}
ref={vncRef}
/>
</div>
) : null}
</>
);
};
export default VncPage;
@@ -7,7 +7,7 @@ import Text from "@ui/Text";
import React, { useRef } from "react";
import Modal from "react-native-modal";
import { Video, ResizeMode } from "expo-av";
import { getFileUrl, openFile } from "@/app/apps/files/utils";
import { getFileUrl, openFile } from "@/app/apps/lib";
import { Image } from "react-native";
import AudioPlayer from "@/components/containers/AudioPlayer";
import { FileItem } from "@/types/files";
+1 -2
View File
@@ -4,11 +4,10 @@ import { FileItem } from "@/types/files";
import Text from "@ui/Text";
import List from "@ui/List";
import { Ionicons } from "@ui/Icons";
import { cn } from "@/lib/utils";
import ActionSheet from "@ui/ActionSheet";
import { HStack } from "@ui/Stack";
import Button from "@ui/Button";
import { openFile } from "@/app/apps/files/utils";
import { openFile } from "@/app/apps/lib";
type Store = {
isVisible: boolean;
+6 -1
View File
@@ -17,13 +17,18 @@ const Apps = (props: Props) => {
{
name: "Files",
icon: <Ionicons name="folder" />,
path: "files/index",
path: "files",
},
{
name: "Terminal",
icon: <Ionicons name="terminal" />,
path: "terminal",
},
{
name: "VNC",
icon: <Ionicons name="eye" />,
path: "vnc",
},
{
name: "Turn on PC",
icon: <Ionicons name="desktop" />,
+4
View File
@@ -3,3 +3,7 @@ export const BASEURL = __DEV__
: location.protocol + "//" + location.host;
export const API_BASEURL = BASEURL + "/api";
export const WS_BASEURL = BASEURL.replace("https://", "wss://").replace(
"http://",
"ws://"
);