diff --git a/core/pool/pool_test.go b/core/pool/pool_test.go
index 1fcf429d..9a14357c 100644
--- a/core/pool/pool_test.go
+++ b/core/pool/pool_test.go
@@ -20,13 +20,9 @@ type testItem struct {
ID int
}
-type results []int
+var processed []int
-func (r results) Len() int { return len(r) }
-
-var processed results
-
-var _ = XDescribe("Pool", func() {
+var _ = Describe("Pool", func() {
var pool *Pool
BeforeEach(func() {
@@ -38,7 +34,7 @@ var _ = XDescribe("Pool", func() {
for i := 0; i < 5; i++ {
pool.Submit(&testItem{ID: i})
}
- Eventually(processed.Len, "10s").Should(Equal(5))
+ Eventually(func() []int { return processed }, "10s").Should(HaveLen(5))
Expect(processed).To(ContainElements(0, 1, 2, 3, 4))
})
})
diff --git a/ui/src/album/AlbumActions.js b/ui/src/album/AlbumActions.js
index b1328475..97778cbf 100644
--- a/ui/src/album/AlbumActions.js
+++ b/ui/src/album/AlbumActions.js
@@ -13,7 +13,7 @@ import CloudDownloadOutlinedIcon from '@material-ui/icons/CloudDownloadOutlined'
import { RiPlayListAddFill, RiPlayList2Fill } from 'react-icons/ri'
import { playNext, addTracks, playTracks, shuffleTracks } from '../actions'
import subsonic from '../subsonic'
-import { formatBytes } from '../common/SizeField'
+import { formatBytes } from '../utils'
import { useMediaQuery } from '@material-ui/core'
import config from '../config'
diff --git a/ui/src/album/AlbumDetails.js b/ui/src/album/AlbumDetails.js
index 65becef5..f3979995 100644
--- a/ui/src/album/AlbumDetails.js
+++ b/ui/src/album/AlbumDetails.js
@@ -4,8 +4,9 @@ import { useTranslate } from 'react-admin'
import Lightbox from 'react-image-lightbox'
import 'react-image-lightbox/style.css'
import subsonic from '../subsonic'
-import { DurationField, formatRange, StarButton, SizeField } from '../common'
+import { DurationField, StarButton, SizeField } from '../common'
import { ArtistLinkField } from '../common'
+import { formatRange } from '../utils'
const AlbumDetails = ({ classes, record }) => {
const [isLightboxOpen, setLightboxOpen] = React.useState(false)
diff --git a/ui/src/authProvider.js b/ui/src/authProvider.js
index d4c15b9a..386d79e5 100644
--- a/ui/src/authProvider.js
+++ b/ui/src/authProvider.js
@@ -1,6 +1,6 @@
import jwtDecode from 'jwt-decode'
import md5 from 'md5-hex'
-import baseUrl from './utils/baseUrl'
+import { baseUrl } from './utils'
import config from './config'
import { v4 as uuidv4 } from 'uuid'
diff --git a/ui/src/common/AddToPlaylistButton.js b/ui/src/common/AddToPlaylistButton.js
index 4748a519..8115239c 100644
--- a/ui/src/common/AddToPlaylistButton.js
+++ b/ui/src/common/AddToPlaylistButton.js
@@ -5,7 +5,7 @@ import { Button, useTranslate, useUnselectAll } from 'react-admin'
import PlaylistAddIcon from '@material-ui/icons/PlaylistAdd'
import { openAddToPlaylist } from '../actions'
-const AddToPlaylistButton = ({ resource, selectedIds, className }) => {
+export const AddToPlaylistButton = ({ resource, selectedIds, className }) => {
const translate = useTranslate()
const dispatch = useDispatch()
const unselectAll = useUnselectAll()
@@ -34,5 +34,3 @@ AddToPlaylistButton.propTypes = {
selectedIds: PropTypes.arrayOf(PropTypes.string).isRequired,
className: PropTypes.object,
}
-
-export default AddToPlaylistButton
diff --git a/ui/src/common/ArtistLinkField.js b/ui/src/common/ArtistLinkField.js
index c5e2d7dc..caaea615 100644
--- a/ui/src/common/ArtistLinkField.js
+++ b/ui/src/common/ArtistLinkField.js
@@ -4,7 +4,7 @@ import { Link } from 'react-admin'
import { useAlbumsPerPage } from './index'
import { withWidth } from '@material-ui/core'
-const useGetHandleArtistClick = (width) => {
+export const useGetHandleArtistClick = (width) => {
const [perPage] = useAlbumsPerPage(width)
return (id) => {
@@ -12,7 +12,7 @@ const useGetHandleArtistClick = (width) => {
}
}
-const ArtistLinkField = ({ record, className, width }) => {
+export const ArtistLinkField = withWidth()(({ record, className, width }) => {
const artistLink = useGetHandleArtistClick(width)
return (
{
{record.albumArtist}
)
-}
+})
ArtistLinkField.propTypes = {
record: PropTypes.object,
@@ -33,7 +33,3 @@ ArtistLinkField.propTypes = {
ArtistLinkField.defaultProps = {
addLabel: true,
}
-
-export { useGetHandleArtistClick }
-
-export default withWidth()(ArtistLinkField)
diff --git a/ui/src/common/BatchPlayButton.js b/ui/src/common/BatchPlayButton.js
index c1a8dc27..37f29a01 100644
--- a/ui/src/common/BatchPlayButton.js
+++ b/ui/src/common/BatchPlayButton.js
@@ -9,7 +9,7 @@ import {
} from 'react-admin'
import { useDispatch } from 'react-redux'
-const BatchPlayButton = ({
+export const BatchPlayButton = ({
resource,
selectedIds,
action,
@@ -60,5 +60,3 @@ BatchPlayButton.propTypes = {
icon: PropTypes.object.isRequired,
className: PropTypes.object,
}
-
-export default BatchPlayButton
diff --git a/ui/src/common/BitrateField.js b/ui/src/common/BitrateField.js
index 01cffd9f..d6ac2fd0 100644
--- a/ui/src/common/BitrateField.js
+++ b/ui/src/common/BitrateField.js
@@ -1,7 +1,7 @@
import React from 'react'
import PropTypes from 'prop-types'
-const BitrateField = ({ record = {}, source }) => {
+export const BitrateField = ({ record = {}, source }) => {
return {`${record[source]} kbps`}
}
@@ -14,5 +14,3 @@ BitrateField.propTypes = {
BitrateField.defaultProps = {
addLabel: true,
}
-
-export default BitrateField
diff --git a/ui/src/common/ContextMenus.js b/ui/src/common/ContextMenus.js
index 88f8d36e..8ed47d24 100644
--- a/ui/src/common/ContextMenus.js
+++ b/ui/src/common/ContextMenus.js
@@ -15,9 +15,9 @@ import {
openAddToPlaylist,
} from '../actions'
import subsonic from '../subsonic'
-import StarButton from './StarButton'
-import { formatBytes } from './SizeField'
+import { StarButton } from './StarButton'
import config from '../config'
+import { formatBytes } from '../utils'
const useStyles = makeStyles({
noWrap: {
diff --git a/ui/src/common/DocLink.js b/ui/src/common/DocLink.js
index 75b0b3c0..d647ad1b 100644
--- a/ui/src/common/DocLink.js
+++ b/ui/src/common/DocLink.js
@@ -1,10 +1,8 @@
import React from 'react'
-import { docsUrl } from '../utils/docsUrl'
+import { docsUrl } from '../utils'
-const DocLink = ({ path, children }) => (
+export const DocLink = ({ path, children }) => (
{children}
)
-
-export default DocLink
diff --git a/ui/src/common/DurationField.js b/ui/src/common/DurationField.js
index 11373791..0222faa8 100644
--- a/ui/src/common/DurationField.js
+++ b/ui/src/common/DurationField.js
@@ -1,26 +1,16 @@
import React from 'react'
import PropTypes from 'prop-types'
+import { formatDuration } from '../utils'
-const DurationField = ({ record = {}, source }) => {
+export const DurationField = ({ record = {}, source }) => {
try {
- return {format(record[source])}
+ return {formatDuration(record[source])}
} catch (e) {
console.log('Error in DurationField! Record:', record)
return 00:00
}
}
-const format = (d) => {
- const hours = Math.floor(d / 3600)
- const minutes = Math.floor(d / 60) % 60
- const seconds = d % 60
- return [hours, minutes, seconds]
- .map((v) => Math.round(v).toString())
- .map((v) => (v.length !== 2 ? '0' + v : v))
- .filter((v, i) => v !== '00' || i > 0)
- .join(':')
-}
-
DurationField.propTypes = {
label: PropTypes.string,
record: PropTypes.object,
@@ -30,5 +20,3 @@ DurationField.propTypes = {
DurationField.defaultProps = {
addLabel: true,
}
-
-export default DurationField
diff --git a/ui/src/common/List.js b/ui/src/common/List.js
index ccd47997..f74ab027 100644
--- a/ui/src/common/List.js
+++ b/ui/src/common/List.js
@@ -1,9 +1,9 @@
import React from 'react'
import { List as RAList } from 'react-admin'
-import Pagination from './Pagination'
+import { Pagination } from './Pagination'
import { Title } from './index'
-const List = (props) => {
+export const List = (props) => {
const { resource } = props
return (
{
/>
)
}
-
-export default List
diff --git a/ui/src/common/Pagination.js b/ui/src/common/Pagination.js
index f2d507a3..e17d9e63 100644
--- a/ui/src/common/Pagination.js
+++ b/ui/src/common/Pagination.js
@@ -1,8 +1,6 @@
import React from 'react'
import { Pagination as RAPagination } from 'react-admin'
-const Pagination = (props) => (
+export const Pagination = (props) => (
)
-
-export default Pagination
diff --git a/ui/src/common/PlayButton.js b/ui/src/common/PlayButton.js
index 69698b8e..d2716495 100644
--- a/ui/src/common/PlayButton.js
+++ b/ui/src/common/PlayButton.js
@@ -13,7 +13,7 @@ const useStyles = makeStyles({
},
})
-const PlayButton = ({ record, color, size, ...rest }) => {
+export const PlayButton = ({ record, color, size, ...rest }) => {
const classes = useStyles({ color })
let extractSongsData = function (response) {
const data = response.data.reduce(
@@ -64,4 +64,3 @@ PlayButton.propTypes = {
PlayButton.defaultProps = {
size: 'small',
}
-export default PlayButton
diff --git a/ui/src/common/QuickFilter.js b/ui/src/common/QuickFilter.js
index 9ce40e9e..679739d6 100644
--- a/ui/src/common/QuickFilter.js
+++ b/ui/src/common/QuickFilter.js
@@ -8,11 +8,9 @@ const useQuickFilterStyles = makeStyles((theme) => ({
},
}))
-const QuickFilter = ({ source, label }) => {
+export const QuickFilter = ({ source, label }) => {
const translate = useTranslate()
const classes = useQuickFilterStyles()
const lbl = label || `resources.song.fields.${source}`
return
}
-
-export default QuickFilter
diff --git a/ui/src/common/RangeField.js b/ui/src/common/RangeField.js
index 2991a771..b88e155e 100644
--- a/ui/src/common/RangeField.js
+++ b/ui/src/common/RangeField.js
@@ -1,21 +1,8 @@
import React from 'react'
import PropTypes from 'prop-types'
+import { formatRange } from '../utils'
-const formatRange = (record, source) => {
- const nameCapitalized = source.charAt(0).toUpperCase() + source.slice(1)
- const min = record[`min${nameCapitalized}`]
- const max = record[`max${nameCapitalized}`]
- let range = []
- if (min) {
- range.push(min)
- }
- if (max && max !== min) {
- range.push(max)
- }
- return range.join('-')
-}
-
-const RangeField = ({ className, record = {}, source }) => {
+export const RangeField = ({ className, record = {}, source }) => {
return {formatRange(record, source)}
}
@@ -28,6 +15,3 @@ RangeField.propTypes = {
RangeField.defaultProps = {
addLabel: true,
}
-
-export { formatRange }
-export default RangeField
diff --git a/ui/src/common/ShuffleAllButton.js b/ui/src/common/ShuffleAllButton.js
index 255a3e5a..8cde1f10 100644
--- a/ui/src/common/ShuffleAllButton.js
+++ b/ui/src/common/ShuffleAllButton.js
@@ -5,7 +5,7 @@ import ShuffleIcon from '@material-ui/icons/Shuffle'
import { playTracks } from '../actions'
import PropTypes from 'prop-types'
-const ShuffleAllButton = ({ filters }) => {
+export const ShuffleAllButton = ({ filters }) => {
const translate = useTranslate()
const dataProvider = useDataProvider()
const dispatch = useDispatch()
@@ -46,5 +46,3 @@ ShuffleAllButton.propTypes = {
ShuffleAllButton.defaultProps = {
filters: {},
}
-
-export default ShuffleAllButton
diff --git a/ui/src/common/SimpleList.js b/ui/src/common/SimpleList.js
index d57c7f34..969bf58a 100644
--- a/ui/src/common/SimpleList.js
+++ b/ui/src/common/SimpleList.js
@@ -46,7 +46,7 @@ const LinkOrNot = ({
)
}
-const SimpleList = ({
+export const SimpleList = ({
basePath,
className,
classes: classesOverride,
@@ -145,5 +145,3 @@ SimpleList.defaultProps = {
hasBulkActions: false,
selectedIds: [],
}
-
-export default SimpleList
diff --git a/ui/src/common/SizeField.js b/ui/src/common/SizeField.js
index e1159b19..c5c02c5e 100644
--- a/ui/src/common/SizeField.js
+++ b/ui/src/common/SizeField.js
@@ -1,22 +1,11 @@
import React from 'react'
import PropTypes from 'prop-types'
+import { formatBytes } from '../utils'
-const SizeField = ({ record = {}, source }) => {
+export const SizeField = ({ record = {}, source }) => {
return {formatBytes(record[source])}
}
-export const formatBytes = (bytes, decimals = 2) => {
- if (bytes === 0) return '0 Bytes'
-
- const k = 1024
- const dm = decimals < 0 ? 0 : decimals
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
-
- const i = Math.floor(Math.log(bytes) / Math.log(k))
-
- return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
-}
-
SizeField.propTypes = {
label: PropTypes.string,
record: PropTypes.object,
@@ -26,5 +15,3 @@ SizeField.propTypes = {
SizeField.defaultProps = {
addLabel: true,
}
-
-export default SizeField
diff --git a/ui/src/common/SongBulkActions.js b/ui/src/common/SongBulkActions.js
index a80eaf2b..56d07b46 100644
--- a/ui/src/common/SongBulkActions.js
+++ b/ui/src/common/SongBulkActions.js
@@ -4,7 +4,7 @@ import { addTracks, playNext, playTracks } from '../actions'
import { RiPlayList2Fill, RiPlayListAddFill } from 'react-icons/ri'
import PlayArrowIcon from '@material-ui/icons/PlayArrow'
import { BatchPlayButton } from './index'
-import AddToPlaylistButton from './AddToPlaylistButton'
+import { AddToPlaylistButton } from './AddToPlaylistButton'
import { makeStyles } from '@material-ui/core/styles'
const useStyles = makeStyles((theme) => ({
@@ -13,7 +13,7 @@ const useStyles = makeStyles((theme) => ({
},
}))
-const SongBulkActions = (props) => {
+export const SongBulkActions = (props) => {
const classes = useStyles()
const unselectAll = useUnselectAll()
useEffect(() => {
@@ -46,5 +46,3 @@ const SongBulkActions = (props) => {
)
}
-
-export default SongBulkActions
diff --git a/ui/src/common/SongContextMenu.js b/ui/src/common/SongContextMenu.js
index f4499b90..ff816af9 100644
--- a/ui/src/common/SongContextMenu.js
+++ b/ui/src/common/SongContextMenu.js
@@ -7,9 +7,9 @@ import { makeStyles } from '@material-ui/core/styles'
import MoreVertIcon from '@material-ui/icons/MoreVert'
import { playNext, addTracks, setTrack, openAddToPlaylist } from '../actions'
import subsonic from '../subsonic'
-import StarButton from './StarButton'
-import { formatBytes } from './SizeField'
+import { StarButton } from './StarButton'
import config from '../config'
+import { formatBytes } from '../utils'
const useStyles = makeStyles({
noWrap: {
@@ -20,7 +20,7 @@ const useStyles = makeStyles({
},
})
-const SongContextMenu = ({
+export const SongContextMenu = ({
resource,
record,
showStar,
@@ -130,5 +130,3 @@ SongContextMenu.defaultProps = {
showStar: true,
addLabel: true,
}
-
-export default SongContextMenu
diff --git a/ui/src/common/SongDetails.js b/ui/src/common/SongDetails.js
index 589e64bf..fe4c39cc 100644
--- a/ui/src/common/SongDetails.js
+++ b/ui/src/common/SongDetails.js
@@ -9,7 +9,7 @@ import { BooleanField, DateField, TextField, useTranslate } from 'react-admin'
import inflection from 'inflection'
import { BitrateField, SizeField } from './index'
-const SongDetails = (props) => {
+export const SongDetails = (props) => {
const translate = useTranslate()
const { record } = props
const data = {
@@ -52,5 +52,3 @@ const SongDetails = (props) => {
)
}
-
-export default SongDetails
diff --git a/ui/src/common/SongTitleField.js b/ui/src/common/SongTitleField.js
index 60915dee..66d53531 100644
--- a/ui/src/common/SongTitleField.js
+++ b/ui/src/common/SongTitleField.js
@@ -24,7 +24,7 @@ const useStyles = makeStyles({
},
})
-const SongTitleField = ({ showTrackNumbers, ...props }) => {
+export const SongTitleField = ({ showTrackNumbers, ...props }) => {
const theme = useTheme()
const classes = useStyles()
const { record } = props
@@ -80,5 +80,3 @@ SongTitleField.defaultProps = {
record: {},
showTrackNumbers: false,
}
-
-export default SongTitleField
diff --git a/ui/src/common/StarButton.js b/ui/src/common/StarButton.js
index 5eecee96..9958747f 100644
--- a/ui/src/common/StarButton.js
+++ b/ui/src/common/StarButton.js
@@ -15,7 +15,7 @@ const useStyles = makeStyles({
},
})
-const StarButton = ({ resource, record, color, visible, size }) => {
+export const StarButton = ({ resource, record, color, visible, size }) => {
const [loading, setLoading] = useState(false)
const classes = useStyles({ color, visible, starred: record.starred })
const notify = useNotify()
@@ -86,5 +86,3 @@ StarButton.defaultProps = {
size: 'small',
color: 'inherit',
}
-
-export default StarButton
diff --git a/ui/src/common/Title.js b/ui/src/common/Title.js
index 0b4765c4..63eabcb1 100644
--- a/ui/src/common/Title.js
+++ b/ui/src/common/Title.js
@@ -2,7 +2,7 @@ import React from 'react'
import { useMediaQuery } from '@material-ui/core'
import { useTranslate } from 'react-admin'
-const Title = ({ subTitle, args }) => {
+export const Title = ({ subTitle, args }) => {
const translate = useTranslate()
const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md'))
const text = translate(subTitle, { ...args, _: subTitle })
@@ -12,5 +12,3 @@ const Title = ({ subTitle, args }) => {
}
return {text ? text : 'Navidrome'}
}
-
-export default Title
diff --git a/ui/src/common/Writable.js b/ui/src/common/Writable.js
index 79138247..0adedb1a 100644
--- a/ui/src/common/Writable.js
+++ b/ui/src/common/Writable.js
@@ -11,7 +11,7 @@ export const isReadOnly = (owner) => {
return !isWritable(owner)
}
-const Writable = (props) => {
+export const Writable = (props) => {
const { record = {}, children } = props
if (isWritable(record.owner)) {
return Children.map(children, (child) =>
@@ -20,5 +20,3 @@ const Writable = (props) => {
}
return null
}
-
-export default Writable
diff --git a/ui/src/common/index.js b/ui/src/common/index.js
index 1bba50b9..18c8c9b8 100644
--- a/ui/src/common/index.js
+++ b/ui/src/common/index.js
@@ -1,53 +1,24 @@
-import Title from './Title'
-import DurationField from './DurationField'
-import BitrateField from './BitrateField'
-import Pagination from './Pagination'
-import PlayButton from './PlayButton'
-import BatchPlayButton from './BatchPlayButton'
-import SongBulkActions from './SongBulkActions'
-import AddToPlaylistButton from './AddToPlaylistButton'
-import SimpleList from './SimpleList'
-import RangeField, { formatRange } from './RangeField'
-import ArtistLinkField, { useGetHandleArtistClick } from './ArtistLinkField'
-import SongDetails from './SongDetails'
-import SizeField from './SizeField'
-import DocLink from './DocLink'
-import List from './List'
-import { SongDatagrid, SongDatagridRow } from './SongDatagrid'
-import SongContextMenu from './SongContextMenu'
-import SongTitleField from './SongTitleField'
-import QuickFilter from './QuickFilter'
-import useAlbumsPerPage from './useAlbumsPerPage'
-import ShuffleAllButton from './ShuffleAllButton'
-import { AlbumContextMenu, ArtistContextMenu } from './ContextMenus'
-import StarButton from './StarButton'
-
-export {
- Title,
- DurationField,
- SizeField,
- BitrateField,
- Pagination,
- List,
- PlayButton,
- BatchPlayButton,
- SongBulkActions,
- AddToPlaylistButton,
- SimpleList,
- RangeField,
- SongDetails,
- SongDatagrid,
- SongDatagridRow,
- SongTitleField,
- DocLink,
- formatRange,
- ArtistLinkField,
- AlbumContextMenu,
- ArtistContextMenu,
- StarButton,
- useGetHandleArtistClick,
- SongContextMenu,
- QuickFilter,
- useAlbumsPerPage,
- ShuffleAllButton,
-}
+export * from './AddToPlaylistButton'
+export * from './ArtistLinkField'
+export * from './BatchPlayButton'
+export * from './BitrateField'
+export * from './ContextMenus'
+export * from './DocLink'
+export * from './DurationField'
+export * from './List'
+export * from './Pagination'
+export * from './PlayButton'
+export * from './QuickFilter'
+export * from './RangeField'
+export * from './ShuffleAllButton'
+export * from './SimpleList'
+export * from './SizeField'
+export * from './SongContextMenu'
+export * from './SongDatagrid'
+export * from './SongDetails'
+export * from './SongTitleField'
+export * from './StarButton'
+export * from './Title'
+export * from './SongBulkActions'
+export * from './useAlbumsPerPage'
+export * from './Writable'
diff --git a/ui/src/common/useAlbumsPerPage.js b/ui/src/common/useAlbumsPerPage.js
index 14c46511..316593f3 100644
--- a/ui/src/common/useAlbumsPerPage.js
+++ b/ui/src/common/useAlbumsPerPage.js
@@ -17,7 +17,7 @@ const getPerPageOptions = (width) => {
return options.map((v) => v * 6)
}
-const useAlbumsPerPage = (width) => {
+export const useAlbumsPerPage = (width) => {
const perPage =
useSelector((state) =>
get(state.admin.resources, ['album', 'list', 'params', 'perPage'])
@@ -25,5 +25,3 @@ const useAlbumsPerPage = (width) => {
return [perPage, getPerPageOptions(width)]
}
-
-export default useAlbumsPerPage
diff --git a/ui/src/dataProvider/httpClient.js b/ui/src/dataProvider/httpClient.js
index e35b44c6..153c2cbd 100644
--- a/ui/src/dataProvider/httpClient.js
+++ b/ui/src/dataProvider/httpClient.js
@@ -1,5 +1,5 @@
import { fetchUtils } from 'react-admin'
-import baseUrl from '../utils/baseUrl'
+import { baseUrl } from '../utils'
import config from '../config'
const customAuthorizationHeader = 'X-ND-Authorization'
diff --git a/ui/src/eventStream.js b/ui/src/eventStream.js
index 7b236cb1..981f99f9 100644
--- a/ui/src/eventStream.js
+++ b/ui/src/eventStream.js
@@ -1,4 +1,4 @@
-import baseUrl from './utils/baseUrl'
+import { baseUrl } from './utils'
import throttle from 'lodash.throttle'
let es = null
diff --git a/ui/src/personal/Personal.js b/ui/src/personal/Personal.js
index 2095d468..254324ba 100644
--- a/ui/src/personal/Personal.js
+++ b/ui/src/personal/Personal.js
@@ -13,7 +13,7 @@ import { makeStyles } from '@material-ui/core/styles'
import HelpOutlineIcon from '@material-ui/icons/HelpOutline'
import { changeTheme } from '../actions'
import themes from '../themes'
-import { docsUrl } from '../utils/docsUrl'
+import { docsUrl } from '../utils'
import { useGetLanguageChoices } from '../i18n'
import albumLists, { defaultAlbumList } from '../album/albumLists'
diff --git a/ui/src/playlist/PlaylistActions.js b/ui/src/playlist/PlaylistActions.js
index 0586514e..b0cc988a 100644
--- a/ui/src/playlist/PlaylistActions.js
+++ b/ui/src/playlist/PlaylistActions.js
@@ -16,7 +16,7 @@ import { playNext, addTracks, playTracks, shuffleTracks } from '../actions'
import { M3U_MIME_TYPE, REST_URL } from '../consts'
import subsonic from '../subsonic'
import PropTypes from 'prop-types'
-import { formatBytes } from '../common/SizeField'
+import { formatBytes } from '../utils'
import { useMediaQuery } from '@material-ui/core'
import config from '../config'
diff --git a/ui/src/playlist/PlaylistList.js b/ui/src/playlist/PlaylistList.js
index a9a0cc47..56d0e5d9 100644
--- a/ui/src/playlist/PlaylistList.js
+++ b/ui/src/playlist/PlaylistList.js
@@ -11,8 +11,7 @@ import {
useNotify,
} from 'react-admin'
import Switch from '@material-ui/core/Switch'
-import { DurationField, List } from '../common'
-import Writable, { isWritable } from '../common/Writable'
+import { DurationField, List, Writable, isWritable } from '../common'
const PlaylistFilter = (props) => (
diff --git a/ui/src/subsonic/index.js b/ui/src/subsonic/index.js
index c59bb9f1..07b061ce 100644
--- a/ui/src/subsonic/index.js
+++ b/ui/src/subsonic/index.js
@@ -1,5 +1,5 @@
import { fetchUtils } from 'react-admin'
-import baseUrl from '../utils/baseUrl'
+import { baseUrl } from '../utils'
const url = (command, id, options) => {
const params = new URLSearchParams()
diff --git a/ui/src/utils/baseUrl.js b/ui/src/utils/baseUrl.js
index 92c29fb8..d9f4dfb3 100644
--- a/ui/src/utils/baseUrl.js
+++ b/ui/src/utils/baseUrl.js
@@ -1,10 +1,8 @@
import config from '../config'
-const baseUrl = (path) => {
+export const baseUrl = (path) => {
const base = config.baseURL || ''
const parts = [base]
parts.push(path.replace(/^\//, ''))
return parts.join('/')
}
-
-export default baseUrl
diff --git a/ui/src/utils/formatters.js b/ui/src/utils/formatters.js
new file mode 100644
index 00000000..537fafc6
--- /dev/null
+++ b/ui/src/utils/formatters.js
@@ -0,0 +1,36 @@
+export const formatBytes = (bytes, decimals = 2) => {
+ if (bytes === 0) return '0 Bytes'
+
+ const k = 1024
+ const dm = decimals < 0 ? 0 : decimals
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
+
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
+
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
+}
+
+export const formatRange = (record, source) => {
+ const nameCapitalized = source.charAt(0).toUpperCase() + source.slice(1)
+ const min = record[`min${nameCapitalized}`]
+ const max = record[`max${nameCapitalized}`]
+ let range = []
+ if (min) {
+ range.push(min)
+ }
+ if (max && max !== min) {
+ range.push(max)
+ }
+ return range.join('-')
+}
+
+export const formatDuration = (d) => {
+ const hours = Math.floor(d / 3600)
+ const minutes = Math.floor(d / 60) % 60
+ const seconds = d % 60
+ return [hours, minutes, seconds]
+ .map((v) => Math.round(v).toString())
+ .map((v) => (v.length !== 2 ? '0' + v : v))
+ .filter((v, i) => v !== '00' || i > 0)
+ .join(':')
+}
diff --git a/ui/src/utils/index.js b/ui/src/utils/index.js
new file mode 100644
index 00000000..b8c5c5c6
--- /dev/null
+++ b/ui/src/utils/index.js
@@ -0,0 +1,3 @@
+export * from './baseUrl'
+export * from './docsUrl'
+export * from './formatters'