├── .env.example
├── .gitignore
├── README.md
├── babel.config.js
├── netlify.toml
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ └── css
│ │ ├── app.css
│ │ └── custom.css
├── components
│ ├── ChatList.vue
│ ├── ChatMessage.vue
│ ├── ChatRoom.vue
│ ├── Credits.vue
│ ├── Home.vue
│ ├── Login.vue
│ ├── Nav.vue
│ ├── User.vue
│ └── UserProfile.vue
├── firebase.js
└── main.js
├── tailwind.config.js
└── vue.config.js
/.env.example:
--------------------------------------------------------------------------------
1 | NODE_ENV=production
2 |
3 | VUE_APP_NAME=Qevoxo
4 | VUE_APP_ENV=production
5 | VUE_APP_DEBUG=false
6 |
7 | VUE_APP_FIREBASE_API=
8 | VUE_APP_FIREBASE_AUTH_DOMAIN=
9 | VUE_APP_FIREBASE_PROJECT_ID=
10 | VUE_APP_FIREBASE_DATABASE_URL=
11 | VUE_APP_FIREBASE_STORAGE_BUCKET=
12 | VUE_APP_FIREBASE_MESSAGING_SENDER_ID=
13 | VUE_APP_FIREBASE_APP_ID=
14 | VUE_APP_FIREBASE_MEASUREMENT_ID=
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # qevoxo
2 |
3 | Project is a work in progress.
4 |
5 | ## Project setup
6 | ```
7 | npm install
8 | ```
9 |
10 | ### Compiles and hot-reloads for development
11 | ```
12 | npm run serve
13 | ```
14 |
15 | ### Compiles and minifies for production
16 | ```
17 | npm run build
18 | ```
19 |
20 | ### Lints and fixes files
21 | ```
22 | npm run lint
23 | ```
24 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | command = "npm run build"
3 | publish = "dist"
4 |
5 | [[redirects]]
6 | from = "/*"
7 | to = "/index.html"
8 | status = 200
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "qevoxo",
3 | "version": "1.2.0",
4 | "private": false,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "@vue/composition-api": "^1.0.0-beta.6",
12 | "core-js": "^3.6.5",
13 | "firebase": "^7.17.1",
14 | "vue": "^2.6.11",
15 | "vue-router": "^3.3.4",
16 | "vuefire": "^2.2.3"
17 | },
18 | "devDependencies": {
19 | "@fortawesome/fontawesome-free": "^5.13.0",
20 | "@fullhuman/postcss-purgecss": "^2.2.0",
21 | "@tailwindcss/custom-forms": "^0.2.1",
22 | "@vue/cli-plugin-babel": "~4.4.0",
23 | "@vue/cli-plugin-eslint": "~4.4.0",
24 | "@vue/cli-service": "~4.4.0",
25 | "babel-eslint": "^10.1.0",
26 | "eslint": "^6.7.2",
27 | "eslint-plugin-vue": "^6.2.2",
28 | "moment": "^2.26.0",
29 | "simplebar-vue": "^1.5.1",
30 | "tailwindcss": "^1.5",
31 | "vue-template-compiler": "^2.6.11"
32 | },
33 | "eslintConfig": {
34 | "root": true,
35 | "env": {
36 | "node": true
37 | },
38 | "extends": [
39 | "plugin:vue/essential",
40 | "eslint:recommended"
41 | ],
42 | "parserOptions": {
43 | "parser": "babel-eslint"
44 | },
45 | "rules": {}
46 | },
47 | "browserslist": [
48 | "> 1%",
49 | "last 2 versions",
50 | "not dead"
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | const autoprefixer = require('autoprefixer');
2 | const tailwindcss = require('tailwindcss');
3 | const postcssPurgecss = require(`@fullhuman/postcss-purgecss`);
4 |
5 | const purgecss = postcssPurgecss({
6 | content: [
7 | './public/**/*.html',
8 | './src/**/*.vue',
9 | ],
10 |
11 | defaultExtractor: content => content.match(/[\w-/:]+(?
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
31 |
32 |
--------------------------------------------------------------------------------
/src/assets/css/app.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 |
3 | a {
4 | @apply .text-blue-500;
5 | }
6 |
7 | a:hover,
8 | a:focus {
9 | @apply .text-blue-600;
10 | }
11 |
12 | @tailwind components;
13 |
14 | @import './custom.css';
15 |
16 | @tailwind utilities;
17 |
--------------------------------------------------------------------------------
/src/assets/css/custom.css:
--------------------------------------------------------------------------------
1 | .btn {
2 | @apply .inline-block;
3 | @apply .px-4;
4 | @apply .py-0;
5 | @apply .leading-10;
6 | @apply .whitespace-no-wrap;
7 | @apply .rounded-lg;
8 | @apply .text-center;
9 | @apply .text-sm;
10 | @apply .font-medium;
11 | @apply .transition;
12 | @apply .ease-in-out;
13 | @apply .duration-150;
14 | @apply .shadow;
15 | }
16 |
17 | .btn:focus {
18 | @apply .outline-none;
19 | @apply .shadow-outline;
20 | }
21 |
22 | .btn.btn-primary {
23 | @apply .bg-blue-500;
24 | @apply .text-white;
25 | }
26 |
27 | .btn.btn-primary:hover {
28 | @apply .bg-blue-600;
29 | @apply .text-white;
30 | }
31 |
32 | .btn.btn-primary:focus {
33 | @apply .bg-blue-600;
34 | @apply .text-white;
35 | }
36 |
37 | .btn.btn-secondary {
38 | @apply .bg-white;
39 | @apply .text-gray-600;
40 | @apply .shadow;
41 | }
42 |
43 | .btn.btn-secondary:hover {
44 | @apply .text-gray-700;
45 | }
46 |
47 | .btn.btn-secondary:focus {
48 | @apply .text-gray-700;
49 | }
50 |
51 | .btn.btn-danger {
52 | @apply .bg-red-500;
53 | @apply .text-white;
54 | }
55 |
56 | .btn.btn-danger:hover {
57 | @apply .bg-red-600;
58 | @apply .text-white;
59 | }
60 |
61 | .btn.btn-danger:focus {
62 | @apply .bg-red-600;
63 | @apply .text-white;
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/ChatList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
14 |
Recent Chats
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Channel ID
26 |
27 |
28 | Join →
29 |
30 |
31 |
32 |
33 | {{ chat.id }}
34 |
35 |
36 |
37 |
38 |
39 | {{ diffForHumans(chat.createdAt) }}
40 |
41 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
No chats found
51 |
52 |
53 |
54 |
55 |
100 |
--------------------------------------------------------------------------------
/src/components/ChatMessage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ message.text }}
6 |
7 |
8 |
9 | {{ diffForHumans(message.createdAt) }}
10 |
11 |
12 |
13 |
14 |
15 |
28 |
--------------------------------------------------------------------------------
/src/components/ChatRoom.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Channel:
10 |
11 |
12 |
13 |
14 | {{ chatId }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Invite other members by sharing the link
24 |
25 |
26 |
27 |
28 | {{ chatLink }}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
47 |
48 |
49 |
50 |
51 |
52 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
137 |
138 |
143 |
--------------------------------------------------------------------------------
/src/components/Credits.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | © {{ year }} Qevoxo
5 |
6 |
7 |
8 |
9 |
20 |
--------------------------------------------------------------------------------
/src/components/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
26 |
27 |
--------------------------------------------------------------------------------
/src/components/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{ newUser ? 'Create an account' : 'Sign in' }}
6 |
7 | Let's get you up and running with a private channel.
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
20 |
25 |
26 |
27 |
28 | {{ error }}
29 |
30 |
31 |
46 |
47 |
48 |
49 |
50 |
51 |
88 |
89 |
--------------------------------------------------------------------------------
/src/components/Nav.vue:
--------------------------------------------------------------------------------
1 |
2 |
31 |
32 |
33 |
49 |
--------------------------------------------------------------------------------
/src/components/User.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
32 |
--------------------------------------------------------------------------------
/src/components/UserProfile.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Let's get started
6 |
7 | You are signed in as, {{ user.email }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Choose a already existing channel or create a new one to get started.
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
47 |
--------------------------------------------------------------------------------
/src/firebase.js:
--------------------------------------------------------------------------------
1 | import firebase from 'firebase/app';
2 | import 'firebase/firestore';
3 | import 'firebase/auth';
4 | import 'firebase/storage';
5 |
6 | const firebaseConfig = {
7 | apiKey: process.env.VUE_APP_FIREBASE_API,
8 | authDomain: process.env.VUE_APP_FIREBASE_AUTH_DOMAIN,
9 | projectId: process.env.VUE_APP_FIREBASE_PROJECT_ID,
10 | databaseURL: process.env.VUE_APP_FIREBASE_DATABASE_URL,
11 | storageBucket: process.env.VUE_APP_FIREBASE_STORAGE_BUCKET,
12 | messagingSenderId: process.env.VUE_APP_FIREBASE_MESSAGING_SENDER_ID,
13 | appId: process.env.VUE_APP_FIREBASE_APP_ID,
14 | measurementId: process.env.VUE_APP_FIREBASE_MEASUREMENT_ID,
15 | };
16 |
17 | firebase.initializeApp(firebaseConfig);
18 |
19 | export const db = firebase.firestore();
20 | export const auth = firebase.auth();
21 | export const storage = firebase.storage();
22 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import App from './App.vue';
3 | import { firestorePlugin } from 'vuefire';
4 | import VueRouter from 'vue-router';
5 | import VueCompositionApi from '@vue/composition-api';
6 |
7 | import './assets/css/app.css';
8 | import '@fortawesome/fontawesome-free/js/all.js';
9 |
10 | Vue.use(VueCompositionApi);
11 | Vue.use(VueRouter);
12 | Vue.use(firestorePlugin);
13 |
14 | Vue.config.productionTip = false;
15 |
16 | import Home from './components/Home';
17 | import ChatRoom from './components/ChatRoom';
18 |
19 | const router = new VueRouter({
20 | mode: 'history',
21 | routes: [
22 | {
23 | path: '/',
24 | component: Home,
25 | name: 'home'
26 | },
27 | {
28 | path: '/chats/:id',
29 | component: ChatRoom,
30 | name: 'chat'
31 | }
32 | ]
33 | });
34 |
35 | new Vue({
36 | router,
37 | render: h => h(App),
38 | }).$mount('#app')
39 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | theme: {
3 | customForms: theme => ({
4 | default: {
5 | input: {
6 | borderRadius: theme('borderRadius.lg'),
7 | backgroundColor: theme('colors.gray.100'),
8 | '&:focus': {
9 | backgroundColor: theme('colors.white'),
10 | }
11 | },
12 | textarea: {
13 | borderRadius: theme('borderRadius.md'),
14 | backgroundColor: theme('colors.gray.100'),
15 | '&:focus': {
16 | backgroundColor: theme('colors.white'),
17 | }
18 | },
19 | select: {
20 | borderRadius: theme('borderRadius.md'),
21 | boxShadow: theme('boxShadow.none'),
22 | backgroundColor: theme('colors.gray.100'),
23 | '&:focus': {
24 | backgroundColor: theme('colors.white'),
25 | }
26 | },
27 | checkbox: {
28 | backgroundColor: theme('colors.gray.100'),
29 | width: theme('spacing.6'),
30 | height: theme('spacing.6'),
31 | },
32 | radio: {
33 | backgroundColor: theme('colors.gray.100'),
34 | width: theme('spacing.6'),
35 | height: theme('spacing.6'),
36 | },
37 | },
38 | }),
39 | typography: theme => ({
40 | default: {
41 | css: {
42 | color: theme('colors.gray.600'),
43 | a: {
44 | color: '#0366D6',
45 | '&:hover': {
46 | color: '#035CC1',
47 | },
48 | },
49 | },
50 | },
51 | }),
52 | extend: {
53 | colors: {
54 | blue: {
55 | 100: '#E6F0FB',
56 | 200: '#C0D9F5',
57 | 300: '#9AC2EF',
58 | 400: '#4F94E2',
59 | 500: '#0366D6',
60 | 600: '#035CC1',
61 | 700: '#023D80',
62 | 800: '#012E60',
63 | 900: '#011F40',
64 | },
65 | }
66 | }
67 | },
68 | variants: {},
69 | plugins: [
70 | require('@tailwindcss/custom-forms'),
71 | ],
72 | }
73 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | chainWebpack: config => {
3 | config
4 | .plugin('html')
5 | .tap(args => {
6 | args[0].title = 'Qevoxo | Web Walkie Talkie'
7 | return args
8 | })
9 | }
10 | }
11 |
--------------------------------------------------------------------------------