mirror of
https://github.com/koel/koel
synced 2024-11-10 14:44:13 +00:00
Enable navigation with a basic router
This commit is contained in:
parent
d2a6e04bb0
commit
032b9bee7c
17 changed files with 162 additions and 82 deletions
|
@ -38,6 +38,7 @@ import { event, showOverlay, hideOverlay, loadMainView, forceReloadWindow, url }
|
|||
import { sharedStore, queueStore, songStore, userStore, preferenceStore as preferences } from './stores';
|
||||
import { playback, ls } from './services';
|
||||
import { focusDirective, clickawayDirective } from './directives';
|
||||
import router from './router';
|
||||
|
||||
export default {
|
||||
components: { siteHeader, siteFooter, mainWrapper, overlay, loginForm, editSongsForm },
|
||||
|
@ -199,6 +200,8 @@ export default {
|
|||
* Parse song ID from permalink and play.
|
||||
*/
|
||||
'koel:ready': () => {
|
||||
router.init();
|
||||
|
||||
const songId = url.parseSongId();
|
||||
if (!songId) return;
|
||||
const song = songStore.byId(songId);
|
||||
|
@ -211,6 +214,8 @@ export default {
|
|||
} else {
|
||||
playback.queueAndPlay(song);
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
<span class="meta" v-show="meta.songCount">
|
||||
by
|
||||
<a class="artist" v-if="isNormalArtist" @click.prevent="viewArtistDetails(album.artist)">{{ album.artist.name }}</a>
|
||||
<a class="artist" v-if="isNormalArtist" :href="'/#!/artist/' + album.artist.id">{{ album.artist.name }}</a>
|
||||
<span class="nope" v-else>{{ album.artist.name }}</span>
|
||||
•
|
||||
{{ meta.songCount | pluralize('song') }}
|
||||
|
@ -67,13 +67,12 @@ import { pluralize, event, loadMainView } from '../../../utils';
|
|||
import { albumStore, artistStore, sharedStore } from '../../../stores';
|
||||
import { playback, download, albumInfo as albumInfoService } from '../../../services';
|
||||
import hasSongList from '../../../mixins/has-song-list';
|
||||
import artistAlbumDetails from '../../../mixins/artist-album-details';
|
||||
import albumInfo from '../extra/album-info.vue';
|
||||
import soundBar from '../../shared/sound-bar.vue';
|
||||
|
||||
export default {
|
||||
name: 'main-wrapper--main-content--album',
|
||||
mixins: [hasSongList, artistAlbumDetails],
|
||||
mixins: [hasSongList],
|
||||
components: { albumInfo, soundBar },
|
||||
filters: { pluralize },
|
||||
|
||||
|
|
|
@ -58,9 +58,10 @@
|
|||
<script>
|
||||
import isMobile from 'ismobilejs';
|
||||
|
||||
import { pluralize, event, loadMainView } from '../../../utils';
|
||||
import { pluralize, event } from '../../../utils';
|
||||
import { playlistStore, sharedStore } from '../../../stores';
|
||||
import { playback, download } from '../../../services';
|
||||
import router from '../../../router';
|
||||
import hasSongList from '../../../mixins/has-song-list';
|
||||
|
||||
export default {
|
||||
|
@ -109,8 +110,8 @@ export default {
|
|||
// any property reference error.
|
||||
this.playlist = playlistStore.stub;
|
||||
|
||||
// Switch back to Queue screen
|
||||
loadMainView('queue');
|
||||
// Switch back to Home screen
|
||||
router.go('/#!/home');
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
<script>
|
||||
import { settingStore } from '../../../stores';
|
||||
import { parseValidationError, forceReloadWindow, event, showOverlay, hideOverlay } from '../../../utils';
|
||||
import router from '../../../router';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
@ -42,6 +43,8 @@ export default {
|
|||
showOverlay();
|
||||
|
||||
settingStore.update().then(() => {
|
||||
// Make sure we're back to home first.
|
||||
router.go('/#!/home');
|
||||
forceReloadWindow();
|
||||
}).catch(r => {
|
||||
let msg = 'Unknown error.';
|
||||
|
|
|
@ -5,28 +5,24 @@
|
|||
|
||||
<ul class="menu">
|
||||
<li>
|
||||
<a class="home" :class="[currentView == 'home' ? 'active' : '']"
|
||||
@click.prevent="loadMainView('home')">Home</a>
|
||||
<a class="home" :class="[currentView == 'home' ? 'active' : '']" href="/#!/home">Home</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="queue"
|
||||
:class="[currentView == 'queue' ? 'active' : '']"
|
||||
@click.prevent="loadMainView('queue')"
|
||||
href="/#!/queue"
|
||||
@dragleave="removeDroppableState"
|
||||
@dragover.prevent="allowDrop"
|
||||
@drop.stop.prevent="handleDrop">Current Queue</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="songs" :class="[currentView == 'songs' ? 'active' : '']"
|
||||
@click.prevent="loadMainView('songs')">All Songs</a>
|
||||
<a class="songs" :class="[currentView == 'songs' ? 'active' : '']" href="/#!/songs">All Songs</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="albums" :class="[currentView == 'albums' ? 'active' : '']"
|
||||
@click.prevent="loadMainView('albums')">Albums</a>
|
||||
<a class="albums" :class="[currentView == 'albums' ? 'active' : '']" href="/#!/albums">Albums</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="artists" :class="[currentView == 'artists' ? 'active' : '']"
|
||||
@click.prevent="loadMainView('artists')">Artists</a>
|
||||
<a class="artists" :class="[currentView == 'artists' ? 'active' : '']" href="/#!/artists">Artists</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
@ -38,12 +34,10 @@
|
|||
|
||||
<ul class="menu">
|
||||
<li>
|
||||
<a class="settings" :class="[currentView == 'settings' ? 'active' : '']"
|
||||
@click.prevent="loadMainView('settings')">Settings</a>
|
||||
</li>
|
||||
<a class="settings" :class="[currentView == 'settings' ? 'active' : '']" href="/#!/settings">Settings</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="users" :class="[currentView == 'users' ? 'active' : '']"
|
||||
@click.prevent="loadMainView('users')">Users</a>
|
||||
<a class="users" :class="[currentView == 'users' ? 'active' : '']" href="/#!/users">Users</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<li @dblclick.prevent="edit" class="playlist" :class="[type, editing ? 'editing' : '']">
|
||||
<a @click.prevent="load"
|
||||
<a :href="isFavorites ? '/#!/favorites' : '/#!/playlist/' + playlist.id"
|
||||
@dragleave="removeDroppableState"
|
||||
@dragover.prevent="allowDrop"
|
||||
@drop.stop.prevent="handleDrop"
|
||||
|
@ -21,7 +21,7 @@
|
|||
<script>
|
||||
import $ from 'jquery';
|
||||
|
||||
import { event, loadPlaylistView, loadFavoritesView } from '../../../utils';
|
||||
import { event } from '../../../utils';
|
||||
import { songStore, playlistStore, favoriteStore } from '../../../stores';
|
||||
|
||||
export default {
|
||||
|
@ -47,17 +47,6 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Load songs in the current playlist.
|
||||
*/
|
||||
load() {
|
||||
if (this.isFavorites) {
|
||||
loadFavoritesView();
|
||||
} else {
|
||||
loadPlaylistView(this.playlist);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the form to edit the playlist.
|
||||
*/
|
||||
|
|
|
@ -29,8 +29,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { loadPlaylistView } from '../../../utils';
|
||||
import { playlistStore, favoriteStore } from '../../../stores';
|
||||
import router from '../../../router';
|
||||
|
||||
import playlistItem from './playlist-item.vue';
|
||||
|
||||
|
@ -58,7 +58,7 @@ export default {
|
|||
playlistStore.store(this.newName).then(p => {
|
||||
this.newName = '';
|
||||
// Activate the new playlist right away
|
||||
this.$nextTick(() => loadPlaylistView(p));
|
||||
this.$nextTick(() => router.go(`/#!/playlist/${p.id}`));
|
||||
});
|
||||
},
|
||||
},
|
||||
|
|
|
@ -28,8 +28,9 @@
|
|||
<script>
|
||||
import { assign } from 'lodash';
|
||||
|
||||
import { pluralize, event, loadPlaylistView } from '../../utils';
|
||||
import { pluralize, event } from '../../utils';
|
||||
import { playlistStore } from '../../stores';
|
||||
import router from '../../router';
|
||||
import songMenuMethods from '../../mixins/song-menu-methods';
|
||||
|
||||
export default {
|
||||
|
@ -72,7 +73,7 @@ export default {
|
|||
playlistStore.store(this.newPlaylistName, this.songs).then(p => {
|
||||
this.newPlaylistName = '';
|
||||
// Activate the new playlist right away
|
||||
this.$nextTick(() => loadPlaylistView(p));
|
||||
this.$nextTick(() => router.go(`/#!/playlist/${p.id}`));
|
||||
});
|
||||
|
||||
this.close();
|
||||
|
|
|
@ -7,12 +7,9 @@
|
|||
</span>
|
||||
<footer>
|
||||
<div class="info">
|
||||
<a class="name" @click.prevent="viewAlbumDetails(album)">{{ album.name }}</a>
|
||||
<a class="name" :href="'/#!/album/' + album.id">{{ album.name }}</a>
|
||||
<span class="sep">by</span>
|
||||
<a class="artist" v-if="isNormalArtist"
|
||||
@click.prevent="viewArtistDetails(album.artist)">
|
||||
{{ album.artist.name }}
|
||||
</a>
|
||||
<a class="artist" v-if="isNormalArtist" :href="'/#!/artist/' + album.artist.id">{{ album.artist.name }}</a>
|
||||
<span class="artist nope" v-else>{{ album.artist.name }}</span>
|
||||
</div>
|
||||
<p class="meta">
|
||||
|
@ -43,12 +40,10 @@ import $ from 'jquery';
|
|||
import { pluralize } from '../../utils';
|
||||
import { queueStore, artistStore, sharedStore } from '../../stores';
|
||||
import { playback, download } from '../../services';
|
||||
import artistAlbumDetails from '../../mixins/artist-album-details';
|
||||
|
||||
export default {
|
||||
name: 'shared--album-item',
|
||||
props: ['album'],
|
||||
mixins: [artistAlbumDetails],
|
||||
filters: { pluralize },
|
||||
|
||||
data() {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</span>
|
||||
<footer>
|
||||
<div class="info">
|
||||
<a class="name" @click.prevent="viewArtistDetails(artist)">{{ artist.name }}</a>
|
||||
<a class="name" :href="'/#!/artist/' + artist.id">{{ artist.name }}</a>
|
||||
</div>
|
||||
<p class="meta">
|
||||
<span class="left">
|
||||
|
@ -34,12 +34,10 @@ import $ from 'jquery';
|
|||
import { pluralize } from '../../utils';
|
||||
import { artistStore, queueStore, sharedStore } from '../../stores';
|
||||
import { playback, download } from '../../services';
|
||||
import artistAlbumDetails from '../../mixins/artist-album-details';
|
||||
|
||||
export default {
|
||||
name: 'shared--artist-item',
|
||||
props: ['artist'],
|
||||
mixins: [artistAlbumDetails],
|
||||
filters: { pluralize },
|
||||
|
||||
data() {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
class="play-count"></span>
|
||||
{{ song.title }}
|
||||
<span class="by">
|
||||
<a href="#" @click.prevent="viewArtistDetails(song.artist)">{{ song.artist.name }}</a> -
|
||||
<a :href="'/#!/artist/' + song.artist.id">{{ song.artist.name }}</a> -
|
||||
{{ song.playCount | pluralize('play') }}
|
||||
</span>
|
||||
</span>
|
||||
|
@ -25,12 +25,10 @@
|
|||
import { pluralize } from '../../utils';
|
||||
import { queueStore } from '../../stores';
|
||||
import { playback } from '../../services';
|
||||
import artistAlbumDetails from '../../mixins/artist-album-details';
|
||||
|
||||
export default {
|
||||
name: 'shared--home-song-item',
|
||||
props: ['song', 'topPlayCount'],
|
||||
mixins: [artistAlbumDetails],
|
||||
filters: { pluralize },
|
||||
|
||||
methods: {
|
||||
|
|
|
@ -33,16 +33,16 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
import songMenuMethods from '../../mixins/song-menu-methods';
|
||||
import artistAlbumDetails from '../../mixins/artist-album-details';
|
||||
|
||||
import { event, isClipboardSupported, copyText } from '../../utils';
|
||||
import { sharedStore, songStore, queueStore, userStore, playlistStore } from '../../stores';
|
||||
import { playback, download } from '../../services';
|
||||
import router from '../../router';
|
||||
|
||||
export default {
|
||||
name: 'song-menu',
|
||||
props: ['songs'],
|
||||
mixins: [songMenuMethods, artistAlbumDetails],
|
||||
mixins: [songMenuMethods],
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
@ -128,6 +128,22 @@ export default {
|
|||
this.close();
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the album details screen.
|
||||
*/
|
||||
viewAlbumDetails(album) {
|
||||
router.go(`/#!/album/${album.id}`);
|
||||
this.close();
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the artist details screen.
|
||||
*/
|
||||
viewArtistDetails(artist) {
|
||||
router.go(`/#!/artist/${artist.id}`);
|
||||
this.close();
|
||||
},
|
||||
|
||||
download() {
|
||||
download.fromSongs(this.songs);
|
||||
this.close();
|
||||
|
|
|
@ -50,8 +50,8 @@
|
|||
<script>
|
||||
import { clone, assign } from 'lodash';
|
||||
|
||||
import { loadMainView } from '../../utils';
|
||||
import { userStore } from '../../stores';
|
||||
import router from '../../router';
|
||||
|
||||
export default {
|
||||
props: ['user'],
|
||||
|
@ -82,7 +82,7 @@ export default {
|
|||
*/
|
||||
edit() {
|
||||
if (this.isCurrentUser) {
|
||||
loadMainView('profile');
|
||||
router.go('/#!/profile');
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import Vue from 'vue';
|
|||
|
||||
import { event } from './utils';
|
||||
import { http } from './services';
|
||||
|
||||
/**
|
||||
* For Ancelot, the ancient cross of war
|
||||
* for the holy town of Gods
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
/**
|
||||
* Add necessary functionalities into a view that triggers artist or album details views.
|
||||
*/
|
||||
|
||||
import { loadAlbumView, loadArtistView } from './../utils';
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
/**
|
||||
* Load the album details screen.
|
||||
*/
|
||||
viewAlbumDetails(album) {
|
||||
loadAlbumView(album);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the artist details screen.
|
||||
*/
|
||||
viewArtistDetails(artist) {
|
||||
loadArtistView(artist);
|
||||
},
|
||||
},
|
||||
};
|
103
resources/assets/js/router.js
Normal file
103
resources/assets/js/router.js
Normal file
|
@ -0,0 +1,103 @@
|
|||
import { loadMainView } from './utils';
|
||||
import { artistStore, albumStore, songStore, playlistStore } from './stores';
|
||||
|
||||
export default {
|
||||
routes: {
|
||||
'/home'() {
|
||||
loadMainView('home');
|
||||
},
|
||||
|
||||
'/queue'() {
|
||||
loadMainView('queue');
|
||||
},
|
||||
|
||||
'/songs'() {
|
||||
loadMainView('songs');
|
||||
},
|
||||
|
||||
'/albums'() {
|
||||
loadMainView('albums');
|
||||
},
|
||||
|
||||
'/album/(\\d+)'(id) {
|
||||
const album = albumStore.byId(Number.parseInt(id, 10));
|
||||
if (album) {
|
||||
loadMainView('album', album);
|
||||
}
|
||||
},
|
||||
|
||||
'/artists'() {
|
||||
loadMainView('artists');
|
||||
},
|
||||
|
||||
'/artist/(\\d+)'(id) {
|
||||
const artist = artistStore.byId(Number.parseInt(id, 10));
|
||||
if (artist) {
|
||||
loadMainView('artist', artist);
|
||||
}
|
||||
},
|
||||
|
||||
'/favorites'() {
|
||||
loadMainView('favorites');
|
||||
},
|
||||
|
||||
'/playlist/(\\d+)'(id) {
|
||||
const playlist = playlistStore.byId(Number.parseInt(id, 10));
|
||||
if (playlist) {
|
||||
loadMainView('playlist', playlist);
|
||||
}
|
||||
},
|
||||
|
||||
'/settings'() {
|
||||
loadMainView('settings');
|
||||
},
|
||||
|
||||
'/users'() {
|
||||
loadMainView('users');
|
||||
},
|
||||
|
||||
'/profile'() {
|
||||
loadMainView('profile');
|
||||
},
|
||||
|
||||
'/login'() {
|
||||
|
||||
},
|
||||
|
||||
'/song/([a-z0-9]{32})'(id) {
|
||||
console.log(id)
|
||||
},
|
||||
},
|
||||
|
||||
init() {
|
||||
this.loadState();
|
||||
window.addEventListener('popstate', () => this.loadState(), true);
|
||||
},
|
||||
|
||||
loadState() {
|
||||
if (!window.location.hash) {
|
||||
return this.go('/#!/home');
|
||||
}
|
||||
|
||||
Object.keys(this.routes).forEach(route => {
|
||||
const matches = window.location.hash.match(new RegExp(`^#!${route}$`));
|
||||
if (matches) {
|
||||
const [, ...params] = matches;
|
||||
this.routes[route](...params);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Navigate to a (relative) path.
|
||||
*
|
||||
* @param {String} path
|
||||
*/
|
||||
go(path) {
|
||||
if (path[0] !== '/') {
|
||||
path = `/${path}`;
|
||||
}
|
||||
document.location.href = `${document.location.origin}${path}`;
|
||||
},
|
||||
};
|
|
@ -3,9 +3,10 @@ import $ from 'jquery';
|
|||
import plyr from 'plyr';
|
||||
import Vue from 'vue';
|
||||
|
||||
import { event, loadMainView } from '../utils';
|
||||
import { event } from '../utils';
|
||||
import { queueStore, sharedStore, userStore, songStore, artistStore, preferenceStore as preferences } from '../stores';
|
||||
import config from '../config';
|
||||
import router from '../router';
|
||||
|
||||
export const playback = {
|
||||
player: null,
|
||||
|
@ -347,11 +348,12 @@ export const playback = {
|
|||
|
||||
queueStore.queue(songs, true);
|
||||
|
||||
loadMainView('queue');
|
||||
|
||||
// Wrap this inside a nextTick() to wait for the DOM to complete updating
|
||||
// and then play the first song in the queue.
|
||||
Vue.nextTick(() => this.play(queueStore.first));
|
||||
Vue.nextTick(() => {
|
||||
router.go('/#!/queue');
|
||||
this.play(queueStore.first);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue