Commit e8f82ad7 authored by Andreas Heimann's avatar Andreas Heimann

fixed login, added playlists

parent ef025358
<template>
<div class="song-item" v-on:auxclick="shortDownload($event)" v-on:contextmenu="showContextMenu($event)">
<router-link :to="{ name: 'SongDetail', params: { id: id } }">
<div class="song-cover" :style="'background-image: url(' + cover + '), url(' + require('@/assets/img/defaultAlbumArt.jpg') + ');'">
<div class="song-charter-info">
<div class="song-charter"><i class="mdi mdi-account-circle"></i><span>{{ charter }}</span></div>
</div>
</div>
<div class="song-metadata">
<div class="song-title">{{ title }}</div>
<div class="song-artist">{{ artist }}</div>
<div class="song-difficulties">
<div :class="hasEasyDifficulty ? 'difficulty active' : 'difficulty'"><span>E</span></div>
<div :class="hasNormalDifficulty ? 'difficulty active' : 'difficulty'"><span>N</span></div>
<div :class="hasHardDifficulty ? 'difficulty active' : 'difficulty'"><span>H</span></div>
<div :class="hasExtremeDifficulty ? 'difficulty active' : 'difficulty'"><span>EX</span></div>
<div :class="hasXDDifficulty ? 'difficulty active' : 'difficulty'"><span>XD</span></div>
</div>
</div>
</router-link>
</div>
</template>
<script>
import { remote } from 'electron';
const { clipboard, shell } = remote;
export default {
name: 'SongItem',
props: [
'id',
'cover',
'title',
'subtitle',
'artist',
'charter',
'hasEasyDifficulty',
'hasNormalDifficulty',
'hasHardDifficulty',
'hasExtremeDifficulty',
'hasXDDifficulty',
'zip'
],
data: function() {
return {
isContextMenuActive: false
}
},
mounted: function() {
},
methods: {
showContextMenu: function(e) {
if(e != undefined) {
e.preventDefault();
}
this.$root.$emit('showContextMenu', {
x: e.pageX,
y: e.pageY,
items: [
{ icon: "eye", title: this.$t('contextmenu.open'), method: () => { this.$router.push({ name: 'SongDetail', params: { id: this.$props.id } }); } },
{ icon: "earth", title: this.$t('contextmenu.openOnSpinShare'), method: () => { shell.openExternal("https://spinsha.re/song/" + this.$props.id); } },
{ icon: "link", title: this.$t('contextmenu.copyLink'), method: () => { clipboard.writeText('https://spinsha.re/playlist/' + this.$props.id); } },
{ icon: "download", title: this.$t('contextmenu.download'), method: () => { this.download(); } }
]});
}
}
}
</script>
<style scoped lang="less">
</style>
......@@ -59,7 +59,7 @@
y: e.pageY,
items: [
{ icon: "eye", title: this.$t('contextmenu.open'), method: () => { this.$router.push({ name: 'SongDetail', params: { id: this.$props.id } }); } },
{ icon: "earth", title: this.$t('contextmenu.openOnSpinShare'), method: () => { shell.openExternal("https://spinsha.re/report/song/" + this.$props.id); } },
{ icon: "earth", title: this.$t('contextmenu.openOnSpinShare'), method: () => { shell.openExternal("https://spinsha.re/song/" + this.$props.id); } },
{ icon: "link", title: this.$t('contextmenu.copyLink'), method: () => { clipboard.writeText('https://spinsha.re/song/' + this.$props.id); } },
{ icon: "download", title: this.$t('contextmenu.download'), method: () => { this.download(); } }
]});
......
<template>
<div class="song-row">
<div class="song-list">
<div class="song-list" v-if="!playlist">
<slot name="song-list"></slot>
</div>
<div class="song-list-playlist" v-if="playlist">
<slot name="song-list"></slot>
</div>
<div class="noresults">
......@@ -16,7 +19,8 @@
name: 'SongRow',
props: [
'title',
'noactions'
'noactions',
'playlist'
]
}
</script>
......@@ -32,6 +36,11 @@
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
grid-gap: 15px;
}
& .song-list-playlist {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 15px;
}
& .song-list-noresults {
display: none;
background: rgba(255,255,255,0.1);
......@@ -54,4 +63,26 @@
}
}
}
@media screen and (max-width: 1600px) {
.song-row {
& .song-list {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
}
& .song-list-playlist {
grid-template-columns: 1fr 1fr 1fr 1fr;
}
}
}
@media screen and (max-width: 1300px) {
.song-row {
& .song-list {
grid-template-columns: 1fr 1fr 1fr 1fr;
}
& .song-list-playlist {
grid-template-columns: 1fr 1fr 1fr;
}
}
}
</style>
This diff is collapsed.
......@@ -16,6 +16,7 @@ import ViewUserDetail from '../views/UserDetail.vue';
import ViewUserDetailCharts from '../views/UserDetailCharts.vue';
import ViewUserDetailReviews from '../views/UserDetailReviews.vue';
import ViewUserDetailSpinPlays from '../views/UserDetailSpinPlays.vue';
import ViewPlaylistDetail from '../views/PlaylistDetail.vue';
import ViewSettings from '../views/Settings.vue';
import ViewTournament from '../views/Tournament.vue';
import ViewError from '../views/Error.vue';
......@@ -106,6 +107,10 @@ const routes = [{
component: ViewUserDetailSpinPlays
}
]
}, {
path: '/playlist/:id',
name: 'PlaylistDetail',
component: ViewPlaylistDetail,
}, {
path: '/settings',
name: 'Settings',
......
......@@ -75,24 +75,32 @@
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.loadIntoProfile();
break;
case 403:
case 404:
// Token invalid
this.showLoginBox();
this.$data.apiLoginCodeError = true;
break;
}
}).catch((error) => {
try {
ssapi.getConnectToken(this.$data.connectCode).then((data) => {
console.log(data);
switch (data.status) {
case 200:
// Successfull
userSettings.set("connectToken", data.data);
this.loadIntoProfile();
break;
default:
// Token invalid
this.showLoginBox();
this.$data.apiLoginCodeError = true;
break;
}
}).catch((error) => {
console.error(error);
this.showLoginBox();
this.$data.apiLoginServerError = true;
});
} catch(error) {
console.error(error);
this.showLoginBox();
this.$data.apiLoginServerError = true;
});
}
},
showLoginBox: function() {
this.$data.apiLoginLoading = false;
......
<template>
<section :class="!apiFinished ? 'section-playlist-detail-loading' : 'section-playlist-detail'">
<section class="section-playlist-detail" v-if="apiFinished">
<div class="cover" :style="'background-image: url(' + cover + ');'">
<div class="shade" v-if="!isOfficial">
<div class="content">
<div class="title">{{ title }}<span class="official-badge" v-if="isOfficial">OFFICIAL</span></div>
<div class="quickinfo">{{ songs.length }} Charts</div>
</div>
</div>
</div>
<div class="playlist-content">
<div class="playlist-detail">
<div class="playlist-description">
{{ description }}
</div>
<div class="playlist-actions">
<div class="action-row">
<div v-on:click="CopyLink()" class="action">
<div class="icon">
<i class="mdi mdi-content-copy"></i>
</div>
</div>
<div v-on:click="AddToQueue()" class="action">
<div class="icon">
<i class="mdi mdi-download"></i>
</div>
</div>
</div>
</div>
<div class="playlist-uploader" v-if="!isOfficial">
<div class="label">Created by</div>
<UserItem v-bind="user" />
</div>
<div class="playlist-charters" v-if="songs.length > 0">
<div class="label">With Charts by</div>
<div class="charters">
charters
</div>
</div>
</div>
<SongRow v-if="apiFinished && songs.length > 0" v-bind:playlist="true">
<template v-slot:song-list>
<SongItem
v-for="song in songs"
v-bind:key="song.id"
v-bind="song"
v-bind:cover="song.cover" />
</template>
</SongRow>
<div class="list-noresults" v-if="songs.length === 0">
<div class="noresults-text">This playlist is empty.</div>
</div>
</div>
</section>
<Loading v-if="!apiFinished" />
</section>
</template>
<script>
import { remote } from 'electron';
const { clipboard, shell } = remote;
import SSAPI from '@/modules/module.api.js';
import UserSettings from '@/modules/module.usersettings.js';
import UserItem from '@/components/User/UserItem.vue';
import SongRow from '@/components/Song/SongRow.vue';
import SongItem from '@/components/Song/SongItem.vue';
import CollapsableText from '@/components/CollapsableText.vue';
import Loading from '@/components/Loading.vue';
export default {
name: 'SongDetail',
components: {
UserItem,
SongItem,
SongRow,
CollapsableText,
Loading
},
data: function() {
return {
apiFinished: false,
id: 0,
cover: "",
title: "",
description: "",
isOfficial: false,
user: null,
songs: [],
}
},
mounted: function() {
let ssapi = new SSAPI();
let userSettings = new UserSettings();
let routeID = this.$route.params.id;
ssapi.getPlaylistDetail(routeID).then((data) => {
if(data.status == 200) {
this.$data.id = data.data.id;
this.$data.cover = data.data.paths.cover;
this.$data.title = data.data.title;
this.$data.description = data.data.description;
this.$data.isOfficial = data.data.isOfficial;
this.$data.user = data.data.user;
this.$data.songs = data.data.songs;
this.$data.apiFinished = true;
} else {
this.$router.push({ name: 'Error', params: { errorCode: data.status } });
}
}).catch((error) => {
console.error(error);
this.$router.push({ name: 'Error', params: { errorCode: 500 } });
});
},
methods: {
AddToQueue: function() {
this.$data.songs.forEach((songItem) => {
this.$root.$emit('download', {id: songItem.id, cover: songItem.cover, title: songItem.title, artist: songItem.artist, downloadPath: songItem.zip});
});
},
CopyLink: function() {
clipboard.writeText("https://spinsha.re/playlist/" + this.$data.id);
},
},
beforeDestroy: function() {
}
}
</script>
<style scoped lang="less">
.section-playlist-detail {
& .cover {
width: 100%;
height: 300px;
background: rgba(255, 255, 255, 0.1) center;
background-size: cover;
& .shade {
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.4);
transition: 0.2s ease all;
display: flex;
justify-content: center;
align-items: center;
& .content {
text-align: center;
text-shadow: 0 4px 18px rgba(0, 0, 0, 0.6);
& .title {
font-size: 48px;
margin-bottom: 5px;
font-family: 'Oswald', sans-serif;
& .official-badge {
display: inline-block;
font-family: 'Open Sans', sans-serif;
font-weight: bold;
font-size: 12px;
padding: 2px 10px;
border-radius: 4px;
background: #fff;
color: #000;
text-shadow: 0 0 0 transparent;
margin-left: 10px;
transform: translateY(-11px);
}
}
& .quickinfo {
font-size: 18px;
opacity: 0.6;
}
}
}
}
& .playlist-content {
padding: 50px;
display: grid;
grid-template-columns: 400px 1fr;
grid-gap: 25px;
& .playlist-detail {
& .playlist-description {
background: #383C3F;
border-radius: 4px;
line-height: 1.5em;
display: grid;
grid-gap: 20px;
padding: 20px;
& .text {
line-height: 1.5em;
opacity: 0.7;
}
}
& .playlist-actions {
margin-top: 25px;
transition: all 0.2s ease-in-out;
border-radius: 4px;
overflow: hidden;
background: #fff;
& .action-row {
display: flex;
& .action {
background: linear-gradient(135deg, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.3));
flex-grow: 1;
display: flex;
justify-content: center;
align-items: center;
color: #222;
text-decoration: none;
transition: all 0.2s ease-in-out;
cursor: pointer;
& .icon {
display: flex;
justify-content: center;
align-items: center;
height: 48px;
width: 48px;
font-size: 24px;
}
&:hover {
opacity: 0.6;
}
}
}
}
& .playlist-uploader {
display: grid;
grid-template-columns: auto 1fr;
grid-gap: 15px;
margin-top: 25px;
& .label {
align-self: center;
opacity: 0.6;
}
}
& .playlist-charters {
display: grid;
grid-template-rows: auto 1fr;
grid-gap: 8px;
margin-top: 25px;
& .label {
align-self: center;
opacity: 0.6;
}
& .charters {
background: #383C3F;
padding: 15px;
line-height: 1.5em;
border-radius: 4px;
}
& .username {
opacity: 1;
color: #fff;
text-decoration: none;
transition: 0.2s ease-in-out opacity;
&:hover {
opacity: 0.6;
}
}
}
}
}
}
</style>
\ No newline at end of file
......@@ -99,11 +99,16 @@
if(this.$data.searchQuery !== "") {
ssapi.search(this.$data.searchQuery).then((data) => {
console.log(data);
if(data.status === 200) {
this.$data.searchResultsUsers = data.data.users;
this.$data.searchResultsSongs = data.data.songs;
this.$data.apiFinished = true;
} else if(data.status === 404) {
this.$data.searchResultsUsers = [];
this.$data.searchResultsSongs = [];
} else {
this.$router.push({ name: 'Error', params: { errorCode: data.status } });
}
......
......@@ -59,7 +59,7 @@
</div>
</div>
<!-- Botch -->
<!-- <div class="settings-box">
<div class="settings-box">
<div class="settings-title">Tournament Hub</div>
<div class="settings-item">
<div class="settings-label">Open</div>
......@@ -67,7 +67,7 @@
<router-link to="/tournament" class="button">Open WarpZone</router-link>
</div>
</div>
</div> -->
</div>
</div>
</section>
</template>
......
......@@ -4,7 +4,7 @@
<header>
<div class="title">Tournament WarpZone</div>
<div class="description">
This page looks very ugly, sorry about that &dash; Andreas
It's gonna be better next SSSO! I promise!! &dash; Laura
</div>
</header>
<div class="tournament-content" v-if="apiFinished">
......@@ -15,21 +15,21 @@
<div class="check-result" v-if="missingCharts.length > 0">
<div class="list-title title-missing">Missing</div>
<div class="item" v-for="missingChart in missingCharts" v-bind:key="missingChart.id">
<div class="cover" :style="'background-image: url(' + missingChart.paths.cover + '), url(' + require('@/assets/img/defaultAlbumArt.jpg') + ');'"></div>
<div class="cover" :style="'background-image: url(' + missingChart.cover + '), url(' + require('@/assets/img/defaultAlbumArt.jpg') + ');'"></div>
<div class="title">{{ missingChart.title }}</div>
</div>
</div>
<div class="check-result" v-if="outdatedCharts.length > 0">
<div class="list-title title-outdated">Outdated</div>
<div class="item" v-for="outdatedChart in outdatedCharts" v-bind:key="outdatedChart.id">
<div class="cover" :style="'background-image: url(' + outdatedChart.paths.cover + '), url(' + require('@/assets/img/defaultAlbumArt.jpg') + ');'"></div>
<div class="cover" :style="'background-image: url(' + outdatedChart.cover + '), url(' + require('@/assets/img/defaultAlbumArt.jpg') + ');'"></div>
<div class="title">{{ outdatedChart.title }}</div>
</div>
</div>
<div class="check-result" v-if="okCharts.length > 0">
<div class="list-title title-ok">OK</div>
<div class="item" v-for="okChart in okCharts" v-bind:key="okChart.id">
<div class="cover" :style="'background-image: url(' + okChart.paths.cover + '), url(' + require('@/assets/img/defaultAlbumArt.jpg') + ');'"></div>
<div class="cover" :style="'background-image: url(' + okChart.cover + '), url(' + require('@/assets/img/defaultAlbumArt.jpg') + ');'"></div>
<div class="title">{{ okChart.title }}</div>
</div>
</div>
......@@ -61,10 +61,10 @@
mounted: function() {
let ssapi = new SSAPI();
ssapi.getTournamentMappool().then((data) => {
ssapi.getPlaylistDetail(5).then((data) => {
this.$data.apiFinished = true;
this.$data.tournamentCharts = data.data;
this.$data.tournamentCharts = data.data.songs;
});
},
methods: {
......@@ -84,7 +84,7 @@
this.$data.missingCharts.push(chart);
} else {
// Check MD5
if(md5File.sync(files[0]) == chart.srtbMD5) {
if(md5File.sync(files[0]) == chart.currentVersion) {
this.$data.okCharts.push(chart);
} else {
this.$data.outdatedCharts.push(chart);
......@@ -99,10 +99,10 @@
downloadCharts: function() {
// Add to Queue
this.$data.missingCharts.forEach((chart) => {
this.$root.$emit('download', {id: chart.id, cover: chart.paths.cover, title: chart.title, artist: chart.artist, downloadPath: chart.paths.zip});
this.$root.$emit('download', {id: chart.id, cover: chart.cover, title: chart.title, artist: chart.artist, downloadPath: chart.zip});
});
this.$data.outdatedCharts.forEach((chart) => {
this.$root.$emit('download', {id: chart.id, cover: chart.paths.cover, title: chart.title, artist: chart.artist, downloadPath: chart.paths.zip});
this.$root.$emit('download', {id: chart.id, cover: chart.cover, title: chart.title, artist: chart.artist, downloadPath: chart.zip});
});
// Clear Analyzation data
......
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