├── .gitignore
├── LICENSE
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ ├── logo.png
│ └── logo.svg
├── components
│ ├── Chat
│ │ ├── Chat.vue
│ │ ├── ChatList.vue
│ │ ├── Create.vue
│ │ └── parts
│ │ │ ├── Chats.vue
│ │ │ ├── Emoji.vue
│ │ │ ├── EmojiPicker.vue
│ │ │ ├── Image.vue
│ │ │ └── Message.vue
│ ├── Home.vue
│ ├── Shared
│ │ └── Alert.vue
│ └── User
│ │ ├── Profile.vue
│ │ ├── Signin.vue
│ │ └── Signup.vue
├── main.js
├── plugins
│ └── vuetify.js
├── router
│ ├── auth-guard.js
│ └── index.js
└── store
│ ├── AuthModule.js
│ ├── ChatModule.js
│ └── index.js
└── vue.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018-2020 Berk Emre Sarıbaş
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | A chat built with Vue + Vuex + Vuetify + Firebase.
4 | ## Features
5 | * Basic authentication with Firebase
6 | * Create chat rooms, join them
7 | * Use emoji-picker to express your emotions.
8 | * Scroll up to load previous messages.
9 |
10 | ## Live Demo
11 | https://mutfak-chat.firebaseapp.com
12 |
13 | ## Build Setup
14 |
15 | ``` bash
16 | # install dependencies
17 | npm install
18 |
19 | # serve with hot reload at localhost:8080
20 | npm run serve
21 |
22 | # build for production with minification
23 | npm run build
24 |
25 | # build for production and view the bundle analyzer report
26 | npm run build --report
27 | ```
28 |
29 | For detailed explanation on how things work, checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
30 |
31 | ## Configuration
32 |
33 | Edit main.js to initialize your Firebase App.
34 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vuetify-chat-2",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "core-js": "^3.4.4",
12 | "firebase": "^7.7.0",
13 | "vue": "^2.6.10",
14 | "vue-router": "^3.1.5",
15 | "vuetify": "^2.1.0",
16 | "vuex": "^3.1.2"
17 | },
18 | "devDependencies": {
19 | "@vue/cli-plugin-babel": "^4.1.0",
20 | "@vue/cli-plugin-eslint": "^4.1.0",
21 | "@vue/cli-service": "^4.1.0",
22 | "babel-eslint": "^10.0.3",
23 | "eslint": "^5.16.0",
24 | "eslint-plugin-vue": "^5.0.0",
25 | "sass": "^1.19.0",
26 | "sass-loader": "^8.0.0",
27 | "vue-cli-plugin-vuetify": "^2.0.3",
28 | "vue-template-compiler": "^2.6.10",
29 | "vuetify-loader": "^1.3.0"
30 | },
31 | "eslintConfig": {
32 | "root": true,
33 | "env": {
34 | "node": true
35 | },
36 | "extends": [
37 | "plugin:vue/essential",
38 | "eslint:recommended"
39 | ],
40 | "rules": {},
41 | "parserOptions": {
42 | "parser": "babel-eslint"
43 | }
44 | },
45 | "browserslist": [
46 | "> 1%",
47 | "last 2 versions"
48 | ]
49 | }
50 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/berksaribas/vuetify-chat/46b85bc5dbcb534b9ad74378f8df6bdbd299c296/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Vuetify Chat
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | mdi-account
8 |
9 |
10 | Online Users {{onlineUsers[0]}}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{user.user.username}}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Vuetify Chat
29 |
30 |
31 |
32 |
33 | {{ item.icon }}
34 | {{ item.title }}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/berksaribas/vuetify-chat/46b85bc5dbcb534b9ad74378f8df6bdbd299c296/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/components/Chat/Chat.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | mdi-emoticon-outline
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
170 |
171 |
232 |
--------------------------------------------------------------------------------
/src/components/Chat/ChatList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{chat.key}}
9 | {{chat.name}}
10 | {{chat.userCount}} members have joined this chat.
11 | Loading user count...
12 |
13 |
14 |
15 | Join
16 | Joined
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/components/Chat/Create.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
17 |
18 |
19 |
20 |
21 |
22 |
60 |
--------------------------------------------------------------------------------
/src/components/Chat/parts/Chats.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Your Chats
4 |
5 |
6 |
7 |
8 |
9 | mdi-chat
10 |
11 |
12 |
13 |
14 |
15 |
27 |
--------------------------------------------------------------------------------
/src/components/Chat/parts/Emoji.vue:
--------------------------------------------------------------------------------
1 |
2 | {{emoji.value}}
3 |
4 |
5 |
20 |
--------------------------------------------------------------------------------
/src/components/Chat/parts/EmojiPicker.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Emoji Picker
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
48 |
49 |
77 |
--------------------------------------------------------------------------------
/src/components/Chat/parts/Image.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
![]()
4 |
5 |
6 |
7 |
29 |
30 |
57 |
--------------------------------------------------------------------------------
/src/components/Chat/parts/Message.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{message.user}}
5 |
{{message.user}}
6 |
7 |
11 |
12 |
13 |
14 |
15 |
40 |
41 |
48 |
--------------------------------------------------------------------------------
/src/components/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Select a chat to start! Or create yours.
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
22 |
--------------------------------------------------------------------------------
/src/components/Shared/Alert.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ text }}
4 |
5 |
6 |
7 |
17 |
--------------------------------------------------------------------------------
/src/components/User/Profile.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/User/Signin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
86 |
--------------------------------------------------------------------------------
/src/components/User/Signup.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
113 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import vuetify from './plugins/vuetify'
4 | import * as firebase from 'firebase'
5 | import router from './router'
6 | import { store } from './store'
7 | import AlertComponent from './components/Shared/Alert.vue'
8 |
9 | Vue.config.productionTip = false
10 | Vue.component('app-alert', AlertComponent)
11 |
12 | new Vue({
13 | vuetify,
14 | router,
15 | store,
16 | render: h => h(App),
17 | created () {
18 | firebase.initializeApp({
19 | apiKey: 'AIzaSyBiqUbF0rN5LUD9G9msHN4xHF3f00MpOFE',
20 | authDomain: 'mutfak-chat.firebaseapp.com',
21 | databaseURL: 'https://mutfak-chat.firebaseio.com',
22 | projectId: 'mutfak-chat',
23 | storageBucket: 'mutfak-chat.appspot.com'
24 | })
25 | }
26 | }).$mount('#app')
27 |
--------------------------------------------------------------------------------
/src/plugins/vuetify.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuetify from 'vuetify/lib'
3 |
4 | Vue.use(Vuetify)
5 |
6 | export default new Vuetify({})
7 |
--------------------------------------------------------------------------------
/src/router/auth-guard.js:
--------------------------------------------------------------------------------
1 | import {store} from '../store/index'
2 |
3 | export default (to, from, next) => {
4 | if (store.getters.user) {
5 | next()
6 | } else {
7 | next('/login')
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import Home from '@/components/Home'
4 | import Chat from '@/components/Chat/Chat'
5 | import Create from '@/components/Chat/Create'
6 | import ChatList from '@/components/Chat/ChatList'
7 | import Profile from '@/components/User/Profile'
8 | import Signup from '@/components/User/Signup'
9 | import Signin from '@/components/User/Signin'
10 | import AuthGuard from './auth-guard'
11 |
12 | Vue.use(Router)
13 |
14 | export default new Router({
15 | routes: [
16 | {
17 | path: '/',
18 | name: 'Home',
19 | component: Home,
20 | beforeEnter: AuthGuard
21 | },
22 | {
23 | path: '/login',
24 | name: 'Signin',
25 | component: Signin
26 | },
27 | {
28 | path: '/register',
29 | name: 'Signup',
30 | component: Signup
31 | },
32 | {
33 | path: '/profile',
34 | name: 'Profile',
35 | component: Profile
36 | },
37 | {
38 | path: '/chat/:id',
39 | name: 'Chat',
40 | component: Chat,
41 | props: true,
42 | beforeEnter: AuthGuard
43 | },
44 | {
45 | path: '/create',
46 | name: 'CreateChat',
47 | component: Create,
48 | beforeEnter: AuthGuard
49 | },
50 | {
51 | path: '/discover',
52 | name: 'JoinChat',
53 | component: ChatList,
54 | beforeEnter: AuthGuard
55 | }
56 | ],
57 | mode: 'history'
58 | })
59 |
--------------------------------------------------------------------------------
/src/store/AuthModule.js:
--------------------------------------------------------------------------------
1 | import * as firebase from 'firebase'
2 |
3 | const AuthModule = {
4 | state: {
5 | user: null
6 | },
7 | mutations: {
8 | setUser (state, payload) {
9 | state.user = payload
10 | const userListRef = firebase.database().ref('presence')
11 | const myUserRef = userListRef.push()
12 |
13 | firebase.database().ref('.info/connected')
14 | .on(
15 | 'value', function (snap) {
16 | if (snap.val()) {
17 | // if we lose network then remove this user from the list
18 | myUserRef.onDisconnect()
19 | .remove()
20 | // set user's online status
21 | let presenceObject = {user: payload, status: 'online'}
22 | myUserRef.set(presenceObject)
23 | } else {
24 | // client has lost network
25 | let presenceObject = {user: payload, status: 'offline'}
26 | myUserRef.set(presenceObject)
27 | }
28 | }
29 | )
30 | }
31 | },
32 | actions: {
33 | signUserUp ({commit}, payload) {
34 | commit('setLoading', true)
35 | commit('clearError')
36 | firebase.auth().createUserWithEmailAndPassword(payload.email, payload.password)
37 | .then(
38 | auth => {
39 | firebase.database().ref('users').child(auth.user.uid).set({
40 | name: payload.username
41 | })
42 | .then(
43 | message => {
44 | commit('setLoading', false)
45 | const newUser = {
46 | id: auth.user.uid,
47 | username: payload.username
48 | }
49 | commit('setUser', newUser)
50 | }
51 | )
52 | .catch(
53 | error => {
54 | commit('setLoading', false)
55 | commit('setError', error)
56 | }
57 | )
58 | }
59 | )
60 | .catch(
61 | error => {
62 | commit('setLoading', false)
63 | commit('setError', error)
64 | }
65 | )
66 | },
67 | signUserIn ({commit}, payload) {
68 | commit('setLoading', true)
69 | commit('clearError')
70 | firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
71 | .then(
72 | auth => {
73 | firebase.database().ref('users').child(auth.user.uid).once('value', function (data) {
74 | commit('setLoading', false)
75 | const newUser = {
76 | id: auth.user.uid,
77 | username: auth.user.email
78 | }
79 | commit('setUser', newUser)
80 | })
81 | }
82 | )
83 | .catch(
84 | error => {
85 | commit('setLoading', false)
86 | commit('setError', error)
87 | }
88 | )
89 | }
90 | },
91 | getters: {
92 | user (state) {
93 | return state.user
94 | }
95 | }
96 | }
97 |
98 | export default AuthModule
99 |
--------------------------------------------------------------------------------
/src/store/ChatModule.js:
--------------------------------------------------------------------------------
1 | import * as firebase from 'firebase'
2 |
3 | const ChatModule = {
4 | state: {
5 | chats: {}
6 | },
7 | mutations: {
8 | setChats (state, payload) {
9 | payload["0"] = {name: "Default"}
10 | state.chats = payload
11 | }
12 | },
13 | actions: {
14 | sendMessage (context, payload) {
15 | let chatID = payload.chatID
16 | const message = {
17 | user: payload.username,
18 | content: payload.content,
19 | date: payload.date
20 | }
21 | firebase.database().ref('messages').child(chatID).child('messages').push(message)
22 | .then(
23 | (data) => {
24 | }
25 | )
26 | .catch(
27 | (error) => {
28 | console.log(error)
29 | }
30 | )
31 | },
32 | loadUserChats (context) {
33 | let user = context.getters.user
34 | firebase.database().ref('users').child(user.id).child('chats').orderByChild("timestamp").once("value", function(snapshot) {
35 | let chats = snapshot.val()
36 | if(chats == null) {
37 | chats = {}
38 | }
39 |
40 | for(let chatId in chats) {
41 | chats[chatId].name = "Loading..."
42 | firebase.database().ref('chats').child(chatId).once('value', function (snapshot) {
43 | chats[chatId].name = snapshot.val().name
44 | context.commit('setChats', chats)
45 | })
46 | }
47 | })
48 | }
49 | },
50 | getters: {
51 | chats (state) {
52 | return state.chats
53 | }
54 | }
55 | }
56 |
57 | export default ChatModule
58 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import * as firebase from 'firebase'
4 |
5 | import AuthModule from './AuthModule'
6 | import ChatModule from './ChatModule'
7 |
8 | Vue.use(Vuex)
9 |
10 | export const store = new Vuex.Store({
11 | modules: {
12 | auth: AuthModule,
13 | chat: ChatModule
14 | },
15 | state: {
16 | loading: false,
17 | error: null,
18 | onlineUsers: []
19 | },
20 | mutations: {
21 | setLoading (state, payload) {
22 | state.loading = payload
23 | },
24 | setError (state, payload) {
25 | state.error = payload
26 | },
27 | clearError (state) {
28 | state.error = null
29 | },
30 | setOnlineUsers (state, payload) {
31 | state.onlineUsers = payload
32 | }
33 | },
34 | actions: {
35 | loadOnlineUsers ({commit}) {
36 | firebase.database().ref('presence').on('value', function (snapshot) {
37 | let result = []
38 | result[0] = snapshot.numChildren()
39 | result[1] = snapshot.val()
40 | commit('setOnlineUsers', result)
41 | })
42 | },
43 | clearError ({commit}) {
44 | commit('clearError')
45 | }
46 | },
47 | getters: {
48 | loading (state) {
49 | return state.loading
50 | },
51 | error (state) {
52 | return state.error
53 | },
54 | onlineUsers (state) {
55 | return state.onlineUsers
56 | }
57 | }
58 | })
59 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "transpileDependencies": [
3 | "vuetify"
4 | ],
5 | lintOnSave: false
6 | }
--------------------------------------------------------------------------------