Commit 8d047319 authored by SpinShare's avatar SpinShare

startup tabs, settings part 1, search fix

parent 0ae69cae
{ {
"name": "client", "name": "spinshare-client",
"version": "0.1.0", "version": "2.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
......
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
], ],
methods: { methods: {
confirm() { confirm() {
console.log("confirm");
this.$parent.$emit('deleteConfirm'); this.$parent.$emit('deleteConfirm');
}, },
close() { close() {
......
...@@ -74,6 +74,22 @@ class SSAPI { ...@@ -74,6 +74,22 @@ class SSAPI {
}); });
} }
async getHotSongs(_offset) {
let apiPath = this.apiBase + "songs/hot/" + _offset;
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.data;
}).catch(function(error) {
throw new Error(error);
});
}
async getPopularSongs(_offset) { async getPopularSongs(_offset) {
let apiPath = this.apiBase + "songs/popular/" + _offset; let apiPath = this.apiBase + "songs/popular/" + _offset;
let supportedVersion = this.supportedVersion; let supportedVersion = this.supportedVersion;
......
...@@ -3,6 +3,10 @@ import VueRouter from 'vue-router'; ...@@ -3,6 +3,10 @@ import VueRouter from 'vue-router';
import VueAxios from 'vue-axios'; import VueAxios from 'vue-axios';
import axios from 'axios'; import axios from 'axios';
import ViewStartup from '../views/Startup.vue'; import ViewStartup from '../views/Startup.vue';
import ViewStartupFrontpage from '../views/StartupFrontpage.vue';
import ViewStartupNewSongs from '../views/StartupNewSongs.vue';
import ViewStartupHotSongs from '../views/StartupHotSongs.vue';
import ViewStartupPopularSongs from '../views/StartupPopularSongs.vue';
import ViewSearch from '../views/Search.vue'; import ViewSearch from '../views/Search.vue';
import ViewLibrary from '../views/Library.vue'; import ViewLibrary from '../views/Library.vue';
import ViewSettings from '../views/Settings.vue'; import ViewSettings from '../views/Settings.vue';
...@@ -13,7 +17,29 @@ Vue.use(VueAxios, axios); ...@@ -13,7 +17,29 @@ Vue.use(VueAxios, axios);
const routes = [{ const routes = [{
path: '/', path: '/',
name: 'Startup', name: 'Startup',
component: ViewStartup component: ViewStartup,
children: [
{
path: '/',
name: 'Frontpage',
component: ViewStartupFrontpage
},
{
path: '/new',
name: 'New Songs',
component: ViewStartupNewSongs
},
{
path: '/hot',
name: 'Hot Songs',
component: ViewStartupHotSongs
},
{
path: '/popular',
name: 'Popular Songs',
component: ViewStartupPopularSongs
}
]
}, { }, {
path: '/search', path: '/search',
name: 'Search', name: 'Search',
......
...@@ -7,19 +7,21 @@ ...@@ -7,19 +7,21 @@
<input type="search" placeholder="Search for songs, tags &amp; profiles..." v-on:input="search()" v-model="searchQuery"> <input type="search" placeholder="Search for songs, tags &amp; profiles..." v-on:input="search()" v-model="searchQuery">
</div> </div>
<div class="search-results"> <div class="search-results">
<UserRow title="Users" v-if="searchResultsUsers.length > 0"> <UserRow title="Users" v-show="searchResultsUsers.length > 0">
<UserItem <UserItem
v-for="user in searchResultsUsers" v-for="user in searchResultsUsers"
v-bind:key="user.id" v-bind:key="user.id"
v-bind="user" /> v-bind="user" />
</UserRow> </UserRow>
<SongRow title="Songs" noactions="true" v-if="searchResultsSongs.length > 0"> <SongRow title="Songs" noactions="true" v-show="searchResultsSongs.length > 0">
<template v-slot:song-list>
<SongItem <SongItem
v-for="song in searchResultsSongs" v-for="song in searchResultsSongs"
v-bind:key="song.id" v-bind:key="song.id"
v-bind="song" /> v-bind="song" />
</template>
</SongRow> </SongRow>
<div class="search-results-noresults" v-if="searchResultsUsers.length == 0 && searchResultsSongs.length == 0 && apiFinished"> <div class="search-results-noresults" v-show="searchResultsUsers.length == 0 && searchResultsSongs.length == 0 && apiFinished">
<div class="noresults-title">{{ searchQuery }}</div> <div class="noresults-title">{{ searchQuery }}</div>
<div class="noresults-text">Your search did not match any songs or users. Make sure, that all words are spelled correctly or try a different query.</div> <div class="noresults-text">Your search did not match any songs or users. Make sure, that all words are spelled correctly or try a different query.</div>
</div> </div>
......
<template> <template>
<section class="section-settings"> <section class="section-settings">
SETTINGS <div class="settings-box">
<div class="settings-title" locale="">SpinShare</div>
<div class="settings-item">
<div class="settings-label" locale="">Version</div>
<div class="settings-input">
<span class="settings-input-version">{{ version }}-{{ environment }}</span>
</div>
</div>
<div class="settings-item">
<div class="settings-label" locale="">Update</div>
<div class="settings-input">
<button onclick="CheckForUpdates(true)" locale="">Check for Updates</button>
</div>
</div>
</div>
<div class="settings-box">
<div class="settings-title" locale="">Language</div>
<div class="settings-item">
<div class="settings-label" locale="">Select Language</div>
<div class="settings-input">
<select onchange="SettingsChangeLanguage()" class="settings-input-language">
<option value="en">English (English)</option>
<option value="de">Deutsch (German)</option>
<option value="nl">Nederlands (Dutch)</option>
<option value="pl">Polski (Polish)</option>
<option value="fr">Français (French)</option>
<option value="pt-BR">Português Brasileiro (Brazilian Portuguese)</option>
<option value="es">Español (Spanish)</option>
<option value="ru">Русский (Russian)</option>
<option value="kr">한국어 (Korean)</option>
<option value="zh-CN">简体中文 (Simplified Chinese)</option>
<option value="speen">Speen (Speen)</option>
</select>
</div>
</div>
</div>
<div class="settings-box">
<div class="settings-title" locale="">Directories</div>
<div class="settings-item">
<div class="settings-label" locale="">Game Directory</div>
<div class="settings-input settings-input-twobuttons">
<input type="text" disabled="" class="settings-input-gamedirectory">
<button onclick="SettingsSelectGameDirectory()" locale="">Change</button>
<button onclick="SettingsResetGameDirectory()" locale="">Reset</button>
</div>
</div>
</div>
</section> </section>
</template> </template>
<script> <script>
import { remote } from 'electron';
const { app } = remote;
export default { export default {
name: 'Settings', name: 'Settings',
components: { data: function() {
return {
version: "",
environment: ""
}
},
mounted: function() {
this.$data.version = app.getVersion();
this.$data.environment = process.env.NODE_ENV;
} }
} }
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
section { .section-settings {
padding: 50px; padding: 50px;
display: flex;
flex-direction: column;
align-items: center;
& .settings-title {
font-size: 14px;
letter-spacing: 0.25em;
font-weight: bold;
text-transform: uppercase;
}
& .settings-box {
width: 600px;
padding: 25px;
background: rgba(255,255,255,0.1);
border-radius: 6px;
margin-bottom: 25px;
& .settings-item {
display: grid;
grid-template-columns: 170px 1fr;
grid-gap: 15px;
margin-top: 15px;
& .settings-label {
align-self: center;
opacity: 0.6;
}
& .settings-input {
display: grid;
grid-template-columns: 1fr;
grid-gap: 5px;
&.settings-input-twobuttons {
grid-template-columns: 1fr auto auto;
}
& select, input[type="text"] {
width: 100%;
font-family: 'Open Sans', sans-serif;
font-size: 12px;
color: #fff;
background: rgba(255,255,255,0.2);
text-transform: uppercase;
font-weight: 700;
border-radius: 4px;
padding: 7px 14px;
border: 0px;
transition: 0.2s ease-in-out all;
&:not(:disabled):hover {
background: rgba(255,255,255,0.4);
color: #fff;
cursor: pointer;
}
&:focus {
outline: 0;
}
&::placeholder {
color: rgba(255,255,255,0.6);
}
& option {
background: #222;
text-transform: initial;
}
&:disabled {
opacity: 0.4;
}
}
& input[type="text"] {
text-transform: initial;
font-weight: normal;
}
}
}
}
} }
</style> </style>
\ No newline at end of file
<template> <template>
<section class="section-startup"> <section class="section-startup">
<div class="staff-promos"> <div class="tabs">
<StaffPromoPlaceholder <router-link to="/"><span>FRONTPAGE</span></router-link>
v-if="isPromoLoading" <router-link to="/new"><span>NEW</span></router-link>
v-for="n in 2" <router-link to="/hot"><span>HOT</span></router-link>
v-bind:key="n" /> <router-link to="/popular"><span>POPULAR</span></router-link>
<StaffPromo
v-if="!isPromoLoading"
v-for="staffPromo in staffPromos"
v-bind:key="staffPromo.id"
v-bind="staffPromo" />
</div> </div>
<SongRow <router-view></router-view>
title="New Songs">
<template v-slot:controls>
<div :class="'item ' + (newSongsOffset == 0 ? 'disabled' : '')" v-on:click="newPrevious()"><i class="mdi mdi-chevron-left"></i></div>
<div :class="'item ' + (newSongs.length < 5 ? 'disabled' : '')" v-on:click="newNext()"><i class="mdi mdi-chevron-right"></i></div>
</template>
<template v-slot:song-list>
<SongItemPlaceholder
v-if="isNewSongsLoading"
v-for="n in 6"
v-bind:key="n" />
<SongItem
v-if="!isNewSongsLoading"
v-for="song in newSongs"
v-bind:key="song.id"
v-bind="song" />
</template>
</SongRow>
<SongRow
title="Popular Songs">
<template v-slot:controls>
<div :class="'item ' + (popularSongsOffset == 0 ? 'disabled' : '')" v-on:click="popularPrevious()"><i class="mdi mdi-chevron-left"></i></div>
<div :class="'item ' + (popularSongs.length < 5 ? 'disabled' : '')" v-on:click="popularNext()"><i class="mdi mdi-chevron-right"></i></div>
</template>
<template v-slot:song-list>
<SongItemPlaceholder
v-if="isPopularSongsLoading"
v-for="n in 6"
v-bind:key="n" />
<SongItem
v-if="!isPopularSongsLoading"
v-for="song in popularSongs"
v-bind:key="song.id"
v-bind="song" />
</template>
</SongRow>
</section> </section>
</template> </template>
<script> <script>
import SSAPI from '@/modules/module.api.js'; import SSAPI from '@/modules/module.api.js';
import StaffPromo from '@/components/Startup/StaffPromo.vue';
import StaffPromoPlaceholder from '@/components/Startup/StaffPromoPlaceholder.vue';
import SongRow from '@/components/Song/SongRow.vue';
import SongItem from '@/components/Song/SongItem.vue';
import SongItemPlaceholder from '@/components/Song/SongItemPlaceholder.vue';
export default { export default {
name: 'Startup', name: 'Startup',
data: function() {
return {
isPromoLoading: true,
staffPromos: [],
isNewSongsLoading: true,
newSongsOffset: 0,
newSongs: [],
isPopularSongsLoading: true,
popularSongsOffset: 0,
popularSongs: []
} }
}, </script>
mounted: function() {
let ssapi = new SSAPI(process.env.NODE_ENV === 'development'); <style scoped lang="less">
section {
padding: 50px;
padding-top: 30px;
ssapi.getPromos().then((data) => { & .tabs {
this.$data.isPromoLoading = false; display: flex;
this.$data.staffPromos = data; justify-content: center;
}); margin-bottom: 25px;
ssapi.getNewSongs(this.$data.newSongsOffset).then((data) => { & a {
this.$data.isNewSongsLoading = false; color: inherit;
this.$data.newSongs = data; text-decoration: none;
}); font-weight: bold;
letter-spacing: 0.1em;
padding: 10px 15px;
margin: 0px 5px;
text-align: center;
opacity: 0.6;
transition: 0.2s ease-in-out background, 0.2s ease-in-out opacity;
ssapi.getPopularSongs(this.$data.popularSongsOffset).then((data) => { & span {
this.$data.isPopularSongsLoading = false; display: block;
this.$data.popularSongs = data; transition: 0.2s ease-in-out transform;
}); transform: translateY(4px);
},
components: {
StaffPromo,
StaffPromoPlaceholder,
SongRow,
SongItem,
SongItemPlaceholder
},
methods: {
newNext: function() {
if(this.$data.newSongs.length > 5) {
this.$data.newSongsOffset++;
this.updateNew();
}
},
newPrevious: function() {
if(this.$data.newSongsOffset > 0) {
this.$data.newSongsOffset--;
this.updateNew();
} }
}, &:hover {
popularNext: function() { background: rgba(255,255,255,0.2);
if(this.$data.popularSongs.length > 5) { border-radius: 5px;
this.$data.popularSongsOffset++;
this.updatePopular();
} }
}, &:after {
popularPrevious: function() { transition: 0.2s ease-in-out background;
if(this.$data.popularSongsOffset > 0) { margin: 0 auto;
this.$data.popularSongsOffset--; margin-top: 5px;
this.updatePopular(); width: 20px;
height: 3px;
border-radius: 5px;
background: transparent;
content: "";
display: block;
} }
},
updateNew: function() {
let ssapi = new SSAPI(process.env.NODE_ENV === 'development');
this.$data.isNewSongsLoading = true;
ssapi.getNewSongs(this.$data.newSongsOffset).then((data) => { &.router-link-exact-active {
this.$data.isNewSongsLoading = false; opacity: 1;
this.$data.newSongs = data;
});
},
updatePopular: function() {
let ssapi = new SSAPI(process.env.NODE_ENV === 'development');
this.$data.isPopularSongsLoading = true;
ssapi.getPopularSongs(this.$data.popularSongsOffset).then((data) => { & span {
this.$data.isPopularSongsLoading = false; transform: translateY(0px);
this.$data.popularSongs = data;
});
} }
&:after {
background: linear-gradient(135deg, #fd2f85, #7a34ec);
} }
} }
</script>
<style scoped lang="less">
section {
padding: 50px;
& .staff-promos {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 25px;
width: 1114px;
margin: 0 auto;
&:empty {
display: none;
}
} }
& .song-row {
margin-top: 25px;
} }
} }
</style> </style>
\ No newline at end of file
<template>
<div class="staff-promos">
<StaffPromoPlaceholder
v-if="isPromoLoading"
v-for="n in 2"
v-bind:key="n" />
<StaffPromo
v-if="!isPromoLoading"
v-for="staffPromo in staffPromos"
v-bind:key="staffPromo.id"
v-bind="staffPromo" />
</div>
</template>
<script>
import SSAPI from '@/modules/module.api.js';
import StaffPromo from '@/components/Startup/StaffPromo.vue';
import StaffPromoPlaceholder from '@/components/Startup/StaffPromoPlaceholder.vue';
export default {
name: 'StartupFrontpage',
data: function() {
return {
isPromoLoading: true,
staffPromos: []
}
},
mounted: function() {
let ssapi = new SSAPI(process.env.NODE_ENV === 'development');
ssapi.getPromos().then((data) => {
this.$data.isPromoLoading = false;
this.$data.staffPromos = data;
});
},
components: {
StaffPromo,
StaffPromoPlaceholder
},
methods: {
}
}
</script>
<style scoped lang="less">
.staff-promos {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 25px;
width: 1114px;
margin: 0 auto;
&:empty {
display: none;
}
}
</style>
\ No newline at end of file
<template>
<SongRow
title="Hot Songs">
<template v-slot:controls>
<div :class="'item ' + (hotSongsOffset == 0 ? 'disabled' : '')" v-on:click="hotPrevious()"><i class="mdi mdi-chevron-left"></i></div>
<div :class="'item ' + (hotSongs.length < 11 ? 'disabled' : '')" v-on:click="hotNext()"><i class="mdi mdi-chevron-right"></i></div>
</template>
<template v-slot:song-list>
<SongItemPlaceholder
v-if="isHotSongsLoading"
v-for="n in 12"
v-bind:key="n" />
<SongItem
v-if="!isHotSongsLoading"
v-for="song in hotSongs"
v-bind:key="song.id"
v-bind="song" />
</template>
</SongRow>
</template>
<script>
import SSAPI from '@/modules/module.api.js';
import SongRow from '@/components/Song/SongRow.vue';
import SongItem from '@/components/Song/SongItem.vue';
import SongItemPlaceholder from '@/components/Song/SongItemPlaceholder.vue';
export default {
name: 'Frontpage',
data: function() {
return {
isHotSongsLoading: true,
hotSongsOffset: 0,
hotSongs: []
}
},
mounted: function() {
let ssapi = new SSAPI(process.env.NODE_ENV === 'development');
ssapi.getHotSongs(this.$data.hotSongsOffset).then((data) => {
this.$data.isHotSongsLoading = false;
this.$data.hotSongs = data;
});
},
components: {
SongRow,
SongItem,
SongItemPlaceholder
},
methods: {
hotNext: function() {
if(this.$data.hotSongs.length > 11) {
this.$data.hotSongsOffset++;
this.updateHot();
}
},
hotPrevious: function() {
if(this.$data.hotSongsOffset > 0) {
this.$data.hotSongsOffset--;
this.updateHot();
}
},
updateHot: function() {
let ssapi = new SSAPI(process.env.NODE_ENV === 'development');
this.$data.isHotSongsLoading = true;
ssapi.getHotSongs(this.$data.hotSongsOffset).then((data) => {
this.$data.isHotSongsLoading = false;
this.$data.hotSongs = data;
});
}
}
}
</script>
<style scoped lang="less">
</style>
\ No newline at end of file
<template>
<SongRow
title="New Songs">
<template v-slot:controls>
<div :class="'item ' + (newSongsOffset == 0 ? 'disabled' : '')" v-on:click="newPrevious()"><i class="mdi mdi-chevron-left"></i></div>
<div :class="'item ' + (newSongs.length < 11 ? 'disabled' : '')" v-on:click="newNext()"><i class="mdi mdi-chevron-right"></i></div>
</template>
<template v-slot:song-list>
<SongItemPlaceholder
v-if="isNewSongsLoading"
v-for="n in 12"
v-bind:key="n" />
<SongItem
v-if="!isNewSongsLoading"
v-for="song in newSongs"
v-bind:key="song.id"
v-bind="song" />
</template>
</SongRow>
</template>
<script>
import SSAPI from '@/modules/module.api.js';
import SongRow from '@/components/Song/SongRow.vue';
import SongItem from '@/components/Song/SongItem.vue';
import SongItemPlaceholder from '@/components/Song/SongItemPlaceholder.vue';
export default {
name: 'Frontpage',
data: function() {
return {
isNewSongsLoading: true,
newSongsOffset: 0,
newSongs: []
}
},
mounted: function() {
let ssapi = new SSAPI(process.env.NODE_ENV === 'development');
ssapi.getNewSongs(this.$data.newSongsOffset).then((data) => {
this.$data.isNewSongsLoading = false;
this.$data.newSongs = data;
});
},
components: {
SongRow,
SongItem,
SongItemPlaceholder
},
methods: {
newNext: function() {
if(this.$data.newSongs.length > 11) {
this.$data.newSongsOffset++;
this.updateNew();
}
},
newPrevious: function() {
if(this.$data.newSongsOffset > 0) {
this.$data.newSongsOffset--;
this.updateNew();
}
},
updateNew: function() {
let ssapi = new SSAPI(process.env.NODE_ENV === 'development');
this.$data.isNewSongsLoading = true;
ssapi.getNewSongs(this.$data.newSongsOffset).then((data) => {
this.$data.isNewSongsLoading = false;
this.$data.newSongs = data;
});
}
}
}
</script>
<style scoped lang="less">
.song-row {
margin-top: 25px;
}
</style>
\ No newline at end of file
<template>
<SongRow
title="Popular Songs">
<template v-slot:controls>
<div :class="'item ' + (popularSongsOffset == 0 ? 'disabled' : '')" v-on:click="popularPrevious()"><i class="mdi mdi-chevron-left"></i></div>
<div :class="'item ' + (popularSongs.length < 11 ? 'disabled' : '')" v-on:click="popularNext()"><i class="mdi mdi-chevron-right"></i></div>
</template>
<template v-slot:song-list>
<SongItemPlaceholder
v-if="isPopularSongsLoading"
v-for="n in 12"
v-bind:key="n" />
<SongItem
v-if="!isPopularSongsLoading"
v-for="song in popularSongs"
v-bind:key="song.id"
v-bind="song" />
</template>
</SongRow>
</template>
<script>
import SSAPI from '@/modules/module.api.js';
import SongRow from '@/components/Song/SongRow.vue';
import SongItem from '@/components/Song/SongItem.vue';
import SongItemPlaceholder from '@/components/Song/SongItemPlaceholder.vue';
export default {
name: 'Startup',
data: function() {
return {
isPopularSongsLoading: true,
popularSongsOffset: 0,
popularSongs: []
}
},
mounted: function() {
let ssapi = new SSAPI(process.env.NODE_ENV === 'development');
ssapi.getPopularSongs(this.$data.popularSongsOffset).then((data) => {
this.$data.isPopularSongsLoading = false;
this.$data.popularSongs = data;
});
},
components: {
SongRow,
SongItem,
SongItemPlaceholder
},
methods: {
popularNext: function() {
if(this.$data.popularSongs.length > 11) {
this.$data.popularSongsOffset++;
this.updatePopular();
}
},
popularPrevious: function() {
if(this.$data.popularSongsOffset > 0) {
this.$data.popularSongsOffset--;
this.updatePopular();
}
},
updatePopular: function() {
let ssapi = new SSAPI(process.env.NODE_ENV === 'development');
this.$data.isPopularSongsLoading = true;
ssapi.getPopularSongs(this.$data.popularSongsOffset).then((data) => {
this.$data.isPopularSongsLoading = false;
this.$data.popularSongs = data;
});
}
}
}
</script>
<style scoped lang="less">
.song-row {
margin-top: 25px;
}
</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