home page redesign.

This commit is contained in:
supertiger1234 2019-04-28 11:21:03 +01:00
parent 6a29642e56
commit a84a097f83
30 changed files with 2367 additions and 1059 deletions

10
package-lock.json generated
View file

@ -7825,6 +7825,11 @@
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"dev": true
},
"particles.js": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/particles.js/-/particles.js-2.0.0.tgz",
"integrity": "sha1-IThsQyjWx/lngKIB6W7t/AnHNvY="
},
"pascalcase": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@ -10949,6 +10954,11 @@
"resolved": "https://registry.npmjs.org/vue-script2/-/vue-script2-2.0.3.tgz",
"integrity": "sha512-sGsFbqCIZDbPq140X1p8T6Y72MoCtkFpQ7CfhknZLqDZxMGWavFqIY/5YsnhqxxatWnYy0us/IoHSDr5l2JZzQ=="
},
"vue-smooth-reflow": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/vue-smooth-reflow/-/vue-smooth-reflow-0.1.12.tgz",
"integrity": "sha512-cic+dmqsBzu/lRMXf/mhUPMM0g0vancJGUyNMMGHjis0ilSFd1RjIrZYcU5B7ef+n5oshH33/zbMA8OYyRTu7Q=="
},
"vue-socket.io": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/vue-socket.io/-/vue-socket.io-3.0.4.tgz",

View file

@ -13,6 +13,7 @@
"futoji": "^0.5.0",
"jquery": "^3.4.0",
"match-sorter": "^2.3.0",
"particles.js": "^2.0.0",
"socket.io": "^2.2.0",
"socket.io-client": "^2.2.0",
"twemoji": "^11.3.0",
@ -22,6 +23,7 @@
"vue-mq": "^1.0.1",
"vue-recaptcha": "^1.1.1",
"vue-router": "^3.0.2",
"vue-smooth-reflow": "^0.1.12",
"vue-socket.io": "^3.0.4",
"vue-socket.io-extended": "^3.2.0",
"vue-twitter": "^0.1.0",

View file

@ -43,8 +43,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Google recaptcha -->
<script src="https://www.google.com/recaptcha/api.js?onload=vueRecaptchaApiLoaded&render=explicit" async defer>
<!-- Google recaptcha
<script src="https://www.google.com/recaptcha/api.js?onload=vueRecaptchaApiLoaded&render=explicit" async defer> -->
</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-131765299-1"></script>

View file

@ -3,3 +3,47 @@
<router-view></router-view>
</div>
</template>
<style>
html {
height: 100%;
}
body {
margin: 0;
height: 100%;
overflow: hidden;
}
#app {
font-family: "Roboto", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #383838;
height: 100%;
}
/* ------- SCROLL BAR -------*/
/* width */
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
background: #8080806b;
border-radius: 10px;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #f5f5f559;
border-radius: 10px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #f5f5f59e;
border-radius: 10px;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

View file

@ -0,0 +1,122 @@
<template>
<transition name="fade-in" appear >
<div class="header">
<div :class="{animation: true, night: !isDay }" id="animation">
<div :class="{map: true, 'night-map': !isDay }"></div>
</div>
<div class="title">Nertivia</div>
</div>
</transition>
</template>
<script>
export default {
data() {
return {
isDay: true
};
},
mounted() {
setInterval(_ => {
if (this.isDay) {
this.$emit("isDay", false);
this.isDay = false;
} else {
this.$emit("isDay", true);
this.isDay = true;
}
}, 10000);
}
};
</script>
<style scoped>
.fade-in-enter-active {
opacity: 0;
animation: bounce-in .5s;
animation-delay: 0.5s;
}
@keyframes bounce-in {
0% {
transform: translateY(-20px);
opacity: 0;
}
50%{
transform: translateY(10px);
}
100% {
transform: translateY(0);
opacity: 1;
}
}
/* .fade-in-enter-active, .fade-in-leave-active {
transition: .5s;
transition-delay: 0.5s
}
.fade-in-enter, .fade-in-leave-to {
opacity: 0;
transform: translateY(-50px)
} */
.header {
display: flex;
flex-direction: column;
margin: auto;
align-items: center;
margin-bottom: 0;
margin-top: 50px;
z-index: 99999;
}
.animation {
height: 200px;
width: 200px;
background-color: #2cb4ff;
border-radius: 50%;
box-shadow: 0px 0px 96px -4px rgba(69, 212, 255, 1);
overflow: hidden;
transition: 10s;
margin: auto;
margin-bottom: 0;
}
.map {
height: 200px;
width: 200px;
background-position: -490px center;
background-size: 170%;
background-repeat: no-repeat;
background-image: url(./../assets/LogoAnimation/map.png);
animation: rotateGlobe;
animation-timing-function: linear;
animation-duration: 10s;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
transition: 10s;
}
.night-map {
filter: grayscale(90%);
}
.night {
background: rgb(0, 48, 73);
box-shadow: 0px 0px 96px -4px rgb(0, 48, 73);
}
@keyframes rotateGlobe {
from {
background-position: -400px center;
}
to {
background-position: 190px center;
}
}
.header .title {
color: white;
font-size: 40px;
margin-top: 10px;
}
</style>

View file

@ -0,0 +1,136 @@
<template>
<div id="particles-js"></div>
</template>
<script>
export default {
name: "ParticlesJS",
mounted() {
require("particles.js");
this.$nextTick(() => {
this.initParticlesJS();
});
},
methods: {
initParticlesJS() {
/* eslint-disable */
particlesJS("particles-js", {
particles: {
number: {
value: 100,
density: {
enable: true,
value_area: 480
}
},
color: {
value: "#ffffff"
},
shape: {
type: "circle",
stroke: {
width: 0,
color: "#000000"
},
},
opacity: {
value: 1,
random: true,
anim: {
enable: true,
speed: 1,
opacity_min: 0,
sync: false
}
},
size: {
value: 2.5,
random: true,
anim: {
enable: false,
speed: 4,
size_min: 0.3,
sync: false
}
},
line_linked: {
enable: false,
distance: 150,
color: "#ffffff",
opacity: 0.4,
width: 1
},
move: {
enable: true,
speed: 0.1,
direction: "none",
random: true,
straight: false,
out_mode: "out",
bounce: false,
attract: {
enable: false,
rotateX: 600,
rotateY: 600
}
}
},
interactivity: {
detect_on: "canvas",
events: {
onhover: {
enable: false,
mode: "bubble"
},
onclick: {
enable: false,
mode: "repulse"
},
resize: true
},
modes: {
grab: {
distance: 400,
line_linked: {
opacity: 1
}
},
bubble: {
distance: 250,
size: 0,
duration: 2,
opacity: 0,
speed: 3
},
repulse: {
distance: 400,
duration: 0.4
},
push: {
particles_nb: 4
},
remove: {
particles_nb: 2
}
}
},
retina_detect: true
});
}
}
};
</script>
<style scoped>
#particles-js {
width: 100%;
height: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
</style>

View file

