Skip to content
Snippets Groups Projects
Commit 8d0a398b authored by Endrizzi, Marco (UG - Comp Sci & Elec Eng)'s avatar Endrizzi, Marco (UG - Comp Sci & Elec Eng)
Browse files

Merge branch 'Signals-backend' into 'master'

Signals backend

See merge request me00531/group23_com2027!16
parents 458847a6 3f9ac814
No related branches found
No related tags found
No related merge requests found
...@@ -19,7 +19,7 @@ q-btn( ...@@ -19,7 +19,7 @@ q-btn(
</template> </template>
<script> <script>
import { mapState } from 'vuex' import { mapState, mapActions } from 'vuex'
import axios from 'axios' import axios from 'axios'
export default { export default {
...@@ -28,7 +28,6 @@ export default { ...@@ -28,7 +28,6 @@ export default {
data() { data() {
return { return {
toggle: false, toggle: false,
center: null,
map: null, map: null,
heatmap: null, heatmap: null,
places: [], places: [],
...@@ -37,35 +36,40 @@ export default { ...@@ -37,35 +36,40 @@ export default {
}, },
methods: { methods: {
// Helper method to turn heatmap on/off /***********************
BUTTON CLICK HANDLERS
************************/
/* Helper method to turn heatmap on/off */
toggleHeatmap() { toggleHeatmap() {
this.heatmap.setMap(this.heatmap.getMap() ? null : this.map) this.heatmap.setMap(this.heatmap.getMap() ? null : this.map)
}, },
// Centers the map on user position when the button is presed /* Centers the map on user position when the button is pressed */
centerMap() { centerMap() {
this.map.setCenter(new google.maps.LatLng(this.center.lat, this.center.lng)) this.map.setCenter(new google.maps.LatLng(this.center.lat, this.center.lng))
this.map.setZoom(15) this.map.setZoom(15)
}, },
/********************* /*********************
APIs IMPLEMENTATION GEOLOCATE API
**********************/ **********************/
// Fetch current position using MDN Geolocation API /* Fetch current position using MDN Geolocation API */
getCurrentPosition(options = {}) { getCurrentPosition(options = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, options) navigator.geolocation.getCurrentPosition(resolve, reject, options)
}) })
}, },
// Set center variable to current estimated coordinates
/* Save current estimated position to the store and firebase's db */
async geolocate() { async geolocate() {
if (navigator.geolocation) { if (navigator.geolocation) {
try { try {
const position = await this.getCurrentPosition() const position = await this.getCurrentPosition()
this.center = { let center = {
lat: position.coords.latitude, lat: position.coords.latitude,
lng: position.coords.longitude lng: position.coords.longitude
} }
this.firebaseSavePosition(center)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
...@@ -73,7 +77,11 @@ export default { ...@@ -73,7 +77,11 @@ export default {
alert('Geolocation is not supported by this browser.') alert('Geolocation is not supported by this browser.')
} }
}, },
// Finds 20 places related to keyword 'nightlife' in 1500m proximity of center coordinates
/*********************
PLACES API
**********************/
/* Finds 20 places related to keyword 'nightlife' in 1500m proximity of center coordinates */
async findPlaces() { async findPlaces() {
const URL = `https://secret-ocean-49799.herokuapp.com/https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${this.center.lat},${this.center.lng} const URL = `https://secret-ocean-49799.herokuapp.com/https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${this.center.lat},${this.center.lng}
&radius=1500 &radius=1500
...@@ -88,7 +96,46 @@ export default { ...@@ -88,7 +96,46 @@ export default {
console.log(error.message) console.log(error.message)
}) })
}, },
// Return an array containing the dates of last 3 months since the uk.police API was updated
/* Return hmtl img element of the first picture listed on maps for an establishment */
getPlaceImage(place) {
if (place.photos) {
const URL =
`https://secret-ocean-49799.herokuapp.com/https://maps.googleapis.com/maps/api/place/photo?maxwidth=120&key=AIzaSyBPUdoB3wV6A9L-H1-J5POiQRmgqqcL9Bk&photoreference=` +
place.photos[0].photo_reference
return `<img src="${URL}" height="80" width="60">`
}
return ''
},
/* Return specific icon based on first element of 'types' attribute for an establishment */
getPlaceIcon(place) {
let icon = 'https://img.icons8.com/color/40/000000/disco-ball.png'
if (place.types[0] == 'bar') {
icon = 'https://img.icons8.com/color/40/000000/beer.png'
} else if (place.types[0] == 'restaurant') {
icon = 'https://img.icons8.com/color/40/000000/food-and-wine.png'
}
return icon
},
/* Return a different html element based on whether an establishment is currently open or closed */
isPlaceOpen(place) {
if (place.opening_hours) {
let htmlElement = `<p class="text-negative">Closed</p>`
if (place.opening_hours.open_now) {
htmlElement = `<p class="text-positive">Open</p>`
}
return htmlElement
}
return ''
},
/*********************
CRIMES API
**********************/
/* Return an array containing the dates of last 3 months since the uk.police API was updated */
async fetchDates() { async fetchDates() {
var datesArray = [] var datesArray = []
const lastUpdateURL = `https://data.police.uk/api/crime-last-updated` const lastUpdateURL = `https://data.police.uk/api/crime-last-updated`
...@@ -114,7 +161,8 @@ export default { ...@@ -114,7 +161,8 @@ export default {
}) })
return datesArray return datesArray
}, },
// Load crime data of past 3 months into crimes variable
/* Load crime data of past 3 months into the 'crimes' variable */
async getCrimeData() { async getCrimeData() {
let dates = await this.fetchDates() let dates = await this.fetchDates()
...@@ -131,10 +179,13 @@ export default { ...@@ -131,10 +179,13 @@ export default {
}) })
} }
}, },
// Initialize stylized map, nightlife places markers and crime heatmap
/*********************
GOOGLE MAPS API
**********************/
/* Initialize stylized map, nightlife places markers and crime heatmap */
async initMap() { async initMap() {
var heatmapData = [] var heatmapData = []
var infoWindow = new google.maps.InfoWindow()
// Initialize the map instance, load the styles from the vuex store // Initialize the map instance, load the styles from the vuex store
var map = new google.maps.Map(this.$refs['map'], { var map = new google.maps.Map(this.$refs['map'], {
center: new google.maps.LatLng(this.center.lat, this.center.lng), center: new google.maps.LatLng(this.center.lat, this.center.lng),
...@@ -149,15 +200,49 @@ export default { ...@@ -149,15 +200,49 @@ export default {
map: map map: map
}) })
// Add marker for every nightlife establishment along with an info window // Save heatmap and map instance for use in toggleHeatmap()
this.heatmap = heatmap
this.map = map
// Wait for google Places API to be finished fetching relevant data
await this.findPlaces() await this.findPlaces()
// Mark all relevant places on the map
this.mapsInitPlaces()
// Add all signals saved in the store on the map
for (const i in this.signals) {
let signal = this.signals[i]
this.mapsAddSignal(signal)
}
// Wait for police.uk API to return crime data
await this.getCrimeData()
// Create a heatmap based on crimes location
this.crimes.forEach(crime => {
const lat = crime.location.latitude
const lng = crime.location.longitude
heatmapData.push(new google.maps.LatLng(lat, lng))
})
var heatmap = new google.maps.visualization.HeatmapLayer({
data: heatmapData
})
heatmap.set('radius', heatmap.get('radius') ? null : 80)
heatmap.set('opacity', heatmap.get('opacity') ? null : 0.3)
},
/* Creates markers and infoWindows for every relevant nightlife establishment in the area */
mapsInitPlaces() {
var infoWindow = new google.maps.InfoWindow()
this.places.forEach(place => { this.places.forEach(place => {
const lat = place.geometry.location.lat const lat = place.geometry.location.lat
const lng = place.geometry.location.lng const lng = place.geometry.location.lng
let marker = new google.maps.Marker({ let marker = new google.maps.Marker({
position: new google.maps.LatLng(lat, lng), position: new google.maps.LatLng(lat, lng),
map: map, map: this.map,
icon: this.getPlaceIcon(place) icon: this.getPlaceIcon(place)
}) })
...@@ -167,74 +252,79 @@ export default { ...@@ -167,74 +252,79 @@ export default {
<div class="col-7"> <p class="text-subtitle2">${place.name}</p> <div class="col-7"> <p class="text-subtitle2">${place.name}</p>
<p class="text-weight-light">${place.vicinity}</p> <p>${place.rating} ⭐</p></div></div>` <p class="text-weight-light">${place.vicinity}</p> <p>${place.rating} ⭐</p></div></div>`
) )
infoWindow.open(map, marker) infoWindow.open(this.map, marker)
}) })
}) })
},
// Create a heatmap based on crimes location /* Code snippet to add a marker and info window on the map for a signal */
await this.getCrimeData() mapsAddSignal(signal) {
this.crimes.forEach(crime => { var infoWindow = new google.maps.InfoWindow()
const lat = crime.location.latitude
const lng = crime.location.longitude
heatmapData.push(new google.maps.LatLng(lat, lng)) let marker = new google.maps.Marker({
position: new google.maps.LatLng(signal.lat, signal.lng),
map: this.map,
icon: this.getSignalIcon(signal.type)
}) })
var heatmap = new google.maps.visualization.HeatmapLayer({ google.maps.event.addListener(marker, 'click', () => {
data: heatmapData infoWindow.setContent(
`<p class="text-subtitle1" style="margin-bottom:8px;">${signal.type}</p>
<p class="text-weight-light">${signal.details}</p>`
)
infoWindow.open(this.map, marker)
}) })
heatmap.set('radius', heatmap.get('radius') ? null : 80)
heatmap.set('opacity', heatmap.get('opacity') ? null : 0.3)
// Save heatmap and map instance for use in toggleHeatmap()
this.heatmap = heatmap
this.map = map
}, },
/********************* /* Returns the image source for every type of signal */
GET PLACE'S DETAILS getSignalIcon(signalType) {
**********************/ switch (signalType) {
// Return html element, either Open or closed case 'Danger':
isPlaceOpen(place) { return 'https://img.icons8.com/color/50/000000/error--v1.png'
if (place.opening_hours) { case 'Emergency':
let htmlElement = `<p class="text-negative">Closed</p>` return 'https://img.icons8.com/color/50/000000/ambulance.png'
if (place.opening_hours.open_now) { case 'Fight Breakout':
htmlElement = `<p class="text-positive">Open</p>` return 'https://img.icons8.com/color/50/000000/judo.png'
} case 'Free Drinks':
return htmlElement return 'https://img.icons8.com/color/50/000000/dizzy-person.png'
case 'Car Rental':
return 'https://img.icons8.com/color/50/000000/car-rental.png'
} }
return ''
}, },
// Return hmtl img element of first picture listed for establishment, if any ...mapActions('firebase', ['firebaseSavePosition'])
getPlaceImage(place) {
if (place.photos) {
const URL =
`https://secret-ocean-49799.herokuapp.com/https://maps.googleapis.com/maps/api/place/photo?maxwidth=120&key=AIzaSyBPUdoB3wV6A9L-H1-J5POiQRmgqqcL9Bk&photoreference=` +
place.photos[0].photo_reference
return `<img src="${URL}" height="80" width="60">`
}
return ''
},
// Return specific icon based on first element of 'types' attribute for an establishment
getPlaceIcon(place) {
let icon = 'https://img.icons8.com/color/40/000000/disco-ball.png'
if (place.types[0] == 'bar') {
icon = 'https://img.icons8.com/color/40/000000/beer.png'
} else if (place.types[0] == 'restaurant') {
icon = 'https://img.icons8.com/color/40/000000/food-and-wine.png'
}
return icon
}
}, },
computed: { computed: {
...mapState(['mapStyles']) ...mapState(['mapStyles']),
...mapState('firebase', ['center', 'signals', 'latestSignalKey'])
},
watch: {
// Keep an eye on the signal object in the store
signals: {
deep: true,
// Whenever a new signal gets added, add it to the map
handler() {
let signal = this.signals[this.latestSignalKey]
this.mapsAddSignal(signal)
}
}
}, },
async mounted() { async mounted() {
// Wait for current location to be fetched and saved to store
await this.geolocate() await this.geolocate()
// Wait for map and map components to be loaded
await this.initMap() await this.initMap()
// Geolocate user every 30 seconds if on Index page
this.interval = window.setInterval(() => {
this.geolocate()
}, 30000)
},
unmounted() {
// Stop geolocating user if not on Index page
window.clearInterval(this.interval)
} }
} }
</script> </script>
...@@ -34,6 +34,8 @@ q-card.full-width ...@@ -34,6 +34,8 @@ q-card.full-width
</template> </template>
<script> <script>
import { mapActions } from 'vuex'
export default { export default {
data() { data() {
return { return {
...@@ -45,8 +47,9 @@ export default { ...@@ -45,8 +47,9 @@ export default {
methods: { methods: {
submitSignal() { submitSignal() {
console.log(this.signalType) this.firebaseSendSignal({ type: this.signalType, details: this.details })
} },
...mapActions('firebase', ['firebaseSendSignal'])
}, },
computed: { computed: {
......
<template lang="pug"> <template lang="pug">
q-page.q-pa-lg.column.justify-end.page-chat(ref='pageChat') q-page.q-pa-lg.q-mb-xl.column.justify-end.page-chat(ref='pageChat')
q-chat-message.text-weight-light( q-chat-message.text-weight-light(
v-for='(message, key) in messages', v-for='(message, key) in messages',
:key='key', :key='key',
...@@ -11,19 +11,18 @@ q-page.q-pa-lg.column.justify-end.page-chat(ref='pageChat') ...@@ -11,19 +11,18 @@ q-page.q-pa-lg.column.justify-end.page-chat(ref='pageChat')
avatar='https://cdn.quasar.dev/img/avatar1.jpg' avatar='https://cdn.quasar.dev/img/avatar1.jpg'
) )
q-footer(elevated) q-footer(elevated)
q-toolbar .fixed-bottom.q-pa-xs.bg-primary.q-px-sm
q-form.full-width(@submit='sendMessage') q-form.full-width.row(@submit='sendMessage')
.row q-input.col-11(
q-input.col-11( v-model='newMessage',
v-model='newMessage', ref='newMessage',
ref='newMessage', bg-color='white',
bg-color='white', outlined,
outlined, rounded,
rounded, label='Message',
label='Message', dense
dense )
) q-btn.col-1(type='submit', icon='send', color='white', round, dense, flat)
q-btn.col-1(type='submit', icon='send', color='white', round, dense, flat)
</template> </template>
<script> <script>
...@@ -58,7 +57,6 @@ export default { ...@@ -58,7 +57,6 @@ export default {
otherUserId: this.$route.params.otherUserId otherUserId: this.$route.params.otherUserId
}) })
this.clearMessage() this.clearMessage()
this.scrollToBottom()
}, },
clearMessage() { clearMessage() {
...@@ -80,11 +78,17 @@ export default { ...@@ -80,11 +78,17 @@ export default {
...mapState('firebase', ['messages', 'userDetails']) ...mapState('firebase', ['messages', 'userDetails'])
}, },
watch: {
messages: {
deep: true,
handler() {
this.scrollToBottom()
}
}
},
mounted() { mounted() {
this.firebaseGetMessages(this.$route.params.otherUserId) this.firebaseGetMessages(this.$route.params.otherUserId)
setTimeout(() => {
this.scrollToBottom()
}, 150)
}, },
// Fire when the user leaves the page // Fire when the user leaves the page
......
...@@ -7,8 +7,7 @@ ...@@ -7,8 +7,7 @@
"phone": null, "phone": null,
"dob": null, "dob": null,
"gender": null, "gender": null,
"description": null, "description": null
"online": false
}, },
"Oqk3C1ownieYIm8K0KgFqRXhKs53": { "Oqk3C1ownieYIm8K0KgFqRXhKs53": {
"name": "Scientific Pig", "name": "Scientific Pig",
...@@ -17,8 +16,7 @@ ...@@ -17,8 +16,7 @@
"phone": null, "phone": null,
"dob": null, "dob": null,
"gender": null, "gender": null,
"description": null, "description": null
"online": false
}, },
"l7TY1PJocphLufQb3zN1tfRJYsy1": { "l7TY1PJocphLufQb3zN1tfRJYsy1": {
"name": "how is this working lmao", "name": "how is this working lmao",
...@@ -27,8 +25,7 @@ ...@@ -27,8 +25,7 @@
"phone": null, "phone": null,
"dob": null, "dob": null,
"gender": null, "gender": null,
"description": null, "description": null
"online": false
}, },
"oVHXZPeYRbQPfKTCmH4LWJtmaeH2": { "oVHXZPeYRbQPfKTCmH4LWJtmaeH2": {
"name": "Eric Sauce", "name": "Eric Sauce",
...@@ -37,8 +34,7 @@ ...@@ -37,8 +34,7 @@
"phone": null, "phone": null,
"dob": null, "dob": null,
"gender": null, "gender": null,
"description": null, "description": null
"online": false
} }
}, },
"chats": { "chats": {
...@@ -77,5 +73,11 @@ ...@@ -77,5 +73,11 @@
"oVHXZPeYRbQPfKTCmH4LWJtmaeH2": true "oVHXZPeYRbQPfKTCmH4LWJtmaeH2": true
} }
} }
},
"status": {
"9HsxN1tMHNOfmQV3E16LNfcFTdc2": false,
"Oqk3C1ownieYIm8K0KgFqRXhKs53": false,
"l7TY1PJocphLufQb3zN1tfRJYsy1": false,
"oVHXZPeYRbQPfKTCmH4LWJtmaeH2": false
} }
} }
\ No newline at end of file
...@@ -4,39 +4,69 @@ let messagesRef ...@@ -4,39 +4,69 @@ let messagesRef
const state = { const state = {
userDetails: {}, userDetails: {},
center: {},
messages: {}, messages: {},
friends: {}, friends: {},
pending: {} pending: {},
signals: {},
latestSignalKey: null
} }
const mutations = { const mutations = {
// Assigns userDetails' value to given payload
setUserDetails(state, payload) { setUserDetails(state, payload) {
state.userDetails = payload state.userDetails = payload
}, },
// Assigns center' value to current lat and long of user
setUserCenter(state, payload) {
state.center = payload
},
// Adds a message in 'messages' object
addMessage(state, payload) {
state.messages[payload.messageId] = payload.messageDetails
},
// Assign an empty object to 'messages', effectively clearing all saved messages
clearMessages(state) {
state.messages = {}
},
// Adds a friend details to 'friends' object
addFriend(state, payload) { addFriend(state, payload) {
state.friends[payload.userId] = payload.userDetails state.friends[payload.userId] = payload.userDetails
}, },
// Modify friends' online status as they go offline / get online
updateFriendStatus(state, payload) {
state.friends[payload.userId].online = payload.online
},
// Adds a pending friend request in 'pending' object
addPending(state, payload) { addPending(state, payload) {
state.pending[payload.userId] = payload.userDetails state.pending[payload.userId] = payload.userDetails
}, },
// Removes a pending friend request from 'pending' object
removePending(state, payload) { removePending(state, payload) {
let key = payload let key = payload
delete state.pending[key] delete state.pending[key]
}, },
addMessage(state, payload) { // Adds a signal in 'signals' object
state.messages[payload.messageId] = payload.messageDetails addSignal(state, payload) {
}, let signalId = Object.keys(payload)[0]
state.signals[signalId] = payload[signalId]
clearMessages(state) { state.latestSignalKey = signalId
state.messages = {}
} }
} }
const actions = { const actions = {
/***********************
AUTH
************************/
/* Call firebase auth method to login a user */
loginUser({}, payload) { loginUser({}, payload) {
firebaseAuth.signInWithEmailAndPassword(payload.email, payload.password) firebaseAuth.signInWithEmailAndPassword(payload.email, payload.password)
.then(response => { .then(response => {
...@@ -47,10 +77,13 @@ const actions = { ...@@ -47,10 +77,13 @@ const actions = {
}) })
}, },
/* Call firebase auth method to logout a user */
logoutUser({}, payload) { logoutUser({}, payload) {
firebaseAuth.signOut() firebaseAuth.signOut()
}, },
/* Call firebase auth method to register a user with email and password and
create an instance of the new user in the Realtime Database */
registerUser({}, payload) { registerUser({}, payload) {
firebaseAuth.createUserWithEmailAndPassword(payload.email, payload.password) firebaseAuth.createUserWithEmailAndPassword(payload.email, payload.password)
.then(response => { .then(response => {
...@@ -59,7 +92,9 @@ const actions = { ...@@ -59,7 +92,9 @@ const actions = {
name: payload.name, name: payload.name,
username: payload.username, username: payload.username,
email: payload.email, email: payload.email,
online: true })
firebaseDb.ref('status/' + userId).set({
online: true,
}) })
}) })
.catch(error => { .catch(error => {
...@@ -67,13 +102,17 @@ const actions = { ...@@ -67,13 +102,17 @@ const actions = {
}) })
}, },
/* This method is called when mounting the app and will therefore fire when the app is first opened or reloaded
It handles the actions to take depending on whether the user is logged in or logged out*/
handleAuthStateChanged({ state, commit, dispatch }) { handleAuthStateChanged({ state, commit, dispatch }) {
firebaseAuth.onAuthStateChanged(user => { firebaseAuth.onAuthStateChanged(user => {
// If user is logged in
if(user) { if(user) {
// User logged in
let userId = firebaseAuth.currentUser.uid let userId = firebaseAuth.currentUser.uid
// Fetch details for current user
firebaseDb.ref('users/' + userId).once('value', snapshot => { firebaseDb.ref('users/' + userId).once('value', snapshot => {
let userDetails = snapshot.val() let userDetails = snapshot.val()
// Save details in store
commit('setUserDetails', { commit('setUserDetails', {
name: userDetails.name, name: userDetails.name,
username: userDetails.username, username: userDetails.username,
...@@ -81,38 +120,49 @@ const actions = { ...@@ -81,38 +120,49 @@ const actions = {
userId: userId userId: userId
}) })
}) })
dispatch('firebaseUpdateUser', { // Get friends for current user and save them in the store
userId: userId,
updates: {
online: true
}
})
dispatch('firebaseGetFriends', userId) dispatch('firebaseGetFriends', userId)
this.$router.push('/') // Get all signals in db and save them in the store
dispatch('firebaseGetSignals')
// Handle online status
firebaseDb.ref('.info/connected').on('value', (snap) => {
if (snap.val() === true) {
// Change online status to true when the app is currently open
firebaseDb.ref('status/' + userId).update({online: true})
// Set online status to false when connection to firebase is lost
firebaseDb.ref('status/' + userId).onDisconnect().update({online: false})
}
});
this.$router.push('/') // Go to Index page
} }
// Else if user has logged out
else if(state.userDetails.userId) { else if(state.userDetails.userId) {
// User logged out // Update offline status to false
dispatch('firebaseUpdateUser', { firebaseDb.ref('status/' + state.userDetails.userId).update({online: false})
userId: state.userDetails.userId, // Empty local user details
updates: {
online: false
}
})
commit('setUserDetails', {}) commit('setUserDetails', {})
this.$router.replace('/auth') // Go to Login page
this.$router.replace('/auth')
}
// Else if user does not have account send him to registration
else {
this.$router.replace('/auth') // Go to Login page
} }
}) })
}, },
firebaseUpdateUser({}, payload) { /***********************
firebaseDb.ref('users/' + payload.userId).update(payload.updates) FRIENDS
}, ************************/
/* Get all of the current user's friends and save them to the store, while adding
firebaseGetFriends({ commit }, payload) { a listener to automatically add to the store new entries were the database to change */
firebaseGetFriends({ commit, dispatch }, payload) {
let userId = payload let userId = payload
// Fetch current user from the db's friends node
firebaseDb.ref('friends/' + userId + '/friendList').on('child_added', snapshot => { firebaseDb.ref('friends/' + userId + '/friendList').on('child_added', snapshot => {
let otherUserId = snapshot.key let otherUserId = snapshot.key
// For every friend, add details inside 'friends' store object
firebaseDb.ref('users').child(otherUserId).once('value', snapshot => { firebaseDb.ref('users').child(otherUserId).once('value', snapshot => {
let userDetails = snapshot.val() let userDetails = snapshot.val()
commit('addFriend', { commit('addFriend', {
...@@ -120,8 +170,10 @@ const actions = { ...@@ -120,8 +170,10 @@ const actions = {
userDetails userDetails
}) })
}) })
// Get the online status of every friend
dispatch('firebaseTrackOnlineStatus', otherUserId)
}) })
// Fetch all pending friend request and save them in the store
firebaseDb.ref('friends/' + userId + '/pending').on('child_added', snapshot => { firebaseDb.ref('friends/' + userId + '/pending').on('child_added', snapshot => {
let otherUserId = snapshot.key let otherUserId = snapshot.key
firebaseDb.ref('users').child(otherUserId).once('value', snapshot => { firebaseDb.ref('users').child(otherUserId).once('value', snapshot => {
...@@ -134,46 +186,102 @@ const actions = { ...@@ -134,46 +186,102 @@ const actions = {
}) })
}, },
/* Updates firebase's db to reflect new pending friend request */
async firebaseSendFriendRequest({ state }, payload) { async firebaseSendFriendRequest({ state }, payload) {
let otherUsername = payload let otherUsername = payload
let requestObject = {} let requestObject = {}
requestObject[state.userDetails.userId] = true requestObject[state.userDetails.userId] = true // { userKey: true }
const snapshot = await firebaseDb.ref('users').orderByChild('username').equalTo(otherUsername).once('value')
let otherUserId = Object.keys(snapshot.val())[0] // Wait for firebase to query db and return the other user's node
const snapshot = await firebaseDb.ref('users').orderByChild('username').equalTo(otherUsername).once('value')
// Get the other user's key
let otherUserId = Object.keys(snapshot.val())[0]
// Add request objects inside pending property in db
firebaseDb.ref('friends/' + otherUserId + '/pending').update(requestObject) firebaseDb.ref('friends/' + otherUserId + '/pending').update(requestObject)
}, },
/* Removes pending request and adds new friend to Realtime Database + local state */
firebaseRemovePending({ state, commit }, payload) {
let otherUserId = payload
commit('removePending', payload)
firebaseDb.ref('friends/' + state.userDetails.userId + '/pending/' + otherUserId).remove()
},
firebaseAcceptRequest({ dispatch }, payload) { firebaseAcceptRequest({ dispatch }, payload) {
dispatch('firebaseRemovePending', payload) // Remove request from the store's pending object
dispatch('firebaseRemovePending', payload)
let friendId = payload let friendId = payload
let friendObject = {} let friendObject = {}
friendObject[friendId] = true friendObject[friendId] = true // {friendKey: true}
// Reflect the new friend in your db's friend list
firebaseDb.ref('friends/' + state.userDetails.userId + '/friendList').update(friendObject) firebaseDb.ref('friends/' + state.userDetails.userId + '/friendList').update(friendObject)
friendObject = {} friendObject = {}
friendObject[state.userDetails.userId] = true friendObject[state.userDetails.userId] = true // {yourkey: true}
// Reflect your friendship in the other user's friend list
firebaseDb.ref('friends/' + friendId + '/friendList').update(friendObject) firebaseDb.ref('friends/' + friendId + '/friendList').update(friendObject)
// Start tracking the online status of the new added friends
dispatch('firebaseTrackOnlineStatus', friendId)
},
/* Remove pending request from firebase's Realtime Database and local state */
firebaseRemovePending({ state, commit }, payload) {
let otherUserId = payload
commit('removePending', payload) // remove from store
firebaseDb.ref('friends/' + state.userDetails.userId + '/pending/' + otherUserId).remove() // remove drom db
}, },
/***********************
SIGNALS
************************/
/* Fetch from db all signals and save them to the store and add
a listener to automatically add new recorded signals */
firebaseGetSignals({ commit }) {
firebaseDb.ref('signals/').on('child_added', snapshot => {
let signal = {}
signal[snapshot.key] = snapshot.val()
commit('addSignal', signal)
})
},
/* Add signal under signals node in firebase's sb */
firebaseSendSignal({ state, commit }, payload) {
let signal = payload
signal['lat'] = state.center.lat
signal['lng'] = state.center.lng
// Add signal to db
let signalRef = firebaseDb.ref('signals/').push(signal)
let newSignal = {}
newSignal[signalRef.key] = signal // {signalKey: signalContent}
// Add signal to the store's 'signal' object
commit('addSignal', newSignal)
},
/***********************
POSITION
************************/
/* Save your current position in store and db */
firebaseSavePosition({ state, commit }, payload) {
let center = payload
// Update position in the db
firebaseDb.ref('status/' + state.userDetails.userId).update({position: center})
// Update position in the store
commit('setUserCenter', center)
},
/***********************
MESSAGES
************************/
/* Saves in store all the messages between current user and another user
and add a listener to automatically add new messages */
firebaseGetMessages({ state, commit }, otherUserId) { firebaseGetMessages({ state, commit }, otherUserId) {
let userId = state.userDetails.userId let userId = state.userDetails.userId
messagesRef = firebaseDb.ref('chats/' + userId + '/' + otherUserId) messagesRef = firebaseDb.ref('chats/' + userId + '/' + otherUserId)
// Fetch all messages and add a listener for new messages
messagesRef.on( messagesRef.on(
'child_added', snapshot => { 'child_added', snapshot => {
let messageDetails = snapshot.val() let messageDetails = snapshot.val()
let messageId = snapshot.key let messageId = snapshot.key
// Add message to the store's 'messages' object
commit('addMessage', { commit('addMessage', {
messageId, messageId,
messageDetails messageDetails
...@@ -182,22 +290,48 @@ const actions = { ...@@ -182,22 +290,48 @@ const actions = {
) )
}, },
/* Removes listener checking for new messages */
firebaseStopGettingMessages({ commit }) { firebaseStopGettingMessages({ commit }) {
if (messagesRef) { if (messagesRef) {
messagesRef.off('child_added') messagesRef.off('child_added') // Remove listener
commit('clearMessages') commit('clearMessages') // Empty messages object
} }
}, },
/* Records sent messages to firebase's db (for both sender and recipient) */
firebaseSendMessage({}, payload) { firebaseSendMessage({}, payload) {
// Add message on messages node with the current user's key
firebaseDb.ref('chats/' + state.userDetails.userId + '/' + payload.otherUserId).push(payload.message) firebaseDb.ref('chats/' + state.userDetails.userId + '/' + payload.otherUserId).push(payload.message)
// Change recipient of message
payload.message.from = 'them' payload.message.from = 'them'
// Add message on messages node with the other user's key
firebaseDb.ref('chats/' + payload.otherUserId + '/' + state.userDetails.userId).push(payload.message) firebaseDb.ref('chats/' + payload.otherUserId + '/' + state.userDetails.userId).push(payload.message)
},
/***********************
OTHER
************************/
/* Tracks online status for a given user */
firebaseTrackOnlineStatus({ commit }, payload) {
let userId = payload
// Change saved friend online status in store whenever the value in the db changes
firebaseDb.ref('status/' + userId).on('value', snapshot => {
let status = snapshot.val()
commit('updateFriendStatus', {
userId: userId,
online: status.online
})
})
},
/* Updates user details in firebase's db */
firebaseUpdateUser({}, payload) {
firebaseDb.ref('users/' + payload.userId).update(payload.updates)
} }
} }
const getters = { const getters = {
// Return the property names for the userDetails object
userDetailsKeys: state => { userDetailsKeys: state => {
return Object.keys(state.userDetails) return Object.keys(state.userDetails)
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment