diff --git a/.changeset/empty-dots-study.md b/.changeset/empty-dots-study.md new file mode 100644 index 000000000..699e2b082 --- /dev/null +++ b/.changeset/empty-dots-study.md @@ -0,0 +1,6 @@ +--- +"@osdk/create-app.template.expo.v2": patch +"@osdk/create-app": patch +--- + +Add Expo template to create-app diff --git a/.lintstagedrc.mjs b/.lintstagedrc.mjs index c703a4bc3..62d5828fe 100644 --- a/.lintstagedrc.mjs +++ b/.lintstagedrc.mjs @@ -51,7 +51,10 @@ export default { ], {}); const mrlCommands = mrlFiles.length > 0 - ? ["monorepolint check --verbose", `dprint fmt ${mrlFiles.join(" ")}`] + ? [ + "monorepolint check --verbose", + `dprint fmt ${mrlFiles.join(" ")} --allow-no-files`, + ] : []; return [...mrlCommands]; diff --git a/examples/example-expo-sdk-2.x/.env.development b/examples/example-expo-sdk-2.x/.env.development new file mode 100644 index 000000000..fc4253896 --- /dev/null +++ b/examples/example-expo-sdk-2.x/.env.development @@ -0,0 +1,3 @@ +EXPO_PUBLIC_FOUNDRY_API_URL=https://fake.palantirfoundry.com +EXPO_PUBLIC_FOUNDRY_REDIRECT_URL=http://localhost:8080/auth/callback +EXPO_PUBLIC_FOUNDRY_CLIENT_ID=123 diff --git a/examples/example-expo-sdk-2.x/.env.production b/examples/example-expo-sdk-2.x/.env.production new file mode 100644 index 000000000..93827cd99 --- /dev/null +++ b/examples/example-expo-sdk-2.x/.env.production @@ -0,0 +1,3 @@ +EXPO_PUBLIC_FOUNDRY_API_URL=https://fake.palantirfoundry.com +EXPO_PUBLIC_FOUNDRY_REDIRECT_URL=https://example.com/auth/callback +EXPO_PUBLIC_FOUNDRY_CLIENT_ID=123 diff --git a/examples/example-expo-sdk-2.x/.gitignore b/examples/example-expo-sdk-2.x/.gitignore new file mode 100644 index 000000000..832205d6b --- /dev/null +++ b/examples/example-expo-sdk-2.x/.gitignore @@ -0,0 +1,23 @@ +node_modules +.expo/ +dist +npm-debug.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision +*.orig.* +web-build/ + +# macOS +.DS_Store + +# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb +# The following patterns were generated by expo-cli + +expo-env.d.ts +# @end expo-cli + +.env +env.ts diff --git a/examples/example-expo-sdk-2.x/README.md b/examples/example-expo-sdk-2.x/README.md new file mode 100644 index 000000000..c004a5473 --- /dev/null +++ b/examples/example-expo-sdk-2.x/README.md @@ -0,0 +1,11 @@ +# example-expo + +This project was generated with [`@osdk/create-app`](https://www.npmjs.com/package/@osdk/create-app) from the `expo` template. It is built against a locally generated SDK and a non-existent Foundry stack, so it is intended for reference purposes only. + +To quickly create your own version of this template run the following command and answer the prompts based on your Developer Console application: + +``` +npm create @osdk/app@latest -- --template expo --sdkVersion 2.x +``` + +Alternatively check out the Developer Console docs for a full guide on creating and deploying frontend applications with the Ontology SDK. diff --git a/examples/example-expo-sdk-2.x/app.json b/examples/example-expo-sdk-2.x/app.json new file mode 100644 index 000000000..ae22c2f0a --- /dev/null +++ b/examples/example-expo-sdk-2.x/app.json @@ -0,0 +1,41 @@ +{ + "expo": { + "name": "example-expo-sdk-2.x", + "slug": "example-expo-sdk-2.x", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/images/icon.png", + "scheme": "my-app", + "userInterfaceStyle": "automatic", + "newArchEnabled": true, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/images/adaptive-icon.png", + "backgroundColor": "#ffffff" + } + }, + "web": { + "bundler": "metro", + "output": "static", + "favicon": "./assets/images/favicon.png" + }, + "plugins": [ + "expo-router", + [ + "expo-splash-screen", + { + "image": "./assets/images/splash-icon.png", + "imageWidth": 200, + "resizeMode": "contain", + "backgroundColor": "#ffffff" + } + ] + ], + "experiments": { + "typedRoutes": true + } + } +} diff --git a/examples/example-expo-sdk-2.x/app/(tabs)/_layout.tsx b/examples/example-expo-sdk-2.x/app/(tabs)/_layout.tsx new file mode 100644 index 000000000..7a90db99f --- /dev/null +++ b/examples/example-expo-sdk-2.x/app/(tabs)/_layout.tsx @@ -0,0 +1,60 @@ +import { Tabs } from "expo-router"; +import React from "react"; +import { Platform } from "react-native"; + +import { HapticTab } from "@/components/HapticTab"; +import { IconSymbol } from "@/components/ui/IconSymbol"; +import TabBarBackground from "@/components/ui/TabBarBackground"; +import { Colors } from "@/constants/Colors"; +import { useColorScheme } from "@/hooks/useColorScheme"; + +export default function TabLayout() { + const colorScheme = useColorScheme(); + + return ( + + ( + + ), + }} + /> + ( + + ), + }} + /> + ( + + ), + }} + /> + + ); +} diff --git a/examples/example-expo-sdk-2.x/app/(tabs)/explore.tsx b/examples/example-expo-sdk-2.x/app/(tabs)/explore.tsx new file mode 100644 index 000000000..6bcac7cf2 --- /dev/null +++ b/examples/example-expo-sdk-2.x/app/(tabs)/explore.tsx @@ -0,0 +1,159 @@ +import { StyleSheet, Image, Platform } from 'react-native'; + +import { Collapsible } from '@/components/Collapsible'; +import { ExternalLink } from '@/components/ExternalLink'; +import ParallaxScrollView from '@/components/ParallaxScrollView'; +import { ThemedText } from '@/components/ThemedText'; +import { ThemedView } from '@/components/ThemedView'; +import { IconSymbol } from '@/components/ui/IconSymbol'; +import { $Actions, $Objects, $Queries } from "@osdk/e2e.generated.catchall"; + +export default function TabTwoScreen() { + const objectApiNames = Object.keys($Objects); + const actionApiNames = Object.keys($Actions); + const queryApiNames = Object.keys($Queries); + const colors = { light: '#D0D0D0', dark: '#353636' }; + return ( + + }> + + Explore + + This app includes example code to help you get started. + + + This app is based on the default Expo template, enriched with the Ontology SDK. + app/(tabs)/login.tsx demonstrate how to use `PublicClientAuth` to login to your Foundry stack, and {' '} + app/(tabs)/index.tsx shows how to list objects in your SDK. + + + Objects ({objectApiNames.length}) + {objectApiNames.map((objectApiName) => ( + + client.ontology.objects.{objectApiName} + + ))} + + + Actions ({actionApiNames.length}) + {actionApiNames.map((actionApiName) => ( + + client.ontology.actions.{actionApiName} + + ))} + + + Queries ({queryApiNames.length}) + {queryApiNames.map((queryApiName) => ( + + client.ontology.queries.{queryApiName} + + ))} + + + + + + This app has two screens:{' '} + app/(tabs)/index.tsx and{' '} + app/(tabs)/explore.tsx + + + The layout file in app/(tabs)/_layout.tsx{' '} + sets up the tab navigator. + + + Learn more + + + + + You can open this project on Android, iOS, and the web. To open the web version, press{' '} + w in the terminal running this project. + + + + + For static images, you can use the @2x and{' '} + @3x suffixes to provide files for + different screen densities + + + + Learn more + + + + + Open app/_layout.tsx to see how to load{' '} + + custom fonts such as this one. + + + + Learn more + + + + + This template has light and dark mode support. The{' '} + useColorScheme() hook lets you inspect + what the user's current color scheme is, and so you can adjust UI colors accordingly. + + + Learn more + + + + + This template includes an example of an animated component. The{' '} + components/HelloWave.tsx component uses + the powerful react-native-reanimated{' '} + library to create a waving hand animation. + + {Platform.select({ + ios: ( + + The components/ParallaxScrollView.tsx{' '} + component provides a parallax effect for the header image. + + ), + })} + + + + ); +} + +const styles = StyleSheet.create({ + headerImage: { + color: '#808080', + bottom: -90, + left: -35, + position: 'absolute', + }, + titleContainer: { + flexDirection: 'row', + gap: 8, + }, + osdkContainer: { + display: 'flex', + paddingTop: 10, + paddingBottom: 10, + gap: 8, + }, + alignToCenter: { + alignSelf: 'center', + }, + font: { + fontFamily: 'space-mono', + }, +}); diff --git a/examples/example-expo-sdk-2.x/app/(tabs)/index.tsx b/examples/example-expo-sdk-2.x/app/(tabs)/index.tsx new file mode 100644 index 000000000..cfe396107 --- /dev/null +++ b/examples/example-expo-sdk-2.x/app/(tabs)/index.tsx @@ -0,0 +1,86 @@ +import { Image, Platform, StyleSheet } from "react-native"; + +import { HelloWave } from "@/components/HelloWave"; +import ParallaxScrollView from "@/components/ParallaxScrollView"; +import { ThemedText } from "@/components/ThemedText"; +import { ThemedView } from "@/components/ThemedView"; +import * as WebBrowser from "expo-web-browser"; + +if (Platform.OS === "web") { + WebBrowser.maybeCompleteAuthSession(); +} + +export default function HomeScreen() { + return ( + + } + > + + Welcome! + + + + Step 1: Try it + + Edit{" "} + app/(tabs)/index.tsx + {" "} + to see changes. Press{" "} + + {Platform.select({ + ios: "cmd + d", + android: "cmd + m", + web: "F12", + })} + {" "} + to open developer tools. + + + + Step 2: Explore + + Tap the Explore tab to learn more about what's included in this + starter app. + + + + Step 3: Get a fresh start + + When you're ready, run{" "} + npm run reset-project + {" "} + to get a fresh app + {" "} + directory. This will move the current{" "} + app to{" "} + app-example. + + + + ); +} + +const styles = StyleSheet.create({ + titleContainer: { + flexDirection: "row", + alignItems: "center", + gap: 8, + }, + stepContainer: { + gap: 8, + marginBottom: 8, + }, + reactLogo: { + height: 178, + width: 290, + bottom: 0, + left: 0, + position: "absolute", + }, +}); diff --git a/examples/example-expo-sdk-2.x/app/(tabs)/login.tsx b/examples/example-expo-sdk-2.x/app/(tabs)/login.tsx new file mode 100644 index 000000000..874a070fb --- /dev/null +++ b/examples/example-expo-sdk-2.x/app/(tabs)/login.tsx @@ -0,0 +1,111 @@ +import { ThemedText } from "@/components/ThemedText"; +import { ThemedView } from "@/components/ThemedView"; +import { setAuthToken } from "@/foundry/Auth"; +import { CLIENT_ID, FOUNDRY_URL } from "@/foundry/osdkConst"; +import { + exchangeCodeAsync, + makeRedirectUri, + useAuthRequest, +} from "expo-auth-session"; +import { useRouter } from "expo-router"; +import { useCallback, useEffect, useMemo } from "react"; +import React from "react"; +import { Button, StyleSheet } from "react-native"; + +// Endpoint +const discovery = { + authorizationEndpoint: `${FOUNDRY_URL}/multipass/api/oauth2/authorize`, + tokenEndpoint: `${FOUNDRY_URL}/multipass/api/oauth2/token`, +}; + +export default function Login() { + const router = useRouter(); + const redirectUri = useMemo(() => + makeRedirectUri({ + path: "", + }), []); + const [request, response, promptAsync] = useAuthRequest( + { + clientId: CLIENT_ID, + scopes: ["api:read-data", "api:write-data"], + redirectUri, + usePKCE: true, + }, + discovery, + ); + + useEffect(() => { + if (response?.type === "success") { + } + }, [response]); + + const onLogin = useCallback(async () => { + promptAsync().then((codeResponse) => { + if (codeResponse.type !== "success") { + return; + } + exchangeCodeAsync( + { + clientId: CLIENT_ID, + code: codeResponse.params.code, + redirectUri, + extraParams: request?.codeVerifier + ? { + code_verifier: request.codeVerifier, + } + : undefined, + }, + discovery, + ).then(async (tokenResponse) => { + setAuthToken(tokenResponse); + router.navigate("/(tabs)/explore"); + }); + }); + }, [promptAsync, redirectUri, request?.codeVerifier, router]); + + return ( + + +