chore: upgrade tooling stack
- TypeScript 3.9 -> 5.8 - ESLint 6 -> 9 (flat config) - Prettier 1 -> 3 - eslint-plugin-vue 6 -> 9 - Add vue-tsc for type checking - Migrate Sass @import to @use syntax - Fix Pinia persistence config for v4 - Add Spotify type declarations - Remove unused registerServiceWorker.ts
This commit is contained in:
21
.eslintrc.js
21
.eslintrc.js
@@ -1,21 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
node: true
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'plugin:vue/essential',
|
|
||||||
'eslint:recommended',
|
|
||||||
'@vue/typescript/recommended',
|
|
||||||
'@vue/prettier',
|
|
||||||
'@vue/prettier/@typescript-eslint'
|
|
||||||
],
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 2020
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/camelcase': 'off',
|
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
{
|
{
|
||||||
"semi": false,
|
"semi": false,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"printWidth": 80,
|
||||||
|
"endOfLine": "lf",
|
||||||
"arrowParens": "always"
|
"arrowParens": "always"
|
||||||
}
|
}
|
||||||
|
|||||||
33
eslint.config.js
Normal file
33
eslint.config.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import js from '@eslint/js'
|
||||||
|
import pluginVue from 'eslint-plugin-vue'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
import eslintConfigPrettier from 'eslint-config-prettier'
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{
|
||||||
|
ignores: ['dist/**', 'node_modules/**', '*.config.js', '*.config.ts']
|
||||||
|
},
|
||||||
|
js.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
...pluginVue.configs['flat/essential'],
|
||||||
|
{
|
||||||
|
files: ['**/*.vue'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
parser: tseslint.parser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-unused-vars': 'warn',
|
||||||
|
'@typescript-eslint/no-unused-expressions': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
'@typescript-eslint/no-empty-object-type': 'off',
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
eslintConfigPrettier
|
||||||
|
)
|
||||||
21
package.json
21
package.json
@@ -7,7 +7,8 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "eslint src --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
"type-check": "vue-tsc --noEmit",
|
||||||
|
"lint": "eslint src --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
@@ -20,21 +21,21 @@
|
|||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.39.2",
|
||||||
"@intlify/unplugin-vue-i18n": "^6.0.8",
|
"@intlify/unplugin-vue-i18n": "^6.0.8",
|
||||||
"@types/webpack": "^4.4.0",
|
"@types/node": "^25.0.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
|
||||||
"@typescript-eslint/parser": "^2.33.0",
|
|
||||||
"@vitejs/plugin-legacy": "^7.0.1",
|
"@vitejs/plugin-legacy": "^7.0.1",
|
||||||
"@vitejs/plugin-vue": "^6.0.0",
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
"eslint": "^6.7.2",
|
"eslint": "^9.18.0",
|
||||||
"eslint-plugin-prettier": "^3.1.3",
|
"eslint-config-prettier": "^10.0.1",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"eslint-plugin-vue": "^9.32.0",
|
||||||
"prettier": "^1.19.1",
|
"prettier": "^3.4.2",
|
||||||
"sass": "^1.89.2",
|
"sass": "^1.89.2",
|
||||||
"sass-loader": "^8.0.2",
|
"typescript": "^5.8.0",
|
||||||
"typescript": "~3.9.3",
|
"typescript-eslint": "^8.21.0",
|
||||||
"vite": "^7.0.5",
|
"vite": "^7.0.5",
|
||||||
"vite-plugin-pwa": "^1.0.1",
|
"vite-plugin-pwa": "^1.0.1",
|
||||||
|
"vue-tsc": "^2.2.0",
|
||||||
"workbox-build": "^7.3.0",
|
"workbox-build": "^7.3.0",
|
||||||
"workbox-window": "^7.3.0"
|
"workbox-window": "^7.3.0"
|
||||||
}
|
}
|
||||||
|
|||||||
4936
pnpm-lock.yaml
generated
4936
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ import SWNewVersion from '@/components/SWNewVersion.vue'
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '@/styles/app';
|
@use '@/styles/app';
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ const check = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '@/styles/variables';
|
|
||||||
|
|
||||||
.dev-session {
|
.dev-session {
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ const onPause = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { ready, play, pause } = useSpotify(onPlay, onPause)
|
const { ready, play: startPlay, pause: stopPlay } = useSpotify(onPlay, onPause)
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
props.play ? play() : pause()
|
props.play ? startPlay() : stopPlay()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -92,8 +92,6 @@ const onPause = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '@/styles/variables';
|
|
||||||
|
|
||||||
.pm-field {
|
.pm-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { ref, reactive, onMounted, computed } from 'vue'
|
|||||||
export const useSpotify = (onPlay: () => void, onPause: () => void) => {
|
export const useSpotify = (onPlay: () => void, onPause: () => void) => {
|
||||||
const ready = ref(false)
|
const ready = ref(false)
|
||||||
const deviceId = ref<string | null>(null)
|
const deviceId = ref<string | null>(null)
|
||||||
const spotify = reactive({
|
const spotify = reactive<{ player: SpotifyPlayer | null }>({
|
||||||
player: null
|
player: null
|
||||||
})
|
})
|
||||||
const store = useMainStore()
|
const store = useMainStore()
|
||||||
@@ -26,25 +26,29 @@ export const useSpotify = (onPlay: () => void, onPause: () => void) => {
|
|||||||
|
|
||||||
spotify.player = new Spotify.Player({
|
spotify.player = new Spotify.Player({
|
||||||
name: 'binome',
|
name: 'binome',
|
||||||
getOAuthToken: (callback) => callback(accessToken.value)
|
getOAuthToken: (callback: (token: string) => void) =>
|
||||||
|
callback(accessToken.value ?? '')
|
||||||
})
|
})
|
||||||
spotify.player.addListener('ready', ({ device_id }) => {
|
spotify.player.addListener('ready', (data: unknown) => {
|
||||||
|
const { device_id } = data as { device_id: string }
|
||||||
deviceId.value = device_id
|
deviceId.value = device_id
|
||||||
ready.value = true
|
ready.value = true
|
||||||
})
|
})
|
||||||
spotify.player.addListener('player_state_changed', (state) => {
|
spotify.player.addListener('player_state_changed', (data: unknown) => {
|
||||||
|
const state = data as SpotifyPlayerState | null
|
||||||
if (!state) {
|
if (!state) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.paused) {
|
if (state.paused) {
|
||||||
store.setSong(null)
|
store.song = null
|
||||||
onPause()
|
onPause()
|
||||||
} else {
|
} else {
|
||||||
store.setSong(state.track_window?.current_track?.name ?? null)
|
store.song = state.track_window?.current_track?.name ?? null
|
||||||
onPlay()
|
onPlay()
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
await spotify.player.connect()
|
await spotify.player.connect()
|
||||||
}
|
}
|
||||||
import('@/lib/spotify-player')
|
import('@/lib/spotify-player')
|
||||||
@@ -59,7 +63,9 @@ export const useSpotify = (onPlay: () => void, onPause: () => void) => {
|
|||||||
const playerState = await spotify.player.getCurrentState()
|
const playerState = await spotify.player.getCurrentState()
|
||||||
|
|
||||||
if (!playerState) {
|
if (!playerState) {
|
||||||
|
if (accessToken.value && deviceId.value) {
|
||||||
playOnSpotify(accessToken.value, deviceId.value)
|
playOnSpotify(accessToken.value, deviceId.value)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await spotify.player.resume()
|
await spotify.player.resume()
|
||||||
@@ -71,7 +77,7 @@ export const useSpotify = (onPlay: () => void, onPause: () => void) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pause = () => {
|
const pause = () => {
|
||||||
if (ready.value) {
|
if (ready.value && accessToken.value && deviceId.value) {
|
||||||
pauseOnSpotify(accessToken.value, deviceId.value)
|
pauseOnSpotify(accessToken.value, deviceId.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,9 +102,9 @@ export const useSpotifyConnect = (hash: string) => {
|
|||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
store.setAccessToken(connection['access_token'])
|
store.accessToken = connection['access_token']
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
store.setTokenExpire(
|
store.tokenExpire = new Date(
|
||||||
new Date(now.getTime() + parseInt(connection['expires_in']) * 1000)
|
now.getTime() + parseInt(connection['expires_in']) * 1000
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ export const useTimer = () => {
|
|||||||
const session = computed(() => formatSeconds(sessionSeconds.value))
|
const session = computed(() => formatSeconds(sessionSeconds.value))
|
||||||
const intervalSeconds = computed(() => interval.value * 60)
|
const intervalSeconds = computed(() => interval.value * 60)
|
||||||
|
|
||||||
let stopwatchId: NodeJS.Timeout | null = null
|
let stopwatchId: ReturnType<typeof setInterval> | null = null
|
||||||
let stopWatchSessionId: NodeJS.Timeout | null = null
|
let stopWatchSessionId: ReturnType<typeof setInterval> | null = null
|
||||||
|
|
||||||
watch(interval, () => {
|
watch(interval, () => {
|
||||||
sessionSeconds.value = interval.value * 60
|
sessionSeconds.value = interval.value * 60
|
||||||
@@ -55,8 +55,8 @@ export const useTimer = () => {
|
|||||||
|
|
||||||
toggleSound.play()
|
toggleSound.play()
|
||||||
|
|
||||||
notify(i18n.t('notification.change.title').toString(), {
|
notify(i18n.global.t('notification.change.title'), {
|
||||||
body: i18n.t('notification.change.body', { dev }).toString()
|
body: i18n.global.t('notification.change.body', { dev })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
interface WakeLock {
|
|
||||||
request(type: string): Promise<WakeLockSentinel>
|
|
||||||
}
|
|
||||||
|
|
||||||
type WakeLockType = 'screen' | null
|
|
||||||
|
|
||||||
interface WakeLockSentinel {
|
|
||||||
release(): Promise<void>
|
|
||||||
readonly WakeLocktype: WakeLockType
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NavigatorExtended extends Navigator {
|
|
||||||
readonly wakeLock: WakeLock
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useWakeLock = () => {
|
export const useWakeLock = () => {
|
||||||
let wakeLock: WakeLockSentinel | null = null
|
let wakeLock: WakeLockSentinel | null = null
|
||||||
|
|
||||||
@@ -22,10 +7,8 @@ export const useWakeLock = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
wakeLock = await (navigator as NavigatorExtended).wakeLock.request(
|
wakeLock = await navigator.wakeLock.request('screen')
|
||||||
'screen'
|
} catch {
|
||||||
)
|
|
||||||
} catch (err) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/lib/spotify-player.d.ts
vendored
Normal file
2
src/lib/spotify-player.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
declare const _default: void
|
||||||
|
export default _default
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/* eslint-disable no-console */
|
|
||||||
|
|
||||||
import { register } from 'register-service-worker'
|
|
||||||
import { setNotificationInstance, notify } from './utils/notification'
|
|
||||||
import i18n from '@/i18n'
|
|
||||||
import { emit } from 'retrobus'
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
register(`${process.env.BASE_URL}service-worker.js`, {
|
|
||||||
ready(sw) {
|
|
||||||
sw.getNotifications().then((notifs) => {
|
|
||||||
notifs.forEach((notif) => notif.close())
|
|
||||||
})
|
|
||||||
console.log('App is being served from cache by a service worker')
|
|
||||||
setNotificationInstance((title, options?) =>
|
|
||||||
sw.showNotification(title, options)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
registered() {
|
|
||||||
console.log('Service worker has been registered.')
|
|
||||||
},
|
|
||||||
cached() {
|
|
||||||
console.log('Content has been cached for offline use.')
|
|
||||||
},
|
|
||||||
updatefound() {
|
|
||||||
console.log('New content is downloading.')
|
|
||||||
},
|
|
||||||
updated(sw) {
|
|
||||||
console.log('New content is available; please refresh.')
|
|
||||||
setNotificationInstance((title, options?) =>
|
|
||||||
sw.showNotification(title, options)
|
|
||||||
)
|
|
||||||
notify(i18n.t('notification.update.title').toString(), {
|
|
||||||
body: i18n.t('notification.update.body').toString(),
|
|
||||||
tag: 'new-version'
|
|
||||||
})
|
|
||||||
emit('new-version')
|
|
||||||
},
|
|
||||||
offline() {
|
|
||||||
console.log(
|
|
||||||
'No internet connection found. App is running in offline mode.'
|
|
||||||
)
|
|
||||||
},
|
|
||||||
error(error) {
|
|
||||||
console.error('Error during service worker registration:', error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -68,14 +68,14 @@ export const useMainStore = defineStore('main', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
cleanStore() {
|
cleanStore() {
|
||||||
this.setSong(null)
|
this.song = null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
persist: {
|
persist: {
|
||||||
key: 'binome',
|
key: 'binome',
|
||||||
storage: localStorage,
|
storage: localStorage,
|
||||||
paths: [
|
pick: [
|
||||||
'interval',
|
'interval',
|
||||||
'hasMusic',
|
'hasMusic',
|
||||||
'hasSpotify',
|
'hasSpotify',
|
||||||
|
|||||||
42
src/types/spotify.d.ts
vendored
Normal file
42
src/types/spotify.d.ts
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
declare global {
|
||||||
|
interface SpotifyPlayer {
|
||||||
|
connect(): Promise<boolean>
|
||||||
|
disconnect(): void
|
||||||
|
addListener(event: string, callback: (data: unknown) => void): void
|
||||||
|
removeListener(event: string, callback?: (data: unknown) => void): void
|
||||||
|
getCurrentState(): Promise<SpotifyPlayerState | null>
|
||||||
|
resume(): Promise<void>
|
||||||
|
pause(): Promise<void>
|
||||||
|
togglePlay(): Promise<void>
|
||||||
|
seek(position_ms: number): Promise<void>
|
||||||
|
previousTrack(): Promise<void>
|
||||||
|
nextTrack(): Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpotifyPlayerState {
|
||||||
|
paused: boolean
|
||||||
|
track_window?: {
|
||||||
|
current_track?: {
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpotifyPlayerConstructorOptions {
|
||||||
|
name: string
|
||||||
|
getOAuthToken: (callback: (token: string) => void) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpotifyNamespace {
|
||||||
|
Player: new (options: SpotifyPlayerConstructorOptions) => SpotifyPlayer
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Window {
|
||||||
|
onSpotifyWebPlaybackSDKReady?: () => void
|
||||||
|
Spotify: SpotifyNamespace
|
||||||
|
}
|
||||||
|
|
||||||
|
const Spotify: SpotifyNamespace
|
||||||
|
}
|
||||||
|
|
||||||
|
export {}
|
||||||
@@ -21,7 +21,7 @@ export const notify: ShowNotification = async (title, options?) => {
|
|||||||
renotify: true,
|
renotify: true,
|
||||||
silent: true,
|
silent: true,
|
||||||
...options
|
...options
|
||||||
})
|
} as NotificationOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -101,8 +101,6 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '@/styles/variables';
|
|
||||||
|
|
||||||
.home {
|
.home {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '@/styles/variables';
|
|
||||||
|
|
||||||
.privacy {
|
.privacy {
|
||||||
font-family: $serif-font-family;
|
font-family: $serif-font-family;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "esnext",
|
"target": "ES2022",
|
||||||
"module": "esnext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "bundler",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
@@ -10,16 +10,19 @@
|
|||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"types": ["vite/client"],
|
"types": ["vite/client", "node"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"]
|
||||||
},
|
},
|
||||||
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noEmit": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
"src/**/*.tsx",
|
"src/**/*.tsx",
|
||||||
"src/**/*.vue",
|
"src/**/*.vue",
|
||||||
|
"src/**/*.d.ts",
|
||||||
"tests/**/*.ts",
|
"tests/**/*.ts",
|
||||||
"tests/**/*.tsx"
|
"tests/**/*.tsx"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export default defineConfig({
|
|||||||
css: {
|
css: {
|
||||||
preprocessorOptions: {
|
preprocessorOptions: {
|
||||||
scss: {
|
scss: {
|
||||||
additionalData: `@import "@/styles/variables.scss";`
|
additionalData: `@use "@/styles/variables" as *;`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user