Commit 56f0edc0 authored by Milan John Paul Digiuseppe's avatar Milan John Paul Digiuseppe

Merge branch 'will-FirebaseAuth' into 'master'

Will firebase auth

See merge request !11
parents 6b83407a 53ea5987
......@@ -2,6 +2,7 @@
import React from 'react';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import { initializeAuth } from './src/store/auth/AuthActions';
import RootReducer from './src/store/RootReducer';
import AppContainer from './src/AppContainer';
......@@ -9,6 +10,9 @@ const store = configureStore({
reducer: RootReducer,
});
store.dispatch(initializeAuth())
const App = () => (
<Provider store={store}>
<AppContainer />
......
......@@ -23,11 +23,20 @@
"assetBundlePatterns": [
"**/*"
],
"facebookScheme" : "fb710462976370982",
"facebookAppId" : "710462976370982",
"facebookDisplayName" : "GeoCache-",
"ios": {
"bundleIdentifier": "com.gamergroup.geo",
"buildNumber": "1.0.0",
"supportsTablet": true,
"config": {
"googleMapsApiKey": "AIzaSyB7ueZ5fnM1SwpWwoUi88H5k3_tkLIoz5I"
}
},
"android": {
"package": "com.gamergroup.geo",
"versionCode": 1
}
}
}
\ No newline at end of file
This diff is collapsed.
......@@ -3,12 +3,14 @@ import React, {
} from 'react';
import MapView, { LatLng } from 'react-native-maps';
import { StyleSheet, Keyboard } from 'react-native';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { watchPositionAsync } from 'expo-location';
import { getAuthToken } from '../store/auth/AuthSelectors';
import { geoSearch } from '../store/caches/CachesActions';
import customMapStyle from '../CustomMapStyle.json';
import UserLocationMarker from './UserLocationMarker';
import { distanceBetweenCoordinates } from '../utils/Geography';
import { getUserLatLng } from '../store/location/LocationSelectors';
const styles = StyleSheet.create({
map: {
......@@ -18,10 +20,12 @@ const styles = StyleSheet.create({
const DiscoverMap: React.FC = ({ children }) => {
const dispatch = useDispatch();
const [userLocation, setUserLocation] = useState<LatLng>();
const [userLocation, setUserLocation] = useState<LatLng>({latitude: 0, longitude: 0});
const [lastGeosearch, setLastGeosearch] = useState<LatLng>();
const mapRef = useRef<MapView>(null);
const idToken = useSelector(getAuthToken);
const onLocationChange = useCallback((location: LatLng) => {
setUserLocation(location);
mapRef.current?.animateCamera({
......@@ -42,17 +46,24 @@ const DiscoverMap: React.FC = ({ children }) => {
// Call geosearch when user has moved 100m since last geosearch location
if (!userLocation) return;
if (!lastGeosearch) {
dispatch(geoSearch(userLocation, '1'));
dispatch(geoSearch(userLocation, '1', idToken));
setLastGeosearch(userLocation);
return;
}
const d = distanceBetweenCoordinates(lastGeosearch, userLocation);
if (d > 0.100) {
dispatch(geoSearch(userLocation, '1'));
dispatch(geoSearch(userLocation, '1', idToken));
setLastGeosearch(userLocation);
}
}, [userLocation, lastGeosearch, setLastGeosearch]);
//alert("userLocation: " + userLocation);
//let keys = Object.keys(userLocation);
//for (key in keys) {
// alert("location key: " + key);
//}
return (
<MapView
ref={mapRef}
......
import React, { useCallback } from 'react';
import { StyleSheet, View } from 'react-native';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { getAuthToken } from '../store/auth/AuthSelectors'
import { reverseGeocodeAsync } from 'expo-location';
import { useNavigation } from '@react-navigation/native';
import { geoSearch } from '../store/caches/CachesActions';
......@@ -23,12 +24,13 @@ const styles = StyleSheet.create({
const DevScreen: React.FC = () => {
const navigation = useNavigation();
const dispatch = useDispatch();
const idToken = useSelector(getAuthToken);
const onClose = useCallback(() => navigation.goBack(), [navigation]);
const onGeosearch = useCallback(async () => {
const userLocation = await getCurrentPosition();
dispatch(geoSearch(userLocation, '1'));
dispatch(geoSearch(userLocation, '1', idToken));
navigation.navigate('Discover');
}, [dispatch, navigation]);
......
......@@ -6,6 +6,7 @@ import CacheFoundModal from '../components/CacheFoundModal';
import { Cache } from '../store/caches/CachesState';
import { getDiscoveredCaches } from '../store/caches/CachesSelectors';
import { saveCache, reportCache } from '../store/caches/CachesActions';
import { getAuthToken } from '../store/auth/AuthSelectors';
import ScreenHeader from '../components/ScreenHeader';
import GeoMarker from '../components/GeoMarker';
import { sampleCaches } from '../SampleCaches';
......@@ -40,6 +41,9 @@ const DiscoverScreen: React.FC = () => {
const [isCacheModalOpen, setCacheModalOpen] = useState(false);
const [openCache, setOpenCache] = useState<Cache | null>(sampleCaches[0]);
const idToken = useSelector(getAuthToken);
const onClose = useCallback(() => {
setCacheModalOpen(false);
setOpenCache(null);
......@@ -49,7 +53,7 @@ const DiscoverScreen: React.FC = () => {
onClose();
}, [dispatch, onClose]);
const onReport = useCallback((id) => {
dispatch(reportCache(id, '1', 'reported'));
dispatch(reportCache(id, '1', 'reported', idToken));
onClose();
}, [dispatch, onClose]);
......
......@@ -10,6 +10,7 @@ import {
getUserCreatedCaches,
getFoundCaches,
} from '../store/caches/CachesSelectors';
import { getAuthToken } from '../store/auth/AuthSelectors';
import { removeSaved, deleteCache } from '../store/caches/CachesActions';
import SwipeableList from '../components/SwipeableList';
......@@ -50,6 +51,8 @@ const SavedMessagesScreen: React.FC = () => {
const foundCaches: Cache[] = useSelector(getFoundCaches);
const userCreatedCaches: Cache[] = useSelector(getUserCreatedCaches);
const idToken = useSelector(getAuthToken)
const listSections = useMemo(() => ([
{
title: 'Found in the Wild',
......@@ -64,7 +67,7 @@ const SavedMessagesScreen: React.FC = () => {
const handleDelete = useCallback(
(cache: Cache) => {
if (cache.isUserCreated) {
dispatch(deleteCache(cache.id));
dispatch(deleteCache(cache.id, idToken));
} else {
dispatch(removeSaved(cache.id));
}
......
import React, { useCallback } from 'react';
import { useSelector } from 'react-redux';
import { View, StyleSheet } from 'react-native';
import { Avatar } from 'react-native-elements';
import { useNavigation } from '@react-navigation/native';
......@@ -6,6 +7,12 @@ import GeoText from '../components/GeoText';
import theme from '../theme';
import GeoButton from '../components/GeoButton';
import {
getUserName,
getUserId,
getUserImageUrl,
} from '../store/auth/AuthSelectors';
const styles = StyleSheet.create({
avatar: {
backgroundColor: theme.colors.mediumgray,
......@@ -16,6 +23,11 @@ const styles = StyleSheet.create({
const SettingsScreen = () => {
const { navigate } = useNavigation();
const onDevPress = useCallback(() => navigate('DevScreen'), [navigate]);
const userName: string = useSelector(getUserName);
const userId: string = useSelector(getUserId);
const userImageUrl: string = useSelector(getUserImageUrl);
return (
<View style={{ flex: 1, justifyContent: 'space-between' }}>
<View style={{ alignItems: 'center' }}>
......@@ -26,7 +38,7 @@ const SettingsScreen = () => {
size={'xlarge'}
/>
<GeoText
text={'milan_epic_gamer'}
text={userName}
variant={'formHeader'}
style={{ marginTop: theme.spacing.small }}
/>
......
import React, { useCallback } from 'react';
import { View } from 'react-native';
import { View, Image, ImageBackground } from 'react-native';
import { useDispatch } from 'react-redux';
import { signInAsync } from '../store/auth/AuthActions';
import { signInAsyncFacebook, signInAsyncGoogle } from '../store/auth/AuthActions';
import GeoButton from '../components/GeoButton';
import { SocialIcon } from 'react-native-elements'
import SplashImage from '../../assets/splash.png';
import LogoImage from '../../assets/LogoHeader.png';
const SignInScreen = () => {
const dispatch = useDispatch();
const signIn = useCallback(() => {
dispatch(signInAsync());
const signInFacebook = useCallback(() => {
dispatch(signInAsyncFacebook());
}, []);
const signInGoogle = useCallback(() => {
dispatch(signInAsyncGoogle());
}, []);
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<GeoButton title={'Sign in'} onPress={signIn} />
<View style={{ flex: 1, justifyContent: 'center',alignItems: 'center'}}>
<Image source={LogoImage}
style={{flex:0.5,
justifyContent: 'center',
resizeMode: 'contain',
}}
/>
<SocialIcon
title='Sign in Facebook'
button
type='facebook'
onPress={signInFacebook}
style={{width:'90%'}}
/>
<SocialIcon
title={'Sign in Google'}
button
type='google'
onPress={signInGoogle}
style={{width:'90%'}}
/>
</View>
);
};
......
......@@ -4,6 +4,7 @@ import { StyleSheet, View } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import theme from '../../theme';
import { createCache, geoSearch } from '../../store/caches/CachesActions';
import { getAuthToken, getUserName } from '../../store/auth/AuthSelectors'
import { getPendingCacheContent, getPendingCacheDuration } from '../../store/pendingNewCache/PendingCacheSelectors';
import GeoButton from '../../components/GeoButton';
import DiscoverMap from '../../components/DiscoverMap';
......@@ -37,19 +38,24 @@ const CacheSubmitScreen = () => {
const cacheContent = useSelector(getPendingCacheContent);
const cacheDuration = useSelector(getPendingCacheDuration);
const userName = useSelector(getUserName);
const idToken = useSelector(getAuthToken);
const onCreate = useCallback(async () => {
const userLocation = await getCurrentPosition();
dispatch(createCache({
lat: userLocation.latitude,
lng: userLocation.longitude,
message: cacheContent,
expire: cacheDuration,
uuid: '1',
name: 'milan.digiuseppe',
name: userName,
isUserCreated: true,
}));
},
idToken));
navigate('Discover');
dispatch(geoSearch(userLocation, '1'));
dispatch(geoSearch(userLocation, '1', idToken));
}, [navigate]);
return (
......
/* eslint-disable linebreak-style */
import { Dispatch } from '@reduxjs/toolkit';
import { AsyncStorage } from 'react-native';
import { useDispatch } from 'react-redux';
import { AsyncStorage, Alert } from 'react-native';
import * as Firebase from 'firebase';
import * as Facebook from 'expo-facebook';
import AuthSlice from './AuthSlice';
import { createDispatchHook } from 'react-redux';
//TODO: using this package for auth;
// will need to use expo-google-sign-in for standalone
import * as Google from 'expo-google-app-auth'
const {
restoreToken,
......@@ -8,17 +17,101 @@ const {
signOut,
} = AuthSlice.actions;
// Initialize Firebase
const firebaseConfig = {
apiKey: 'AIzaSyB7ueZ5fnM1SwpWwoUi88H5k3_tkLIoz5I',
authDomain: 'my-project-1569171242510.firebaseapp.com',
databaseURL: 'https://my-project-1569171242510.firebaseio.com',
projectId: 'my-project-1569171242510',
storageBucket: 'my-project-1569171242510.appspot.com',
messagingSenderId: '834336886747',
appId: '1:834336886747:web:db805542e470265dbc61fe',
measurementId: '834336886747',
};
export const initializeAuth = () => async (dispatch: Dispatch) => {
if (Firebase.apps.length === 0) {
// don't double initialize when expo hot-reloading
Firebase.initializeApp(firebaseConfig);
//await Firebase.auth().setPersistence(Firebase.auth.Auth.Persistence.LOCAL);
}
// Listen for authentication state to change.
Firebase.auth().onAuthStateChanged(async (user) => {
// const dispatch = useDispatch();
if (user == null) {
console.log('we are not authenticated');
AsyncStorage.removeItem('userToken');
dispatch(signOut());
} else {
console.log('Authenticated success');
let idToken = await user.getIdToken();
let userInfo = {
//token: user.refreshToken,
name: user.displayName,
idToken: idToken,
id: user.email,
imageUrl: user.photoURL,
};
dispatch(restoreToken(userInfo));
}
// Do other things ...
});
}
export const authBootstrap = () => async (dispatch: Dispatch) => {
let userToken;
/*
let refreshToken;
try {
userToken = await AsyncStorage.getItem('userToken');
refreshToken = await AsyncStorage.getItem('userToken');
// Firebase.auth.Refresh(refreshToken)
console.log('Saved Token')
//dispatch(restoreToken(refreshToken));
dispatch(signIn());
} catch (e) {
console.warn('token not found');
console.log('NONE Saved Token');
//dispatch(startSignin()) // don't dispatch a thign??
dispatch(signIn());
}
dispatch(restoreToken(userToken));
*/
};
async function FacebookLogIn() {
try {
const AppID = '710462976370982';
await Facebook.initializeAsync(AppID, undefined);
const {
type,
token,
expires,
permissions,
declinedPermissions,
} = await Facebook.logInWithReadPermissionsAsync(
{
permissions: ['public_profile', 'email'],
},
);
if (type === 'success') {
// Handle successful authentication here
console.log('Facebook Login Succeeded');
return token;
} else {
console.log('Facebook Login Failed');
// Handle errors here ...
}
} catch ({message}) {
console.log(`Facebook Login Error: ${message}`);
}
throw {};
}
const persistToken = async (token: string) => {
try {
await AsyncStorage.setItem('userToken', token);
......@@ -27,22 +120,60 @@ const persistToken = async (token: string) => {
}
};
export const signInAsync = () => async (dispatch: Dispatch) => {
export const signInAsyncFacebook = () => async (dispatch: Dispatch) => {
// TODO: get token from signing in via third-party auth
const token = 'dummy_token';
persistToken(token);
dispatch(signIn(token));
try {
const token = await FacebookLogIn();
} catch (exception) {
return;
}
// do firebase login
const credential = Firebase.auth.FacebookAuthProvider.credential(token);
//await Firebase.auth().setPersistence(Firebase.auth.Auth.Persistence.LOCAL);
let userCredential = await Firebase.auth().signInWithCredential(credential).catch((error) => {
console.warn(`Firebase auth error : ${error}`);
return;
});
let user = userCredential.user;
// --- state change done by auth change listener
let userInfo = { token: user.refreshToken,
name: user.displayName,
// idToken: user.getIdToken(),
id: user.email,
imageUrl: user.photoURL
};
//dispatch(restoreToken(userInfo));
};
export const signUpAsync = () => async (dispatch: Dispatch) => {
// TODO: get token from signing up via third-party auth
const token = 'dummy_token';
persistToken(token);
dispatch(signIn(token));
export const signInAsyncGoogle = () => async (dispatch: Dispatch) => {
// First- obtain access token from Expo's Google API
try {
const { type, accessToken, idToken, user } = await Google.logInAsync({
androidClientId: '834336886747-tdkqs7n2nrr2kgib3efdu253fuopbbgh.apps.googleusercontent.com',
iosClientId: '834336886747-qktght10v1b1o4pmd7sv0m7gmkfp8bci.apps.googleusercontent.com',
scopes: ['profile', 'email'],
});
if (type === 'success') {
//await Firebase.auth().setPersistence(Firebase.auth.Auth.Persistence.LOCAL);
const credential = await Firebase.auth.GoogleAuthProvider.credential(idToken, accessToken);
const signIn = Firebase.auth().signInWithCredential(credential).catch((error) => {
console.warn(`Firebase auth error : ${error}`);
return;
});
}
} catch (exception) {
return;
}
};
export const signOutAsync = () => async (dispatch: Dispatch) => {
try {
await Firebase.auth().signOut();
await AsyncStorage.removeItem('userToken');
} catch (e) {
console.warn('error removing token');
......
import { RootState } from '../RootReducer';
export const getAuthToken = (state: RootState) => state.auth.userToken;
export const getAuthToken = (state: RootState) => state.auth.userIdToken;
export const getAuthLoading = (state: RootState) => state.auth.isLoading;
export const getUserName = (state: RootState) => state.auth.userName;
export const getUserId = (state: RootState) => state.auth.userId;
export const getUserImageUrl = (state: RootState) => state.auth.userimageUrl;
\ No newline at end of file
......@@ -6,17 +6,35 @@ const authSlice = createSlice({
initialState: {
isLoading: true,
userToken: undefined,
userIdToken: undefined,
userName: undefined,
userId: undefined,
userimageUrl: undefined,
} as AuthState,
reducers: {
restoreToken(state, action) {
state.userToken = action.payload;
state.userToken = action.payload.token;
state.userIdToken = action.payload.idToken;
state.isLoading = false;
state.userName = action.payload.name;
state.userId = action.payload.id;
state.userimageUrl = action.payload.imageUrl;
},
signIn(state, action) {
state.userToken = action.payload;
signIn(state) {
state.userIdToken = undefined;
state.userToken = undefined;
state.isLoading = false;
},
signOut(state) {
state.userIdToken = undefined;
state.userToken = undefined;
state.isLoading = false;
state.userName = undefined;
state.userId = undefined;
state.userimageUrl = undefined;
},
},
});
......
export interface AuthState {
isLoading: boolean;
userToken: string | undefined;
// storing here... todo: put it in its own state
userId: string | undefined;
userName: string | undefined;
userimageUrl: string | undefined;
}
......@@ -12,8 +12,9 @@ export const {
const backendURL = 'http://45.62.247.67:3000';
export const createCache = (cache: Cache) => async (dispatch: Dispatch) => {
const result = await fetch(`${backendURL}/message`, {
export const createCache = (cache: Cache, idToken: string) => async (dispatch: Dispatch) => {
const result = await fetch(`${backendURL}/message`
+ `?idtoken=${encodeURIComponent(idToken)}`, {
method: 'POST',
headers: {
Accept: 'application/json',
......@@ -31,9 +32,10 @@ export const createCache = (cache: Cache) => async (dispatch: Dispatch) => {
}));
};
export const deleteCache = (id: string) => async (dispatch: Dispatch) => {
export const deleteCache = (id: string, idToken: string) => async (dispatch: Dispatch) => {
dispatch(removeSaved(id));
fetch(`${backendURL}/message`,
fetch(`${backendURL}/message`
+ `?idtoken=${encodeURIComponent(idToken)}`,
{
method: 'DELETE',
headers: {
......@@ -44,11 +46,12 @@ export const deleteCache = (id: string) => async (dispatch: Dispatch) => {
});
};
export const geoSearch = (location: LatLng, uuid: string) => async (dispatch: Dispatch) => {
export const geoSearch = (location: LatLng, uuid: string, idToken: string) => async (dispatch: Dispatch) => {
const response = await fetch(`${backendURL}/message/geosearch`
+ `?lat=${encodeURIComponent(location.latitude)}
&lng=${encodeURIComponent(location.longitude)}
&uuid=${encodeURIComponent(uuid)}`,
&uuid=${encodeURIComponent(uuid)}
&idtoken=${encodeURIComponent(idToken)}`,
{
method: 'GET',
headers: {
......@@ -64,9 +67,11 @@ export const reportCache = (
id: string,
uuid: string,
reason: string,
idToken : string
) => async (dispatch: Dispatch) => {
dispatch(removeDiscovered(id));
const result = await fetch(`${backendURL}/report`,
const result = await fetch(`${backendURL}/report`
+ `?idtoken=${encodeURIComponent(idToken)}`,
{
method: 'POST',
headers: {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment