diff --git a/app/app.tsx b/app/app.tsx index 32c4550..cc54478 100644 --- a/app/app.tsx +++ b/app/app.tsx @@ -7,7 +7,7 @@ import { DevSettings, NativeModules, StatusBar, View, type ViewStyle } from "react-native" import { connectToServer } from "./state/connectToServer" import { useTheme, themed } from "./theme/theme" -import { useEffect, useMemo } from "react" +import { useEffect, useMemo, useState } from "react" import { TimelineScreen } from "./screens/TimelineScreen" import { useMenuItem } from "./utils/useMenuItem" import { Titlebar } from "./components/Titlebar/Titlebar" @@ -19,6 +19,7 @@ import { HelpScreen } from "./screens/HelpScreen" import { TimelineItem } from "./types" import { PortalHost } from "./components/Portal" import { StateScreen } from "./screens/StateScreen" +import { AboutModal } from "./components/AboutModal" if (__DEV__) { // This is for debugging Reactotron with ... Reactotron! @@ -31,11 +32,19 @@ function App(): React.JSX.Element { const { toggleSidebar } = useSidebar() const [activeItem, setActiveItem] = useGlobal("sidebar-active-item", "logs") const [, setTimelineItems] = withGlobal("timelineItems", []) + const [aboutVisible, setAboutVisible] = useState(false) const menuConfig = useMemo( () => ({ - remove: ["File", "Edit", "Format"], + remove: ["File", "Edit", "Format", "Reactotron > About Reactotron"], items: { + Reactotron: [ + { + label: "About Reactotron", + position: 0, + action: () => setAboutVisible(true), + }, + ], View: [ { label: "Toggle Sidebar", @@ -93,7 +102,7 @@ function App(): React.JSX.Element { ], }, }), - [toggleSidebar], + [toggleSidebar, setActiveItem], ) useMenuItem(menuConfig) @@ -131,6 +140,7 @@ function App(): React.JSX.Element { {renderActiveItem()} + setAboutVisible(false)} /> ) } diff --git a/app/components/AboutModal.tsx b/app/components/AboutModal.tsx new file mode 100644 index 0000000..c6a8ef8 --- /dev/null +++ b/app/components/AboutModal.tsx @@ -0,0 +1,154 @@ +import { + Image, + NativeModules, + Pressable, + Text, + View, + type ImageStyle, + type TextStyle, + type ViewStyle, +} from "react-native" +import { themed } from "../theme/theme" +import { Portal } from "./Portal" +import { getReactotronAppId } from "../state/connectToServer" +import Clipboard from "../native/IRClipboard/NativeIRClipboard" +import { useState } from "react" + +interface AboutModalProps { + visible: boolean + onClose: () => void +} + +export function AboutModal({ visible, onClose }: AboutModalProps) { + const [copied, setCopied] = useState(false) + if (!visible) return null + + const copyToClipboard = () => { + Clipboard.setString(reactotronAppId) + setCopied(true) + setTimeout(() => setCopied(false), 3000) + } + + const reactotronAppId = getReactotronAppId() + const appVersion: string = require("../../package.json").version + const appBuild: string = + (NativeModules as any)?.PlatformConstants?.appBuildVersion ?? + (NativeModules as any)?.PlatformConstants?.CFBundleVersion ?? + "" + + return ( + + + + + + + + Reactotron + Copyright © 2025 Infinite Red, Inc. + {`Version ${appVersion}${ + appBuild ? ` (${appBuild})` : "" + }`} + + {copied ? "Copied!" : `UUID: ${reactotronAppId}`} + + + + + + Close + + + + + + ) +} + +const $backdrop = themed(({ colors }) => ({ + position: "absolute", + top: 0, + left: 0, + right: 0, + bottom: 0, + backgroundColor: colors.background, + alignItems: "center", + justifyContent: "center", +})) + +const $backdropTouchable: ViewStyle = { + position: "absolute", + top: 0, + left: 0, + right: 0, + bottom: 0, + cursor: "default", +} + +const $card = themed(({ colors, spacing }) => ({ + width: 520, + maxWidth: "90%", + backgroundColor: colors.cardBackground, + borderColor: colors.border, + borderWidth: 1, + borderRadius: spacing.sm, + overflow: "hidden", +})) + +const $header = themed(({ spacing }) => ({ + alignItems: "center", + padding: spacing.lg, + gap: spacing.sm, +})) + +const $logo = themed(() => ({ + width: 64, + height: 64, +})) + +const $title = themed(({ typography, colors }) => ({ + fontSize: typography.heading, + color: colors.mainText, +})) + +const $bodyText = themed(({ colors }) => ({ + color: colors.mainText, +})) + +const $footer = themed(({ spacing, colors }) => ({ + borderTopColor: colors.border, + borderTopWidth: 1, + padding: spacing.md, + alignItems: "flex-end", +})) + +const $button = themed(({ colors, spacing }) => ({ + backgroundColor: colors.background, + borderColor: colors.border, + borderWidth: 1, + paddingHorizontal: spacing.md, + paddingVertical: spacing.xs, + borderRadius: spacing.xs, +})) + +const $buttonText = themed(({ colors }) => ({ + color: colors.mainText, +})) + +const $linkContainer = themed(() => ({ + cursor: "pointer", +})) + +const $uuidButton = themed(({ spacing }) => ({ + marginTop: spacing.lg, + alignItems: "center", + justifyContent: "center", + width: "90%", +})) diff --git a/app/components/Portal.tsx b/app/components/Portal.tsx index 715f7a7..24e48df 100644 --- a/app/components/Portal.tsx +++ b/app/components/Portal.tsx @@ -46,7 +46,9 @@ export function PortalHost() { // Listen for portal changes and force re-render when they occur const listener = () => forceUpdate({}) listeners.add(listener) - return () => listeners.delete(listener) + return () => { + listeners.delete(listener) + } }, []) return ( diff --git a/app/components/Sidebar/Sidebar.tsx b/app/components/Sidebar/Sidebar.tsx index c254f13..929bae5 100644 --- a/app/components/Sidebar/Sidebar.tsx +++ b/app/components/Sidebar/Sidebar.tsx @@ -20,7 +20,6 @@ export const Sidebar = () => { inputRange: [0, 1], outputRange: [COLLAPSED_WIDTH, EXPANDED_WIDTH], }) - console.log("progress", progress) return (