Commit 0fbd4383 authored by SpinShare's avatar SpinShare

library removal, frontpage pagination

parent 1bca3c81
...@@ -7,33 +7,91 @@ ...@@ -7,33 +7,91 @@
<ContextMenu> <ContextMenu>
</ContextMenu> </ContextMenu>
<DeleteOverlay v-if="showDeleteOverlay" v-bind:deleteFiles="deleteFiles" />
</div> </div>
</template> </template>
<script> <script>
import fs from 'fs';
import glob from 'glob';
import path from 'path';
import UserSettings from '@/modules/module.usersettings.js';
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 DeleteOverlay from '@/components/Overlays/DeleteOverlay.vue';
export default { export default {
name: 'App', name: 'App',
components: { components: {
Navigation, Navigation,
ContextMenu ContextMenu,
DeleteOverlay
}, },
data: function() { data: function() {
return { return {
downloadQueue: [] downloadQueue: [],
showDeleteOverlay: false,
deleteFiles: []
} }
}, },
mounted: function() { mounted: function() {
this.$root.$on('download', (url) => { this.$root.$on('download', (url) => {
this.addToQueue(url); this.addToQueue(url);
}); });
this.$root.$on('delete', (file) => {
this.$data.showDeleteOverlay = true;
this.$data.deleteFiles = this.getConnectedFiles(file);
console.log(this.$data.deleteFiles);
});
this.$root.$on('deleteDeny', () => {
this.$data.showDeleteOverlay = false;
this.$data.deleteFiles = "";
});
this.$root.$on('deleteConfirmed', () => {
this.$data.deleteFiles.forEach((file) => {
fs.unlinkSync(file);
});
this.$data.showDeleteOverlay = false;
this.$data.deleteFiles = "";
});
}, },
methods: { methods: {
addToQueue: function(url) { addToQueue: function(url) {
this.$data.downloadQueue.push(url); this.$data.downloadQueue.push(url);
console.log("Added " + url + " to queue"); console.log("Added " + url + " to queue");
},
getConnectedFiles: function(srtbFilePath) {
let userSettings = new UserSettings();
let srtbContent = JSON.parse( fs.readFileSync(srtbFilePath) );
let connectedFiles = [];
connectedFiles.push(srtbFilePath);
let stringValueContainers = srtbContent['largeStringValuesContainer'].values;
stringValueContainers.forEach((stringValueContainer) => {
if(stringValueContainer.key == "SO_TrackInfo_TrackInfo") {
let rawTrackInfo = JSON.parse( stringValueContainer.val );
let coverPath = glob.sync(path.join(userSettings.get('gameDirectory'), "AlbumArt", rawTrackInfo.albumArtReference.assetName + ".*"))[0];
if(coverPath) {
connectedFiles.push(coverPath);
}
}
if(stringValueContainer.key.includes("SO_ClipInfo")) {
let rawClipInfo = JSON.parse( stringValueContainer.val );
let clipPath = glob.sync(path.join(userSettings.get('gameDirectory'), "AudioClips", rawClipInfo.clipAssetReference.assetName + ".*"))[0];
if(clipPath) {
connectedFiles.push(clipPath);
}
}
});
return connectedFiles;
} }
}, },
watch: { watch: {
......
<template>
<div class="delete-overlay">
<div class="delete-content">
<div class="delete-main">
<div class="delete-title">library.deletemodal.title</div>
<div class="delete-text">library.deletemodal.text</div>
<div class="delete-files">
<span v-for="deleteFile in deleteFiles">{{ deleteFile }}</span>
</div>
</div>
<div class="delete-actions">
<button class="button">Delete</button>
<button class="button" v-on:click="close()">Close</button>
</div>
</div>
</div>
</template>
<script>
import { remote } from 'electron';
const { shell } = remote;
export default {
name: 'DeleteOverlay',
props: [
'deleteFiles'
],
methods: {
confirm() {
this.$root.$emit('deleteConfirm');
},
close() {
this.$root.$emit('deleteDeny');
}
}
}
</script>
<style scoped lang="less">
.delete-overlay {
position: fixed;
z-index: 100;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background: rgba(0,0,0,0.75);
display: flex;
justify-content: center;
align-items: center;
& .delete-content {
width: 500px;
background: #212629;
border-radius: 6px;
position: relative;
overflow: hidden;
& .delete-main {
padding: 25px;
& .delete-title {
letter-spacing: 0.25em;
font-size: 14px;
font-weight: bold;
text-transform: uppercase;
}
& .delete-text {
padding: 15px 0px;
opacity: 0.6;
}
& .delete-files {
font-size: 12px;
& span {
display: block;
padding: 5px 0px;
}
}
}
& .delete-actions {
display: flex;
justify-content: flex-end;
padding: 25px;
background: rgba(0,0,0,0.4);
& button {
margin-left: 10px;
}
}
}
}
</style>
...@@ -52,9 +52,9 @@ ...@@ -52,9 +52,9 @@
x: e.pageX, x: e.pageX,
y: e.pageY, y: e.pageY,
items: [ items: [
{ icon: "eye", title: "Open", method: function() { alert('TODO') }.bind(this) }, { icon: "eye", title: "Open", method: () => { alert('TODO') } },
{ icon: "link", title: "Copy Link", method: function() { clipboard.writeText('https://spinsha.re/song/' + this.$props.id) }.bind(this) }, { icon: "link", title: "Copy Link", method: () => { clipboard.writeText('https://spinsha.re/song/' + this.$props.id) } },
{ icon: "download", title: "Download", method: function() { this.$root.$emit('download', this.$props.zip); }.bind(this) } { icon: "download", title: "Download", method: () => { this.$root.$emit('download', this.$props.zip); } }
]}); ]});
} }
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<div class="song-title">&nbsp;</div> <div class="song-title">&nbsp;</div>
<div class="song-artist">&nbsp;</div> <div class="song-artist">&nbsp;</div>
<div class="song-difficulties"> <div class="song-difficulties">
<img src="@/assets/img/difficultyEasy.svg" />
</div> </div>
</div> </div>
</div> </div>
...@@ -41,14 +42,32 @@ ...@@ -41,14 +42,32 @@
& .song-metadata { & .song-metadata {
padding: 15px; padding: 15px;
& .song-title {
height: 19px;
}
& .song-artist { & .song-artist {
margin-top: 5px; margin-top: 5px;
height: 19px;
} }
& .song-difficulties { & .song-difficulties {
margin-top: 10px; margin-top: 10px;
height: 20px; height: 20px;
display: flex; display: flex;
& img {
height: 18px;
margin-right: 10px;
opacity: 0;
}
} }
} }
} }
@keyframes songLoadingShimmer {
from {
background-position: 0px 0px;
}
to {
background-position: 173px 0px;
}
}
</style> </style>
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
name: 'SongLocalItem', name: 'SongLocalItem',
props: [ props: [
'id', 'id',
'file',
'detail', 'detail',
'cover', 'cover',
'isSpinShare' 'isSpinShare'
...@@ -37,7 +38,7 @@ ...@@ -37,7 +38,7 @@
x: e.pageX, x: e.pageX,
y: e.pageY, y: e.pageY,
items: [ items: [
{ icon: "delete", title: "Delete", method: function() { alert('TODO') }.bind(this) } { icon: "delete", title: "Delete", method: () => { this.$root.$emit('delete', this.$props.file); } }
]}); ]});
} }
} }
......
...@@ -3,12 +3,11 @@ ...@@ -3,12 +3,11 @@
<div class="song-header"> <div class="song-header">
<div :class="'row-title ' + (noactions ? 'row-title-noactions' : '')">{{ title }}</div> <div :class="'row-title ' + (noactions ? 'row-title-noactions' : '')">{{ title }}</div>
<div class="row-controls" v-if="!noactions"> <div class="row-controls" v-if="!noactions">
<div class="item disabled row-controls-previous"><i class="mdi mdi-chevron-left"></i></div> <slot name="controls"></slot>
<div class="item row-controls-next"><i class="mdi mdi-chevron-right"></i></div>
</div> </div>
</div> </div>
<div class="song-list"> <div class="song-list">
<slot></slot> <slot name="song-list"></slot>
</div> </div>
</div> </div>
</template> </template>
......
<template> <template>
<div class="user-row"> <div class="user-row">
<div class="user-header"> <div class="user-header">
<div :class="'row-title ' + (noactions ? 'row-title-noactions' : '')">{{ title }}</div> <div class="row-title">{{ title }}</div>
<div class="row-controls" v-if="!noactions">
<div class="item disabled row-controls-previous"><i class="mdi mdi-chevron-left"></i></div>
<div class="item row-controls-next"><i class="mdi mdi-chevron-right"></i></div>
</div>
</div> </div>
<div class="user-list"> <div class="user-list">
<slot></slot> <slot></slot>
...@@ -20,8 +16,7 @@ ...@@ -20,8 +16,7 @@
export default { export default {
name: 'UserRow', name: 'UserRow',
props: [ props: [
'title', 'title'
'noactions'
] ]
} }
</script> </script>
...@@ -41,10 +36,7 @@ ...@@ -41,10 +36,7 @@
font-size: 14px; font-size: 14px;
font-weight: bold; font-weight: bold;
text-transform: uppercase; text-transform: uppercase;
margin: 10px 0px;
&.row-title-noactions {
margin: 10px 0px;
}
} }
} }
& .user-list { & .user-list {
......
<template> <template>
<section class="section-library"> <section class="section-library">
<SongRow title="Installed Songs" noactions="true"> <SongRow title="Installed Songs">
<SongInstallItem /> <template v-slot:controls>
<div class="item"></div>
<div class="item" v-on:click="refreshLibrary()"><i class="mdi mdi-refresh"></i></div>
</template>
<template v-slot:song-list>
<SongInstallItem />
<SongLocalItem <SongLocalItem
v-for="song in librarySongs" v-for="song in librarySongs"
v-bind:key="song.detail.id" v-bind:key="song.detail.id"
v-bind="song" /> v-bind="song" />
</template>
</SongRow> </SongRow>
</section> </section>
</template> </template>
...@@ -61,6 +67,7 @@ ...@@ -61,6 +67,7 @@
} }
let librarySong = { let librarySong = {
file: file,
detail: songDetail, detail: songDetail,
cover: songCover, cover: songCover,
isSpinShare: songSpinShareReference isSpinShare: songSpinShareReference
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<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" noactions="true" v-if="searchResultsUsers.length > 0"> <UserRow title="Users" v-if="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"
......
...@@ -14,28 +14,40 @@ ...@@ -14,28 +14,40 @@
<SongRow <SongRow
title="New Songs"> title="New Songs">
<SongItemPlaceholder <template v-slot:controls>
v-if="isNewSongsLoading" <div :class="'item ' + (newSongsOffset == 0 ? 'disabled' : '')" v-on:click="newPrevious()"><i class="mdi mdi-chevron-left"></i></div>
v-for="n in 6" <div :class="'item ' + (newSongs.length < 5 ? 'disabled' : '')" v-on:click="newNext()"><i class="mdi mdi-chevron-right"></i></div>
v-bind:key="n" /> </template>
<SongItem <template v-slot:song-list>
v-if="!isNewSongsLoading" <SongItemPlaceholder
v-for="song in newSongs" v-if="isNewSongsLoading"
v-bind:key="song.id" v-for="n in 6"
v-bind="song" /> v-bind:key="n" />
<SongItem
v-if="!isNewSongsLoading"
v-for="song in newSongs"
v-bind:key="song.id"
v-bind="song" />
</template>
</SongRow> </SongRow>
<SongRow <SongRow
title="Popular Songs"> title="Popular Songs">
<SongItemPlaceholder <template v-slot:controls>
v-if="isPopularSongsLoading" <div :class="'item ' + (popularSongsOffset == 0 ? 'disabled' : '')" v-on:click="popularPrevious()"><i class="mdi mdi-chevron-left"></i></div>
v-for="n in 6" <div :class="'item ' + (popularSongs.length < 5 ? 'disabled' : '')" v-on:click="popularNext()"><i class="mdi mdi-chevron-right"></i></div>
v-bind:key="n" /> </template>
<SongItem <template v-slot:song-list>
v-if="!isPopularSongsLoading" <SongItemPlaceholder
v-for="song in popularSongs" v-if="isPopularSongsLoading"
v-bind:key="song.id" v-for="n in 6"
v-bind="song" /> v-bind:key="n" />
<SongItem
v-if="!isPopularSongsLoading"
v-for="song in popularSongs"
v-bind:key="song.id"
v-bind="song" />
</template>
</SongRow> </SongRow>
</section> </section>
</template> </template>
...@@ -86,6 +98,50 @@ ...@@ -86,6 +98,50 @@
SongRow, SongRow,
SongItem, SongItem,
SongItemPlaceholder 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();
}
},
popularNext: function() {
if(this.$data.popularSongs.length > 5) {
this.$data.popularSongsOffset++;
this.updatePopular();
}
},
popularPrevious: function() {
if(this.$data.popularSongsOffset > 0) {
this.$data.popularSongsOffset--;
this.updatePopular();
}
},
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;
});
},
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> </script>
......
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