Merge branch 'webpack-tweaking' into 'develop'
Improve front-end performance See merge request funkwhale/funkwhale!499
This commit is contained in:
commit
e1450d28b8
|
|
@ -93,5 +93,6 @@ po/*.po
|
||||||
docs/swagger
|
docs/swagger
|
||||||
_build
|
_build
|
||||||
front/src/translations.json
|
front/src/translations.json
|
||||||
|
front/src/translations/*.json
|
||||||
front/locales/en_US/LC_MESSAGES/app.po
|
front/locales/en_US/LC_MESSAGES/app.po
|
||||||
*.prof
|
*.prof
|
||||||
|
|
|
||||||
|
|
@ -58,15 +58,6 @@ class RavenDSN(types.StringPreference):
|
||||||
field_kwargs = {"required": False}
|
field_kwargs = {"required": False}
|
||||||
|
|
||||||
|
|
||||||
@global_preferences_registry.register
|
|
||||||
class RavenEnabled(types.BooleanPreference):
|
|
||||||
show_in_api = True
|
|
||||||
section = raven
|
|
||||||
name = "front_enabled"
|
|
||||||
default = False
|
|
||||||
verbose_name = "Report front-end errors with Raven"
|
|
||||||
|
|
||||||
|
|
||||||
@global_preferences_registry.register
|
@global_preferences_registry.register
|
||||||
class InstanceNodeinfoEnabled(types.BooleanPreference):
|
class InstanceNodeinfoEnabled(types.BooleanPreference):
|
||||||
show_in_api = False
|
show_in_api = False
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
Improved front-end performance by stripping unused dependencies, reducing bundle size
|
||||||
|
and enabling gzip compression
|
||||||
|
|
||||||
|
Enable gzip compression [manual action suggested]
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
Gzip compression will be enabled on new instances by default
|
||||||
|
and will reduce the amount of bandwidth consumed by your instance.
|
||||||
|
|
||||||
|
If you with to benefit from gzip compression on your instance,
|
||||||
|
edit your reverse proxy virtualhost file (located at ``/etc/nginx/sites-available/funkwhale.conf``) and add the following snippet
|
||||||
|
in the server block, then reload your nginx server::
|
||||||
|
|
||||||
|
server {
|
||||||
|
# ... exiting configuration
|
||||||
|
|
||||||
|
# compression settings
|
||||||
|
gzip on;
|
||||||
|
gzip_comp_level 5;
|
||||||
|
gzip_min_length 256;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_vary on;
|
||||||
|
|
||||||
|
gzip_types
|
||||||
|
application/atom+xml
|
||||||
|
application/javascript
|
||||||
|
application/json
|
||||||
|
application/ld+json
|
||||||
|
application/activity+json
|
||||||
|
application/manifest+json
|
||||||
|
application/rss+xml
|
||||||
|
application/vnd.geo+json
|
||||||
|
application/vnd.ms-fontobject
|
||||||
|
application/x-font-ttf
|
||||||
|
application/x-web-app-manifest+json
|
||||||
|
application/xhtml+xml
|
||||||
|
application/xml
|
||||||
|
font/opentype
|
||||||
|
image/bmp
|
||||||
|
image/svg+xml
|
||||||
|
image/x-icon
|
||||||
|
text/cache-manifest
|
||||||
|
text/css
|
||||||
|
text/plain
|
||||||
|
text/vcard
|
||||||
|
text/vnd.rim.location.xloc
|
||||||
|
text/vtt
|
||||||
|
text/x-component
|
||||||
|
text/x-cross-domain-policy;
|
||||||
|
# end of compression settings
|
||||||
|
}
|
||||||
|
|
@ -29,6 +29,40 @@ server {
|
||||||
# HSTS
|
# HSTS
|
||||||
add_header Strict-Transport-Security "max-age=31536000";
|
add_header Strict-Transport-Security "max-age=31536000";
|
||||||
|
|
||||||
|
# compression settings
|
||||||
|
gzip on;
|
||||||
|
gzip_comp_level 5;
|
||||||
|
gzip_min_length 256;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_vary on;
|
||||||
|
|
||||||
|
gzip_types
|
||||||
|
application/atom+xml
|
||||||
|
application/javascript
|
||||||
|
application/json
|
||||||
|
application/ld+json
|
||||||
|
application/activity+json
|
||||||
|
application/manifest+json
|
||||||
|
application/rss+xml
|
||||||
|
application/vnd.geo+json
|
||||||
|
application/vnd.ms-fontobject
|
||||||
|
application/x-font-ttf
|
||||||
|
application/x-web-app-manifest+json
|
||||||
|
application/xhtml+xml
|
||||||
|
application/xml
|
||||||
|
font/opentype
|
||||||
|
image/bmp
|
||||||
|
image/svg+xml
|
||||||
|
image/x-icon
|
||||||
|
text/cache-manifest
|
||||||
|
text/css
|
||||||
|
text/plain
|
||||||
|
text/vcard
|
||||||
|
text/vnd.rim.location.xloc
|
||||||
|
text/vtt
|
||||||
|
text/x-component
|
||||||
|
text/x-cross-domain-policy;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
include /etc/nginx/funkwhale_proxy.conf;
|
include /etc/nginx/funkwhale_proxy.conf;
|
||||||
proxy_pass http://fw/;
|
proxy_pass http://fw/;
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,41 @@ server {
|
||||||
|
|
||||||
root ${FUNKWHALE_FRONTEND_PATH};
|
root ${FUNKWHALE_FRONTEND_PATH};
|
||||||
|
|
||||||
|
# compression settings
|
||||||
|
gzip on;
|
||||||
|
gzip_comp_level 5;
|
||||||
|
gzip_min_length 256;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_vary on;
|
||||||
|
|
||||||
|
gzip_types
|
||||||
|
application/atom+xml
|
||||||
|
application/javascript
|
||||||
|
application/json
|
||||||
|
application/ld+json
|
||||||
|
application/activity+json
|
||||||
|
application/manifest+json
|
||||||
|
application/rss+xml
|
||||||
|
application/vnd.geo+json
|
||||||
|
application/vnd.ms-fontobject
|
||||||
|
application/x-font-ttf
|
||||||
|
application/x-web-app-manifest+json
|
||||||
|
application/xhtml+xml
|
||||||
|
application/xml
|
||||||
|
font/opentype
|
||||||
|
image/bmp
|
||||||
|
image/svg+xml
|
||||||
|
image/x-icon
|
||||||
|
text/cache-manifest
|
||||||
|
text/css
|
||||||
|
text/plain
|
||||||
|
text/vcard
|
||||||
|
text/vnd.rim.location.xloc
|
||||||
|
text/vtt
|
||||||
|
text/x-component
|
||||||
|
text/x-cross-domain-policy;
|
||||||
|
|
||||||
|
# end of compression settings
|
||||||
location / {
|
location / {
|
||||||
include /etc/nginx/funkwhale_proxy.conf;
|
include /etc/nginx/funkwhale_proxy.conf;
|
||||||
# this is needed if you have file import via upload enabled
|
# this is needed if you have file import via upload enabled
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,39 @@ http {
|
||||||
charset utf-8;
|
charset utf-8;
|
||||||
client_max_body_size 30M;
|
client_max_body_size 30M;
|
||||||
include /etc/nginx/funkwhale_proxy.conf;
|
include /etc/nginx/funkwhale_proxy.conf;
|
||||||
|
# compression settings
|
||||||
|
gzip on;
|
||||||
|
gzip_comp_level 5;
|
||||||
|
gzip_min_length 256;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_vary on;
|
||||||
|
|
||||||
|
gzip_types
|
||||||
|
application/atom+xml
|
||||||
|
application/javascript
|
||||||
|
application/json
|
||||||
|
application/ld+json
|
||||||
|
application/activity+json
|
||||||
|
application/manifest+json
|
||||||
|
application/rss+xml
|
||||||
|
application/vnd.geo+json
|
||||||
|
application/vnd.ms-fontobject
|
||||||
|
application/x-font-ttf
|
||||||
|
application/x-web-app-manifest+json
|
||||||
|
application/xhtml+xml
|
||||||
|
application/xml
|
||||||
|
font/opentype
|
||||||
|
image/bmp
|
||||||
|
image/svg+xml
|
||||||
|
image/x-icon
|
||||||
|
text/cache-manifest
|
||||||
|
text/css
|
||||||
|
text/plain
|
||||||
|
text/vcard
|
||||||
|
text/vnd.rim.location.xloc
|
||||||
|
text/vtt
|
||||||
|
text/x-component
|
||||||
|
text/x-cross-domain-policy;
|
||||||
|
|
||||||
location /front/ {
|
location /front/ {
|
||||||
proxy_pass http://funkwhale-front/front/;
|
proxy_pass http://funkwhale-front/front/;
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ work. We will store the necessary files in the ``/srv/funkwhale/custom`` directo
|
||||||
mkdir custom
|
mkdir custom
|
||||||
cat <<EOF > custom/settings.json
|
cat <<EOF > custom/settings.json
|
||||||
{
|
{
|
||||||
"additionalStylesheets": ["/custom/custom.css"]
|
"additionalStylesheets": ["/front/custom/custom.css"]
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
cat <<EOF > custom/custom.css
|
cat <<EOF > custom/custom.css
|
||||||
|
|
@ -194,7 +194,7 @@ work. We will store the necessary files in the ``/srv/funkwhale/custom`` directo
|
||||||
By executing the previous commands, you will end up with two files in your ``/srv/funkwhale/custom``
|
By executing the previous commands, you will end up with two files in your ``/srv/funkwhale/custom``
|
||||||
directory:
|
directory:
|
||||||
|
|
||||||
- ``settings.json`` will tell the front-end what stylesheets you want to load (``/custom/custom.css`` in this example)
|
- ``settings.json`` will tell the front-end what stylesheets you want to load (``/front/custom/custom.css`` in this example)
|
||||||
- ``custom.css`` will hold your custom CSS
|
- ``custom.css`` will hold your custom CSS
|
||||||
|
|
||||||
The last step to make this work is to ensure both files are served by the reverse proxy.
|
The last step to make this work is to ensure both files are served by the reverse proxy.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "scripts/i18n-compile.sh && vue-cli-service serve --port ${VUE_PORT:-8000} --host ${VUE_HOST:-0.0.0.0}",
|
"serve": "vue-cli-service serve --port ${VUE_PORT:-8000} --host ${VUE_HOST:-0.0.0.0}",
|
||||||
"build": "scripts/i18n-compile.sh && vue-cli-service build",
|
"build": "scripts/i18n-compile.sh && vue-cli-service build",
|
||||||
"lint": "vue-cli-service lint",
|
"lint": "vue-cli-service lint",
|
||||||
"i18n-extract": "scripts/i18n-extract.sh",
|
"i18n-extract": "scripts/i18n-extract.sh",
|
||||||
|
|
@ -20,8 +20,7 @@
|
||||||
"lodash": "^4.17.10",
|
"lodash": "^4.17.10",
|
||||||
"masonry-layout": "^4.2.2",
|
"masonry-layout": "^4.2.2",
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.22.2",
|
||||||
"raven-js": "^3.26.4",
|
"semantic-ui-css": "^2.4.1",
|
||||||
"semantic-ui-css": "^2.3.3",
|
|
||||||
"showdown": "^1.8.6",
|
"showdown": "^1.8.6",
|
||||||
"vue": "^2.5.17",
|
"vue": "^2.5.17",
|
||||||
"vue-gettext": "^2.1.0",
|
"vue-gettext": "^2.1.0",
|
||||||
|
|
@ -49,7 +48,8 @@
|
||||||
"node-sass": "^4.9.3",
|
"node-sass": "^4.9.3",
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^7.1.0",
|
||||||
"sinon": "^6.1.5",
|
"sinon": "^6.1.5",
|
||||||
"vue-template-compiler": "^2.5.17"
|
"vue-template-compiler": "^2.5.17",
|
||||||
|
"webpack-bundle-size-analyzer": "^3.0.0"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"root": true,
|
"root": true,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
{
|
{
|
||||||
"additionalStylesheets": ["/custom.css"]
|
"additionalStylesheets": [
|
||||||
|
"/front/custom.css"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
#!/bin/bash -eux
|
#!/bin/bash -eux
|
||||||
locales=$(tail -n +2 src/locales.js | sed -e 's/export default //' | jq '.locales[].code' | xargs echo)
|
locales=$(tail -n +2 src/locales.js | sed -e 's/export default //' | jq '.locales[].code' | grep -v 'en_US' | xargs echo)
|
||||||
find locales -name '*.po' | xargs $(yarn bin)/gettext-compile --output src/translations.json
|
for locale in $locales; do
|
||||||
|
find "locales/$locale" -name '*.po' | $(yarn bin)/gettext-compile locales/$locale/LC_MESSAGES/app.po --output src/translations/$locale.json
|
||||||
|
done
|
||||||
|
|
||||||
|
# find locales -name '*.po' | xargs $(yarn bin)/gettext-compile --output src/translations.json
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,6 @@
|
||||||
:version="version"
|
:version="version"
|
||||||
@show:shortcuts-modal="showShortcutsModal = !showShortcutsModal"
|
@show:shortcuts-modal="showShortcutsModal = !showShortcutsModal"
|
||||||
></app-footer>
|
></app-footer>
|
||||||
<raven
|
|
||||||
v-if="$store.state.instance.settings.raven.front_enabled.value"
|
|
||||||
:dsn="$store.state.instance.settings.raven.front_dsn.value"
|
|
||||||
></raven>
|
|
||||||
<playlist-modal v-if="$store.state.auth.authenticated"></playlist-modal>
|
<playlist-modal v-if="$store.state.auth.authenticated"></playlist-modal>
|
||||||
<shortcuts-modal @update:show="showShortcutsModal = $event" :show="showShortcutsModal"></shortcuts-modal>
|
<shortcuts-modal @update:show="showShortcutsModal = $event" :show="showShortcutsModal"></shortcuts-modal>
|
||||||
<GlobalEvents @keydown.h.exact="showShortcutsModal = !showShortcutsModal"/>
|
<GlobalEvents @keydown.h.exact="showShortcutsModal = !showShortcutsModal"/>
|
||||||
|
|
@ -56,18 +52,16 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
import { WebSocketBridge } from 'django-channels'
|
import { WebSocketBridge } from 'django-channels'
|
||||||
import GlobalEvents from '@/components/utils/global-events'
|
import GlobalEvents from '@/components/utils/global-events'
|
||||||
|
|
||||||
import translations from '@/translations'
|
|
||||||
|
|
||||||
import Sidebar from '@/components/Sidebar'
|
import Sidebar from '@/components/Sidebar'
|
||||||
import AppFooter from '@/components/Footer'
|
import AppFooter from '@/components/Footer'
|
||||||
import Raven from '@/components/Raven'
|
|
||||||
import ServiceMessages from '@/components/ServiceMessages'
|
import ServiceMessages from '@/components/ServiceMessages'
|
||||||
|
|
||||||
|
import locales from './locales'
|
||||||
import PlaylistModal from '@/components/playlists/PlaylistModal'
|
import PlaylistModal from '@/components/playlists/PlaylistModal'
|
||||||
import ShortcutsModal from '@/components/ShortcutsModal'
|
import ShortcutsModal from '@/components/ShortcutsModal'
|
||||||
|
|
||||||
|
|
@ -76,7 +70,6 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
AppFooter,
|
AppFooter,
|
||||||
Raven,
|
|
||||||
PlaylistModal,
|
PlaylistModal,
|
||||||
ShortcutsModal,
|
ShortcutsModal,
|
||||||
GlobalEvents,
|
GlobalEvents,
|
||||||
|
|
@ -139,7 +132,7 @@ export default {
|
||||||
},
|
},
|
||||||
autodetectLanguage () {
|
autodetectLanguage () {
|
||||||
let userLanguage = navigator.language || navigator.userLanguage
|
let userLanguage = navigator.language || navigator.userLanguage
|
||||||
let available = _.keys(translations)
|
let available = locales.locales.map(e => { return e.code })
|
||||||
let matching = available.filter((a) => {
|
let matching = available.filter((a) => {
|
||||||
return userLanguage.replace('-', '_') === a
|
return userLanguage.replace('-', '_') === a
|
||||||
})
|
})
|
||||||
|
|
@ -223,181 +216,5 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
// we do the import here instead in main.js
|
@import "style/_main";
|
||||||
// as resolve order is not deterministric in webpack
|
|
||||||
// and we end up with CSS rules not applied,
|
|
||||||
// see https://github.com/webpack/webpack/issues/215
|
|
||||||
@import "semantic/semantic.css";
|
|
||||||
@import "style/vendor/media";
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
@include media("<desktop") {
|
|
||||||
font-size: 90%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#app {
|
|
||||||
font-family: "Avenir", Helvetica, Arial, sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance-chooser {
|
|
||||||
margin-top: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main.pusher,
|
|
||||||
.footer {
|
|
||||||
@include media(">desktop") {
|
|
||||||
margin-left: 350px !important;
|
|
||||||
margin-top: 50px;
|
|
||||||
}
|
|
||||||
transform: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main.pusher > .ui.secondary.menu {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
border: none;
|
|
||||||
box-shadow: inset 0px -2px 0px 0px rgba(34, 36, 38, 0.15);
|
|
||||||
.ui.item {
|
|
||||||
border: none;
|
|
||||||
border-bottom-style: none;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
&.active {
|
|
||||||
box-shadow: inset 0px -2px 0px 0px #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@include media(">tablet") {
|
|
||||||
padding: 0 2.5rem;
|
|
||||||
}
|
|
||||||
@include media(">desktop") {
|
|
||||||
position: fixed;
|
|
||||||
left: 350px;
|
|
||||||
right: 0px;
|
|
||||||
top: 0px;
|
|
||||||
z-index: 99;
|
|
||||||
}
|
|
||||||
background-color: white;
|
|
||||||
.item {
|
|
||||||
padding-top: 1.5em;
|
|
||||||
padding-bottom: 1.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-messages {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 1em;
|
|
||||||
left: 1em;
|
|
||||||
@include media(">desktop") {
|
|
||||||
left: 350px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.main-pusher {
|
|
||||||
padding: 1.5rem 0;
|
|
||||||
}
|
|
||||||
.ui.stripe.segment,
|
|
||||||
#footer {
|
|
||||||
padding: 2em;
|
|
||||||
@include media(">tablet") {
|
|
||||||
padding: 4em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ellipsis {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui.small.text.container {
|
|
||||||
max-width: 500px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button.icon.tiny {
|
|
||||||
padding: 0.5em !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
.logo {
|
|
||||||
path {
|
|
||||||
fill: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.discrete {
|
|
||||||
color: rgba(0, 0, 0, 0.87);
|
|
||||||
}
|
|
||||||
.link {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui.really.basic.button {
|
|
||||||
&:not(:focus) {
|
|
||||||
box-shadow: none !important;
|
|
||||||
background-color: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.floated.buttons .button ~ .dropdown {
|
|
||||||
border-left: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui.icon.header .circular.icon {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.segment-content .button {
|
|
||||||
margin: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.segment.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.reset {
|
|
||||||
border: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: auto;
|
|
||||||
overflow: visible;
|
|
||||||
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
/* inherit font & color from ancestor */
|
|
||||||
color: inherit;
|
|
||||||
font: inherit;
|
|
||||||
|
|
||||||
/* Normalize `line-height`. Cannot be changed from `normal` in Firefox 4+. */
|
|
||||||
line-height: normal;
|
|
||||||
|
|
||||||
/* Corrects font smoothing for webkit */
|
|
||||||
-webkit-font-smoothing: inherit;
|
|
||||||
-moz-osx-font-smoothing: inherit;
|
|
||||||
/* Corrects inability to style clickable `input` types in iOS */
|
|
||||||
-webkit-appearance: none;
|
|
||||||
text-align: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui.table > caption {
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 0.5em;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
[role="button"] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left.floated {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right.floated {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 21 KiB |
|
|
@ -27,9 +27,9 @@
|
||||||
<p>{{ instance.short_description.value }}</p>
|
<p>{{ instance.short_description.value }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="instance.long_description.value"
|
v-if="markdown && instance.long_description.value"
|
||||||
class="ui middle aligned stackable text container"
|
class="ui middle aligned stackable text container"
|
||||||
v-html="$options.filters.markdown(instance.long_description.value)">
|
v-html="markdown.makeHtml(instance.long_description.value)">
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
@ -43,8 +43,17 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
Stats
|
Stats
|
||||||
},
|
},
|
||||||
created() {
|
data () {
|
||||||
|
return {
|
||||||
|
markdown: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
this.$store.dispatch("instance/fetchSettings")
|
this.$store.dispatch("instance/fetchSettings")
|
||||||
|
let self = this
|
||||||
|
import('showdown').then(module => {
|
||||||
|
self.markdown = new module.default.Converter()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<div class="ui field">
|
<div class="ui field">
|
||||||
<label><translate>Change language</translate></label>
|
<label><translate>Change language</translate></label>
|
||||||
<select class="ui dropdown" v-model="$language.current">
|
<select class="ui dropdown" :value="$language.current" @change="updateLanguage($event.target.value)">
|
||||||
<option v-for="(language, key) in $language.available" :key="key" :value="key">{{ language }}</option>
|
<option v-for="(language, key) in $language.available" :key="key" :value="key">{{ language }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -60,7 +60,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Vue from "vue"
|
||||||
import { mapState } from "vuex"
|
import { mapState } from "vuex"
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ["version"],
|
props: ["version"],
|
||||||
|
|
@ -74,6 +76,13 @@ export default {
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
this.$store.commit("instance/instanceUrl", null)
|
this.$store.commit("instance/instanceUrl", null)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
updateLanguage(value) {
|
||||||
|
let self = this
|
||||||
|
import(`../translations/${value}.json`).then((response) =>{
|
||||||
|
Vue.$translations[value] = response.default[value]
|
||||||
|
self.$language.current = value
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from "lodash"
|
import _ from "@/lodash"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="raven"></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Raven from 'raven-js'
|
|
||||||
import RavenVue from 'raven-js/plugins/vue'
|
|
||||||
import Vue from 'vue'
|
|
||||||
import logger from '@/logging'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: ['dsn'],
|
|
||||||
created () {
|
|
||||||
Raven.uninstall()
|
|
||||||
this.setUp()
|
|
||||||
},
|
|
||||||
destroyed () {
|
|
||||||
Raven.uninstall()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setUp () {
|
|
||||||
Raven.uninstall()
|
|
||||||
logger.default.info('Installing raven...')
|
|
||||||
Raven.config(this.dsn).addPlugin(RavenVue, Vue).install()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
dsn: function () {
|
|
||||||
this.setUp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped >
|
|
||||||
.raven {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -16,8 +16,8 @@
|
||||||
|
|
||||||
<div class="menu-area">
|
<div class="menu-area">
|
||||||
<div class="ui compact fluid two item inverted menu">
|
<div class="ui compact fluid two item inverted menu">
|
||||||
<a class="active item" role="button" @click.prevent.stop="selectedTab = 'library'" data-tab="library"><translate>Browse</translate></a>
|
<a :class="[{active: selectedTab === 'library'}, 'item']" role="button" @click.prevent.stop="selectedTab = 'library'" data-tab="library"><translate>Browse</translate></a>
|
||||||
<a class="item" role="button" @click.prevent.stop="selectedTab = 'queue'" data-tab="queue">
|
<a :class="[{active: selectedTab === 'queue'}, 'item']" role="button" @click.prevent.stop="selectedTab = 'queue'" data-tab="queue">
|
||||||
<translate>Queue</translate>
|
<translate>Queue</translate>
|
||||||
<template v-if="queue.tracks.length === 0">
|
<template v-if="queue.tracks.length === 0">
|
||||||
<translate>(empty)</translate>
|
<translate>(empty)</translate>
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<section class="ui bottom attached active tab" data-tab="library" :aria-label="labels.mainMenu">
|
<section :class="['ui', 'bottom', 'attached', {active: selectedTab === 'library'}, 'tab']" :aria-label="labels.mainMenu">
|
||||||
<nav class="ui inverted vertical large fluid menu" role="navigation" :aria-label="labels.mainMenu">
|
<nav class="ui inverted vertical large fluid menu" role="navigation" :aria-label="labels.mainMenu">
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<header class="header"><translate>My account</translate></header>
|
<header class="header"><translate>My account</translate></header>
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
<translate :translate-params="{username: $store.state.auth.username}">
|
<translate :translate-params="{username: $store.state.auth.username}">
|
||||||
Logged in as %{ username }
|
Logged in as %{ username }
|
||||||
</translate>
|
</translate>
|
||||||
<img class="ui right floated circular tiny avatar image" v-if="$store.state.auth.profile.avatar.square_crop" :src="$store.getters['instance/absoluteUrl']($store.state.auth.profile.avatar.square_crop)" />
|
<img class="ui right floated circular tiny avatar image" v-if="$store.state.auth.profile.avatar.square_crop" v-lazy="$store.getters['instance/absoluteUrl']($store.state.auth.profile.avatar.square_crop)" />
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{path: '/settings'}"><i class="setting icon"></i><translate>Settings</translate></router-link>
|
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{path: '/settings'}"><i class="setting icon"></i><translate>Settings</translate></router-link>
|
||||||
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'notifications'}">
|
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'notifications'}">
|
||||||
|
|
@ -113,7 +113,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section class="ui bottom attached tab" data-tab="queue">
|
<section :class="['ui', 'bottom', 'attached', {active: selectedTab === 'queue'}, 'tab']">
|
||||||
<table class="ui compact inverted very basic fixed single line unstackable table">
|
<table class="ui compact inverted very basic fixed single line unstackable table">
|
||||||
<draggable v-model="tracks" element="tbody" @update="reorder">
|
<draggable v-model="tracks" element="tbody" @update="reorder">
|
||||||
<tr
|
<tr
|
||||||
|
|
@ -188,11 +188,6 @@ export default {
|
||||||
fetchInterval: null
|
fetchInterval: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
|
||||||
$(this.$el)
|
|
||||||
.find(".menu .item")
|
|
||||||
.tab()
|
|
||||||
},
|
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.fetchInterval) {
|
if (this.fetchInterval) {
|
||||||
clearInterval(this.fetchInterval)
|
clearInterval(this.fetchInterval)
|
||||||
|
|
@ -206,10 +201,8 @@ export default {
|
||||||
labels() {
|
labels() {
|
||||||
let mainMenu = this.$gettext("Main menu")
|
let mainMenu = this.$gettext("Main menu")
|
||||||
let selectTrack = this.$gettext("Play this track")
|
let selectTrack = this.$gettext("Play this track")
|
||||||
let pendingRequests = this.$gettext("Pending import requests")
|
|
||||||
let pendingFollows = this.$gettext("Pending follow requests")
|
let pendingFollows = this.$gettext("Pending follow requests")
|
||||||
return {
|
return {
|
||||||
pendingRequests,
|
|
||||||
pendingFollows,
|
pendingFollows,
|
||||||
mainMenu,
|
mainMenu,
|
||||||
selectTrack
|
selectTrack
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import logger from '@/logging'
|
import logger from '@/logging'
|
||||||
import AlbumCard from '@/components/audio/album/Card'
|
import AlbumCard from '@/components/audio/album/Card'
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import url from '@/utils/url'
|
import url from '@/utils/url'
|
||||||
import {Howl} from 'howler'
|
import {Howl} from 'howler'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<div class="ui loader"></div>
|
<div class="ui loader"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card" v-for="album in albums" :key="album.id">
|
<div class="card" v-for="album in albums" :key="album.id">
|
||||||
<div :class="['ui', 'image', 'with-overlay', {'default-cover': !album.cover.original}]" :style="getImageStyle(album)">
|
<div :class="['ui', 'image', 'with-overlay', {'default-cover': !album.cover.original}]" v-lazy:background-image="getImageUrl(album)">
|
||||||
<play-button class="play-overlay" :icon-only="true" :is-playable="album.is_playable" :button-classes="['ui', 'circular', 'large', 'orange', 'icon', 'button']" :album="album.id"></play-button>
|
<play-button class="play-overlay" :icon-only="true" :is-playable="album.is_playable" :button-classes="['ui', 'circular', 'large', 'orange', 'icon', 'button']" :album="album.id"></play-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import PlayButton from '@/components/audio/PlayButton'
|
import PlayButton from '@/components/audio/PlayButton'
|
||||||
|
|
||||||
|
|
@ -87,17 +87,15 @@ export default {
|
||||||
this.offset = Math.max(this.offset - this.limit, 0)
|
this.offset = Math.max(this.offset - this.limit, 0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getImageStyle (album) {
|
getImageUrl (album) {
|
||||||
let url = '../../../assets/audio/default-cover.png'
|
let url = '../../../assets/audio/default-cover.png'
|
||||||
|
|
||||||
if (album.cover.original) {
|
if (album.cover.original) {
|
||||||
url = this.$store.getters['instance/absoluteUrl'](album.cover.medium_square_crop)
|
url = this.$store.getters['instance/absoluteUrl'](album.cover.medium_square_crop)
|
||||||
} else {
|
} else {
|
||||||
return {}
|
return null
|
||||||
}
|
|
||||||
return {
|
|
||||||
'background-image': `url("${url}")`
|
|
||||||
}
|
}
|
||||||
|
return url
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
@ -108,10 +106,10 @@ export default {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '../../../style/vendor/media';
|
@import "../../../style/vendor/media";
|
||||||
|
|
||||||
.default-cover {
|
.default-cover {
|
||||||
background-image: url('../../../assets/audio/default-cover.png') !important;
|
background-image: url("../../../assets/audio/default-cover.png") !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="album in albums">
|
<tr v-for="album in albums">
|
||||||
<td>
|
<td>
|
||||||
<img class="ui mini image" v-if="album.cover.original" :src="$store.getters['instance/absoluteUrl'](album.cover.small_square_crop)">
|
<img class="ui mini image" v-if="album.cover.original" v-lazy="$store.getters['instance/absoluteUrl'](album.cover.small_square_crop)">
|
||||||
<img class="ui mini image" v-else src="../../../assets/audio/default-cover.png">
|
<img class="ui mini image" v-else src="../../../assets/audio/default-cover.png">
|
||||||
</td>
|
</td>
|
||||||
<td colspan="4">
|
<td colspan="4">
|
||||||
|
|
@ -82,5 +82,4 @@ export default {
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import PlayButton from '@/components/audio/PlayButton'
|
import PlayButton from '@/components/audio/PlayButton'
|
||||||
|
|
||||||
|
|
@ -109,7 +109,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '../../../style/vendor/media';
|
@import "../../../style/vendor/media";
|
||||||
|
|
||||||
.play-overlay {
|
.play-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<div :class="['ui', 'head', 'vertical', 'center', 'aligned', 'stripe', 'segment']">
|
<div :class="['ui', 'head', 'vertical', 'center', 'aligned', 'stripe', 'segment']">
|
||||||
<h2 class="ui center aligned icon header">
|
<h2 class="ui center aligned icon header">
|
||||||
<i v-if="!profile.avatar.square_crop" class="circular inverted user green icon"></i>
|
<i v-if="!profile.avatar.square_crop" class="circular inverted user green icon"></i>
|
||||||
<img class="ui big circular image" v-else :src="$store.getters['instance/absoluteUrl'](profile.avatar.square_crop)" />
|
<img class="ui big circular image" v-else v-lazy="$store.getters['instance/absoluteUrl'](profile.avatar.square_crop)" />
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{{ profile.username }}
|
{{ profile.username }}
|
||||||
<div class="sub header" v-translate="{date: signupDate}">Registered since %{ date }</div>
|
<div class="sub header" v-translate="{date: signupDate}">Registered since %{ date }</div>
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="ui six wide column">
|
<div class="ui six wide column">
|
||||||
<h3 class="ui header"><translate>Current avatar</translate></h3>
|
<h3 class="ui header"><translate>Current avatar</translate></h3>
|
||||||
<img class="ui circular image" v-if="currentAvatar && currentAvatar.square_crop" :src="$store.getters['instance/absoluteUrl'](currentAvatar.medium_square_crop)" />
|
<img class="ui circular image" v-if="currentAvatar && currentAvatar.square_crop" v-lazy="$store.getters['instance/absoluteUrl'](currentAvatar.medium_square_crop)" />
|
||||||
<div class="ui hidden divider"></div>
|
<div class="ui hidden divider"></div>
|
||||||
<button @click="removeAvatar" v-if="currentAvatar && currentAvatar.square_crop" :class="['ui', {'loading': isLoadingAvatar}, ,'yellow', 'button']">
|
<button @click="removeAvatar" v-if="currentAvatar && currentAvatar.square_crop" :class="['ui', {'loading': isLoadingAvatar}, ,'yellow', 'button']">
|
||||||
<translate>Remove avatar</translate>
|
<translate>Remove avatar</translate>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<img
|
<img
|
||||||
class="ui tiny circular avatar"
|
class="ui tiny circular avatar"
|
||||||
v-if="user.avatar && user.avatar.small_square_crop"
|
v-if="user.avatar && user.avatar.small_square_crop"
|
||||||
:src="$store.getters['instance/absoluteUrl'](user.avatar.small_square_crop)" />
|
v-lazy="$store.getters['instance/absoluteUrl'](user.avatar.small_square_crop)" />
|
||||||
<span v-else :style="defaultAvatarStyle" class="ui circular label">{{ user.username[0]}}</span>
|
<span v-else :style="defaultAvatarStyle" class="ui circular label">{{ user.username[0]}}</span>
|
||||||
@{{ user.username }}
|
@{{ user.username }}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="comment">
|
|
||||||
<div class="content">
|
|
||||||
<a class="author">{{ user.username }}</a>
|
|
||||||
<div class="metadata">
|
|
||||||
<div class="date"><human-date :date="date"></human-date></div>
|
|
||||||
</div>
|
|
||||||
<div class="text" v-html="comment"></div>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
<span
|
|
||||||
@click="collapsed = false"
|
|
||||||
v-if="truncated && collapsed"
|
|
||||||
class="expand">
|
|
||||||
<translate>Expand</translate>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
@click="collapsed = true"
|
|
||||||
v-if="truncated && !collapsed"
|
|
||||||
class="collapse">
|
|
||||||
<translate>Collapse</translate>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
user: {type: Object, required: true},
|
|
||||||
date: {required: true},
|
|
||||||
content: {type: String, required: true}
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
collapsed: true,
|
|
||||||
length: 50
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
comment () {
|
|
||||||
let text = this.content
|
|
||||||
if (this.collapsed) {
|
|
||||||
text = this.$options.filters.truncate(text, this.length)
|
|
||||||
}
|
|
||||||
return this.$options.filters.markdown(text)
|
|
||||||
},
|
|
||||||
truncated () {
|
|
||||||
return this.content.length > this.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import LibraryCard from '@/views/content/remote/Card'
|
import LibraryCard from '@/views/content/remote/Card'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import logger from '@/logging'
|
import logger from '@/logging'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from "lodash"
|
import _ from "@/lodash"
|
||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import logger from "@/logging"
|
import logger from "@/logging"
|
||||||
import backend from "@/audio/backend"
|
import backend from "@/audio/backend"
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import _ from "lodash"
|
import _ from "@/lodash"
|
||||||
import $ from "jquery"
|
import $ from "jquery"
|
||||||
|
|
||||||
import logger from "@/logging"
|
import logger from "@/logging"
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import _ from "@/lodash"
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import logger from "@/logging";
|
import logger from "@/logging";
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import _ from "lodash"
|
import _ from "@/lodash"
|
||||||
import $ from "jquery"
|
import $ from "jquery"
|
||||||
|
|
||||||
import logger from "@/logging"
|
import logger from "@/logging"
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import $ from "jquery"
|
import $ from "jquery"
|
||||||
import _ from "lodash"
|
import _ from "@/lodash"
|
||||||
import BuilderFilter from "./Filter"
|
import BuilderFilter from "./Filter"
|
||||||
import TrackTable from "@/components/audio/track/Table"
|
import TrackTable from "@/components/audio/track/Table"
|
||||||
import RadioButton from "@/components/radios/Button"
|
import RadioButton from "@/components/radios/Button"
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
|
|
||||||
import Modal from '@/components/semantic/Modal'
|
import Modal from '@/components/semantic/Modal'
|
||||||
import TrackTable from '@/components/audio/track/Table'
|
import TrackTable from '@/components/audio/track/Table'
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import time from '@/utils/time'
|
import time from '@/utils/time'
|
||||||
import Pagination from '@/components/Pagination'
|
import Pagination from '@/components/Pagination'
|
||||||
import ActionTable from '@/components/common/ActionTable'
|
import ActionTable from '@/components/common/ActionTable'
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import Pagination from '@/components/Pagination'
|
import Pagination from '@/components/Pagination'
|
||||||
import ActionTable from '@/components/common/ActionTable'
|
import ActionTable from '@/components/common/ActionTable'
|
||||||
import OrderingMixin from '@/components/mixins/Ordering'
|
import OrderingMixin from '@/components/mixins/Ordering'
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import time from '@/utils/time'
|
import time from '@/utils/time'
|
||||||
import Pagination from '@/components/Pagination'
|
import Pagination from '@/components/Pagination'
|
||||||
import ActionTable from '@/components/common/ActionTable'
|
import ActionTable from '@/components/common/ActionTable'
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import PlaylistCard from '@/components/playlists/Card'
|
import PlaylistCard from '@/components/playlists/Card'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
<template>
|
|
||||||
<div :class="['ui', {collapsed: collapsed}, 'card']">
|
|
||||||
<div class="content">
|
|
||||||
<div class="header">{{ request.artist_name }}</div>
|
|
||||||
<div class="description">
|
|
||||||
<div
|
|
||||||
v-if="request.albums" v-html="$options.filters.markdown(request.albums)"></div>
|
|
||||||
<div class="ui comments">
|
|
||||||
<comment
|
|
||||||
:user="request.user"
|
|
||||||
:content="request.comment || ''"
|
|
||||||
:date="request.creation_date"></comment>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="extra content">
|
|
||||||
<span >
|
|
||||||
<i v-if="request.status === 'pending'" class="hourglass start icon"></i>
|
|
||||||
<i v-if="request.status === 'accepted'" class="hourglass half icon"></i>
|
|
||||||
<i v-if="request.status === 'imported'" class="check icon"></i>
|
|
||||||
{{ request.status | capitalize }}
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
@click="createImport"
|
|
||||||
v-if="request.status === 'pending' && importAction && $store.state.auth.availablePermissions['library']"
|
|
||||||
class="ui mini basic green right floated button"><translate>Create import</translate></button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Comment from '@/components/discussion/Comment'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
request: {type: Object, required: true},
|
|
||||||
importAction: {type: Boolean, default: true}
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Comment
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
collapsed: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
createImport () {
|
|
||||||
this.$router.push({
|
|
||||||
name: 'library.import.launch',
|
|
||||||
query: {request: this.request.id}})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<form v-if="!over" class="ui form" @submit.prevent="submit">
|
|
||||||
<p><translate>Something's missing in the library? Let us know what you would like to listen!</translate></p>
|
|
||||||
<div class="required field">
|
|
||||||
<label><translate>Artist name</translate></label>
|
|
||||||
<input v-model="currentArtistName" :placeholder="labels.artistNamePlaceholder" required maxlength="200">
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label><translate>Albums</translate></label>
|
|
||||||
<p><translate>Leave this field empty if you're requesting the whole discography.</translate></p>
|
|
||||||
<input v-model="currentAlbums" :placeholder="labels.albumTitlePlaceholder" maxlength="2000">
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label><translate>Comment</translate></label>
|
|
||||||
<textarea v-model="currentComment" rows="3" :placeholder="labels.commentPlaceholder" maxlength="2000"></textarea>
|
|
||||||
</div>
|
|
||||||
<button class="ui submit button" type="submit"><translate>Submit</translate></button>
|
|
||||||
</form>
|
|
||||||
<div v-else class="ui success message">
|
|
||||||
<div class="header"><translate>Request submitted!</translate></div>
|
|
||||||
<p><translate>We've received your request, you'll get some groove soon ;)</translate></p>
|
|
||||||
<button @click="reset" class="ui button"><translate>Submit another request</translate></button>
|
|
||||||
</div>
|
|
||||||
<div v-if="requests.length > 0">
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<h3 class="ui header"><translate>Pending requests</translate></h3>
|
|
||||||
<div class="ui list">
|
|
||||||
<div v-for="request in requests" class="item">
|
|
||||||
<div class="content">
|
|
||||||
<div class="header">{{ request.artist_name }}</div>
|
|
||||||
<div v-if="request.albums" class="description">
|
|
||||||
{{ request.albums|truncate }}</div>
|
|
||||||
<div v-if="request.comment" class="description">
|
|
||||||
{{ request.comment|truncate }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import $ from 'jquery'
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
import logger from '@/logging'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
defaultArtistName: {type: String, default: ''},
|
|
||||||
defaultAlbums: {type: String, default: ''},
|
|
||||||
defaultComment: {type: String, default: ''}
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
this.fetchRequests()
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
$('.ui.radio.checkbox').checkbox()
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
currentArtistName: this.defaultArtistName,
|
|
||||||
currentAlbums: this.defaultAlbums,
|
|
||||||
currentComment: this.defaultComment,
|
|
||||||
isLoading: false,
|
|
||||||
over: false,
|
|
||||||
requests: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
labels () {
|
|
||||||
let artistNamePlaceholder = this.$gettext('The Beatles, Mickael Jackson…')
|
|
||||||
let albumTitlePlaceholder = this.$gettext('The White Album, Thriller…')
|
|
||||||
let commentPlaceholder = this.$gettext('Use this comment box to add details to your request if needed')
|
|
||||||
return {
|
|
||||||
artistNamePlaceholder,
|
|
||||||
albumTitlePlaceholder,
|
|
||||||
commentPlaceholder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
fetchRequests () {
|
|
||||||
let self = this
|
|
||||||
let url = 'requests/import-requests/'
|
|
||||||
axios.get(url, {}).then((response) => {
|
|
||||||
self.requests = response.data.results
|
|
||||||
})
|
|
||||||
},
|
|
||||||
submit () {
|
|
||||||
let self = this
|
|
||||||
this.isLoading = true
|
|
||||||
let url = 'requests/import-requests/'
|
|
||||||
let payload = {
|
|
||||||
artist_name: this.currentArtistName,
|
|
||||||
albums: this.currentAlbums,
|
|
||||||
comment: this.currentComment
|
|
||||||
}
|
|
||||||
axios.post(url, payload).then((response) => {
|
|
||||||
logger.default.info('Submitted request!')
|
|
||||||
self.isLoading = false
|
|
||||||
self.over = true
|
|
||||||
self.requests.unshift(response.data)
|
|
||||||
}, (response) => {
|
|
||||||
logger.default.error('error while submitting request')
|
|
||||||
self.isLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
reset () {
|
|
||||||
this.over = false
|
|
||||||
this.currentArtistName = ''
|
|
||||||
this.currentAlbums = ''
|
|
||||||
this.currentComment = ''
|
|
||||||
},
|
|
||||||
truncate (string, length) {
|
|
||||||
if (string.length > length) {
|
|
||||||
return string.substring(0, length) + '…'
|
|
||||||
}
|
|
||||||
return string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
|
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Embed from './Embed'
|
import Embed from './Embed'
|
||||||
import axios from 'axios'
|
|
||||||
import VuePlyr from 'vue-plyr'
|
import VuePlyr from 'vue-plyr'
|
||||||
|
|
||||||
Vue.use(VuePlyr)
|
Vue.use(VuePlyr)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import showdown from 'showdown'
|
|
||||||
|
|
||||||
export function truncate (str, max, ellipsis) {
|
export function truncate (str, max, ellipsis) {
|
||||||
max = max || 100
|
max = max || 100
|
||||||
|
|
@ -14,13 +13,6 @@ export function truncate (str, max, ellipsis) {
|
||||||
|
|
||||||
Vue.filter('truncate', truncate)
|
Vue.filter('truncate', truncate)
|
||||||
|
|
||||||
export function markdown (str) {
|
|
||||||
const converter = new showdown.Converter()
|
|
||||||
return converter.makeHtml(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
Vue.filter('markdown', markdown)
|
|
||||||
|
|
||||||
export function ago (date) {
|
export function ago (date) {
|
||||||
const m = moment(date)
|
const m = moment(date)
|
||||||
return m.fromNow()
|
return m.fromNow()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// cherry-pick specific lodash methods here to reduce bundle size
|
||||||
|
|
||||||
|
export default {
|
||||||
|
clone: require('lodash/clone'),
|
||||||
|
debounce: require('lodash/debounce'),
|
||||||
|
get: require('lodash/get'),
|
||||||
|
merge: require('lodash/merge'),
|
||||||
|
range: require('lodash/range'),
|
||||||
|
shuffle: require('lodash/shuffle'),
|
||||||
|
sortBy: require('lodash/sortBy'),
|
||||||
|
throttle: require('lodash/throttle'),
|
||||||
|
uniq: require('lodash/uniq'),
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,6 @@ import VueLazyload from 'vue-lazyload'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
import GetTextPlugin from 'vue-gettext'
|
import GetTextPlugin from 'vue-gettext'
|
||||||
import { sync } from 'vuex-router-sync'
|
import { sync } from 'vuex-router-sync'
|
||||||
import translations from './translations.json'
|
|
||||||
import locales from '@/locales'
|
import locales from '@/locales'
|
||||||
|
|
||||||
import filters from '@/filters' // eslint-disable-line
|
import filters from '@/filters' // eslint-disable-line
|
||||||
|
|
@ -23,12 +22,9 @@ import globals from '@/components/globals' // eslint-disable-line
|
||||||
sync(store, router)
|
sync(store, router)
|
||||||
|
|
||||||
window.$ = window.jQuery = require('jquery')
|
window.$ = window.jQuery = require('jquery')
|
||||||
|
require('./semantic.js')
|
||||||
// this is absolutely dirty but at the moment, semantic UI does not
|
|
||||||
// play really nice with webpack and I want to get rid of Google Fonts
|
|
||||||
// require('./semantic/semantic.css')
|
|
||||||
require('semantic-ui-css/semantic.js')
|
|
||||||
require('masonry-layout')
|
require('masonry-layout')
|
||||||
|
|
||||||
let availableLanguages = (function () {
|
let availableLanguages = (function () {
|
||||||
let l = {}
|
let l = {}
|
||||||
locales.locales.forEach(c => {
|
locales.locales.forEach(c => {
|
||||||
|
|
@ -54,7 +50,7 @@ Vue.use(GetTextPlugin, {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
translations: translations,
|
translations: {},
|
||||||
silent: true
|
silent: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// require('semantic-ui-css/components/accordion.min.js')
|
||||||
|
require('semantic-ui-css/components/api.min.js')
|
||||||
|
require('semantic-ui-css/components/checkbox.min.js')
|
||||||
|
// require('semantic-ui-css/components/colorize.min.js')
|
||||||
|
require('semantic-ui-css/components/dimmer.min.js')
|
||||||
|
require('semantic-ui-css/components/dropdown.min.js')
|
||||||
|
// require('semantic-ui-css/components/embed.min.js')
|
||||||
|
// require('semantic-ui-css/components/form.min.js')
|
||||||
|
require('semantic-ui-css/components/modal.min.js')
|
||||||
|
// require('semantic-ui-css/components/nag.min.js')
|
||||||
|
// require('semantic-ui-css/components/popup.min.js')
|
||||||
|
require('semantic-ui-css/components/progress.min.js')
|
||||||
|
// require('semantic-ui-css/components/rating.min.js')
|
||||||
|
require('semantic-ui-css/components/search.min.js')
|
||||||
|
// require('semantic-ui-css/components/shape.min.js')
|
||||||
|
// require('semantic-ui-css/components/sidebar.min.js')
|
||||||
|
require('semantic-ui-css/components/site.min.js')
|
||||||
|
require('semantic-ui-css/components/state.min.js')
|
||||||
|
require('semantic-ui-css/components/sticky.min.js')
|
||||||
|
// require('semantic-ui-css/components/tab.min.js')
|
||||||
|
require('semantic-ui-css/components/transition.min.js')
|
||||||
|
// require('semantic-ui-css/components/video.min.js')
|
||||||
|
require('semantic-ui-css/components/visibility.min.js')
|
||||||
|
// require('semantic-ui-css/components/visit.min.js')
|
||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 957 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB |
|
|
@ -1,6 +1,6 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import logger from '@/logging'
|
import logger from '@/logging'
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
|
|
||||||
function getDefaultUrl () {
|
function getDefaultUrl () {
|
||||||
return (
|
return (
|
||||||
|
|
@ -40,14 +40,6 @@ export default {
|
||||||
enabled: {
|
enabled: {
|
||||||
value: true
|
value: true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
raven: {
|
|
||||||
front_enabled: {
|
|
||||||
value: false
|
|
||||||
},
|
|
||||||
front_dsn: {
|
|
||||||
value: null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -131,7 +123,7 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fetchFrontSettings ({commit}) {
|
fetchFrontSettings ({commit}) {
|
||||||
return axios.get('/settings.json').then(response => {
|
return axios.get('/front/settings.json').then(response => {
|
||||||
commit('frontSettings', response.data)
|
commit('frontSettings', response.data)
|
||||||
}, response => {
|
}, response => {
|
||||||
logger.default.error('Error when fetching front-end configuration (or no customization available)')
|
logger.default.error('Error when fetching front-end configuration (or no customization available)')
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import logger from '@/logging'
|
import logger from '@/logging'
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
|
|
@ -86,7 +86,7 @@ export default {
|
||||||
if (callback && i + 1 === total) {
|
if (callback && i + 1 === total) {
|
||||||
p.then(callback)
|
p.then(callback)
|
||||||
}
|
}
|
||||||
if (shouldPlay && p) {
|
if (shouldPlay && p && i + 1 === total) {
|
||||||
p.then(() => {
|
p.then(() => {
|
||||||
dispatch('next')
|
dispatch('next')
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
███████╗███████╗███╗ ███╗ █████╗ ███╗ ██╗████████╗██╗ ██████╗ ██╗ ██╗██╗
|
||||||
|
██╔════╝██╔════╝████╗ ████║██╔══██╗████╗ ██║╚══██╔══╝██║██╔════╝ ██║ ██║██║
|
||||||
|
███████╗█████╗ ██╔████╔██║███████║██╔██╗ ██║ ██║ ██║██║ ██║ ██║██║
|
||||||
|
╚════██║██╔══╝ ██║╚██╔╝██║██╔══██║██║╚██╗██║ ██║ ██║██║ ██║ ██║██║
|
||||||
|
███████║███████╗██║ ╚═╝ ██║██║ ██║██║ ╚████║ ██║ ██║╚██████╗ ╚██████╔╝██║
|
||||||
|
╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝
|
||||||
|
|
||||||
|
Import this file into your LESS project to use Semantic UI without build tools
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Global */
|
||||||
|
@import "~semantic-ui-css/components/reset.css";
|
||||||
|
// we use our custom site css here to avoid loading google font
|
||||||
|
@import "./site";
|
||||||
|
|
||||||
|
/* Elements */
|
||||||
|
@import "~semantic-ui-css/components/button.css";
|
||||||
|
@import "~semantic-ui-css/components/container.css";
|
||||||
|
@import "~semantic-ui-css/components/divider.css";
|
||||||
|
// @import "~semantic-ui-css/components/flag.css";
|
||||||
|
@import "~semantic-ui-css/components/header.css";
|
||||||
|
@import "~semantic-ui-css/components/icon.css";
|
||||||
|
@import "~semantic-ui-css/components/image.css";
|
||||||
|
@import "~semantic-ui-css/components/input.css";
|
||||||
|
@import "~semantic-ui-css/components/label.css";
|
||||||
|
@import "~semantic-ui-css/components/list.css";
|
||||||
|
@import "~semantic-ui-css/components/loader.css";
|
||||||
|
// @import "~semantic-ui-css/components/placeholder.css";
|
||||||
|
// @import "~semantic-ui-css/components/rail.css";
|
||||||
|
// @import "~semantic-ui-css/components/reveal.css";
|
||||||
|
@import "~semantic-ui-css/components/segment.css";
|
||||||
|
@import "~semantic-ui-css/components/step.css";
|
||||||
|
|
||||||
|
/* Collections */
|
||||||
|
// @import "~semantic-ui-css/components/breadcrumb.css";
|
||||||
|
@import "~semantic-ui-css/components/form.css";
|
||||||
|
@import "~semantic-ui-css/components/grid.css";
|
||||||
|
@import "~semantic-ui-css/components/menu.css";
|
||||||
|
@import "~semantic-ui-css/components/message.css";
|
||||||
|
@import "~semantic-ui-css/components/table.css";
|
||||||
|
|
||||||
|
/* Views */
|
||||||
|
// @import "~semantic-ui-css/components/ad.css";
|
||||||
|
@import "~semantic-ui-css/components/card.css";
|
||||||
|
// @import "~semantic-ui-css/components/comment.css";
|
||||||
|
// @import "~semantic-ui-css/components/feed.css";
|
||||||
|
@import "~semantic-ui-css/components/item.css";
|
||||||
|
@import "~semantic-ui-css/components/statistic.css";
|
||||||
|
|
||||||
|
/* Modules */
|
||||||
|
// @import "~semantic-ui-css/components/accordion.css";
|
||||||
|
@import "~semantic-ui-css/components/checkbox.css";
|
||||||
|
@import "~semantic-ui-css/components/dimmer.css";
|
||||||
|
@import "~semantic-ui-css/components/dropdown.css";
|
||||||
|
// @import "~semantic-ui-css/components/embed.css";
|
||||||
|
@import "~semantic-ui-css/components/modal.css";
|
||||||
|
// @import "~semantic-ui-css/components/nag.css";
|
||||||
|
@import "~semantic-ui-css/components/popup.css";
|
||||||
|
@import "~semantic-ui-css/components/progress.css";
|
||||||
|
// @import "~semantic-ui-css/components/rating.css";
|
||||||
|
@import "~semantic-ui-css/components/search.css";
|
||||||
|
// @import "~semantic-ui-css/components/shape.css";
|
||||||
|
@import "~semantic-ui-css/components/sidebar.css";
|
||||||
|
@import "~semantic-ui-css/components/sticky.css";
|
||||||
|
@import "~semantic-ui-css/components/tab.css";
|
||||||
|
@import "~semantic-ui-css/components/transition.css";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// we do the import here instead in main.js
|
||||||
|
// as resolve order is not deterministric in webpack
|
||||||
|
// and we end up with CSS rules not applied,
|
||||||
|
// see https://github.com/webpack/webpack/issues/215
|
||||||
|
@import "./vendor/media";
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
@include media("<desktop") {
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#app {
|
||||||
|
font-family: "Avenir", Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instance-chooser {
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main.pusher,
|
||||||
|
.footer {
|
||||||
|
@include media(">desktop") {
|
||||||
|
margin-left: 350px !important;
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main.pusher > .ui.secondary.menu {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
border: none;
|
||||||
|
box-shadow: inset 0px -2px 0px 0px rgba(34, 36, 38, 0.15);
|
||||||
|
.ui.item {
|
||||||
|
border: none;
|
||||||
|
border-bottom-style: none;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
&.active {
|
||||||
|
box-shadow: inset 0px -2px 0px 0px #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@include media(">tablet") {
|
||||||
|
padding: 0 2.5rem;
|
||||||
|
}
|
||||||
|
@include media(">desktop") {
|
||||||
|
position: fixed;
|
||||||
|
left: 350px;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
background-color: white;
|
||||||
|
.item {
|
||||||
|
padding-top: 1.5em;
|
||||||
|
padding-bottom: 1.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-messages {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 1em;
|
||||||
|
left: 1em;
|
||||||
|
@include media(">desktop") {
|
||||||
|
left: 350px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.main-pusher {
|
||||||
|
padding: 1.5rem 0;
|
||||||
|
}
|
||||||
|
.ui.stripe.segment,
|
||||||
|
#footer {
|
||||||
|
padding: 2em;
|
||||||
|
@include media(">tablet") {
|
||||||
|
padding: 4em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ellipsis {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.small.text.container {
|
||||||
|
max-width: 500px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.icon.tiny {
|
||||||
|
padding: 0.5em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
.logo {
|
||||||
|
&.bordered.icon {
|
||||||
|
padding: .5em .41em !important;
|
||||||
|
}
|
||||||
|
path {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.discrete {
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
.link {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.really.basic.button {
|
||||||
|
&:not(:focus) {
|
||||||
|
box-shadow: none !important;
|
||||||
|
background-color: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.floated.buttons .button ~ .dropdown {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.icon.header .circular.icon {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segment-content .button {
|
||||||
|
margin: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.segment.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.reset {
|
||||||
|
border: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: auto;
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
/* inherit font & color from ancestor */
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
|
||||||
|
/* Normalize `line-height`. Cannot be changed from `normal` in Firefox 4+. */
|
||||||
|
line-height: normal;
|
||||||
|
|
||||||
|
/* Corrects font smoothing for webkit */
|
||||||
|
-webkit-font-smoothing: inherit;
|
||||||
|
-moz-osx-font-smoothing: inherit;
|
||||||
|
/* Corrects inability to style clickable `input` types in iOS */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
text-align: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.table > caption {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0.5em;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
[role="button"] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left.floated {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right.floated {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
/*!
|
||||||
|
* # Semantic UI 2.4.1 - Site
|
||||||
|
* http://github.com/semantic-org/semantic-ui/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Released under the MIT license
|
||||||
|
* http://opensource.org/licenses/MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
Page
|
||||||
|
*******************************/
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
min-width: 320px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.4285em;
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
Headers
|
||||||
|
*******************************/
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;
|
||||||
|
line-height: 1.28571429em;
|
||||||
|
margin: calc(2rem - 0.14285714em ) 0em 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0em;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
min-height: 1rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.71428571rem;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.28571429rem;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
font-size: 1.07142857rem;
|
||||||
|
}
|
||||||
|
h5 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
h1:first-child,
|
||||||
|
h2:first-child,
|
||||||
|
h3:first-child,
|
||||||
|
h4:first-child,
|
||||||
|
h5:first-child {
|
||||||
|
margin-top: 0em;
|
||||||
|
}
|
||||||
|
h1:last-child,
|
||||||
|
h2:last-child,
|
||||||
|
h3:last-child,
|
||||||
|
h4:last-child,
|
||||||
|
h5:last-child {
|
||||||
|
margin-bottom: 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
Text
|
||||||
|
*******************************/
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0em 0em 1em;
|
||||||
|
line-height: 1.4285em;
|
||||||
|
}
|
||||||
|
p:first-child {
|
||||||
|
margin-top: 0em;
|
||||||
|
}
|
||||||
|
p:last-child {
|
||||||
|
margin-bottom: 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------
|
||||||
|
Links
|
||||||
|
--------------------*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #4183C4;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #1e70bf;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
Scrollbars
|
||||||
|
*******************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
Highlighting
|
||||||
|
*******************************/
|
||||||
|
|
||||||
|
|
||||||
|
/* Site */
|
||||||
|
::-webkit-selection {
|
||||||
|
background-color: #CCE2FF;
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
::-moz-selection {
|
||||||
|
background-color: #CCE2FF;
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
::selection {
|
||||||
|
background-color: #CCE2FF;
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form */
|
||||||
|
textarea::-webkit-selection,
|
||||||
|
input::-webkit-selection {
|
||||||
|
background-color: rgba(100, 100, 100, 0.4);
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
textarea::-moz-selection,
|
||||||
|
input::-moz-selection {
|
||||||
|
background-color: rgba(100, 100, 100, 0.4);
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
textarea::selection,
|
||||||
|
input::selection {
|
||||||
|
background-color: rgba(100, 100, 100, 0.4);
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force Simple Scrollbars */
|
||||||
|
body ::-webkit-scrollbar {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
body ::-webkit-scrollbar-track {
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
body ::-webkit-scrollbar-thumb {
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: rgba(0, 0, 0, 0.25);
|
||||||
|
-webkit-transition: color 0.2s ease;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
body ::-webkit-scrollbar-thumb:window-inactive {
|
||||||
|
background: rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
body ::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(128, 135, 139, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inverted UI */
|
||||||
|
body .ui.inverted::-webkit-scrollbar-track {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
body .ui.inverted::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
body .ui.inverted::-webkit-scrollbar-thumb:window-inactive {
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
body .ui.inverted::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
Global Overrides
|
||||||
|
*******************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
Site Overrides
|
||||||
|
*******************************/
|
||||||
|
|
@ -16,7 +16,7 @@ export default {
|
||||||
durationFormatted (v) {
|
durationFormatted (v) {
|
||||||
let duration = parseInt(v)
|
let duration = parseInt(v)
|
||||||
if (duration % 1 !== 0) {
|
if (duration % 1 !== 0) {
|
||||||
return time.parse(0)
|
return this.parse(0)
|
||||||
}
|
}
|
||||||
duration = Math.round(duration)
|
duration = Math.round(duration)
|
||||||
return this.parse(duration)
|
return this.parse(duration)
|
||||||
|
|
|
||||||
|
|
@ -142,11 +142,6 @@ export default {
|
||||||
"instance__nodeinfo_stats_enabled",
|
"instance__nodeinfo_stats_enabled",
|
||||||
"instance__nodeinfo_private"
|
"instance__nodeinfo_private"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
label: errorLabel,
|
|
||||||
id: "reporting",
|
|
||||||
settings: ["raven__front_enabled", "raven__front_dsn"]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
import time from '@/utils/time'
|
import time from '@/utils/time'
|
||||||
import {normalizeQuery, parseTokens, compileTokens} from '@/search'
|
import {normalizeQuery, parseTokens, compileTokens} from '@/search'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import _ from "lodash"
|
import _ from "@/lodash"
|
||||||
import $ from "jquery"
|
import $ from "jquery"
|
||||||
|
|
||||||
import OrderingMixin from "@/components/mixins/Ordering"
|
import OrderingMixin from "@/components/mixins/Ordering"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import {expect} from 'chai'
|
import {expect} from 'chai'
|
||||||
|
|
||||||
import {truncate, markdown, ago, capitalize, year} from '@/filters'
|
import {truncate, ago, capitalize, year} from '@/filters'
|
||||||
|
|
||||||
describe('filters', () => {
|
describe('filters', () => {
|
||||||
describe('truncate', () => {
|
describe('truncate', () => {
|
||||||
|
|
@ -20,13 +20,6 @@ describe('filters', () => {
|
||||||
expect(output).to.equal('Hello pouet')
|
expect(output).to.equal('Hello pouet')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('markdown', () => {
|
|
||||||
it('renders markdown', () => {
|
|
||||||
const input = 'Hello world'
|
|
||||||
let output = markdown(input)
|
|
||||||
expect(output).to.equal('<p>Hello world</p>')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('ago', () => {
|
describe('ago', () => {
|
||||||
it('works', () => {
|
it('works', () => {
|
||||||
const input = new Date()
|
const input = new Date()
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,11 @@ describe('store/instance', () => {
|
||||||
|
|
||||||
describe('mutations', () => {
|
describe('mutations', () => {
|
||||||
it('settings', () => {
|
it('settings', () => {
|
||||||
const state = {settings: {raven: {front_dsn: {value: 'test'}}}}
|
const state = {settings: {users: {upload_quota: {value: 1}}}}
|
||||||
let settings = {raven: {front_enabled: {value: true}}}
|
let settings = {users: {registration_enabled: {value: true}}}
|
||||||
store.mutations.settings(state, settings)
|
store.mutations.settings(state, settings)
|
||||||
expect(state.settings).to.deep.equal({
|
expect(state.settings).to.deep.equal({
|
||||||
raven: {front_dsn: {value: 'test'}, front_enabled: {value: true}}
|
users: {upload_quota: {value: 1}, registration_enabled: {value: true}}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -32,13 +32,13 @@ describe('store/instance', () => {
|
||||||
status: 200,
|
status: 200,
|
||||||
response: [
|
response: [
|
||||||
{
|
{
|
||||||
section: 'raven',
|
section: 'users',
|
||||||
name: 'front_dsn',
|
name: 'upload_quota',
|
||||||
value: 'test'
|
value: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
section: 'raven',
|
section: 'users',
|
||||||
name: 'front_enabled',
|
name: 'registration_enabled',
|
||||||
value: false
|
value: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -50,15 +50,15 @@ describe('store/instance', () => {
|
||||||
{
|
{
|
||||||
type: 'settings',
|
type: 'settings',
|
||||||
payload: {
|
payload: {
|
||||||
raven: {
|
users: {
|
||||||
front_dsn: {
|
upload_quota: {
|
||||||
section: 'raven',
|
section: 'users',
|
||||||
name: 'front_dsn',
|
name: 'upload_quota',
|
||||||
value: 'test'
|
value: 1
|
||||||
},
|
},
|
||||||
front_enabled: {
|
registration_enabled: {
|
||||||
section: 'raven',
|
section: 'users',
|
||||||
name: 'front_enabled',
|
name: 'registration_enabled',
|
||||||
value: false
|
value: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
var sinon = require('sinon')
|
var sinon = require('sinon')
|
||||||
import {expect} from 'chai'
|
import {expect} from 'chai'
|
||||||
|
|
||||||
import _ from 'lodash'
|
import _ from '@/lodash'
|
||||||
|
|
||||||
import store from '@/store/queue'
|
import store from '@/store/queue'
|
||||||
import { testAction } from '../../utils'
|
import { testAction } from '../../utils'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,14 @@
|
||||||
|
|
||||||
|
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||||
|
const webpack = require('webpack');
|
||||||
|
|
||||||
|
let plugins = [
|
||||||
|
// do not include moment.js locales since it's quite heavy
|
||||||
|
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||||
|
]
|
||||||
|
if (process.env.BUNDLE_ANALYZE === '1') {
|
||||||
|
plugins.push(new BundleAnalyzerPlugin())
|
||||||
|
}
|
||||||
module.exports = {
|
module.exports = {
|
||||||
baseUrl: '/front/',
|
baseUrl: '/front/',
|
||||||
pages: {
|
pages: {
|
||||||
|
|
@ -17,6 +27,7 @@ module.exports = {
|
||||||
config.optimization.delete('splitChunks')
|
config.optimization.delete('splitChunks')
|
||||||
},
|
},
|
||||||
configureWebpack: {
|
configureWebpack: {
|
||||||
|
plugins: plugins,
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'vue$': 'vue/dist/vue.esm.js'
|
'vue$': 'vue/dist/vue.esm.js'
|
||||||
|
|
|
||||||
5060
front/yarn.lock
5060
front/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue