├── .eslintrc.js ├── .gitignore ├── .prettierrc.json ├── .vscode └── settings.json ├── apps ├── expo │ ├── .gitignore │ ├── app-env.d.ts │ ├── app.config.ts │ ├── app │ │ ├── (tabs).tsx │ │ └── (tabs) │ │ │ ├── (home).tsx │ │ │ ├── (home) │ │ │ └── index.tsx │ │ │ ├── account.tsx │ │ │ ├── menus.tsx │ │ │ ├── menus │ │ │ ├── index.tsx │ │ │ ├── tweet.tsx │ │ │ └── twitter.tsx │ │ │ ├── users.tsx │ │ │ └── users │ │ │ ├── [id].tsx │ │ │ └── index.tsx │ ├── babel.config.js │ ├── google │ │ ├── GoogleService-Info.plist │ │ └── google-services.json │ ├── index.js │ ├── metro.config.js │ ├── package.json │ ├── src │ │ └── stack.tsx │ └── tsconfig.json └── next │ ├── .babelrc.json │ ├── .gitignore │ ├── app-env.d.ts │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ ├── _app.tsx │ ├── _document.tsx │ ├── account.tsx │ ├── index.tsx │ ├── menus │ │ └── twitter │ │ │ └── index.tsx │ └── users │ │ ├── [id].tsx │ │ └── index.tsx │ ├── public │ ├── favicon.ico │ ├── feed-web.png │ └── vercel.svg │ └── tsconfig.json ├── package.json ├── packages └── app │ ├── features │ ├── auth │ │ ├── context │ │ │ └── index.tsx │ │ ├── firebase │ │ │ ├── index.native.ts │ │ │ ├── index.ts │ │ │ ├── init.native.ts │ │ │ ├── init.web.ts │ │ │ └── types.ts │ │ ├── gate │ │ │ └── index.tsx │ │ └── my-account │ │ │ └── screen.tsx │ ├── home │ │ └── screen.tsx │ ├── menu │ │ ├── list-screen.tsx │ │ └── twitter │ │ │ ├── assets │ │ │ ├── feed.png │ │ │ ├── list-item.png │ │ │ ├── preview.png │ │ │ └── tweet-detail.jpeg │ │ │ ├── feed.tsx │ │ │ ├── feed.web.tsx │ │ │ └── tweet.tsx │ └── user │ │ ├── detail-screen.tsx │ │ └── list-screen.tsx │ ├── layout │ └── web │ │ └── index.tsx │ ├── package.json │ ├── provider │ ├── dripsy.tsx │ └── index.tsx │ └── rnw-overrides.d.ts ├── readme.md ├── tsconfig.json ├── turbo.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'next', 3 | settings: { 4 | next: { 5 | rootDir: 'apps/next/', 6 | }, 7 | }, 8 | root: true, 9 | } 10 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /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-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/expo/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ExpoConfig } from '@expo/config' 2 | 3 | // you can delete this function once you add your own google creds 4 | const checkIfAndroidConfigIsUpdated = () => { 5 | const android = require('./google/google-services.json') 6 | 7 | if (android.project_info.project_number == '960783729432') { 8 | console.warn(`You need to add your own Firebase credentials. Make sure you edit the following: 9 | 10 | - apps/expo/google/google-services.json 11 | - apps/expo/google/GoogleService-Info.plist 12 | - packages/app/features/auth/firebase/init.web.ts 13 | 14 | `) 15 | } 16 | } 17 | checkIfAndroidConfigIsUpdated() 18 | 19 | export default { 20 | name: 'solito-next-conf', 21 | slug: 'solito-next-conf', 22 | version: '1.0.0', 23 | scheme: 'solito-next-conf', 24 | platforms: ['ios', 'android'], 25 | ios: { 26 | bundleIdentifier: 'com.solito.next-conf', 27 | googleServicesFile: './google/GoogleService-Info.plist', 28 | }, 29 | android: { 30 | package: 'com.solito.nextconf', 31 | googleServicesFile: './google/google-services.json', 32 | }, 33 | plugins: ['@react-native-firebase/app'], 34 | } as ExpoConfig 35 | -------------------------------------------------------------------------------- /apps/expo/app/(tabs).tsx: -------------------------------------------------------------------------------- 1 | import { Provider } from 'app/provider' 2 | import { StatusBar } from 'expo-status-bar' 3 | import { Tabs } from 'expo-router' 4 | import { useDripsyTheme } from 'dripsy' 5 | import { Ionicons } from '@expo/vector-icons' 6 | import { useAuth } from 'app/features/auth/context' 7 | 8 | function MyTabs() { 9 | const { colors } = useDripsyTheme().theme 10 | const auth = useAuth() 11 | 12 | return ( 13 | 23 | 34 | ) 35 | }, 36 | }} 37 | /> 38 | 49 | ) 50 | }, 51 | tabBarButton: auth ? undefined : () => null, 52 | }} 53 | /> 54 | 65 | ) 66 | }, 67 | }} 68 | /> 69 | 80 | ) 81 | }, 82 | tabBarStyle: { 83 | display: 'none', 84 | }, 85 | }} 86 | /> 87 | 88 | ) 89 | } 90 | 91 | export default function Root() { 92 | return ( 93 | 94 | 95 | 96 | 97 | ) 98 | } 99 | -------------------------------------------------------------------------------- /apps/expo/app/(tabs)/(home).tsx: -------------------------------------------------------------------------------- 1 | import { useDripsyTheme } from 'dripsy' 2 | import { Stack } from '../../src/stack' 3 | 4 | export default function HomeTab({ children }) { 5 | return ( 6 | 7 | 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /apps/expo/app/(tabs)/(home)/index.tsx: -------------------------------------------------------------------------------- 1 | import { HomeScreen } from 'app/features/home/screen' 2 | 3 | export default HomeScreen 4 | -------------------------------------------------------------------------------- /apps/expo/app/(tabs)/account.tsx: -------------------------------------------------------------------------------- 1 | export { default } from 'app/features/auth/my-account/screen' 2 | -------------------------------------------------------------------------------- /apps/expo/app/(tabs)/menus.tsx: -------------------------------------------------------------------------------- 1 | import { Stack } from '../../src/stack' 2 | 3 | export default function MenusStack() { 4 | return ( 5 | 10 | 17 | 18 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /apps/expo/app/(tabs)/menus/index.tsx: -------------------------------------------------------------------------------- 1 | export { MenuListScreen as default } from 'app/features/menu/list-screen' 2 | -------------------------------------------------------------------------------- /apps/expo/app/(tabs)/menus/tweet.tsx: -------------------------------------------------------------------------------- 1 | export { default } from 'app/features/menu/twitter/tweet' 2 | -------------------------------------------------------------------------------- /apps/expo/app/(tabs)/menus/twitter.tsx: -------------------------------------------------------------------------------- 1 | export { default } from 'app/features/menu/twitter/feed' 2 | -------------------------------------------------------------------------------- /apps/expo/app/(tabs)/users.tsx: -------------------------------------------------------------------------------- 1 | import { useDripsyTheme } from 'dripsy' 2 | import { Stack } from '../../src/stack' 3 | 4 | export default function UserTab({ children }) { 5 | return ( 6 | 7 | 13 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /apps/expo/app/(tabs)/users/[id].tsx: -------------------------------------------------------------------------------- 1 | import { UserDetailScreen } from 'app/features/user/detail-screen' 2 | 3 | export default UserDetailScreen 4 | -------------------------------------------------------------------------------- /apps/expo/app/(tabs)/users/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from 'app/features/user/list-screen' 2 | -------------------------------------------------------------------------------- /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: [ 6 | 'react-native-reanimated/plugin', 7 | // https://expo.github.io/router/docs/intro#configure-the-babel-plugin 8 | require.resolve('expo-router/babel'), 9 | ], 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apps/expo/google/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 960783729432-oi7vjohmsjmmchok1410771rakkpkh6m.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.960783729432-oi7vjohmsjmmchok1410771rakkpkh6m 9 | API_KEY 10 | AIzaSyD_YOZWJkoezJ5lYJUnhg0xrACJRe6neFQ 11 | GCM_SENDER_ID 12 | 960783729432 13 | PLIST_VERSION 14 | 1 15 | BUNDLE_ID 16 | com.solito.next-conf 17 | PROJECT_ID 18 | solito-example 19 | STORAGE_BUCKET 20 | solito-example.appspot.com 21 | IS_ADS_ENABLED 22 | 23 | IS_ANALYTICS_ENABLED 24 | 25 | IS_APPINVITE_ENABLED 26 | 27 | IS_GCM_ENABLED 28 | 29 | IS_SIGNIN_ENABLED 30 | 31 | GOOGLE_APP_ID 32 | 1:960783729432:ios:65ff683ec3df1446b3146d 33 | 34 | -------------------------------------------------------------------------------- /apps/expo/google/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "960783729432", 4 | "project_id": "solito-example", 5 | "storage_bucket": "solito-example.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:960783729432:android:df8aeb26500d4a79b3146d", 11 | "android_client_info": { 12 | "package_name": "com.solito.nextconf" 13 | } 14 | }, 15 | "oauth_client": [ 16 | { 17 | "client_id": "960783729432-d9gs06vgugdegpjraa5mb6hclntu68tq.apps.googleusercontent.com", 18 | "client_type": 3 19 | } 20 | ], 21 | "api_key": [ 22 | { 23 | "current_key": "AIzaSyAqPvxTjO2tkgNzYjXeq1h_wAh9XUAdhFs" 24 | } 25 | ], 26 | "services": { 27 | "appinvite_service": { 28 | "other_platform_oauth_client": [ 29 | { 30 | "client_id": "960783729432-d9gs06vgugdegpjraa5mb6hclntu68tq.apps.googleusercontent.com", 31 | "client_type": 3 32 | }, 33 | { 34 | "client_id": "960783729432-oi7vjohmsjmmchok1410771rakkpkh6m.apps.googleusercontent.com", 35 | "client_type": 2, 36 | "ios_info": { 37 | "bundle_id": "com.solito.next-conf" 38 | } 39 | } 40 | ] 41 | } 42 | } 43 | } 44 | ], 45 | "configuration_version": "1" 46 | } -------------------------------------------------------------------------------- /apps/expo/index.js: -------------------------------------------------------------------------------- 1 | import 'expo-router/entry' 2 | -------------------------------------------------------------------------------- /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(projectRoot) 12 | 13 | config.watchFolders = [workspaceRoot] 14 | config.resolver.nodeModulesPaths = [ 15 | path.resolve(projectRoot, 'node_modules'), 16 | path.resolve(workspaceRoot, 'node_modules'), 17 | ] 18 | 19 | module.exports = config 20 | -------------------------------------------------------------------------------- /apps/expo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@react-native-firebase/app": "~14.9.1", 4 | "@react-native-firebase/auth": "~14.9.1", 5 | "@react-native-menu/menu": "^0.5.3", 6 | "app": "*", 7 | "expo": "~46.0.13", 8 | "expo-router": "^0.0.25", 9 | "expo-splash-screen": "~0.16.2", 10 | "expo-status-bar": "~1.4.0", 11 | "react": "18.0.0", 12 | "react-dom": "18.0.0", 13 | "react-native": "0.69.6", 14 | "react-native-gesture-handler": "~2.5.0", 15 | "react-native-ios-context-menu": "^1.14.0", 16 | "react-native-reanimated": "2.9.1", 17 | "react-native-safe-area-context": "4.3.1", 18 | "react-native-screens": "~3.15.0", 19 | "react-native-web": "~0.18.7" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.18.6", 23 | "@types/react": "~18.0.0", 24 | "@types/react-native": "~0.69.1", 25 | "typescript": "^4.6.3" 26 | }, 27 | "scripts": { 28 | "start": "expo start --dev-client", 29 | "android": "expo run:android", 30 | "ios": "expo run:ios" 31 | }, 32 | "version": "1.0.0", 33 | "private": true, 34 | "name": "expo-app" 35 | } 36 | -------------------------------------------------------------------------------- /apps/expo/src/stack.tsx: -------------------------------------------------------------------------------- 1 | import { NativeStack } from 'expo-router' 2 | import { useDripsyTheme } from 'dripsy' 3 | 4 | export function Stack({ 5 | children, 6 | ...props 7 | }: React.ComponentProps) { 8 | const { colors } = useDripsyTheme().theme 9 | return ( 10 | 24 | {children} 25 | 26 | ) 27 | } 28 | 29 | Stack.Screen = NativeStack.Screen 30 | -------------------------------------------------------------------------------- /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/app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /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 | const { withExpo } = require('@expo/next-adapter') 2 | const withFonts = require('next-fonts') 3 | const withImages = require('next-images') 4 | const withPlugins = require('next-compose-plugins') 5 | const withTM = require('next-transpile-modules')([ 6 | 'solito', 7 | 'dripsy', 8 | '@dripsy/core', 9 | 'moti', 10 | 'app', 11 | 'zeego', 12 | ]) 13 | 14 | /** @type {import('next').NextConfig} */ 15 | const nextConfig = { 16 | // reanimated (and thus, Moti) doesn't work with strict mode currently... 17 | // https://github.com/nandorojo/moti/issues/224 18 | // https://github.com/necolas/react-native-web/pull/2330 19 | // https://github.com/nandorojo/moti/issues/224 20 | // once that gets fixed, set this back to true 21 | reactStrictMode: false, 22 | webpack5: true, 23 | images: { 24 | disableStaticImages: true, 25 | }, 26 | } 27 | 28 | const transform = withPlugins([withTM, withFonts, withImages, withExpo]) 29 | 30 | module.exports = function (name, { defaultConfig }) { 31 | return transform(name, { 32 | ...defaultConfig, 33 | ...nextConfig, 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /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": "^4.0.12", 13 | "app": "*", 14 | "next": "^12.2.5", 15 | "raf": "^3.4.1" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "17.0.21", 19 | "eslint-config-next": "12.2.5", 20 | "next-compose-plugins": "^2.2.1", 21 | "next-fonts": "^1.5.1", 22 | "next-images": "^1.8.4", 23 | "next-transpile-modules": "^9.0.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /apps/next/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import 'raf/polyfill' 2 | import { WebLayout } from 'app/layout/web' 3 | 4 | const fixReanimatedIssue = () => { 5 | // FIXME remove this once this reanimated fix gets released 6 | // https://github.com/software-mansion/react-native-reanimated/issues/3355 7 | if (process.browser) { 8 | // @ts-ignore 9 | window._frameTimestamp = null 10 | } 11 | } 12 | 13 | fixReanimatedIssue() 14 | 15 | import { Provider } from 'app/provider' 16 | import Head from 'next/head' 17 | import React from 'react' 18 | import type { SolitoAppProps } from 'solito' 19 | 20 | function MyApp({ Component, pageProps }: SolitoAppProps) { 21 | return ( 22 | <> 23 | 24 | Solito Example App 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ) 38 | } 39 | 40 | export default MyApp 41 | -------------------------------------------------------------------------------- /apps/next/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | export { default } from '@expo/next-adapter/document' 2 | -------------------------------------------------------------------------------- /apps/next/pages/account.tsx: -------------------------------------------------------------------------------- 1 | export { default } from 'app/features/auth/my-account/screen' 2 | -------------------------------------------------------------------------------- /apps/next/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { HomeScreen } from 'app/features/home/screen' 2 | 3 | export default HomeScreen 4 | -------------------------------------------------------------------------------- /apps/next/pages/menus/twitter/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from 'app/features/menu/twitter/feed' 2 | -------------------------------------------------------------------------------- /apps/next/pages/users/[id].tsx: -------------------------------------------------------------------------------- 1 | import { UserDetailScreen } from 'app/features/user/detail-screen' 2 | 3 | export default UserDetailScreen 4 | -------------------------------------------------------------------------------- /apps/next/pages/users/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from 'app/features/user/list-screen' 2 | -------------------------------------------------------------------------------- /apps/next/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nandorojo/nextjs-conf-22-example/3701060984b9aef7d277eeeb80328ae8a25d366f/apps/next/public/favicon.ico -------------------------------------------------------------------------------- /apps/next/public/feed-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nandorojo/nextjs-conf-22-example/3701060984b9aef7d277eeeb80328ae8a25d366f/apps/next/public/feed-web.png -------------------------------------------------------------------------------- /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", "app-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solito-expo-router", 3 | "private": true, 4 | "workspaces": [ 5 | "apps/*", 6 | "packages/*" 7 | ], 8 | "devDependencies": { 9 | "@types/react": "^18.0.17", 10 | "@types/react-native": "^0.69.5", 11 | "eslint": "^8.21.0", 12 | "turbo": "^1.4.2" 13 | }, 14 | "scripts": { 15 | "native": "cd apps/expo && expo start", 16 | "web": "cd apps/next && yarn next" 17 | }, 18 | "resolutions": { 19 | "metro": "0.72.3", 20 | "typescript": "~4.4" 21 | }, 22 | "nohoist": [ 23 | "**/expo-router", 24 | "**/expo-router/**" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /packages/app/features/auth/context/index.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useSyncExternalStore } from 'react' 2 | import { getCurrentUser, onAuthStateChanged } from '../firebase' 3 | import { Firebase } from '../firebase/types' 4 | 5 | const AuthContext = createContext(null) 6 | 7 | export function AuthProvider({ 8 | children, 9 | ...props 10 | }: { 11 | children: React.ReactNode 12 | onAuthStateChanged?: Parameters[0] 13 | }) { 14 | const store = useSyncExternalStore( 15 | (callback) => { 16 | const remove = onAuthStateChanged((user) => { 17 | callback() 18 | props.onAuthStateChanged?.(user) 19 | }) 20 | 21 | return () => remove() 22 | }, 23 | getCurrentUser, 24 | () => null 25 | ) 26 | 27 | return {children} 28 | } 29 | 30 | export function useAuth() { 31 | return useContext(AuthContext) 32 | } 33 | -------------------------------------------------------------------------------- /packages/app/features/auth/firebase/index.native.ts: -------------------------------------------------------------------------------- 1 | import Constants from 'expo-constants' 2 | import { Firebase } from './types' 3 | 4 | const isExpoGo = Constants.appOwnership == 'expo' 5 | 6 | let exports: Firebase 7 | 8 | // here we check if the user has properly set up their native app 9 | // if not, we fall back to firebase JS 10 | 11 | if (isExpoGo) { 12 | exports = require('./init.web') 13 | } else { 14 | exports = require('./init.native') 15 | } 16 | 17 | module.exports = exports 18 | -------------------------------------------------------------------------------- /packages/app/features/auth/firebase/index.ts: -------------------------------------------------------------------------------- 1 | export * from './init.web' 2 | -------------------------------------------------------------------------------- /packages/app/features/auth/firebase/init.native.ts: -------------------------------------------------------------------------------- 1 | import auth from '@react-native-firebase/auth' 2 | import { Firebase } from './types' 3 | 4 | const getIsSignedIn: Firebase['getIsSignedIn'] = () => 5 | Boolean(auth().currentUser) 6 | 7 | const signOut: Firebase['signOut'] = () => auth().signOut() 8 | 9 | const signInAnonymously: Firebase['signInAnonymously'] = async () => { 10 | return (await auth().signInAnonymously()).user 11 | } 12 | 13 | const onAuthStateChanged: Firebase['onAuthStateChanged'] = (callback) => { 14 | return auth().onAuthStateChanged(callback) 15 | } 16 | 17 | const getCurrentUser: Firebase['getCurrentUser'] = () => auth().currentUser 18 | 19 | export { 20 | getIsSignedIn, 21 | signOut, 22 | signInAnonymously, 23 | onAuthStateChanged, 24 | getCurrentUser, 25 | } 26 | -------------------------------------------------------------------------------- /packages/app/features/auth/firebase/init.web.ts: -------------------------------------------------------------------------------- 1 | // please note that firebase auth adds about 30kb to your bundle size on Web 2 | import { initializeApp } from 'firebase/app' 3 | import { 4 | initializeAuth, 5 | browserPopupRedirectResolver, 6 | browserLocalPersistence, 7 | signInAnonymously as signInAnonymouslyFirebase, 8 | onAuthStateChanged as onAuthStateChangedFirebase, 9 | } from 'firebase/auth' 10 | import { Firebase } from './types' 11 | 12 | let auth: ReturnType 13 | 14 | if (typeof window !== 'undefined') { 15 | const firebaseApp = initializeApp({ 16 | apiKey: 'AIzaSyAQZ1A-bJMQqjdzNQhRPkbA7swEFnwUS_w', 17 | authDomain: 'solito-example.firebaseapp.com', 18 | projectId: 'solito-example', 19 | storageBucket: 'solito-example.appspot.com', 20 | messagingSenderId: '960783729432', 21 | appId: '1:960783729432:web:f2052cb298f0fc7bb3146d', 22 | }) 23 | 24 | auth = initializeAuth(firebaseApp, { 25 | persistence: browserLocalPersistence, 26 | }) 27 | } 28 | 29 | const getIsSignedIn: Firebase['getIsSignedIn'] = () => 30 | Boolean(auth?.currentUser) 31 | 32 | const signOut: Firebase['signOut'] = () => auth.signOut() 33 | 34 | const signInAnonymously: Firebase['signInAnonymously'] = async () => { 35 | return (await signInAnonymouslyFirebase(auth)).user 36 | } 37 | 38 | const onAuthStateChanged: Firebase['onAuthStateChanged'] = (callback) => { 39 | return onAuthStateChangedFirebase(auth, callback) 40 | } 41 | 42 | const getCurrentUser: Firebase['getCurrentUser'] = () => auth.currentUser 43 | 44 | export { 45 | getIsSignedIn, 46 | signInAnonymously, 47 | signOut, 48 | onAuthStateChanged, 49 | getCurrentUser, 50 | } 51 | -------------------------------------------------------------------------------- /packages/app/features/auth/firebase/types.ts: -------------------------------------------------------------------------------- 1 | import type * as firebase from 'firebase/auth' 2 | 3 | type User = Pick 4 | 5 | export type Firebase = { 6 | getIsSignedIn: () => boolean 7 | signInAnonymously: () => Promise 8 | signOut: () => Promise 9 | onAuthStateChanged: ( 10 | callback: (user: { uid: string } | null) => void 11 | ) => () => void 12 | getCurrentUser: () => User | null 13 | } 14 | -------------------------------------------------------------------------------- /packages/app/features/auth/gate/index.tsx: -------------------------------------------------------------------------------- 1 | import { Text, View } from '@dripsy/core' 2 | import { useAuth } from '../context' 3 | import { signInAnonymously } from '../firebase' 4 | 5 | export function AuthGate({ 6 | children, 7 | }: { 8 | children: React.ReactNode | ((user: { uid: string }) => React.ReactNode) 9 | }) { 10 | const auth = useAuth() 11 | 12 | if (!auth) { 13 | return ( 14 | <> 15 | 23 | Sign In 24 | 25 | 26 | ) 27 | } 28 | 29 | return <>{typeof children == 'function' ? children(auth) : children} 30 | } 31 | -------------------------------------------------------------------------------- /packages/app/features/auth/my-account/screen.tsx: -------------------------------------------------------------------------------- 1 | import { Text, View } from 'dripsy' 2 | import { signOut } from '../firebase' 3 | import { AuthGate } from '../gate' 4 | 5 | export default function MyAccountScreen() { 6 | return ( 7 | 8 | {({ uid }) => ( 9 | 17 | Welcome, {uid}. 18 | 19 | Sign Out 20 | 21 | 22 | )} 23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /packages/app/features/home/screen.tsx: -------------------------------------------------------------------------------- 1 | import { Text, View } from 'dripsy' 2 | import { Link } from 'solito/link' 3 | 4 | export function HomeScreen() { 5 | return ( 6 | 14 | 15 | 22 | Hello Solito 23 | 24 | 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /packages/app/features/menu/list-screen.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text } from 'dripsy' 2 | import { Link } from 'solito/link' 3 | import { Fragment } from 'react' 4 | import { Ionicons } from '@expo/vector-icons' 5 | 6 | const menus = [ 7 | { 8 | name: 'Twitter', 9 | href: '/menus/twitter', 10 | icon: , 11 | }, 12 | ] 13 | 14 | export function MenuListScreen() { 15 | return ( 16 | 17 | {menus.map((menu) => ( 18 | 19 | 20 | 29 | {menu.icon} 30 | {menu.name} 31 | 32 | 33 | 34 | ))} 35 | 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /packages/app/features/menu/twitter/assets/feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nandorojo/nextjs-conf-22-example/3701060984b9aef7d277eeeb80328ae8a25d366f/packages/app/features/menu/twitter/assets/feed.png -------------------------------------------------------------------------------- /packages/app/features/menu/twitter/assets/list-item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nandorojo/nextjs-conf-22-example/3701060984b9aef7d277eeeb80328ae8a25d366f/packages/app/features/menu/twitter/assets/list-item.png -------------------------------------------------------------------------------- /packages/app/features/menu/twitter/assets/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nandorojo/nextjs-conf-22-example/3701060984b9aef7d277eeeb80328ae8a25d366f/packages/app/features/menu/twitter/assets/preview.png -------------------------------------------------------------------------------- /packages/app/features/menu/twitter/assets/tweet-detail.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nandorojo/nextjs-conf-22-example/3701060984b9aef7d277eeeb80328ae8a25d366f/packages/app/features/menu/twitter/assets/tweet-detail.jpeg -------------------------------------------------------------------------------- /packages/app/features/menu/twitter/feed.tsx: -------------------------------------------------------------------------------- 1 | import { Image as Img, View } from '@dripsy/core' 2 | import { StatusBar, useWindowDimensions } from 'react-native' 3 | import { Link } from 'solito/link' 4 | import { useRouter } from 'solito/router' 5 | 6 | import * as ContextMenu from 'zeego/context-menu' 7 | 8 | export default function TwitterFeed() { 9 | const { push } = useRouter() 10 | return ( 11 | <> 12 |