@ -13,6 +13,17 @@ export default {
}
},
beforeMount(){
const existing = document.getElementById('reCaptchaScript');
if (existing)
existing.remove();
const $script = document.createElement('script')
$script.async = true
$script.id = "reCaptchaScript"
$script.src = 'https://www.google.com/recaptcha/api.js?onload=vueRecaptchaApiLoaded&render=explicit'
document.head.appendChild($script)
},
methods: {
submit(response) {
this.$emit('verify', response)

View file

@ -1,15 +1,18 @@
<template>
<div class="loading-screen">
<div class="loading-animation"></div>
<div class="title">{{$props.msg}}</div>
<div class="loading-animation" :style="`height: ${size}px ; width: ${size}px ;`"></div>
<div class="title" v-if="msg">{{$props.msg}}</div>
</div>
</template>
<script>
export default {
props: [
"msg"
]
props: {
msg: String,
size: {
default: 100
}
}
}
</script>
@ -19,8 +22,6 @@ export default {
margin: auto;
}
.loading-animation{
height: 100px;
width: 100px;
background-size: 100%;
background-image: url(../assets/spinner.svg);
display: table;

View file

@ -525,8 +525,8 @@ export default {
}
.heading {
border-bottom: solid 2px white;
margin: 5px;
padding: 5px;
background: rgba(0, 0, 0, 0.185);
margin-bottom: 0;
height: 40px;
padding-bottom: 2spx;

View file

@ -278,6 +278,7 @@ export default {
}
.username:hover{
color: rgb(199, 199, 199);
text-decoration: underline;
}
.date {
color: rgb(161, 161, 161);

View file

@ -24,8 +24,6 @@ import config from "@/config.js";
import MessageTemplate from '@/components/app/MessageTemplate.vue';
import SettingsService from '@/services/settingsService.js';
import { mapState } from "vuex";
export default {
components: {
MessageTemplate
@ -47,7 +45,9 @@ export default {
}
},
computed: {
...mapState('settingsModule', ['apperance']),
apperance() {
return this.$store.getters['settingsModule/settings'].apperance;
},
user() {
return this.$store.getters.user
}

View file

@ -1,34 +1,78 @@
<template>
<div class="left-panel">
<MyMiniInformation />
<MyMiniInformation/>
<div class="list">
<online-friends />
<server v-for="(data, index) in serverData" :key="index" :server-data="data" @click.native="toggleChannel(index, $event)" :open-channel="openedServer && openedServer === index"/>
<server mode="ADD_SERVER"/>
</div>
</div>
</template>
<script>
import MyMiniInformation from '../../components/app/MyMiniInformation.vue'
import OnlineFriends from './relationships/OnlineFriends.vue'
import MyMiniInformation from "@/components/app/MyMiniInformation.vue";
import Server from "@/components/app/ServerTemplate/ServerTemplate.vue";
export default {
components: {
MyMiniInformation,
OnlineFriends,
Server
},
data() {
return {
openedServer: null,
serverData: {
"67574563576897": {
name: "DevHelp",
channels: [
{ name: "General", channelID: 3563567574767467 },
{ name: "NodeJS", channelID: 758546746747378 },
{ name: "Java", channelID: 57355675747875 },
{ name: "C#", channelID: 45656764765676 }
]
},
"24325587980787": {
name: "Musica",
channels: [
{ name: "General", channelID: 3563567574767467 },
{ name: "NodeJS", channelID: 758546746747378 },
{ name: "Java", channelID: 57355675747875 },
{ name: "C#", channelID: 45656764765676 }
]
},
"3468636435": {
name: "IDK",
channels: [
{ name: "General", channelID: 3563567574767467 },
{ name: "NodeJS", channelID: 758546746747378 },
{ name: "Java", channelID: 57355675747875 },
{ name: "C#", channelID: 45656764765676 }
]
},
"63575764574645": {
name: "OWO",
channels: [
{ name: "General", channelID: 3563567574767467 },
{ name: "NodeJS", channelID: 758546746747378 },
{ name: "Java", channelID: 57355675747875 },
{ name: "C#", channelID: 45656764765676 }
]
}
}
};
},
methods: {
toggleChannel(index, event) {
if (!event.target.closest('.small-view')) return;
if (this.openedServer === index)
this.openedServer = null;
else
this.openedServer = index;
}
}
}
};
</script>
<style scoped>
.left-panel {
height: 100%;
background-color: rgba(0, 0, 0, 0.671);
@ -38,33 +82,33 @@ export default {
flex-direction: column;
z-index: 1;
}
.list{
margin: 10px;
.list {
margin: 2px;
margin-left: 5px;
margin-right: 5px;
flex: 1;
overflow: auto;
user-select: none;
}
/* ------- SCROLL BAR -------*/
/* width */
.list::-webkit-scrollbar {
width: 3px;
width: 3px;
}
/* Track */
.list::-webkit-scrollbar-track {
background: #8080806b;
background: #8080806b;
}
/* Handle */
.list::-webkit-scrollbar-thumb {
background: #f5f5f559;
background: #f5f5f559;
}
/* Handle on hover */
.list::-webkit-scrollbar-thumb:hover {
background: #f5f5f59e;
background: #f5f5f59e;
}
</style>

View file

@ -0,0 +1,35 @@
<template>
<div class="channel">
<i class="material-icons">storage</i>
<div class="channel-name">{{ChannelData.name}}</div>
</div>
</template>
<script>
export default {
props: ["ChannelData"]
};
</script>
<style scoped>
.channel {
display: flex;
align-items: center;
margin: 5px;
margin-top: 3px;
margin-bottom: 3px;
padding: 5px;
border-radius: 5px;
transition: 0.3s;
font-size: 14px;
}
.channel:hover {
background: rgba(139, 139, 139, 0.288);
}
.channel-name {
overflow: hidden;
text-overflow: ellipsis;
margin-left: 5px;
}
</style>

View file

@ -0,0 +1,99 @@
<template>
<div :class="{server: true, 'add-server': mode === 'ADD_SERVER'}">
<div class="small-view" @click="showChannels = !showChannels">
<profile-picture size="50px" v-if="!mode" :url="tempImage"/>
<div class="add-icon" v-if="mode === 'ADD_SERVER'">
<i class="material-icons">add</i>
</div>
<div class="server-name">{{mode === 'ADD_SERVER'? 'Add Server' : ServerData.name}}</div>
</div>
<div ref="container">
<div class="channel-list" v-if="openChannel">
<ChannelTemplate
v-for="channel in ServerData.channels"
:key="channel.name"
:channel-data="channel"
/>
</div>
</div>
</div>
</template>
<script>
import config from "@/config.js";
import ChannelTemplate from "@/components/app/ServerTemplate/ChannelTemplate.vue";
import ProfilePicture from "@/components/ProfilePictureTemplate.vue";
import smoothReflow from "vue-smooth-reflow";
export default {
mixins: [smoothReflow],
props: ["ServerData", "openChannel", "mode"],
components: { ProfilePicture, ChannelTemplate },
data() {
return {
showChannels: false,
tempImage: config.domain + "/avatars/noob"
};
},
mounted() {
this.$smoothReflow({
el: this.$refs.container
});
}
};
</script>
<style scoped>
.server {
color: white;
display: flex;
flex-direction: column;
background-color: rgba(0, 0, 0, 0.137);
border-radius: 5px;
margin: 5px;
transition: 0.3s;
}
.add-server{
background: rgba(0, 255, 21, 0.277);
}
.server:hover {
background: rgba(0, 0, 0, 0.288);
}
.add-server:hover{
background: rgba(0, 255, 21, 0.377);
}
.small-view {
padding-right: 0;
display: flex;
transition: 0.3s;
position: relative;
overflow: hidden;
align-items: center;
padding: 5px;
}
.channel-list {
background: rgba(0, 0, 0, 0.288);
display: flex;
flex-direction: column;
}
.server-name {
overflow: hidden;
text-overflow: ellipsis;
margin-left: 5px;
}
.add-icon{
height: 56px;
display: flex;
align-items: center;
margin-right: 10px;
margin-left: 5px;
}
.add-icon .material-icons {
font-size: 40px;
}
</style>

View file

@ -58,8 +58,6 @@ export default {
@media (max-width: 600px) {
.left-panel {
position: absolute;
top: 47px;
height: calc(100% - 47px);
background-color: rgba(39, 39, 39, 0.97);
}
}

View file

@ -58,8 +58,6 @@ export default {
@media (max-width: 600px) {
.left-panel {
position: absolute;
top: 47px;
height: calc(100% - 47px);
background-color: rgba(39, 39, 39, 0.97);
}
}

View file

@ -1,130 +0,0 @@
<template>
<div class="logged-in">
<div class="card">
<div class="avatar-outer">
<profile-picture :url="avatar" :admin="user.admin" size="90px" emoteSize="27px" />
</div>
<div class="info">
<div class="username">{{user.username}}<span class="tag">@{{user.tag}}</span></div>
<div class="buttons">
<button class="button" @click="openChat">Enter</button>
<button class="button logout" @click="logout">Logout</button>
</div>
</div>
</div>
</div>
</template>
<script>
import ProfilePicture from "@/components/ProfilePictureTemplate.vue";
import config from '@/config.js'
export default {
components: {
ProfilePicture
},
methods: {
logout() {
this.$store.commit('logout')
},
openChat() {
window.location.href = "/app"
}
},
computed: {
user() {
return this.$store.getters.user;
},
avatar() {
return config.domain + "/avatars/" +this.$store.getters.user.avatar
}
}
}
</script>
<style scoped>
.logged-in {
color: white;
margin-top: 10px;
z-index: -1;
}
.card{
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;
margin-top: 10px;
font-size: 25px;
text-align: center;
}
.profile-picture {
margin-right: 0;
}
.info {
display: flex;
flex-direction: column;
margin-left: 10px;
margin-top: 20px;
}
.username {
font-size: 20px;
word-wrap: break-word;
word-break: break-word;
white-space: pre-wrap;
overflow: hidden;
user-select: all;
max-width: 200px;
margin: auto;
}
.tag{
font-size: 13px;
color: rgb(194, 194, 194);
}
.buttons{
display: flex;
margin: auto;
}
.button {
color: white;
background: rgba(0, 0, 0, 0.308);
padding: 10px;
border: none;
outline: none;
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(219, 36, 36, 0.438);
}
.button.logout:hover {
background: rgba(255, 18, 18, 0.582);
}
</style>

View file

@ -1,103 +0,0 @@
<template>
<div class="form">
<form action="#" @submit.prevent="login">
<div class="input">
<div class="alert other-alert">{{otherError}}</div>
<div class="input-title">Email: <div class="alert">{{email.alert}}</div></div>
<input type="email" autocomplete="on" v-model="email.value">
</div>
<div class="input">
<div class="input-title">Password: <div class="alert">{{password.alert}}</div></div>
<input type="password" autocomplete="current-password" v-model="password.value">
</div>
<div class="cap">
<recaptcha ref="recaptcha" @verify="captchaSubmit" />
</div>
<loadingButton :loading="currentMessage == 1" :message="buttonMessages[currentMessage]" />
</form>
</div>
</template>
<script>
import Recaptcha from '../Recaptcha.vue'
import {bus} from '../../main';
import loadingButton from "../../components/Button.vue"
import AuthenticationService from '@/services/AuthenticationService';
export default {
components: {
loadingButton,
Recaptcha
},
data() {
return {
email: {value: "", alert: ""},
password: {value: "", alert: ""},
otherError: "",
buttonMessages: [
"Login",
"Logging in..."
],
currentMessage: 0,
captcha: null
}
},
methods: {
resetValues() {
// Resets all of the alert values
this.email.alert = "";
this.password.alert = "";
this.otherError = "";
},
async login() {
this.currentMessage = 1
this.resetValues();
const email = this.email.value.trim();
const password = this.password.value.trim();
const captcha = this.captcha;
const {ok, error, result} = await AuthenticationService.login({email, password, token: captcha})
this.currentMessage = 0
if (ok) {
this.$store.dispatch('token', result.data.token)
this.$store.dispatch('user', result.data.user)
} else {
this.captcha = null;
this.$refs.recaptcha.resetRecaptcha();
if (error.response === undefined) {
this.otherError = "Can't connect to server."
return;
}
const errors = error.response.data.errors;
for (let index in errors) {
const message = errors[index].msg;
const param = errors[index].param;
if(this[param] === undefined) {
this.otherError = message;
} else {
this[param].alert = message;
}
}
}
},
captchaSubmit(token) {
this.captcha = token;
}
}
}
</script>
<style scoped>
.input {
display: table;
margin: auto;
}
.cap {
margin: 20px;
opacity: 0.8;
transition: 0.3s;
}
.cap:hover {
opacity: 1;
}
</style>

View file

@ -0,0 +1,131 @@
<template>
<div class="profile-popout">
<div class="triangle"></div>
<div class="inner">
<div class="outer-profile-picture" v-if="user">
<profile-picture
:url="avatarDomain + user.avatar"
:admin="user.admin"
size="40px"
emoteSize="17px"
/>
</div>
<div class="information">
<div
class="username"
>{{user.username}}</div>
<div class="tag">@{{user.tag}}</div>
</div>
<div class="buttons">
<i class="material-icons warn" @click="logOut">exit_to_app</i>
<i class="material-icons" @click="openApp">message</i>
</div>
</div>
</div>
</template>
<script>
import ProfilePicture from "@/components/ProfilePictureTemplate.vue";
import config from "@/config.js";
export default {
props: ["user"],
components: { ProfilePicture },
data() {
return {
avatarDomain: config.domain + "/avatars/"
};
},
methods: {
openApp() {
window.location.href = "/app";
},
logOut() {
this.$emit('logout');
}
}
};
</script>
<style scoped>
.profile-popout {
position: absolute;
display: flex;
flex-direction: column;
right: 3px;
top: 75px;
}
.triangle {
position: absolute;
right: 30px;
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 15px solid rgba(0, 0, 0, 0.826);
}
.inner {
right: 0;
top: 15px;
position: absolute;
display: flex;
background: rgba(0, 0, 0, 0.826);
border-radius: 5px;
width: 300px;
height: 60px;
padding: 10px;
align-items: center;
}
.outer-profile-picture {
z-index: 999999;
margin-left: 10px;
}
.information {
margin-left: 10px;
width: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.username {
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
height: 17px;
}
.tag {
color: rgb(163, 163, 163);
}
.buttons {
display: flex;
margin: 5px;
}
.buttons .material-icons {
display: block;
margin: 5px;
color: rgba(255, 255, 255, 0.651);
transition: 0.3s;
cursor: default;
font-size: 27px;
}
.buttons .material-icons:hover {
color: white;
}
.buttons .warn {
color: rgba(255, 0, 0, 0.651);
}
.buttons .warn:hover {
color: rgba(255, 0, 0, 0.795);
}
@media (max-width: 342px) {
.inner {
left: 0;
right:0;
width: 100%;
}
.profile-popout{
left: 0;
right:20px;
}
}
</style>

View file

@ -1,120 +0,0 @@
<template>
<div class="form">
<form action="#" @submit.prevent="register">
<div class="input">
<div class="alert">{{otherError}}</div>
<div class="input-title">Email: <div class="alert">{{email.alert}}</div></div>
<input type="email" autocomplete="on" v-model="email.value">
</div>
<div class="input">
<div class="input-title">Username: <div class="alert">{{username.alert}}</div></div>
<input type="username" autocomplete="off" v-model="username.value">
</div>
<div class="input">
<div class="input-title">Password: <div class="alert">{{password.alert}}</div></div>
<input type="password" autocomplete="new-password" v-model="password.value">
</div>
<div class="input">
<div class="input-title">Password confirm: <div class="alert">{{passwordConfirm.alert}}</div></div>
<input type="password" autocomplete="new-password" v-model="passwordConfirm.value">
</div>
<div class="cap">
<recaptcha ref="recaptcha" @verify="captchaSubmit" />
</div>
<loading-button :loading="currentMessage == 1" :message="buttonMessages[currentMessage]" />
</form>
</div>
</template>
<script>
import Recaptcha from '../Recaptcha.vue'
import AuthenticationService from '@/services/AuthenticationService.js'
import {bus} from '../../main';
import LoadingButton from "../../components/Button.vue"
export default {
components: {
LoadingButton,
Recaptcha
},
data() {
return {
email: {value: "", alert: ""},
username: {value: "", alert: ""},
password: {value: "", alert: ""},
passwordConfirm: {value: "", alert: ""},
otherError: "",
buttonMessages: [
"Register",
"Creating... ( Thank you c: )"
],
currentMessage: 0,
captcha: null
}
},
methods: {
resetValues() {
// Resets all of the alert values
this.email.alert = "";
this.username.alert = "";
this.password.alert = "";
this.passwordConfirm.alert = "";
this.otherError = ""
},
async register() {
this.currentMessage = 1
this.resetValues();
const email = this.email.value.trim();
const username = this.username.value.trim();
const password = this.password.value.trim();
const passwordConfirm = this.passwordConfirm.value.trim();
// check if password + password confirm matches.
if ( password != passwordConfirm ) {
this.currentMessage = 0;
return this.passwordConfirm.alert = "Passwords do not match!"
}
const {ok, error, result} = await AuthenticationService.register({email, username, password, token: this.captcha})
this.currentMessage = 0
if ( ok ) {
this.$store.dispatch('token', result.data.token)
this.$store.dispatch('user', result.data.user)
} else {
this.captcha = null;
this.$refs.recaptcha.resetRecaptcha();
if (error.response === undefined) {
this.otherError = "Can't connect to server."
return;
}
const errors = error.response.data.errors;
for (let index in errors) {
const message = errors[index].msg;
const param = errors[index].param;
if(this[param] === undefined) {
this.otherError = message;
} else {
this[param].alert = message;
}
}
}
},
captchaSubmit(token) {
this.captcha = token;
}
}
}
</script>
<style scoped>
.input {
display: table;
margin: auto;
}
.cap {
margin: 20px;
opacity: 0.8;
transition: 0.3s;
}
.cap:hover {
opacity: 1;
}
</style>

View file

@ -1,184 +0,0 @@
<template>
<div class="right-panel-home">
<div class="right-panel-inner">
<img class="logo" src="./../../assets/logo.svg" />
<div class="title">Nertivia</div>
<spinner :msg="spinnerMessage" v-if="previouslyLoggedIn && user == null && tokenExists" />
<transition name="component-fade" appear mode="out-in" v-else>
<logged-in v-if="tokenExists && user != null" />
<div class="new-member" v-if="!tokenExists">
<div class="details">
Nertivia chat is the best chat client to be made with 99% uptime, you won't miss a thing! Join now if youre new!
</div>
<div class="switch-buttons">
<div :class="{button: true, selected: loginSelected}" @click="loginSelected = true">Already a pro</div>
<div :class="{button: true, selected: !loginSelected}" @click="loginSelected = false">I'm new!</div>
</div>
<transition name="switch-selected" mode="out-in">
<login-panel v-if="loginSelected" />
<register-panel v-if="!loginSelected" />
</transition>
</div>
</transition>
</div>
</div>
</template>
<script>
import {bus} from '../../main';
import AuthenticationService from '@/services/AuthenticationService.js'
import RegisterPanel from "../../components/homePage/RegisterPanel.vue"
import LoginPanel from "../../components/homePage/LoginPanel.vue"
import LoggedIn from "../../components/homePage/LoggedIn.vue"
import Spinner from "../../components/Spinner.vue"
export default {
components: {
RegisterPanel,
LoginPanel,
LoggedIn,
Spinner
},
data() {
return {
loginSelected: true,
previouslyLoggedIn: false,
spinnerMessage: "Logging in...",
connectionRetryCount: 0
}
},
methods: {
async getUser() {
// Get details if previously logged in.
if (this.previouslyLoggedIn) {
const { ok, error, result } = await AuthenticationService.user();
if ( ok ) {
this.$store.commit( 'user', result.data.user );
} else {
if ( error.response === undefined ) {
this.connectionRetryCount++;
this.spinnerMessage = `Connection failed. Trying again (${this.connectionRetryCount})`
setTimeout(() => {
this.getUser();
}, 5000);
return;
}
this.$store.commit( 'logout' );
}
}
}
},
async mounted() {
this.previouslyLoggedIn = this.tokenExists;
this.getUser()
},
computed: {
tokenExists() {
return this.$store.getters.tokenExists;
},
user() {
return this.$store.getters.user;
}
},
}
</script>
<style scoped>
.component-fade-enter-active, .component-fade-leave-active {
transition: .3s ease;
}
.component-fade-enter, .component-fade-leave-to {
transform: translateY(20px);
opacity: 0;
}
.switch-selected-enter-active, .switch-selected-leave-active {
transition: .3s ease;
}
.switch-selected-enter, .switch-selected-leave-to {
transform: translateY(20px);
opacity: 0;
}
.right-panel-home {
width: 400px;
height: 100%;
background: rgba(0, 0, 0, 0.493);
display: flex;
flex-direction: column;
overflow: auto;
user-select: none;
}
.right-panel-inner{
display: flex;
flex-direction: column;
height: 100%;
z-index: 0;
}
.new-member{
display: flex;
flex-direction: column;
transition: .3s;
}
.logo {
height: 150px;
width: 150px;
border-radius: 50%;
box-shadow: 0px 0px 96px -4px rgba(69,212,255,1);
margin: auto;
margin-bottom: 0;
margin-top: 40px;
flex-shrink: 0;
}
.title{
color: white;
font-size: 35px;
text-align: center;
margin-top: 50px;
margin-bottom: 10px;
}
.right-panel .title {
margin: auto;
margin-top: 10px;
width: 230px;
font-size: 40px;
text-align: center;
}
.details{
color: rgb(204, 204, 204);
margin: 49px;
margin-top: 10px;
}
.switch-buttons{
display: table;
margin: auto;
}
.button{
color: white;
font-size: 18px;
display: inline-block;
padding: 10px;
margin: 5px;
user-select: none;
transition: 0.3s;
}
.button:hover{
border-bottom: solid 2px rgba(255, 255, 255, 0.493);
}
.button.selected{
border-bottom: solid 2px white;
}
</style>

View file

@ -1,58 +1,88 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import {store} from './store/index';
import VueSocketio from 'vue-socket.io-extended';
import io from 'socket.io-client';
import config from './config'
import VueMq from 'vue-mq'
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
import { store } from "./store/index";
import VueSocketio from "vue-socket.io-extended";
import io from "socket.io-client";
import config from "./config";
import VueMq from "vue-mq";
import MainApp from '../src/views/App.vue'
import HomePage from '../src/views/HomePage.vue'
import GDriveCallback from '../src/views/GDriveCallback.vue';
// import MainApp from '../src/views/App.vue'
//import HomePage from '../src/views/HomePage.vue'
//import GDriveCallback from '../src/views/GDriveCallback.vue';
//import LoginPage from '../src/views/LoginPage.vue';
//import RegisterPage from '../src/views/RegisterPage.vue';
// const MainApp = () => import('../src/views/App.vue');
// const HomePage = () => import('../src/views/HomePage.vue');
// const GDriveCallback = () => import( '../src/views/GDriveCallback.vue');
const MainApp = () => import(/* webpackChunkName: "MainApp" */ "../src/views/App.vue");
const HomePage = () => import(/* webpackChunkName: "HomePage" */ "../src/views/HomePage.vue");
const GDriveCallback = () => import(/* webpackChunkName: "GDriveCallback" */ "../src/views/GDriveCallback.vue");
const LoginPage = () => import(/* webpackChunkName: "LoginPage" */ "../src/views/LoginPage.vue");
const RegisterPage = () => import(/* webpackChunkName: "RegisterPage" */ "../src/views/RegisterPage.vue");
export const router = new VueRouter({
mode: 'history',
mode: "history",
routes: [
{
path: '/',
name: 'HomePage',
path: "/",
name: "HomePage",
component: HomePage
},
{
path: '/app',
name: 'app',
component: MainApp,
beforeEnter (to, from, next) {
if (!localStorage.getItem('hauthid')){
return router.push({ path: '/' })
path: "/login",
name: "Login",
component: LoginPage,
beforeEnter(to, from, next) {
if (localStorage.getItem("hauthid")) {
return router.push({ path: "/app" });
}
Vue.use(VueSocketio, io(config.socketIP, {
transportOptions: {
polling: {
extraHeaders: {
authorization: localStorage.getItem('hauthid')
}
}
}
}), {store});
Vue.use(VueMq, {
breakpoints: {
mobile: 600,
desktop: Infinity,
}
})
next()
next();
}
},
{
path: '/GDrive_callback',
name: 'GDrive callback',
component: GDriveCallback
path: "/register",
name: "Register",
component: RegisterPage,
beforeEnter(to, from, next) {
if (localStorage.getItem("hauthid")) {
return router.push({ path: "/app" });
}
next();
}
},
{
path: "/app",
name: "app",
component: MainApp,
beforeEnter(to, from, next) {
if (!localStorage.getItem("hauthid")) {
return router.push({ path: "/login" });
}
Vue.use(
VueSocketio,
io(config.socketIP, {
transportOptions: {
polling: {
extraHeaders: {
authorization: localStorage.getItem("hauthid")
}
}
}
}),
{ store }
);
Vue.use(VueMq, {
breakpoints: {
mobile: 600,
desktop: Infinity
}
});
next();
}
},
{
path: "/GDrive_callback",
name: "GDrive callback",
component: GDriveCallback
}
]
})
});

View file

@ -78,9 +78,7 @@ const actions = {
const mutations = {
setApperance(state, data) {
const apperance = state.apperance || {};
apperance[Object.keys(data)[0]] = data[Object.keys(data)[0]]
state['apperance'] = apperance
Vue.set(state.apperance, Object.keys(data)[0], data[Object.keys(data)[0]])
},
setSettings(state, settings) {
Vue.set(state, Object.assign(state, settings))

View file

@ -1,14 +1,33 @@
const config = [
{
version: 2,
const prototype = {
version: 3,
title: "Make your own servers!",
shortTitle: "Servers",
date: "24/04/2019",
headColor: "rgba(155, 244, 66, 0.77)",
new: [
"",
"",
],
next: [""]
}
const config = [
{
version: 3,
title: "Page Re-designs",
shortTitle: "Page Re-designs",
date: "28/04/2019",
headColor: "rgba(155, 244, 66, 0.77)",
new: [
"Home page has been re-designed and has some cool animations.",
"New login and register pages.",
"Padding and margin adjusted and small design changes throughout the app."
],
fix: [
'Fixed bugs with appearance settings not correctly working.'
],
next: ["Servers (If nothing else distracts me -_-)"]
},
{
version: 1.5,

View file

@ -57,7 +57,7 @@ import Spinner from "./../components/Spinner.vue";
const ElectronFrameButtons = () =>
import("./../components/ElectronJS/FrameButtons.vue");
const News = () => import("./../components/app/Tabs/News.vue");
const News = () => import(/* webpackChunkName: "News" */"./../components/app/Tabs/News.vue");
//const DirectMessage = () => import('./../components/app/Tabs/DirectMessage.vue');
const DirectMessage = () => ({
component: import("./../components/app/Tabs/DirectMessage.vue"),
@ -154,7 +154,7 @@ export default {
overflow-y: hidden;
overflow-x: auto;
height: 35px;
max-width: 473px;
max-width: 479px;
flex-basis: auto; /* default value */
flex-grow: 1;
-webkit-app-region: no-drag;
@ -168,7 +168,7 @@ export default {
flex-shrink: 0;
margin: auto;
margin-right: 1px;
margin-left: 1px;
margin-left: 3px;
margin-bottom: 0;
background: rgba(0, 0, 0, 0.63);
color: white;
@ -227,23 +227,7 @@ export default {
<style>
html {
height: 100%;
}
body {
margin: 0;
height: 100%;
overflow: hidden;
}
#app {
font-family: "Roboto", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #383838;
height: 100%;
}
textarea {
font-family: "Roboto", sans-serif;
}
@ -270,32 +254,30 @@ textarea {
display: flex;
overflow: auto;
height: 100%;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
</style>
<style>
/* ------- SCROLL BAR -------*/
/* width */
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
background: #8080806b;
border-radius: 10px;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #f5f5f559;
border-radius: 10px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #f5f5f59e;
border-radius: 10px;
}
input{
padding: 10px;
background: rgba(0, 0, 0, 0.301);
outline: none;
border: none;
color: white;
margin-top: 5px;
margin-bottom: 10px;
width: 200px;
transition: 0.3s;
}
input:hover{
background: rgba(0, 0, 0, 0.452);
}
input:focus {
background: rgba(0, 0, 0, 0.603);
}
</style>

View file

@ -0,0 +1,477 @@
<template>
<div id="app">
<vue-headful title="Nertivia" description="Nertivia Chat Client"/>
<div class="background-image"></div>
<div class="layout">
<div class="small-view-nav-bar">
<div class="small-logo"></div>
<div class="small-title">Nertivia</div>
<div class="show-menu-button" @click="showMobileMenu = !showMobileMenu">
<i class="material-icons">
menu
</i>
</div>
</div>
<div class="panels">
<div class="left-panel">
<div class="title">The best chat client that wont restrict you from important and fun features.</div>
<img src="../assets/graphics/HomeGraphics.png" class="graphic-app" />
<div class="change-log-mini" @click="showChangeLog = true">
<div class="change-title">Change log <span style="font-size: 15px; color: rgba(211, 211, 211, 0.774);">Click for details</span></div>
<div class="change-list">
<div class="change" v-for="change in changelogFiltered" :key="change.title">
<div class="notable-changes">{{change.shortTitle}}</div>
<div class="change-date">{{change.date}}</div>
</div>
</div>
</div>
<div class="twitter-outer">
<twitter class="twitter">
<div slot="loading">loading .....</div>
<a class="twitter-timeline" data-height="500" data-theme="dark" href="https://twitter.com/NertiviaApp?ref_src=twsrc%5Etfw">Tweets by NertiviaApp</a>
</twitter>
</div>
</div>
<RightPanel :class="{'show-menu-content': showMobileMenu }" />
</div>
</div>
<transition name="fade">
<ChangeLog v-if="showChangeLog"/>
</transition>
</div>
</template>
<script>
import { twitter } from 'vue-twitter'
import {bus} from '../main';
import RightPanel from "./../components/homePage/RightPanel.vue"
import ChangeLog from "./../components/ChangeLog.vue"
import changelog from '@/utils/changelog.js'
export default {
components: {
RightPanel,
ChangeLog,
twitter
},
data() {
return {
loginSelected: true,
showMobileMenu: false,
showChangeLog: false,
changelog
}
},
mounted() {
bus.$on('closeChangeLog', () => {
this.showChangeLog = false;
})
},
computed: {
changelogFiltered() {
return this.changelog.slice(0, 3)
}
}
}
</script>
<style>
html {
height: 100%;
}
body {
margin: 0;
height: 100%;
overflow: hidden;
}
#app {
font-family: "Roboto", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #383838;
height: 100%;
}
</style>
<style scoped>
.twitter-outer{
margin: auto;
width: 600px;
opacity: 0.8;
transition: 0.3s;
}
.twitter-outer:hover{
opacity: 1;
}
.fade-enter-active, .fade-leave-active {
transition: opacity .2s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
#app {
font-family: 'Roboto', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #383838;
height: 100%;
}
button {
font-family: 'Roboto', sans-serif;
}
.spinner{
margin: auto;
padding: 30px;
}
.background-image {
background: url(./../assets/background.jpg);
position: absolute;
z-index: -1;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-repeat: no-repeat;
background-position: bottom;
background-size: cover;
}
.layout{
display: flex;
height: 100%;
width:100%;
flex-direction: column;
}
.panels{
display: flex;
height: 100%;
width: 100%;
}
.left-panel {
flex: 1;
background: rgba(0, 0, 0, 0.253);
overflow: auto;
display: flex;
flex-direction: column;
}
.loader{
display: flex;
flex-direction: column;
}
.title-panel{
width: 100%;
height: 150px;
}
.graphics-panel{
flex: 1;
}
.graphic-app{
display: table;
margin: auto;
margin-top: 20px;
margin-bottom: 20px;
width: 900px;
height: auto;
user-select: none;
}
.title{
color: white;
font-size: 35px;
text-align: center;
margin-top: 120px;
}
.change-log-mini{
background: rgba(0, 0, 0, 0.322);
height: 150px;
width: 640px;
margin: auto;
margin-top: 20px;
color: white;
margin-bottom: 50px;
flex-shrink: 0;
}
.change-title {
font-size: 18px;
margin-top: 10px;
margin-bottom: 10px;
margin-left: 10px;
user-select: none;
}
.change-list{
display: flex;
}
.change {
background: rgba(0, 0, 0, 0.335);
width: 200px;
height: 90px;
margin-left: 10px;
border-radius: 5px;
transition: 0.3s;
position: relative
}
.change:hover {
background: rgba(0, 0, 0, 0.466);
}
.notable-changes{
margin: 10px;
cursor: default;
user-select: none;
}
.change-date{
position: absolute;
bottom: 10px;
right: 10px;
color: rgba(255, 255, 255, 0.753);
cursor: default;
user-select: none;
}
.small-view-nav-bar{
width: 100%;
height: 50px;
background: rgba(0, 0, 0, 0.411);
display: none;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.small-logo {
height: 30px;
width: 30px;
background: url(./../assets/logo.png);
background-size: 105%;
background-position: center;
border-radius: 50%;
box-shadow: 0px 0px 96px -4px rgba(69,212,255,1);
margin: auto;
margin-left: 10px;
flex-shrink: 0;
}
.small-title{
color: white;
font-size: 20px;
flex: 1;
margin-left: 10px;
}
.show-menu-button{
color: rgba(255, 255, 255, 0.698);
margin-right: 20px;
margin-top: 7px;
user-select: none;
transition: 0.3s
}
.show-menu-button:hover {
color: rgb(255, 255, 255);
}
.show-menu-content {
display: flex !important;
width: 400px !important;
opacity: 1 !important;
transform: scale(1) !important;
}
@media (max-width: 1051px) {
.change:nth-child(3){
display: none;
}
.change-log-mini{
width: 430px;
}
}
@media (max-width: 906px) {
.change:nth-child(3){
display: block;
}
.change-log-mini{
width: 640px;
}
}
@media (max-width: 649px) {
.twitter-outer{
margin-top: 20px;
margin-bottom: 50px;
width: initial;
}
.change-list{
flex-direction: column;
}
.change-log-mini{
height: initial;
width: calc(100% - 20px);
padding-bottom: 10px;
margin: auto;
}
.change{
margin-bottom: 5px;
margin-left: 5px;
margin-right: 0;
width: calc(100% - 10px);
}
}
@media (max-width: 1380px) {
.graphic-app{
width: calc(100% - 80px);
}
.title{
font-size: 30px;
margin-left: 20px;
margin-right: 20px;
}
}
@media (max-width: 906px) {
.right-panel-home {
position: absolute;
bottom: 0;
right: 0;
top: 50px;
display: flex;
margin-right: 0;
margin-top: 0;
height:calc(100% - 50px);
background-color: rgb(34, 34, 34);
width: 0;
overflow-x: hidden;
transition: 0.5s ease;
transform: scale(0.97);
opacity: 0;
}
.right-panel-inner{
width: 400px;
}
.small-view-nav-bar{
display: flex;
}
}
@media (max-width: 401px) {
.show-menu-content {
width: 100% !important;
}
.right-panel-inner{
width: 100%;
}
}
</style>
<!-- Used for forms !-->
<style>
@media (max-width: 1380px) {
.graphic-app{
width: calc(100% - 80px);
}
}
@media (max-width: 906px) {
.right-panel-home {
position: absolute;
bottom: 0;
right: 0;
top: 50px;
display: flex;
margin-right: 0;
margin-top: 0;
height:calc(100% - 50px);
background-color: rgba(34, 34, 34, 0.877);
width: 0;
overflow-x: hidden;
transition: 0.5s ease;
transform: scale(0.97);
opacity: 0;
}
.right-panel-inner{
width: 400px;
}
.small-view-nav-bar{
display: flex;
}
}
@media (max-width: 401px) {
.show-menu-content {
width: 100% !important;
}
.right-panel-inner{
width: 100%;
}
}
.form {
color: white;
margin: auto;
padding: 10px;
}
input{
padding: 10px;
background: rgba(0, 0, 0, 0.301);
outline: none;
border: none;
color: white;
margin-top: 5px;
margin-bottom: 10px;
width: 200px;
transition: 0.3s;
}
input:hover{
background: rgba(0, 0, 0, 0.452);
}
input:focus {
background: rgba(0, 0, 0, 0.603);
}
.input-title{
margin-top: 10px;
}
.form-button{
padding: 10px;
background: rgba(0, 0, 0, 0.226);
display: table;
transition: 0.5s;
margin: auto;
color: white;
border: none;
outline: none;
}
.form-button:hover{
background: rgba(0, 0, 0, 0.534)
}
.alert{
color: red;
font-size: 15px;
width: 220px;
}
</style>

View file

@ -1,457 +1,409 @@
<template>
<div id="app">
<vue-headful title="Nertivia" description="Nertivia Chat Client"/>
<div class="background-image"></div>
<div class="layout">
<div class="small-view-nav-bar">
<div class="small-logo"></div>
<div class="small-title">Nertivia</div>
<div class="show-menu-button" @click="showMobileMenu = !showMobileMenu">
<i class="material-icons">
menu
</i>
</div>
</div>
<div class="panels">
<div class="left-panel">
<div class="title">The best chat client that wont restrict you from important and fun features.</div>
<img src="../assets/graphics/HomeGraphics.png" class="graphic-app" />
<div class="change-log-mini" @click="showChangeLog = true">
<div class="change-title">Change log <span style="font-size: 15px; color: rgba(211, 211, 211, 0.774);">Click for details</span></div>
<div class="change-list">
<div class="change" v-for="change in changelogFiltered" :key="change.title">
<div class="notable-changes">{{change.shortTitle}}</div>
<div class="change-date">{{change.date}}</div>
<div id="app">
<vue-headful title="Nertivia" description="Nertivia Chat Client"/>
<div class="background-image" ref="backgroundImage"></div>
<spinner v-if="!showPage"/>
<div class="content" v-if="showPage">
<transition name="fall-down" appear>
<div class="header">
<div class="logo"></div>
<div class="name">Nertivia</div>
<div class="links">
<div class="link" @click="signupPage" v-if="!loggedIn">Sign up</div>
<div class="link" @click="loginPage" v-if="!loggedIn">Login</div>
<spinner class="spinner-profile" :size="50" v-if="loggedIn && !user" />
<div class="outer-profile-picture" v-if="loggedIn && user">
<profile-picture @click.native="showPopout = !showPopout" :hover='true' :url="avatarDomain + user.avatar" :admin="user.admin" size="40px" emoteSize="17px"/>
</div>
<transition name="fall-down-fast">
<Popout v-if="user && loggedIn && showPopout" @logout="logOut" :user="user"/>
</transition>
</div>
</div>
</transition>
<transition name="side-in" appear>
<div class="inner-content">
<div
class="title"
>The best chat client that won't restrict you from important and fun features.</div>
<img class="graphic" src="@/assets/graphics/HomeGraphics2.png">
<div class="buttons">
<div class="button" @click="openApp">Open In Browser</div>
<!-- <div class="button" >Download App</div> -->
</div>
<div class="features-list">
<div class="title">Things you can do in Nertivia</div>
<div class="list">
<div class="feature">
<i class="material-icons">insert_drive_file</i>
<div class="description">1GB per file limit, upload huge files!</div>
</div>
<div class="feature">
<i class="material-icons">face</i>
<div class="description">Free custom gif emojis and profile picture.</div>
</div>
<div class="feature">
<i class="material-icons">storage</i>
<div class="description">Make your own servers with channels.</div>
</div>
</div>
</div>
<div class="twitter-outer">
<twitter class="twitter">
<div slot="loading">loading .....</div>
<a class="twitter-timeline" data-height="500" data-theme="dark" href="https://twitter.com/NertiviaApp?ref_src=twsrc%5Etfw">Tweets by NertiviaApp</a>
</twitter>
</div>
</div>
<RightPanel :class="{'show-menu-content': showMobileMenu }" />
</transition>
</div>
</div>
<transition name="fade">
<ChangeLog v-if="showChangeLog"/>
</transition>
</div>
</template>
<script>
import { twitter } from 'vue-twitter'
import {bus} from '../main';
import RightPanel from "./../components/homePage/RightPanel.vue"
import ChangeLog from "./../components/ChangeLog.vue"
import changelog from '@/utils/changelog.js'
import Spinner from "@/components/Spinner.vue";
import ProfilePicture from "@/components/ProfilePictureTemplate.vue";
import Popout from "@/components/homePage/Popout.vue";
import AuthenticationService from "@/services/AuthenticationService.js";
import config from '@/config.js'
export default {
components: {
RightPanel,
ChangeLog,
twitter
},
components: { Spinner, ProfilePicture, Popout },
data() {
return {
loginSelected: true,
showMobileMenu: false,
showChangeLog: false,
changelog
showPopout: false,
showPage: false,
loggedIn: localStorage.getItem("hauthid") || null,
user: null,
avatarDomain: config.domain + '/avatars/'
};
},
methods: {
logOut() {
localStorage.removeItem("hauthid");
this.loggedIn = null;
},
loginPage() {
window.location.href = "/login";
},
signupPage() {
window.location.href = "/register";
},
openApp() {
window.location.href = "/app";
},
async imagePreloader(srcArray) {
srcArray.forEach(async element => {
await wrapper(element);
});
function wrapper(src) {
return new Promise(resolve => {
const image = new Image();
image.src = src;
image.onload = () => resolve("done");
});
}
},
async preloadImages() {
await this.imagePreloader([
require("@/assets/logo.png"),
require("@/assets/graphics/HomeGraphics2.png")
]);
const src = require("@/assets/background.jpg");
const background = new Image();
setTimeout(() => {
background.src = src;
background.onload = () => {
this.$refs.backgroundImage.style.backgroundImage = `url(${
background.src
})`;
this.$refs.backgroundImage.style.opacity = 0.7;
setTimeout(() => (this.showPage = true), 500);
};
}, 500);
},
async getUser() {
const { ok, error, result } = await AuthenticationService.user();
if (!ok) {
// connection error
if (error.response === undefined) {
setTimeout(() => {
this.getUser();
}, 5000);
return;
} else {
localStorage.removeItem('hauthid');
this.loggedIn = null
}
} else {
this.user = result.data.user;
}
}
},
mounted() {
bus.$on('closeChangeLog', () => {
this.showChangeLog = false;
})
},
computed: {
changelogFiltered() {
return this.changelog.slice(0, 3)
}
}
async mounted() {
if (this.loggedIn) this.getUser();
this.preloadImages();
}
}
};
</script>
<style scoped>
.twitter-outer{
margin: auto;
width: 600px;
opacity: 0.8;
transition: 0.3s;
}
.twitter-outer:hover{
opacity: 1;
}
.fade-enter-active, .fade-leave-active {
transition: opacity .2s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
.fall-down-enter-active {
opacity: 0;
animation: fall-down 0.5s;
animation-delay: 0.3s;
}
#app {
font-family: 'Roboto', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #383838;
.fall-down-fast-enter-active {
opacity: 0;
animation: bounce-in 0.2s;
}
.fall-down-fast-leave-active {
opacity: 0;
animation: bounce-in 0.2s reverse;
}
@keyframes fall-down {
0% {
transform: translateY(-20px);
opacity: 0;
}
50% {
transform: translateY(10px);
opacity: 1;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
@keyframes bounce-in {
0% {
transform: translateY(-20px);
opacity: 0;
}
50% {
transform: translateY(10px);
opacity: 1;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
.side-in-enter-active {
opacity: 0;
animation: side-in 0.5s;
animation-delay: 0.5s;
}
@keyframes side-in {
0% {
transform: translateX(-20px);
opacity: 0;
}
50% {
transform: translateX(10px);
opacity: 1;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
html,
body {
height: 100%;
}
button {
font-family: 'Roboto', sans-serif;
#app {
display: flex;
flex-direction: column;
height: 100%;
}
.spinner{
margin: auto;
padding: 30px;
.header {
background: rgba(0, 0, 0, 0.52);
display: flex;
height: 70px;
margin: 5px;
border-radius: 10px;
flex-shrink: 0;
border: 10px;
position: relative;
}
.logo {
background-image: url("../assets/logo.png");
background-size: 100%;
height: 50px;
width: 50px;
margin-top: auto;
margin-bottom: auto;
margin-left: 10px;
}
.name {
margin-top: auto;
margin-bottom: auto;
font-size: 20px;
margin-left: 10px;
color: white;
}
.outer-profile-picture{
user-select: none;
z-index: 9999999;
}
.background-image {
background: url(./../assets/background.jpg);
position: absolute;
position: fixed;
z-index: -1;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-position: bottom;
background-size: cover;
opacity: 0;
transition: 0.5s;
}
.layout{
display: flex;
height: 100%;
width:100%;
flex-direction: column;
}
.panels{
display: flex;
height: 100%;
width: 100%;
}
.left-panel {
flex: 1;
background: rgba(0, 0, 0, 0.253);
overflow: auto;
display: flex;
flex-direction: column;
}
.loader{
.content {
height: 100%;
display: flex;
flex-direction: column;
}
.title-panel{
width: 100%;
height: 150px;
overflow: auto;
overflow-x: hidden;
}
.graphics-panel{
flex: 1;
.inner-content {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
flex: 1;
margin-top: 40px;
}
.graphic-app{
display: table;
margin: auto;
margin-top: 20px;
margin-bottom: 20px;
width: 900px;
height: auto;
user-select: none;
}
.title{
color: white;
font-size: 35px;
text-align: center;
margin-top: 120px;
}
.change-log-mini{
background: rgba(0, 0, 0, 0.322);
height: 150px;
width: 640px;
.graphic {
width: 700px;
height: auto;
margin: auto;
margin-top: 20px;
color: white;
margin-bottom: 50px;
flex-shrink: 0;
}
.change-title {
font-size: 18px;
margin-top: 10px;
margin-bottom: 10px;
margin-left: 10px;
margin-bottom: 0;
user-select: none;
}
.change-list{
.title {
font-size: 25px;
color: white;
text-align: center;
padding: 10px;
}
.buttons {
display: flex;
justify-content: center;
margin-top: 30px;
}
.button {
padding: 15px;
background: rgba(24, 132, 255, 0.733);
border-radius: 5px;
color: white;
box-shadow: 3px 3px rgb(5, 86, 179);
user-select: none;
transition: 0.3s;
}
.button:hover {
background: rgb(24, 132, 255);
}
.button:active {
transform: translate(3px, 3px);
box-shadow: 0 0 rgb(5, 86, 179);
}
.features-list {
margin-top: 20px;
}
.features-list .list {
display: flex;
justify-content: center;
}
.feature {
background: rgba(0, 0, 0, 0.541);
color: white;
margin: 10px;
padding: 2px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
justify-content: center;
height: 200px;
width: 200px;
border-radius: 10px;
flex-shrink: 0;
transition: 0.3s;
}
.feature .description {
width: 150px;
margin-top: 15px;
}
.feature .material-icons {
font-size: 60px;
}
.links {
color: white;
margin: auto;
margin-right: 20px;
display: flex;
}
.change {
background: rgba(0, 0, 0, 0.335);
width: 200px;
height: 90px;
margin-left: 10px;
.link {
padding: 5px;
background: rgba(0, 0, 0, 0.219);
border-radius: 5px;
user-select: none;
margin-left: 5px;
transition: 0.3s;
position: relative
}
.change:hover {
background: rgba(0, 0, 0, 0.466);
.link:hover {
background: rgba(255, 255, 255, 0.26);
}
.notable-changes{
margin: 10px;
cursor: default;
user-select: none;
.warn {
color: red;
}
.change-date{
position: absolute;
bottom: 10px;
right: 10px;
color: rgba(255, 255, 255, 0.753);
cursor: default;
user-select: none;
}
.small-view-nav-bar{
width: 100%;
height: 50px;
background: rgba(0, 0, 0, 0.411);
display: none;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.small-logo {
height: 30px;
width: 30px;
background: url(./../assets/logo.png);
background-size: 105%;
background-position: center;
border-radius: 50%;
box-shadow: 0px 0px 96px -4px rgba(69,212,255,1);
margin: auto;
margin-left: 10px;
flex-shrink: 0;
}
.small-title{
color: white;
font-size: 20px;
flex: 1;
margin-left: 10px;
}
.show-menu-button{
color: rgba(255, 255, 255, 0.698);
margin-right: 20px;
margin-top: 7px;
user-select: none;
transition: 0.3s
}
.show-menu-button:hover {
color: rgb(255, 255, 255);
}
.show-menu-content {
display: flex !important;
width: 400px !important;
opacity: 1 !important;
transform: scale(1) !important;
}
@media (max-width: 1051px) {
.change:nth-child(3){
display: none;
}
.change-log-mini{
width: 430px;
@media (max-width: 1000px) {
.graphic {
width: calc(100% - 30px);
}
}
@media (max-width: 906px) {
.change:nth-child(3){
display: block;
}
.change-log-mini{
width: 640px;
@media (max-width: 662px) {
.feature {
margin: 5px;
padding: 0px;
height: 150px;
width: 150px;
}
}
@media (max-width: 649px) {
.twitter-outer{
margin-top: 20px;
margin-bottom: 50px;
width: initial;
}
.change-list{
@media (max-width: 465px) {
.features-list .list {
flex-direction: column;
}
.change-log-mini{
height: initial;
width: calc(100% - 20px);
padding-bottom: 10px;
margin: auto;
.feature {
margin: 5px;
padding: 0px;
height: 150px;
width: auto;
font-size: 15px;
}
.change{
margin-bottom: 5px;
margin-left: 5px;
margin-right: 0;
width: calc(100% - 10px);
}
}
@media (max-width: 1380px) {
.graphic-app{
width: calc(100% - 80px);
}
.title{
font-size: 30px;
margin-left: 20px;
margin-right: 20px;
}
}
@media (max-width: 906px) {
.right-panel-home {
position: absolute;
bottom: 0;
right: 0;
top: 50px;
display: flex;
margin-right: 0;
margin-top: 0;
height:calc(100% - 50px);
background-color: rgb(34, 34, 34);
width: 0;
overflow-x: hidden;
transition: 0.5s ease;
transform: scale(0.97);
opacity: 0;
}
.right-panel-inner{
width: 400px;
}
.small-view-nav-bar{
display: flex;
}
}
@media (max-width: 401px) {
.show-menu-content {
width: 100% !important;
}
.right-panel-inner{
.feature .description {
margin-top: 15px;
width: 100%;
}
}
</style>
<!-- Used for forms !-->
<style>
@media (max-width: 1380px) {
.graphic-app{
width: calc(100% - 80px);
}
}
@media (max-width: 906px) {
.right-panel-home {
position: absolute;
bottom: 0;
right: 0;
top: 50px;
display: flex;
margin-right: 0;
margin-top: 0;
height:calc(100% - 50px);
background-color: rgba(34, 34, 34, 0.877);
width: 0;
overflow-x: hidden;
transition: 0.5s ease;
transform: scale(0.97);
opacity: 0;
}
.right-panel-inner{
width: 400px;
}
.small-view-nav-bar{
display: flex;
}
}
@media (max-width: 401px) {
.show-menu-content {
width: 100% !important;
}
.right-panel-inner{
width: 100%;
}
}
.form {
color: white;
margin: auto;
padding: 10px;
}
input{
padding: 10px;
background: rgba(0, 0, 0, 0.301);
outline: none;
border: none;
color: white;
margin-top: 5px;
margin-bottom: 10px;
width: 200px;
transition: 0.3s;
}
input:hover{
background: rgba(0, 0, 0, 0.452);
}
input:focus {
background: rgba(0, 0, 0, 0.603);
}
.input-title{
margin-top: 10px;
}
.form-button{
padding: 10px;
background: rgba(0, 0, 0, 0.226);
display: table;
transition: 0.5s;
margin: auto;
color: white;
border: none;
outline: none;
}
.form-button:hover{
background: rgba(0, 0, 0, 0.534)
}
.alert{
color: red;
font-size: 15px;
width: 220px;
body {
background: rgb(46, 37, 49);
}
</style>

372
src/views/LoginPage.vue Normal file
View file

@ -0,0 +1,372 @@
<template>
<div id="app">
<div class="app-content">
<header-login v-on:isDay="isDayEvent"/>
<div class="content">
<transition appear name="fade-up">
<div class="box" v-if="visible">
<div class="title">
<i class="material-icons">account_circle</i>
Login
</div>
<div class="info">Login to access Nertivia</div>
<form action="#" @submit.prevent="submitForm" v-if="!showCaptcha">
<div class="input">
<div class="input-text">
Email
<span class="error" v-if="email.alert">- {{email.alert}}</span>
</div>
<input type="email" v-model="email.value" placeholder="Email">
</div>
<div class="input">
<div class="input-text">
Password
<span class="error" v-if="password.alert">- {{password.alert}}</span>
</div>
<input
type="password"
v-model="password.value"
autocomplete="password"
placeholder="Password"
>
</div>
<span class="error" style="text-align: center;" v-if="otherError">{{otherError}}</span>
<div class="buttons">
<button type="submit" :class="{button: true, deactive: deactive}">Login</button>
<button class="button register-button" @click.prevent="registerButton">I'm new!</button>
</div>
</form>
<div class="captcha-box" v-if="showCaptcha">
<div class="input captcha-input">
<div class="input-text">
Beep Boop
<span class="error" v-if="reCaptcha.alert">- {{reCaptcha.alert}}</span>
</div>
<div class="captcha">
<Recaptcha ref="recaptcha" @verify="captchaSubmit"/>
</div>
</div>
</div>
</div>
</transition>
</div>
</div>
<div class="background">
<div :class="{background: true, 'night-background': true, chosen: !isDay}"><particlesJS class="particles"/></div>
<div class="background day-background"></div>
</div>
</div>
</template>
<script>
import Recaptcha from "@/components/Recaptcha.vue";
import HeaderLogin from "@/components/HeaderLoginTemplate.vue";
import AuthenticationService from "@/services/AuthenticationService";
import particlesJS from "@/components/ParticlesJS.vue";
export default {
components: { HeaderLogin, Recaptcha, particlesJS },
data() {
return {
showCaptcha: false,
visible: true,
email: { value: "", alert: "" },
password: { value: "", alert: "" },
reCaptcha: { alert: "" },
otherError: "",
captchaToken: "",
isDay: true,
deactive: false
};
},
methods: {
resetValues() {
// Resets all of the alert values
this.email.alert = "";
this.password.alert = "";
this.reCaptcha.alert = "";
this.otherError = "";
},
isDayEvent(data) {
this.isDay = data;
},
captchaSubmit(token) {
this.captchaToken = token;
this.login();
},
submitForm() {
this.showCaptcha = true;
},
async login() {
if (this.deactive === true) return;
this.resetValues();
const email = this.email.value.trim();
const password = this.password.value.trim();
const captcha = this.captchaToken;
this.deactive = true;
const { ok, error, result } = await AuthenticationService.login({
email,
password,
token: captcha
});
if (ok) {
this.visible = false;
this.$store.dispatch("token", result.data.token);
setTimeout(_ => {
window.location.href = "/app";
}, 1000);
} else {
this.showCaptcha = false
this.deactive = false;
this.captchaToken = null;
this.$refs.recaptcha.resetRecaptcha();
if (error.response === undefined) {
this.otherError = "Can't connect to server.";
return;
}
const errors = error.response.data.errors;
for (let index in errors) {
const message = errors[index].msg;
const param = errors[index].param;
if (this[param] === undefined) {
this.otherError = message;
} else {
this[param].alert = message;
}
}
}
},
registerButton() {
this.$router.push("/register");
}
}
};
</script>
<style scoped>
.fade-up-enter-active {
opacity: 0;
animation: bounce-in 0.5s;
animation-delay: 0.7s;
}
.fade-up-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: translateX(30px);
opacity: 0;
}
50% {
transform: translateX(-10px);
}
100% {
transform: translateX(0);
opacity: 1;
}
}
/* .fade-up-enter-active, .fade-up-leave-active {
transition: .5s;
transition-delay: 0.5s
}
.fade-up-enter, .fade-up-leave-to /* .fade-leave-active {
opacity: 0;
transform: translateX(20px)
} */
#app {
display: flex;
flex-direction: column;
transition: background 10s;
color: white;
}
.app-content {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
overflow: auto;
z-index: 9999;
padding-bottom: 100px;
}
.background {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
transition: background 10s;
}
.night-background {
opacity: 0;
transition: 10s;
background: linear-gradient(to bottom, #000000 0%,#606060 100%) !important;
}
.day-background {
opacity: 1;
background: linear-gradient(to bottom, #165dc0 0%,#5482bf 100%);
z-index: -1
}
.night-background.chosen {
opacity: 1 !important;
}
.night-background .particles {
opacity: 1;
}
.content {
display: flex;
height: 100%;
margin: 10px;
}
.box {
width: 100%;
max-width: 400px;
background: rgba(44, 44, 44, 0.774);
margin: auto;
margin-top: 20px;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
z-index: 9999;
padding-bottom: 20px;
}
.box .title {
text-align: center;
font-size: 25px;
margin-top: 10px;
margin-bottom: 5px;
color: white;
user-select: none;
display: flex;
align-items: center;
height: 60px;
}
.box .title .material-icons {
margin-right: 5px;
font-size: 40px;
}
.info {
text-align: center;
margin-bottom: 10px;
}
form {
display: flex;
flex-direction: column;
width: 100%;
}
.input {
display: flex;
flex-direction: column;
margin: 10px;
width: 80%;
align-self: center;
background: rgb(59, 59, 59);
padding: 10px;
border-radius: 10px;
}
.input-text {
margin-bottom: 5px;
margin-left: 3px;
}
input {
outline: none;
padding: 10px;
border: solid 1px rgba(0, 0, 0, 0);
background: none;
border-radius: 5px;
background: rgb(36, 36, 36);
color: white;
}
.buttons {
display: flex;
align-items: center;
align-content: center;
justify-content: center;
justify-items: center;
}
.button {
padding: 10px;
background: rgba(25, 151, 255, 0.699);
border-radius: 5px;
margin: 10px;
margin-top: 10px;
user-select: none;
border: none;
color: white;
font-size: 17px;
outline: none;
transition: 0.2s;
box-shadow: 3px 3px rgba(23, 112, 255, 0.479);
}
.button:hover {
background: rgb(25, 151, 255);
}
.button:focus {
background: rgb(25, 151, 255);
}
.button:active {
background: rgb(25, 151, 255);
transform: translate(3px, 3px);
box-shadow: 0px 0px rgba(0, 97, 207, 0.479);
}
.button.deactive {
background: rgba(70, 70, 70, 0.699);
box-shadow: 3px 3px rgba(0, 0, 0, 0.479);
}
.button.deactive :hover {
background: rgba(70, 70, 70, 0.699);
}
.button.deactive :focus {
background: rgba(70, 70, 70, 0.699);
}
.button.deactive:active {
background: rgba(70, 70, 70, 0.699);
transform: translate(3px, 3px);
box-shadow: 0px 0px rgba(0, 0, 0, 0.479);
}
.register-button {
background: #2ecc70ab;
box-shadow: 3px 3px #0f7e3d;
}
.register-button.button:hover {
background: #2ecc71;
}
.register-button.button:focus {
background: #2ecc71;
}
.register-button.button:active {
background: #2ecc71;
transform: translate(3px, 3px);
box-shadow: 0px 0px #0f7e3d;
}
.captcha-input {
width: initial;
}
.captcha {
opacity: 0.7;
transition: 0.3s;
}
.captcha:hover {
opacity: 1;
}
.error {
color: red;
}
</style>

383
src/views/RegisterPage.vue Normal file
View file

@ -0,0 +1,383 @@
<template>
<div id="app">
<div class="app-content">
<header-login v-on:isDay="isDayEvent"/>
<div class="content">
<transition appear name="fade-up">
<div class="box" v-if="visible">
<div class="title">
<i class="material-icons">account_circle</i>
Register
</div>
<div class="info">Welcome, new user! Hope you enjoy Nertivia!</div>
<form action="#" @submit.prevent="formSubmit" v-if="!showCaptcha">
<div class="input">
<div class="input-text">
Email
<span class="error" v-if="email.alert">- {{email.alert}}</span>
</div>
<input type="email" v-model="email.value" placeholder="Email" autocomplete="off">
</div>
<div class="input">
<div class="input-text">
Username
<span class="error" v-if="username.alert">- {{username.alert}}</span>
</div>
<input
type="text"
v-model="username.value"
placeholder="Username"
autocomplete="off"
>
</div>
<div class="input">
<div class="input-text">
Password
<span class="error" v-if="password.alert">- {{password.alert}}</span>
</div>
<input
type="password"
v-model="password.value"
autocomplete="off"
placeholder="Password"
>
</div>
<span class="error" style="text-align: center;" v-if="otherError">{{otherError}}</span>
<div class="buttons">
<button type="submit" :class="{button: true, deactive: deactive}">Register</button>
<button
class="button register-button"
@click.prevent="loginButton"
>I'm already a user!</button>
</div>
</form>
<div class="captcha-box" v-if="showCaptcha">
<div class="input captcha-input">
<div class="input-text">Beep Boop</div>
<div class="captcha">
<Recaptcha ref="recaptcha" @verify="captchaSubmit"/>
</div>
</div>
</div>
</div>
</transition>
</div>
</div>
<div class="background">
<div :class="{background: true, 'night-background': true, chosen: !isDay}"><particlesJS class="particles"/></div>
<div class="background day-background"></div>
</div>
</div>
</template>
<script>
import Recaptcha from "@/components/Recaptcha.vue";
import HeaderLogin from "@/components/HeaderLoginTemplate.vue";
import AuthenticationService from "@/services/AuthenticationService";
import particlesJS from "@/components/ParticlesJS.vue";
export default {
components: { HeaderLogin, Recaptcha, particlesJS },
data() {
return {
showCaptcha: false,
visible: true,
email: { value: "", alert: "" },
username: { value: "", alert: "" },
password: { value: "", alert: "" },
otherError: "",
captchaToken: "",
isDay: true,
deactive: false
};
},
methods: {
resetValues() {
// Resets all of the alert values
this.email.alert = "";
this.username.alert = "";
this.password.alert = "";
this.otherError = "";
},
isDayEvent(data) {
this.isDay = data;
},
captchaSubmit(token) {
this.captchaToken = token;
this.register();
},
formSubmit() {
this.showCaptcha = true;
},
async register() {
this.resetValues();
const email = this.email.value.trim();
const username = this.username.value.trim();
const password = this.password.value.trim();
const { ok, error, result } = await AuthenticationService.register({
email,
username,
password,
token: this.captchaToken
});
if (ok) {
this.visible = false;
this.$store.dispatch("token", result.data.token);
setTimeout(_ => {
window.location.href = "/app";
}, 1000);
} else {
this.showCaptcha = false
this.deactive = false;
this.captchaToken = null;
this.$refs.recaptcha.resetRecaptcha();
if (error.response === undefined) {
this.otherError = "Can't connect to server.";
return;
}
const errors = error.response.data.errors;
for (let index in errors) {
const message = errors[index].msg;
const param = errors[index].param;
if (this[param] === undefined) {
this.otherError = message;
} else {
this[param].alert = message;
}
}
}
},
loginButton() {
this.$router.push("/login");
}
}
};
</script>
<style scoped>
.fade-up-enter-active {
opacity: 0;
animation: bounce-in 0.5s;
animation-delay: 0.7s;
}
.fade-up-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: translateX(30px);
opacity: 0;
}
50% {
transform: translateX(-10px);
}
100% {
transform: translateX(0);
opacity: 1;
}
}
/* .fade-up-enter-active, .fade-up-leave-active {
transition: .5s;
transition-delay: 0.5s
}
.fade-up-enter, .fade-up-leave-to /* .fade-leave-active {
opacity: 0;
transform: translateX(20px)
} */
#app {
display: flex;
flex-direction: column;
transition: background 10s;
color: white;
}
.app-content {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
overflow: auto;
z-index: 9999;
padding-bottom: 50px;
}
.background {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
transition: background 10s;
}
.night-background {
opacity: 0;
transition: 10s;
background: linear-gradient(to bottom, #000000 0%,#606060 100%) !important;
}
.day-background {
opacity: 1;
background: linear-gradient(to bottom, #165dc0 0%,#5482bf 100%);
z-index: -1
}
.night-background.chosen {
opacity: 1 !important;
}
.night-background .particles {
opacity: 1;
}
.content {
display: flex;
height: 100%;
margin: 10px;
}
.box {
width: 100%;
max-width: 400px;
background: rgba(44, 44, 44, 0.774);
margin: auto;
margin-top: 20px;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
z-index: 9999;
padding-bottom: 20px;
}
.box .title {
text-align: center;
font-size: 25px;
margin-top: 10px;
margin-bottom: 5px;
color: white;
user-select: none;
display: flex;
align-items: center;
height: 60px;
}
.box .title .material-icons {
margin-right: 5px;
font-size: 40px;
}
.info {
text-align: center;
margin-bottom: 10px;
}
form {
display: flex;
flex-direction: column;
width: 100%;
}
.input {
display: flex;
flex-direction: column;
margin: 10px;
width: 80%;
align-self: center;
background: rgb(59, 59, 59);
padding: 10px;
border-radius: 10px;
}
.input-text {
margin-bottom: 5px;
margin-left: 3px;
}
input {
outline: none;
padding: 10px;
border: solid 1px rgba(0, 0, 0, 0);
background: none;
border-radius: 5px;
background: rgb(36, 36, 36);
color: white;
}
.buttons {
display: flex;
align-items: center;
align-content: center;
justify-content: center;
justify-items: center;
}
.button {
padding: 10px;
background: rgba(25, 151, 255, 0.699);
border-radius: 5px;
margin: 10px;
margin-top: 10px;
user-select: none;
border: none;
color: white;
font-size: 17px;
outline: none;
transition: 0.2s;
box-shadow: 3px 3px rgba(23, 112, 255, 0.479);
}
.button:hover {
background: rgb(25, 151, 255);
}
.button:focus {
background: rgb(25, 151, 255);
}
.button:active {
background: rgb(25, 151, 255);
transform: translate(3px, 3px);
box-shadow: 0px 0px rgba(0, 97, 207, 0.479);
}
.button.deactive {
background: rgba(70, 70, 70, 0.699);
box-shadow: 3px 3px rgba(0, 0, 0, 0.479);
}
.button.deactive :hover {
background: rgba(70, 70, 70, 0.699);
}
.button.deactive :focus {
background: rgba(70, 70, 70, 0.699);
}
.button.deactive:active {
background: rgba(70, 70, 70, 0.699);
transform: translate(3px, 3px);
box-shadow: 0px 0px rgba(0, 0, 0, 0.479);
}
.register-button {
background: #2ecc70ab;
box-shadow: 3px 3px #0f7e3d;
}
.register-button.button:hover {
background: #2ecc71;
}
.register-button.button:focus {
background: #2ecc71;
}
.register-button.button:active {
background: #2ecc71;
transform: translate(3px, 3px);
box-shadow: 0px 0px #0f7e3d;
}
.captcha-input {
width: initial;
}
.captcha {
opacity: 0.7;
transition: 0.3s;
}
.captcha:hover {
opacity: 1;
}
.error {
color: red;
}
</style>