diff --git a/README.md b/README.md index c47870e..5b39197 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ This project demonstrates how to share React Native code across multiple TV plat ### Supported Platforms -| Platform | Target Devices | -|----------|----------------| -| Vega (Kepler) | Fire TV | -| Expo TV | Android TV, Apple TV | -| Expo Web | Browser | +| Platform | Target Devices | +| ------------- | -------------------- | +| Vega (Kepler) | Fire TV | +| Expo TV | Android TV, Apple TV | +| Expo Web | Browser | ## Project Structure @@ -52,6 +52,7 @@ This project demonstrates how to share React Native code across multiple TV plat ## Prerequisites ### Core Requirements + - [Node.js](https://nodejs.org/) (v18 or higher) - [Yarn](https://yarnpkg.com/) (v4.5.0 or higher) - [Git](https://git-scm.com/) @@ -80,6 +81,15 @@ yarn # Build the Vega/Fire TV app (debug) yarn vega:build +# Run on Vega Virtual Device (Mac M-series) +yarn vega:vvd:mseries + +# Run on Vega Virtual Device (Intel Mac) +yarn vega:vvd:intel + +# Run on a Fire TV Stick (pass DSN) +yarn vega:firetv + # Prebuild Expo TV native projects yarn expotv:prebuild @@ -114,19 +124,32 @@ Run on a Vega virtual device: ```bash vega virtual-device start +# Mac M-series (aarch64) - using yarn script +yarn vega:vvd:mseries + +# Intel Mac (x86_64) - using yarn script +yarn vega:vvd:intel + +# Or directly with the Vega CLI # Mac M-series (aarch64) -vega run-app packages/vega/build/aarch64-debug/vega_aarch64.vpkg +vega run-app packages/vega/build/aarch64-debug/vega_aarch64.vpkg com.amazondeveloper.hellosharedworkspace.main -d VirtualDevice # Intel Mac (x86_64) -vega run-app packages/vega/build/x86_64-debug/vega_x86_64.vpkg +vega run-app packages/vega/build/x86_64-debug/vega_x86_64.vpkg com.amazondeveloper.hellosharedworkspace.main -d VirtualDevice ``` -Run on a Fire TV Stick: +Run on a Fire TV Stick (replace `` with your device serial number): ```bash -vega run-app packages/vega/build/armv7-release/vega_armv7.vpkg +# Using the yarn script +yarn vega:firetv + +# Or directly +vega run-app packages/vega/build/armv7-release/vega_armv7.vpkg com.amazondeveloper.hellosharedworkspace.main -d ``` +The `vega run-app` command takes the form `vega run-app -d `. The App ID is the interactive component id from `manifest.toml` (here, `com.amazondeveloper.hellosharedworkspace.main`). Use `VirtualDevice` for the VVD or the device serial number (DSN) for a Fire TV Stick. See the [Vega CLI reference](https://developer.amazon.com/docs/vega/0.22/cli-tools.html) for details. + [Fast Refresh](https://reactnative.dev/docs/fast-refresh) is available in debug builds. See [Set Up Fast Refresh](https://developer.amazon.com/docs/vega/latest/fast-refresh.html) for configuration. ### Expo TV @@ -154,14 +177,15 @@ yarn expotv:web ## Tech Stack -| | Expo TV | Vega (Fire TV) | -|---|---------|----------------| -| Framework | Expo SDK 54 | Kepler (@amazon-devices/react-native-kepler ^2.0.0) | -| React | 19.1.0 | 18.2.0 | -| React Native | react-native-tvos 0.81-stable | 0.72.0 | -| TypeScript | ~5.9.2 | 4.8.4 | +| | Expo TV | Vega (Fire TV) | +| ------------ | ----------------------------- | --------------------------------------------------- | +| Framework | Expo SDK 54 | Kepler (@amazon-devices/react-native-kepler ^2.0.0) | +| React | 19.1.0 | 18.2.0 | +| React Native | react-native-tvos 0.81-stable | 0.72.0 | +| TypeScript | ~5.9.2 | 4.8.4 | The shared package (`@multitv/shared`) provides: + - UI components: Header, HeaderLogo (with platform-specific variants), Tile, ApiDemo, IconReactNativeAnimated - HomeScreen with tile-based navigation and focus management - Scaling utilities for TV display dimensions (1920x1080 base). The scaling approach used here is simple and works for a demo, but for production apps you may want a more robust solution like responsive layouts or a design system. @@ -194,6 +218,7 @@ See [Vega CLI Installation](https://developer.amazon.com/docs/vega/latest/instal ### Fast Refresh Not Working Fast Refresh only works with debug builds: + - `vega_aarch64.vpkg` from `aarch64-debug/` for M-series Mac - `vega_x86_64.vpkg` from `x86_64-debug/` for Intel Mac - `vega_armv7.vpkg` from `armv7-debug/` for Fire TV Stick diff --git a/package.json b/package.json index 61e8a44..3f0589f 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,9 @@ "scripts": { "clean": "yarn workspaces foreach --all run clean ; rm -rf node_modules", "vega:build": "yarn workspace @multitv/vega run build:debug", + "vega:vvd:mseries": "yarn workspace @multitv/vega run vvd:mseries", + "vega:vvd:intel": "yarn workspace @multitv/vega run vvd:intel", + "vega:firetv": "yarn workspace @multitv/vega run firetv", "expotv": "yarn workspace @multitv/expotv", "expotv:prebuild": "yarn workspace @multitv/expotv run prebuild", "expotv:android": "yarn workspace @multitv/expotv run android", diff --git a/packages/shared/src/components/Tile.tsx b/packages/shared/src/components/Tile.tsx index 7f97c61..1332b50 100644 --- a/packages/shared/src/components/Tile.tsx +++ b/packages/shared/src/components/Tile.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {memo, useCallback} from 'react'; import { Image, ImageSourcePropType, @@ -10,45 +10,51 @@ import { import {scaleFontSize, scaleWidth} from '../utils/scaling'; export interface TileProps { + id: string; label: string; icon: ImageSourcePropType; isFocused: boolean; - onFocus: () => void; + onFocus: (id: string) => void; onBlur: () => void; testID?: string; accessibilityLabel?: string; hasTVPreferredFocus?: boolean; } -export const Tile = ({ - label, - icon, - isFocused, - onFocus, - onBlur, - testID, - accessibilityLabel, - hasTVPreferredFocus, -}: TileProps) => { - return ( - - - - - - {label} - - - ); -}; +export const Tile = memo( + ({ + id, + label, + icon, + isFocused, + onFocus, + onBlur, + testID, + accessibilityLabel, + hasTVPreferredFocus, + }: TileProps) => { + const handleFocus = useCallback(() => onFocus(id), [id, onFocus]); + + return ( + + + + + + {label} + + + ); + }, +); const styles = StyleSheet.create({ tile: { @@ -88,6 +94,6 @@ const styles = StyleSheet.create({ fontWeight: 'bold', textAlign: 'center', lineHeight: scaleFontSize(52), - includeFontPadding: false, + width: '100%', }, }); diff --git a/packages/shared/src/screens/HomeScreen.tsx b/packages/shared/src/screens/HomeScreen.tsx index f7e8c33..1f31682 100644 --- a/packages/shared/src/screens/HomeScreen.tsx +++ b/packages/shared/src/screens/HomeScreen.tsx @@ -17,6 +17,10 @@ export const HomeScreen = () => { setFocusedTileId(tileId); }, []); + const handleTileBlur = useCallback(() => { + setFocusedTileId('home'); + }, []); + const renderFocusedContent = () => { if (focusedTileId === 'home') { return
; @@ -49,11 +53,12 @@ export const HomeScreen = () => { {tiles.map(tile => ( handleTileFocus(tile.id)} - onBlur={() => setFocusedTileId('home')} + onFocus={handleTileFocus} + onBlur={handleTileBlur} testID={`tile-${tile.id}`} accessibilityLabel={tile.accessibilityLabel} hasTVPreferredFocus={tile.id === 'home'} diff --git a/packages/vega/package.json b/packages/vega/package.json index 6a8c273..9197318 100644 --- a/packages/vega/package.json +++ b/packages/vega/package.json @@ -7,7 +7,7 @@ ], "scripts": { "clean": "rm -rf node_modules buildinfo.json dist build", - "start": "react-native start --port 8083", + "start": "react-native start", "test": "jest --colors ", "test:snapshot": "jest --colors --updateSnapshot", "lint": "eslint src test", @@ -16,7 +16,10 @@ "build:debug": "react-native build-kepler --build-type Debug", "build:app": "npm-run-all build:release", "build": "npm-run-all build:release", - "release": "npm-run-all lint test build:app" + "release": "npm-run-all lint test build:app", + "vvd:mseries": "vega run-app build/aarch64-debug/vega_aarch64.vpkg com.amazondeveloper.hellosharedworkspace.main -d VirtualDevice", + "vvd:intel": "vega run-app build/x86_64-debug/vega_x86_64.vpkg com.amazondeveloper.hellosharedworkspace.main -d VirtualDevice", + "firetv": "vega run-app build/armv7-release/vega_armv7.vpkg com.amazondeveloper.hellosharedworkspace.main -d" }, "dependencies": { "@amazon-devices/kepler-module-resolver-preset": "^0.1.15",