diff --git a/apps/docs/content/guides/getting-started/tutorials/with-ionic-react.mdx b/apps/docs/content/guides/getting-started/tutorials/with-ionic-react.mdx
index 56ad0ee998..09021360b5 100644
--- a/apps/docs/content/guides/getting-started/tutorials/with-ionic-react.mdx
+++ b/apps/docs/content/guides/getting-started/tutorials/with-ionic-react.mdx
@@ -9,7 +9,7 @@ description: 'Learn how to use Supabase in your Ionic React App.'
-If you get stuck while working through this guide, refer to the [full example on GitHub](https://github.com/mhartington/supabase-ionic-react).
+If you get stuck while working through this guide, refer to the [full example on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/ionic-react-user-management).
@@ -17,11 +17,11 @@ If you get stuck while working through this guide, refer to the [full example on
## Building the app
-Let's start building the React app from scratch.
+Start building the React app from scratch.
### Initialize an Ionic React app
-We can use the [Ionic CLI](https://ionicframework.com/docs/cli) to initialize
+Use the [Ionic CLI](https://ionicframework.com/docs/cli) to initialize
an app called `supabase-ionic-react`:
```bash
@@ -30,352 +30,63 @@ ionic start supabase-ionic-react blank --type react
cd supabase-ionic-react
```
-Then let's install the only additional dependency: [supabase-js](https://github.com/supabase/supabase-js)
+Install the only additional dependency: [supabase-js](https://github.com/supabase/supabase-js)
```bash
npm install @supabase/supabase-js
```
-And finally we want to save the environment variables in a `.env`.
-All we need are the API URL and the key that you copied [earlier](#get-api-details).
+Save the environment variables in a `.env`. You need the API URL and the key that you copied [earlier](#get-api-details).
<$CodeTabs>
```bash name=.env
-VITE_SUPABASE_URL=YOUR_SUPABASE_URL
-VITE_SUPABASE_PUBLISHABLE_KEY=YOUR_SUPABASE_PUBLISHABLE_KEY
+REACT_APP_SUPABASE_URL=YOUR_SUPABASE_URL
+REACT_APP_SUPABASE_KEY=YOUR_SUPABASE_PUBLISHABLE_KEY
```
$CodeTabs>
-Now that we have the API credentials in place, let's create a helper file to initialize the Supabase client. These variables will be exposed
-on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
+With the API credentials in place, create a helper file to initialize the Supabase client. These variables will be exposed
+on the browser, and that's fine as Supabase enabled [Row Level Security](/docs/guides/auth#row-level-security) by default on databases.
-<$CodeTabs>
-
-```js name=src/supabaseClient.ts
-import { createClient } from '@supabase/supabase-js'
-
-const supabaseUrl = import.meta.env.VITE_SUPABASE_URL || ''
-const supabasePublishableKey = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY || ''
-
-export const supabase = createClient(supabaseUrl, supabasePublishableKey)
-```
-
-$CodeTabs>
+<$CodeSample
+path="/user-management/ionic-react-user-management/src/supabaseClient.ts"
+lines={[[1, -1]]}
+meta="name=src/supabaseClient.ts"
+/>
### Set up a login route
-Let's set up a React component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
-
-<$CodeTabs>
-
-```jsx name=/src/pages/Login.tsx
-import { useState } from 'react';
-import {
- IonButton,
- IonContent,
- IonHeader,
- IonInput,
- IonItem,
- IonLabel,
- IonList,
- IonPage,
- IonTitle,
- IonToolbar,
- useIonToast,
- useIonLoading,
-} from '@ionic/react';
-
-import {supabase} from '../supabaseClient'
-
-export function LoginPage() {
- const [email, setEmail] = useState('');
-
- const [showLoading, hideLoading] = useIonLoading();
- const [showToast ] = useIonToast();
- const handleLogin = async (e: React.FormEvent) => {
- console.log()
- e.preventDefault();
- await showLoading();
- try {
- await supabase.auth.signInWithOtp({
- "email": email
- });
- await showToast({ message: 'Check your email for the login link!' });
- } catch (e: any) {
- await showToast({ message: e.error_description || e.message , duration: 5000});
- } finally {
- await hideLoading();
- }
- };
- return (
-
-
-
- Login
-
-
-
-
-
-
Supabase + Ionic React
-
Sign in via magic link with your email below
-
-
-
-
-
-
- );
-}
-```
+Set up a React component to manage logins and sign ups which uses Magic Links, so users can sign in with their email without using passwords.
-$CodeTabs>
+<$CodeSample
+path="/user-management/ionic-react-user-management/src/pages/Login.tsx"
+lines={[[1, -1]]}
+meta="name=src/pages/Login.tsx"
+/>
### Account page
-After a user is signed in we can allow them to edit their profile details and manage their account.
-
-Let's create a new component for that called `Account.tsx`.
-
-<$CodeTabs>
+After a user signs in, they should be able to edit their profile details and manage their account.
-```jsx name=src/pages/Account.tsx
-import {
- IonButton,
- IonContent,
- IonHeader,
- IonInput,
- IonItem,
- IonLabel,
- IonPage,
- IonTitle,
- IonToolbar,
- useIonLoading,
- useIonToast,
- useIonRouter
-} from '@ionic/react';
-import { useEffect, useState } from 'react';
-import { supabase } from '../supabaseClient';
-import { Session } from '@supabase/supabase-js';
-
-export function AccountPage() {
- const [showLoading, hideLoading] = useIonLoading();
- const [showToast] = useIonToast();
- const [session, setSession] = useState(null)
- const router = useIonRouter();
- const [profile, setProfile] = useState({
- username: '',
- website: '',
- avatar_url: '',
- });
-
- useEffect(() => {
- const getSession = async () => {
- setSession(await supabase.auth.getSession().then((res) => res.data.session))
- }
- getSession()
- supabase.auth.onAuthStateChange((_event, session) => {
- setSession(session)
- })
- }, [])
-
- useEffect(() => {
- getProfile();
- }, [session]);
- const getProfile = async () => {
- console.log('get');
- await showLoading();
- try {
- const user = await supabase.auth.getUser();
- const { data, error, status } = await supabase
- .from('profiles')
- .select(`username, website, avatar_url`)
- .eq('id', user!.data.user?.id)
- .single();
-
- if (error && status !== 406) {
- throw error;
- }
-
- if (data) {
- setProfile({
- username: data.username,
- website: data.website,
- avatar_url: data.avatar_url,
- });
- }
- } catch (error: any) {
- showToast({ message: error.message, duration: 5000 });
- } finally {
- await hideLoading();
- }
- };
- const signOut = async () => {
- await supabase.auth.signOut();
- router.push('/', 'forward', 'replace');
- }
- const updateProfile = async (e?: any, avatar_url: string = '') => {
- e?.preventDefault();
-
- console.log('update ');
- await showLoading();
-
- try {
- const user = await supabase.auth.getUser();
-
- const updates = {
- id: user!.data.user?.id,
- ...profile,
- avatar_url: avatar_url,
- updated_at: new Date(),
- };
-
- const { error } = await supabase.from('profiles').upsert(updates);
-
- if (error) {
- throw error;
- }
- } catch (error: any) {
- showToast({ message: error.message, duration: 5000 });
- } finally {
- await hideLoading();
- }
- };
- return (
-
-
-
- Account
-
-
-
-
-
-
-
-
- Log Out
-
-
-
-
- );
-}
-```
+Create a new component for that called `Account.tsx`.
-$CodeTabs>
+<$CodeSample
+path="/user-management/ionic-react-user-management/src/pages/Account.tsx"
+lines={[[1, -1]]}
+meta="name=src/pages/Account.tsx"
+/>
### Launch!
-Now that we have all the components in place, let's update `App.tsx`:
-
-<$CodeTabs>
-
-```jsx name=src/App.tsx
-import { Redirect, Route } from 'react-router-dom'
-import { IonApp, IonRouterOutlet, setupIonicReact } from '@ionic/react'
-import { IonReactRouter } from '@ionic/react-router'
-import { supabase } from './supabaseClient'
-
-import '@ionic/react/css/ionic.bundle.css'
-
-/* Theme variables */
-import './theme/variables.css'
-import { LoginPage } from './pages/Login'
-import { AccountPage } from './pages/Account'
-import { useEffect, useState } from 'react'
-import { Session } from '@supabase/supabase-js'
-
-setupIonicReact()
-
-const App: React.FC = () => {
- const [session, setSession] = useState(null)
- useEffect(() => {
- const getSession = async () => {
- setSession(await supabase.auth.getSession().then((res) => res.data.session))
- }
- getSession()
- supabase.auth.onAuthStateChange((_event, session) => {
- setSession(session)
- })
- }, [])
- return (
-
-
-
- {
- return session ? :
- }}
- />
-
-
-
-
-
-
- )
-}
-
-export default App
-```
+Now that you have all the components in place, update `App.tsx`:
-$CodeTabs>
+<$CodeSample
+path="/user-management/ionic-react-user-management/src/App.tsx"
+lines={[[1, -1]]}
+meta="name=src/App.tsx"
+/>
Once that's done, run this in a terminal window:
@@ -403,142 +114,30 @@ npm install @ionic/pwa-elements @capacitor/camera
Ionic PWA elements is a companion package that will polyfill certain browser APIs that provide no user interface with custom Ionic UI.
-With those packages installed we can update our `index.tsx` to include an additional bootstrapping call for the Ionic PWA Elements.
-
-<$CodeTabs>
-
-```ts name=src/index.tsx
-import React from 'react'
-import ReactDOM from 'react-dom'
-import App from './App'
-import * as serviceWorkerRegistration from './serviceWorkerRegistration'
-import reportWebVitals from './reportWebVitals'
-
-import { defineCustomElements } from '@ionic/pwa-elements/loader'
-defineCustomElements(window)
-
-ReactDOM.render(
-
-
- ,
- document.getElementById('root')
-)
-
-serviceWorkerRegistration.unregister()
-reportWebVitals()
-```
+With those packages installed update `index.tsx` to include an additional bootstrapping call for the Ionic PWA Elements.
-$CodeTabs>
+<$CodeSample
+path="/user-management/ionic-react-user-management/src/index.tsx"
+lines={[[1, -1]]}
+meta="name=src/index.tsx"
+/>
Then create an `AvatarComponent`.
-<$CodeTabs>
-
-```jsx name=src/components/Avatar.tsx
-import { IonIcon } from '@ionic/react';
-import { person } from 'ionicons/icons';
-import { Camera, CameraResultType } from '@capacitor/camera';
-import { useEffect, useState } from 'react';
-import { supabase } from '../supabaseClient';
-import './Avatar.css'
-export function Avatar({
- url,
- onUpload,
-}: {
- url: string;
- onUpload: (e: any, file: string) => Promise;
-}) {
- const [avatarUrl, setAvatarUrl] = useState();
-
- useEffect(() => {
- if (url) {
- downloadImage(url);
- }
- }, [url]);
- const uploadAvatar = async () => {
- try {
- const photo = await Camera.getPhoto({
- resultType: CameraResultType.DataUrl,
- });
-
- const file = await fetch(photo.dataUrl!)
- .then((res) => res.blob())
- .then(
- (blob) =>
- new File([blob], 'my-file', { type: `image/${photo.format}` })
- );
-
- const fileName = `${Math.random()}-${new Date().getTime()}.${
- photo.format
- }`;
- const { error: uploadError } = await supabase.storage
- .from('avatars')
- .upload(fileName, file);
- if (uploadError) {
- throw uploadError;
- }
- onUpload(null, fileName);
- } catch (error) {
- console.log(error);
- }
- };
-
- const downloadImage = async (path: string) => {
- try {
- const { data, error } = await supabase.storage
- .from('avatars')
- .download(path);
- if (error) {
- throw error;
- }
- const url = URL.createObjectURL(data!);
- setAvatarUrl(url);
- } catch (error: any) {
- console.log('Error downloading image: ', error.message);
- }
- };
-
- return (
-