diff --git a/quasar.conf.js b/quasar.conf.js
index 304aaf8b97da9a4ecd16b63da0f3e3b95f37abba..70c88ed16e5d784c02ec62335237aed540a4fd72 100644
--- a/quasar.conf.js
+++ b/quasar.conf.js
@@ -97,7 +97,7 @@ module.exports = configure(function (/* ctx */) {
       // directives: [],
 
       // Quasar plugins
-      plugins: []
+      plugins: ['Notify']
     },
 
     // animations: 'all', // --- includes all animations
diff --git a/src/components/Friends.vue b/src/components/Friends.vue
index 64dcd74aaa6d8a6b175f6fb81466ef79487e48a2..32ab5cba20e10f50745ee8c68108bfb42bcd4dec 100644
--- a/src/components/Friends.vue
+++ b/src/components/Friends.vue
@@ -2,7 +2,7 @@
 q-toolbar.bg-primary.text-white.shadow-2
   q-toolbar-title Contacts
 q-list(bordered)
-  q-item.q-my-sm(v-for='(user, key) in users', :key='key', :to='"/chat/" + key', clickable, v-ripple)
+  q-item.q-my-sm(v-for='(user, key) in friends', :key='key', :to='"/chat/" + key', clickable)
     q-item-section(avatar)
       q-avatar(color='primary', text-color='white')
         | {{ user.name.charAt(0) }}
@@ -11,22 +11,62 @@ q-list(bordered)
       q-item-label(caption, lines='1') @{{ user.username }}
     q-item-section(side)
       q-icon(name='chat_bubble', :color='user.online ? "light-green-6" : "blue-grey-5"')
+q-item.absolute-bottom.q-mb-xl(v-for='(user, key) in pending', :key='key')
+  .text-weight-light.text-subtitle1 {{ user }}
+    q-icon.q-pl-sm.cursor-pointer(
+      @click='acceptRequest(key)',
+      name='check_circle_outline',
+      color='green',
+      left,
+      size='1.5rem'
+    )
+    q-icon.cursor-pointer(@click='denyRequest(key)', name='highlight_off', color='red', size='1.5rem')
 q-toolbar.absolute-bottom.bg-primary.text-white.shadow-2
-  q-btn(@click='addFriends()', icon='person_add', flat, dense, label='Add Friends')
+  q-btn(icon='person_add', flat, dense, label='Add Friends')
+  q-popup-edit(v-model='usernameInput', :cover='false', v-slot='scope', @keyup.enter='sendRequest()')
+    q-input(v-model='scope.value', dense, autofocus, counter, @keyup.enter='scope.set')
+      template(v-slot:prepend)
+        q-icon(name='person_add', color='primary')
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
+import { mapState, mapActions } from 'vuex'
 
 export default {
-  methods: {
-    addFriends() {
-      console.log('clicked')
+  data() {
+    return {
+      usernameInput: '@'
     }
   },
 
+  methods: {
+    showNotif() {
+      this.$q.notify({
+        message: 'Friend Request Sent',
+        color: 'pink',
+        actions: [
+          {
+            label: '✕',
+            color: 'white'
+          }
+        ]
+      })
+    },
+    acceptRequest(otherUserId) {
+      this.firebaseAcceptRequest(otherUserId)
+    },
+    denyRequest(otherUserId) {
+      this.firebaseRemovePending(otherUserId)
+    },
+    sendRequest() {
+      this.showNotif()
+      this.firebaseSendFriendRequest(this.usernameInput.slice(1))
+    },
+    ...mapActions('firebase', ['firebaseRemovePending', 'firebaseAcceptRequest', 'firebaseSendFriendRequest'])
+  },
+
   computed: {
-    ...mapGetters('firebase', ['users'])
+    ...mapState('firebase', ['friends', 'pending'])
   }
 }
 </script>
diff --git a/src/mixins/mixin-other-user-details.js b/src/mixins/mixin-other-user-details.js
index 0184609786f10e8d1909e33759634411803c3970..65d4fc8e71e625fdc61c21cb011640167db7c628 100644
--- a/src/mixins/mixin-other-user-details.js
+++ b/src/mixins/mixin-other-user-details.js
@@ -1,8 +1,8 @@
 export default {
   computed: {
     otherUserDetails() {
-      if (this.$store.state.firebase.users[this.$route.params.otherUserId]) {
-        return this.$store.state.firebase.users[this.$route.params.otherUserId]
+      if (this.$store.state.firebase.friends[this.$route.params.otherUserId]) {
+        return this.$store.state.firebase.friends[this.$route.params.otherUserId]
       }
       return {}
     },
diff --git a/src/pages/UserInfo.vue b/src/pages/UserInfo.vue
index cf7bbc565e97dd91289852583e5d6d4bae22fbd7..1826cd91a8465fc985c78d1f01721f8dd33641f9 100644
--- a/src/pages/UserInfo.vue
+++ b/src/pages/UserInfo.vue
@@ -6,22 +6,33 @@
       img(
         src='https://avataaars.io/?avatarStyle=Circle&topType=Eyepatch&facialHairType=Blank&clotheType=ShirtVNeck&clotheColor=Pink&eyeType=Squint&eyebrowType=AngryNatural&mouthType=Default&skinColor=Light'
       )
-  q-input.q-pb-md(v-model='name', label='Full Name', stack-label, :readonly='isEditing')
-  q-input.q-pb-md(v-model='username', label='Username', stack-label, :readonly='isEditing')
-  q-input.q-pb-md(v-model='password', label='Password', stack-label, :readonly='isEditing')
-  q-input.q-pb-md(v-model='email', label='Email', stack-label, :readonly='isEditing')
-  q-input.q-pb-md(v-model='phone', label='Phone Number', stack-label, :readonly='isEditing')
-  q-input.q-pb-lg(v-model='date', mask='date', :rules='["date"]', label='Date of Birth', :readonly='isEditing')
+  q-input.q-py-md(v-model='localDetails.name', label='Full Name', stack-label, :readonly='isEditing')
+  q-input.q-pb-md(v-model='localDetails.username', label='Username', stack-label, :readonly='isEditing')
+  q-input.q-pb-md(v-model='localDetails.email', label='Email', stack-label, :readonly='isEditing')
+  q-input.q-pb-md(v-model='localDetails.phone', label='Phone Number', stack-label, :readonly='isEditing')
+  q-input.q-pb-lg(
+    v-model='localDetails.date',
+    mask='date',
+    label='Date of Birth',
+    :rules='["date"]',
+    :readonly='isEditing'
+  )
     template(v-slot:append)
       q-icon.cursor-pointer(name='event')
         q-menu
-          q-date(v-model='date', :disable='isEditing')
+          q-date(v-model='localDetails.date', :disable='isEditing')
   .text-subtitle1.text-grey-9.q-pb-sm Gender:
-    q-radio.q-pr-sm.q-ml-md(v-model='gender', val='male', label='Male', color='cyan', :disable='isEditing')
-    q-radio.q-pr-sm(v-model='gender', val='female', label='Female', color='pink', :disable='isEditing')
-    q-radio.q-pr-sm(v-model='gender', val='other', label='Other', color='accent', :disable='isEditing')
+    q-radio.q-pr-sm.q-ml-md(
+      v-model='localDetails.gender',
+      val='male',
+      label='Male',
+      color='cyan',
+      :disable='isEditing'
+    )
+    q-radio.q-pr-sm(v-model='localDetails.gender', val='female', label='Female', color='pink', :disable='isEditing')
+    q-radio.q-pr-sm(v-model='localDetails.gender', val='other', label='Other', color='accent', :disable='isEditing')
   q-input.q-pb-xl(
-    v-model='description',
+    v-model='localDetails.description',
     label='Description',
     stack-label='',
     autogrow='',
@@ -29,29 +40,56 @@
   )
   div
     q-btn(@click='editing = !editing', color='blue', icon='mode_edit', label='Edit')
-    q-btn.float-right(icon='save', color='green', label='save')
+    q-btn.float-right(@click='saveUserDetails()', icon='save', color='green', label='save')
 </template>
 
 <script>
+import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
+
 export default {
   data() {
     return {
-      name: 'Eric Sauce',
-      username: 'Er1csauce',
-      password: 'Er1csauce14',
-      email: 'ek00722@surrey.ac.uk',
-      phone: '0123456789',
-      date: '2019/02/01',
-      gender: 'Male',
-      description: 'Very hot lad',
+      localDetails: {
+        name: null,
+        username: null,
+        email: null,
+        phone: null,
+        date: '1990/01/01',
+        gender: null,
+        description: null
+      },
       editing: false
     }
   },
 
+  methods: {
+    saveUserDetails() {
+      this.firebaseUpdateUser({
+        userId: this.userDetails.userId,
+        updates: this.localDetails
+      })
+
+      this.localDetails['userId'] = this.userDetails.userId
+      this.setUserDetails(JSON.parse(JSON.stringify(this.localDetails)))
+
+      this.editing = false
+    },
+    ...mapMutations('firebase', ['setUserDetails']),
+    ...mapActions('firebase', ['firebaseUpdateUser'])
+  },
+
   computed: {
     isEditing() {
       return this.editing ? false : true
-    }
+    },
+    ...mapState('firebase', ['userDetails']),
+    ...mapGetters('firebase', ['userDetailsKeys'])
+  },
+
+  mounted() {
+    this.userDetailsKeys.forEach(key => {
+      this.localDetails[key] = this.userDetails[key]
+    })
   }
 }
 </script>
diff --git a/src/static/data-structure.json b/src/static/data-structure.json
index 3933ceb971454054e6046d2a35e02881f1946a73..6f524638d086b319ce331ccb4a932f8b6d09b1ad 100644
--- a/src/static/data-structure.json
+++ b/src/static/data-structure.json
@@ -4,24 +4,40 @@
       "name": "Test Name",
       "username": "testusername",
       "email": "test@test.com",
+      "phone": null,
+      "dob": null,
+      "gender": null,
+      "description": null,
       "online": false
     },
     "Oqk3C1ownieYIm8K0KgFqRXhKs53": {
       "name": "Scientific Pig",
       "username": "dontlookitup",
       "email": "test3@test.com",
+      "phone": null,
+      "dob": null,
+      "gender": null,
+      "description": null,
       "online": false
     },
     "l7TY1PJocphLufQb3zN1tfRJYsy1": {
       "name": "how is this working lmao",
       "username": "amogus",
       "email": "test4@test.com",
+      "phone": null,
+      "dob": null,
+      "gender": null,
+      "description": null,
       "online": false
     },
     "oVHXZPeYRbQPfKTCmH4LWJtmaeH2": {
       "name": "Eric Sauce",
       "username": "theSaucer",
       "email": "test2@test.com",
+      "phone": null,
+      "dob": null,
+      "gender": null,
+      "description": null,
       "online": false
     }
   },
@@ -50,5 +66,16 @@
         }
       }
     }
