mirror of
https://github.com/danbulant/Nertivia-Client
synced 2026-06-24 17:11:43 +00:00
Merge branch 'master' into polish
This commit is contained in:
commit
5045f96103
29 changed files with 1476 additions and 369 deletions
13
package-lock.json
generated
13
package-lock.json
generated
|
|
@ -4449,10 +4449,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"filesize": {
|
"filesize": {
|
||||||
"version": "3.6.1",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/filesize/-/filesize-4.1.2.tgz",
|
||||||
"integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==",
|
"integrity": "sha512-iSWteWtfNcrWQTkQw8ble2bnonSl7YJImsn9OZKpE2E4IHhXI78eASpDYUljXZZdYj36QsEKjOs/CsiDqmKMJw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"fill-range": {
|
"fill-range": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
|
|
@ -10842,6 +10841,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
||||||
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==",
|
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
},
|
||||||
|
"filesize": {
|
||||||
|
"version": "3.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
|
||||||
|
"integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==",
|
||||||
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
|
"filesize": "^4.1.2",
|
||||||
"futoji": "^0.2.4",
|
"futoji": "^0.2.4",
|
||||||
"jquery": "^3.3.1",
|
"jquery": "^3.3.1",
|
||||||
"socket.io": "^2.2.0",
|
"socket.io": "^2.2.0",
|
||||||
|
|
|
||||||
46
src/components/app/DragDropFileUploadDialog.vue
Normal file
46
src/components/app/DragDropFileUploadDialog.vue
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
<template>
|
||||||
|
<div class="drop-background">
|
||||||
|
<div class="box">
|
||||||
|
<i class="material-icons">insert_drive_file</i>
|
||||||
|
<div class="info">Drop file</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.drop-background {
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(0, 0, 0, 0.521);
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 999;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.box {
|
||||||
|
margin: auto;
|
||||||
|
height: 230px;
|
||||||
|
width: 300px;
|
||||||
|
background: rgba(0, 0, 0, 0.466);
|
||||||
|
border: solid 1px white;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.material-icons{
|
||||||
|
font-size: 80px;
|
||||||
|
color: white;
|
||||||
|
margin: auto;
|
||||||
|
margin-top: 50px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.info{
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -1,19 +1,60 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dark-background">
|
<div class="dark-background" @click="backgroundClick">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="text">
|
<div class="text">
|
||||||
To upload files and images, You must link your Google Drive account with your Nertivia account.
|
To upload files, images or set avatars, You must link your Google Drive account with your Nertivia account.
|
||||||
</div>
|
</div>
|
||||||
<div class="images">
|
<div class="images">
|
||||||
<div class="image GDrive-img"></div>
|
<div class="image GDrive-img"></div>
|
||||||
<div class="arrow">></div>
|
<div class="arrow">></div>
|
||||||
<div class="image nertivia-img"></div>
|
<div class="image nertivia-img"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button">Link me</div>
|
<div class="buttons">
|
||||||
|
<div class="button deny" @click="closeMenu">No thanks</div>
|
||||||
|
<div class="button" @click="link">Link me</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {bus} from '@/main'
|
||||||
|
import settingsService from '@/services/settingsService';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
closeMenu() {
|
||||||
|
this.$store.dispatch('setPopoutVisibility', {name: 'GDLinkMenu', visibility: false})
|
||||||
|
},
|
||||||
|
backgroundClick(e) {
|
||||||
|
if (e.target.classList.contains('dark-background')) {
|
||||||
|
this.closeMenu()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async link() {
|
||||||
|
const {ok, error, result} = await settingsService.GDriveURL();
|
||||||
|
if (ok) {
|
||||||
|
const {url} = result.data;
|
||||||
|
//open a new window
|
||||||
|
const left = (screen.width/2)-(400/2);
|
||||||
|
const top = (screen.height/2)-(500/2);
|
||||||
|
const consentWindow = window.open(url, "",
|
||||||
|
'width=400,height=500,top='+top+', left='+left
|
||||||
|
);
|
||||||
|
window.onmessage = async (e) => {
|
||||||
|
consentWindow.close();
|
||||||
|
if (!e.data.code) return;
|
||||||
|
const url = new URL(e.data.code);
|
||||||
|
const code = url.searchParams.get("code");
|
||||||
|
await settingsService.GDriveAuth(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.dark-background {
|
.dark-background {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
@ -22,7 +63,7 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background: rgba(0, 0, 0, 0.781);
|
background: rgba(0, 0, 0, 0.781);
|
||||||
z-index: 1;
|
z-index: 111111;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.inner {
|
.inner {
|
||||||
|
|
@ -30,16 +71,19 @@
|
||||||
height: 400px;
|
height: 400px;
|
||||||
width: 400px;
|
width: 400px;
|
||||||
background: rgb(32, 32, 32);
|
background: rgb(32, 32, 32);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
.text{
|
.text{
|
||||||
color: white;
|
color: white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
.images{
|
.images{
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -54,15 +98,23 @@
|
||||||
}
|
}
|
||||||
.nertivia-img {
|
.nertivia-img {
|
||||||
background-image: url(./../../assets/logo.png);
|
background-image: url(./../../assets/logo.png);
|
||||||
|
border-radius: 50%;
|
||||||
|
background-size: calc(100% + 34px);
|
||||||
|
box-shadow: 0px 0px 66px -4px rgba(69,212,255,1);
|
||||||
}
|
}
|
||||||
.arrow{
|
.arrow{
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
.GDrive-img {
|
.GDrive-img {
|
||||||
background-image: url(./../../assets/Google_Drive_logo.png);
|
background-image: url(./../../assets/Google_Drive_logo.png);
|
||||||
}
|
}
|
||||||
|
.buttons{
|
||||||
|
margin: auto;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
.button{
|
.button{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
@ -72,10 +124,18 @@
|
||||||
cursor: default;
|
cursor: default;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
transition: 0.3s;
|
transition: 0.3s;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
.button:hover {
|
.button:hover {
|
||||||
background:rgb(58, 134, 255);
|
background:rgb(58, 134, 255);
|
||||||
}
|
}
|
||||||
|
.button.deny {
|
||||||
|
background: rgb(255, 32, 32);
|
||||||
|
|
||||||
|
}
|
||||||
|
.button.deny:hover {
|
||||||
|
background: rgb(255, 53, 53);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,179 +1,284 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="{message: true, ownMessage: user.uniqueID === $props.uniqueID}">
|
<div :class="{message: true, ownMessage: user.uniqueID === $props.uniqueID}">
|
||||||
<div class="profile-picture" :style="`background-image: url(${userAvatar})`"></div>
|
<div class="profile-picture" :style="`background-image: url(${userAvatar})`"></div>
|
||||||
<div class="triangle">
|
<div class="triangle">
|
||||||
<div class="triangle-inner"></div>
|
<div class="triangle-inner"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
<div class="username">{{this.$props.username}}</div>
|
<div class="username">{{this.$props.username}}</div>
|
||||||
<div class="date">{{getDate}}</div>
|
<div class="date">{{getDate}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-message" v-html="formatMessage"></div>
|
<div class="content-message" v-html="formatMessage"></div>
|
||||||
</div>
|
|
||||||
<div class="sending-status">{{statusMessage}}</div>
|
<div class="file-content" v-if="getFile">
|
||||||
</div>
|
<div class="icon">
|
||||||
|
<i class="material-icons">insert_drive_file</i>
|
||||||
|
</div>
|
||||||
|
<div class="information">
|
||||||
|
<div class="info">{{getFile.fileName}}</div>
|
||||||
|
<a :href="getFile.url" target="_blank">
|
||||||
|
<div class="download-button">Download</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="image-content" v-if="getImage">
|
||||||
|
<img :src="getImage" @click="imageClicked">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sending-status">{{statusMessage}}</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import messageFormatter from '@/messageFormatter.js'
|
import messageFormatter from "@/messageFormatter.js";
|
||||||
import config from '@/config.js'
|
import config from "@/config.js";
|
||||||
import friendlyDate from '@/date'
|
import friendlyDate from "@/date";
|
||||||
export default {
|
import path from "path";
|
||||||
props: ['message', 'status', 'username', 'avatar', 'date', 'uniqueID'],
|
|
||||||
computed: {
|
|
||||||
formatMessage() {
|
|
||||||
return messageFormatter(this.$props.message)
|
|
||||||
},
|
|
||||||
getDate() {
|
|
||||||
return friendlyDate(this.$props.date);
|
|
||||||
},
|
|
||||||
userAvatar() {
|
|
||||||
return config.domain + "/avatars/" + this.$props.avatar
|
|
||||||
},
|
|
||||||
statusMessage(){
|
|
||||||
let status = this.$props.status;
|
|
||||||
|
|
||||||
if (status == 0) {
|
export default {
|
||||||
return "Sending"
|
props: [
|
||||||
} else if (status == 1) {
|
"message",
|
||||||
return "Sent"
|
"status",
|
||||||
} else if (status == 2) {
|
"username",
|
||||||
return "Failed"
|
"avatar",
|
||||||
} else {
|
"date",
|
||||||
return ""
|
"uniqueID",
|
||||||
}
|
"files"
|
||||||
},
|
],
|
||||||
user() {
|
methods: {
|
||||||
return this.$store.getters.user
|
imageClicked(event) {
|
||||||
}
|
this.$store.dispatch("setImagePreviewURL", event.target.src);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
computed: {
|
||||||
|
getImage() {
|
||||||
|
if (!this.$props.files || this.$props.files.length === 0)
|
||||||
|
return undefined;
|
||||||
|
const file = this.$props.files[0];
|
||||||
|
if (!file.fileID) return undefined;
|
||||||
|
const filetypes = /jpeg|jpg|gif|png/;
|
||||||
|
const extname = filetypes.test(path.extname(file.fileName).toLowerCase());
|
||||||
|
if (!extname) return undefined;
|
||||||
|
return config.domain + "/files/" + file.fileID;
|
||||||
|
},
|
||||||
|
getFile() {
|
||||||
|
if (!this.$props.files || this.$props.files.length === 0)
|
||||||
|
return undefined;
|
||||||
|
let file = this.$props.files[0];
|
||||||
|
if (!file.fileID) return undefined;
|
||||||
|
const filetypes = /jpeg|jpg|gif|png/;
|
||||||
|
const extname = filetypes.test(path.extname(file.fileName).toLowerCase());
|
||||||
|
if (extname) return undefined;
|
||||||
|
file.url = config.domain + '/files/' + file.fileID;
|
||||||
|
return file;
|
||||||
|
},
|
||||||
|
formatMessage() {
|
||||||
|
return messageFormatter(this.$props.message);
|
||||||
|
},
|
||||||
|
getDate() {
|
||||||
|
return friendlyDate(this.$props.date);
|
||||||
|
},
|
||||||
|
userAvatar() {
|
||||||
|
return config.domain + "/avatars/" + this.$props.avatar;
|
||||||
|
},
|
||||||
|
statusMessage() {
|
||||||
|
let status = this.$props.status;
|
||||||
|
|
||||||
|
if (status == 0) {
|
||||||
|
return "Sending";
|
||||||
|
} else if (status == 1) {
|
||||||
|
return "Sent";
|
||||||
|
} else if (status == 2) {
|
||||||
|
return "Failed";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
user() {
|
||||||
|
return this.$store.getters.user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.message {
|
||||||
|
margin: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
.message{
|
animation: showMessage 0.3s ease-in-out;
|
||||||
margin: 10px;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
animation: showMessage .3s ease-in-out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ownMessage .triangle-inner{
|
.ownMessage .triangle-inner {
|
||||||
border-right: 7px solid rgba(184, 184, 184, 0.219);
|
border-right: 7px solid rgba(184, 184, 184, 0.219);
|
||||||
|
}
|
||||||
|
.ownMessage .content {
|
||||||
|
background: rgba(184, 184, 184, 0.219);
|
||||||
|
}
|
||||||
|
.ownMessage .date {
|
||||||
|
color: rgb(209, 209, 209);
|
||||||
}
|
}
|
||||||
.ownMessage .content{
|
|
||||||
background: rgba(184, 184, 184, 0.219);
|
|
||||||
|
|
||||||
|
.file-content {
|
||||||
|
display: flex;
|
||||||
|
background: rgba(0, 0, 0, 0.089);
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.file-content .material-icons {
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
||||||
|
.file-content .download-button {
|
||||||
|
font-size: 14px;
|
||||||
|
background: rgba(0, 0, 0, 0.158);
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 3px;
|
||||||
|
text-align: center;
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 3px;
|
||||||
|
transition: 0.3s;
|
||||||
|
user-select: none;
|
||||||
|
cursor: default;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.file-content .download-button:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.329);
|
||||||
|
}
|
||||||
|
.file-content .info {
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-size: 14px;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 100%;
|
||||||
|
color: white;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
margin-top: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes showMessage {
|
@keyframes showMessage {
|
||||||
from {
|
from {
|
||||||
transform: translate(0px, 9px);
|
transform: translate(0px, 9px);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-picture{
|
.profile-picture {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
background-color: rgba(0, 0, 0, 0.281);
|
background-color: rgba(0, 0, 0, 0.281);
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
.triangle{
|
.triangle {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: bottom;
|
justify-content: bottom;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
margin-bottom: 8.7px;
|
margin-bottom: 8.7px;
|
||||||
}
|
}
|
||||||
.triangle-inner{
|
.triangle-inner {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|
||||||
border-top: 1px solid transparent;
|
border-top: 1px solid transparent;
|
||||||
border-bottom: 7px solid transparent;
|
border-bottom: 7px solid transparent;
|
||||||
border-right: 7px solid rgba(0, 0, 0, 0.301);
|
border-right: 7px solid rgba(0, 0, 0, 0.301);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content{
|
.content {
|
||||||
background: rgba(0, 0, 0, 0.301);
|
background: rgba(0, 0, 0, 0.301);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: rgb(231, 231, 231);
|
||||||
|
margin: auto;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
transition: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
display: flex;
|
.image-content {
|
||||||
justify-content: center;
|
margin-top: 10px;
|
||||||
flex-direction: column;
|
padding: 5px;
|
||||||
border-radius: 10px;
|
border-radius: 5px;
|
||||||
color: rgb(231, 231, 231);
|
background: rgba(0, 0, 0, 0.493);
|
||||||
margin: auto;
|
display: -ms-flexbox;
|
||||||
margin-left: 0;
|
display: flex;
|
||||||
margin-right: 0;
|
flex-direction: column;
|
||||||
transition: 1s;
|
}
|
||||||
|
.image-content img {
|
||||||
|
width: 170px;
|
||||||
|
height: auto;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
.image-content:hover img {
|
||||||
|
filter: brightness(70%);
|
||||||
}
|
}
|
||||||
.user-info {
|
.user-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.username {
|
.username {
|
||||||
color: rgb(219, 219, 219);
|
color: rgb(219, 219, 219);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
.date{
|
.date {
|
||||||
color: rgb(161, 161, 161);
|
color: rgb(161, 161, 161);
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
|
||||||
}
|
}
|
||||||
.content-message {
|
.content-message {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
color: white;
|
color: white;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
|
margin-top: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message .sending-status {
|
.message .sending-status {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: white;
|
color: white;
|
||||||
align-self: normal;
|
align-self: normal;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
transition: 0.5;
|
transition: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.codeblock{
|
.codeblock {
|
||||||
background-color: rgba(0, 0, 0, 0.397);
|
background-color: rgba(0, 0, 0, 0.397);
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openSettings() {
|
openSettings() {
|
||||||
bus.$emit('openSettings');
|
this.$store.dispatch('setPopoutVisibility', {name: 'settings', visibility: true})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
|
|
||||||
107
src/components/app/Popouts.vue
Normal file
107
src/components/app/Popouts.vue
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
<template>
|
||||||
|
<div class="popouts">
|
||||||
|
<transition-group name="show" mode="out-in">
|
||||||
|
<settings key="settings" v-if="popouts.settings"/>
|
||||||
|
<upload-dialog key="upload-dialog" v-if="popouts.uploadDialog"/>
|
||||||
|
<GDriveLinkMenu key="GDriveLinkMenu" v-if="popouts.GDLinkMenu"/>
|
||||||
|
<image-large-preview key="ilp" v-if="popouts.ImagePreviewURL"/>
|
||||||
|
<drag-drop-file-upload-dialog key="ddfud" v-if="showUploadDrapDrop"/>
|
||||||
|
</transition-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { bus } from "@/main";
|
||||||
|
//popouts
|
||||||
|
import Settings from "@/components/app/Settings.vue";
|
||||||
|
import uploadDialog from "@/components/app/uploadDialog.vue";
|
||||||
|
import GDriveLinkMenu from "@/components/app/GDriveLinkMenu.vue";
|
||||||
|
import imageLargePreview from "@/components/app/imageLargePreview.vue";
|
||||||
|
import DragDropFileUploadDialog from "@/components/app/DragDropFileUploadDialog.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Settings,
|
||||||
|
uploadDialog,
|
||||||
|
GDriveLinkMenu,
|
||||||
|
DragDropFileUploadDialog,
|
||||||
|
imageLargePreview
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showUploadDrapDrop: false,
|
||||||
|
uploadDialogLastTarget: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isFile(evt) {
|
||||||
|
var dt = evt.dataTransfer;
|
||||||
|
for (var i = 0; i < dt.types.length; i++) {
|
||||||
|
if (dt.types[i] === "Files") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
dragenter(event) {
|
||||||
|
if (!this.isFile(event) || !this.selectedChannelID) return;
|
||||||
|
|
||||||
|
this.lastTarget = event.target; // cache the last target here
|
||||||
|
this.showUploadDrapDrop = true;
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
dragleave(event) {
|
||||||
|
if (event.target === this.lastTarget || event.target === document) {
|
||||||
|
this.showUploadDrapDrop = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dragover(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
drop(event) {
|
||||||
|
this.showUploadDrapDrop = false;
|
||||||
|
event.preventDefault();
|
||||||
|
if (!event.dataTransfer.files.length || !this.selectedChannelID) return;
|
||||||
|
this.$store.dispatch("setFile", event.dataTransfer.files[0]);
|
||||||
|
this.$store.dispatch("setPopoutVisibility", {
|
||||||
|
name: "uploadDialog",
|
||||||
|
visibility: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
uploadDrapDropEvent() {
|
||||||
|
window.addEventListener("dragenter", this.dragenter);
|
||||||
|
window.addEventListener("dragleave", this.dragleave);
|
||||||
|
window.addEventListener("dragover", this.dragover);
|
||||||
|
window.addEventListener("drop", this.drop);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.uploadDrapDropEvent();
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
window.removeEventListener("dragenter", this.dragenter);
|
||||||
|
window.removeEventListener("dragleave", this.dragleave);
|
||||||
|
window.removeEventListener("dragover", this.dragover);
|
||||||
|
window.removeEventListener("drop", this.drop);
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
popouts() {
|
||||||
|
return this.$store.getters.popouts;
|
||||||
|
},
|
||||||
|
selectedChannelID() {
|
||||||
|
return this.$store.getters.selectedChannelID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.show-enter-active,
|
||||||
|
.show-leave-active {
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
.show-enter, .show-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -2,35 +2,63 @@
|
||||||
<div class="right-panel">
|
<div class="right-panel">
|
||||||
<div class="heading">
|
<div class="heading">
|
||||||
<div class="show-menu-button" cli>
|
<div class="show-menu-button" cli>
|
||||||
<i class="material-icons" @click="toggleLeftMenu">
|
<i class="material-icons" @click="toggleLeftMenu">menu</i>
|
||||||
menu
|
</div>
|
||||||
</i>
|
<div class="current-channel">
|
||||||
|
<span v-if="!selectedChannelID">Welcome back, {{user.username}}!</span>
|
||||||
|
<span v-else>{{channelName}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="current-channel"><span v-if="!selectedChannelID">Welcome back!</span><span v-else>{{channelName}}</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="loading" v-if="selectedChannelID && !messages[selectedChannelID]">
|
<div class="loading" v-if="selectedChannelID && !messages[selectedChannelID]">
|
||||||
<spinner />
|
<spinner/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="selectedChannelID" class="message-logs" @wheel="invertScroll">
|
<div v-else-if="selectedChannelID" class="message-logs" @wheel="invertScroll">
|
||||||
<div class="scroll">
|
<div class="scroll">
|
||||||
<message v-for="(msg, index) in messages[selectedChannelID]" :key="index" :date="msg.created" :username="msg.creator.username" :uniqueID="msg.creator.uniqueID" :avatar="msg.creator.avatar" :message="msg.message" :status="msg.status" />
|
<message
|
||||||
|
v-for="(msg, index) in messages[selectedChannelID]"
|
||||||
|
:key="index"
|
||||||
|
:date="msg.created"
|
||||||
|
:username="msg.creator.username"
|
||||||
|
:uniqueID="msg.creator.uniqueID"
|
||||||
|
:avatar="msg.creator.avatar"
|
||||||
|
:message="msg.message"
|
||||||
|
:files="msg.files"
|
||||||
|
:status="msg.status"
|
||||||
|
/>
|
||||||
|
<uploadsQueue v-if="uploadQueue !== undefined" :queue="uploadQueue"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<news v-else />
|
<news v-else/>
|
||||||
<div class="chat-input-area" v-if="selectedChannelID">
|
<div class="chat-input-area" v-if="selectedChannelID">
|
||||||
<div class="message-area">
|
<div class="message-area">
|
||||||
|
|
||||||
|
<input type="file" ref="sendFileBrowse" @change="attachmentChange" class="hidden">
|
||||||
|
<div class="attachment-button" @click="attachmentButton">
|
||||||
|
<i class="material-icons">attach_file</i>
|
||||||
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
rows="1"
|
|
||||||
class="chat-input"
|
class="chat-input"
|
||||||
|
rows="1"
|
||||||
ref="input-box"
|
ref="input-box"
|
||||||
placeholder="Message"
|
placeholder="Message"
|
||||||
@keydown="chatInput"
|
@keydown="chatInput"
|
||||||
|
|
||||||
@keyup="delayedResize"
|
@keyup="delayedResize"
|
||||||
@change="resize"
|
@change="resize"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
v-model="message"
|
v-model="message"
|
||||||
></textarea>
|
></textarea>
|
||||||
<button :class="{'send-button': true, 'error-send-button': messageLength > 5000}" @click="sendMessage">Send</button>
|
<button :class="{'send-button': true, 'error-send-button': messageLength > 5000}" @click="sendMessage">Send</button>
|
||||||
|
@input="onInput"
|
||||||
|
@paste="onPaste"
|
||||||
|
v-model="message"
|
||||||
|
></textarea>
|
||||||
|
<button
|
||||||
|
:class="{'send-button': true, 'error-send-button': messageLength > 5000}"
|
||||||
|
@click="sendMessage"
|
||||||
|
>
|
||||||
|
<i class="material-icons">send</i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="typing-outer">
|
<div class="typing-outer">
|
||||||
|
|
@ -38,29 +66,33 @@
|
||||||
<typing-status v-if="typing" :username="whosTyping"/>
|
<typing-status v-if="typing" :username="whosTyping"/>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
<div :class="{'message-count': true, 'error-info': messageLength > 5000 }">{{messageLength}}/5000</div>
|
<div
|
||||||
|
:class="{'message-count': true, 'error-info': messageLength > 5000 }"
|
||||||
|
>{{messageLength}}/5000</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import messagesService from '@/services/messagesService'
|
import messagesService from "@/services/messagesService";
|
||||||
import typingService from '@/services/TypingService'
|
import typingService from "@/services/TypingService";
|
||||||
import {bus} from '../../main'
|
import { bus } from "../../main";
|
||||||
import JQuery from 'jquery'
|
import JQuery from "jquery";
|
||||||
let $ = JQuery
|
let $ = JQuery;
|
||||||
import News from '../../components/app/News.vue'
|
import News from "../../components/app/News.vue";
|
||||||
import Message from '../../components/app/MessageTemplate.vue'
|
import Message from "../../components/app/MessageTemplate.vue";
|
||||||
import Spinner from '@/components/Spinner.vue'
|
import Spinner from "@/components/Spinner.vue";
|
||||||
import TypingStatus from '@/components/app/TypingStatus.vue'
|
import TypingStatus from "@/components/app/TypingStatus.vue";
|
||||||
|
import uploadsQueue from "@/components/app/uploadsQueue.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Message,
|
Message,
|
||||||
Spinner,
|
Spinner,
|
||||||
News,
|
News,
|
||||||
TypingStatus
|
TypingStatus,
|
||||||
|
uploadsQueue
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -69,31 +101,32 @@ export default {
|
||||||
postTimerID: null,
|
postTimerID: null,
|
||||||
getTimerID: null,
|
getTimerID: null,
|
||||||
typing: false,
|
typing: false,
|
||||||
whosTyping: "",
|
whosTyping: ""
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
methods:{
|
methods: {
|
||||||
toggleLeftMenu(){
|
toggleLeftMenu() {
|
||||||
bus.$emit('toggleLeftMenu')
|
bus.$emit("toggleLeftMenu");
|
||||||
},
|
},
|
||||||
generateNum(n) {
|
generateNum(n) {
|
||||||
var add = 1, max = 12 - add; // 12 is the min safe number Math.random() can generate without it starting to pad the end with zeros.
|
var add = 1,
|
||||||
|
max = 12 - add; // 12 is the min safe number Math.random() can generate without it starting to pad the end with zeros.
|
||||||
|
|
||||||
if ( n > max ) {
|
if (n > max) {
|
||||||
return this.generateNum(max) + this.generateNum(n - max);
|
return this.generateNum(max) + this.generateNum(n - max);
|
||||||
}
|
}
|
||||||
|
|
||||||
max = Math.pow(10, n+add);
|
max = Math.pow(10, n + add);
|
||||||
var min = max/10; // Math.pow(10, n) basically
|
var min = max / 10; // Math.pow(10, n) basically
|
||||||
var number = Math.floor( Math.random() * (max - min + 1) ) + min;
|
var number = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
|
|
||||||
return ("" + number).substring(add);
|
return ("" + number).substring(add);
|
||||||
},
|
},
|
||||||
async sendMessage(){
|
async sendMessage() {
|
||||||
this.$refs["input-box"].focus()
|
this.$refs["input-box"].focus();
|
||||||
this.message = this.message.trim();
|
this.message = this.message.trim();
|
||||||
|
|
||||||
if(this.message == "")return;
|
if (this.message == "") return;
|
||||||
if (this.message.length > 5000) return;
|
if (this.message.length > 5000) return;
|
||||||
clearInterval(this.postTimerID);
|
clearInterval(this.postTimerID);
|
||||||
this.postTimerID = null;
|
this.postTimerID = null;
|
||||||
|
|
@ -102,7 +135,7 @@ export default {
|
||||||
const msg = this.message;
|
const msg = this.message;
|
||||||
const tempID = this.generateNum(25);
|
const tempID = this.generateNum(25);
|
||||||
|
|
||||||
this.$store.dispatch('addMessage', {
|
this.$store.dispatch("addMessage", {
|
||||||
sender: true,
|
sender: true,
|
||||||
channelID: this.selectedChannelID,
|
channelID: this.selectedChannelID,
|
||||||
message: {
|
message: {
|
||||||
|
|
@ -111,36 +144,40 @@ export default {
|
||||||
channelID: this.selectedChannelID,
|
channelID: this.selectedChannelID,
|
||||||
created: new Date()
|
created: new Date()
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
this.message = ""
|
this.message = "";
|
||||||
this.$store.dispatch('updateChannelLastMessage', this.selectedChannelID);
|
this.$store.dispatch("updateChannelLastMessage", this.selectedChannelID);
|
||||||
const { ok, error, result } = await messagesService.post(this.selectedChannelID, {
|
const { ok, error, result } = await messagesService.post(
|
||||||
message: msg,
|
this.selectedChannelID,
|
||||||
socketID: this.$socket.id,
|
{
|
||||||
tempID
|
message: msg,
|
||||||
})
|
socketID: this.$socket.id,
|
||||||
if ( ok ) {
|
tempID
|
||||||
const message = result.data.messageCreated
|
}
|
||||||
|
);
|
||||||
|
if (ok) {
|
||||||
|
const message = result.data.messageCreated;
|
||||||
message.status = 1;
|
message.status = 1;
|
||||||
this.$store.dispatch('replaceMessage', {
|
this.$store.dispatch("replaceMessage", {
|
||||||
tempID: result.data.tempID,
|
tempID: result.data.tempID,
|
||||||
message
|
message
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// TODO: Error handling
|
// TODO: Error handling
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async postTimer() {
|
async postTimer() {
|
||||||
this.postTimerID = setInterval(async () => {
|
this.postTimerID = setInterval(async () => {
|
||||||
if (this.$refs['input-box'].value.trim() == "") {
|
if (this.$refs["input-box"].value.trim() == "") {
|
||||||
clearInterval(this.postTimerID);
|
clearInterval(this.postTimerID);
|
||||||
return this.postTimerID = null;
|
return (this.postTimerID = null);
|
||||||
}
|
}
|
||||||
await typingService.post(this.selectedChannelID);
|
await typingService.post(this.selectedChannelID);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
},
|
},
|
||||||
|
|
||||||
resize(event) {
|
resize(event) {
|
||||||
let input = this.$refs["input-box"]
|
let input = this.$refs["input-box"]
|
||||||
|
|
||||||
|
|
@ -161,6 +198,13 @@ export default {
|
||||||
const value = event.target.value.trim();
|
const value = event.target.value.trim();
|
||||||
if (value && this.postTimerID == null) {
|
if (value && this.postTimerID == null) {
|
||||||
this.postTimer()
|
this.postTimer()
|
||||||
|
|
||||||
|
async onInput(event) {
|
||||||
|
this.messageLength = this.message.length;
|
||||||
|
const value = event.target.value.trim();
|
||||||
|
if (value && this.postTimerID == null) {
|
||||||
|
// Post typing status
|
||||||
|
this.postTimer();
|
||||||
await typingService.post(this.selectedChannelID);
|
await typingService.post(this.selectedChannelID);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -177,45 +221,98 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
invertScroll(event) {
|
invertScroll(event) {
|
||||||
if(event.deltaY) {
|
if (event.deltaY) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.currentTarget.scrollTop -= parseFloat(getComputedStyle(event.currentTarget).getPropertyValue('font-size')) * (event.deltaY < 0 ? -1 : 1) * 2;
|
event.currentTarget.scrollTop -=
|
||||||
|
parseFloat(
|
||||||
|
getComputedStyle(event.currentTarget).getPropertyValue("font-size")
|
||||||
|
) *
|
||||||
|
(event.deltaY < 0 ? -1 : 1) *
|
||||||
|
2;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hideTypingStatus(data) {
|
hideTypingStatus(data) {
|
||||||
if(this.user.uniqueID === data.message.creator.uniqueID) return;
|
if (this.user.uniqueID === data.message.creator.uniqueID) return;
|
||||||
this.typing = false;
|
this.typing = false;
|
||||||
|
},
|
||||||
|
attachmentButton() {
|
||||||
|
this.$refs.sendFileBrowse.click();
|
||||||
|
},
|
||||||
|
uploadFile(file) {
|
||||||
|
// 1073741824 = 1GB || 1024GB
|
||||||
|
const sizeLimit = 1073741824;
|
||||||
|
if (file.size >= sizeLimit) {
|
||||||
|
//show a warning.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$store.dispatch('setFile', file)
|
||||||
|
this.$store.dispatch('setPopoutVisibility', {name: 'uploadDialog', visibility: true})
|
||||||
|
},
|
||||||
|
attachmentChange(event) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
event.target.value = "";
|
||||||
|
this.uploadFile(file);
|
||||||
|
},
|
||||||
|
onPaste(event) {
|
||||||
|
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
||||||
|
for (let index in items) {
|
||||||
|
var item = items[index];
|
||||||
|
if (item.kind === "file") {
|
||||||
|
var blob = item.getAsFile();
|
||||||
|
this.$store.dispatch('setFile', blob)
|
||||||
|
this.$store.dispatch('setPopoutVisibility', {name: 'uploadDialog', visibility: true})
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$options.sockets.typingStatus = (data) => {
|
this.$options.sockets.typingStatus = data => {
|
||||||
const {channelID, userID} = data;
|
const { channelID, userID } = data;
|
||||||
if (channelID !== this.selectedChannelID) return;
|
if (channelID !== this.selectedChannelID) return;
|
||||||
this.typing = true;
|
this.typing = true;
|
||||||
this.whosTyping = this.channel.recipients.find(function(recipient) {
|
this.whosTyping = this.channel.recipients.find(function(recipient) {
|
||||||
return recipient.uniqueID == userID;
|
return recipient.uniqueID == userID;
|
||||||
}).username
|
}).username;
|
||||||
clearTimeout(this.getTimerID);
|
clearTimeout(this.getTimerID);
|
||||||
this.getTimerID = setTimeout(() => {
|
this.getTimerID = setTimeout(() => {
|
||||||
this.typing = false;
|
this.typing = false;
|
||||||
}, 2500);
|
}, 2500);
|
||||||
}
|
};
|
||||||
bus.$on('newMessage', this.hideTypingStatus)
|
bus.$on("newMessage", this.hideTypingStatus);
|
||||||
//dismiss notification on focus
|
//dismiss notification on focus
|
||||||
window.onfocus = () => {
|
window.onfocus = () => {
|
||||||
bus.$emit('title:change', "Nertivia");
|
bus.$emit("title:change", "Nertivia");
|
||||||
if (!this.$store.getters.selectedChannelID) return;
|
if (!this.$store.getters.selectedChannelID) return;
|
||||||
const find = this.$store.getters.notifications.find(notification => {return notification.channelID === this.$store.getters.selectedChannelID});
|
const find = this.$store.getters.notifications.find(notification => {
|
||||||
|
return notification.channelID === this.$store.getters.selectedChannelID;
|
||||||
|
});
|
||||||
if (find && find.count >= 1) {
|
if (find && find.count >= 1) {
|
||||||
this.$socket.emit('notification:dismiss', {channelID: this.$store.getters.selectedChannelID});
|
this.$socket.emit("notification:dismiss", {
|
||||||
}
|
channelID: this.$store.getters.selectedChannelID
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
bus.$off('newMessage', this.hideTypingStatus);
|
bus.$off("newMessage", this.hideTypingStatus);
|
||||||
delete this.$options.sockets.typingStatus;
|
delete this.$options.sockets.typingStatus;
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
uploadQueue() {
|
||||||
|
const allUploads = this.$store.getters.getAllUploads;
|
||||||
|
const selectedChannelID = this.$store.getters.selectedChannelID;
|
||||||
|
|
||||||
|
const filteredArray = [];
|
||||||
|
|
||||||
|
for (let upload in allUploads) {
|
||||||
|
if (allUploads[upload].channelID === selectedChannelID) {
|
||||||
|
filteredArray.push(allUploads[upload]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!filteredArray.length) return undefined;
|
||||||
|
return filteredArray;
|
||||||
|
},
|
||||||
user() {
|
user() {
|
||||||
return this.$store.getters.user;
|
return this.$store.getters.user;
|
||||||
},
|
},
|
||||||
|
|
@ -226,45 +323,46 @@ export default {
|
||||||
return this.$store.getters.messages;
|
return this.$store.getters.messages;
|
||||||
},
|
},
|
||||||
selectedChannelID() {
|
selectedChannelID() {
|
||||||
return this.$store.getters.selectedChannelID
|
return this.$store.getters.selectedChannelID;
|
||||||
},
|
},
|
||||||
channelName() {
|
channelName() {
|
||||||
return this.$store.getters.channelName;
|
return this.$store.getters.channelName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.typing-animate-enter-active {
|
.typing-animate-enter-active {
|
||||||
transition: .10s;
|
transition: 0.1s;
|
||||||
}
|
}
|
||||||
.typing-animate-enter /* .fade-leave-active below version 2.1.8 */ {
|
.typing-animate-enter /* .fade-leave-active below version 2.1.8 */ {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(3px)
|
transform: translateY(3px);
|
||||||
}
|
}
|
||||||
.typing-animate-leave-to {
|
.typing-animate-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-3px)
|
transform: translateY(-3px);
|
||||||
}
|
}
|
||||||
.error-info {
|
.error-info {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heading{
|
.heading {
|
||||||
border-bottom: solid 2px white;
|
border-bottom: solid 2px white;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding-bottom: 2spx;
|
padding-bottom: 2spx;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-menu-button{
|
.show-menu-button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
@ -275,7 +373,7 @@ export default {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heading .current-channel{
|
.heading .current-channel {
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
@ -285,36 +383,58 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-panel {
|
.right-panel {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba(0, 0, 0, 0.507);
|
background-color: rgba(0, 0, 0, 0.507);
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.message-logs{
|
.message-logs {
|
||||||
overflow: auto;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.message-logs, .message-logs .scroll {transform: scale(1,-1);}
|
|
||||||
|
|
||||||
.loading{
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.chat-input-area{
|
.message-logs,
|
||||||
display: flex;
|
.message-logs .scroll {
|
||||||
flex-direction: column;
|
transform: scale(1, -1);
|
||||||
padding-top: 10px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
}
|
||||||
.chat-input-area .info{
|
|
||||||
|
.loading {
|
||||||
|
overflow: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.chat-input-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-top: 10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-button {
|
||||||
|
width: 50px;
|
||||||
|
background: rgba(0, 0, 0, 0.219);
|
||||||
|
margin-right: 2px;
|
||||||
|
margin-left: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
.attachment-button:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.322);
|
||||||
|
}
|
||||||
|
.attachment-button .material-icons {
|
||||||
color: white;
|
color: white;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.chat-input-area .info {
|
||||||
|
color: rgba(255, 255, 255, 0.466);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-left: 25px;
|
margin-left: 25px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.typing-outer{
|
.typing-outer {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
@ -323,52 +443,57 @@ export default {
|
||||||
margin-right: 30px;
|
margin-right: 30px;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
}
|
}
|
||||||
.message-area{
|
.message-area {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.chat-input{
|
|
||||||
font-family: 'Roboto', sans-serif;
|
.chat-input {
|
||||||
background: rgba(0, 0, 0, 0.158);
|
font-family: "Roboto", sans-serif;
|
||||||
color: white;
|
background: rgba(0, 0, 0, 0.158);
|
||||||
flex: 1;
|
color: white;
|
||||||
height: 20px;
|
width: 100%;
|
||||||
padding: 10px;
|
height: 20px;
|
||||||
margin: auto;
|
padding: 10px;
|
||||||
margin-left: 20px;
|
margin: auto;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
resize: none;
|
resize: none;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
transition: 0.3s;
|
transition: 0.3s;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
max-height: 30vh;
|
max-height: 30vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input:hover{
|
.chat-input:hover {
|
||||||
background: rgba(0, 0, 0, 0.288);
|
background: rgba(0, 0, 0, 0.288);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input:focus{
|
.chat-input:focus {
|
||||||
background: rgba(0, 0, 0, 0.466);
|
background: rgba(0, 0, 0, 0.466);
|
||||||
}
|
}
|
||||||
|
|
||||||
.send-button{
|
.send-button {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color:white;
|
color: white;
|
||||||
background: rgba(0, 0, 0, 0.274);
|
background: rgba(0, 0, 0, 0.274);
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
transition: 0.3s;
|
width: 50px;
|
||||||
|
transition: 0.3s;
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.send-button:hover{
|
.send-button .material-icons {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.send-button:hover {
|
||||||
background: rgba(0, 0, 0, 0.514);
|
background: rgba(0, 0, 0, 0.514);
|
||||||
}
|
}
|
||||||
.error-send-button {
|
.error-send-button {
|
||||||
|
|
@ -379,7 +504,7 @@ export default {
|
||||||
background-color: rgba(255, 0, 0, 0.294);
|
background-color: rgba(255, 0, 0, 0.294);
|
||||||
}
|
}
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.show-menu-button{
|
.show-menu-button {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export default {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
bus.$emit('closeSettings');
|
this.$store.dispatch('setPopoutVisibility', {name: 'settings', visibility: false})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<input type="file" accept="image/*" ref="avatarBrowser" @change="avatarBrowse" class="hidden">
|
<input type="file" accept="image/*" ref="avatarBrowser" @change="avatarBrowse" class="hidden">
|
||||||
<div class="option" @click="$refs.avatarBrowser.click()">Edit Avatar</div>
|
<div class="option" @click="editAvatarBtn">Edit Avatar</div>
|
||||||
<div class="option" @click="changePassword">Change Password</div>
|
<div class="option" @click="changePassword">Change Password</div>
|
||||||
<div class="option red" @click="logout">Logout</div>
|
<div class="option red" @click="logout">Logout</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -26,8 +26,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import UploadService from '@/services/UploadService.js'
|
import AvatarUpload from '@/services/AvatarUpload.js'
|
||||||
import config from '@/config.js'
|
import config from '@/config.js'
|
||||||
|
import {bus} from '@/main'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -60,7 +61,7 @@ export default {
|
||||||
}
|
}
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('avatar', file);
|
formData.append('avatar', file);
|
||||||
const {ok, error, result} = await UploadService.uploadAvatar(formData, this.onProgress);
|
const {ok, error, result} = await AvatarUpload.uploadAvatar(formData, this.onProgress);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
this.alert.content = 'Upload failed - Something went wrong. Try again later.';
|
this.alert.content = 'Upload failed - Something went wrong. Try again later.';
|
||||||
return this.alert.show = true;
|
return this.alert.show = true;
|
||||||
|
|
@ -74,6 +75,12 @@ export default {
|
||||||
changePassword() {
|
changePassword() {
|
||||||
this.alert.content = 'Not implemented yet.';
|
this.alert.content = 'Not implemented yet.';
|
||||||
return this.alert.show = true;
|
return this.alert.show = true;
|
||||||
|
},
|
||||||
|
editAvatarBtn() {
|
||||||
|
if(!this.$store.getters.settings.GDriveLinked) {
|
||||||
|
return this.$store.dispatch('setPopoutVisibility', {name: 'GDLinkMenu', visibility: true})
|
||||||
|
}
|
||||||
|
this.$refs.avatarBrowser.click()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
||||||
62
src/components/app/imageLargePreview.vue
Normal file
62
src/components/app/imageLargePreview.vue
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
<template>
|
||||||
|
<div ref="background" class="drop-background">
|
||||||
|
<div class="img-outer">
|
||||||
|
<img :src="$store.getters.popouts.ImagePreviewURL">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
backgroundClickEvent(event) {
|
||||||
|
if(event.target.matches('.img-outer') || event.target.matches('.drop-background')){
|
||||||
|
this.$store.dispatch('setImagePreviewURL', event.target.src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$refs["background"].addEventListener(
|
||||||
|
"click",
|
||||||
|
this.backgroundClickEvent
|
||||||
|
);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.$refs["background"].removeEventListener(
|
||||||
|
"click",
|
||||||
|
this.backgroundClickEvent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.drop-background {
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(0, 0, 0, 0.774);
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 999;
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-outer{
|
||||||
|
margin: auto;
|
||||||
|
height: 90%;
|
||||||
|
width: 90%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
margin: auto;
|
||||||
|
border: solid 1px white;
|
||||||
|
max-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -42,6 +42,7 @@ export default {
|
||||||
}
|
}
|
||||||
this.$store.dispatch('selectedChannelID', this.$props.channelID);
|
this.$store.dispatch('selectedChannelID', this.$props.channelID);
|
||||||
this.$store.dispatch('setChannelName', this.$props.username);
|
this.$store.dispatch('setChannelName', this.$props.username);
|
||||||
|
if (this.$store.getters.messages[this.$props.channelID]) return;
|
||||||
if (this.$store.getters.channels[this.$props.channelID] && !this.$store.getters.messages[this.$props.channelID]) return this.getMessages();
|
if (this.$store.getters.channels[this.$props.channelID] && !this.$store.getters.messages[this.$props.channelID]) return this.getMessages();
|
||||||
const {ok, error, result} = await channelService.post(this.$props.channelID);
|
const {ok, error, result} = await channelService.post(this.$props.channelID);
|
||||||
if ( ok ) {
|
if ( ok ) {
|
||||||
|
|
|
||||||
287
src/components/app/uploadDialog.vue
Normal file
287
src/components/app/uploadDialog.vue
Normal file
|
|
@ -0,0 +1,287 @@
|
||||||
|
<template>
|
||||||
|
<div class="dark-background">
|
||||||
|
<div class="inner">
|
||||||
|
<div class="info">
|
||||||
|
<div class="preview-image" v-show="image" ref="preview-image"></div>
|
||||||
|
<div class="file-icon" v-if="!image">
|
||||||
|
<i class="material-icons">insert_drive_file</i>
|
||||||
|
</div>
|
||||||
|
<div class="data">
|
||||||
|
<div class="name">
|
||||||
|
<strong>Name:</strong>
|
||||||
|
{{name}}
|
||||||
|
</div>
|
||||||
|
<div class="size">
|
||||||
|
<strong>Size:</strong>
|
||||||
|
{{size}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="message">Add a message</div>
|
||||||
|
<div class="message-area">
|
||||||
|
<textarea class="chat-input" v-model="message" placeholder></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="bottom-panel">
|
||||||
|
<div class="close-button button" @click="closeButton">
|
||||||
|
<i class="material-icons">close</i>
|
||||||
|
<div class="text">Cancel</div>
|
||||||
|
</div>
|
||||||
|
<div class="send-button button" @click="send">
|
||||||
|
<i class="material-icons">send</i>
|
||||||
|
<div class="text">Send</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import filesize from "filesize";
|
||||||
|
import messagesService from "@/services/messagesService";
|
||||||
|
import { bus } from "../../main";
|
||||||
|
export default {
|
||||||
|
props: ["file"],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
message: "",
|
||||||
|
name: "",
|
||||||
|
size: "",
|
||||||
|
image: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
generateNum(n) {
|
||||||
|
var add = 1,
|
||||||
|
max = 12 - add; // 12 is the min safe number Math.random() can generate without it starting to pad the end with zeros.
|
||||||
|
|
||||||
|
if (n > max) {
|
||||||
|
return this.generateNum(max) + this.generateNum(n - max);
|
||||||
|
}
|
||||||
|
|
||||||
|
max = Math.pow(10, n + add);
|
||||||
|
var min = max / 10; // Math.pow(10, n) basically
|
||||||
|
var number = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
|
|
||||||
|
return ("" + number).substring(add);
|
||||||
|
},
|
||||||
|
closeButton() {
|
||||||
|
this.$store.dispatch("setPopoutVisibility", {
|
||||||
|
name: "uploadDialog",
|
||||||
|
visibility: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadFileInfo(file) {
|
||||||
|
const previewImage = this.$refs["preview-image"];
|
||||||
|
const mimeType = file.type;
|
||||||
|
if (mimeType.split("/")[0] === "image") {
|
||||||
|
this.image = true;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = function() {
|
||||||
|
previewImage.style.backgroundImage = `url(${reader.result})`;
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async send() {
|
||||||
|
const tempID = this.generateNum(25);
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("message", this.message);
|
||||||
|
formData.append("avatar", this.popouts.fileToUpload);
|
||||||
|
this.$store.dispatch("setPopoutVisibility", {
|
||||||
|
name: "uploadDialog",
|
||||||
|
visibility: false
|
||||||
|
});
|
||||||
|
this.$store.dispatch("addUpload", {
|
||||||
|
channelID: this.selectedChannelID,
|
||||||
|
tempID,
|
||||||
|
name: this.popouts.fileToUpload.name,
|
||||||
|
size: filesize(this.popouts.fileToUpload.size),
|
||||||
|
percent: 0,
|
||||||
|
created: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
const { ok, error, result } = await messagesService.post(
|
||||||
|
this.selectedChannelID,
|
||||||
|
formData,
|
||||||
|
percent => {
|
||||||
|
this.$store.dispatch("updatePercentUpload", {
|
||||||
|
tempID,
|
||||||
|
percent
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (ok) {
|
||||||
|
this.$store.dispatch("removeUpload", tempID);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.dispatch("setPopoutVisibility", {
|
||||||
|
name: "uploadDialog",
|
||||||
|
visibility: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
keyDownEvent(event) {
|
||||||
|
const keyCode = event.keyCode;
|
||||||
|
if (keyCode == 13) {
|
||||||
|
return this.send();
|
||||||
|
}
|
||||||
|
if (keyCode == 27) {
|
||||||
|
return this.$store.dispatch("setPopoutVisibility", {
|
||||||
|
name: "uploadDialog",
|
||||||
|
visibility: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
if (this.popouts.fileToUpload.size == 0) {
|
||||||
|
this.$store.dispatch("setPopoutVisibility", {
|
||||||
|
name: "uploadDialog",
|
||||||
|
visibility: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!this.$store.getters.settings.GDriveLinked) {
|
||||||
|
this.$store.dispatch("setPopoutVisibility", {
|
||||||
|
name: "uploadDialog",
|
||||||
|
visibility: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
(this.name = this.popouts.fileToUpload.name),
|
||||||
|
(this.size = filesize(this.popouts.fileToUpload.size)),
|
||||||
|
this.loadFileInfo(this.popouts.fileToUpload);
|
||||||
|
document.addEventListener("keydown", this.keyDownEvent);
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
document.removeEventListener("keydown", this.keyDownEvent);
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedChannelID() {
|
||||||
|
return this.$store.getters.selectedChannelID;
|
||||||
|
},
|
||||||
|
|
||||||
|
popouts() {
|
||||||
|
return this.$store.getters.popouts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dark-background {
|
||||||
|
background: rgba(0, 0, 0, 0.877);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 999;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.inner {
|
||||||
|
background: rgba(47, 47, 47, 0.938);
|
||||||
|
display: flex;
|
||||||
|
margin: auto;
|
||||||
|
width: 500px;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
margin: 20px;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
.size {
|
||||||
|
color: grey;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.data {
|
||||||
|
margin-left: 10px;
|
||||||
|
color: white;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.preview-image {
|
||||||
|
background-color: #343434;
|
||||||
|
height: 100px;
|
||||||
|
width: 179px;
|
||||||
|
background-position: center;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
border-radius: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.file-icon {
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.file-icon .material-icons {
|
||||||
|
color: white;
|
||||||
|
margin: auto;
|
||||||
|
font-size: 50px;
|
||||||
|
}
|
||||||
|
.bottom-panel {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: white;
|
||||||
|
bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
float: right;
|
||||||
|
align-items: flex-end;
|
||||||
|
align-content: flex-end;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin: auto;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(0, 0, 0, 0.267);
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
.button:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.445);
|
||||||
|
}
|
||||||
|
.button .text {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.send-button {
|
||||||
|
}
|
||||||
|
.close-button {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
.message {
|
||||||
|
color: rgb(235, 235, 235);
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.message-area {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.chat-input {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
background: rgb(26, 26, 26);
|
||||||
|
color: white;
|
||||||
|
width: 100%;
|
||||||
|
height: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
margin: auto;
|
||||||
|
font-size: 15px;
|
||||||
|
resize: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
89
src/components/app/uploadsQueue.vue
Normal file
89
src/components/app/uploadsQueue.vue
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
<template>
|
||||||
|
<div class="uploads-queue">
|
||||||
|
<div class="upload" v-for="(upload, index) in $props.queue" :key="index">
|
||||||
|
<div class="icon">
|
||||||
|
<i class="material-icons">insert_drive_file</i>
|
||||||
|
</div>
|
||||||
|
<div class="information">
|
||||||
|
<div class="info">{{upload.name}}</div>
|
||||||
|
<div class="info size">{{upload.size}}</div>
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar">
|
||||||
|
<div class="progress-bar-inner" :style="{width: `${upload.percent}%`}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="percent">{{upload.percent}}%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ["queue"]
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.uploads-queue {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.upload {
|
||||||
|
color: white;
|
||||||
|
background: rgba(0, 0, 0, 0.513);
|
||||||
|
margin: 10px;
|
||||||
|
margin-left: 70px;
|
||||||
|
display: flex;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.icon .material-icons {
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-size: 14px;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 100%;
|
||||||
|
color: white;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
.size {
|
||||||
|
font-size: 15px;
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
.progress {
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.progress-bar {
|
||||||
|
width: 100px;
|
||||||
|
height: 10px;
|
||||||
|
background: rgba(0, 0, 0, 0.513);
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.progress-bar-inner {
|
||||||
|
height: 10px;
|
||||||
|
background: rgb(255, 255, 255);
|
||||||
|
animation: dimProgress;
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-direction: normal;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
@keyframes dimProgress {
|
||||||
|
0% {
|
||||||
|
background: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background: rgba(253, 253, 253, 0.705);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="logged-in">
|
<div class="logged-in">
|
||||||
<div class="title">Welcome!</div>
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="avatar-outer">
|
<div class="avatar-outer">
|
||||||
<div class="avatar" :style="`background-image: url(${avatar})`"></div>
|
<div class="avatar" :style="`background-image: url(${avatar})`"></div>
|
||||||
|
|
@ -41,22 +40,33 @@ export default {
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.logged-in {
|
.logged-in {
|
||||||
margin-top: 50px;
|
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card{
|
.card{
|
||||||
background-color: rgba(0, 0, 0, 0.26);
|
background-color: rgba(0, 0, 0, 0.041);
|
||||||
padding: 10px;
|
padding: 20px;
|
||||||
margin: 20px;
|
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
align-self: center;
|
||||||
|
justify-content: center;
|
||||||
|
justify-items: center;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
display: table;
|
display: table;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
font-size: 30px;
|
margin-top: 10px;
|
||||||
|
font-size: 25px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
|
|
@ -73,7 +83,7 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-top: 5px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.username {
|
.username {
|
||||||
|
|
@ -96,18 +106,20 @@ export default {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
margin-right: 5px;
|
margin-right: 10px;
|
||||||
margin-top: 10px;
|
margin-left: 10px;
|
||||||
|
margin-top: 30px;
|
||||||
transition: 0.3s;
|
transition: 0.3s;
|
||||||
|
font-size: 17px;
|
||||||
}
|
}
|
||||||
.button:hover {
|
.button:hover {
|
||||||
background: rgba(0, 0, 0, 0.582);
|
background: rgba(0, 0, 0, 0.582);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button.logout{
|
.button.logout{
|
||||||
background: rgba(149, 0, 0, 0.404);
|
background: rgba(219, 36, 36, 0.438);
|
||||||
}
|
}
|
||||||
.button.logout:hover {
|
.button.logout:hover {
|
||||||
background: rgba(255, 0, 0, 0.582);
|
background: rgba(255, 18, 18, 0.582);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
const config = {
|
const config = {
|
||||||
devMode:true,
|
devMode:true,
|
||||||
breeMode: true,
|
breeMode: false,
|
||||||
recaptcha: "",
|
recaptcha: "",
|
||||||
IP: [
|
IP: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ Vue.use(VueRouter)
|
||||||
import {store} from './store/index';
|
import {store} from './store/index';
|
||||||
import MainApp from '../src/views/App.vue'
|
import MainApp from '../src/views/App.vue'
|
||||||
import HomePage from '../src/views/HomePage.vue'
|
import HomePage from '../src/views/HomePage.vue'
|
||||||
|
import GDriveCallback from '../src/views/GDriveCallback.vue';
|
||||||
import VueSocketio from 'vue-socket.io-extended';
|
import VueSocketio from 'vue-socket.io-extended';
|
||||||
import io from 'socket.io-client';
|
import io from 'socket.io-client';
|
||||||
import config from './config'
|
import config from './config'
|
||||||
|
|
@ -42,6 +43,11 @@ export const router = new VueRouter({
|
||||||
})
|
})
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
path: '/GDrive_callback',
|
||||||
|
name: 'GDrive callback',
|
||||||
|
component: GDriveCallback
|
||||||
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
@ -3,7 +3,8 @@ import config from '@/config';
|
||||||
|
|
||||||
export const instance = () => {
|
export const instance = () => {
|
||||||
return axios.create({
|
return axios.create({
|
||||||
baseURL: config.domain
|
baseURL: config.domain,
|
||||||
|
withCredentials: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
import {instance, wrapper} from './Api';
|
import {
|
||||||
|
instance,
|
||||||
|
wrapper
|
||||||
|
} from './Api';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
uploadAvatar(data, onProgress){
|
uploadAvatar(data, onProgress) {
|
||||||
const url = `/settings/avatar`;
|
const url = `/settings/avatar`;
|
||||||
let config = {
|
let config = {
|
||||||
onUploadProgress(progressEvent) {
|
onUploadProgress(progressEvent) {
|
||||||
|
|
@ -1,12 +1,28 @@
|
||||||
import {instance, wrapper} from './Api';
|
import {instance, wrapper} from './Api';
|
||||||
|
import filesize from "filesize";
|
||||||
export default {
|
export default {
|
||||||
// TODO: add ?continue=id
|
// TODO: add ?continue=id
|
||||||
get ( channelID ) {
|
get ( channelID ) {
|
||||||
return wrapper(instance().get(`messages/${channelID}`));
|
return wrapper(instance().get(`messages/${channelID}`));
|
||||||
},
|
},
|
||||||
|
|
||||||
post (channelID, data) {
|
post (channelID, data, onProgress) {
|
||||||
return wrapper(instance().post(`messages/${channelID}`, data))
|
const url = `messages/${channelID}`;
|
||||||
|
|
||||||
|
var start = +new Date();
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
onUploadProgress(progressEvent) {
|
||||||
|
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
||||||
|
|
||||||
|
// execute the callback
|
||||||
|
if (onProgress) onProgress(percentCompleted)
|
||||||
|
|
||||||
|
return percentCompleted;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return wrapper(instance().post(url, data, config));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,4 +4,10 @@ export default {
|
||||||
setStatus ( status ) {
|
setStatus ( status ) {
|
||||||
return wrapper(instance().post('/settings/status', { status }));
|
return wrapper(instance().post('/settings/status', { status }));
|
||||||
},
|
},
|
||||||
|
GDriveURL () {
|
||||||
|
return wrapper(instance().get('/settings/drive/url'));
|
||||||
|
},
|
||||||
|
GDriveAuth (code) {
|
||||||
|
return wrapper(instance().post('/settings/drive/auth', {code}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,12 +5,26 @@ import socketModule from './modules/socketIOModule';
|
||||||
import channelModule from './modules/channelModule';
|
import channelModule from './modules/channelModule';
|
||||||
import messageModule from './modules/messageModule';
|
import messageModule from './modules/messageModule';
|
||||||
import notificationsModule from './modules/notificationsModule';
|
import notificationsModule from './modules/notificationsModule';
|
||||||
import {router} from './../router'
|
import settingsModule from './modules/settingsModule';
|
||||||
|
import uploadFilesModule from './modules/uploadFilesModule';
|
||||||
|
import popoutsModule from './modules/popoutsModule';
|
||||||
|
import {
|
||||||
|
router
|
||||||
|
} from './../router'
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
export const store = new Vuex.Store({
|
export const store = new Vuex.Store({
|
||||||
modules: { user, channelModule, messageModule, notificationsModule, socketModule },
|
modules: {
|
||||||
|
user,
|
||||||
|
channelModule,
|
||||||
|
messageModule,
|
||||||
|
notificationsModule,
|
||||||
|
socketModule,
|
||||||
|
settingsModule,
|
||||||
|
uploadFilesModule,
|
||||||
|
popoutsModule
|
||||||
|
},
|
||||||
state: {
|
state: {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
|
||||||
55
src/store/modules/popoutsModule.js
Normal file
55
src/store/modules/popoutsModule.js
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import axios from 'axios'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import {
|
||||||
|
bus
|
||||||
|
} from '../../main'
|
||||||
|
import VueRouter from 'vue-router';
|
||||||
|
import NotificationSounds from '@/notificationSound';
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
fileToUpload: null,
|
||||||
|
uploadDialog: false,
|
||||||
|
ImagePreviewURL: null,
|
||||||
|
|
||||||
|
dragDropFileUploadDialog: false,
|
||||||
|
settings: false,
|
||||||
|
GDLinkMenu: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
popouts(state) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
setPopoutVisibility(context, data) {
|
||||||
|
context.commit('setPopoutVisibility', data)
|
||||||
|
},
|
||||||
|
setFile(context, file) {
|
||||||
|
context.commit('setFileToUpload', file);
|
||||||
|
},
|
||||||
|
setImagePreviewURL(context, url) {
|
||||||
|
context.commit('setImagePreviewURL', url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
setPopoutVisibility(state, data) {
|
||||||
|
Vue.set(state, data.name, data.visibility)
|
||||||
|
},
|
||||||
|
setFileToUpload(state, file) {
|
||||||
|
Vue.set(state, 'fileToUpload', file);
|
||||||
|
},
|
||||||
|
setImagePreviewURL(state, url) {
|
||||||
|
Vue.set(state, 'ImagePreviewURL', url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespace: true,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
actions,
|
||||||
|
mutations
|
||||||
|
}
|
||||||
42
src/store/modules/settingsModule.js
Normal file
42
src/store/modules/settingsModule.js
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import {bus} from '../../main'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
settings: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
settings(state) {
|
||||||
|
return state.settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
setSettings(context, settings) {
|
||||||
|
context.commit('setSettings', settings)
|
||||||
|
},
|
||||||
|
setGDriveLinked(context, status) {
|
||||||
|
context.commit('GoogleDriveLinked', status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
setSettings(state, settings) {
|
||||||
|
state.settings = settings;
|
||||||
|
},
|
||||||
|
GoogleDriveLinked(state, status) {
|
||||||
|
Vue.set(state.settings, 'GDriveLinked', status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespace: true,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
actions,
|
||||||
|
mutations
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import {bus} from '../../main'
|
||||||
import {router} from './../../router'
|
import {router} from './../../router'
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -16,7 +17,7 @@ const actions = {
|
||||||
},
|
},
|
||||||
socket_success(context, data) {
|
socket_success(context, data) {
|
||||||
|
|
||||||
const {message, user, dms, notifications, currentFriendStatus} = data;
|
const {message, user, dms, notifications, currentFriendStatus, settings} = data;
|
||||||
const friendsArray = user.friends;
|
const friendsArray = user.friends;
|
||||||
const friendObject = {};
|
const friendObject = {};
|
||||||
|
|
||||||
|
|
@ -44,6 +45,7 @@ const actions = {
|
||||||
}
|
}
|
||||||
context.commit('addAllChannels', channelsObject)
|
context.commit('addAllChannels', channelsObject)
|
||||||
context.dispatch('addAllNotifications', notifications)
|
context.dispatch('addAllNotifications', notifications)
|
||||||
|
context.dispatch('setSettings', settings)
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
@ -100,6 +102,10 @@ const actions = {
|
||||||
['socket_notification:dismiss'](context, data){
|
['socket_notification:dismiss'](context, data){
|
||||||
const {channelID} = data;
|
const {channelID} = data;
|
||||||
context.dispatch('dismissNotification', channelID);
|
context.dispatch('dismissNotification', channelID);
|
||||||
|
},
|
||||||
|
['socket_googleDrive:linked'](context) {
|
||||||
|
context.dispatch('setPopoutVisibility', {name: 'GDLinkMenu', visibility: false})
|
||||||
|
context.dispatch('setGDriveLinked', true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
45
src/store/modules/uploadFilesModule.js
Normal file
45
src/store/modules/uploadFilesModule.js
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import {bus} from '../../main'
|
||||||
|
import {router} from '../../router'
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
uploads: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
getAllUploads(state) {
|
||||||
|
return state.uploads;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
addUpload(context, data) {
|
||||||
|
context.commit('addUpload', data)
|
||||||
|
},
|
||||||
|
updatePercentUpload(context, data) {
|
||||||
|
context.commit('updatePercentUpload', data)
|
||||||
|
},
|
||||||
|
removeUpload(context, tempID) {
|
||||||
|
context.commit('removeUpload', tempID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
updatePercentUpload(state, data) {
|
||||||
|
Vue.set(state.uploads[data.tempID], 'percent', data.percent);
|
||||||
|
},
|
||||||
|
addUpload(state, data) {
|
||||||
|
Vue.set(state.uploads, data.tempID, data);
|
||||||
|
},
|
||||||
|
removeUpload(state, tempID) {
|
||||||
|
Vue.delete(state.uploads, tempID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespace: true,
|
||||||
|
state,
|
||||||
|
actions,
|
||||||
|
mutations,
|
||||||
|
getters
|
||||||
|
}
|
||||||
|
|
@ -2,108 +2,104 @@
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<vue-headful :title="title" description="Nertivia Chat Client"/>
|
<vue-headful :title="title" description="Nertivia Chat Client"/>
|
||||||
<div class="background-image"></div>
|
<div class="background-image"></div>
|
||||||
<transition name="fade-between-two" appear >
|
<transition name="fade-between-two" appear>
|
||||||
<ConnectingScreen v-if="!loggedIn" />
|
<ConnectingScreen v-if="!loggedIn"/>
|
||||||
<div class="box" v-if="loggedIn">
|
<div class="box" v-if="loggedIn">
|
||||||
<div class="panel-layout">
|
<div class="panel-layout">
|
||||||
<transition name="slidein">
|
<transition name="slidein">
|
||||||
<LeftPanel class="left-panel" v-click-outside="hideLeftPanel" v-show="$mq === 'mobile' && showLeftPanel || $mq === 'desktop'"> </LeftPanel>
|
<LeftPanel
|
||||||
|
class="left-panel"
|
||||||
|
v-click-outside="hideLeftPanel"
|
||||||
|
v-show="$mq === 'mobile' && showLeftPanel || $mq === 'desktop'"
|
||||||
|
></LeftPanel>
|
||||||
</transition>
|
</transition>
|
||||||
<RightPanel> </RightPanel>
|
<RightPanel/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<transition name="fade">
|
<Popouts v-if="loggedIn"/>
|
||||||
<settings v-if="showSettings && loggedIn" />
|
|
||||||
<!--<GDriveLinkMenu v-if="loggedIn" /> -->
|
|
||||||
</transition>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {bus} from '../main'
|
import { bus } from "../main";
|
||||||
import Settings from '@/components/app/Settings.vue'
|
import Popouts from "@/components/app/Popouts.vue";
|
||||||
// import GDriveLinkMenu from '@/components/app/GDriveLinkMenu.vue'
|
import LeftPanel from "./../components/app/LeftPanel.vue";
|
||||||
import LeftPanel from './../components/app/LeftPanel.vue'
|
import RightPanel from "./../components/app/RightPanel.vue";
|
||||||
import RightPanel from './../components/app/RightPanel.vue'
|
import ConnectingScreen from "./../components/app/ConnectingScreen.vue";
|
||||||
import ConnectingScreen from './../components/app/ConnectingScreen.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'app',
|
name: "app",
|
||||||
components: {
|
components: {
|
||||||
LeftPanel,
|
LeftPanel,
|
||||||
RightPanel,
|
RightPanel,
|
||||||
ConnectingScreen,
|
ConnectingScreen,
|
||||||
Settings,
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showLeftPanel: false,
|
showLeftPanel: false,
|
||||||
showSettings: false,
|
|
||||||
title: "Nertivia"
|
title: "Nertivia"
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
hideLeftPanel(test) {
|
hideLeftPanel(event) {
|
||||||
if (this.showLeftPanel){
|
if (this.showLeftPanel) {
|
||||||
if(test.target.closest('.show-menu-button') == null){
|
if (event.target.closest(".show-menu-button") == null) {
|
||||||
this.showLeftPanel = false;
|
this.showLeftPanel = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
bus.$on('toggleLeftMenu', () => {
|
bus.$on("toggleLeftMenu", () => {
|
||||||
this.showLeftPanel = !this.showLeftPanel;
|
this.showLeftPanel = !this.showLeftPanel;
|
||||||
})
|
});
|
||||||
bus.$on('closeLeftMenu', () => {
|
bus.$on("closeLeftMenu", () => {
|
||||||
this.showLeftPanel = false;
|
this.showLeftPanel = false;
|
||||||
})
|
});
|
||||||
bus.$on('openSettings', () => {
|
|
||||||
this.showSettings = true;
|
bus.$on("title:change", title => {
|
||||||
})
|
|
||||||
bus.$on('closeSettings', () => {
|
|
||||||
this.showSettings = false;
|
|
||||||
})
|
|
||||||
bus.$on('title:change', (title) => {
|
|
||||||
this.title = title;
|
this.title = title;
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
loggedIn() {
|
loggedIn() {
|
||||||
return this.$store.getters.loggedIn
|
return this.$store.getters.loggedIn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.slidein-enter-active, .slidein-leave-active {
|
.slidein-enter-active,
|
||||||
transition: .5s;
|
.slidein-leave-active {
|
||||||
|
transition: 0.5s;
|
||||||
}
|
}
|
||||||
.slidein-enter, .slidein-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
.slidein-enter, .slidein-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||||
margin-left: -300px;
|
margin-left: -300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-between-two-enter-active, .fade-between-two-leave-active{
|
.fade-between-two-enter-active,
|
||||||
|
.fade-between-two-leave-active {
|
||||||
transition: 0.3s;
|
transition: 0.3s;
|
||||||
}
|
}
|
||||||
.fade-between-two-enter, .fade-between-two-leave-to {
|
.fade-between-two-enter,
|
||||||
|
.fade-between-two-leave-to {
|
||||||
opacity: 0 !important;
|
opacity: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
.fade-enter-active, .fade-leave-active {
|
transition: opacity 0.2s;
|
||||||
transition: opacity .2s;
|
|
||||||
}
|
}
|
||||||
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.left-panel{
|
.left-panel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 47px;
|
top: 47px;
|
||||||
height: calc(100% - 47px);
|
height: calc(100% - 47px);
|
||||||
|
|
@ -115,19 +111,18 @@ export default {
|
||||||
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
html {
|
||||||
html{
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body{
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
color: #383838;
|
color: #383838;
|
||||||
|
|
@ -148,37 +143,34 @@ body{
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: bottom;
|
background-position: bottom;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-layout {
|
.panel-layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* ------- SCROLL BAR -------*/
|
/* ------- SCROLL BAR -------*/
|
||||||
/* width */
|
/* width */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Track */
|
/* Track */
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: #8080806b;
|
background: #8080806b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle */
|
/* Handle */
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: #f5f5f559;
|
background: #f5f5f559;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle on hover */
|
/* Handle on hover */
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: #f5f5f59e;
|
background: #f5f5f59e;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
||||||
14
src/views/GDriveCallback.vue
Normal file
14
src/views/GDriveCallback.vue
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
Redirecting...
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
mounted() {
|
||||||
|
window.opener.postMessage({code: location.href}, "*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -312,7 +312,7 @@ button {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
height:calc(100% - 50px);
|
height:calc(100% - 50px);
|
||||||
background-color: rgba(34, 34, 34, 0.877);
|
background-color: rgb(34, 34, 34);
|
||||||
width: 0;
|
width: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
transition: 0.5s ease;
|
transition: 0.5s ease;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue