From 6ec08f30f0802a4d420c8e7d2638656134a8d7fd Mon Sep 17 00:00:00 2001 From: space Date: Sun, 1 Mar 2026 12:31:14 +0100 Subject: [PATCH] testing something --- functions/control/main.py | 5 +- mobile/App.tsx | 44 ++++++++- mobile/src/pages/image.tsx | 145 +++++++++++++++++++++++++++++ mobile/src/pages/index.tsx | 186 ++++++++++--------------------------- mobile/src/pages/text.tsx | 117 +++++++++++++++++++++++ mobile/src/router.tsx | 2 +- tv/src/App.tsx | 71 ++++++++++---- 7 files changed, 412 insertions(+), 158 deletions(-) create mode 100644 mobile/src/pages/image.tsx create mode 100644 mobile/src/pages/text.tsx diff --git a/functions/control/main.py b/functions/control/main.py index 0b2abdb..52cbea2 100644 --- a/functions/control/main.py +++ b/functions/control/main.py @@ -11,7 +11,8 @@ DEFAULT_STATE = { }, "image_popup": { "showing": False, - "image_url": "" + "image_url": "", + "caption": "" } } @@ -54,6 +55,7 @@ def main(args): return {"status": "success"} elif route == "push_image": image_b64 = body.get("image_b64", "") + caption = body.get("caption", "") image_bytes = base64.b64decode(image_b64) file_name = f"tv-image-{int(time.time() * 1000)}.jpg" MINIO_CLIENT.put_object( @@ -62,6 +64,7 @@ def main(args): public_url = f"https://content2.reversed.dev/{BUCKET}/{file_name}" current = _read_state() current["image_popup"]["image_url"] = public_url + current["image_popup"]["caption"] = caption current["image_popup"]["showing"] = True _write_state(current) return {"status": "success", "image_url": public_url} diff --git a/mobile/App.tsx b/mobile/App.tsx index 2240586..6381c94 100644 --- a/mobile/App.tsx +++ b/mobile/App.tsx @@ -2,14 +2,20 @@ import { StatusBar } from "expo-status-bar"; import { StyleSheet, View } from "react-native"; import { BottomNav } from "./src/components/BottomNav"; import { NotFoundPage } from "./src/pages/NotFound"; +import { ImagePage } from "./src/pages/image"; import { IndexPage } from "./src/pages/index"; +import { TextPage } from "./src/pages/text"; import { Route, RouterProvider, useRouter } from "./src/router"; interface Tab { label: string; route: Route; page: React.ComponentType; } -const TABS: Tab[] = [{ label: "Home", route: "home", page: IndexPage }]; +const TABS: Tab[] = [ + { label: "Home", route: "home", page: IndexPage }, + { label: "Text", route: "text", page: TextPage }, + { label: "Image", route: "image", page: ImagePage }, +]; export { TABS, type Tab }; function Screen() { @@ -46,3 +52,39 @@ const styles = StyleSheet.create({ backgroundColor: "#fff", }, }); + + +function Screen() { + const { route } = useRouter(); + return ( + <> + {TABS.map((tab) => { + if (tab.route === route) { + const Page = tab.page; + return ; + } + return null; + })} + {!TABS.some((tab) => tab.route === route) && } + + ); +} + +export default function App() { + return ( + + + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "#fff", + }, +}); diff --git a/mobile/src/pages/image.tsx b/mobile/src/pages/image.tsx new file mode 100644 index 0000000..c1e1081 --- /dev/null +++ b/mobile/src/pages/image.tsx @@ -0,0 +1,145 @@ +import * as ImagePicker from "expo-image-picker"; +import { useState } from "react"; +import { ActivityIndicator, Button, Image, StyleSheet, Text, TextInput, View } from "react-native"; + +const BASE_URL = + "https://shsf-api.reversed.dev/api/exec/15/1c44d8b5-4065-4a54-b259-748561021329"; + +export function ImagePage() { + const [caption, setCaption] = useState(""); + const [uploading, setUploading] = useState(false); + const [previewUri, setPreviewUri] = useState(null); + + const handleTakePhoto = async () => { + const { granted } = await ImagePicker.requestCameraPermissionsAsync(); + if (!granted) { + alert("Camera permission is required to take photos."); + return; + } + + const result = await ImagePicker.launchCameraAsync({ + mediaTypes: "images", + quality: 1, + base64: true, + }); + + if (result.canceled) return; + + const asset = result.assets[0]; + setPreviewUri(asset.uri); + setUploading(true); + + try { + const res = await fetch(`${BASE_URL}/push_image`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ image_b64: asset.base64, caption }), + }); + const data = await res.json(); + if (data.status !== "success") throw new Error(data.message ?? "Upload failed"); + } catch (error) { + console.error("Upload error:", error); + alert("Failed to upload image."); + } finally { + setUploading(false); + } + }; + + const handleDismiss = () => { + fetch(`${BASE_URL}/push_dismiss_image`, { method: "POST" }) + .then((r) => r.json()) + .then((data) => { + if (data.status !== "success") alert("Failed to dismiss image."); + }) + .catch((error) => { + console.error("Error dismissing image:", error); + alert("Error dismissing image."); + }); + }; + + return ( + + Image Popup + Show a photo on the TV with an optional caption. + + + Caption (optional) + + + + {previewUri && ( + + )} + + {uploading ? ( + + ) : ( + + + ) : ( -
- {imageUrl && ( - TV display +
+ {/* Text popup modal */} + {textState.showing && ( +
+
+

+ {textState.title} +

+
+
)} - {text && ( -

- {text} -

+ + {/* Image popup modal */} + {imagePopup.showing && imagePopup.image_url && ( +
+
+ {imagePopup.caption && ( +

+ {imagePopup.caption} +

+ )} + TV display +
+
+ )} + + {/* Background idle state */} + {!textState.showing && !imagePopup.showing && ( +
+

Waiting for content...

+
)}
)}