Handling long playlist comments (#2973)

Closes #1737

* wrapping playlist comment in a <Collapse> element

* Extract common collapsible logic into a component

---------

Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Tim 2024-04-26 02:28:25 +02:00 committed by GitHub
parent cf66594b6d
commit 8f9ed1b994
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 77 additions and 61 deletions

View File

@ -1,4 +1,4 @@
import React, { useMemo, useCallback, useState, useEffect } from 'react' import { useState, useEffect, useCallback } from 'react'
import { import {
Card, Card,
CardContent, CardContent,
@ -17,7 +17,6 @@ import {
ChipField, ChipField,
Link, Link,
} from 'react-admin' } from 'react-admin'
import clsx from 'clsx'
import Lightbox from 'react-image-lightbox' import Lightbox from 'react-image-lightbox'
import 'react-image-lightbox/style.css' import 'react-image-lightbox/style.css'
import subsonic from '../subsonic' import subsonic from '../subsonic'
@ -29,11 +28,11 @@ import {
LoveButton, LoveButton,
RatingField, RatingField,
useAlbumsPerPage, useAlbumsPerPage,
CollapsibleComment,
} from '../common' } from '../common'
import config from '../config' import config from '../config'
import { formatFullDate, intersperse } from '../utils' import { formatFullDate, intersperse } from '../utils'
import AlbumExternalLinks from './AlbumExternalLinks' import AlbumExternalLinks from './AlbumExternalLinks'
import AnchorMe from '../common/Linkify'
const useStyles = makeStyles( const useStyles = makeStyles(
(theme) => ({ (theme) => ({
@ -85,12 +84,6 @@ const useStyles = makeStyles(
top: theme.spacing(-0.2), top: theme.spacing(-0.2),
left: theme.spacing(0.5), left: theme.spacing(0.5),
}, },
commentBlock: {
display: 'inline-block',
marginTop: '1em',
float: 'left',
wordBreak: 'break-word',
},
notes: { notes: {
display: 'inline-block', display: 'inline-block',
marginTop: '1em', marginTop: '1em',
@ -98,9 +91,6 @@ const useStyles = makeStyles(
wordBreak: 'break-word', wordBreak: 'break-word',
cursor: 'pointer', cursor: 'pointer',
}, },
pointerCursor: {
cursor: 'pointer',
},
recordName: {}, recordName: {},
recordArtist: {}, recordArtist: {},
recordMeta: {}, recordMeta: {},
@ -116,41 +106,6 @@ const useStyles = makeStyles(
}, },
) )
const AlbumComment = ({ record }) => {
const classes = useStyles()
const [expanded, setExpanded] = React.useState(false)
const lines = record.comment.split('\n')
const formatted = useMemo(() => {
return lines.map((line, idx) => (
<span key={record.id + '-comment-' + idx}>
<AnchorMe text={line} />
<br />
</span>
))
}, [lines, record.id])
const handleExpandClick = useCallback(() => {
setExpanded(!expanded)
}, [expanded, setExpanded])
return (
<Collapse
collapsedHeight={'1.5em'}
in={expanded}
timeout={'auto'}
className={clsx(
classes.commentBlock,
lines.length > 1 && classes.pointerCursor,
)}
>
<Typography variant={'body1'} onClick={handleExpandClick}>
{formatted}
</Typography>
</Collapse>
)
}
export const useGetHandleGenreClick = (width) => { export const useGetHandleGenreClick = (width) => {
const [perPage] = useAlbumsPerPage(width) const [perPage] = useAlbumsPerPage(width)
@ -268,7 +223,7 @@ const AlbumDetails = (props) => {
const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs')) const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs'))
const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('lg')) const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('lg'))
const classes = useStyles() const classes = useStyles()
const [isLightboxOpen, setLightboxOpen] = React.useState(false) const [isLightboxOpen, setLightboxOpen] = useState(false)
const [expanded, setExpanded] = useState(false) const [expanded, setExpanded] = useState(false)
const [albumInfo, setAlbumInfo] = useState() const [albumInfo, setAlbumInfo] = useState()
@ -296,11 +251,8 @@ const AlbumDetails = (props) => {
const imageUrl = subsonic.getCoverArtUrl(record, 300) const imageUrl = subsonic.getCoverArtUrl(record, 300)
const fullImageUrl = subsonic.getCoverArtUrl(record) const fullImageUrl = subsonic.getCoverArtUrl(record)
const handleOpenLightbox = React.useCallback(() => setLightboxOpen(true), []) const handleOpenLightbox = useCallback(() => setLightboxOpen(true), [])
const handleCloseLightbox = React.useCallback( const handleCloseLightbox = useCallback(() => setLightboxOpen(false), [])
() => setLightboxOpen(false),
[],
)
return ( return (
<Card className={classes.root}> <Card className={classes.root}>
<div className={classes.cardContents}> <div className={classes.cardContents}>
@ -373,11 +325,15 @@ const AlbumDetails = (props) => {
</Typography> </Typography>
</Collapse> </Collapse>
)} )}
{isDesktop && record['comment'] && <AlbumComment record={record} />} {isDesktop && record['comment'] && (
<CollapsibleComment record={record} />
)}
</CardContent> </CardContent>
</div> </div>
</div> </div>
{!isDesktop && record['comment'] && <AlbumComment record={record} />} {!isDesktop && record['comment'] && (
<CollapsibleComment record={record} />
)}
{!isDesktop && ( {!isDesktop && (
<div className={classes.notes}> <div className={classes.notes}>
<Collapse collapsedHeight={'1.5em'} in={expanded} timeout={'auto'}> <Collapse collapsedHeight={'1.5em'} in={expanded} timeout={'auto'}>

View File

@ -0,0 +1,57 @@
import { useCallback, useMemo, useState } from 'react'
import { Typography, Collapse } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import AnchorMe from './Linkify'
import clsx from 'clsx'
const useStyles = makeStyles(
(theme) => ({
commentBlock: {
display: 'inline-block',
marginTop: '1em',
float: 'left',
wordBreak: 'break-word',
},
pointerCursor: {
cursor: 'pointer',
},
}),
{
name: 'NDCollapsibleComment',
},
)
export const CollapsibleComment = ({ record }) => {
const classes = useStyles()
const [expanded, setExpanded] = useState(false)
const lines = record.comment.split('\n')
const formatted = useMemo(() => {
return lines.map((line, idx) => (
<span key={record.id + '-comment-' + idx}>
<AnchorMe text={line} />
<br />
</span>
))
}, [lines, record.id])
const handleExpandClick = useCallback(() => {
setExpanded(!expanded)
}, [expanded, setExpanded])
return (
<Collapse
collapsedHeight={'2em'}
in={expanded}
timeout={'auto'}
className={clsx(
classes.commentBlock,
lines.length > 1 && classes.pointerCursor,
)}
>
<Typography variant={'h6'} onClick={handleExpandClick}>
{formatted}
</Typography>
</Collapse>
)
}

View File

@ -2,6 +2,7 @@ export * from './AddToPlaylistButton'
export * from './ArtistLinkField' export * from './ArtistLinkField'
export * from './BatchPlayButton' export * from './BatchPlayButton'
export * from './BitrateField' export * from './BitrateField'
export * from './CollapsibleComment'
export * from './ContextMenus' export * from './ContextMenus'
export * from './DateField' export * from './DateField'
export * from './DocLink' export * from './DocLink'

View File

@ -1,9 +1,7 @@
import React from 'react'
import { Card, CardContent, Typography } from '@material-ui/core' import { Card, CardContent, Typography } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import { useTranslate } from 'react-admin' import { useTranslate } from 'react-admin'
import { DurationField, SizeField } from '../common' import { CollapsibleComment, DurationField, SizeField } from '../common'
import Linkify from '../common/Linkify'
const useStyles = makeStyles( const useStyles = makeStyles(
(theme) => ({ (theme) => ({
@ -52,9 +50,7 @@ const PlaylistDetails = (props) => {
<Typography variant="h5" className={classes.title}> <Typography variant="h5" className={classes.title}>
{record.name || translate('ra.page.loading')} {record.name || translate('ra.page.loading')}
</Typography> </Typography>
<Typography component="h6"> <CollapsibleComment record={record} />
<Linkify text={record.comment} />
</Typography>
<Typography component="p"> <Typography component="p">
{record.songCount ? ( {record.songCount ? (
<span> <span>

View File

@ -300,6 +300,8 @@ export default {
fontSize: '.875rem', fontSize: '.875rem',
color: 'rgb(113 113 113 / 80%)', color: 'rgb(113 113 113 / 80%)',
}, },
},
NDCollapsibleComment: {
commentBlock: { commentBlock: {
fontSize: '.875rem', fontSize: '.875rem',
color: 'rgb(113 113 113 / 80%)', color: 'rgb(113 113 113 / 80%)',

View File

@ -289,6 +289,8 @@ export default {
fontSize: '.875rem', fontSize: '.875rem',
color: 'rgba(255,255,255, 0.8)', color: 'rgba(255,255,255, 0.8)',
}, },
},
NDCollapsibleComment: {
commentBlock: { commentBlock: {
fontSize: '.875rem', fontSize: '.875rem',
color: 'rgba(255,255,255, 0.8)', color: 'rgba(255,255,255, 0.8)',

View File

@ -230,6 +230,8 @@ export default {
fontSize: '.875rem', fontSize: '.875rem',
color: 'rgba(255,255,255, 0.8)', color: 'rgba(255,255,255, 0.8)',
}, },
},
NDCollapsibleComment: {
commentBlock: { commentBlock: {
fontSize: '.875rem', fontSize: '.875rem',
color: 'rgba(255,255,255, 0.8)', color: 'rgba(255,255,255, 0.8)',