diff --git a/OwnTube.tv/app/(home)/index.tsx b/OwnTube.tv/app/(home)/index.tsx index c9bde11..85ada03 100644 --- a/OwnTube.tv/app/(home)/index.tsx +++ b/OwnTube.tv/app/(home)/index.tsx @@ -1,13 +1,13 @@ import { Link, useFocusEffect, useLocalSearchParams, useNavigation, useRouter } from "expo-router"; import { ROUTES, SOURCES, STORAGE } from "../../types"; import { readFromAsyncStorage, writeToAsyncStorage } from "../../utils"; -import { Loader } from "../../components"; +import { DeviceCapabilitiesModal, Loader } from "../../components"; import { useCallback, useState } from "react"; import Head from "expo-router/head"; import { HomeScreen } from "../../screens"; import { Feather } from "@expo/vector-icons"; import { useTheme } from "@react-navigation/native"; -import { StyleSheet } from "react-native"; +import { StyleSheet, View } from "react-native"; export default function index() { const router = useRouter(); @@ -38,9 +38,12 @@ export default function index() { navigation.setOptions({ title: `OwnTube.tv@${backend}`, headerRight: () => ( - - - + + + + + + ), }); } @@ -68,4 +71,5 @@ const styles = StyleSheet.create({ headerButton: { paddingHorizontal: 11, }, + headerControls: { alignItems: "center", flexDirection: "row", paddingRight: 11 }, }); diff --git a/OwnTube.tv/components/BuildInfoToast.tsx b/OwnTube.tv/components/BuildInfoToast.tsx index d9859ac..c76ad8d 100644 --- a/OwnTube.tv/components/BuildInfoToast.tsx +++ b/OwnTube.tv/components/BuildInfoToast.tsx @@ -2,6 +2,7 @@ import { Linking, StyleSheet, View } from "react-native"; import { Typography } from "./Typography"; import build_info from "../build-info.json"; import { useTheme } from "@react-navigation/native"; +import { removeSecondsFromISODate } from "../utils"; export const BuildInfoToast = () => { const { colors } = useTheme(); @@ -13,7 +14,7 @@ export const BuildInfoToast = () => { Linking.openURL(build_info.COMMIT_URL)}> {build_info.GITHUB_SHA_SHORT} {" "} - built at {`${build_info.BUILD_TIMESTAMP.slice(0, build_info.BUILD_TIMESTAMP.lastIndexOf(":"))}Z`} by{" "} + built at {removeSecondsFromISODate(build_info.BUILD_TIMESTAMP)} by{" "} { + const { deviceCapabilities } = useAppConfigContext(); + const { colors } = useTheme(); + + const handleCopyToClipboard = async () => { + await Clipboard.setStringAsync(JSON.stringify(deviceCapabilities)); + }; + + return ( + + + Device Capability info: + + + + + + + Player implementation: + {deviceCapabilities.playerImplementation} + + + Device type: + {deviceCapabilities.deviceType} + + + Operating system: + {deviceCapabilities.OS} + + {deviceCapabilities.browser && ( + + Browser: + {deviceCapabilities.browser} + + )} + {deviceCapabilities.device && ( + + Device: + {deviceCapabilities.device} + + )} + + Screen dimensions: + {deviceCapabilities.dimensions} + + + Orientation: + {deviceCapabilities.orientation} + + + ); +}; + +const styles = StyleSheet.create({ + modalContainer: { borderRadius: 8, minWidth: "50%", padding: 16 }, + modalHeader: { flexDirection: "row", gap: 16, justifyContent: "space-between" }, + row: { flexWrap: "wrap", justifyContent: "space-between", marginVertical: 8, width: "100%" }, +}); diff --git a/OwnTube.tv/components/DeviceCapabilitiesModal.tsx b/OwnTube.tv/components/DeviceCapabilitiesModal.tsx index 1030a8b..dc794e8 100644 --- a/OwnTube.tv/components/DeviceCapabilitiesModal.tsx +++ b/OwnTube.tv/components/DeviceCapabilitiesModal.tsx @@ -1,12 +1,9 @@ import { Ionicons } from "@expo/vector-icons"; import { useTheme } from "@react-navigation/native"; -import { Modal, Pressable, StyleSheet, View } from "react-native"; +import { Modal, Pressable, StyleSheet } from "react-native"; import { useState } from "react"; -import { Typography } from "./Typography"; -import { useAppConfigContext } from "../contexts"; -import { Spacer } from "./shared/Spacer"; -import * as Clipboard from "expo-clipboard"; import { colors } from "../colors"; +import { DeviceCapabilities } from "./DeviceCapabilities"; export const DeviceCapabilitiesModal = () => { const { colors } = useTheme(); @@ -14,7 +11,6 @@ export const DeviceCapabilitiesModal = () => { const toggleModal = () => { setModalVisible((prevState) => !prevState); }; - const { deviceCapabilities } = useAppConfigContext(); return ( <> @@ -23,47 +19,7 @@ export const DeviceCapabilitiesModal = () => { - - - Device Capability info: - Clipboard.setStringAsync(JSON.stringify(deviceCapabilities))}> - - - - - - Player implementation: - {deviceCapabilities.playerImplementation} - - - Device type: - {deviceCapabilities.deviceType} - - - Operating system: - {deviceCapabilities.OS} - - {deviceCapabilities.browser && ( - - Browser: - {deviceCapabilities.browser} - - )} - {deviceCapabilities.device && ( - - Device: - {deviceCapabilities.device} - - )} - - Screen dimensions: - {deviceCapabilities.dimensions} - - - Orientation: - {deviceCapabilities.orientation} - - + @@ -72,9 +28,6 @@ export const DeviceCapabilitiesModal = () => { const styles = StyleSheet.create({ modal: { height: "100%" }, - modalContainer: { borderRadius: 8, minWidth: "50%", padding: 16 }, - modalHeader: { flexDirection: "row", gap: 16, justifyContent: "space-between" }, - row: { flexWrap: "wrap", justifyContent: "space-between", marginVertical: 8, width: "100%" }, showModalBtn: { alignItems: "center", backgroundColor: colors._50percentBlackTint, diff --git a/OwnTube.tv/components/VideoView/VideoView.tsx b/OwnTube.tv/components/VideoView/VideoView.tsx index a70c78f..a0a9b28 100644 --- a/OwnTube.tv/components/VideoView/VideoView.tsx +++ b/OwnTube.tv/components/VideoView/VideoView.tsx @@ -29,9 +29,10 @@ const VideoView = ({ uri, testID, handleSetTimeStamp, timestamp }: VideoViewProp const handlePlaybackStatusUpdate = (status: AVPlaybackStatus) => { if (status?.isLoaded) { setPlaybackStatus(status); - setPlayerImplementation( - status.androidImplementation ? `Android ${status.androidImplementation}` : "iOS Native player", - ); + + if (status.androidImplementation) { + setPlayerImplementation(`Android ${status.androidImplementation}`); + } } else if (status?.error) { console.error(status.error); } diff --git a/OwnTube.tv/components/VideoView/VideoView.web.tsx b/OwnTube.tv/components/VideoView/VideoView.web.tsx index 0f5954d..d41c336 100644 --- a/OwnTube.tv/components/VideoView/VideoView.web.tsx +++ b/OwnTube.tv/components/VideoView/VideoView.web.tsx @@ -6,7 +6,6 @@ import { VideoViewProps } from "./VideoView"; import { styles } from "./styles"; import "./styles.web.css"; import videojs from "video.js"; -import { useAppConfigContext } from "../../contexts"; declare const window: { videojs: typeof videojs; @@ -25,7 +24,6 @@ const VideoView = ({ uri, testID, handleSetTimeStamp, timestamp }: VideoViewProp duration: 1, playableDuration: 0, }); - const { setPlayerImplementation } = useAppConfigContext(); const updatePlaybackStatus = (updatedStatus: Partial) => { setPlaybackStatus((prev) => ({ ...prev, ...updatedStatus })); @@ -114,7 +112,6 @@ const VideoView = ({ uri, testID, handleSetTimeStamp, timestamp }: VideoViewProp useEffect(() => { if (!videojs) return; - setPlayerImplementation("Web video.js"); if (!playerRef.current) { const videoElement = document.createElement("video-js"); diff --git a/OwnTube.tv/components/index.ts b/OwnTube.tv/components/index.ts index 1c678b1..8a5a20b 100644 --- a/OwnTube.tv/components/index.ts +++ b/OwnTube.tv/components/index.ts @@ -9,6 +9,8 @@ export * from "./VideoView/VideoView"; export * from "./VideoControlsOverlay"; export * from "./Loader"; export * from "./DeviceCapabilitiesModal"; +export * from "./DeviceCapabilities"; export * from "./BuildInfoToast"; export * from "./ClickableHeaderText"; export * from "./ResumeWatching"; +export * from "./ViewHistory"; diff --git a/OwnTube.tv/hooks/useDeviceCapabilities.ts b/OwnTube.tv/hooks/useDeviceCapabilities.ts index 8113f35..b9ae1aa 100644 --- a/OwnTube.tv/hooks/useDeviceCapabilities.ts +++ b/OwnTube.tv/hooks/useDeviceCapabilities.ts @@ -3,6 +3,7 @@ import * as Device from "expo-device"; import { DeviceType } from "expo-device"; import { Platform, useWindowDimensions } from "react-native"; import UAParser from "ua-parser-js"; +import { capitalize } from "../utils"; export interface DeviceCapabilities { playerImplementation: string; @@ -15,7 +16,12 @@ export interface DeviceCapabilities { } export const useDeviceCapabilities = () => { - const [playerImplementation, setPlayerImplementation] = useState(""); + const [playerImplementation, setPlayerImplementation] = useState( + Platform.select({ + web: "Web video.js", + default: capitalize(Platform.OS), + }), + ); const { height, width } = useWindowDimensions(); const getBrowserInfo = () => { diff --git a/OwnTube.tv/screens/SettingsScreen/index.tsx b/OwnTube.tv/screens/SettingsScreen/index.tsx index 25d3f89..1f51ecd 100644 --- a/OwnTube.tv/screens/SettingsScreen/index.tsx +++ b/OwnTube.tv/screens/SettingsScreen/index.tsx @@ -1,10 +1,9 @@ import { View, Switch } from "react-native"; -import { SourceSelect, Typography } from "../../components"; +import { SourceSelect, Typography, ViewHistory, DeviceCapabilities } from "../../components"; import { useAppConfigContext, useColorSchemeContext } from "../../contexts"; import { Screen } from "../../layouts"; import { styles } from "./styles"; import { useTheme } from "@react-navigation/native"; -import { ViewHistory } from "../../components/ViewHistory"; export const SettingsScreen = () => { const { isDebugMode, setIsDebugMode } = useAppConfigContext(); @@ -13,15 +12,20 @@ export const SettingsScreen = () => { return ( - - Debug logging - + + + + + Debug logging + + + + Toggle Theme + + + + - - Toggle Theme - - - ); diff --git a/OwnTube.tv/screens/SettingsScreen/styles.ts b/OwnTube.tv/screens/SettingsScreen/styles.ts index 87e9681..64eb414 100644 --- a/OwnTube.tv/screens/SettingsScreen/styles.ts +++ b/OwnTube.tv/screens/SettingsScreen/styles.ts @@ -6,8 +6,11 @@ export const styles = StyleSheet.create({ gap: 10, padding: 20, }, + deviceInfoAndToggles: { flexDirection: "row", flexWrap: "wrap", gap: 16, width: "100%" }, option: { + alignItems: "center", flexDirection: "row", gap: 5, }, + togglesContainer: { flex: 1, minWidth: 200 }, }); diff --git a/OwnTube.tv/utils/common.test.tsx b/OwnTube.tv/utils/common.test.tsx new file mode 100644 index 0000000..bafd5dd --- /dev/null +++ b/OwnTube.tv/utils/common.test.tsx @@ -0,0 +1,7 @@ +import { capitalize } from "./common"; + +describe("capitalize", () => { + it("should capitalize a string", () => { + expect(capitalize("testing")).toBe("Testing"); + }); +}); diff --git a/OwnTube.tv/utils/common.ts b/OwnTube.tv/utils/common.ts index faf47eb..825304d 100644 --- a/OwnTube.tv/utils/common.ts +++ b/OwnTube.tv/utils/common.ts @@ -7,3 +7,7 @@ export const getThumbnailDimensions = () => { return { width, height }; }; + +export const capitalize = (input: string) => { + return input.charAt(0).toUpperCase() + input.slice(1); +}; diff --git a/OwnTube.tv/utils/time.test.ts b/OwnTube.tv/utils/time.test.ts index 1c922f5..f38e199 100644 --- a/OwnTube.tv/utils/time.test.ts +++ b/OwnTube.tv/utils/time.test.ts @@ -1,4 +1,4 @@ -import { getHumanReadableDuration } from "./time"; +import { getHumanReadableDuration, removeSecondsFromISODate } from "./time"; describe("getHumanReadableDuration", () => { it("should output 40m30s for 2_430_000 ms input", () => { @@ -14,3 +14,9 @@ describe("getHumanReadableDuration", () => { expect(getHumanReadableDuration(undefined)).not.toBe("NaN:NaN"); }); }); + +describe("removeSecondsFromISODate", () => { + it("should remove seconds from an ISO formatted date", () => { + expect(removeSecondsFromISODate("1970-01-01T00:00:00Z")).toBe("1970-01-01T00:00Z"); + }); +}); diff --git a/OwnTube.tv/utils/time.ts b/OwnTube.tv/utils/time.ts index 2338a95..efa9f3d 100644 --- a/OwnTube.tv/utils/time.ts +++ b/OwnTube.tv/utils/time.ts @@ -7,3 +7,7 @@ export const getHumanReadableDuration = (ms: number = 0) => { return hours > 0 ? `${String(hours).padStart(2, "0")}:${mmss}` : mmss; }; + +export const removeSecondsFromISODate = (isoDate: string) => { + return `${isoDate.slice(0, isoDate.lastIndexOf(":"))}Z`; +};