From 7eb8ccae48b0cc18a9dcaa9c3626a02df8e6d919 Mon Sep 17 00:00:00 2001 From: JustZvan Date: Fri, 6 Feb 2026 13:22:33 +0100 Subject: feat: initial commit! --- app/(tabs)/controls.tsx | 270 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 app/(tabs)/controls.tsx (limited to 'app/(tabs)/controls.tsx') diff --git a/app/(tabs)/controls.tsx b/app/(tabs)/controls.tsx new file mode 100644 index 0000000..ac44f1b --- /dev/null +++ b/app/(tabs)/controls.tsx @@ -0,0 +1,270 @@ +import { useEffect, useState } from "react"; +import { Pressable, Switch, View } from "react-native"; +import { ControlsData, getControlsData, updateSafetyControl } from "../../api"; +import { useDevice } from "../../lib/device"; +import { t } from "../../lib/locales"; +import { colors } from "../../lib/theme"; +import { + Card, + ConfirmDialog, + DeviceSelector, + Divider, + H2, + Muted, + Row, + Screen, + SelectDialog, +} from "../../lib/ui"; + +export default function ControlsScreen() { + const [data, setData] = useState(null); + const [state, setState] = useState>({}); + const [communication, setCommunication] = useState<"scan" | "block">("scan"); + const [communicationModalVisible, setCommunicationModalVisible] = + useState(false); + const [confirmModalVisible, setConfirmModalVisible] = useState(false); + const [pendingSelection, setPendingSelection] = useState< + "scan" | "block" | null + >(null); + + const { + devices, + selectedDevice, + selectDevice, + isLoading: isDeviceLoading, + } = useDevice(); + + useEffect(() => { + if (selectedDevice) { + getControlsData().then((d) => { + setData(d); + setState( + Object.fromEntries( + d.safetyControls.map((c) => [c.key, c.defaultValue]), + ), + ); + const blockDefault = d.safetyControls.find( + (c) => c.key === "block_strangers", + )?.defaultValue; + setCommunication(blockDefault ? "block" : "scan"); + }); + } + }, [selectedDevice]); + + const handleToggle = (key: string, value: boolean) => { + setState((prev) => ({ ...prev, [key]: value })); + updateSafetyControl(key, value); + }; + + const openCommunicationOptions = () => { + setCommunicationModalVisible(true); + }; + + const chooseCommunication = (choice: "scan" | "block") => { + if (choice === "scan") { + setCommunication("scan"); + handleToggle("block_strangers", false); + setCommunicationModalVisible(false); + } else { + // block chosen - show confirmation + setPendingSelection("block"); + setConfirmModalVisible(true); + setCommunicationModalVisible(false); + } + }; + + const confirmBlock = () => { + setCommunication("block"); + handleToggle("block_strangers", true); + setConfirmModalVisible(false); + setPendingSelection(null); + }; + + const cancelConfirm = () => { + setConfirmModalVisible(false); + setPendingSelection(null); + }; + + if (!selectedDevice && !isDeviceLoading) + return ( + + + {t("noDeviceSelected")} + + ); + + if (!data) + return ( + + + + ); + + return ( + + + + +

{t("disableBuddy")}

+ + +

{t("disableBuddy")}

+ {t("temporarilyDisablesBuddy")} + + } + right={ + handleToggle("disable_buddy", value)} + trackColor={{ false: colors.outline, true: colors.primary }} + thumbColor={colors.onPrimary} + /> + } + /> +
+ + +

{t("contentBlocking")}

+ + +

{t("adultSites")}

+ {t("blockAdultWebsites")} + + } + right={ + handleToggle("adult_sites", value)} + trackColor={{ false: colors.outline, true: colors.primary }} + thumbColor={colors.onPrimary} + /> + } + /> +
+ + +

{t("familyLink")}

+ + +

{t("antiCircumvention")}

+ {t("preventFamilyLinkBypasses")} + + } + right={ + + handleToggle("family_link_anti_circumvention", value) + } + trackColor={{ false: colors.outline, true: colors.primary }} + thumbColor={colors.onPrimary} + /> + } + /> +
+ + +

{t("communication")}

+ + {t("communicationWithStrangers")}} + right={ + + + {communication === "block" + ? t("blockAllCommunications") + : t("scanCommunicationsWithAI")} + + + } + /> + + {t("chooseHowBuddyShouldHandleStrangers")} + + + chooseCommunication("scan"), + }, + { + label: t("blockAllCommunications"), + onPress: () => chooseCommunication("block"), + destructive: true, + }, + ]} + onClose={() => setCommunicationModalVisible(false)} + /> + + +
+ + +

{t("notifications")}

+ + {t("dangerousMessages")}} + right={ + + handleToggle("notify_dangerous_messages", value) + } + trackColor={{ false: colors.outline, true: colors.primary }} + thumbColor={colors.onPrimary} + /> + } + /> + + + + {t("newContactAdded")}} + right={ + + handleToggle("notify_new_contact_added", value) + } + trackColor={{ false: colors.outline, true: colors.primary }} + thumbColor={colors.onPrimary} + /> + } + /> +
+
+ ); +} -- cgit v1.2.3