Compare commits

...

12 Commits

Author SHA1 Message Date
github-actions[bot] 0873c6cb65 [web] Rebuild web interface 2024-04-21 18:09:35 +00:00
Alain Nussbaumer 1ef62ac3a6 [web] Fix error in GroupedList provoked by the linting 2024-04-21 20:09:00 +02:00
github-actions[bot] 06f658e1c4 [web] Rebuild web interface 2024-04-21 16:21:13 +00:00
Alain Nussbaumer a2000c0bc7 [web] Lint source code 2024-04-21 18:20:40 +02:00
Alain Nussbaumer c3d5c6eab9 [web] Lint source code 2024-04-21 17:59:21 +02:00
github-actions[bot] 0d11f732e1 [web] Rebuild web interface 2024-04-21 15:53:56 +00:00
Alain Nussbaumer d6391621a0 [web] Upgrade to eslint 9.1.0 2024-04-21 17:48:14 +02:00
Alain Nussbaumer b8373a4ee0 [web] Lint source code 2024-04-21 17:44:55 +02:00
Alain Nussbaumer 2fda829ac4 [web] Remove unused variable 2024-04-21 16:14:55 +02:00
Alain Nussbaumer 5115e04664 [web] Lint source code 2024-04-21 13:17:22 +02:00
Alain Nussbaumer 369afe11e3 [web] Remove unused variable 2024-04-21 12:58:15 +02:00
Alain Nussbaumer 9690bc2447 [web] Format source code 2024-04-21 12:55:49 +02:00
25 changed files with 400 additions and 386 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,11 +0,0 @@
module.exports = {
env: {
node: true
},
extends: ['eslint:recommended', 'plugin:vue/vue3-recommended', 'prettier'],
rules: {
// Override/add rules settings here, such as:
'no-unused-vars': ['error', { args: 'none' }],
'vue/prop-name-casing': ['warn', 'snake_case']
}
}

50
web-src/eslint.config.js Normal file
View File

@ -0,0 +1,50 @@
import eslintConfigPrettier from 'eslint-config-prettier'
import globals from 'globals'
import js from '@eslint/js'
import pluginVue from 'eslint-plugin-vue'
export default [
{
files: ['src/**/*.js', 'src/**/.vue'],
languageOptions: {
globals: {
...globals.node
}
}
},
eslintConfigPrettier,
js.configs.all,
...pluginVue.configs['flat/recommended'],
{
rules: {
camelcase: 'off',
'consistent-this': 'off',
'default-param-last': 'off',
'id-length': 'off',
'max-lines': 'off',
'max-lines-per-function': 'off',
'max-statements': 'off',
'no-bitwise': 'off',
'no-magic-numbers': 'off',
'no-negated-condition': 'off',
'no-nested-ternary': 'off',
'no-plusplus': 'off',
'no-shadow': 'off',
'no-ternary': 'off',
'no-undef': 'off',
'no-undefined': 'off',
'no-unused-expressions': 'off',
'no-unused-vars': ['error', { args: 'none', caughtErrors: 'none' }],
'no-useless-assignment': 'off',
'one-var': 'off',
'prefer-destructuring': 'off',
'prefer-named-capture-group': 'off',
'sort-keys': 'off',
'sort-vars': 'off',
'vue/html-self-closing': 'off',
'vue/max-attributes-per-line': 'off',
'vue/prop-name-casing': 'off',
'vue/singleline-html-element-content-newline': 'off'
}
}
]

View File