+  },
+  "friends": {
+    "9HsxN1tMHNOfmQV3E16LNfcFTdc2": {
+      "friendList": {
+        "Oqk3C1ownieYIm8K0KgFqRXhKs53": true,
+        "l7TY1PJocphLufQb3zN1tfRJYsy1": true
+      },
+      "pending": {
+        "oVHXZPeYRbQPfKTCmH4LWJtmaeH2": true
+      }
+    }
   }
 }
\ No newline at end of file
diff --git a/src/store/firebase.js b/src/store/firebase.js
index 69976e6245fe4aff9dbb836267e76772f15c57dd..62927eed76efa5f406afd4c6571da48034e60c34 100644
--- a/src/store/firebase.js
+++ b/src/store/firebase.js
@@ -4,8 +4,9 @@ let messagesRef
 
 const state = {
   userDetails: {},
-  users: {},
-  messages: {}
+  messages: {},
+  friends: {},
+  pending: {}
 }
 
 const mutations = {
@@ -13,12 +14,17 @@ const mutations = {
     state.userDetails = payload
   },
 
-  addUser(state, payload) {
-    state.users[payload.userId] = payload.userDetails
+  addFriend(state, payload) {
+    state.friends[payload.userId] = payload.userDetails
   },
 
-  updateUser(state, payload) {
-    Object.assign(state.users[payload.userId], payload.userDetails)
+  addPending(state, payload) {
+    state.pending[payload.userId] = payload.userDetails
+  },
+
+  removePending(state, payload) {
+    let key = payload
+    delete state.pending[key]
   },
 
   addMessage(state, payload) {
@@ -81,7 +87,7 @@ const actions = {
             online: true
           }
         })
-        dispatch('firebaseGetUsers')
+        dispatch('firebaseGetFriends', userId)
         this.$router.push('/')
       }
       else if(state.userDetails.userId) {
@@ -102,25 +108,65 @@ const actions = {
     firebaseDb.ref('users/' + payload.userId).update(payload.updates)
   },
 
-  firebaseGetUsers({ commit }) {
-    firebaseDb.ref('users').on('child_added', snapshot => {
-      let userDetails = snapshot.val()
-      let userId = snapshot.key
-      commit('addUser', {
-        userId,
-        userDetails
+  firebaseGetFriends({ commit }, payload) {
+    let userId = payload
+
+    firebaseDb.ref('friends/' + userId + '/friendList').on('child_added', snapshot => {
+      let otherUserId = snapshot.key
+      firebaseDb.ref('users').child(otherUserId).once('value', snapshot => {
+        let userDetails = snapshot.val()
+        commit('addFriend', {
+          userId: otherUserId, 
+          userDetails
+        })
       })
     })
-    firebaseDb.ref('users').on('child_changed', snapshot => {
-      let userDetails = snapshot.val()
-      let userId = snapshot.key
-      commit('updateUser', {
-        userId,
-        userDetails
+
+    firebaseDb.ref('friends/' + userId + '/pending').on('child_added', snapshot => {
+      let otherUserId = snapshot.key
+      firebaseDb.ref('users').child(otherUserId).once('value', snapshot => {
+        let userDetails = snapshot.val().username
+        commit('addPending', {
+          userId: otherUserId, 
+          userDetails
+        })
       })
     })
   },
 
+  async firebaseSendFriendRequest({ state }, payload) {
+    let otherUsername = payload 
+    let requestObject = {}
+    requestObject[state.userDetails.userId] = true
+    
+    const snapshot = await firebaseDb.ref('users').orderByChild('username').equalTo(otherUsername).once('value')
+
+    let otherUserId = Object.keys(snapshot.val())[0]    
+    firebaseDb.ref('friends/' + otherUserId + '/pending').update(requestObject)
+  },
+
+
+  firebaseRemovePending({ state, commit }, payload) {
+    let otherUserId = payload
+    commit('removePending', payload)
+    firebaseDb.ref('friends/' + state.userDetails.userId + '/pending/' + otherUserId).remove()
+  },
+
+  firebaseAcceptRequest({ dispatch }, payload) {
+    dispatch('firebaseRemovePending', payload)
+
+    let friendId = payload
+    let friendObject = {}
+    friendObject[friendId] = true
+  
+    firebaseDb.ref('friends/' + state.userDetails.userId + '/friendList').update(friendObject)
+
+    friendObject = {}
+    friendObject[state.userDetails.userId] = true
+
+    firebaseDb.ref('friends/' + friendId + '/friendList').update(friendObject)
+  },
+
   firebaseGetMessages({ state, commit }, otherUserId) {
     let userId = state.userDetails.userId
     messagesRef = firebaseDb.ref('chats/' + userId + '/' + otherUserId)
@@ -152,14 +198,8 @@ const actions = {
 }
 
 const getters = {
-  users: state => {
-    let usersFiltered = {}
-    Object.keys(state.users).forEach(key => {
-      if (key !== state.userDetails.userId) {
-        usersFiltered[key] = state.users[key]
-      }
-    })
-    return usersFiltered
+  userDetailsKeys: state => {
+    return Object.keys(state.userDetails)
   }
 }