This commit is contained in:
Copple committed 2022-04-26 10:15:53 +02:00
1 parent cfc6c67444
commit d9e90dd046
17 files changed
+236 -233

No files matched your search

+79 -65
View File
@@ -23,11 +23,11 @@ First off, thanks for your interest in Supabase and for wanting to contribute! b
Before you can build Supabase, you must install and configure the following dependencies on your
machine:
* [Git](http://git-scm.com/)
- [Git](http://git-scm.com/)
* [Node.js v16.x (LTS)](http://nodejs.org)
- [Node.js v16.x (LTS)](http://nodejs.org)
* [npm](https://www.npmjs.com/) version 7+.
- [npm](https://www.npmjs.com/) version 7+.
### Forking Supabase on GitHub
@@ -38,6 +38,7 @@ To contribute code to Supabase, you must fork the [Supabase Repository](https://
To build Supabase, you clone the source code repository:
2. Clone your GitHub forked repository:
```sh
git clone https://github.com/<github_username>/supabase.git
```
@@ -52,29 +53,36 @@ To build Supabase, you clone the source code repository:
Before you start a development server, you must choose if you want to work on the [Supabase Website](https://supabase.com), [Supabase Docs](https://supabase.com/docs/), or [Supabase Studio](https://app.supabase.io).
1. Go to the [Supabase Website](https://supabase.com) directory
```sh
cd www
```
Go to the [Supabase Docs](https://supabase.com/docs/) directory
```sh
cd web
```
Go to the [Supabase Studio](https://app.supabase.io) directory
```sh
cd studio
```
```sh
cd www
```
Go to the [Supabase Docs](https://supabase.com/docs/) directory
```sh
cd web
```
Go to the [Supabase Studio](https://app.supabase.io) directory
```sh
cd studio
```
2. Install npm dependencies:
npm
```sh
npm install
```
npm
or with yarn
```sh
yarn install
```
```sh
npm install
```
or with yarn
```sh
yarn install
```
## Start a Development Server
@@ -88,73 +96,81 @@ To debug code, and to see changes in real time, it is often useful to have a loc
1. Start development server
npm
```sh
npm run dev
```
npm
or with yarn
```sh
yarn dev
```
```sh
npm run dev
```
or with yarn
```sh
yarn dev
```
2. To access the local server, enter the following URL into your web browser:
```sh
http://localhost:3000/
```
```sh
http://localhost:3000/
```
### Supabase Docs Development Server
1. Build development server
npm
```sh
npm run build
```
npm
or with yarn
```sh
yarn build
```
```sh
npm run build
```
or with yarn
```sh
yarn build
```
2. Start development server
npm
```sh
npm run start
```
npm
or with yarn
```sh
yarn start
```
```sh
npm run start
```
or with yarn
```sh
yarn start
```
3. To access the local server, enter the following URL into your web browser:
```sh
http://localhost:3005/docs
```
```sh
http://localhost:3005/docs
```
### Supabase Studio Development Server
1. Start development server
npm
```sh
npm run dev
```
npm
or with yarn
```sh
yarn dev
```
```sh
npm run dev
```
or with yarn
```sh
yarn dev
```
2. To access the local server, enter the following URL into your web browser:
```sh
http://localhost:8082/
```
```sh
http://localhost:8082/
```
For more information on Supabase Studio, see the [Supabase Studio readme](./studio/README.md).
@@ -168,7 +184,6 @@ Did you have an issue, like a merge conflict, or don't know how to open a pull r
Stuck somewhere? Have any questions? please join the [Discord Community Server](https://discord.supabase.com/) or the [Github Discussions](https://github.com/supabase/supabase/discussions). We are here to help!
## Monorepo
This is a monorepo, using Turborepo. You must be using NPM 7 or higher.
@@ -177,7 +192,6 @@ This is a monorepo, using Turborepo. You must be using NPM 7 or higher.
- `npm install @supabase/ui -w ui`: installs into `./packages/ui`
- `npm install @supabase/ui -w docs`: installs into `./packages/docs`
### Development
`npm run dev`
@@ -17,7 +17,7 @@ export default function Main() {
<Layouts.SidebarLayout
sidebarContent={
<div className="p-4 text-white">
<div className='text-sm uppercase'>Categories</div>
<div className="text-sm uppercase">Categories</div>
<div>One</div>
<div>Two</div>
</div>
+1 -1
View File
@@ -41,4 +41,4 @@ export async function fetchDiscussions() {
},
})
return repository.discussions.nodes
}
}
@@ -50,6 +50,6 @@ export async function fetchPosts() {
return data
} catch (err) {
console.log(err)
return []
return []
}
}
+13 -13
View File
@@ -1,10 +1,10 @@
import React from 'react';
import {StyleSheet, View, Platform, StatusBar} from 'react-native';
import {ThemeProvider, colors, Text} from 'react-native-elements';
import {Styles} from './native/lib/constants';
import {UserContextProvider, useUser} from './native/components/UserContext';
import List from './native/components/TodoList';
import Auth from './native/components/Auth';
import React from 'react'
import { StyleSheet, View, Platform, StatusBar } from 'react-native'
import { ThemeProvider, colors, Text } from 'react-native-elements'
import { Styles } from './native/lib/constants'
import { UserContextProvider, useUser } from './native/components/UserContext'
import List from './native/components/TodoList'
import Auth from './native/components/Auth'
const theme = {
colors: {
@@ -13,13 +13,13 @@ const theme = {
ios: colors.platform.ios,
}),
},
};
}
const Container = () => {
const {user} = useUser();
const { user } = useUser()
return user ? <List /> : <Auth />;
};
return user ? <List /> : <Auth />
}
export default function App() {
return (
@@ -34,7 +34,7 @@ export default function App() {
</View>
</ThemeProvider>
</UserContextProvider>
);
)
}
const styles = StyleSheet.create({
@@ -47,4 +47,4 @@ const styles = StyleSheet.create({
paddingBottom: 4,
alignSelf: 'stretch',
},
});
})
@@ -2,13 +2,13 @@
* @format
*/
import 'react-native';
import React from 'react';
import App from '../App';
import 'react-native'
import React from 'react'
import App from '../App'
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
import renderer from 'react-test-renderer'
it('renders correctly', () => {
renderer.create(<App />);
});
renderer.create(<App />)
})
@@ -1,37 +1,37 @@
import React, {useState} from 'react';
import {Alert, StyleSheet, View} from 'react-native';
import {Styles} from '../lib/constants';
import {supabase} from '../lib/initSupabase';
import React, { useState } from 'react'
import { Alert, StyleSheet, View } from 'react-native'
import { Styles } from '../lib/constants'
import { supabase } from '../lib/initSupabase'
import {Button, Input} from 'react-native-elements';
import { Button, Input } from 'react-native-elements'
export default function Auth() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState('');
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState('')
const handleLogin = async (type: string, email: string, password: string) => {
setLoading(type);
const {error, user} =
setLoading(type)
const { error, user } =
type === 'LOGIN'
? await supabase.auth.signIn({email, password})
: await supabase.auth.signUp({email, password});
? await supabase.auth.signIn({ email, password })
: await supabase.auth.signUp({ email, password })
if (!error && user && type === 'SIGNUP') {
Alert.alert('Check your email for the login link!');
Alert.alert('Check your email for the login link!')
}
if (error) {
Alert.alert(error.message);
Alert.alert(error.message)
}
setLoading('');
};
setLoading('')
}
return (
<View>
<View style={[styles.verticallySpaced, {marginTop: 20}]}>
<View style={[styles.verticallySpaced, { marginTop: 20 }]}>
<Input
label="Email"
leftIcon={{type: 'font-awesome', name: 'envelope'}}
onChangeText={text => setEmail(text)}
leftIcon={{ type: 'font-awesome', name: 'envelope' }}
onChangeText={(text) => setEmail(text)}
value={email}
placeholder="email@address.com"
autoCapitalize={'none'}
@@ -40,15 +40,15 @@ export default function Auth() {
<View style={styles.verticallySpaced}>
<Input
label="Password"
leftIcon={{type: 'font-awesome', name: 'lock'}}
onChangeText={text => setPassword(text)}
leftIcon={{ type: 'font-awesome', name: 'lock' }}
onChangeText={(text) => setPassword(text)}
value={password}
secureTextEntry={true}
placeholder="Password"
autoCapitalize={'none'}
/>
</View>
<View style={[styles.verticallySpaced, {marginTop: 20}]}>
<View style={[styles.verticallySpaced, { marginTop: 20 }]}>
<Button
title="Sign in"
disabled={!!loading.length}
@@ -65,7 +65,7 @@ export default function Auth() {
/>
</View>
</View>
);
)
}
const styles = StyleSheet.create({
@@ -78,4 +78,4 @@ const styles = StyleSheet.create({
paddingBottom: 4,
alignSelf: 'stretch',
},
});
})
@@ -1,98 +1,93 @@
import React, {useEffect, useState} from 'react';
import {
View,
StyleSheet,
SafeAreaView,
FlatList,
} from 'react-native';
import {Styles} from '../lib/constants';
import {Text} from 'react-native-elements';
import {supabase} from '../lib/initSupabase';
import {useUser} from './UserContext';
import React, { useEffect, useState } from 'react'
import { View, StyleSheet, SafeAreaView, FlatList } from 'react-native'
import { Styles } from '../lib/constants'
import { Text } from 'react-native-elements'
import { supabase } from '../lib/initSupabase'
import { useUser } from './UserContext'
import {Button, Input, ListItem, CheckBox} from 'react-native-elements';
import { Button, Input, ListItem, CheckBox } from 'react-native-elements'
/** URL polyfill. Required for Supabase queries to work in React Native. */
import 'react-native-url-polyfill/auto';
import 'react-native-url-polyfill/auto'
type Todo = {
id: number;
user_id: string;
task: string;
is_complete: boolean;
inserted_at: Date;
};
id: number
user_id: string
task: string
is_complete: boolean
inserted_at: Date
}
export default function TodoList() {
const {user} = useUser();
const [todos, setTodos] = useState<Array<Todo>>([]);
const [newTaskText, setNewTaskText] = useState<string>('');
const { user } = useUser()
const [todos, setTodos] = useState<Array<Todo>>([])
const [newTaskText, setNewTaskText] = useState<string>('')
useEffect(() => {
fetchTodos();
}, []);
fetchTodos()
}, [])
const fetchTodos = async () => {
const {data: todos, error} = await supabase
const { data: todos, error } = await supabase
.from<Todo>('todos')
.select('*')
.order('id', {ascending: false});
.order('id', { ascending: false })
if (error) {
console.log('error', error);
console.log('error', error)
} else {
setTodos(todos!);
setTodos(todos!)
}
};
}
const addTodo = async (taskText: string) => {
const task = taskText.trim();
console.log('newtask:', task);
const task = taskText.trim()
console.log('newtask:', task)
if (task.length) {
const {data: todo, error} = await supabase
const { data: todo, error } = await supabase
.from<Todo>('todos')
.insert({task, user_id: user!.id})
.single();
.insert({ task, user_id: user!.id })
.single()
if (error) {
console.log(error.message);
console.log(error.message)
} else {
setTodos([todo!, ...todos]);
setNewTaskText('');
setTodos([todo!, ...todos])
setNewTaskText('')
}
}
};
}
const toggleCompleted = async (id: number, is_complete: boolean) => {
const {data, error} = await supabase
const { data, error } = await supabase
.from<Todo>('todos')
.update({is_complete: !is_complete})
.update({ is_complete: !is_complete })
.eq('id', id)
.single();
.single()
if (error) {
console.log(error);
console.log(error)
} else {
setTodos(todos.map(todo => (todo.id === id ? data! : todo)));
setTodos(todos.map((todo) => (todo.id === id ? data! : todo)))
}
};
}
const deleteTodo = async (id: number) => {
const {error} = await supabase.from<Todo>('todos').delete().eq('id', id);
const { error } = await supabase.from<Todo>('todos').delete().eq('id', id)
if (error) {
console.log('error', error);
console.log('error', error)
} else {
setTodos(todos.filter(x => x.id !== Number(id)));
setTodos(todos.filter((x) => x.id !== Number(id)))
}
};
}
return (
<View>
<View style={[styles.verticallySpaced, {marginTop: 20}]}>
<View style={[styles.verticallySpaced, { marginTop: 20 }]}>
<Button title="Sign out" onPress={() => supabase.auth.signOut()} />
</View>
<View style={[styles.verticallySpaced, {marginTop: 20}]}>
<View style={[styles.verticallySpaced, { marginTop: 20 }]}>
<Input
label="New todo"
leftIcon={{type: 'font-awesome', name: 'tasks'}}
onChangeText={text => setNewTaskText(text)}
leftIcon={{ type: 'font-awesome', name: 'tasks' }}
onChangeText={(text) => setNewTaskText(text)}
value={newTaskText}
/>
<Button title="Add" onPress={() => addTodo(newTaskText)} />
@@ -101,8 +96,8 @@ export default function TodoList() {
<FlatList
scrollEnabled={true}
data={todos}
keyExtractor={item => `${item.id}`}
renderItem={({item: todo}) => (
keyExtractor={(item) => `${item.id}`}
renderItem={({ item: todo }) => (
<ListItem bottomDivider>
<ListItem.Content>
<View
@@ -112,12 +107,13 @@ export default function TodoList() {
flexDirection: 'row',
justifyContent: 'space-between',
},
]}>
]}
>
<CheckBox
checked={todo.is_complete}
onPress={() => toggleCompleted(todo.id, todo.is_complete)}
/>
<Text h3 style={{margin: 'auto'}}>
<Text h3 style={{ margin: 'auto' }}>
{todo.task}
</Text>
<Button title="Delete" onPress={() => deleteTodo(todo.id)} />
@@ -128,7 +124,7 @@ export default function TodoList() {
/>
</SafeAreaView>
</View>
);
)
}
const styles = StyleSheet.create({
@@ -141,4 +137,4 @@ const styles = StyleSheet.create({
paddingBottom: 4,
alignSelf: 'stretch',
},
});
})
@@ -1,48 +1,46 @@
import React, {useEffect, useState, createContext, useContext} from 'react';
import {Session, User} from '@supabase/supabase-js';
import {supabase} from '../lib/initSupabase';
import React, { useEffect, useState, createContext, useContext } from 'react'
import { Session, User } from '@supabase/supabase-js'
import { supabase } from '../lib/initSupabase'
export const UserContext = createContext<{
user: User | null;
session: Session | null;
user: User | null
session: Session | null
}>({
user: null,
session: null,
});
})
export const UserContextProvider = (props: any) => {
const [session, setSession] = useState<Session | null>(null);
const [user, setUser] = useState<User | null>(null);
const [session, setSession] = useState<Session | null>(null)
const [user, setUser] = useState<User | null>(null)
useEffect(() => {
const session = supabase.auth.session();
setSession(session);
setUser(session?.user ?? null);
const {data: authListener} = supabase.auth.onAuthStateChange(
async (event, session) => {
console.log(`Supabase auth event: ${event}`);
setSession(session);
setUser(session?.user ?? null);
},
);
const session = supabase.auth.session()
setSession(session)
setUser(session?.user ?? null)
const { data: authListener } = supabase.auth.onAuthStateChange(async (event, session) => {
console.log(`Supabase auth event: ${event}`)
setSession(session)
setUser(session?.user ?? null)
})
return () => {
authListener!.unsubscribe();
};
authListener!.unsubscribe()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [])
const value = {
session,
user,
};
return <UserContext.Provider value={value} {...props} />;
};
}
return <UserContext.Provider value={value} {...props} />
}
export const useUser = () => {
const context = useContext(UserContext);
const context = useContext(UserContext)
if (context === undefined) {
throw new Error('useUser must be used within a UserContextProvider.');
throw new Error('useUser must be used within a UserContextProvider.')
}
return context;
};
return context
}
@@ -1,21 +1,15 @@
let supabaseUrl: string = '';
let supabaseAnonKey: string = '';
let supabaseUrl: string = ''
let supabaseAnonKey: string = ''
if (
!process.env.REACT_NATIVE_SUPABASE_URL ||
!process.env.REACT_NATIVE_SUPABASE_ANON_KEY
) {
console.log(
'constants.ts',
'Make sure you have a `.env` file to populate your variables.',
);
if (!process.env.REACT_NATIVE_SUPABASE_URL || !process.env.REACT_NATIVE_SUPABASE_ANON_KEY) {
console.log('constants.ts', 'Make sure you have a `.env` file to populate your variables.')
} else {
supabaseUrl = process.env.REACT_NATIVE_SUPABASE_URL;
supabaseAnonKey = process.env.REACT_NATIVE_SUPABASE_ANON_KEY;
supabaseUrl = process.env.REACT_NATIVE_SUPABASE_URL
supabaseAnonKey = process.env.REACT_NATIVE_SUPABASE_ANON_KEY
}
export const SUPABASE_URL = supabaseUrl;
export const SUPABASE_ANON_KEY = supabaseAnonKey;
export const SUPABASE_URL = supabaseUrl
export const SUPABASE_ANON_KEY = supabaseAnonKey
// console.log('SUPABASE: ', SUPABASE_ANON_KEY, SUPABASE_URL);
@@ -26,4 +20,4 @@ export const Styles = {
fontExtraLarge: 40,
colorPrimary: 'black',
spacing: 12,
};
}
@@ -1,7 +1,7 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import {createClient} from '@supabase/supabase-js';
import {SUPABASE_URL, SUPABASE_ANON_KEY} from './constants';
import AsyncStorage from '@react-native-async-storage/async-storage'
import { createClient } from '@supabase/supabase-js'
import { SUPABASE_URL, SUPABASE_ANON_KEY } from './constants'
export const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
localStorage: AsyncStorage as any,
});
})
@@ -155,7 +155,7 @@ export default function Account({ session }: { session: AuthSession }) {
</div>
<div>
<button className="button block primary" onClick={() => updateProfile()} disabled={loading}>
<button className="button primary block" onClick={() => updateProfile()} disabled={loading}>
{loading ? 'Loading ...' : 'Update'}
</button>
</div>
@@ -6,7 +6,7 @@ export default function Footer({}) {
<span className="text-sm">Powered by</span>
<img src="logo.png" />
</div>
<div className="flex flex-end">
<div className="flex-end flex">
<a
target="_blank"
href="https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fsupabase%2Fsupabase%2Ftree%2Fmaster%2Fexamples%2Fnextjs-ts-user-management&project-name=supabase-user-management&repository-name=supabase-user-management&demo-title=Supabase%20User%20Management&demo-description=An%20example%20web%20app%20using%20Supabase%20and%20Next.js&demo-url=https%3A%2F%2Fsupabase-nextjs-ts-user-management.vercel.app&demo-image=https%3A%2F%2Fi.imgur.com%2FZ3HkQqe.png&integration-ids=oac_jUduyjQgOyzev1fjrW83NYOv&external-id=nextjs-user-management"
@@ -57,7 +57,7 @@ export default function ProfileList({ profiles }: ProfileListProps) {
return (
<>
{state.profiles.length === 0 ? (
<p className="opacity-half font-light m-0">There are no public profiles created yet</p>
<p className="opacity-half m-0 font-light">There are no public profiles created yet</p>
) : (
<div className="profileList">
{state.profiles?.map((profile: any) => (
+3 -3
View File
@@ -1,4 +1,4 @@
import * as React from "react";
import * as React from 'react'
export const Button = () => {
return <button>Boop</button>;
};
return <button>Boop</button>
}
+3 -3
View File
@@ -1,8 +1,8 @@
import React from "react";
import React from 'react'
import SidebarLayout from "./layouts/SidebarLayout";
import SidebarLayout from './layouts/SidebarLayout'
export * from "./Button";
export * from './Button'
export const Layouts = {
SidebarLayout,
+9 -8
View File
@@ -11,7 +11,13 @@ function classNames(...classes: string[]) {
return classes.filter(Boolean).join(' ')
}
export default function SidebarLayout({ children, sidebarContent }: { children: React.ReactNode, sidebarContent: React.ReactNode }) {
export default function SidebarLayout({
children,
sidebarContent,
}: {
children: React.ReactNode
sidebarContent: React.ReactNode
}) {
const [sidebarOpen, setSidebarOpen] = useState(false)
return (
@@ -67,10 +73,7 @@ export default function SidebarLayout({ children, sidebarContent }: { children:
alt="Workflow"
/>
</div>
<div className="mt-5 h-0 flex-1 overflow-y-auto">
{sidebarContent}
</div>
<div className="mt-5 h-0 flex-1 overflow-y-auto">{sidebarContent}</div>
</div>
</Transition.Child>
<div className="w-14 flex-shrink-0" aria-hidden="true">
@@ -90,9 +93,7 @@ export default function SidebarLayout({ children, sidebarContent }: { children:
alt="Workflow"
/>
</div>
<div className="mt-5 flex flex-1 flex-col">
{sidebarContent}
</div>
<div className="mt-5 flex flex-1 flex-col">{sidebarContent}</div>
</div>
</div>
<div className="flex flex-1 flex-col md:pl-64">