@ -724,9 +724,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.15.0.tgz",
"integrity": "sha512-O63bJ7p909pRRQfOJ0k/Jp8gNFMud+ZzLLG5EBWquylHxmRT2k18M2ifg8WyjCgFVdpA7+rI0YZ8EkAtg6dSUw==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.16.0.tgz",
"integrity": "sha512-4fDVBAfWYlw2CtYgHEWarAYSozTx5OYLsSM/cdGW7H51FwI10DaGnjKgdqWyWXY/VjugelzriCiKf1UdM20Bxg==",
"cpu": [
"arm"
],
@ -737,9 +737,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.15.0.tgz",
"integrity": "sha512-5UywPdmC9jiVOShjQx4uuIcnTQOf85iA4jgg8bkFoH5NYWFfAfrJpv5eeokmTdSmYwUTT5IrcrBCJNkowhrZDA==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.16.0.tgz",
"integrity": "sha512-JltUBgsKgN108NO4/hj0B/dJYNrqqmdRCtUet5tFDi/w+0tvQP0FToyWBV4HKBcSX4cvFChrCyt5Rh4FX6M6QQ==",
"cpu": [
"arm64"
],
@ -750,9 +750,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.15.0.tgz",
"integrity": "sha512-hNkt75uFfWpRxHItCBmbS0ba70WnibJh6yz60WShSWITLlVRbkvAu1E/c7RlliPY4ajhqJd0UPZz//gNalTd4g==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.16.0.tgz",
"integrity": "sha512-UwF7tkWf0roggMRv7Vrkof7VgX9tEZIc4vbaQl0/HNX3loWlcum+0ODp1Qsd8s7XvQGT+Zboxx1qxav3vq8YDw==",
"cpu": [
"arm64"
],
@ -763,9 +763,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.15.0.tgz",
"integrity": "sha512-HnC5bTP7qdfO9nUw/mBhNcjOEZfbS8NwV+nFegiMhYOn1ATAGZF4kfAxR9BuZevBrebWCxMmxm8NCU1CUoz+wQ==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.16.0.tgz",
"integrity": "sha512-RIY42wn6+Yb0qD29T7Dvm9/AhxrkGDf7X5dgI6rUFXR19+vCLh3u45yLcKOayu2ZQEba9rf/+BX3EggVwckiIw==",
"cpu": [
"x64"
],
@ -776,9 +776,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.15.0.tgz",
"integrity": "sha512-QGOIQIJZeIIqMsc4BUGe8TnV4dkXhSW2EhaQ1G4LqMUNpkyeLztvlDlOoNHn7SR7a4dBANdcEbPkkEzz3rzjzA==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.16.0.tgz",
"integrity": "sha512-r2TGCIKzqk8VwjOvW7sveledh6aPao131ejUfZNIyFlWBCruF4HOu51KtLArDa7LL6qKd0vkgxGX3/2NmYpWig==",
"cpu": [
"arm"
],
@ -789,9 +789,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.15.0.tgz",
"integrity": "sha512-PS/Cp8CinYgoysQ8i4UXYH/TZl06fXszvY/RDkyBYgUB1+tKyOMS925/4FZhfrhkl3XQEKjMc3BKtsxpB9Tz9Q==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.16.0.tgz",
"integrity": "sha512-/QwaDp0RXQTtm25wQFSl02zEm9oveRXr9qAHbdxWCm9YG9dR8esqpyqzS/3GgHDm7jHktPNz9gTENfoUKRCcXQ==",
"cpu": [
"arm"
],
@ -802,9 +802,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.15.0.tgz",
"integrity": "sha512-XzOsnD6lGDP+k+vGgTYAryVGu8N89qpjMN5BVFUj75dGVFP3FzIVAufJAraxirpDwEQZA7Gjs0Vo5p4UmnnjsA==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.16.0.tgz",
"integrity": "sha512-iypHsz7YEfoyNL0iHbQ7B7pY6hpymvvMgFXXaMd5+WCtvJ9zqWPZKFmo78UeWzWNmTP9JtPiNIQt6efRxx/MNA==",
"cpu": [
"arm64"
],
@ -815,9 +815,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.15.0.tgz",
"integrity": "sha512-+ScJA4Epbx/ZQGjDnbvTAcb8ZD06b+TlIka2UkujbKf1I/A+yrvEcJwG3/27zMmvcWMQyeCJhbL9TlSjzL0B7Q==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.16.0.tgz",
"integrity": "sha512-7UpYcO0uVeomnoL5MpQhrS0FT7xZUJrEXtKVLmps5bRA7x5AiA1PDuPnMbxcOBWjIM2HHIG1t3ndnRTVMIbk5A==",
"cpu": [
"arm64"
],
@ -828,9 +828,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.15.0.tgz",
"integrity": "sha512-1cUSvYgnyTakM4FDyf/GxUCDcqmj/hUh1NOizEOJU7+D5xEfFGCxgcNOs3hYBeRMUCcGmGkt01EhD3ILgKpGHQ==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.16.0.tgz",
"integrity": "sha512-FSuFy4/hOQy0lH135ifnElP/6dKoHcZGHovsaRY0jrfNRR2yjMnVYaqNHKGKy0b/1I8DkD/JtclgJfq7SPti1w==",
"cpu": [
"ppc64"
],
@ -841,9 +841,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.15.0.tgz",
"integrity": "sha512-3A1FbHDbBUvpJXFAZwVsiROIcstVHP9AX/cwnyIhAp+xyQ1cBCxywKtuzmw0Av1MDNNg/y/9dDHtNypfRa8bdw==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.16.0.tgz",
"integrity": "sha512-qxAB8MiHuDI8jU0D+WI9Gym3fvUJHA/AjKRXxbEH921SB3AeKQStq1FKFA59dAoqqCArjJ1voXM/gMvgEc1q4Q==",
"cpu": [
"riscv64"
],
@ -854,9 +854,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.15.0.tgz",
"integrity": "sha512-hYPbhg9ow6/mXIkojc8LOeiip2sCTuw1taWyoOXTOWk9vawIXz8x7B4KkgWUAtvAElssxhSyEXr2EZycH/FGzQ==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.16.0.tgz",
"integrity": "sha512-j/9yBgWFlNFBfG/S1M2zkBNLeLkNVG59T5c4tlmlrxU+XITWJ3aMVWdpcZ/+mu7auGZftAXueAgAE9mb4lAlag==",
"cpu": [
"s390x"
],
@ -867,9 +867,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.15.0.tgz",
"integrity": "sha512-511qln5mPSUKwv7HI28S1jCD1FK+2WbX5THM9A9annr3c1kzmfnf8Oe3ZakubEjob3IV6OPnNNcesfy+adIrmw==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.16.0.tgz",
"integrity": "sha512-SjsBA1a9wrEleNneGEsR40HdxKdwCatyHC547o/XINqwPW4cqTYiNy/lL1WTJYWU/KgWIb8HH4SgmFStbWoBzw==",
"cpu": [
"x64"
],
@ -880,9 +880,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.15.0.tgz",
"integrity": "sha512-4qKKGTDIv2bQZ+afhPWqPL+94+dLtk4lw1iwbcylKlLNqQ/Yyjof2CFYBxf6npiDzPV+zf4EWRiHb26/4Vsm9w==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.16.0.tgz",
"integrity": "sha512-YKCs7ghJZ5po6/qgfONiXyFKOKcTK4Kerzk/Kc89QK0JT94Qg4NurL+3Y3rZh5am2tu1OlvHPpBHQNBE8cFgJQ==",
"cpu": [
"x64"
],
@ -893,9 +893,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.15.0.tgz",
"integrity": "sha512-nEtaFBHp1OnbOf+tz66DtID579sNRHGgMC23to8HUyVuOCpCMD0CvRNqiDGLErLNnwApWIUtUl1VvuovCWUxwg==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.16.0.tgz",
"integrity": "sha512-+wtkF+z2nw0ZwwHji01wOW0loxFl24lBNxPtVAXtnPPDL9Ew0EhiCMOegXe/EAH3Zlr8Iw9tyPJXB3DltQLEyw==",
"cpu": [
"arm64"
],
@ -906,9 +906,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.15.0.tgz",
"integrity": "sha512-5O49NykwSgX6iT2HgZ6cAoGHt6T/FqNMB5OqFOGxU/y1GyFSHquox1sK2OqApQc0ANxiHFQEMNDLNVCL7AUDnQ==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.16.0.tgz",
"integrity": "sha512-7qLyKTL7Lf2g0B8bduETVAEI3WVUVwBRVcECojVevPNVAmi19IW1P2X+uMSwhmWNy36Q/qEvxXsfts1I8wpawg==",
"cpu": [
"ia32"
],
@ -919,9 +919,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.15.0.tgz",
"integrity": "sha512-YA0hTwCunmKNeTOFWdJuKhdXse9jBqgo34FDo+9aS0spfCkp+wj0o1bCcOOTu+0P48O95GTfkLTAaVonwNuIdQ==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.16.0.tgz",
"integrity": "sha512-tkfxXt+7c3Ecgn7ln9NJPdBM+QKwQdmFFpgAP+FYhAuRS5y3tY8xeza82gFjbPpytkHmaQnVdMtuzbToCz2tuw==",
"cpu": [
"x64"
],
@ -2494,9 +2494,9 @@
}
},
"node_modules/rollup": {
"version": "4.15.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.15.0.tgz",
"integrity": "sha512-i0ir57IMF5o7YvNYyUNeIGG+IZaaucnGZAOsSctO2tPLXlCEaZzyBa+QhpHNSgtpyLMoDev2DyN6a7J1dQA8Tw==",
"version": "4.16.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.16.0.tgz",
"integrity": "sha512-joxy/Hd4Ee289394//Q1aoebcxXyHasDieCTk8YtP4G4al4TUlx85EnuCLrfrdtLzrna9kNjH++Sx063wxSgmA==",
"dev": true,
"dependencies": {
"@types/estree": "1.0.5"
@ -2509,22 +2509,22 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.15.0",
"@rollup/rollup-android-arm64": "4.15.0",
"@rollup/rollup-darwin-arm64": "4.15.0",
"@rollup/rollup-darwin-x64": "4.15.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.15.0",
"@rollup/rollup-linux-arm-musleabihf": "4.15.0",
"@rollup/rollup-linux-arm64-gnu": "4.15.0",
"@rollup/rollup-linux-arm64-musl": "4.15.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.15.0",
"@rollup/rollup-linux-riscv64-gnu": "4.15.0",
"@rollup/rollup-linux-s390x-gnu": "4.15.0",
"@rollup/rollup-linux-x64-gnu": "4.15.0",
"@rollup/rollup-linux-x64-musl": "4.15.0",
"@rollup/rollup-win32-arm64-msvc": "4.15.0",
"@rollup/rollup-win32-ia32-msvc": "4.15.0",
"@rollup/rollup-win32-x64-msvc": "4.15.0",
"@rollup/rollup-android-arm-eabi": "4.16.0",
"@rollup/rollup-android-arm64": "4.16.0",
"@rollup/rollup-darwin-arm64": "4.16.0",
"@rollup/rollup-darwin-x64": "4.16.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.16.0",
"@rollup/rollup-linux-arm-musleabihf": "4.16.0",
"@rollup/rollup-linux-arm64-gnu": "4.16.0",
"@rollup/rollup-linux-arm64-musl": "4.16.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.16.0",
"@rollup/rollup-linux-riscv64-gnu": "4.16.0",
"@rollup/rollup-linux-s390x-gnu": "4.16.0",
"@rollup/rollup-linux-x64-gnu": "4.16.0",
"@rollup/rollup-linux-x64-musl": "4.16.0",
"@rollup/rollup-win32-arm64-msvc": "4.16.0",
"@rollup/rollup-win32-ia32-msvc": "4.16.0",
"@rollup/rollup-win32-x64-msvc": "4.16.0",
"fsevents": "~2.3.2"
}
},

