mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
add explicit / clean indicators for album and song titles (#1634)
This commit is contained in:
@@ -28,6 +28,7 @@ import {
|
|||||||
formatRating,
|
formatRating,
|
||||||
} from '/@/renderer/utils/format';
|
} from '/@/renderer/utils/format';
|
||||||
import { SEPARATOR_STRING } from '/@/shared/api/utils';
|
import { SEPARATOR_STRING } from '/@/shared/api/utils';
|
||||||
|
import { ExplicitIndicator } from '/@/shared/components/explicit-indicator/explicit-indicator';
|
||||||
import { Group } from '/@/shared/components/group/group';
|
import { Group } from '/@/shared/components/group/group';
|
||||||
import { Icon } from '/@/shared/components/icon/icon';
|
import { Icon } from '/@/shared/components/icon/icon';
|
||||||
import { Separator } from '/@/shared/components/separator/separator';
|
import { Separator } from '/@/shared/components/separator/separator';
|
||||||
@@ -1010,6 +1011,7 @@ export const getDataRows = (type?: 'compact' | 'default' | 'poster'): DataRow[]
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
format: (data) => {
|
format: (data) => {
|
||||||
|
const explicitStatus = 'explicitStatus' in data ? data.explicitStatus : null;
|
||||||
if ('name' in data && data.name) {
|
if ('name' in data && data.name) {
|
||||||
if ('id' in data && data.id) {
|
if ('id' in data && data.id) {
|
||||||
if ('_itemType' in data) {
|
if ('_itemType' in data) {
|
||||||
@@ -1022,6 +1024,7 @@ export const getDataRows = (type?: 'compact' | 'default' | 'poster'): DataRow[]
|
|||||||
albumId: data.id,
|
albumId: data.id,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
<ExplicitIndicator explicitStatus={explicitStatus} />
|
||||||
{data.name}
|
{data.name}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@@ -1036,6 +1039,7 @@ export const getDataRows = (type?: 'compact' | 'default' | 'poster'): DataRow[]
|
|||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
<ExplicitIndicator explicitStatus={explicitStatus} />
|
||||||
{data.name}
|
{data.name}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@@ -1062,11 +1066,21 @@ export const getDataRows = (type?: 'compact' | 'default' | 'poster'): DataRow[]
|
|||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return data.name;
|
return (
|
||||||
|
<>
|
||||||
|
<ExplicitIndicator explicitStatus={explicitStatus} />
|
||||||
|
{data.name}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return data.name;
|
return (
|
||||||
|
<>
|
||||||
|
<ExplicitIndicator explicitStatus={explicitStatus} />
|
||||||
|
{data.name}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
import { useIsActiveRow } from '/@/renderer/components/item-list/item-table-list/item-table-list-context';
|
import { useIsActiveRow } from '/@/renderer/components/item-list/item-table-list/item-table-list-context';
|
||||||
import { JoinedArtists } from '/@/renderer/features/albums/components/joined-artists';
|
import { JoinedArtists } from '/@/renderer/features/albums/components/joined-artists';
|
||||||
|
import { ExplicitIndicator } from '/@/shared/components/explicit-indicator/explicit-indicator';
|
||||||
import { Icon } from '/@/shared/components/icon/icon';
|
import { Icon } from '/@/shared/components/icon/icon';
|
||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
import { Folder, LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
import { Folder, LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
||||||
@@ -52,6 +53,7 @@ export const DefaultTitleArtistColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Text className={styles.title} isNoSelect size="md" {...titleLinkProps}>
|
<Text className={styles.title} isNoSelect size="md" {...titleLinkProps}>
|
||||||
|
<ExplicitIndicator explicitStatus={item?.explicitStatus} />
|
||||||
{item.name as string}
|
{item.name as string}
|
||||||
</Text>
|
</Text>
|
||||||
<div className={styles.artists}>
|
<div className={styles.artists}>
|
||||||
@@ -120,6 +122,7 @@ export const QueueSongTitleArtistColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
size="md"
|
size="md"
|
||||||
{...titleLinkProps}
|
{...titleLinkProps}
|
||||||
>
|
>
|
||||||
|
<ExplicitIndicator explicitStatus={song?.explicitStatus} />
|
||||||
{row.name as string}
|
{row.name as string}
|
||||||
{song?.trackSubtitle && props.itemType !== LibraryItem.QUEUE_SONG && (
|
{song?.trackSubtitle && props.itemType !== LibraryItem.QUEUE_SONG && (
|
||||||
<Text
|
<Text
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
TableColumnContainer,
|
TableColumnContainer,
|
||||||
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
import { useIsActiveRow } from '/@/renderer/components/item-list/item-table-list/item-table-list-context';
|
import { useIsActiveRow } from '/@/renderer/components/item-list/item-table-list/item-table-list-context';
|
||||||
|
import { ExplicitIndicator } from '/@/shared/components/explicit-indicator/explicit-indicator';
|
||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ function DefaultTitleColumn(props: ItemTableListInnerColumn) {
|
|||||||
isNoSelect
|
isNoSelect
|
||||||
{...titleLinkProps}
|
{...titleLinkProps}
|
||||||
>
|
>
|
||||||
|
<ExplicitIndicator explicitStatus={item?.explicitStatus} />
|
||||||
{row}
|
{row}
|
||||||
</Text>
|
</Text>
|
||||||
</TableColumnContainer>
|
</TableColumnContainer>
|
||||||
@@ -103,6 +105,7 @@ function QueueSongTitleColumn(props: ItemTableListInnerColumn) {
|
|||||||
isNoSelect
|
isNoSelect
|
||||||
{...titleLinkProps}
|
{...titleLinkProps}
|
||||||
>
|
>
|
||||||
|
<ExplicitIndicator explicitStatus={song?.explicitStatus} />
|
||||||
{row}
|
{row}
|
||||||
{song?.trackSubtitle && props.itemType !== LibraryItem.QUEUE_SONG && (
|
{song?.trackSubtitle && props.itemType !== LibraryItem.QUEUE_SONG && (
|
||||||
<Text
|
<Text
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
PlayTooltip,
|
PlayTooltip,
|
||||||
} from '/@/renderer/features/shared/components/play-button-group';
|
} from '/@/renderer/features/shared/components/play-button-group';
|
||||||
import { usePlayButtonBehavior } from '/@/renderer/store';
|
import { usePlayButtonBehavior } from '/@/renderer/store';
|
||||||
|
import { ExplicitIndicator } from '/@/shared/components/explicit-indicator/explicit-indicator';
|
||||||
import { Icon } from '/@/shared/components/icon/icon';
|
import { Icon } from '/@/shared/components/icon/icon';
|
||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
import { Folder, LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
import { Folder, LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
||||||
@@ -140,6 +141,7 @@ export const DefaultTitleCombinedColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Text className={styles.title} isNoSelect size="md" {...titleLinkProps}>
|
<Text className={styles.title} isNoSelect size="md" {...titleLinkProps}>
|
||||||
|
<ExplicitIndicator explicitStatus={item?.explicitStatus} />
|
||||||
{item.name as string}
|
{item.name as string}
|
||||||
</Text>
|
</Text>
|
||||||
<div className={styles.artists}>
|
<div className={styles.artists}>
|
||||||
@@ -289,6 +291,7 @@ export const QueueSongTitleCombinedColumn = (props: ItemTableListInnerColumn) =>
|
|||||||
size="md"
|
size="md"
|
||||||
{...titleLinkProps}
|
{...titleLinkProps}
|
||||||
>
|
>
|
||||||
|
<ExplicitIndicator explicitStatus={song?.explicitStatus} />
|
||||||
{row.name as string}
|
{row.name as string}
|
||||||
{song?.trackSubtitle && props.itemType !== LibraryItem.QUEUE_SONG && (
|
{song?.trackSubtitle && props.itemType !== LibraryItem.QUEUE_SONG && (
|
||||||
<Text
|
<Text
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
.root {
|
||||||
|
font-family:
|
||||||
|
'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', 'Segoe UI Symbol', Twemoji,
|
||||||
|
sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-xs {
|
||||||
|
font-size: var(--theme-font-size-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-sm {
|
||||||
|
font-size: var(--theme-font-size-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-md {
|
||||||
|
font-size: var(--theme-font-size-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-lg {
|
||||||
|
font-size: var(--theme-font-size-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-xl {
|
||||||
|
font-size: var(--theme-font-size-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-2xl {
|
||||||
|
font-size: var(--theme-font-size-2xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-3xl {
|
||||||
|
font-size: var(--theme-font-size-3xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-4xl {
|
||||||
|
font-size: var(--theme-font-size-4xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.muted {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import clsx from 'clsx';
|
||||||
|
import { ComponentPropsWithoutRef } from 'react';
|
||||||
|
|
||||||
|
import styles from './explicit-indicator.module.css';
|
||||||
|
|
||||||
|
import { ExplicitStatus } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
|
const EXPLICIT_SYMBOL = '🅴';
|
||||||
|
const CLEAN_SYMBOL = '🅲';
|
||||||
|
|
||||||
|
export interface ExplicitIndicatorProps extends ComponentPropsWithoutRef<'span'> {
|
||||||
|
explicitStatus: ExplicitStatus | null | undefined;
|
||||||
|
size?: ExplicitIndicatorSize;
|
||||||
|
withSpace?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExplicitIndicatorSize = '2xl' | '3xl' | '4xl' | 'lg' | 'md' | 'sm' | 'xl' | 'xs';
|
||||||
|
|
||||||
|
export const ExplicitIndicator = ({
|
||||||
|
className,
|
||||||
|
explicitStatus,
|
||||||
|
size = 'lg',
|
||||||
|
withSpace = true,
|
||||||
|
...rest
|
||||||
|
}: ExplicitIndicatorProps) => {
|
||||||
|
if (explicitStatus !== ExplicitStatus.EXPLICIT && explicitStatus !== ExplicitStatus.CLEAN) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const symbol = explicitStatus === ExplicitStatus.EXPLICIT ? EXPLICIT_SYMBOL : CLEAN_SYMBOL;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
aria-label={explicitStatus === ExplicitStatus.EXPLICIT ? 'Explicit' : 'Clean'}
|
||||||
|
className={clsx(styles.root, styles[`size-${size}`], className)}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{withSpace ? `${symbol} ` : symbol}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user