mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
feat: "open in spotify" button (#1839)
* feat: open in spotify * fix: disable native spotify URI by default
This commit is contained in:
@@ -298,6 +298,8 @@ interface AlbumMetadataExternalLinksProps {
|
||||
lastFM: boolean;
|
||||
mbzId?: null | string;
|
||||
musicBrainz: boolean;
|
||||
nativeSpotify: boolean;
|
||||
spotify: boolean;
|
||||
}
|
||||
|
||||
const AlbumMetadataExternalLinks = ({
|
||||
@@ -307,10 +309,12 @@ const AlbumMetadataExternalLinks = ({
|
||||
lastFM,
|
||||
mbzId,
|
||||
musicBrainz,
|
||||
nativeSpotify,
|
||||
spotify,
|
||||
}: AlbumMetadataExternalLinksProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!externalLinks || (!lastFM && !musicBrainz)) return null;
|
||||
if (!externalLinks || (!lastFM && !musicBrainz && !spotify)) return null;
|
||||
|
||||
return (
|
||||
<Stack gap="xs">
|
||||
@@ -358,6 +362,28 @@ const AlbumMetadataExternalLinks = ({
|
||||
variant="subtle"
|
||||
/>
|
||||
) : null}
|
||||
{spotify && (
|
||||
<ActionIcon
|
||||
component="a"
|
||||
href={
|
||||
nativeSpotify
|
||||
? `spotify:search:${encodeURIComponent(albumArtist || '')}%20${encodeURIComponent(albumName || '')}`
|
||||
: `https://open.spotify.com/search/${encodeURIComponent(albumArtist || '')}%20${encodeURIComponent(albumName || '')}`
|
||||
}
|
||||
icon="brandSpotify"
|
||||
iconProps={{
|
||||
fill: 'default',
|
||||
size: 'xl',
|
||||
}}
|
||||
radius="md"
|
||||
rel="noopener noreferrer"
|
||||
target={nativeSpotify ? undefined : '_blank'}
|
||||
tooltip={{
|
||||
label: t('action.openIn.spotify'),
|
||||
}}
|
||||
variant="subtle"
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
@@ -370,7 +396,7 @@ export const AlbumDetailContent = () => {
|
||||
albumQueries.detail({ query: { id: albumId }, serverId: server.id }),
|
||||
);
|
||||
|
||||
const { externalLinks, lastFM, musicBrainz } = useExternalLinks();
|
||||
const { externalLinks, lastFM, musicBrainz, nativeSpotify, spotify } = useExternalLinks();
|
||||
|
||||
const comment = detailQuery?.data?.comment;
|
||||
|
||||
@@ -403,6 +429,8 @@ export const AlbumDetailContent = () => {
|
||||
lastFM={lastFM}
|
||||
mbzId={mbzId || undefined}
|
||||
musicBrainz={musicBrainz}
|
||||
nativeSpotify={nativeSpotify}
|
||||
spotify={spotify}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -890,7 +890,9 @@ interface AlbumArtistMetadataExternalLinksProps {
|
||||
lastFM: boolean;
|
||||
mbzId?: null | string;
|
||||
musicBrainz: boolean;
|
||||
nativeSpotify: boolean;
|
||||
order?: number;
|
||||
spotify: boolean;
|
||||
}
|
||||
|
||||
const AlbumArtistMetadataExternalLinks = ({
|
||||
@@ -899,11 +901,13 @@ const AlbumArtistMetadataExternalLinks = ({
|
||||
lastFM,
|
||||
mbzId,
|
||||
musicBrainz,
|
||||
nativeSpotify,
|
||||
order,
|
||||
spotify,
|
||||
}: AlbumArtistMetadataExternalLinksProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!externalLinks || (!lastFM && !musicBrainz)) return null;
|
||||
if (!externalLinks || (!lastFM && !musicBrainz && !spotify)) return null;
|
||||
|
||||
return (
|
||||
<Grid.Col order={order} span={12}>
|
||||
@@ -948,6 +952,27 @@ const AlbumArtistMetadataExternalLinks = ({
|
||||
variant="subtle"
|
||||
/>
|
||||
) : null}
|
||||
{spotify && (
|
||||
<ActionIcon
|
||||
component="a"
|
||||
href={
|
||||
nativeSpotify
|
||||
? `spotify:search:${encodeURIComponent(artistName || '')}`
|
||||
: `https://open.spotify.com/search/${encodeURIComponent(artistName || '')}`
|
||||
}
|
||||
icon="brandSpotify"
|
||||
iconProps={{
|
||||
fill: 'default',
|
||||
size: 'xl',
|
||||
}}
|
||||
rel="noopener noreferrer"
|
||||
target={nativeSpotify ? undefined : '_blank'}
|
||||
tooltip={{
|
||||
label: t('action.openIn.spotify'),
|
||||
}}
|
||||
variant="subtle"
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
@@ -1050,7 +1075,7 @@ export const AlbumArtistDetailContent = ({
|
||||
}: AlbumArtistDetailContentProps) => {
|
||||
const artistItems = useArtistItems();
|
||||
const artistRadioCount = useArtistRadioCount();
|
||||
const { externalLinks, lastFM, musicBrainz } = useExternalLinks();
|
||||
const { externalLinks, lastFM, musicBrainz, nativeSpotify, spotify } = useExternalLinks();
|
||||
const { albumArtistId, artistId } = useParams() as {
|
||||
albumArtistId?: string;
|
||||
artistId?: string;
|
||||
@@ -1136,14 +1161,16 @@ export const AlbumArtistDetailContent = ({
|
||||
genres={detailQuery.data?.genres}
|
||||
order={genresOrder}
|
||||
/>
|
||||
{externalLinks && (lastFM || musicBrainz) && (
|
||||
{externalLinks && (lastFM || musicBrainz || spotify) && (
|
||||
<AlbumArtistMetadataExternalLinks
|
||||
artistName={detailQuery.data?.name}
|
||||
externalLinks={externalLinks}
|
||||
lastFM={lastFM}
|
||||
mbzId={mbzId}
|
||||
musicBrainz={musicBrainz}
|
||||
nativeSpotify={nativeSpotify}
|
||||
order={externalLinksOrder}
|
||||
spotify={spotify}
|
||||
/>
|
||||
)}
|
||||
{enabledItem.biography && (
|
||||
|
||||
@@ -601,6 +601,48 @@ export const ApplicationSettings = memo(() => {
|
||||
isHidden: !settings.externalLinks,
|
||||
title: t('setting.musicbrainz', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
defaultChecked={settings.spotify}
|
||||
onChange={(e) => {
|
||||
setSettings({
|
||||
general: {
|
||||
...settings,
|
||||
spotify: e.currentTarget.checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: t('setting.spotify', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: !settings.externalLinks,
|
||||
title: t('setting.spotify', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
defaultChecked={settings.nativeSpotify}
|
||||
onChange={(e) => {
|
||||
setSettings({
|
||||
general: {
|
||||
...settings,
|
||||
nativeSpotify: e.currentTarget.checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: t('setting.nativeSpotify', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: !settings.externalLinks || !settings.spotify,
|
||||
title: t('setting.nativeSpotify', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
|
||||
@@ -154,6 +154,8 @@ const ENV_SETTING_SPECS: EnvSettingSpec[] = [
|
||||
{ key: 'FS_GENERAL_LASTFM_API_KEY', path: ['general', 'lastfmApiKey'], type: 'string' },
|
||||
{ key: 'FS_GENERAL_LAST_FM', path: ['general', 'lastFM'], type: 'bool' },
|
||||
{ key: 'FS_GENERAL_MUSIC_BRAINZ', path: ['general', 'musicBrainz'], type: 'bool' },
|
||||
{ key: 'FS_GENERAL_SPOTIFY', path: ['general', 'spotify'], type: 'bool' },
|
||||
{ key: 'FS_GENERAL_SPOTIFY_NATIVE_APP', path: ['general', 'nativeSpotify'], type: 'bool' },
|
||||
{ key: 'FS_GENERAL_NATIVE_ASPECT_RATIO', path: ['general', 'nativeAspectRatio'], type: 'bool' },
|
||||
{
|
||||
key: 'FS_GENERAL_PLAYERBAR_OPEN_DRAWER',
|
||||
|
||||
@@ -477,6 +477,7 @@ export const GeneralSettingsSchema = z.object({
|
||||
lastfmApiKey: z.string(),
|
||||
musicBrainz: z.boolean(),
|
||||
nativeAspectRatio: z.boolean(),
|
||||
nativeSpotify: z.boolean(),
|
||||
passwordStore: z.string().optional(),
|
||||
pathReplace: z.string(),
|
||||
pathReplaceWith: z.string(),
|
||||
@@ -499,6 +500,7 @@ export const GeneralSettingsSchema = z.object({
|
||||
sidebarPlaylistSorting: z.boolean(),
|
||||
sideQueueType: SideQueueTypeSchema,
|
||||
skipButtons: SkipButtonsSchema,
|
||||
spotify: z.boolean(),
|
||||
theme: z.nativeEnum(AppTheme),
|
||||
themeDark: z.nativeEnum(AppTheme),
|
||||
themeLight: z.nativeEnum(AppTheme),
|
||||
@@ -1129,6 +1131,7 @@ const initialState: SettingsState = {
|
||||
lastfmApiKey: '',
|
||||
musicBrainz: true,
|
||||
nativeAspectRatio: false,
|
||||
nativeSpotify: false,
|
||||
passwordStore: undefined,
|
||||
pathReplace: '',
|
||||
pathReplaceWith: '',
|
||||
@@ -1161,6 +1164,7 @@ const initialState: SettingsState = {
|
||||
skipBackwardSeconds: 5,
|
||||
skipForwardSeconds: 10,
|
||||
},
|
||||
spotify: true,
|
||||
theme: AppTheme.DEFAULT_DARK,
|
||||
themeDark: AppTheme.DEFAULT_DARK,
|
||||
themeLight: AppTheme.DEFAULT_LIGHT,
|
||||
@@ -2549,6 +2553,8 @@ export const useExternalLinks = () =>
|
||||
externalLinks: state.general.externalLinks,
|
||||
lastFM: state.general.lastFM,
|
||||
musicBrainz: state.general.musicBrainz,
|
||||
nativeSpotify: state.general.nativeSpotify,
|
||||
spotify: state.general.spotify,
|
||||
}),
|
||||
shallow,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user