View File

@ -5,7 +5,7 @@
"scripts": {
"serve": "vite --port 3000",
"build": "vite build --base='./'",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
"lint": "eslint",
"dev": "vite",
"format": "prettier . --write",
"i18n:report": "vue-cli-service i18n:report --src \"./src/**/*.?(js|vue)\" --locales \"./src/i18n/**/*.json\"",
@ -33,7 +33,7 @@
"devDependencies": {
"@intlify/unplugin-vue-i18n": "^4.0.0",
"@vitejs/plugin-vue": "^5.0.4",
"eslint": "^8.56.0",
"eslint": "^9.1.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-vue": "^9.25.0",
"prettier": "^3.2.5",

View File

@ -37,18 +37,18 @@ import webapi from '@/webapi'
export default {
name: 'App',
components: {
NavbarTop,
NavbarBottom,
NotificationList,
ModalDialogRemotePairing,
ModalDialogUpdate
ModalDialogUpdate,
NavbarBottom,
NavbarTop,
NotificationList
},
data() {
return {
token_timer_id: 0,
pairing_active: false,
reconnect_attempts: 0,
pairing_active: false
token_timer_id: 0
}
},
@ -90,10 +90,8 @@ export default {
created() {
this.connect()
// Start the progress bar on app start
this.$Progress.start()
// Hook the progress bar to start before we move router-view
this.$router.beforeEach((to, from, next) => {
if (to.meta.show_progress && !(to.path === from.path && to.hash)) {
@ -104,7 +102,6 @@ export default {
}
next()
})
// Hook the progress bar to finish after we've finished moving router-view
this.$router.afterEach((to, from) => {
if (to.meta.show_progress) {
@ -128,12 +125,11 @@ export default {
.catch(() => {
this.$store.dispatch('add_notification', {
text: this.$t('server.connection-failed'),
type: 'danger',
topic: 'connection'
topic: 'connection',
type: 'danger'
})
})
},
open_ws() {
if (this.$store.state.config.websocket_port <= 0) {
this.$store.dispatch('add_notification', {
@ -143,15 +139,13 @@ export default {
return
}
const vm = this
let protocol = 'ws://'
if (window.location.protocol === 'https:') {
protocol = 'wss://'
}
let wsUrl = `${protocol + window.location.hostname}:${
vm.$store.state.config.websocket_port
this.$store.state.config.websocket_port
}`
if (import.meta.env.DEV && import.meta.env.VITE_OWNTONE_URL) {
@ -161,15 +155,16 @@ export default {
*/
const owntoneUrl = new URL(import.meta.env.VITE_OWNTONE_URL)
wsUrl = `${protocol + owntoneUrl.hostname}:${
vm.$store.state.config.websocket_port
this.$store.state.config.websocket_port
}`
}
const socket = new ReconnectingWebSocket(wsUrl, 'notify', {
reconnectInterval: 1000,
maxReconnectInterval: 2000
maxReconnectInterval: 2000,
reconnectInterval: 1000
})
const vm = this
socket.onopen = () => {
vm.reconnect_attempts = 0
socket.send(
@ -188,7 +183,6 @@ export default {
]
})
)
vm.update_outputs()
vm.update_player_status()
vm.update_library_stats()
@ -207,11 +201,10 @@ export default {
*/
let update_throttled = false
function update_info() {
const update_info = () => {
if (update_throttled) {
return
}
vm.update_outputs()
vm.update_player_status()
vm.update_library_stats()
@ -220,7 +213,6 @@ export default {
vm.update_spotify()
vm.update_lastfm()
vm.update_pairing()
update_throttled = true
setTimeout(() => {
update_throttled = false
@ -270,7 +262,29 @@ export default {
}
}
},
update_is_clipped() {
if (this.show_burger_menu || this.show_player_menu) {
document.querySelector('html').classList.add('is-clipped')
} else {
document.querySelector('html').classList.remove('is-clipped')
}
},
update_outputs() {
webapi.outputs().then(({ data }) => {
this.$store.commit(types.UPDATE_OUTPUTS, data.outputs)
})
},
update_player_status() {
webapi.player_status().then(({ data }) => {
this.$store.commit(types.UPDATE_PLAYER_STATUS, data)
this.update_lyrics()
})
},
update_lastfm() {
webapi.lastfm().then(({ data }) => {
this.$store.commit(types.UPDATE_LASTFM, data)
})
},
update_library_stats() {
webapi.library_stats().then(({ data }) => {
this.$store.commit(types.UPDATE_LIBRARY_STATS, data)
@ -279,27 +293,6 @@ export default {
this.$store.commit(types.UPDATE_LIBRARY_RSS_COUNT, data)
})
},
update_outputs() {
webapi.outputs().then(({ data }) => {
this.$store.commit(types.UPDATE_OUTPUTS, data.outputs)
})
},
update_player_status() {
webapi.player_status().then(({ data }) => {
this.$store.commit(types.UPDATE_PLAYER_STATUS, data)
this.update_lyrics()
})
},
update_queue() {
webapi.queue().then(({ data }) => {
this.$store.commit(types.UPDATE_QUEUE, data)
this.update_lyrics()
})
},
update_lyrics() {
const track = this.$store.getters.now_playing
if (track && track.track_id) {
@ -310,19 +303,23 @@ export default {
this.$store.commit(types.UPDATE_LYRICS)
}
},
update_pairing() {
webapi.pairing().then(({ data }) => {
this.$store.commit(types.UPDATE_PAIRING, data)
this.pairing_active = data.active
})
},
update_queue() {
webapi.queue().then(({ data }) => {
this.$store.commit(types.UPDATE_QUEUE, data)
this.update_lyrics()
})
},
update_settings() {
webapi.settings().then(({ data }) => {
this.$store.commit(types.UPDATE_SETTINGS, data)
})
},
update_lastfm() {
webapi.lastfm().then(({ data }) => {
this.$store.commit(types.UPDATE_LASTFM, data)
})
},
update_spotify() {
webapi.spotify().then(({ data }) => {
this.$store.commit(types.UPDATE_SPOTIFY, data)
@ -338,21 +335,6 @@ export default {
)
}
})
},
update_pairing() {
webapi.pairing().then(({ data }) => {
this.$store.commit(types.UPDATE_PAIRING, data)
this.pairing_active = data.active
})
},
update_is_clipped() {
if (this.show_burger_menu || this.show_player_menu) {
document.querySelector('html').classList.add('is-clipped')
} else {
document.querySelector('html').classList.remove('is-clipped')
}
}
},
template: '<App/>'

View File

@ -1,15 +1,15 @@
<template>
<div
v-click-away="onClickOutside"
v-click-away="deactivate"
class="dropdown"
:class="{ 'is-active': is_active }"
:class="{ 'is-active': active }"
>
<div class="dropdown-trigger">
<button
class="button"
aria-haspopup="true"
aria-controls="dropdown"
@click="is_active = !is_active"
@click="active = !active"
>
<span v-text="option.name" />
<mdicon class="icon" name="chevron-down" size="16" />
@ -41,7 +41,7 @@ export default {
data() {
return {
is_active: false
active: false
}
},
@ -54,12 +54,11 @@ export default {
},
methods: {
onClickOutside(event) {
this.is_active = false
deactivate() {
this.active = false
},
select(option) {
this.is_active = false
this.active = false
this.$emit('update:value', option.id)
}
}

View File

@ -6,8 +6,9 @@
:key="index"
class="button is-small"
:to="{ hash: `#index_${index}`, query: $route.query }"
>{{ index }}</router-link
>
{{ index }}
</router-link>
</nav>
</section>
</template>

View File

@ -50,6 +50,44 @@ export default {
is_playing() {
return this.player.state === 'play'
},
lyrics() {
const raw = this.$store.state.lyrics.content
const parsed = []
if (raw) {
// Parse the lyrics
const regex = /(\[(\d+):(\d+)(?:\.\d+)?\] ?)?(.*)/u
raw.split('\n').forEach((item, index) => {
const matches = regex.exec(item)
if (matches && matches[4]) {
const verse = {
text: matches[4],
time: matches[2] * 60 + Number(matches[3])
}
parsed.push(verse)
}
})
// Split the verses into words
parsed.forEach((verse, index, lyrics) => {
const duration =
index < lyrics.length - 1 ? lyrics[index + 1].time - verse.time : 3
const unitDuration = duration / verse.text.length
let delay = 0
verse.words = verse.text.match(/\S+\s*/gu).map((text) => {
const duration = text.length * unitDuration
delay += duration
return {
duration,
delay,
text
}
})
})
}
return parsed
},
player() {
return this.$store.state.player
},
verse_index() {
if (this.lyrics.length && this.lyrics[0].time) {
const currentTime = this.player.item_progress_ms / 1000,
@ -68,17 +106,19 @@ export default {
(this.lastIndex < la.length - 1 &&
la[this.lastIndex + 1].time > currentTime) ||
this.lastIndex === la.length - 1
)
) {
return this.lastIndex
}
if (
this.lastIndex < la.length - 2 &&
la[this.lastIndex + 2].time > currentTime
)
) {
return this.lastIndex + 1
}
// Not found, then start a binary search
let start = 0,
end = la.length - 1,
index
let end = la.length - 1,
index = 0,
start = 0
while (start <= end) {
index = (start + end) >> 1
const currentVerse = la[index]
@ -97,44 +137,6 @@ export default {
}
this.reset_scrolling()
return -1
},
lyrics() {
const raw = this.$store.state.lyrics.content
const parsed = []
if (raw) {
// Parse the lyrics
const regex = /(\[(\d+):(\d+)(?:\.\d+)?\] ?)?(.*)/
raw.split('\n').forEach((item, index) => {
const matches = regex.exec(item)
if (matches && matches[4]) {
const verse = {
text: matches[4],
time: matches[2] * 60 + Number(matches[3])
}
parsed.push(verse)
}
})
// Split the verses into words
parsed.forEach((verse, index, lyrics) => {
const duration =
index < lyrics.length - 1 ? lyrics[index + 1].time - verse.time : 3
const unitDuration = duration / verse.text.length
let delay = 0
verse.words = verse.text.match(/\S+\s*/g).map((text) => {
const duration = text.length * unitDuration
delay += duration
return {
duration,
delay,
text
}
})
})
}
return parsed
},
player() {
return this.$store.state.player
}
},
watch: {
@ -152,17 +154,6 @@ export default {
this.lastItemId = this.player.item_id
this.lastIndex = -1
},
start_scrolling(e) {
// Consider only user events
if (e.screenX || e.screenX !== 0 || e.screenY || e.screenY !== 0) {
this.autoScrolling = false
if (this.scrollingTimer) {
clearTimeout(this.scrollingTimer)
}
// Reenable automatic scrolling after 2 seconds
this.scrollingTimer = setTimeout((this.autoScrolling = true), 2000)
}
},
scroll_to_verse() {
const pane = this.$refs.lyrics
if (this.verse_index === -1) {
@ -179,6 +170,17 @@ export default {
(currentVerse.offsetHeight >> 1) -
pane.scrollTop
})
},
start_scrolling(e) {
// Consider only user events
if (e.screenX || e.screenX !== 0 || e.screenY || e.screenY !== 0) {
this.autoScrolling = false
if (this.scrollingTimer) {
clearTimeout(this.scrollingTimer)
}
// Reenable automatic scrolling after 2 seconds
this.scrollingTimer = setTimeout((this.autoScrolling = true), 2000)
}
}
}
}

View File

@ -134,7 +134,7 @@ export default {
mark_played() {
webapi
.library_album_track_update(this.item.id, { play_count: 'played' })
.then(({ data }) => {
.then(() => {
this.$emit('play-count-changed')
this.$emit('close')
})

View File

@ -6,23 +6,22 @@
<form class="card" @submit.prevent="save">
<div class="card-content">
<p class="title is-4" v-text="$t('dialog.playlist.save.title')" />
<div class="field">
<p class="control has-icons-left">
<input
ref="playlist_name_field"
v-model="playlist_name"
class="input is-shadowless"
type="text"
pattern=".+"
required
:placeholder="$t('dialog.playlist.save.playlist-name')"
:disabled="loading"
@input="check_name"
/>
<mdicon class="icon is-left" name="file-music" size="16" />
</p>
</div>
<div class="field">
<p class="control has-icons-left">
<input
ref="playlist_name_field"
v-model="playlist_name"
class="input is-shadowless"
type="text"
pattern=".+"
required
:placeholder="$t('dialog.playlist.save.playlist-name')"
:disabled="loading"
@input="check_name"
/>
<mdicon class="icon is-left" name="file-music" size="16" />
</p>
</div>
</div>
<footer v-if="loading" class="card-footer">
<a class="card-footer-item has-text-dark">

View File

@ -60,7 +60,8 @@
'is-loading': loading
}"
@click="togglePlay"
><mdicon class="icon" name="broadcast" size="18" />
>
<mdicon class="icon" name="broadcast" size="18" />
</a>
</div>
<div class="level-item">
@ -70,8 +71,8 @@
:class="{ 'has-text-grey-light': !playing }"
>
<p class="heading" v-text="$t('navigation.stream')" />
<a href="stream.mp3" class="heading ml-2" target="_blank"
><mdicon
<a href="stream.mp3" class="heading ml-2" target="_blank">
<mdicon
class="icon is-small"
name="open-in-new"
size="16"
@ -218,7 +219,8 @@
'is-loading': loading
}"
@click="togglePlay"
><mdicon class="icon" name="radio-tower" size="16" />
>
<mdicon class="icon" name="radio-tower" size="16" />
</a>
</div>
<div class="level-item">
@ -228,8 +230,8 @@
:class="{ 'has-text-grey-light': !playing }"
>
<p class="heading" v-text="$t('navigation.stream')" />
<a href="stream.mp3" class="heading ml-2" target="_blank"
><mdicon
<a href="stream.mp3" class="heading ml-2" target="_blank">
<mdicon
class="icon is-small"
name="open-in-new"
size="16"
@ -367,19 +369,19 @@ export default {
},
setupAudio() {
const a = audio.setup()
a.addEventListener('waiting', (e) => {
a.addEventListener('waiting', () => {
this.playing = false
this.loading = true
})
a.addEventListener('playing', (e) => {
a.addEventListener('playing', () => {
this.playing = true
this.loading = false
})
a.addEventListener('ended', (e) => {
a.addEventListener('ended', () => {
this.playing = false
this.loading = false
})
a.addEventListener('error', (e) => {
a.addEventListener('error', () => {
this.closeAudio()
this.$store.dispatch('add_notification', {
text: this.$t('navigation.stream-error'),

View File

@ -94,17 +94,17 @@
<b v-text="$t('navigation.search')" />
</navbar-item-link>
<hr class="my-3" />
<navbar-item-link :to="{ name: 'settings-webinterface' }">{{
$t('navigation.settings')
}}</navbar-item-link>
<navbar-item-link :to="{ name: 'settings-webinterface' }">
{{ $t('navigation.settings') }}
</navbar-item-link>
<a
class="navbar-item"
@click.stop.prevent="open_update_dialog()"
v-text="$t('navigation.update-library')"
/>
<navbar-item-link :to="{ name: 'about' }">{{
$t('navigation.about')
}}</navbar-item-link>
<navbar-item-link :to="{ name: 'about' }">
{{ $t('navigation.about') }}
</navbar-item-link>
</div>
</div>
</div>

View File

@ -8,8 +8,8 @@ const stringComparator = (a, b) => a.localeCompare(b, locale.value)
const dateComparator = (a, b) =>
new Date(a) - new Date(b) || (!a ? -1 : !b ? 1 : 0)
function createComparators(criteria) {
return criteria.map(({ field, type, order = 1 }) => {
const createComparators = (criteria) =>
criteria.map(({ field, type, order = 1 }) => {
switch (type) {
case String:
return (a, b) => stringComparator(a[field], b[field]) * order
@ -17,9 +17,10 @@ function createComparators(criteria) {
return (a, b) => numberComparator(a[field], b[field]) * order
case Date:
return (a, b) => dateComparator(a[field], b[field]) * order
default:
return (a, b) => 0
}
})
}
const characterIndex = (string = '') => {
const value = string.charAt(0)
@ -49,7 +50,7 @@ const timeIndex = (string) => {
return times.find((item) => isNaN(diff) || diff < item.difference)?.text(date)
}
function createIndexer({ field, type = undefined } = {}) {
const createIndexer = ({ field, type = undefined } = {}) => {
switch (type) {
case String:
return (item) => characterIndex(item[field])

View File

@ -146,25 +146,26 @@
keypath="page.about.built-with"
scope="global"
>
<template #bulma><a href="https://bulma.io">Bulma</a></template>
<template #mdi
><a href="https://pictogrammers.com/library/mdi/"
>Material Design Icons</a
></template
>
<template #vuejs
><a href="https://vuejs.org/">Vue.js</a></template
>
<template #axios
><a href="https://github.com/mzabriskie/axios"
>axios</a
></template
>
<template #others
><a
<template #bulma>
<a href="https://bulma.io">Bulma</a>
</template>
<template #mdi>
<a href="https://pictogrammers.com/library/mdi/">
Material Design Icons
</a>
</template>
<template #vuejs>
<a href="https://vuejs.org/">Vue.js</a>
</template>
<template #axios>
<a href="https://github.com/mzabriskie/axios">axios</a>
</template>
<template #others>
<a
href="https://github.com/owntone/owntone-server/network/dependencies"
v-text="$t('page.about.more')"
/></template>
/>
</template>
</i18n-t>
</div>
</div>

View File

@ -15,8 +15,9 @@
<router-link
class="button is-light is-small is-rounded"
:to="{ name: 'music-recently-added' }"
>{{ $t('page.music.show-more') }}</router-link
>
{{ $t('page.music.show-more') }}
</router-link>
</p>
</nav>
</template>
@ -35,8 +36,9 @@
<router-link
class="button is-light is-small is-rounded"
:to="{ name: 'music-recently-played' }"
>{{ $t('page.music.show-more') }}</router-link
>
{{ $t('page.music.show-more') }}
</router-link>
</p>
</nav>
</template>

View File

@ -15,8 +15,9 @@
<router-link
:to="{ name: 'music-spotify-new-releases' }"
class="button is-light is-small is-rounded"
>{{ $t('page.spotify.music.show-more') }}</router-link
>
{{ $t('page.spotify.music.show-more') }}
</router-link>
</p>
</nav>
</template>
@ -38,8 +39,9 @@
<router-link
:to="{ name: 'music-spotify-featured-playlists' }"
class="button is-light is-small is-rounded"
>{{ $t('page.spotify.music.show-more') }}</router-link
>
{{ $t('page.spotify.music.show-more') }}
</router-link>
</p>
</nav>
</template>

View File

@ -86,53 +86,6 @@ export default {
},
computed: {
is_live() {
return this.track.length_ms === 0
},
lyrics_visible() {
return this.$store.state.lyrics.pane
},
player() {
return this.$store.state.player
},
track() {
return this.$store.getters.now_playing
},
track_progress: {
get() {
return Math.floor(this.player.item_progress_ms / INTERVAL)
},
set(value) {
this.player.item_progress_ms = value * INTERVAL
}
},
track_progress_max() {
return this.is_live ? 1 : Math.floor(this.track.length_ms / INTERVAL)
},
track_elapsed_time() {
return this.$filters.durationInHours(this.track_progress * INTERVAL)
},
track_total_time() {
return this.is_live
? this.$t('page.now-playing.live')
: this.$filters.durationInHours(this.track.length_ms)
},
settings_option_show_composer_now_playing() {
return this.$store.getters.settings_option_show_composer_now_playing
},
settings_option_show_composer_for_genre() {
return this.$store.getters.settings_option_show_composer_for_genre
},
composer() {
if (this.settings_option_show_composer_now_playing) {
if (
@ -151,16 +104,51 @@ export default {
}
return null
},
settings_option_show_filepath_now_playing() {
return this.$store.getters.settings_option_show_filepath_now_playing
},
filepath() {
if (this.settings_option_show_filepath_now_playing) {
return this.track.path
}
return null
},
is_live() {
return this.track.length_ms === 0
},
lyrics_visible() {
return this.$store.state.lyrics.pane
},
player() {
return this.$store.state.player
},
settings_option_show_composer_for_genre() {
return this.$store.getters.settings_option_show_composer_for_genre
},
settings_option_show_composer_now_playing() {
return this.$store.getters.settings_option_show_composer_now_playing
},
settings_option_show_filepath_now_playing() {
return this.$store.getters.settings_option_show_filepath_now_playing
},
track() {
return this.$store.getters.now_playing
},
track_elapsed_time() {
return this.$filters.durationInHours(this.track_progress * INTERVAL)
},
track_progress: {
get() {
return Math.floor(this.player.item_progress_ms / INTERVAL)
},
set(value) {
this.player.item_progress_ms = value * INTERVAL
}
},
track_progress_max() {
return this.is_live ? 1 : Math.floor(this.track.length_ms / INTERVAL)
},
track_total_time() {
return this.is_live
? this.$t('page.now-playing.live')
: this.$filters.durationInHours(this.track.length_ms)
}
},
@ -193,29 +181,25 @@ export default {
},
methods: {
tick() {
if (!this.is_dragged) {
this.track_progress += 1
}
},
start_dragging() {
this.is_dragged = true
},
end_dragging() {
this.is_dragged = false
},
open_dialog(item) {
this.selected_item = item
this.show_details_modal = true
},
seek() {
if (!this.is_live) {
webapi.player_seek_to_pos(this.track_progress * INTERVAL)
}
},
open_dialog(item) {
this.selected_item = item
this.show_details_modal = true
start_dragging() {
this.is_dragged = true
},
tick() {
if (!this.is_dragged) {
this.track_progress += 1
}
}
}
}

View File

@ -117,7 +117,7 @@ export default {
this.new_episodes.items = {}
},
open_add_podcast_dialog(item) {
open_add_podcast_dialog() {
this.show_url_modal = true
},

View File

@ -172,27 +172,26 @@ export default {
webapi.queue_move(item.id, newPosition)
}
},
open_add_stream_dialog() {
this.show_url_modal = true
},
open_dialog(item) {
this.selected_item = item
this.show_details_modal = true
},
open_add_stream_dialog() {
this.show_url_modal = true
},
queue_clear() {
webapi.queue_clear()
},
remove(item) {
webapi.queue_remove(item.id)
},
update_show_next_items() {
this.$store.commit(types.SHOW_ONLY_NEXT_ITEMS, !this.show_only_next_items)
},
save_dialog() {
if (this.queue_items.length > 0) {
this.show_pls_save_modal = true
}
},
update_show_next_items() {
this.$store.commit(types.SHOW_ONLY_NEXT_ITEMS, !this.show_only_next_items)
}
}
}

View File

@ -23,12 +23,13 @@
scope="global"
>
<template #query><code>query:</code></template>
<template #help
><a
<template #help>
<a
href="https://owntone.github.io/owntone-server/smart-playlists/"
target="_blank"
v-text="$t('page.search.expression')"
/></template>
/>
</template>
</i18n-t>
</div>
</form>

View File

@ -59,8 +59,8 @@ export default createStore({
settings: {
categories: []
},
show_only_next_items: false,
show_burger_menu: false,
show_only_next_items: false,
show_player_menu: false,
show_update_dialog: false,
spotify: {},
@ -213,18 +213,18 @@ export default createStore({
state.recent_searches.pop()
}
},
remove_recent_search({ commit, state }, query) {
const index = state.recent_searches.indexOf(query)
if (index !== -1) {
state.recent_searches.splice(index, 1)
}
},
delete_notification({ commit, state }, notification) {
const index = state.notifications.list.indexOf(notification)
if (index !== -1) {
state.notifications.list.splice(index, 1)
}
},
remove_recent_search({ commit, state }, query) {
const index = state.recent_searches.indexOf(query)
if (index !== -1) {
state.recent_searches.splice(index, 1)
}
},
update_settings_option({ commit, state }, option) {
const settingCategory = state.settings.categories.find(
(e) => e.name === option.category

View File

@ -7,9 +7,9 @@
<div ref="options_ref" style="height: 1px" />
<slot name="options" />
<nav class="buttons is-centered mt-4 mb-2">
<router-link class="button is-small is-white" :to="position"
><mdicon class="icon is-small" :name="icon_name" size="16"
/></router-link>
<router-link class="button is-small is-white" :to="position">
<mdicon class="icon is-small" :name="icon_name" size="16" />
</router-link>
</nav>
</section>
<div :class="{ 'is-full-height': $slots.options }">

View File

@ -1,7 +1,7 @@
import path from 'path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import i18n from '@intlify/unplugin-vue-i18n/vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
/*
* In development mode, use the VITE_OWNTONE_URL environment variable to set