├── .eslintrc.js ├── .gitignore ├── .prettierrc.json ├── apps ├── expo │ ├── .gitignore │ ├── App.tsx │ ├── app.json │ ├── babel.config.js │ ├── index.js │ ├── metro.config.js │ ├── package.json │ └── tsconfig.json └── next │ ├── .babelrc.json │ ├── .gitignore │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ ├── _app.tsx │ ├── _document.tsx │ ├── home.tsx │ ├── index.tsx │ ├── terms.tsx │ └── user │ │ └── [id].tsx │ ├── public │ ├── favicon.ico │ └── vercel.svg │ └── tsconfig.json ├── package.json ├── packages └── app │ ├── features │ ├── auth │ │ ├── AuthContext.tsx │ │ ├── LoginScreen.tsx │ │ └── TermsScreen.tsx │ ├── home │ │ └── screen.tsx │ └── user │ │ └── detail-screen.tsx │ ├── index.ts │ ├── navigation │ └── native │ │ ├── AuthNavigator.tsx │ │ ├── HomeNavigator.tsx │ │ └── index.tsx │ ├── package.json │ ├── provider │ ├── dripsy.tsx │ ├── index.tsx │ ├── navigation │ │ ├── index.tsx │ │ └── index.web.tsx │ └── safe-area │ │ ├── index.tsx │ │ ├── index.web.tsx │ │ ├── use-safe-area.ts │ │ └── use-safe-area.web.ts │ └── rnw-overrides.tsx ├── readme.md ├── tsconfig.json ├── turbo.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | // TODO next build breaks unless I put this??? 4 | // but if I actually install it, then it breaks again??? 5 | 'plugin:@next/next/recommended', 6 | ], 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifacts 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | 61 | # Expo 62 | .expo/* 63 | web-build/ 64 | 65 | **/*/.expo 66 | 67 | **/*/.next 68 | 69 | **/*/ios 70 | **/*/android 71 | 72 | .turbo 73 | build/** -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "useTabs": false, 4 | "tabWidth": 2, 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /apps/expo/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | 34 | # node.js 35 | # 36 | node_modules/ 37 | npm-debug.log 38 | yarn-error.log 39 | 40 | # BUCK 41 | buck-out/ 42 | \.buckd/ 43 | *.keystore 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://docs.fastlane.tools/best-practices/source-control/ 51 | 52 | */fastlane/report.xml 53 | */fastlane/Preview.html 54 | */fastlane/screenshots 55 | 56 | # Bundle artifacts 57 | *.jsbundle 58 | 59 | # CocoaPods 60 | /ios/Pods/ 61 | 62 | # Expo 63 | .expo/* 64 | web-build/ 65 | 66 | # @generated expo-cli sync-e7dcf75f4e856f7b6f3239b3f3a7dd614ee755a8 67 | # The following patterns were generated by expo-cli 68 | 69 | # OSX 70 | # 71 | .DS_Store 72 | 73 | # Xcode 74 | # 75 | build/ 76 | *.pbxuser 77 | !default.pbxuser 78 | *.mode1v3 79 | !default.mode1v3 80 | *.mode2v3 81 | !default.mode2v3 82 | *.perspectivev3 83 | !default.perspectivev3 84 | xcuserdata 85 | *.xccheckout 86 | *.moved-aside 87 | DerivedData 88 | *.hmap 89 | *.ipa 90 | *.xcuserstate 91 | project.xcworkspace 92 | 93 | # Android/IntelliJ 94 | # 95 | build/ 96 | .idea 97 | .gradle 98 | local.properties 99 | *.iml 100 | *.hprof 101 | 102 | # node.js 103 | # 104 | node_modules/ 105 | npm-debug.log 106 | yarn-error.log 107 | 108 | # BUCK 109 | buck-out/ 110 | \.buckd/ 111 | *.keystore 112 | !debug.keystore 113 | 114 | # Bundle artifacts 115 | *.jsbundle 116 | 117 | # CocoaPods 118 | /ios/Pods/ 119 | 120 | # Expo 121 | .expo/ 122 | web-build/ 123 | dist/ 124 | 125 | # @end expo-cli -------------------------------------------------------------------------------- /apps/expo/App.tsx: -------------------------------------------------------------------------------- 1 | import { NativeNavigation } from 'app/navigation/native' 2 | import { Provider } from 'app/provider' 3 | 4 | export default function App() { 5 | return ( 6 | 7 | 8 | 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /apps/expo/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "solito-blank", 4 | "slug": "solito-blank", 5 | "version": "1.0.0", 6 | "sdkVersion": "44.0.0", 7 | "scheme": "solito-blank", 8 | "platforms": ["ios", "android"], 9 | "ios": { 10 | "bundleIdentifier": "com.solito.blank" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/expo/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true) 3 | return { 4 | presets: [['babel-preset-expo', { jsxRuntime: 'automatic' }]], 5 | plugins: ['react-native-reanimated/plugin'], 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/expo/index.js: -------------------------------------------------------------------------------- 1 | import { registerRootComponent } from 'expo'; 2 | 3 | import App from './App'; 4 | 5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App); 6 | // It also ensures that whether you load the app in Expo Go or in a native build, 7 | // the environment is set up appropriately 8 | registerRootComponent(App); 9 | -------------------------------------------------------------------------------- /apps/expo/metro.config.js: -------------------------------------------------------------------------------- 1 | // Learn more https://docs.expo.io/guides/customizing-metro 2 | /** 3 | * @type {import('expo/metro-config')} 4 | */ 5 | const { getDefaultConfig } = require('expo/metro-config') 6 | const path = require('path') 7 | 8 | const projectRoot = __dirname 9 | const workspaceRoot = path.resolve(__dirname, '../..') 10 | 11 | const config = getDefaultConfig(__dirname) 12 | 13 | config.watchFolders = [workspaceRoot] 14 | config.resolver.nodeModulesPath = [ 15 | path.resolve(projectRoot, 'node_modules'), 16 | path.resolve(workspaceRoot, 'node_modules'), 17 | ] 18 | 19 | config.transformer = { 20 | ...config.transformer, 21 | minifierPath: require.resolve('metro-minify-esbuild'), 22 | minifierConfig: {}, 23 | } 24 | 25 | module.exports = config 26 | -------------------------------------------------------------------------------- /apps/expo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "app": "*", 4 | "expo": "^44.0.0", 5 | "expo-splash-screen": "~0.14.1", 6 | "expo-status-bar": "~1.2.0", 7 | "react": "17.0.1", 8 | "react-dom": "17.0.1", 9 | "react-native": "0.64.3", 10 | "react-native-gesture-handler": "~2.1.0", 11 | "react-native-reanimated": "~2.3.1", 12 | "react-native-safe-area-context": "3.3.2", 13 | "react-native-web": "0.17.1" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "^7.12.9", 17 | "@types/react": "~17.0.21", 18 | "@types/react-native": "~0.64.12", 19 | "metro-minify-esbuild": "^0.1.0", 20 | "typescript": "~4.3.5" 21 | }, 22 | "scripts": { 23 | "start": "expo start --dev-client", 24 | "android": "expo run:android", 25 | "ios": "expo run:ios", 26 | "web": "expo start --web" 27 | }, 28 | "version": "1.0.0", 29 | "private": true, 30 | "name": "expo-app" 31 | } 32 | -------------------------------------------------------------------------------- /apps/expo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig" 3 | } 4 | -------------------------------------------------------------------------------- /apps/next/.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "next/babel", 4 | ["babel-preset-expo", { "jsxRuntime": "automatic" }] 5 | ], 6 | "plugins": [ 7 | ["@babel/plugin-proposal-class-properties", { "loose": true }], 8 | ["@babel/plugin-proposal-private-methods", { "loose": true }], 9 | ["@babel/plugin-proposal-private-property-in-object", { "loose": true }], 10 | "react-native-reanimated/plugin" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /apps/next/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env.local 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | -------------------------------------------------------------------------------- /apps/next/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/next/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | webpack5: true, 5 | } 6 | 7 | const { withExpo } = require('@expo/next-adapter') 8 | const withPlugins = require('next-compose-plugins') 9 | const withTM = require('next-transpile-modules')([ 10 | 'solito', 11 | 'dripsy', 12 | '@dripsy/core', 13 | 'moti', 14 | '@motify/core', 15 | '@motify/components', 16 | 'app', 17 | ]) 18 | const withFonts = require('next-fonts') 19 | 20 | module.exports = withPlugins( 21 | [withTM, withFonts, [withExpo, { projectRoot: __dirname }]], 22 | nextConfig 23 | ) 24 | -------------------------------------------------------------------------------- /apps/next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@expo/next-adapter": "^3.1.20", 13 | "app": "*", 14 | "next": "12.1.0", 15 | "next-fonts": "^1.5.1", 16 | "raf": "^3.4.1" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "17.0.21", 20 | "file-loader": "^6.2.0", 21 | "next-compose-plugins": "^2.2.1", 22 | "next-transpile-modules": "^9.0.0", 23 | "ttf-loader": "^1.0.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /apps/next/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { Provider } from 'app/provider' 2 | import Head from 'next/head' 3 | import React from 'react' 4 | import type { SolitoAppProps } from 'solito' 5 | import 'raf/polyfill' 6 | 7 | function MyApp({ Component, pageProps }: SolitoAppProps) { 8 | return ( 9 | <> 10 | 11 | Solito Example App 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ) 23 | } 24 | 25 | export default MyApp 26 | -------------------------------------------------------------------------------- /apps/next/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | export { default } from "@expo/next-adapter/document" 2 | -------------------------------------------------------------------------------- /apps/next/pages/home.tsx: -------------------------------------------------------------------------------- 1 | import { HomeScreen } from 'app/features/home/screen' 2 | 3 | export default HomeScreen 4 | -------------------------------------------------------------------------------- /apps/next/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { LoginScreen } from 'app/features/auth/LoginScreen' 2 | 3 | export default LoginScreen 4 | -------------------------------------------------------------------------------- /apps/next/pages/terms.tsx: -------------------------------------------------------------------------------- 1 | import { TermsScreen } from 'app/features/auth/TermsScreen' 2 | 3 | export default TermsScreen 4 | -------------------------------------------------------------------------------- /apps/next/pages/user/[id].tsx: -------------------------------------------------------------------------------- 1 | import { UserDetailScreen } from 'app/features/user/detail-screen' 2 | 3 | export default UserDetailScreen 4 | -------------------------------------------------------------------------------- /apps/next/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flexbox/react-native-nextjs-monorepo/9ad217ac45ac2bf06e6cbe8c074cad9dc4fb09ed/apps/next/public/favicon.ico -------------------------------------------------------------------------------- /apps/next/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /apps/next/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solito-blank", 3 | "private": true, 4 | "workspaces": [ 5 | "apps/*", 6 | "packages/*" 7 | ], 8 | "devDependencies": { 9 | "@types/react": "^17.0.39", 10 | "@types/react-native": "^0.67.2", 11 | "eslint": "8.10.0", 12 | "turbo": "^1.1.6", 13 | "typescript": "^4.6.2" 14 | }, 15 | "scripts": { 16 | "native": "cd apps/expo && expo start", 17 | "web": "cd apps/next && yarn next" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/app/features/auth/AuthContext.tsx: -------------------------------------------------------------------------------- 1 | 2 | import type { FunctionComponent } from "react"; 3 | import React, { createContext, useContext, useState } from "react"; 4 | 5 | interface AuthContextProps { 6 | user: boolean; 7 | setUser: (user: boolean) => void; 8 | } 9 | 10 | const AuthContext = createContext( 11 | {} as AuthContextProps 12 | ); 13 | 14 | export const AuthenticationProvider: FunctionComponent = ({ children }) => { 15 | const [user, setUser] = useState(false); 16 | 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | }; 23 | 24 | export const useAuthentication = () => useContext(AuthContext); 25 | -------------------------------------------------------------------------------- /packages/app/features/auth/LoginScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { StyleSheet, TouchableOpacity, Text, View, Platform } from 'react-native' 3 | import { Button, Colors, TextInput } from 'react-native-paper' 4 | import { useRouter } from 'solito/router' 5 | import { useAuthentication } from './AuthContext' 6 | 7 | export const LoginScreen = () => { 8 | const [email, setEmail] = useState('') 9 | const [password, setPassword] = useState('') 10 | const [isVisble, setIsVisible] = useState(true) 11 | 12 | const { push } = useRouter() 13 | 14 | const navigateToTerms = () => { 15 | push('/terms/') 16 | } 17 | 18 | const { setUser } = useAuthentication() 19 | function handleLogin() { 20 | setUser(true) 21 | if(Platform.OS ==='web'){ 22 | push('/home') 23 | } 24 | } 25 | 26 | function toggleSecureIcon() { 27 | setIsVisible(!isVisble) 28 | } 29 | 30 | return ( 31 | 32 | 33 | setEmail(value)} 37 | /> 38 | setPassword(value)} 43 | right={ 44 | 48 | } 49 | /> 50 | 57 | 58 | 59 | by login you accept the Terms and Conditions. 60 | 61 | 62 | 63 | 64 | ) 65 | } 66 | 67 | const styles = StyleSheet.create({ 68 | content: { 69 | padding: 16, 70 | }, 71 | submitButton: { 72 | marginVertical: 32, 73 | marginHorizontal: 16, 74 | backgroundColor: Colors.purple500, 75 | }, 76 | tocText: { 77 | textAlign: 'center', 78 | fontSize: 14, 79 | color: Colors.grey500, 80 | }, 81 | }) 82 | -------------------------------------------------------------------------------- /packages/app/features/auth/TermsScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text, Image, ScrollView } from "react-native"; 3 | import { Appbar, Colors, Title } from "react-native-paper"; 4 | import { useRouter } from "solito/router"; 5 | 6 | export function TermsScreen() { 7 | const { back } = useRouter() 8 | 9 | function goBack() { 10 | back() 11 | } 12 | 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | This Terms of Service, including all supplemental terms as amended 22 | from time to time, governs your use of the online game, service, or 23 | web site known as Star Wars: The Old Republic ("TOR" and the "TOR 24 | Services") operated by BioWare Austin LLC, its subsidiaries and 25 | affiliates (collectively, "BioWare"). Please read the Supplemental 26 | Terms which are contained in links to this Terms of Service or at the 27 | bottom of this document. Supplemental Terms and TOR Privacy Policy and 28 | other Terms are an integral part of this Terms of Service and 29 | incorporated in this document by reference. You can find the Terms of 30 | Service at the "Terms of Service" link at the bottom of the TOR 31 | website from which you may also print a copy for your records. 32 | 33 | 1. Account 34 | 35 | A TOR Account ("Account") may be required to access and use TOR 36 | Services. If you have questions about Account registration, please 37 | contact us by visiting http://www.swtor.com/support. To create a TOR 38 | Account, you must have an email address, and provide truthful and 39 | accurate information. TOR Accounts are available only to adults, or in 40 | their discretion, their minor child. If you are a minor, your parent 41 | or guardian must complete the registration process, in which case he 42 | or she takes full responsibility for all obligations under this Terms 43 | of Service. TOR Accounts will not be allowed for minors under thirteen 44 | (13) years of age, regardless of permission and/or registration by 45 | such minor's parent or guardian. You must be eligible to use the TOR 46 | Service for which you are registering. Some TOR Services may require 47 | creation of a ["User Name" or "Persona"]. [User names or Personas] are 48 | tied to your Account. You may not use a ["User Name" or "Persona"] 49 | that is used by someone else, is vulgar or offensive, or otherwise 50 | violates the Terms of Service. You are solely responsible for all 51 | activity on your Account. Your Account may be terminated if someone 52 | else uses it to engage in activity that violates the Terms of Service 53 | or is otherwise improper or illegal. You should not reveal your 54 | Account password to others. BioWare and LucasArts will not ask you to 55 | reveal your password, or ever initiate any contact with you asking for 56 | your password reminder words. 57 | 58 | 2. Service 59 | 60 | Some TOR Services require payment of a fee. You must have an Account 61 | and pay the subscription or other fees to participate in these 62 | activities. Information about subscription and other fees for TOR 63 | Services is published in the relevant pages at 64 | http://www.swtor.com/support. 65 | 66 | 3. Privacy 67 | 68 | Your privacy is important to us. Please read the TOR Privacy Policy 69 | carefully for information relating to TOR collection and use of 70 | personal information. You may access the TOR Privacy Policy by 71 | visiting “Privacy Policy” at 72 | http://www.swtor.com/legalnotices/privacypolicy. When you connect to 73 | TOR Service, we may retrieve information from the computer used to log 74 | onto the TOR Service. The information we collect may include 75 | information about the computer's hardware system and any data related 76 | to the computer's operation of the TOR Software or use of TOR 77 | Services. We will not collect any personal information about you, 78 | however, without your knowledge and consent as stated in our Privacy 79 | Policy at http://www.swtor.com/legalnotices/privacypolicy. 80 | 81 | 4. Content 82 | 83 | "Content" on TOR Services may include software, technology, text, 84 | artwork, music, sound, and other audio visual material, and the design 85 | and appearance of our websites. Content may be provided by third 86 | parties, including other users of TOR Services. BioWare and LucasArts 87 | do not pre-screen all Content and does not endorse, approve, or 88 | prescreen any Content that you and other users may contribute to TOR 89 | Services. You bear the entire risk of the completeness, accuracy or 90 | usefulness of Content found on TOR Services. BioWare and LucasArts 91 | reserves the right to remove Content that is objectionable to us for 92 | any reason. The decision to remove Content is in BioWare's and 93 | LucasArts' sole and final discretion. To the maximum extent permitted 94 | by applicable law, BioWare and LucasArts does not assume any 95 | responsibility or liability for Content that is generated by third 96 | parties or for the failure or delay in removing any such Content. 97 | 98 | 104 | 105 | May the force be with you 106 | 107 | 108 | ); 109 | } 110 | -------------------------------------------------------------------------------- /packages/app/features/home/screen.tsx: -------------------------------------------------------------------------------- 1 | import { Text, useSx, View, H1, P, Row, A } from 'dripsy' 2 | import { TextLink } from 'solito/link' 3 | import { MotiLink } from 'solito/moti' 4 | 5 | export function HomeScreen() { 6 | const sx = useSx() 7 | 8 | return ( 9 | 12 |

Welcome to Solito.

13 | 14 |

15 | Here is a basic starter to show you how you can navigate from one 16 | screen to another. This screen uses the same code on Next.js and React 17 | Native. 18 |

19 |

20 | Solito is made by{' '} 21 | 30 | Fernando Rojo 31 | 32 | . 33 |

34 |
35 | 36 | 37 | 43 | Regular Link 44 | 45 | 46 | { 49 | 'worklet' 50 | 51 | return { 52 | scale: pressed ? 0.95 : hovered ? 1.1 : 1, 53 | rotateZ: pressed ? '0deg' : hovered ? '-3deg' : '0deg', 54 | } 55 | }} 56 | transition={{ 57 | type: 'timing', 58 | duration: 150, 59 | }} 60 | > 61 | 65 | Moti Link 66 | 67 | 68 | 69 | 70 | ) 71 | } 72 | -------------------------------------------------------------------------------- /packages/app/features/user/detail-screen.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text } from 'dripsy' 2 | import { createParam } from 'solito' 3 | import { TextLink } from 'solito/link' 4 | 5 | const { useParam } = createParam<{ id: string }>() 6 | 7 | export function UserDetailScreen() { 8 | const [id] = useParam('id') 9 | 10 | return ( 11 | 12 | {`User ID: ${id}`} 15 | 16 | 👈 Go Home 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /packages/app/index.ts: -------------------------------------------------------------------------------- 1 | // leave this blank 2 | // don't re-export files from this workspace. it'll break next.js tree shaking 3 | // https://github.com/vercel/next.js/issues/12557 4 | export {} 5 | -------------------------------------------------------------------------------- /packages/app/navigation/native/AuthNavigator.tsx: -------------------------------------------------------------------------------- 1 | import { createNativeStackNavigator } from '@react-navigation/native-stack' 2 | import { LoginScreen } from 'app/features/auth/LoginScreen' 3 | import { TermsScreen } from 'app/features/auth/TermsScreen' 4 | 5 | const Stack = createNativeStackNavigator<{ 6 | login: undefined 7 | terms: undefined 8 | }>() 9 | 10 | export function AuthNavigator() { 11 | return ( 12 | 13 | 20 | 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /packages/app/navigation/native/HomeNavigator.tsx: -------------------------------------------------------------------------------- 1 | import { createNativeStackNavigator } from '@react-navigation/native-stack' 2 | import { HomeScreen } from 'app/features/home/screen' 3 | import { UserDetailScreen } from 'app/features/user/detail-screen' 4 | 5 | const Stack = createNativeStackNavigator<{ 6 | home: undefined 7 | 'user-detail': { 8 | id: string 9 | } 10 | }>() 11 | 12 | export function HomeNavigator() { 13 | return ( 14 | 15 | 22 | 29 | 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /packages/app/navigation/native/index.tsx: -------------------------------------------------------------------------------- 1 | import { createNativeStackNavigator } from '@react-navigation/native-stack' 2 | import { useAuthentication } from 'app/features/auth/AuthContext' 3 | import { LoginScreen } from 'app/features/auth/LoginScreen' 4 | import { TermsScreen } from 'app/features/auth/TermsScreen' 5 | 6 | import { HomeScreen } from '../../features/home/screen' 7 | import { UserDetailScreen } from '../../features/user/detail-screen' 8 | import { AuthNavigator } from './AuthNavigator' 9 | import { HomeNavigator } from './HomeNavigator' 10 | 11 | const Stack = createNativeStackNavigator<{ 12 | 'auth-stack': undefined 13 | 'home-stack': undefined 14 | }>() 15 | 16 | export function NativeNavigation() { 17 | const { user } = useAuthentication(); 18 | 19 | return ( 20 | 21 | {!user ? ( 22 | 23 | ) : ( 24 | 25 | )} 26 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /packages/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.0", 3 | "name": "app", 4 | "main": "index.ts", 5 | "dependencies": { 6 | "@react-navigation/native": "^6.0.8", 7 | "@react-navigation/native-stack": "^6.5.0", 8 | "dripsy": "^3.6.0", 9 | "expo-linking": "^3.0.0", 10 | "moti": "0.18.0-alpha.8", 11 | "react-native-paper": "^4.11.2", 12 | "solito": "0.0.22" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/app/provider/dripsy.tsx: -------------------------------------------------------------------------------- 1 | import { DripsyProvider, makeTheme } from 'dripsy' 2 | 3 | const theme = makeTheme({ 4 | // https://www.dripsy.xyz/usage/theming/create 5 | text: { 6 | p: { 7 | fontSize: 16, 8 | }, 9 | }, 10 | }) 11 | 12 | export function Dripsy({ children }: { children: React.ReactNode }) { 13 | return ( 14 | 19 | {children} 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /packages/app/provider/index.tsx: -------------------------------------------------------------------------------- 1 | import { AuthenticationProvider } from 'app/features/auth/AuthContext' 2 | import React from 'react' 3 | import { Dripsy } from './dripsy' 4 | import { NavigationProvider } from './navigation' 5 | 6 | export function Provider({ children }: { children: React.ReactNode }) { 7 | return ( 8 | 9 | 10 | {children} 11 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /packages/app/provider/navigation/index.tsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from '@react-navigation/native' 2 | import * as Linking from 'expo-linking' 3 | import { useMemo } from 'react' 4 | 5 | export function NavigationProvider({ 6 | children, 7 | }: { 8 | children: React.ReactElement 9 | }) { 10 | return ( 11 | ({ 14 | prefixes: [Linking.createURL('/')], 15 | config: { 16 | initialRouteName: 'login', 17 | screens: { 18 | login: '', 19 | terms: '/terms', 20 | home: '/home', 21 | 'user-detail': 'user/:id', 22 | }, 23 | }, 24 | }), 25 | [] 26 | )} 27 | > 28 | {children} 29 | 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /packages/app/provider/navigation/index.web.tsx: -------------------------------------------------------------------------------- 1 | // on Web, we don't use React Navigation, so we avoid the provider altogether 2 | // instead, we just have a no-op here 3 | // for more, see: https://solito.dev/recipes/tree-shaking 4 | 5 | export const NavigationProvider = ({ 6 | children, 7 | }: { 8 | children: React.ReactElement 9 | }) => <>{children} 10 | -------------------------------------------------------------------------------- /packages/app/provider/safe-area/index.tsx: -------------------------------------------------------------------------------- 1 | export { SafeAreaProvider as SafeArea } from 'react-native-safe-area-context' 2 | -------------------------------------------------------------------------------- /packages/app/provider/safe-area/index.web.tsx: -------------------------------------------------------------------------------- 1 | // on Web, we don't use React Navigation, so we are going to avoid the safe area provider 2 | // instead, we just have a no-op here 3 | // for more, see: https://solito.dev/recipes/tree-shaking 4 | 5 | // if you need safe area hooks yourself, you can implement this yourself 6 | // however, you may be better off using the CSS selector for env(safe-area-inset-top) on Web 7 | 8 | // for more, see the `./use-safe-area.web.ts` file 9 | 10 | export const SafeArea = ({ children }: { children: React.ReactElement }) => ( 11 | <>{children} 12 | ) 13 | -------------------------------------------------------------------------------- /packages/app/provider/safe-area/use-safe-area.ts: -------------------------------------------------------------------------------- 1 | import { useSafeAreaInsets } from 'react-native-safe-area-context' 2 | 3 | const useSafeArea = useSafeAreaInsets 4 | 5 | // `export { useSafeAreaInsets as useSafeArea }` breaks autoimport, so do this instead 6 | export { useSafeArea } 7 | -------------------------------------------------------------------------------- /packages/app/provider/safe-area/use-safe-area.web.ts: -------------------------------------------------------------------------------- 1 | // I don't use the real useSafeAreaInsets() hook, since 2 | // 1) the SafeAreaProvider forces you to render null on Web until it measures 3 | // 2) you might not need to support it, unless you're doing landscape stuff 4 | // 3) react-native-safe-area-context has a massive import on Web 5 | // see: https://github.com/th3rdwave/react-native-safe-area-context/pull/189#issuecomment-815274313 6 | // 4) most importantly, I think you can just use the env(safe-area-inset-bottom) CSS variable instead 7 | // after all, safe area code is few-and-far-between, so if you have to write some platform-speciifc code for it, 8 | // that is probably better than a massive bundle size for little benefit 9 | 10 | import type { useSafeArea as nativeHook } from './use-safe-area' 11 | 12 | const area = { 13 | bottom: 0, 14 | left: 0, 15 | right: 0, 16 | top: 0, 17 | 18 | // you could also use CSS env variables like below: 19 | // but you'll have to be sure to override the types for `useSafeArea` 20 | // and make sure to never add numbers and strings when you consue useSafeArea 21 | // just keep in mind that the env() doesn't work on older browsers I think 22 | 23 | // top: `env(safe-area-inset-top)`, 24 | // right: `env(safe-area-inset-right)`, 25 | // bottom: `env(safe-area-inset-bottom)`, 26 | // left: `env(safe-area-inset-left)`, 27 | } 28 | 29 | export function useSafeArea(): ReturnType { 30 | return area 31 | } 32 | -------------------------------------------------------------------------------- /packages/app/rnw-overrides.tsx: -------------------------------------------------------------------------------- 1 | // override react-native types with react-native-web types 2 | import 'react-native' 3 | 4 | declare module 'react-native' { 5 | interface PressableStateCallbackType { 6 | hovered?: boolean 7 | focused?: boolean 8 | } 9 | interface ViewStyle { 10 | transitionProperty?: string 11 | transitionDuration?: string 12 | } 13 | interface TextProps { 14 | accessibilityComponentType?: never 15 | accessibilityTraits?: never 16 | href?: string 17 | hrefAttrs?: { 18 | rel: 'noreferrer' 19 | target?: '_blank' 20 | } 21 | } 22 | interface ViewProps { 23 | accessibilityRole?: string 24 | href?: string 25 | hrefAttrs?: { 26 | rel: 'noreferrer' 27 | target?: '_blank' 28 | } 29 | onClick?: (e: React.MouseEvent) => void 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # React Native Next.js Monorepo 2 | 3 | > Exploring universal apps (_native and web_) —[bootstrapped with solito](https://solito.dev/) 4 | 5 | 👾 [View the website](https://example.solito.dev) 6 | 7 | - Select the folder `apps/next` as the root of your Next.js app on the Vercel setup. 8 | 9 | ## 📦 Included packages 10 | 11 | - `solito` for cross-platform navigation ([Talk at Next.js Conf](https://www.youtube.com/watch?v=0lnbdRweJtA)) 12 | - `moti` for animations 13 | - `dripsy` for theming/design (you can bring your own, too) 14 | - Expo SDK 15 | - Next.js 16 | - React Navigation 17 | 18 | ## 🗂 Folder layout 19 | 20 | - `apps` entry points for each app 21 | - `expo` 22 | - `next` 23 | 24 | - `packages` shared packages across apps 25 | - `app` you'll be importing most files from `app/` 26 | - `features` (don't use a `screens` folder. organize by feature.) 27 | - `provider` (all the providers that wrap the app, and some no-ops for Web.) 28 | - `navigation` Next.js has a `pages/` folder. React Native doesn't. This folder contains navigation-related code for RN. You may use it for any navigation code, such as custom links. 29 | 30 | You can add other folders inside of `packages/` if you know what you're doing and have a good reason to. 31 | 32 | ## 🏁 Getting started 33 | 34 | ``` 35 | yarn 36 | yarn web 37 | yarn native 38 | ``` 39 | 40 | ## 🆕 Add new dependencies 41 | 42 |
43 | Pure JS dependencies 44 | 45 | If you're installing a JavaScript-only dependency that will be used across platforms, install it in `packages/app`: 46 | 47 | ```sh 48 | cd packages/app 49 | yarn add date-fns 50 | cd ../.. 51 | yarn 52 | ``` 53 |
54 | 55 |
56 | Native dependencies 57 | 58 | If you're installing a library with any native code, you must install it in `apps/expo`: 59 | 60 | ```sh 61 | cd apps/expo 62 | yarn add react-native-reanimated 63 | 64 | cd ../.. 65 | yarn 66 | ``` 67 | 68 | You can also install the native library inside of `packages/app` if you want to get autoimport for that package inside of the `app` folder. However, you need to be careful and install the _exact_ same version in both packages. If the versions mismatch at all, you'll potentially get terrible bugs. This is a classic monorepo issue. I use `lerna-update-wizard` to help with this (you don't need to use Lerna to use that lib). 69 |
70 | 71 | ## 👏 Kudos 72 | 73 | - Fernando Rojo on Twitter: [@FernandoTheRojo](https://twitter.com/intent/follow?screen_name=fernandotherojo) 74 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strictNullChecks": true, 4 | "noUncheckedIndexedAccess": true, 5 | "paths": { 6 | "app/*": ["./packages/app/*"] 7 | } 8 | }, 9 | "extends": "expo/tsconfig.base" 10 | } 11 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "pipeline": { 3 | "build": { 4 | "dependsOn": ["^build"], 5 | "outputs": [".next/**"] 6 | } 7 | } 8 | } 9 | --------------------------------------------------------------------------------