Commit 61ec76cf authored by Andreas Heimann's avatar Andreas Heimann

added stage 1 for login

parent f5903bde
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"description": "SpinSha.re Client", "description": "SpinSha.re Client",
"homepage": "https://spinsha.re", "homepage": "https://spinsha.re",
"copyright": "Copyright © SpinSha.re", "copyright": "Copyright © SpinSha.re",
"version": "2.4.0", "version": "2.5.0",
"author": "SpinSha.re", "author": "SpinSha.re",
"private": true, "private": true,
"scripts": { "scripts": {
......
<template> <template>
<div id="app" :class="platform != 'darwin' ? 'app' : 'app-darwin'" tabindex="-1" v-on:keydown.esc="closeOverlays()"> <div id="app" tabindex="-1" v-on:keydown.esc="closeOverlays()">
<!-- <WindowTitleBar /> --> <main :class="(['Login'].indexOf($route.name) > -1) ? 'hide-navigation' : ''">
<main>
<Navigation v-bind:downloadQueueCount="downloadQueue.length" v-bind:downloadOverlayShown="showDownloadOverlay" /> <Navigation v-bind:downloadQueueCount="downloadQueue.length" v-bind:downloadOverlayShown="showDownloadOverlay" />
<router-view /> <router-view />
</main> </main>
...@@ -31,7 +29,6 @@ ...@@ -31,7 +29,6 @@
import SSAPI from '@/modules/module.api.js'; import SSAPI from '@/modules/module.api.js';
import SRXD from '@/modules/module.srxd.js'; import SRXD from '@/modules/module.srxd.js';
import WindowTitleBar from '@/components/WindowTitleBar.vue';
import Navigation from '@/components/Navigation/Navigation.vue'; import Navigation from '@/components/Navigation/Navigation.vue';
import ContextMenu from '@/components/ContextMenu/ContextMenu.vue'; import ContextMenu from '@/components/ContextMenu/ContextMenu.vue';
import UpdateOverlay from '@/components/Overlays/UpdateOverlay.vue'; import UpdateOverlay from '@/components/Overlays/UpdateOverlay.vue';
...@@ -40,7 +37,6 @@ ...@@ -40,7 +37,6 @@
export default { export default {
name: 'App', name: 'App',
components: { components: {
WindowTitleBar,
Navigation, Navigation,
ContextMenu, ContextMenu,
UpdateOverlay, UpdateOverlay,
...@@ -260,9 +256,15 @@ ...@@ -260,9 +256,15 @@
left: 0px; left: 0px;
right: 0px; right: 0px;
overflow-y: scroll; overflow-y: scroll;
&.hide-navigation {
top: 0px;
overflow: hidden;
& > aside {
display: none;
}
} }
.app-darwin main {
top: 40px;
} }
button, .button { button, .button {
font-family: 'Open Sans', sans-serif; font-family: 'Open Sans', sans-serif;
...@@ -286,6 +288,14 @@ ...@@ -286,6 +288,14 @@
&:focus { &:focus {
outline: 0; outline: 0;
} }
&.button-label {
background: transparent;
&:hover {
background: rgba(255,255,255,0.15);
opacity: 1;
}
}
&:disabled, &.button-disabled { &:disabled, &.button-disabled {
opacity: 0.4; opacity: 0.4;
cursor: not-allowed; cursor: not-allowed;
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
<nav class="items-left"> <nav class="items-left">
<div class="item" v-tooltip.down="'Back'" v-on:click="navigateBack()"><i class="mdi mdi-arrow-left"></i></div> <div class="item" v-tooltip.down="'Back'" v-on:click="navigateBack()"><i class="mdi mdi-arrow-left"></i></div>
<div class="logo"> <div class="logo">
<router-link to="/"><img src="https://spinshare.b-cdn.net/assets/img/logo_colored_ondark.svg" alt="SpinShare Logo" /></router-link> <router-link to="/startup"><img src="https://spinsha.re/assets/img/logo_colored_ondark.svg" alt="SpinShare Logo" /></router-link>
</div> </div>
<router-link to="/" exact class="item" v-tooltip.down="'Frontpage'"><i class="mdi mdi-home-outline"></i></router-link> <router-link to="/startup" exact class="item" v-tooltip.down="'Frontpage'"><i class="mdi mdi-home-outline"></i></router-link>
<router-link to="/search" class="item" v-tooltip.down="'Search'"><i class="mdi mdi-magnify"></i></router-link> <router-link to="/search" class="item" v-tooltip.down="'Search'"><i class="mdi mdi-magnify"></i></router-link>
<router-link to="/library" class="item" v-tooltip.down="'Library'"><i class="mdi mdi-music-box-multiple-outline"></i></router-link> <router-link to="/library" class="item" v-tooltip.down="'Library'"><i class="mdi mdi-music-box-multiple-outline"></i></router-link>
<div v-on:click="openExternal('https://spinsha.re/support');" class="item" v-tooltip.down="'Support'"><i class="mdi mdi-hand-heart"></i></div> <div v-on:click="openExternal('https://spinsha.re/support');" class="item" v-tooltip.down="'Support'"><i class="mdi mdi-hand-heart"></i></div>
...@@ -290,83 +290,3 @@ ...@@ -290,83 +290,3 @@
} }
} }
</style> </style>
\ No newline at end of file
<!-- <style scoped lang="less">
aside {
background: #383C3F;
display: grid;
position: fixed;
z-index: 50;
top: 0px;
left: 0px;
right: 0px;
grid-template-rows: 1fr auto;
& .item, & .external-item {
width: 60px;
height: 60px;
display: flex;
position: relative;
justify-content: center;
align-items: center;
background: transparent;
color: #fff;
transition: 0.2s ease opacity, 0.2s ease background;
&:hover {
cursor: pointer;
& .mdi {
opacity: 1;
}
}
&.router-link-active {
opacity: 1;
&:before {
transition: 0.2s ease-in-out opacity;
opacity: 1;
}
& .mdi {
opacity: 1;
}
}
& .mdi {
font-size: 22px;
opacity: 0.4;
position: relative;
z-index: 2;
}
& .badge {
position: absolute;
z-index: 3;
bottom: 5px;
right: 5px;
font-size: 12px;
font-weight: bold;
padding: 2px 6px;
border-radius: 5px;
background: rgba(255,255,255,0.2);
}
&:before {
content: "";
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
background: rgb(226, 44, 120);
opacity: 0;
z-index: 0;
}
}
}
.app-darwin aside {
top: 40px;
}
</style>
-->
\ No newline at end of file
...@@ -99,7 +99,4 @@ ...@@ -99,7 +99,4 @@
} }
} }
} }
.app-darwin .delete-overlay {
top: 40px;
}
</style> </style>
...@@ -147,7 +147,4 @@ ...@@ -147,7 +147,4 @@
} }
} }
} }
.app-darwin .play-overlay {
top: 40px;
}
</style> </style>
...@@ -107,7 +107,4 @@ ...@@ -107,7 +107,4 @@
} }
} }
} }
.app-darwin .update-overlay {
top: 40px;
}
</style> </style>
<template>
<div class="window-title-bar">
<div class="title">
<div class="back" v-on:click="NavigateBack()"><i class="mdi mdi-arrow-left"></i></div>
<div class="text">SpinShare</div>
</div>
<div class="actions" v-if="platform != 'darwin'">
<div class="action" v-on:click="WindowMinimize()"><i class="mdi mdi-window-minimize"></i></div>
<div class="action" v-on:click="WindowMaximize()" v-if="!isMaximized"><i class="mdi mdi-window-maximize"></i></div>
<div class="action" v-on:click="WindowMaximize()" v-if="isMaximized"><i class="mdi mdi-window-restore"></i></div>
<div class="action action-close" v-on:click="WindowClose()"><i class="mdi mdi-window-close"></i></div>
</div>
</div>
</template>
<script>
import { remote } from 'electron';
export default {
name: 'WindowTitleBar',
data: function() {
return {
platform: "win32",
isMaximized: false
}
},
mounted: function() {
this.$data.platform = process.platform;
if(remote.BrowserWindow.getFocusedWindow()) {
this.$data.isMaximized = remote.BrowserWindow.getFocusedWindow().isMaximized();
}
},
methods: {
NavigateBack: function() {
this.$router.back();
},
WindowMinimize: function() {
remote.BrowserWindow.getFocusedWindow().minimize();
},
WindowMaximize: function() {
if(remote.BrowserWindow.getFocusedWindow().isMaximized()) {
remote.BrowserWindow.getFocusedWindow().unmaximize();
} else {
remote.BrowserWindow.getFocusedWindow().maximize();
}
if(remote.BrowserWindow.getFocusedWindow()) {
this.$data.isMaximized = remote.BrowserWindow.getFocusedWindow().isMaximized();
}
},
WindowClose: function() {
remote.BrowserWindow.getFocusedWindow().close();
}
}
}
</script>
<style scoped lang="less">
.window-title-bar {
position: fixed;
display: grid;
grid-template-columns: 1fr auto;
top: 0px;
left: 0px;
right: 0px;
z-index: 1000;
font-size: 12px;
height: 30px;
overflow: hidden;
background: #000;
color: #fff;
-webkit-app-region: drag;
& .title {
display: grid;
grid-template-columns: auto 1fr;
grid-gap: 8px;
align-items: center;
& .back {
display: flex;
justify-content: center;
align-items: center;
height: 30px;
font-size: 16px;
padding: 0px 15px;
-webkit-app-region: no-drag;
&:hover {
cursor: pointer;
background: rgba(255,255,255,0.2);
}
}
}
& .actions {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
& .action {
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
padding: 0px 15px;
-webkit-app-region: no-drag;
&:hover {
background: rgba(255,255,255,0.2);
}
&.action-close:hover {
background: rgb(204, 28, 28);
}
}
}
}
.app-darwin .window-title-bar {
height: 40px;
& .title {
padding-left: 80px;
& .back {
height: 40px;
}
}
}
</style>
...@@ -9,6 +9,8 @@ class SSAPI { ...@@ -9,6 +9,8 @@ class SSAPI {
} }
} }
connectAppApiKey = "c02b7d24a066adb747fdeb12deb21bfa";
constructor() { constructor() {
if(this.isDev()) { if(this.isDev()) {
this.apiBase = "http://localhost/www/spinshare/server/public/index.php/api/"; this.apiBase = "http://localhost/www/spinshare/server/public/index.php/api/";
...@@ -305,6 +307,45 @@ class SSAPI { ...@@ -305,6 +307,45 @@ class SSAPI {
throw new Error(error); throw new Error(error);
}); });
} }
async getConnectToken(connectCode) {
let apiPath = this.apiBase + "connect/getToken?connectCode=" + connectCode + "&connectAppApiKey=" + this.connectAppApiKey;
let supportedVersion = this.supportedVersion;
return axios.get(apiPath)
.then(function(response) {
if(response.data.version !== supportedVersion) {
throw new Error("Client is outdated!");
}
return response.data;
}).catch(function(error) {
throw new Error(error);
});
}
async validateConnectToken(connectToken) {
let apiPath = this.apiBase + "connect/validateToken/?connectToken=" + connectToken;
let supportedVersion = this.supportedVersion;
return axios.get(apiPath)
.then(function(response) {
if(response.data.version !== supportedVersion) {
throw new Error("Client is outdated!");
}
switch(response.data.status) {
case 200:
return true;
case 403:
return false;
case 500:
return false;
}
}).catch(function(error) {
throw new Error(error);
});
}
} }
module.exports = SSAPI; module.exports = SSAPI;
...@@ -2,6 +2,7 @@ import Vue from 'vue'; ...@@ -2,6 +2,7 @@ import Vue from 'vue';
import VueRouter from 'vue-router'; import VueRouter from 'vue-router';
import VueAxios from 'vue-axios'; import VueAxios from 'vue-axios';
import axios from 'axios'; import axios from 'axios';
import ViewLogin from '../views/Login.vue';
import ViewStartup from '../views/Startup.vue'; import ViewStartup from '../views/Startup.vue';
import ViewStartupFrontpage from '../views/StartupFrontpage.vue'; import ViewStartupFrontpage from '../views/StartupFrontpage.vue';
import ViewStartupNewSongs from '../views/StartupNewSongs.vue'; import ViewStartupNewSongs from '../views/StartupNewSongs.vue';
...@@ -25,6 +26,10 @@ Vue.use(VueAxios, axios); ...@@ -25,6 +26,10 @@ Vue.use(VueAxios, axios);
const routes = [{ const routes = [{
path: '/', path: '/',
name: 'Login',
component: ViewLogin
}, {
path: '/startup',
name: 'Startup', name: 'Startup',
component: ViewStartup, component: ViewStartup,
children: [ children: [
......
<template>
<section class="section-login">
<div class="login-box login-loading" v-if="apiLoginLoading">
<Loading />
</div>
<div class="login-box" v-if="!hasValidToken && !apiLoginLoading">
<div class="logo"><img src="https://spinsha.re/assets/img/logo_colored_ondark.svg" alt="SpinShare Logo" /></div>
<div class="explaination">To use the SpinShare client, you need an active SpinShare user profile. Insert your connect code to log into your account. You can find your connect code in your account settings under the "Connect" tab.</div>
<input type="text" maxlength="6" class="connectcodeInput" placeholder="000000" v-model="connectCode" />
<div class="error" v-if="apiLoginCodeError">The connect code is wrong or expired. Please try again!</div>
<div class="error" v-if="apiLoginServerError">Couldn't reach the SpinShare server. Please try again later!</div>
<div class="actions">
<button class="button" v-on:click="connect()" :disabled="!canConnect">Connect</button>
</div>
</div>
</section>
</template>
<script>
import SSAPI from '@/modules/module.api.js';
import UserSettings from '@/modules/module.usersettings.js';
import Loading from '@/components/Loading.vue';
export default {
name: 'Login',
components: {
Loading
},
data: function() {
return {
hasValidToken: false,
connectCode: "",
canConnect: false,
apiLoginLoading: true,
apiLoginCodeError: false,
apiLoginServerError: false
}
},
mounted: function() {
let ssapi = new SSAPI();
let userSettings = new UserSettings();
if(!userSettings.get("connectToken")) {
this.showLoginBox();
} else {
ssapi.validateConnectToken(userSettings.get("connectToken")).then((data) => {
if(data) {
this.$router.replace({ name: 'StartupFrontpage' });
} else {
this.showLoginBox();
}
}).catch(() => {
this.showLoginBox();
});
// Check if connect token is valid
// Show Login Startup if not
// Show Startup if yes
}
},
methods: {
connect: function() {
let ssapi = new SSAPI();
let userSettings = new UserSettings();
this.$data.apiLoginLoading = true;
this.$data.apiLoginCodeError = false;
this.$data.apiLoginServerError = false;
console.log("Trying connection with code '" + this.$data.connectCode + "'");
ssapi.getConnectToken(this.$data.connectCode).then((data) => {
switch(data.status) {
case 200:
// Successfull
userSettings.set("connectToken", data.data);
this.$router.replace({ name: 'StartupFrontpage' });
break;
case 403:
case 404:
// Token invalid
this.showLoginBox();
this.$data.apiLoginCodeError = true;
break;
}
}).catch((error) => {
this.showLoginBox();
this.$data.apiLoginServerError = true;
});
},
showLoginBox: function() {
this.$data.apiLoginLoading = false;
this.$data.apiLoginCodeError = false;
this.$data.apiLoginServerError = false;
}
},
watch: {
connectCode: function(newVal) {
if(newVal.length == 6) {
this.$data.canConnect = true;
} else {
this.$data.canConnect = false;
}
}
},
}
</script>
<style scoped lang="less">
.section-login {
position: absolute;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
display: flex;
justify-content: center;
align-items: center;
& .loading {
background: black;
}
& .login-box {
width: 500px;
background: #000;
border-radius: 6px;
padding: 40px;
& .logo {
text-align: center;
margin-bottom: 30px;
& img {
height: 50px;
}
}
& .explaination {
line-height: 1.5;
}
& .error {
border: 2px solid #fb1357;
padding: 15px;
background: #fb1357;
border-radius: 4px;
margin-bottom: 15px;
}
& .connectcodeInput {
font-size: 42px;
background: transparent;
border: 0px;
width: 100%;
text-align: center;
color: #fff;
padding: 15px;
margin-top: 30px;
margin-bottom: 15px;
letter-spacing: 0.15em;
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
& .actions {
display: flex;
flex-direction: row-reverse;
}
&.login-loading {
padding: 60px;
}
}
}
</style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment