favorite: v0.1
This commit is contained in:
@@ -31,7 +31,22 @@
|
|||||||
r.type }}</span>
|
r.type }}</span>
|
||||||
<span class="text-slate-900 dark:text-slate-100">{{ r.label }}</span>
|
<span class="text-slate-900 dark:text-slate-100">{{ r.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span v-if="r.sublabel" class="text-sm text-slate-500 dark:text-slate-400">{{ r.sublabel }}</span>
|
<div class="flex items-center gap-2">
|
||||||
|
<span v-if="r.sublabel" class="text-sm text-slate-500 dark:text-slate-400">{{ r.sublabel }}</span>
|
||||||
|
<button
|
||||||
|
v-if="r.type === 'TRACK'"
|
||||||
|
class="p-1 rounded hover:bg-slate-100 dark:hover:bg-slate-800"
|
||||||
|
aria-label="Toggle favorite"
|
||||||
|
@click.stop="fav.toggle(r.payload)"
|
||||||
|
>
|
||||||
|
<svg v-if="fav.isFavorite(r.payload.id)" class="h-5 w-5 text-rose-500" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M12 21s-6.716-4.35-9.428-7.062C.86 12.226.5 10.64.5 9.5.5 6.462 2.962 4 6 4c1.657 0 3.157.806 4 2.09C10.843 4.806 12.343 4 14 4c3.038 0 5.5 2.462 5.5 5.5 0 1.14-.36 2.726-2.072 4.438C18.716 16.65 12 21 12 21z"/>
|
||||||
|
</svg>
|
||||||
|
<svg v-else class="h-5 w-5 text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.6l-1-1a5.5 5.5 0 0 0-7.8 7.8l1 1L12 21l7.8-7.6 1-1a5.5 5.5 0 0 0 0-7.8z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
@@ -49,10 +64,12 @@ import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
|||||||
import { useUiStore } from '~/store/ui'
|
import { useUiStore } from '~/store/ui'
|
||||||
import { useDataStore } from '~/store/data'
|
import { useDataStore } from '~/store/data'
|
||||||
import { usePlayerStore } from '~/store/player'
|
import { usePlayerStore } from '~/store/player'
|
||||||
|
import { useFavoritesStore } from '~/store/favorites'
|
||||||
|
|
||||||
const ui = useUiStore()
|
const ui = useUiStore()
|
||||||
const data = useDataStore()
|
const data = useDataStore()
|
||||||
const player = usePlayerStore()
|
const player = usePlayerStore()
|
||||||
|
const fav = useFavoritesStore()
|
||||||
const inputRef = ref<HTMLInputElement | null>(null)
|
const inputRef = ref<HTMLInputElement | null>(null)
|
||||||
const activeIndex = ref(0)
|
const activeIndex = ref(0)
|
||||||
|
|
||||||
|
|||||||
6
app/plugins/favorites.client.ts
Normal file
6
app/plugins/favorites.client.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { useFavoritesStore } from '~/store/favorites'
|
||||||
|
|
||||||
|
export default defineNuxtPlugin(() => {
|
||||||
|
const fav = useFavoritesStore()
|
||||||
|
fav.load()
|
||||||
|
})
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Box, Artist, Track } from '~/../types/types'
|
import type { Box, Artist, Track } from '~/../types/types'
|
||||||
|
import { FAVORITES_BOX_ID, useFavoritesStore } from '~/store/favorites'
|
||||||
|
|
||||||
// stores/data.ts
|
// stores/data.ts
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
@@ -44,6 +45,21 @@ export const useDataStore = defineStore('data', {
|
|||||||
artist: artistObj
|
artist: artistObj
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const favBox: Box = {
|
||||||
|
id: FAVORITES_BOX_ID,
|
||||||
|
type: 'playlist',
|
||||||
|
name: 'Favoris',
|
||||||
|
duration: 0,
|
||||||
|
tracks: [],
|
||||||
|
description: '',
|
||||||
|
color1: '#0f172a',
|
||||||
|
color2: '#1e293b',
|
||||||
|
color3: '#334155',
|
||||||
|
state: 'box-list'
|
||||||
|
}
|
||||||
|
if (!this.boxes.find((b) => b.id === FAVORITES_BOX_ID)) {
|
||||||
|
this.boxes = [favBox, ...this.boxes]
|
||||||
|
}
|
||||||
this.isLoaded = true
|
this.isLoaded = true
|
||||||
const uiStore = useUiStore()
|
const uiStore = useUiStore()
|
||||||
uiStore.closeBox()
|
uiStore.closeBox()
|
||||||
@@ -59,6 +75,12 @@ export const useDataStore = defineStore('data', {
|
|||||||
},
|
},
|
||||||
// Obtenir toutes les pistes d'une box donnée
|
// Obtenir toutes les pistes d'une box donnée
|
||||||
getTracksByboxId: (state) => (id: string) => {
|
getTracksByboxId: (state) => (id: string) => {
|
||||||
|
if (id === FAVORITES_BOX_ID) {
|
||||||
|
const fav = useFavoritesStore()
|
||||||
|
return fav.trackIds
|
||||||
|
.map((tid) => state.tracks.find((t) => t.id === tid))
|
||||||
|
.filter((t): t is Track => !!t)
|
||||||
|
}
|
||||||
return state.tracks.filter((track) => track.boxId === id)
|
return state.tracks.filter((track) => track.boxId === id)
|
||||||
},
|
},
|
||||||
// Filtrer les artistes selon certains critères
|
// Filtrer les artistes selon certains critères
|
||||||
|
|||||||
52
app/store/favorites.ts
Normal file
52
app/store/favorites.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import type { Track } from '~/../types/types'
|
||||||
|
|
||||||
|
export const FAVORITES_BOX_ID = 'FAV'
|
||||||
|
const STORAGE_KEY = 'evilspins:favorites:v1'
|
||||||
|
|
||||||
|
export const useFavoritesStore = defineStore('favorites', {
|
||||||
|
state: () => ({
|
||||||
|
trackIds: [] as number[]
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
load() {
|
||||||
|
if (!process.client) return
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(STORAGE_KEY)
|
||||||
|
if (raw) {
|
||||||
|
const arr = JSON.parse(raw)
|
||||||
|
if (Array.isArray(arr)) this.trackIds = arr.filter((x) => typeof x === 'number')
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
if (!process.client) return
|
||||||
|
try {
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(this.trackIds))
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
toggle(track: Track) {
|
||||||
|
const id = track.id
|
||||||
|
const idx = this.trackIds.indexOf(id)
|
||||||
|
if (idx >= 0) this.trackIds.splice(idx, 1)
|
||||||
|
else this.trackIds.unshift(id)
|
||||||
|
this.save()
|
||||||
|
},
|
||||||
|
add(track: Track) {
|
||||||
|
if (!this.trackIds.includes(track.id)) {
|
||||||
|
this.trackIds.unshift(track.id)
|
||||||
|
this.save()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remove(trackId: number) {
|
||||||
|
const idx = this.trackIds.indexOf(trackId)
|
||||||
|
if (idx >= 0) {
|
||||||
|
this.trackIds.splice(idx, 1)
|
||||||
|
this.save()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isFavorite(trackId: number) {
|
||||||
|
return this.trackIds.includes(trackId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user