more good stuff

This commit is contained in:
Phan An 2022-04-20 11:37:22 +02:00
parent 014e109b3a
commit ac83736192
No known key found for this signature in database
GPG key ID: A81E4477F0BB6FDC
19 changed files with 74 additions and 102 deletions

View file

@ -38,6 +38,7 @@
"@typescript-eslint/no-explicit-any": 0, "@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-non-null-assertion": 0, "@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/ban-ts-comment": 0, "@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/no-empty-function": 0,
"vue/no-side-effects-in-computed-properties": 0, "vue/no-side-effects-in-computed-properties": 0,
"@typescript-eslint/explicit-module-boundary-types": 0, "@typescript-eslint/explicit-module-boundary-types": 0,
"standard/no-callback-literal": 0, "standard/no-callback-literal": 0,

View file

@ -31,7 +31,7 @@ import LoginForm from '@/components/auth/login-form.vue'
import MainWrapper from '@/components/layout/main-wrapper/index.vue' import MainWrapper from '@/components/layout/main-wrapper/index.vue'
import Overlay from '@/components/ui/overlay.vue' import Overlay from '@/components/ui/overlay.vue'
import { $, eventBus, hideOverlay, showOverlay } from '@/utils' import { $, eventBus, hideOverlay, showOverlay, arrayify } from '@/utils'
import { favoriteStore, preferenceStore as preferences, queueStore, sharedStore } from '@/stores' import { favoriteStore, preferenceStore as preferences, queueStore, sharedStore } from '@/stores'
import { auth, playback, socket } from '@/services' import { auth, playback, socket } from '@/services'
@ -84,8 +84,8 @@ onMounted(async () => {
$.addClass(document.documentElement, navigator.userAgent.includes('Mac') ? 'mac' : 'non-mac') $.addClass(document.documentElement, navigator.userAgent.includes('Mac') ? 'mac' : 'non-mac')
}) })
eventBus.on('SONG_CONTEXT_MENU_REQUESTED', async (e: MouseEvent, songs: Song[]) => { eventBus.on('SONG_CONTEXT_MENU_REQUESTED', async (e: MouseEvent, songs: Song | Song[]) => {
contextMenuSongs.value = ([] as Song[]).concat(songs) contextMenuSongs.value = arrayify(songs)
await nextTick() await nextTick()
songContextMenu.value?.open(e.pageY, e.pageX) songContextMenu.value?.open(e.pageY, e.pageX)
}) })

View file

@ -1,8 +1,8 @@
<template> <template>
<section id="queueWrapper"> <section id="queueWrapper">
<screen-header> <ScreenHeader>
Current Queue Current Queue
<controls-toggler :showing-controls="showingControls" @toggleControls="toggleControls"/> <ControlsToggler :showing-controls="showingControls" @toggleControls="toggleControls"/>
<template v-slot:meta> <template v-slot:meta>
<span v-if="meta.songCount" data-test="list-meta"> <span v-if="meta.songCount" data-test="list-meta">
@ -11,27 +11,27 @@
</template> </template>
<template v-slot:controls> <template v-slot:controls>
<song-list-controls <SongListControls
v-if="state.songs.length && (!isPhone || showingControls)" v-if="songs.length && (!isPhone || showingControls)"
@playAll="playAll" @playAll="playAll"
@playSelected="playSelected" @playSelected="playSelected"
@clearQueue="clearQueue" @clearQueue="clearQueue"
:songs="state.songs" :songs="songs"
:config="songListControlConfig" :config="songListControlConfig"
:selectedSongs="selectedSongs" :selectedSongs="selectedSongs"
/> />
</template> </template>
</screen-header> </ScreenHeader>
<song-list <SongList
v-if="state.songs.length" v-if="songs.length"
:items="state.songs" :items="songs"
:config="{ sortable: false }" :config="{ sortable: false }"
type="queue" type="queue"
ref="songList" ref="songList"
/> />
<screen-placeholder v-else> <ScreenPlaceholder v-else>
<template v-slot:icon> <template v-slot:icon>
<i class="fa fa-coffee"></i> <i class="fa fa-coffee"></i>
</template> </template>
@ -41,7 +41,7 @@
How about How about
<a class="start" @click.prevent="shuffleAll">shuffling all songs</a>? <a class="start" @click.prevent="shuffleAll">shuffling all songs</a>?
</span> </span>
</screen-placeholder> </ScreenPlaceholder>
</section> </section>
</template> </template>
@ -50,7 +50,7 @@ import { pluralize } from '@/utils'
import { queueStore, songStore } from '@/stores' import { queueStore, songStore } from '@/stores'
import { playback } from '@/services' import { playback } from '@/services'
import { useSongList } from '@/composables' import { useSongList } from '@/composables'
import { computed, defineAsyncComponent, reactive } from 'vue' import { computed, defineAsyncComponent, reactive, toRef } from 'vue'
const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue')) const ScreenHeader = defineAsyncComponent(() => import('@/components/ui/screen-header.vue'))
const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue')) const ScreenPlaceholder = defineAsyncComponent(() => import('@/components/ui/screen-placeholder.vue'))
@ -71,14 +71,13 @@ const {
clearQueue: true clearQueue: true
}) })
const state = reactive(queueStore.state) const songs = toRef(queueStore.state, 'songs')
const songState = reactive(songStore.state) const songState = reactive(songStore.state)
const shouldShowShufflingAllLink = computed(() => songState.songs.length > 0) const shouldShowShufflingAllLink = computed(() => songState.songs.length > 0)
const playAll = () => { const playAll = () => {
// @ts-ignore playback.queueAndPlay(songs.value.length ? songList.value.getAllSongsWithSort() : songStore.all)
playback.queueAndPlay(state.songs.length ? songList.value?.getAllSongsWithSort() : songStore.all)
} }
const shuffleAll = async () => await playback.queueAndPlay(songStore.all, true) const shuffleAll = async () => await playback.queueAndPlay(songStore.all, true)

