Merge branch 'master' into polish

This commit is contained in:
Supertiger 2019-03-08 10:57:17 +00:00 committed by GitHub
commit 5045f96103
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 1476 additions and 369 deletions

13
package-lock.json generated
View file

@ -4449,10 +4449,9 @@
}
},
"filesize": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
"integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==",
"dev": true
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-4.1.2.tgz",
"integrity": "sha512-iSWteWtfNcrWQTkQw8ble2bnonSl7YJImsn9OZKpE2E4IHhXI78eASpDYUljXZZdYj36QsEKjOs/CsiDqmKMJw=="
},
"fill-range": {
"version": "4.0.0",
@ -10842,6 +10841,12 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==",
"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
}
}
},

View file

@ -9,6 +9,7 @@
},
"dependencies": {
"axios": "^0.18.0",
"filesize": "^4.1.2",
"futoji": "^0.2.4",
"jquery": "^3.3.1",
"socket.io": "^2.2.0",

View 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>

View file

@ -1,19 +1,60 @@
<template>
<div class="dark-background">
<div class="dark-background" @click="backgroundClick">
<div class="inner">
<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 class="images">
<div class="image GDrive-img"></div>
<div class="arrow">></div>
<div class="image nertivia-img"></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>
</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>
.dark-background {
position: absolute;
@ -22,7 +63,7 @@
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.781);
z-index: 1;
z-index: 111111;
display: flex;
}
.inner {
@ -30,16 +71,19 @@
height: 400px;
width: 400px;
background: rgb(32, 32, 32);
display: flex;
flex-direction: column;
color: white;
border-radius: 3px;
display: flex;
flex-direction: column;
color: white;
border-radius: 3px;
}
.text{
color: white;
text-align: center;
margin-top: 30px;
font-size: 17px;
padding-left: 10px;
padding-right: 10px;
user-select: none;
}
.images{
display: flex;
@ -54,15 +98,23 @@
}
.nertivia-img {
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{
font-size: 40px;
margin: auto;
padding: 20px;
user-select: none;
}
.GDrive-img {
background-image: url(./../../assets/Google_Drive_logo.png);
}
.buttons{
margin: auto;
display: flex;
}
.button{
display: inline-block;
margin: auto;
@ -72,10 +124,18 @@
cursor: default;
user-select: none;
transition: 0.3s;
margin-left: 10px;
margin-right: 10px;
}
.button:hover {
background:rgb(58, 134, 255);
}
.button.deny {
background: rgb(255, 32, 32);
}
.button.deny:hover {
background: rgb(255, 53, 53);
}
</style>

View file

@ -1,179 +1,284 @@
<template>
<div :class="{message: true, ownMessage: user.uniqueID === $props.uniqueID}">
<div class="profile-picture" :style="`background-image: url(${userAvatar})`"></div>
<div class="triangle">
<div class="triangle-inner"></div>
</div>
<div class="content">
<div class="user-info">
<div class="username">{{this.$props.username}}</div>
<div class="date">{{getDate}}</div>
</div>
<div class="content-message" v-html="formatMessage"></div>
</div>
<div class="sending-status">{{statusMessage}}</div>
</div>
<div :class="{message: true, ownMessage: user.uniqueID === $props.uniqueID}">
<div class="profile-picture" :style="`background-image: url(${userAvatar})`"></div>
<div class="triangle">
<div class="triangle-inner"></div>
</div>
<div class="content">
<div class="user-info">
<div class="username">{{this.$props.username}}</div>
<div class="date">{{getDate}}</div>
</div>
<div class="content-message" v-html="formatMessage"></div>
<div class="file-content" v-if="getFile">
<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>
<script>
import messageFormatter from '@/messageFormatter.js'
import config from '@/config.js'
import friendlyDate from '@/date'
export default {
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;
import messageFormatter from "@/messageFormatter.js";
import config from "@/config.js";
import friendlyDate from "@/date";
import path from "path";
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
}
}
}
export default {
props: [
"message",
"status",
"username",
"avatar",
"date",
"uniqueID",
"files"
],
methods: {
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>
<style scoped>
.message {
margin: 10px;
margin-top: 10px;
margin-bottom: 10px;
display: flex;
.message{
margin: 10px;
margin-top: 10px;
margin-bottom: 10px;
display: flex;
animation: showMessage .3s ease-in-out;
animation: showMessage 0.3s ease-in-out;
}
.ownMessage .triangle-inner{
border-right: 7px solid rgba(184, 184, 184, 0.219);
.ownMessage .triangle-inner {
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 {
from {
transform: translate(0px, 9px);
opacity: 0;
}
from {
transform: translate(0px, 9px);
opacity: 0;
}
}
.profile-picture{
height: 50px;
width: 50px;
background-color: rgba(0, 0, 0, 0.281);
margin: auto;
margin-bottom: 0;
border-radius: 50%;
margin-right: 5px;
margin-left: 0;
flex-shrink: 0;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
.profile-picture {
height: 50px;
width: 50px;
background-color: rgba(0, 0, 0, 0.281);
margin: auto;
margin-bottom: 0;
border-radius: 50%;
margin-right: 5px;
margin-left: 0;
flex-shrink: 0;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
}
.triangle{
display: flex;
justify-content: bottom;
flex-direction: column;
margin: auto;
margin-left: 0;
margin-right: 0px;
margin-bottom: 8.7px;
.triangle {
display: flex;
justify-content: bottom;
flex-direction: column;
margin: auto;
margin-left: 0;
margin-right: 0px;
margin-bottom: 8.7px;
}
.triangle-inner{
width: 0;
height: 0;
.triangle-inner {
width: 0;
height: 0;
border-top: 1px solid transparent;
border-bottom: 7px solid transparent;
border-right: 7px solid rgba(0, 0, 0, 0.301);
border-top: 1px solid transparent;
border-bottom: 7px solid transparent;
border-right: 7px solid rgba(0, 0, 0, 0.301);
}
.content{
background: rgba(0, 0, 0, 0.301);
padding: 10px;
.content {
background: rgba(0, 0, 0, 0.301);
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;
justify-content: center;
flex-direction: column;
border-radius: 10px;
color: rgb(231, 231, 231);
margin: auto;
margin-left: 0;
margin-right: 0;
transition: 1s;
.image-content {
margin-top: 10px;
padding: 5px;
border-radius: 5px;
background: rgba(0, 0, 0, 0.493);
display: -ms-flexbox;
display: flex;
flex-direction: column;
}
.image-content img {
width: 170px;
height: auto;
transition: 0.2s;
}
.image-content:hover img {
filter: brightness(70%);
}
.user-info {
display: flex;
display: flex;
}
.username {
color: rgb(219, 219, 219);
font-size: 14px;
margin: auto;
margin-left: 0;
margin-right: 0;
color: rgb(219, 219, 219);
font-size: 14px;
margin: auto;
margin-left: 0;
margin-right: 0;
}
.date{
color: rgb(161, 161, 161);
font-size: 10px;
margin: auto;
margin-left: 5px;
.date {
color: rgb(161, 161, 161);
font-size: 10px;
margin: auto;
margin-left: 5px;
}
.content-message {
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;
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;
}
.message .sending-status {
display: flex;
justify-content: flex-end;
flex-direction: column;
padding-bottom: 5px;
margin-left: 10px;
font-size: 15px;
color: white;
align-self: normal;
user-select: none;
transition: 0.5;
display: flex;
justify-content: flex-end;
flex-direction: column;
padding-bottom: 5px;
margin-left: 10px;
font-size: 15px;
color: white;
align-self: normal;
user-select: none;
transition: 0.5;
}
</style>
<style>
.codeblock{
background-color: rgba(0, 0, 0, 0.397);
padding: 5px;
border-radius: 5px;
.codeblock {
background-color: rgba(0, 0, 0, 0.397);
padding: 5px;
border-radius: 5px;
}
</style>

View file

@ -50,7 +50,7 @@ export default {
}
},
openSettings() {
bus.$emit('openSettings');
this.$store.dispatch('setPopoutVisibility', {name: 'settings', visibility: true})
}
},
created() {

View 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>

View file

@ -2,35 +2,63 @@
<div class="right-panel">
<div class="heading">
<div class="show-menu-button" cli>
<i class="material-icons" @click="toggleLeftMenu">
menu
</i>
<i class="material-icons" @click="toggleLeftMenu">menu</i>
</div>
<div class="current-channel">
<span v-if="!selectedChannelID">Welcome back, {{user.username}}!</span>
<span v-else>{{channelName}}</span>
</div>
<div class="current-channel"><span v-if="!selectedChannelID">Welcome back!</span><span v-else>{{channelName}}</span></div>
</div>
<div class="loading" v-if="selectedChannelID && !messages[selectedChannelID]">
<spinner />
<spinner/>
</div>
<div v-else-if="selectedChannelID" class="message-logs" @wheel="invertScroll">
<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>
<news v-else />
<news v-else/>
<div class="chat-input-area" v-if="selectedChannelID">
<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
rows="1"
class="chat-input"
rows="1"
ref="input-box"
placeholder="Message"
@keydown="chatInput"
@keyup="delayedResize"
@change="resize"
@input="onInput"
v-model="message"
></textarea>
<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 class="info">
<div class="typing-outer">
@ -38,29 +66,33 @@
<typing-status v-if="typing" :username="whosTyping"/>
</transition>
</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>
</template>
<script>
import messagesService from '@/services/messagesService'
import typingService from '@/services/TypingService'
import {bus} from '../../main'
import JQuery from 'jquery'
let $ = JQuery
import News from '../../components/app/News.vue'
import Message from '../../components/app/MessageTemplate.vue'
import Spinner from '@/components/Spinner.vue'
import TypingStatus from '@/components/app/TypingStatus.vue'
import messagesService from "@/services/messagesService";
import typingService from "@/services/TypingService";
import { bus } from "../../main";
import JQuery from "jquery";
let $ = JQuery;
import News from "../../components/app/News.vue";
import Message from "../../components/app/MessageTemplate.vue";
import Spinner from "@/components/Spinner.vue";
import TypingStatus from "@/components/app/TypingStatus.vue";
import uploadsQueue from "@/components/app/uploadsQueue.vue";
export default {
components: {
Message,
Spinner,
Spinner,
News,
TypingStatus
TypingStatus,
uploadsQueue
},
data() {
return {
@ -69,31 +101,32 @@ export default {
postTimerID: null,
getTimerID: null,
typing: false,
whosTyping: "",
}
whosTyping: ""
};
},
methods:{
toggleLeftMenu(){
bus.$emit('toggleLeftMenu')
methods: {
toggleLeftMenu() {
bus.$emit("toggleLeftMenu");
},
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 ) {
return this.generateNum(max) + this.generateNum(n - max);
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;
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);
return ("" + number).substring(add);
},
async sendMessage(){
this.$refs["input-box"].focus()
async sendMessage() {
this.$refs["input-box"].focus();
this.message = this.message.trim();
if(this.message == "")return;
if (this.message == "") return;
if (this.message.length > 5000) return;
clearInterval(this.postTimerID);
this.postTimerID = null;
@ -102,7 +135,7 @@ export default {
const msg = this.message;
const tempID = this.generateNum(25);
this.$store.dispatch('addMessage', {
this.$store.dispatch("addMessage", {
sender: true,
channelID: this.selectedChannelID,
message: {
@ -111,36 +144,40 @@ export default {
channelID: this.selectedChannelID,
created: new Date()
}
})
});
this.message = ""
this.$store.dispatch('updateChannelLastMessage', this.selectedChannelID);
const { ok, error, result } = await messagesService.post(this.selectedChannelID, {
message: msg,
socketID: this.$socket.id,
tempID
})
if ( ok ) {
const message = result.data.messageCreated
this.message = "";
this.$store.dispatch("updateChannelLastMessage", this.selectedChannelID);
const { ok, error, result } = await messagesService.post(
this.selectedChannelID,
{
message: msg,
socketID: this.$socket.id,
tempID
}
);
if (ok) {
const message = result.data.messageCreated;
message.status = 1;
this.$store.dispatch('replaceMessage', {
this.$store.dispatch("replaceMessage", {
tempID: result.data.tempID,
message
});
} else {
// TODO: Error handling
console.log(error)
console.log(error);
}
},
async postTimer() {
this.postTimerID = setInterval(async () => {
if (this.$refs['input-box'].value.trim() == "") {
if (this.$refs["input-box"].value.trim() == "") {
clearInterval(this.postTimerID);
return this.postTimerID = null;
return (this.postTimerID = null);
}
await typingService.post(this.selectedChannelID);
}, 2000);
},
resize(event) {
let input = this.$refs["input-box"]
@ -161,6 +198,13 @@ export default {
const value = event.target.value.trim();
if (value && this.postTimerID == null) {
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);
}
},
@ -177,45 +221,98 @@ export default {
}
},
invertScroll(event) {
if(event.deltaY) {
if (event.deltaY) {
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) {
if(this.user.uniqueID === data.message.creator.uniqueID) return;
if (this.user.uniqueID === data.message.creator.uniqueID) return;
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() {
this.$options.sockets.typingStatus = (data) => {
const {channelID, userID} = data;
this.$options.sockets.typingStatus = data => {
const { channelID, userID } = data;
if (channelID !== this.selectedChannelID) return;
this.typing = true;
this.typing = true;
this.whosTyping = this.channel.recipients.find(function(recipient) {
return recipient.uniqueID == userID;
}).username
}).username;
clearTimeout(this.getTimerID);
this.getTimerID = setTimeout(() => {
this.typing = false;
this.typing = false;
}, 2500);
}
bus.$on('newMessage', this.hideTypingStatus)
};
bus.$on("newMessage", this.hideTypingStatus);
//dismiss notification on focus
window.onfocus = () => {
bus.$emit('title:change', "Nertivia");
bus.$emit("title:change", "Nertivia");
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) {
this.$socket.emit('notification:dismiss', {channelID: this.$store.getters.selectedChannelID});
}
}
this.$socket.emit("notification:dismiss", {
channelID: this.$store.getters.selectedChannelID
});
}
};
},
beforeDestroy() {
bus.$off('newMessage', this.hideTypingStatus);
bus.$off("newMessage", this.hideTypingStatus);
delete this.$options.sockets.typingStatus;
},
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() {
return this.$store.getters.user;
},
@ -226,45 +323,46 @@ export default {
return this.$store.getters.messages;
},
selectedChannelID() {
return this.$store.getters.selectedChannelID
return this.$store.getters.selectedChannelID;
},
channelName() {
return this.$store.getters.channelName;
}
}
}
};
</script>
<style scoped>
.hidden {
display: none;
}
.typing-animate-enter-active {
transition: .10s;
transition: 0.1s;
}
.typing-animate-enter /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
transform: translateY(3px)
transform: translateY(3px);
}
.typing-animate-leave-to {
opacity: 0;
transform: translateY(-3px)
transform: translateY(-3px);
}
.error-info {
color: red;
}
.heading{
border-bottom: solid 2px white;
margin: 5px;
height: 40px;
padding-bottom: 2spx;
display: flex;
flex-shrink: 0;
.heading {
border-bottom: solid 2px white;
margin: 5px;
height: 40px;
padding-bottom: 2spx;
display: flex;
flex-shrink: 0;
}
.show-menu-button{
.show-menu-button {
display: inline-block;
margin: auto;
color: white;
@ -275,7 +373,7 @@ export default {
display: none;
}
.heading .current-channel{
.heading .current-channel {
color: white;
font-size: 20px;
margin: auto;
@ -285,36 +383,58 @@ export default {
}
.right-panel {
height: 100%;
background-color: rgba(0, 0, 0, 0.507);
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
background-color: rgba(0, 0, 0, 0.507);
flex: 1;
display: flex;
flex-direction: column;
}
.message-logs{
overflow: auto;
flex: 1;
}
.message-logs, .message-logs .scroll {transform: scale(1,-1);}
.loading{
.message-logs {
overflow: auto;
flex: 1;
}
.chat-input-area{
display: flex;
flex-direction: column;
padding-top: 10px;
margin-bottom: 5px;
.message-logs,
.message-logs .scroll {
transform: scale(1, -1);
}
.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;
margin: auto;
}
.chat-input-area .info {
color: rgba(255, 255, 255, 0.466);
font-size: 12px;
margin-left: 25px;
margin-top: 5px;
display: flex;
}
.typing-outer{
.typing-outer {
flex: 1;
height: 20px;
}
@ -323,52 +443,57 @@ export default {
margin-right: 30px;
margin-top: 3px;
}
.message-area{
.message-area {
display: flex;
width: 100%;
}
.chat-input{
font-family: 'Roboto', sans-serif;
background: rgba(0, 0, 0, 0.158);
color: white;
flex: 1;
height: 20px;
padding: 10px;
margin: auto;
margin-left: 20px;
font-size: 15px;
resize: none;
border: none;
outline: none;
padding-left: 10px;
transition: 0.3s;
height: 1em;
overflow: hidden;
max-height: 30vh;
.chat-input {
font-family: "Roboto", sans-serif;
background: rgba(0, 0, 0, 0.158);
color: white;
width: 100%;
height: 20px;
padding: 10px;
margin: auto;
font-size: 15px;
resize: none;
border: none;
outline: none;
padding-left: 10px;
transition: 0.3s;
height: 1em;
overflow: hidden;
max-height: 30vh;
}
.chat-input:hover{
background: rgba(0, 0, 0, 0.288);
.chat-input:hover {
background: rgba(0, 0, 0, 0.288);
}
.chat-input:focus{
background: rgba(0, 0, 0, 0.466);
.chat-input:focus {
background: rgba(0, 0, 0, 0.466);
}
.send-button{
font-size: 20px;
color:white;
background: rgba(0, 0, 0, 0.274);
border: none;
outline: none;
margin: auto;
margin-left: 2px;
margin-right: 20px;
height: 40px;
transition: 0.3s;
.send-button {
font-size: 20px;
color: white;
background: rgba(0, 0, 0, 0.274);
border: none;
outline: none;
margin: auto;
margin-left: 2px;
margin-right: 20px;
height: 40px;
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);
}
.error-send-button {
@ -379,7 +504,7 @@ export default {
background-color: rgba(255, 0, 0, 0.294);
}
@media (max-width: 600px) {
.show-menu-button{
.show-menu-button {
display: block;
}
}

View file

@ -60,7 +60,7 @@ export default {
this.title = title;
},
close() {
bus.$emit('closeSettings');
this.$store.dispatch('setPopoutVisibility', {name: 'settings', visibility: false})
}
}
}

View file

@ -7,7 +7,7 @@
</div>
<div class="options">
<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 red" @click="logout">Logout</div>
</div>
@ -26,8 +26,9 @@
</template>
<script>
import UploadService from '@/services/UploadService.js'
import AvatarUpload from '@/services/AvatarUpload.js'
import config from '@/config.js'
import {bus} from '@/main'
import path from 'path'
export default {
@ -60,7 +61,7 @@ export default {
}
const formData = new FormData();
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) {
this.alert.content = 'Upload failed - Something went wrong. Try again later.';
return this.alert.show = true;
@ -74,6 +75,12 @@ export default {
changePassword() {
this.alert.content = 'Not implemented yet.';
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: {

View 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>

View file

@ -42,6 +42,7 @@ export default {
}
this.$store.dispatch('selectedChannelID', this.$props.channelID);
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();
const {ok, error, result} = await channelService.post(this.$props.channelID);
if ( ok ) {

View 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>

View 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>

View file

@ -1,6 +1,5 @@
<template>
<div class="logged-in">
<div class="title">Welcome!</div>
<div class="card">
<div class="avatar-outer">
<div class="avatar" :style="`background-image: url(${avatar})`"></div>
@ -41,22 +40,33 @@ export default {
<style scoped>
.logged-in {
margin-top: 50px;
color: white;
margin-top: 10px;
}
.card{
background-color: rgba(0, 0, 0, 0.26);
padding: 10px;
margin: 20px;
background-color: rgba(0, 0, 0, 0.041);
padding: 20px;
margin-bottom: 30px;
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 {
display: table;
margin: auto;
font-size: 30px;
margin-top: 10px;
font-size: 25px;
text-align: center;
}
.avatar {
@ -73,7 +83,7 @@ export default {
display: flex;
flex-direction: column;
margin-left: 10px;
margin-top: 5px;
margin-top: 20px;
}
.username {
@ -96,18 +106,20 @@ export default {
padding: 10px;
border: none;
outline: none;
margin-right: 5px;
margin-top: 10px;
margin-right: 10px;
margin-left: 10px;
margin-top: 30px;
transition: 0.3s;
font-size: 17px;
}
.button:hover {
background: rgba(0, 0, 0, 0.582);
}
.button.logout{
background: rgba(149, 0, 0, 0.404);
background: rgba(219, 36, 36, 0.438);
}
.button.logout:hover {
background: rgba(255, 0, 0, 0.582);
background: rgba(255, 18, 18, 0.582);
}
</style>

View file

@ -1,6 +1,6 @@
const config = {
devMode:true,
breeMode: true,
breeMode: false,
recaptcha: "",
IP: [
{

View file

@ -4,6 +4,7 @@ Vue.use(VueRouter)
import {store} from './store/index';
import MainApp from '../src/views/App.vue'
import HomePage from '../src/views/HomePage.vue'
import GDriveCallback from '../src/views/GDriveCallback.vue';
import VueSocketio from 'vue-socket.io-extended';
import io from 'socket.io-client';
import config from './config'
@ -42,6 +43,11 @@ export const router = new VueRouter({
})
next()
}
}
},
{
path: '/GDrive_callback',
name: 'GDrive callback',
component: GDriveCallback
},
]
})

View file

@ -3,7 +3,8 @@ import config from '@/config';
export const instance = () => {
return axios.create({
baseURL: config.domain
baseURL: config.domain,
withCredentials: true
})
}

View file

@ -1,7 +1,10 @@
import {instance, wrapper} from './Api';
import {
instance,
wrapper
} from './Api';
export default {
uploadAvatar(data, onProgress){
uploadAvatar(data, onProgress) {
const url = `/settings/avatar`;
let config = {
onUploadProgress(progressEvent) {

View file

@ -1,12 +1,28 @@
import {instance, wrapper} from './Api';
import filesize from "filesize";
export default {
// TODO: add ?continue=id
get ( channelID ) {
return wrapper(instance().get(`messages/${channelID}`));
},
post (channelID, data) {
return wrapper(instance().post(`messages/${channelID}`, data))
post (channelID, data, onProgress) {
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));
}
}

View file

@ -4,4 +4,10 @@ export default {
setStatus ( 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}));
}
}

View file

@ -5,12 +5,26 @@ import socketModule from './modules/socketIOModule';
import channelModule from './modules/channelModule';
import messageModule from './modules/messageModule';
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);
export const store = new Vuex.Store({
modules: { user, channelModule, messageModule, notificationsModule, socketModule },
modules: {
user,
channelModule,
messageModule,
notificationsModule,
socketModule,
settingsModule,
uploadFilesModule,
popoutsModule
},
state: {
},

View 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
}

View 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
}

View file

@ -2,6 +2,7 @@ import {bus} from '../../main'
import {router} from './../../router'
import Vue from 'vue';
const state = {
}
@ -16,7 +17,7 @@ const actions = {
},
socket_success(context, data) {
const {message, user, dms, notifications, currentFriendStatus} = data;
const {message, user, dms, notifications, currentFriendStatus, settings} = data;
const friendsArray = user.friends;
const friendObject = {};
@ -44,6 +45,7 @@ const actions = {
}
context.commit('addAllChannels', channelsObject)
context.dispatch('addAllNotifications', notifications)
context.dispatch('setSettings', settings)
},
@ -100,6 +102,10 @@ const actions = {
['socket_notification:dismiss'](context, data){
const {channelID} = data;
context.dispatch('dismissNotification', channelID);
},
['socket_googleDrive:linked'](context) {
context.dispatch('setPopoutVisibility', {name: 'GDLinkMenu', visibility: false})
context.dispatch('setGDriveLinked', true)
}
}

View 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
}

View file

@ -2,108 +2,104 @@
<div id="app">
<vue-headful :title="title" description="Nertivia Chat Client"/>
<div class="background-image"></div>
<transition name="fade-between-two" appear >
<ConnectingScreen v-if="!loggedIn" />
<transition name="fade-between-two" appear>
<ConnectingScreen v-if="!loggedIn"/>
<div class="box" v-if="loggedIn">
<div class="panel-layout">
<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>
<RightPanel> </RightPanel>
<RightPanel/>
</div>
</div>
</transition>
<transition name="fade">
<settings v-if="showSettings && loggedIn" />
<!--<GDriveLinkMenu v-if="loggedIn" /> -->
</transition>
<Popouts v-if="loggedIn"/>
</div>
</template>
<script>
import {bus} from '../main'
import Settings from '@/components/app/Settings.vue'
// import GDriveLinkMenu from '@/components/app/GDriveLinkMenu.vue'
import LeftPanel from './../components/app/LeftPanel.vue'
import RightPanel from './../components/app/RightPanel.vue'
import ConnectingScreen from './../components/app/ConnectingScreen.vue'
import { bus } from "../main";
import Popouts from "@/components/app/Popouts.vue";
import LeftPanel from "./../components/app/LeftPanel.vue";
import RightPanel from "./../components/app/RightPanel.vue";
import ConnectingScreen from "./../components/app/ConnectingScreen.vue";
export default {
name: 'app',
name: "app",
components: {
LeftPanel,
RightPanel,
ConnectingScreen,
Settings,
},
data() {
return {
showLeftPanel: false,
showSettings: false,
title: "Nertivia"
}
};
},
methods: {
hideLeftPanel(test) {
if (this.showLeftPanel){
if(test.target.closest('.show-menu-button') == null){
hideLeftPanel(event) {
if (this.showLeftPanel) {
if (event.target.closest(".show-menu-button") == null) {
this.showLeftPanel = false;
}
}
}
},
mounted() {
bus.$on('toggleLeftMenu', () => {
bus.$on("toggleLeftMenu", () => {
this.showLeftPanel = !this.showLeftPanel;
})
bus.$on('closeLeftMenu', () => {
});
bus.$on("closeLeftMenu", () => {
this.showLeftPanel = false;
})
bus.$on('openSettings', () => {
this.showSettings = true;
})
bus.$on('closeSettings', () => {
this.showSettings = false;
})
bus.$on('title:change', (title) => {
});
bus.$on("title:change", title => {
this.title = title;
})
});
},
computed: {
loggedIn() {
return this.$store.getters.loggedIn
return this.$store.getters.loggedIn;
}
}
}
};
</script>
<style scoped>
.slidein-enter-active, .slidein-leave-active {
transition: .5s;
.slidein-enter-active,
.slidein-leave-active {
transition: 0.5s;
}
.slidein-enter, .slidein-leave-to /* .fade-leave-active below version 2.1.8 */ {
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;
}
.fade-between-two-enter, .fade-between-two-leave-to {
.fade-between-two-enter,
.fade-between-two-leave-to {
opacity: 0 !important;
}
.fade-enter-active, .fade-leave-active {
transition: opacity .2s;
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
@media (max-width: 600px) {
.left-panel{
.left-panel {
position: absolute;
top: 47px;
height: calc(100% - 47px);
@ -115,19 +111,18 @@ export default {
<style>
html{
html {
height: 100%;
}
body{
body {
margin: 0;
height: 100%;
overflow: hidden;
}
#app {
font-family: 'Roboto', sans-serif;
font-family: "Roboto", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #383838;
@ -148,37 +143,34 @@ body{
background-repeat: no-repeat;
background-position: bottom;
background-size: cover;
}
.panel-layout {
display: flex;
height: 100%;
}
</style>
<style>
/* ------- SCROLL BAR -------*/
/* width */
::-webkit-scrollbar {
width: 10px;
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
background: #8080806b;
background: #8080806b;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #f5f5f559;
background: #f5f5f559;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #f5f5f59e;
background: #f5f5f59e;
}
</style>

View file

@ -0,0 +1,14 @@
<template>
<div>
Redirecting...
</div>
</template>
<script>
export default {
mounted() {
window.opener.postMessage({code: location.href}, "*");
}
}
</script>

View file

@ -312,7 +312,7 @@ button {
margin-right: 0;
margin-top: 0;
height:calc(100% - 50px);
background-color: rgba(34, 34, 34, 0.877);
background-color: rgb(34, 34, 34);
width: 0;
overflow-x: hidden;
transition: 0.5s ease;