diff --git a/changes/changelog.d/594.feature b/changes/changelog.d/594.feature
new file mode 100644
index 000000000..3ab7ccdd9
--- /dev/null
+++ b/changes/changelog.d/594.feature
@@ -0,0 +1 @@
+Brand new navigation, queue and player redesign (#594)
diff --git a/changes/notes.rst b/changes/notes.rst
index 1323a70ee..3ffbcfd25 100644
--- a/changes/notes.rst
+++ b/changes/notes.rst
@@ -6,6 +6,13 @@ Next release notes
Those release notes refer to the current development branch and are reset
after each release.
+Redesigned navigation, player and queue
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This release includes a full redesign of our navigation, player and queue. Overall, it should provide
+a better, less confusing experience, especially on mobile devices. This redesign was suggested
+14 months ago, and took a while, but thanks to the involvement and feedback of many people, we got it done!
+
Improved search performance
^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/front/package.json b/front/package.json
index c6b71944e..2d24fb952 100644
--- a/front/package.json
+++ b/front/package.json
@@ -25,7 +25,7 @@
"qs": "^6.7.0",
"sanitize-html": "^1.20.1",
"showdown": "^1.8.6",
- "vue": "^2.5.17",
+ "vue": "^2.6.10",
"vue-gettext": "^2.1.0",
"vue-lazyload": "^1.2.6",
"vue-masonry": "^0.11.5",
@@ -50,6 +50,7 @@
"mocha": "^5.2.0",
"moxios": "^0.4.0",
"node-sass": "^4.9.3",
+ "preload-webpack-plugin": "^3.0.0-beta.4",
"purgecss-webpack-plugin": "^1.6.0",
"sass-loader": "^7.1.0",
"sinon": "^6.1.5",
diff --git a/front/public/index.html b/front/public/index.html
index 142419ca6..42adc6d41 100644
--- a/front/public/index.html
+++ b/front/public/index.html
@@ -7,13 +7,85 @@
Funkwhale
+
-
- We're sorry but Funkwhale doesn't work properly without JavaScript enabled. Please enable it to continue.
-
-
+
+
+
+
+ We're sorry but Funkwhale doesn't work properly without JavaScript enabled. Please enable it to continue.
+
+
Loading Funkwhale…
+
+
+
+
+
+
+
+
+
diff --git a/front/src/App.vue b/front/src/App.vue
index 5be97dfb5..c1fee631b 100644
--- a/front/src/App.vue
+++ b/front/src/App.vue
@@ -1,5 +1,5 @@
-
+
-
-
+
+
+
+
+
import(/* webpackChunkName: "audio" */ "@/components/audio/Player"),
+ Queue: () => import(/* webpackChunkName: "audio" */ "@/components/Queue"),
+ PlaylistModal: () => import(/* webpackChunkName: "auth-audio" */ "@/components/playlists/PlaylistModal"),
+ Sidebar: () => import(/* webpackChunkName: "core" */ "@/components/Sidebar"),
+ AppFooter: () => import(/* webpackChunkName: "core" */ "@/components/Footer"),
+ ServiceMessages: () => import(/* webpackChunkName: "core" */ "@/components/ServiceMessages"),
+ SetInstanceModal: () => import(/* webpackChunkName: "core" */ "@/components/SetInstanceModal"),
+ ShortcutsModal: () => import(/* webpackChunkName: "core" */ "@/components/ShortcutsModal"),
+ FilterModal: () => import(/* webpackChunkName: "moderation" */ "@/components/moderation/FilterModal"),
+ ReportModal: () => import(/* webpackChunkName: "moderation" */ "@/components/moderation/ReportModal"),
GlobalEvents,
- ServiceMessages,
- SetInstanceModal,
},
data () {
return {
bridge: null,
instanceUrl: null,
showShortcutsModal: false,
- showSetInstanceModal: false,
+ showSetInstanceModal: false
}
},
async created () {
@@ -82,6 +80,10 @@ export default {
if (serverUrl) {
this.$store.commit('instance/instanceUrl', serverUrl)
}
+ const url = urlParams.get('_url')
+ if (url) {
+ this.$router.replace(url)
+ }
else if (!this.$store.state.instance.instanceUrl) {
// we have several way to guess the API server url. By order of precedence:
// 1. use the url provided in settings.json, if any
@@ -127,6 +129,9 @@ export default {
self.$router.push(event.target.getAttribute('href'))
event.preventDefault();
}, false);
+ this.$nextTick(() => {
+ document.getElementById('fake-content').classList.add('loaded')
+ })
},
destroyed () {
@@ -238,10 +243,27 @@ export default {
...mapState({
messages: state => state.ui.messages,
nodeinfo: state => state.instance.nodeinfo,
+ playing: state => state.player.playing,
+ bufferProgress: state => state.player.bufferProgress,
+ isLoadingAudio: state => state.player.isLoadingAudio,
}),
...mapGetters({
- currentTrack: 'queue/currentTrack'
+ hasNext: "queue/hasNext",
+ currentTrack: 'queue/currentTrack',
+ progress: "player/progress",
}),
+ labels() {
+ let play = this.$pgettext('Sidebar/Player/Icon.Tooltip/Verb', "Play track")
+ let pause = this.$pgettext('Sidebar/Player/Icon.Tooltip/Verb', "Pause track")
+ let next = this.$pgettext('Sidebar/Player/Icon.Tooltip', "Next track")
+ let expandQueue = this.$pgettext('Sidebar/Player/Icon.Tooltip/Verb', "Expand queue")
+ return {
+ play,
+ pause,
+ next,
+ expandQueue,
+ }
+ },
suggestedInstances () {
let instances = this.$store.state.instance.knownInstances.slice(0)
if (this.$store.state.instance.frontSettings.defaultServerUrl) {
@@ -264,7 +286,7 @@ export default {
if (this.$store.state.instance.frontSettings) {
return this.$store.state.instance.frontSettings.additionalStylesheets || []
}
- }
+ },
},
watch: {
'$store.state.instance.instanceUrl' () {
@@ -290,7 +312,7 @@ export default {
immediate: true,
handler(newValue) {
let self = this
- import(`./translations/${newValue}.json`).then((response) =>{
+ import(/* webpackChunkName: "locale-[request]" */ `./translations/${newValue}.json`).then((response) =>{
Vue.$translations[newValue] = response.default[newValue]
}).finally(() => {
// set current language twice, otherwise we seem to have a cache somewhere
@@ -302,12 +324,12 @@ export default {
return self.$store.commit('ui/momentLocale', 'en')
}
let momentLocale = newValue.replace('_', '-').toLowerCase()
- import(`moment/locale/${momentLocale}.js`).then(() => {
+ import(/* webpackChunkName: "moment-locale-[request]" */ `moment/locale/${momentLocale}.js`).then(() => {
self.$store.commit('ui/momentLocale', momentLocale)
}).catch(() => {
console.log('No momentjs locale available for', momentLocale)
let shortLocale = momentLocale.split('-')[0]
- import(`moment/locale/${shortLocale}.js`).then(() => {
+ import(/* webpackChunkName: "moment-locale-[request]" */ `moment/locale/${shortLocale}.js`).then(() => {
self.$store.commit('ui/momentLocale', shortLocale)
}).catch(() => {
console.log('No momentjs locale available for', shortLocale)
@@ -333,4 +355,185 @@ export default {
diff --git a/front/src/assets/logo/text-white.svg b/front/src/assets/logo/text-white.svg
new file mode 100644
index 000000000..f812c7c99
--- /dev/null
+++ b/front/src/assets/logo/text-white.svg
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/front/src/components/Queue.vue b/front/src/components/Queue.vue
new file mode 100644
index 000000000..f03b12c62
--- /dev/null
+++ b/front/src/components/Queue.vue
@@ -0,0 +1,576 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The next track will play automatically in a few seconds…
+
+
+
+ You may have a connectivity issue.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{currentTimeFormatted}}
+ {{durationFormatted}}
+
+
+ 00:00
+ 00:00
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ track.title }}
+
+ {{ track.artist.name }}
+
+
+
+
+
+ {{ time.durationFormatted(track.uploads[0].duration) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/front/src/components/ShortcutsModal.vue b/front/src/components/ShortcutsModal.vue
index 999d24dd3..097672f2c 100644
--- a/front/src/components/ShortcutsModal.vue
+++ b/front/src/components/ShortcutsModal.vue
@@ -42,12 +42,11 @@
@@ -331,16 +339,24 @@ export default {
diff --git a/front/src/components/audio/SearchBar.vue b/front/src/components/audio/SearchBar.vue
index ae2ae08fb..ed18805aa 100644
--- a/front/src/components/audio/SearchBar.vue
+++ b/front/src/components/audio/SearchBar.vue
@@ -1,7 +1,7 @@