-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
448 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
module.exports = function(api) { | ||
api.cache(true); | ||
module.exports = function (api) { | ||
api.cache(true) | ||
return { | ||
presets: ['babel-preset-expo'], | ||
plugins: ["module:react-native-dotenv"] | ||
}; | ||
}; | ||
plugins: ['module:react-native-dotenv'] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
import { registerRootComponent } from 'expo'; | ||
import { registerRootComponent } from 'expo' | ||
|
||
import App from './App'; | ||
import App from './App' | ||
|
||
// registerRootComponent calls AppRegistry.registerComponent('main', () => App); | ||
// It also ensures that whether you load the app in Expo Go or in a native build, | ||
// the environment is set up appropriately | ||
registerRootComponent(App); | ||
registerRootComponent(App) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
// Learn more https://docs.expo.io/guides/customizing-metro | ||
const { getDefaultConfig } = require('expo/metro-config'); | ||
const { getDefaultConfig } = require('expo/metro-config') | ||
|
||
module.exports = getDefaultConfig(__dirname); | ||
module.exports = getDefaultConfig(__dirname) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from 'react' | ||
import { Text } from 'react-native' | ||
|
||
/** | ||
* Here you can implement the logic to subscribe to your tasks and CRUD them. | ||
* See: https://github.com/meteorrn/sample | ||
* @param props | ||
* @returns {JSX.Element} | ||
* @constructor | ||
*/ | ||
export const MyTasks = () => (<Text>My Tasks not yet implemented</Text>) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { createContext } from 'react' | ||
|
||
/** | ||
* Our authentication context provides an API for our components | ||
* that allows them to communicate with the servers in a decoupled way. | ||
* @method signIn | ||
* @method signUp | ||
* @method signOut | ||
* @type {React.Context<object>} | ||
*/ | ||
export const AuthContext = createContext() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { useEffect, useState } from 'react' | ||
import Meteor from '@meteorrn/core' | ||
import * as SecureStore from 'expo-secure-store' | ||
import config from '../../config.json' | ||
|
||
// get detailed info about internals | ||
Meteor.isVerbose = true | ||
|
||
// connect with Meteor and use a secure store | ||
// to persist our received login token, so it's encrypted | ||
// and only readable for this very app | ||
// read more at: https://docs.expo.dev/versions/latest/sdk/securestore/ | ||
Meteor.connect(config.backend.url, { | ||
AsyncStorage: { | ||
getItem: SecureStore.getItemAsync, | ||
setItem: SecureStore.setItemAsync, | ||
removeItem: SecureStore.deleteItemAsync | ||
} | ||
}) | ||
|
||
/** | ||
* Hook that handle auto-reconnect and updates state accordingly. | ||
* @return {{connected: boolean|null, connectionError: Error|null}} | ||
*/ | ||
export const useConnection = () => { | ||
const [connected, setConnected] = useState(null) | ||
const [connectionError, setConnectionError] = useState(null) | ||
|
||
// we use separate functions as the handlers, so they get removed | ||
// on unmount, which happens on auto-reload and would cause errors | ||
// if not handled | ||
useEffect(() => { | ||
const onError = (e) => setConnectionError(e) | ||
Meteor.ddp.on('error', onError) | ||
|
||
const onConnected = () => connected !== true && setConnected(true) | ||
Meteor.ddp.on('connected', onConnected) | ||
|
||
// if the connection is lost, we not only switch the state | ||
// but also force to reconnect to the server | ||
const onDisconnected = () => { | ||
Meteor.ddp.autoConnect = true | ||
if (connected !== false) { | ||
setConnected(false) | ||
} | ||
Meteor.reconnect() | ||
} | ||
Meteor.ddp.on('disconnected', onDisconnected) | ||
|
||
// remove all of these listeners on unmount | ||
return () => { | ||
Meteor.ddp.off('error', onError) | ||
Meteor.ddp.off('connected', onConnected) | ||
Meteor.ddp.off('disconnected', onDisconnected) | ||
} | ||
}, []) | ||
|
||
return { connected, connectionError } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import { useReducer, useEffect, useMemo } from 'react' | ||
import Meteor from '@meteorrn/core' | ||
|
||
/** @private */ | ||
const initialState = { | ||
isLoading: true, | ||
isSignout: false, | ||
userToken: null | ||
} | ||
|
||
/** @private */ | ||
const reducer = (state, action) => { | ||
switch (action.type) { | ||
case 'RESTORE_TOKEN': | ||
return { | ||
...state, | ||
userToken: action.token, | ||
isLoading: false | ||
} | ||
case 'SIGN_IN': | ||
return { | ||
...state, | ||
isSignOut: false, | ||
userToken: action.token | ||
} | ||
case 'SIGN_OUT': | ||
return { | ||
...state, | ||
isSignout: true, | ||
userToken: null | ||
} | ||
} | ||
} | ||
|
||
/** @private */ | ||
const Data = Meteor.getData() | ||
|
||
/** | ||
* Provides a state and authentication context for components to decide, whether | ||
* the user is authenticated and also to run several authentication actions. | ||
* | ||
* The returned state contains the following structure: | ||
* {{ | ||
* isLoading: boolean, | ||
* isSignout: boolean, | ||
* userToken: string|null | ||
* } | ||
* }} | ||
* | ||
* the authcontext provides the following methods: | ||
* {{ | ||
* signIn: function, | ||
* signOut: function, | ||
* signUp: function | ||
* }} | ||
* | ||
* @returns {{ | ||
* state:object, | ||
* authContext: object | ||
* }} | ||
*/ | ||
export const useLogin = () => { | ||
const [state, dispatch] = useReducer(reducer, initialState, undefined) | ||
|
||
// Case 1: restore token already exists | ||
// MeteorRN loads the token on connection automatically, | ||
// in case it exists, but we need to "know" that for our auth workflow | ||
useEffect(() => { | ||
const handleOnLogin = () => dispatch({ type: 'RESTORE_TOKEN', token: Meteor.getAuthToken() }) | ||
Data.on('onLogin', handleOnLogin) | ||
return () => Data.off('onLogin', handleOnLogin) | ||
}, []) | ||
|
||
// the auth can be referenced via useContext in the several | ||
// screens later on | ||
const authContext = useMemo(() => ({ | ||
signIn: ({ email, password, onError }) => { | ||
Meteor.loginWithPassword(email, password, async (err) => { | ||
if (err) { | ||
if (err.message === 'Match failed [400]') { | ||
err.message = 'Login failed, please check your credentials and retry.' | ||
} | ||
return onError(err) | ||
} | ||
const token = Meteor.getAuthToken() | ||
const type = 'SIGN_IN' | ||
dispatch({ type, token }) | ||
}) | ||
}, | ||
signOut: () => { | ||
Meteor.logout(err => { | ||
if (err) { | ||
// TODO display error, merge into the above workflow | ||
return console.error(err) | ||
} | ||
dispatch({ type: 'SIGN_OUT' }) | ||
}) | ||
}, | ||
signUp: ({ email, password, onError }) => { | ||
Meteor.call('register', { email, password }, (err, res) => { | ||
if (err) { | ||
return onError(err) | ||
} | ||
// TODO move the below code and the code from signIn into an own function | ||
Meteor.loginWithPassword(email, password, async (err) => { | ||
if (err) { | ||
if (err.message === 'Match failed [400]') { | ||
err.message = 'Login failed, please check your credentials and retry.' | ||
} | ||
return onError(err) | ||
} | ||
const token = Meteor.getAuthToken() | ||
const type = 'SIGN_IN' | ||
dispatch({ type, token }) | ||
}) | ||
}) | ||
} | ||
}), []) | ||
|
||
return { state, authContext } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import React, { useContext, useState } from 'react' | ||
import { View, Text, Button, StyleSheet } from 'react-native' | ||
import { AuthContext } from '../contexts/AuthContext' | ||
import { MyTasks } from '../components/MyTasks' | ||
|
||
export const HomeScreen = () => { | ||
const [error, setError] = useState(null) | ||
const { signOut } = useContext(AuthContext) | ||
const onError = err => setError(err) | ||
const handleSignOut = () => signOut({ onError }) | ||
|
||
const renderError = () => { | ||
if (!error) { return null } | ||
return ( | ||
<View style={{ alignItems: 'center' }}> | ||
<Text>{error.message}</Text> | ||
</View> | ||
) | ||
} | ||
|
||
return ( | ||
<View style={styles.container}> | ||
<MyTasks /> | ||
{renderError()} | ||
<Button title='Sign out' onPress={handleSignOut} /> | ||
</View> | ||
) | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
flex: 1, | ||
backgroundColor: '#efefef', | ||
alignItems: 'center', | ||
justifyContent: 'center' | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import React, { useState, useContext } from 'react' | ||
import { View, Text, TextInput, Button } from 'react-native' | ||
import { AuthContext } from '../contexts/AuthContext' | ||
import { inputStyles } from '../styles/inputStyles' | ||
|
||
/** | ||
* Provides a login form and links to RegisterScreen | ||
* @param navigation {object} automatically passed from our Navigator, use to move to RegisterScreen | ||
* @component | ||
* @returns {JSX.Element} | ||
*/ | ||
export const LoginScreen = ({ navigation }) => { | ||
const [email, setEmail] = useState('') | ||
const [password, setPassword] = useState('') | ||
const [error, setError] = useState(null) | ||
const { signIn } = useContext(AuthContext) | ||
|
||
// handlers | ||
const onError = err => setError(err) | ||
const onSignIn = () => signIn({ email, password, onError }) | ||
const renderError = () => { | ||
if (!error) { return null } | ||
return ( | ||
<View style={{ alignItems: 'center', padding: 15 }}> | ||
<Text style={{ color: 'red' }}>{error.message}</Text> | ||
</View> | ||
) | ||
} | ||
|
||
// render login form | ||
return ( | ||
<View> | ||
<TextInput | ||
placeholder='Your Email' | ||
placeholderTextColor='#8a8a8a' | ||
style={inputStyles.text} | ||
value={email} | ||
onChangeText={setEmail} | ||
/> | ||
<TextInput | ||
placeholder='Password' | ||
placeholderTextColor='#8a8a8a' | ||
style={inputStyles.text} | ||
value={password} | ||
onChangeText={setPassword} | ||
secureTextEntry | ||
/> | ||
{renderError()} | ||
<Button title='Sign in' onPress={onSignIn} /> | ||
<View style={{ alignItems: 'center', padding: 15 }}> | ||
<Text>or</Text> | ||
</View> | ||
<Button title='Sign up' onPress={() => navigation.navigate('SignUp')} /> | ||
</View> | ||
) | ||
} |
Oops, something went wrong.