View file

@ -31,23 +31,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, PropType, toRefs } from 'vue' import { computed, defineAsyncComponent, toRefs } from 'vue'
import { eventBus, pluralize, startDragging } from '@/utils' import { eventBus, pluralize, startDragging } from '@/utils'
import { queueStore } from '@/stores' import { queueStore } from '@/stores'
import { playback } from '@/services' import { playback } from '@/services'
const LikeButton = defineAsyncComponent(() => import('@/components/song/like-button.vue')) const LikeButton = defineAsyncComponent(() => import('@/components/song/like-button.vue'))
const props = defineProps({ const props = withDefaults(defineProps<{ song: Song, topPlayCount?: number }>(), { topPlayCount: 0 })
song: {
type: Object as PropType<Song>,
required: true
},
topPlayCount: {
type: Number,
default: 0
}
})
const { song, topPlayCount } = toRefs(props) const { song, topPlayCount } = toRefs(props)

View file

@ -112,7 +112,7 @@
import { computed, defineAsyncComponent, nextTick, reactive, ref, toRefs } from 'vue' import { computed, defineAsyncComponent, nextTick, reactive, ref, toRefs } from 'vue'
import { isEqual, union } from 'lodash' import { isEqual, union } from 'lodash'
import { alerts, br2nl, getDefaultCover } from '@/utils' import { alerts, br2nl, getDefaultCover, arrayify } from '@/utils'
import { songInfo } from '@/services/info' import { songInfo } from '@/services/info'
import { albumStore, artistStore, songStore } from '@/stores' import { albumStore, artistStore, songStore } from '@/stores'
@ -234,7 +234,7 @@ const initCompilationStateCheckbox = async () => {
} }
const open = async () => { const open = async () => {
mutatedSongs.value = ([] as Song[]).concat(songs.value) mutatedSongs.value = arrayify(songs.value)
currentView.value = initialTab!.value currentView.value = initialTab!.value
const firstSong = mutatedSongs.value[0] const firstSong = mutatedSongs.value[0]

View file

@ -79,7 +79,7 @@ import {
} from 'vue' } from 'vue'
import { RecycleScroller } from 'vue-virtual-scroller' import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import { $, eventBus, orderBy, startDragging } from '@/utils' import { $, eventBus, orderBy, startDragging, arrayify } from '@/utils'
import { favoriteStore, playlistStore, queueStore } from '@/stores' import { favoriteStore, playlistStore, queueStore } from '@/stores'
import { playback } from '@/services' import { playback } from '@/services'
import router from '@/router' import router from '@/router'
@ -101,26 +101,10 @@ interface SongRow {
const SongItem = defineAsyncComponent(() => import('@/components/song/item.vue')) const SongItem = defineAsyncComponent(() => import('@/components/song/item.vue'))
// @ts-ignore const props = withDefaults(
getCurrentInstance()!.__IS_SONG_LIST__ = true defineProps<{ items: Song[], playlist?: Playlist, type?: SongListType, config?: Partial<SongListConfig> }>(),
{ type: 'all-songs', config: () => ({}) }
const props = defineProps({ )
items: {
type: Array as PropType<Song[]>,
required: true
},
playlist: {
type: Object as PropType<Playlist>
},
type: {
type: String as PropType<SongListType>,
default: 'all-songs'
},
config: {
type: Object as PropType<Partial<SongListConfig>>,
default: () => ({})
}
})
const { items, playlist, type, config } = toRefs(props) const { items, playlist, type, config } = toRefs(props)
@ -170,7 +154,7 @@ const sort = (field: SortField | SortField[] = [], order: SortOrder | null = nul
return return
} }
sortFields.value = ([] as SortField[]).concat(field) sortFields.value = arrayify(field)
if (!sortFields.value.length && ['album', 'artist'].includes(type.value)) { if (!sortFields.value.length && ['album', 'artist'].includes(type.value)) {
// by default, sort Album/Artist by track numbers for a more friendly UX // by default, sort Album/Artist by track numbers for a more friendly UX

View file

@ -25,15 +25,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { PropType, ref, toRefs, watchEffect } from 'vue' import { ref, toRefs, watchEffect } from 'vue'
const props = defineProps({
value: {
type: String as PropType<ArtistAlbumViewMode>,
default: 'thumbnails'
}
})
const props = withDefaults(defineProps<{ value?: ArtistAlbumViewMode }>(), { value: 'thumbnails' })
const { value } = toRefs(props) const { value } = toRefs(props)
let modelValue = ref<ArtistAlbumViewMode>() let modelValue = ref<ArtistAlbumViewMode>()

View file

@ -13,7 +13,7 @@ import SongListControls from '@/components/song/list-controls.vue'
import { songStore } from '@/stores' import { songStore } from '@/stores'
export const useSongList = (controlsConfig: Partial<SongListControlsConfig> = {}) => { export const useSongList = (controlsConfig: Partial<SongListControlsConfig> = {}) => {
const songList = ref(null) const songList = ref<InstanceType<typeof SongList>>()
const state = reactive<SongListState>({ songs: [] }) const state = reactive<SongListState>({ songs: [] })
const meta = reactive<SongListMeta>({ const meta = reactive<SongListMeta>({
@ -35,7 +35,7 @@ export const useSongList = (controlsConfig: Partial<SongListControlsConfig> = {}
meta.totalLength = songStore.getFormattedLength(state.songs) meta.totalLength = songStore.getFormattedLength(state.songs)
}) })
const getSongsToPlay = (): Song[] => (songList.value as any).getAllSongsWithSort() const getSongsToPlay = (): Song[] => songList.value.getAllSongsWithSort()
const playAll = (shuffled: boolean) => playback.queueAndPlay(getSongsToPlay(), shuffled) const playAll = (shuffled: boolean) => playback.queueAndPlay(getSongsToPlay(), shuffled)
const playSelected = (shuffled: boolean) => playback.queueAndPlay(selectedSongs.value, shuffled) const playSelected = (shuffled: boolean) => playback.queueAndPlay(selectedSongs.value, shuffled)
const toggleControls = () => (showingControls.value = !showingControls.value) const toggleControls = () => (showingControls.value = !showingControls.value)

View file

@ -5,6 +5,8 @@ import { favoriteStore, playlistStore, queueStore } from '@/stores'
* Each component including this mixin must have a `songs` array as either data, prop, or computed. * Each component including this mixin must have a `songs` array as either data, prop, or computed.
*/ */
export const useSongMenuMethods = (songs: Song[], close: TAnyFunction) => { export const useSongMenuMethods = (songs: Song[], close: TAnyFunction) => {
console.log(songs)
const queueSongsAfterCurrent = () => { const queueSongsAfterCurrent = () => {
queueStore.queueAfterCurrent(songs) queueStore.queueAfterCurrent(songs)
close() close()

View file

@ -1,16 +1,10 @@
import { playlistStore, favoriteStore } from '@/stores' import { favoriteStore, playlistStore } from '@/stores'
import { auth } from '.' import { auth } from '.'
import { alerts } from '@/utils' import { alerts, arrayify } from '@/utils'
let events: any
if (KOEL_ENV === 'app') {
events = require('&/events').default
}
export const download = { export const download = {
fromSongs (songs: Song | Song[]): void { fromSongs (songs: Song | Song[]): void {
const query = (<Song[]>[]).concat(songs).reduce((q, song) => `songs[]=${song.id}&${q}`, '') const query = arrayify(songs).reduce((q, song) => `songs[]=${song.id}&${q}`, '')
this.trigger(`songs?${query}`) this.trigger(`songs?${query}`)
}, },

View file

@ -4,7 +4,7 @@ import { union, difference, take, orderBy } from 'lodash'
import stub from '@/stubs/album' import stub from '@/stubs/album'
import { artistStore } from '.' import { artistStore } from '.'
import { http } from '@/services' import { http } from '@/services'
import { use } from '@/utils' import { arrayify, use } from '@/utils'
const UNKNOWN_ALBUM_ID = 1 const UNKNOWN_ALBUM_ID = 1
@ -53,7 +53,7 @@ export const albumStore = {
}, },
add (albums: Album | Album[]): void { add (albums: Album | Album[]): void {
(<Album[]>[]).concat(albums).forEach(album => { arrayify(albums).forEach(album => {
this.setupAlbum(album) this.setupAlbum(album)
album.playCount = album.songs.reduce((count, song) => count + song.playCount, 0) album.playCount = album.songs.reduce((count, song) => count + song.playCount, 0)
this.all.push(album) this.all.push(album)

View file

@ -2,7 +2,7 @@ import { difference, take, orderBy } from 'lodash'
import { http } from '@/services' import { http } from '@/services'
import stub from '@/stubs/artist' import stub from '@/stubs/artist'
import { use } from '@/utils' import { arrayify, use } from '@/utils'
const UNKNOWN_ARTIST_ID = 1 const UNKNOWN_ARTIST_ID = 1
const VARIOUS_ARTISTS_ID = 2 const VARIOUS_ARTISTS_ID = 2
@ -50,7 +50,7 @@ export const artistStore = {
}, },
add (artists: Artist | Artist[]) { add (artists: Artist | Artist[]) {
(<Artist[]>[]).concat(artists).forEach(artist => { arrayify(artists).forEach(artist => {
this.setupArtist(artist) this.setupArtist(artist)
artist.playCount = artist.songs.reduce((count, song) => count + song.playCount, 0) artist.playCount = artist.songs.reduce((count, song) => count + song.playCount, 0)
this.all.push(artist) this.all.push(artist)

View file

@ -1,5 +1,6 @@
import { difference, union } from 'lodash' import { difference, union } from 'lodash'
import { http } from '@/services' import { http } from '@/services'
import { arrayify } from '@/utils'
export const favoriteStore = { export const favoriteStore = {
state: { state: {
@ -29,14 +30,14 @@ export const favoriteStore = {
* Add a song/songs into the store. * Add a song/songs into the store.
*/ */
add (songs: Song | Song[]): void { add (songs: Song | Song[]): void {
this.all = union(this.all, (<Song[]>[]).concat(songs)) this.all = union(this.all, arrayify(songs))
}, },
/** /**
* Remove a song/songs from the store. * Remove a song/songs from the store.
*/ */
remove (songs: Song | Song[]): void { remove (songs: Song | Song[]): void {
this.all = difference(this.all, (<Song[]>[]).concat(songs)) this.all = difference(this.all, arrayify(songs))
}, },
clear (): void { clear (): void {

View file

@ -2,7 +2,7 @@ import { difference, union, orderBy } from 'lodash'
import stub from '@/stubs/playlist' import stub from '@/stubs/playlist'
import { http } from '@/services' import { http } from '@/services'
import { alerts, pluralize } from '@/utils' import { alerts, pluralize, arrayify } from '@/utils'
import { songStore } from '.' import { songStore } from '.'
import models from '@/config/smart-playlist/models' import models from '@/config/smart-playlist/models'
import operators from '@/config/smart-playlist/operators' import operators from '@/config/smart-playlist/operators'
@ -79,7 +79,7 @@ export const playlistStore = {
* Add a playlist/playlists into the store. * Add a playlist/playlists into the store.
*/ */
add (playlists: Playlist | Playlist[]) { add (playlists: Playlist | Playlist[]) {
const playlistsToAdd = (<Playlist[]>[]).concat(playlists) const playlistsToAdd = arrayify(playlists)
playlistsToAdd.forEach(playlist => this.setupPlaylist(playlist)) playlistsToAdd.forEach(playlist => this.setupPlaylist(playlist))
this.all = this.sort(union(this.all, playlistsToAdd)) this.all = this.sort(union(this.all, playlistsToAdd))
}, },
@ -88,7 +88,7 @@ export const playlistStore = {
* Remove a playlist/playlists from the store. * Remove a playlist/playlists from the store.
*/ */
remove (playlists: Playlist | Playlist[]) { remove (playlists: Playlist | Playlist[]) {
this.all = difference(this.all, (<Playlist[]>[]).concat(playlists)) this.all = difference(this.all, arrayify(playlists))
}, },
async store (name: string, songs: Song[] = [], rules: SmartPlaylistRuleGroup[] = []): Promise<Playlist> { async store (name: string, songs: Song[] = [], rules: SmartPlaylistRuleGroup[] = []): Promise<Playlist> {
@ -169,7 +169,7 @@ export const playlistStore = {
/** /**
* Serialize the rule (groups) to be ready for database. * Serialize the rule (groups) to be ready for database.
*/ */
serializeSmartPlaylistRulesForStorage: (ruleGroups: SmartPlaylistRuleGroup[]): object[] | null => { serializeSmartPlaylistRulesForStorage: (ruleGroups: SmartPlaylistRuleGroup[]): any[] | null => {
if (!ruleGroups || !ruleGroups.length) { if (!ruleGroups || !ruleGroups.length) {
return null return null
} }

View file

@ -1,10 +1,12 @@
import { union, difference, shuffle } from 'lodash' import { union, difference, shuffle } from 'lodash'
import { reactive } from 'vue'
import { arrayify } from '@/utils'
export const queueStore = { export const queueStore = {
state: { state: reactive({
songs: [] as Song[], songs: [] as Song[],
current: undefined as Song | undefined current: undefined as Song | undefined
}, }),
init () { init () {
// We don't have anything to do here yet. // We don't have anything to do here yet.
@ -57,7 +59,7 @@ export const queueStore = {
*/ */
queue (songs: Song | Song[]): void { queue (songs: Song | Song[]): void {
this.unqueue(songs) this.unqueue(songs)
this.all = union(this.all, (<Song[]>[]).concat(songs)) this.all = union(this.all, arrayify(songs))
}, },
/** /**
@ -65,7 +67,7 @@ export const queueStore = {
* @param {Song|Song[]} songs The song, or an array of songs * @param {Song|Song[]} songs The song, or an array of songs
*/ */
queueToTop (songs: Song | Song[]): void { queueToTop (songs: Song | Song[]): void {
this.all = union((<Song[]>[]).concat(songs), this.all) this.all = union(arrayify(songs), this.all)
}, },
/** /**
@ -73,7 +75,7 @@ export const queueStore = {
* @param {Song|Song[]} songs The song, or an array of songs * @param {Song|Song[]} songs The song, or an array of songs
*/ */
replaceQueueWith (songs: Song | Song[]): void { replaceQueueWith (songs: Song | Song[]): void {
this.all = (<Song[]>[]).concat(songs) this.all = arrayify(songs)
}, },
/** /**
@ -81,7 +83,7 @@ export const queueStore = {
* @param {Song|Song[]} songs The song, or an array of songs * @param {Song|Song[]} songs The song, or an array of songs
*/ */
queueAfterCurrent (songs: Song | Song[]): void { queueAfterCurrent (songs: Song | Song[]): void {
songs = (<Song[]>[]).concat(songs) songs = arrayify(songs)
if (!this.current || !this.all.length) { if (!this.current || !this.all.length) {
return this.queue(songs) return this.queue(songs)
@ -95,7 +97,7 @@ export const queueStore = {
}, },
unqueue (songs: Song | Song[]): void { unqueue (songs: Song | Song[]): void {
this.all = difference(this.all, (<Song[]>[]).concat(songs)) this.all = difference(this.all, arrayify(songs))
}, },
/** /**
@ -106,7 +108,7 @@ export const queueStore = {
*/ */
move (songs: Song | Song[], target: Song): void { move (songs: Song | Song[], target: Song): void {
const targetIndex = this.indexOf(target) const targetIndex = this.indexOf(target)
const movedSongs = (<Song[]>[]).concat(songs) const movedSongs = arrayify(songs)
movedSongs.forEach(song => { movedSongs.forEach(song => {
this.all.splice(this.indexOf(song), 1) this.all.splice(this.indexOf(song), 1)

View file

@ -1,11 +1,11 @@
import Vue from 'vue' import { reactive } from 'vue'
import slugify from 'slugify' import slugify from 'slugify'
import { without, take, remove, orderBy, unionBy } from 'lodash' import { orderBy, remove, take, unionBy, without } from 'lodash'
import isMobile from 'ismobilejs' import isMobile from 'ismobilejs'
import { secondsToHis, alerts, pluralize, use } from '@/utils' import { alerts, arrayify, pluralize, secondsToHis, use } from '@/utils'
import { http, auth, ls } from '@/services' import { auth, http, ls } from '@/services'
import { sharedStore, favoriteStore, albumStore, artistStore, preferenceStore } from '.' import { albumStore, artistStore, favoriteStore, preferenceStore, sharedStore } from '.'
import stub from '@/stubs/song' import stub from '@/stubs/song'
interface BroadcastSongData { interface BroadcastSongData {
@ -34,10 +34,10 @@ export const songStore = {
stub, stub,
cache: {} as { [key: string]: Song }, cache: {} as { [key: string]: Song },
state: { state: reactive({
songs: [] as Song[], songs: [] as Song[],
recentlyPlayed: [] as Song[] recentlyPlayed: [] as Song[]
}, }),
init (songs: Song[]): void { init (songs: Song[]): void {
this.all = songs this.all = songs
@ -107,7 +107,7 @@ export const songStore = {
}, },
getFormattedLength (songs: Song[]): string { getFormattedLength (songs: Song[]): string {
return <string>this.getLength(songs, true) return String(this.getLength(songs, true))
}, },
get all (): Song[] { get all (): Song[] {
@ -124,7 +124,7 @@ export const songStore = {
byIds (ids: string[]): Song[] { byIds (ids: string[]): Song[] {
const songs = [] as Song[] const songs = [] as Song[]
([] as string[]).concat(ids).forEach(id => use(this.byId(id), song => songs.push(song!))) arrayify(ids).forEach(id => use(this.byId(id), song => songs.push(song!)))
return songs return songs
}, },

View file

@ -1,5 +1,6 @@
declare module '*.jpg' declare module '*.jpg'
declare module '*.png' declare module '*.png'
declare module '*.svg'
declare type TAnyFunction = (...args: Array<unknown|any>) => unknown|any declare type TAnyFunction = (...args: Array<unknown|any>) => unknown|any

View file

@ -1,5 +1,5 @@
import select from 'select' import select from 'select'
import { eventBus, noop, pluralize } from '@/utils' import { eventBus, noop, pluralize, arrayify } from '@/utils'
import defaultCover from '@/../img/covers/unknown-album.png' import defaultCover from '@/../img/covers/unknown-album.png'
/** /**
@ -81,7 +81,7 @@ export const startDragging = (event: DragEvent, dragged: Song | Song[] | Album |
switch (type) { switch (type) {
case 'Song': case 'Song':
dragged = (<Song[]>[]).concat(<Song>dragged) dragged = arrayify(<Song>dragged)
text = dragged.length === 1 text = dragged.length === 1
? `${dragged[0].title} by ${dragged[0].artist.name}` ? `${dragged[0].title} by ${dragged[0].artist.name}`
: pluralize(dragged.length, 'song') : pluralize(dragged.length, 'song')

View file

@ -1,4 +1,4 @@
export const use = <T>(value: T, cb: (arg: T) => void): void => { export const use = <T> (value: T, cb: (arg: T) => void) => {
if (typeof value === 'undefined' || value === null) { if (typeof value === 'undefined' || value === null) {
return return
} }
@ -6,4 +6,7 @@ export const use = <T>(value: T, cb: (arg: T) => void): void => {
cb(value) cb(value)
} }
export const arrayify = <T> (maybeArray: T | Array<T>) => ([] as Array<T>).concat(maybeArray)
// @ts-ignore
export const noop = () => {} export const noop = () => {}