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,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 80,
|
||||
"endOfLine": "lf",
|
||||
"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",
|
||||
"build": "vite build",
|
||||
"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": {
|
||||
"core-js": "^3.6.5",
|
||||
@@ -20,21 +21,21 @@
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@intlify/unplugin-vue-i18n": "^6.0.8",
|
||||
"@types/webpack": "^4.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||
"@typescript-eslint/parser": "^2.33.0",
|
||||
"@types/node": "^25.0.10",
|
||||
"@vitejs/plugin-legacy": "^7.0.1",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"prettier": "^1.19.1",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"prettier": "^3.4.2",
|
||||
"sass": "^1.89.2",
|
||||
"sass-loader": "^8.0.2",
|
||||
"typescript": "~3.9.3",
|
||||
"typescript": "^5.8.0",
|
||||
"typescript-eslint": "^8.21.0",
|
||||
"vite": "^7.0.5",
|
||||
"vite-plugin-pwa": "^1.0.1",
|
||||
"vue-tsc": "^2.2.0",
|
||||
"workbox-build": "^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>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/styles/app';
|
||||
@use '@/styles/app';
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
|
||||
@@ -44,8 +44,6 @@ const check = () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/variables';
|
||||
|
||||
.dev-session {
|
||||
h2 {
|
||||
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(() => {
|
||||
props.play ? play() : pause()
|
||||
props.play ? startPlay() : stopPlay()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -92,8 +92,6 @@ const onPause = () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/variables';
|
||||
|
||||
.pm-field {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ref, reactive, onMounted, computed } from 'vue'
|
||||
export const useSpotify = (onPlay: () => void, onPause: () => void) => {
|
||||
const ready = ref(false)
|
||||
const deviceId = ref<string | null>(null)
|
||||
const spotify = reactive({
|
||||
const spotify = reactive<{ player: SpotifyPlayer | null }>({
|
||||
player: null
|
||||
})
|
||||
const store = useMainStore()
|
||||
@@ -26,25 +26,29 @@ export const useSpotify = (onPlay: () => void, onPause: () => void) => {
|
||||
|
||||
spotify.player = new Spotify.Player({
|
||||
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
|
||||
ready.value = true
|
||||
})
|
||||
spotify.player.addListener('player_state_changed', (state) => {
|
||||
if (!state) {
|
||||
return
|
||||
}
|
||||
spotify.player.addListener('player_state_changed', (data: unknown) => {
|
||||
const state = data as SpotifyPlayerState | null
|
||||
if (!state) {
|
||||
return
|
||||
}
|
||||
|
||||
if (state.paused) {
|
||||
store.setSong(null)
|
||||
onPause()
|
||||
} else {
|
||||
store.setSong(state.track_window?.current_track?.name ?? null)
|
||||
onPlay()
|
||||
if (state.paused) {
|
||||
store.song = null
|
||||
onPause()
|
||||
} else {
|
||||
store.song = state.track_window?.current_track?.name ?? null
|
||||
onPlay()
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
await spotify.player.connect()
|
||||
}
|
||||
import('@/lib/spotify-player')
|
||||
@@ -59,7 +63,9 @@ export const useSpotify = (onPlay: () => void, onPause: () => void) => {
|
||||
const playerState = await spotify.player.getCurrentState()
|
||||
|
||||
if (!playerState) {
|
||||
playOnSpotify(accessToken.value, deviceId.value)
|
||||
if (accessToken.value && deviceId.value) {
|
||||
playOnSpotify(accessToken.value, deviceId.value)
|
||||
}
|
||||
return
|
||||
}
|
||||
await spotify.player.resume()
|
||||
@@ -71,7 +77,7 @@ export const useSpotify = (onPlay: () => void, onPause: () => void) => {
|
||||
}
|
||||
|
||||
const pause = () => {
|
||||
if (ready.value) {
|
||||
if (ready.value && accessToken.value && deviceId.value) {
|
||||
pauseOnSpotify(accessToken.value, deviceId.value)
|
||||
}
|
||||
}
|
||||
@@ -96,9 +102,9 @@ export const useSpotifyConnect = (hash: string) => {
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
store.setAccessToken(connection['access_token'])
|
||||
store.accessToken = connection['access_token']
|
||||
const now = new Date()
|
||||
store.setTokenExpire(
|
||||
new Date(now.getTime() + parseInt(connection['expires_in']) * 1000)
|
||||
store.tokenExpire = new Date(
|
||||
now.getTime() + parseInt(connection['expires_in']) * 1000
|
||||
)
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ export const useTimer = () => {
|
||||
const session = computed(() => formatSeconds(sessionSeconds.value))
|
||||
const intervalSeconds = computed(() => interval.value * 60)
|
||||
|
||||
let stopwatchId: NodeJS.Timeout | null = null
|
||||
let stopWatchSessionId: NodeJS.Timeout | null = null
|
||||
let stopwatchId: ReturnType<typeof setInterval> | null = null
|
||||
let stopWatchSessionId: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
watch(interval, () => {
|
||||
sessionSeconds.value = interval.value * 60
|
||||
@@ -55,8 +55,8 @@ export const useTimer = () => {
|
||||
|
||||
toggleSound.play()
|
||||
|
||||
notify(i18n.t('notification.change.title').toString(), {
|
||||
body: i18n.t('notification.change.body', { dev }).toString()
|
||||
notify(i18n.global.t('notification.change.title'), {
|
||||
body: i18n.global.t('notification.change.body', { dev })
|
||||
})
|
||||
}
|
||||
}, 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 = () => {
|
||||
let wakeLock: WakeLockSentinel | null = null
|
||||
|
||||
@@ -22,10 +7,8 @@ export const useWakeLock = () => {
|
||||
}
|
||||
|
||||
try {
|
||||
wakeLock = await (navigator as NavigatorExtended).wakeLock.request(
|
||||
'screen'
|
||||
)
|
||||
} catch (err) {
|
||||
wakeLock = await navigator.wakeLock.request('screen')
|
||||
} catch {
|
||||
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() {
|
||||
this.setSong(null)
|
||||
this.song = null
|
||||
}
|
||||
},
|
||||
|
||||
persist: {
|
||||
key: 'binome',
|
||||
storage: localStorage,
|
||||
paths: [
|
||||
pick: [
|
||||
'interval',
|
||||
'hasMusic',
|
||||
'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,
|
||||
silent: true,
|
||||
...options
|
||||
})
|
||||
} as NotificationOptions)
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -101,8 +101,6 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/variables';
|
||||
|
||||
.home {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/variables';
|
||||
|
||||
.privacy {
|
||||
font-family: $serif-font-family;
|
||||
flex: 1;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
@@ -10,16 +10,19 @@
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": ["vite/client"],
|
||||
"types": ["vite/client", "node"],
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"src/**/*.d.ts",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
|
||||
@@ -128,7 +128,7 @@ export default defineConfig({
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: `@import "@/styles/variables.scss";`
|
||||
additionalData: `@use "@/styles/variables" as *;`
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user