Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"build": {
"appId": "nuclear",
"productName": "nuclear",
"artifactName": "${productName}-v${env.VERSION}-${arch}.${ext}",
"artifactName": "${productName}-v${version}-${arch}.${ext}",
"directories": {
"output": "release"
},
Expand Down Expand Up @@ -213,4 +213,4 @@
"dependencies": {
"sqlite3": "^5.1.6"
}
}
}
Binary file added packages/app/app/.DS_Store
Binary file not shown.
Empty file.
14 changes: 14 additions & 0 deletions packages/app/app/containers/AudiobookContainer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

class AudiobookContainer extends React.Component {
render() {
return (
<div className='audiobook-container'>
<h1>Audiobooks</h1>
{/* your audiobook UI goes here */}
</div>
);
}
}

export default AudiobookContainer;
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,11 @@ exports[`Dashboard container should display top tracks after going to top tracks
>
Genres
</a>
<a
class="item"
>
Audiobooks
</a>
</div>
<div
class="ui segment active tab popular_tracks_tab"
Expand Down
3 changes: 3 additions & 0 deletions packages/app/app/containers/MainContentContainer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import VisualizerNode from '../VisualizerContainer/VisualizerNode';
import DeezerPlaylistAdapter from '../DeezerPlaylistAdapter';
import { ListeningHistoryContainer } from '../ListeningHistoryContainer';
import { SpotifyPlaylistAdapter } from '../SpotifyPlaylistAdapter';
import AudiobookContainer from '../AudiobookContainer';


class MainContentContainer extends React.Component {
componentDidMount() {
Expand Down Expand Up @@ -60,6 +62,7 @@ class MainContentContainer extends React.Component {
<Route path='/visualizer' component={VisualizerNode} />
<Route path='/library' component={LibraryViewContainer} />
<Route path='/listening-history' component={ListeningHistoryContainer} />
<Route path='/audiobooks' component={AudiobookContainer} />
</Switch>
</MainLayout>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export default [
{ name: 'settings', path: 'settings', icon: 'cog' },
{ name: 'equalizer', path: 'equalizer', icon: 'align right' },
{ name: 'visualizer', path: 'visualizer', icon: 'tint' },
{ name: 'listening-history', path: 'listening-history', icon: 'history' }
{ name: 'listening-history', path: 'listening-history', icon: 'history' },
{ name: 'Audiobooks', path: 'audiobooks', icon: 'flask'}
]
}, {
name: 'collection',
Expand Down
204 changes: 130 additions & 74 deletions packages/app/app/containers/TrackTableContainer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import { Icon } from 'semantic-ui-react';
Expand All @@ -9,6 +9,7 @@ import { GridTrackTable, areTracksEqualByName } from '@nuclear/ui';
import { TrackTableProps } from '@nuclear/ui/lib/components/TrackTable';
import { TrackTableSettings } from '@nuclear/ui/lib/components/TrackTable/types';
import { Track } from '@nuclear/ui/lib/types';
import { Column } from 'react-table';

import { playlistsSelectors } from '../../selectors/playlists';
import * as downloadsActions from '../../actions/downloads';
Expand All @@ -29,7 +30,7 @@ export type TrackTableContainerProps<T extends Track> = TrackTableSettings & {
displayAddToFavorites?: boolean;
};

function TrackTableContainer<T extends Track> ({
function TrackTableContainer<T extends Track>({
tracks,
onDelete,
onReorder,
Expand All @@ -44,71 +45,93 @@ function TrackTableContainer<T extends Track> ({
const playlists = useSelector(playlistsSelectors.localPlaylists);
const favoriteTracks: Track[] = useSelector(favoritesSelectors.tracks);

const [hoveredTrack, setHoveredTrack] = useState<Track | null>(null);
const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 });

useEffect(() => {
dispatch(favoritesActions.readFavorites());
}, [dispatch]);

const isTrackFavorite = (track: Track) => !_.isNil(favoriteTracks.find(t => areTracksEqualByName(t, track)));
const isTrackFavorite = (track: Track) =>
!_.isNil(favoriteTracks.find(t => areTracksEqualByName(t, track)));

const onAddToQueue = useCallback((track: Track) => {
dispatch(queueActions.addToQueue(queueActions.toQueueItem(track)));
}, [dispatch]);
const onAddToQueue = useCallback(
(track: Track) => dispatch(queueActions.addToQueue(queueActions.toQueueItem(track))),
[dispatch]
);

const onPlayNow = useCallback((track: Track) => {
dispatch(queueActions.playTrack(null, queueActions.toQueueItem(track)));
}, [dispatch]);
const onPlayNow = useCallback(
(track: Track) => dispatch(queueActions.playTrack(null, queueActions.toQueueItem(track))),
[dispatch]
);

const onPlayNext = useCallback((track: Track) => {
dispatch(queueActions.playNext(queueActions.toQueueItem(track)));
}, [dispatch]);
const onPlayNext = useCallback(
(track: Track) => dispatch(queueActions.playNext(queueActions.toQueueItem(track))),
[dispatch]
);

const onPlayAll = useCallback((tracks: Track[]) => {
dispatch(queueActions.clearQueue());
dispatch(queueActions.addPlaylistTracksToQueue(tracks));
dispatch(queueActions.selectSong(0));
dispatch(playerActions.startPlayback(false));
}, [dispatch]);
const onPlayAll = useCallback(
(tracksArr: Track[]) => {
dispatch(queueActions.clearQueue());
dispatch(queueActions.addPlaylistTracksToQueue(tracksArr));
dispatch(queueActions.selectSong(0));
dispatch(playerActions.startPlayback(false));
},
[dispatch]
);

const onAddToFavorites = useCallback((track: Track) => {
dispatch(favoritesActions.addFavoriteTrack(track));
}, [dispatch]);
const onAddToFavorites = useCallback(
(track: Track) => dispatch(favoritesActions.addFavoriteTrack(track)),
[dispatch]
);

const onRemoveFromFavorites = useCallback((track: Track) => {
dispatch(favoritesActions.removeFavoriteTrack(track));
}, [dispatch]);
const onRemoveFromFavorites = useCallback(
(track: Track) => dispatch(favoritesActions.removeFavoriteTrack(track)),
[dispatch]
);

const onAddToDownloads = useCallback((track: Track) => {
dispatch(downloadsActions.addToDownloads(null, track));
}, [dispatch]);
const onAddToDownloads = useCallback(
(track: Track) => dispatch(downloadsActions.addToDownloads(null, track)),
[dispatch]
);

const onAddToPlaylist = useCallback((track: Track, playlist: Playlist ) => {
const clonedTrack = {...safeAddUuid(track)};
const foundPlaylist = playlists.data?.find(p => p.name === playlist.name);
const newPlaylist = {
...foundPlaylist,
tracks: [
...foundPlaylist.tracks,
clonedTrack
]
};
dispatch(playlistActions.updatePlaylist(newPlaylist));
}, [dispatch, playlists]);
const onAddToPlaylist = useCallback(
(track: Track, playlist: Playlist) => {
const clonedTrack = { ...safeAddUuid(track) };
const foundPlaylist = playlists.data?.find(p => p.name === playlist.name);
if (!foundPlaylist) {
return;
}
const newPlaylist = {
...foundPlaylist,
tracks: [...foundPlaylist.tracks, clonedTrack]
};
dispatch(playlistActions.updatePlaylist(newPlaylist));
},
[dispatch, playlists]
);

const onCreatePlaylist = useCallback(
(track: Track, { name }: { name: string } ) => {
const clonedTrack = {...safeAddUuid(track)};
if (clonedTrack.artist.name) {
_.set(clonedTrack, 'artist', clonedTrack.artist.name);
(track: Track, { name }: { name: string }) => {
const clonedTrack = { ...safeAddUuid(track) };
if ((clonedTrack as any).artist?.name) {
_.set(clonedTrack, 'artist', (clonedTrack as any).artist.name);
}
dispatch(playlistActions.addPlaylist([clonedTrack], name));
},
[dispatch]
);

const onDragEnd = useCallback<TrackTableProps<Track>['onDragEnd']>((result) => {
const { source, destination } = result;
onReorder(source.index, destination.index);
}, [onReorder]);
const onDragEnd = useCallback<TrackTableProps<Track>['onDragEnd']>(
result => {
if (!onReorder) {
return;
}
const { source, destination } = result;
onReorder(source.index, destination.index);
},
[onReorder]
);

const popupTranstation = useTranslation('track-popup').t;
const popupStrings = {
Expand Down Expand Up @@ -138,33 +161,66 @@ function TrackTableContainer<T extends Track> ({
filterInputPlaceholder: trackTableTranslation('filter-input-placeholder')
};

return <TrackTableComponent
{...settings}
tracks={tracks}
positionHeader={<Icon name='hashtag' />}
thumbnailHeader={<Icon name='image' />}
artistHeader={t('artist')}
titleHeader={t('title')}
albumHeader={t('album')}
durationHeader={t('duration')}
strings={trackTableStrings}
playlists={playlists.data}
customColumns={customColumns}
onAddToQueue={onAddToQueue}
onPlay={onPlayNow}
onPlayNext={onPlayNext}
onPlayAll={onPlayAll}
onAddToFavorites={Boolean(displayAddToFavorites) && onAddToFavorites}
onRemoveFromFavorites={onRemoveFromFavorites}
onAddToDownloads={Boolean(displayAddToDownloads) && onAddToDownloads}
onAddToPlaylist={onAddToPlaylist}
onCreatePlaylist={onCreatePlaylist}
onDelete={onDelete}
onDragEnd={Boolean(onReorder) && onDragEnd}
popupActionStrings={popupStrings}

isTrackFavorite={isTrackFavorite}
/>;
const enhancedCustomColumns: Column<T>[] = [
...(customColumns || []) as Column<T>[],
{
id: 'hoverOverlay',
Header: '',
Cell: ({ row }: { row: any }) => {
const track = row.original as T;
return (
<div
onMouseEnter={(e: React.MouseEvent) => {
setHoveredTrack(track as unknown as Track);
setCursorPos({ x: e.pageX, y: e.pageY });
}}
onMouseMove={(e: React.MouseEvent) => {
setCursorPos({ x: e.pageX, y: e.pageY });
}}
onMouseLeave={() => setHoveredTrack(null)}
style={{ cursor: 'pointer' }}
>
<div>{(track as unknown as Track).title}</div>
{/* artist might be an object; use optional chaining */}
{((track as any).artist && (track as any).artist.name) && (
<div style={{ fontSize: '0.85em', color: '#666' }}>
{(track as any).artist.name}
</div>
)}
</div>
);
}
}
];

return (
<TrackTableComponent<T>
{...settings}
tracks={tracks}
positionHeader={<Icon name='hashtag' />}
thumbnailHeader={<Icon name='image' />}
artistHeader={t('artist')}
titleHeader={t('title')}
albumHeader={t('album')}
durationHeader={t('duration')}
strings={trackTableStrings}
playlists={playlists.data}
customColumns={enhancedCustomColumns}
onAddToQueue={onAddToQueue}
onPlay={onPlayNow}
onPlayNext={onPlayNext}
onPlayAll={onPlayAll}
onAddToFavorites={displayAddToFavorites ? onAddToFavorites : undefined}
onRemoveFromFavorites={onRemoveFromFavorites}
onAddToDownloads={displayAddToDownloads ? onAddToDownloads : undefined}
onAddToPlaylist={onAddToPlaylist}
onCreatePlaylist={onCreatePlaylist}
onDelete={onDelete}
onDragEnd={onReorder ? onDragEnd : undefined}
popupActionStrings={popupStrings}
isTrackFavorite={isTrackFavorite}
/>
);
}

export default TrackTableContainer;
Loading