enable comments, safer note
This commit is contained in:
parent
960427fce8
commit
5516daab6e
8 changed files with 62 additions and 14 deletions
|
@ -214,6 +214,7 @@ const normalizeAlbum = (
|
||||||
name: entry.Name,
|
name: entry.Name,
|
||||||
})),
|
})),
|
||||||
backdropImageUrl: null,
|
backdropImageUrl: null,
|
||||||
|
comment: null,
|
||||||
createdAt: item.DateCreated,
|
createdAt: item.DateCreated,
|
||||||
duration: item.RunTimeTicks / 10000,
|
duration: item.RunTimeTicks / 10000,
|
||||||
genres: item.GenreItems?.map((entry) => ({
|
genres: item.GenreItems?.map((entry) => ({
|
||||||
|
|
|
@ -154,6 +154,7 @@ const normalizeAlbum = (
|
||||||
albumArtists: [{ id: item.albumArtistId, imageUrl: null, name: item.albumArtist }],
|
albumArtists: [{ id: item.albumArtistId, imageUrl: null, name: item.albumArtist }],
|
||||||
artists: [{ id: item.artistId, imageUrl: null, name: item.artist }],
|
artists: [{ id: item.artistId, imageUrl: null, name: item.artist }],
|
||||||
backdropImageUrl: imageBackdropUrl,
|
backdropImageUrl: imageBackdropUrl,
|
||||||
|
comment: item.comment || null,
|
||||||
createdAt: item.createdAt.split('T')[0],
|
createdAt: item.createdAt.split('T')[0],
|
||||||
duration: item.duration * 1000 || null,
|
duration: item.duration * 1000 || null,
|
||||||
genres: item.genres?.map((genre) => ({
|
genres: item.genres?.map((genre) => ({
|
||||||
|
|
|
@ -111,6 +111,7 @@ const album = z.object({
|
||||||
allArtistIds: z.string(),
|
allArtistIds: z.string(),
|
||||||
artist: z.string(),
|
artist: z.string(),
|
||||||
artistId: z.string(),
|
artistId: z.string(),
|
||||||
|
comment: z.string().optional(),
|
||||||
compilation: z.boolean(),
|
compilation: z.boolean(),
|
||||||
coverArtId: z.string().optional(), // Removed after v0.48.0
|
coverArtId: z.string().optional(), // Removed after v0.48.0
|
||||||
coverArtPath: z.string().optional(), // Removed after v0.48.0
|
coverArtPath: z.string().optional(), // Removed after v0.48.0
|
||||||
|
|
|
@ -155,6 +155,7 @@ const normalizeAlbum = (
|
||||||
: [],
|
: [],
|
||||||
artists: item.artistId ? [{ id: item.artistId, imageUrl: null, name: item.artist }] : [],
|
artists: item.artistId ? [{ id: item.artistId, imageUrl: null, name: item.artist }] : [],
|
||||||
backdropImageUrl: null,
|
backdropImageUrl: null,
|
||||||
|
comment: null,
|
||||||
createdAt: item.created,
|
createdAt: item.created,
|
||||||
duration: item.duration,
|
duration: item.duration,
|
||||||
genres: item.genre
|
genres: item.genre
|
||||||
|
|
|
@ -147,6 +147,7 @@ export type Album = {
|
||||||
albumArtists: RelatedArtist[];
|
albumArtists: RelatedArtist[];
|
||||||
artists: RelatedArtist[];
|
artists: RelatedArtist[];
|
||||||
backdropImageUrl: string | null;
|
backdropImageUrl: string | null;
|
||||||
|
comment: string | null;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
duration: number | null;
|
duration: number | null;
|
||||||
genres: Genre[];
|
genres: Genre[];
|
||||||
|
|
|
@ -3,17 +3,7 @@ import { Skeleton } from '/@/renderer/components/skeleton';
|
||||||
import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell';
|
import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { Text } from '/@/renderer/components/text';
|
import { Text } from '/@/renderer/components/text';
|
||||||
|
import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify';
|
||||||
const URL_REGEX =
|
|
||||||
/((?:https?:\/\/)?(?:[\w-]{1,32}(?:\.[\w-]{1,32})+)(?:\/[\w\-./?%&=][^.|^\s]*)?)/g;
|
|
||||||
|
|
||||||
const replaceURLWithHTMLLinks = (text: string) => {
|
|
||||||
const urlRegex = new RegExp(URL_REGEX, 'g');
|
|
||||||
return text.replaceAll(
|
|
||||||
urlRegex,
|
|
||||||
(url) => `<a href="${url}" target="_blank" rel="noreferrer">${url}</a>`,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const NoteCell = ({ value }: ICellRendererParams) => {
|
export const NoteCell = ({ value }: ICellRendererParams) => {
|
||||||
const formattedValue = useMemo(() => {
|
const formattedValue = useMemo(() => {
|
||||||
|
@ -39,9 +29,10 @@ export const NoteCell = ({ value }: ICellRendererParams) => {
|
||||||
<CellContainer $position="left">
|
<CellContainer $position="left">
|
||||||
<Text
|
<Text
|
||||||
$secondary
|
$secondary
|
||||||
dangerouslySetInnerHTML={{ __html: formattedValue }}
|
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
/>
|
>
|
||||||
|
{formattedValue}
|
||||||
|
</Text>
|
||||||
</CellContainer>
|
</CellContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { MutableRefObject, useCallback, useMemo } from 'react';
|
import { MutableRefObject, useCallback, useMemo } from 'react';
|
||||||
import { RowDoubleClickedEvent, RowHeightParams, RowNode } from '@ag-grid-community/core';
|
import { RowDoubleClickedEvent, RowHeightParams, RowNode } from '@ag-grid-community/core';
|
||||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||||
import { Box, Group, Stack } from '@mantine/core';
|
import { Box, Group, Spoiler, Stack } from '@mantine/core';
|
||||||
import { useSetState } from '@mantine/hooks';
|
import { useSetState } from '@mantine/hooks';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { RiHeartFill, RiHeartLine, RiMoreFill, RiSettings2Fill } from 'react-icons/ri';
|
import { RiHeartFill, RiHeartLine, RiMoreFill, RiSettings2Fill } from 'react-icons/ri';
|
||||||
|
@ -41,6 +41,7 @@ import {
|
||||||
useTableSettings,
|
useTableSettings,
|
||||||
} from '/@/renderer/store/settings.store';
|
} from '/@/renderer/store/settings.store';
|
||||||
import { Play } from '/@/renderer/types';
|
import { Play } from '/@/renderer/types';
|
||||||
|
import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify';
|
||||||
|
|
||||||
const isFullWidthRow = (node: RowNode) => {
|
const isFullWidthRow = (node: RowNode) => {
|
||||||
return node.id?.startsWith('disc-');
|
return node.id?.startsWith('disc-');
|
||||||
|
@ -279,6 +280,7 @@ export const AlbumDetailContent = ({ tableRef, background }: AlbumDetailContentP
|
||||||
};
|
};
|
||||||
|
|
||||||
const showGenres = detailQuery?.data?.genres ? detailQuery?.data?.genres.length !== 0 : false;
|
const showGenres = detailQuery?.data?.genres ? detailQuery?.data?.genres.length !== 0 : false;
|
||||||
|
const comment = detailQuery?.data?.comment;
|
||||||
|
|
||||||
const handleGeneralContextMenu = useHandleGeneralContextMenu(
|
const handleGeneralContextMenu = useHandleGeneralContextMenu(
|
||||||
LibraryItem.ALBUM,
|
LibraryItem.ALBUM,
|
||||||
|
@ -395,6 +397,18 @@ export const AlbumDetailContent = ({ tableRef, background }: AlbumDetailContentP
|
||||||
</Group>
|
</Group>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
{comment && (
|
||||||
|
<Box component="section">
|
||||||
|
<Spoiler
|
||||||
|
hideLabel={t('common.collapse')}
|
||||||
|
maxHeight={60}
|
||||||
|
mb={20}
|
||||||
|
showLabel={t('common.expand')}
|
||||||
|
>
|
||||||
|
{replaceURLWithHTMLLinks(comment)}
|
||||||
|
</Spoiler>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
<Box style={{ minHeight: '300px' }}>
|
<Box style={{ minHeight: '300px' }}>
|
||||||
<VirtualTable
|
<VirtualTable
|
||||||
key={`table-${tableConfig.rowHeight}`}
|
key={`table-${tableConfig.rowHeight}`}
|
||||||
|
|
38
src/renderer/utils/linkify.tsx
Normal file
38
src/renderer/utils/linkify.tsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Inspired by https://github.com/navidrome/navidrome/blob/c530ccf13854e3a840ddf63eef5e2323fbe2827d/ui/src/common/AnchorMe.js
|
||||||
|
const URL_REGEX =
|
||||||
|
/((?:https?:\/\/)?(?:[\w-]{1,32}(?:\.[\w-]{1,32})+)(?:\/[\w\-./?%&=][^.|^\s]*)?)/g;
|
||||||
|
|
||||||
|
export const replaceURLWithHTMLLinks = (text: string) => {
|
||||||
|
const urlRegex = new RegExp(URL_REGEX, 'g');
|
||||||
|
const matches = text.matchAll(urlRegex);
|
||||||
|
const elements = [];
|
||||||
|
let lastIndex = 0;
|
||||||
|
|
||||||
|
for (const match of matches) {
|
||||||
|
const position = match.index!;
|
||||||
|
|
||||||
|
if (position > lastIndex) {
|
||||||
|
elements.push(text.substring(lastIndex, position));
|
||||||
|
}
|
||||||
|
|
||||||
|
const link = match[0];
|
||||||
|
elements.push(
|
||||||
|
<a
|
||||||
|
key={lastIndex}
|
||||||
|
href={link}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{link}
|
||||||
|
</a>,
|
||||||
|
);
|
||||||
|
|
||||||
|
lastIndex = position + link.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.length > lastIndex) {
|
||||||
|
elements.push(text.substring(lastIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
};
|
Reference in a new issue