add more emphasis to current song (#283)

* add more emphasis to current song

* add css indicator (rivolumelineup)

* don't use absolute position, support album track number

* Respect order of set-queue function (fix race condition)

* Fix table row actions button on album detail and play queue

* Fix album detail table customizations

* Bump to v0.4.1

* Fix opacity mask for unsynced lyrics container

* Separate sidebar icons to new component

- Fixes react render issue

* Add app focus hook

* Remove css play image

* Add player status as cell refresh condition for queue

* Add current song images

* Add current song styles for all song tables

* Revert row index cell width

* Remove animated svg on browser

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
Co-authored-by: Jeff <42182408+jeffvli@users.noreply.github.com>
This commit is contained in:
Kendall Garner 2023-10-19 01:32:11 +00:00 committed by GitHub
parent 9964f95d5d
commit 8a53fab751
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 248 additions and 16 deletions

View file

@ -0,0 +1,19 @@
import type { ICellRendererParams } from '@ag-grid-community/core';
import { Text } from '/@/renderer/components/text';
import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell';
export const RowIndexCell = ({ value }: ICellRendererParams) => {
return (
<CellContainer $position="right">
<Text
$secondary
align="right"
className="current-song-child current-song-index"
overflow="hidden"
size="md"
>
{value}
</Text>
</CellContainer>
);
};

View file

@ -1,7 +1,8 @@
import { RowClassRules, RowNode } from '@ag-grid-community/core';
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
import { MutableRefObject, useEffect, useMemo } from 'react';
import { MutableRefObject, useEffect, useMemo, useRef } from 'react';
import { Song } from '/@/renderer/api/types';
import { useAppFocus } from '/@/renderer/hooks';
import { useCurrentSong, usePlayerStore } from '/@/renderer/store';
interface UseCurrentSongRowStylesProps {
@ -10,17 +11,43 @@ interface UseCurrentSongRowStylesProps {
export const useCurrentSongRowStyles = ({ tableRef }: UseCurrentSongRowStylesProps) => {
const currentSong = useCurrentSong();
const isFocused = useAppFocus();
const isFocusedRef = useRef<boolean>(isFocused);
useEffect(() => {
// Redraw rows if the app focus changes
if (isFocusedRef.current !== isFocused) {
isFocusedRef.current = isFocused;
if (tableRef?.current) {
const { api, columnApi } = tableRef?.current || {};
if (api == null || columnApi == null) {
return;
}
const currentNode = currentSong?.id ? api.getRowNode(currentSong.id) : undefined;
const rowNodes = [currentNode].filter((e) => e !== undefined) as RowNode<any>[];
if (rowNodes) {
api.redrawRows({ rowNodes });
}
}
}
}, [currentSong?.id, isFocused, tableRef]);
const rowClassRules = useMemo<RowClassRules<Song> | undefined>(() => {
return {
'current-song': (params) => {
return params?.data?.id === currentSong?.id;
return (
params?.data?.id === currentSong?.id &&
params?.data?.albumId === currentSong?.albumId
);
},
};
}, [currentSong?.id]);
}, [currentSong?.albumId, currentSong?.id]);
// Redraw song rows when current song changes
useEffect(() => {
// Redraw song rows when current song changes
const unsubSongChange = usePlayerStore.subscribe(
(state) => state.current.song,
(song, previousSong) => {
@ -46,8 +73,35 @@ export const useCurrentSongRowStyles = ({ tableRef }: UseCurrentSongRowStylesPro
{ equalityFn: (a, b) => a?.id === b?.id },
);
// Redraw song rows when the status changes
const unsubStatusChange = usePlayerStore.subscribe(
(state) => state.current.song,
(song, previousSong) => {
if (tableRef?.current) {
const { api, columnApi } = tableRef?.current || {};
if (api == null || columnApi == null) {
return;
}
const currentNode = song?.id ? api.getRowNode(song.id) : undefined;
const previousNode = previousSong?.id
? api.getRowNode(previousSong?.id)
: undefined;
const rowNodes = [currentNode, previousNode].filter(
(e) => e !== undefined,
) as RowNode<any>[];
api.redrawRows({ rowNodes });
}
},
{ equalityFn: (a, b) => a?.id === b?.id },
);
return () => {
unsubSongChange();
unsubStatusChange();
};
}, [tableRef]);

View file

@ -19,6 +19,7 @@ import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import formatDuration from 'format-duration';
import { AnimatePresence } from 'framer-motion';
import isElectron from 'is-electron';
import { generatePath } from 'react-router';
import styled from 'styled-components';
import { AlbumArtistCell } from '/@/renderer/components/virtual-table/cells/album-artist-cell';
@ -29,7 +30,11 @@ import { GenreCell } from '/@/renderer/components/virtual-table/cells/genre-cell
import { GenericTableHeader } from '/@/renderer/components/virtual-table/headers/generic-table-header';
import { AppRoute } from '/@/renderer/router/routes';
import { PersistedTableColumn } from '/@/renderer/store/settings.store';
import { TableColumn, TablePagination as TablePaginationType } from '/@/renderer/types';
import {
PlayerStatus,
TableColumn,
TablePagination as TablePaginationType,
} from '/@/renderer/types';
import { FavoriteCell } from '/@/renderer/components/virtual-table/cells/favorite-cell';
import { RatingCell } from '/@/renderer/components/virtual-table/cells/rating-cell';
import { TablePagination } from '/@/renderer/components/virtual-table/table-pagination';
@ -37,6 +42,7 @@ import { ActionsCell } from '/@/renderer/components/virtual-table/cells/actions-
import { TitleCell } from '/@/renderer/components/virtual-table/cells/title-cell';
import { useFixedTableHeader } from '/@/renderer/components/virtual-table/hooks/use-fixed-table-header';
import { NoteCell } from '/@/renderer/components/virtual-table/cells/note-cell';
import { RowIndexCell } from '/@/renderer/components/virtual-table/cells/row-index-cell';
export * from './table-config-dropdown';
export * from './table-pagination';
@ -260,7 +266,16 @@ const tableColumns: { [key: string]: ColDef } = {
width: 80,
},
rowIndex: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'right' }),
cellClass: 'row-index',
cellClassRules: {
focused: (params) => {
return isElectron() && params.context?.isFocused;
},
playing: (params) => {
return params.context?.status === PlayerStatus.PLAYING;
},
},
cellRenderer: RowIndexCell,
colId: TableColumn.ROW_INDEX,
headerComponent: (params: IHeaderParams) =>
GenericTableHeader(params, { position: 'right', preset: 'rowIndex' }),
@ -311,7 +326,29 @@ const tableColumns: { [key: string]: ColDef } = {
width: 250,
},
trackNumber: {
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }),
cellClass: 'track-number',
cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'right' }),
colId: TableColumn.TRACK_NUMBER,
field: 'trackNumber',
headerComponent: (params: IHeaderParams) =>
GenericTableHeader(params, { position: 'center' }),
headerName: 'Track',
suppressSizeToFit: true,
valueGetter: (params: ValueGetterParams) =>
params.data ? params.data.trackNumber : undefined,
width: 80,
},
trackNumberDetail: {
cellClass: 'row-index',
cellClassRules: {
focused: (params) => {
return isElectron() && params.context?.isFocused;
},
playing: (params) => {
return params.context?.status === PlayerStatus.PLAYING;
},
},
cellRenderer: RowIndexCell,
colId: TableColumn.TRACK_NUMBER,
field: 'trackNumber',
headerComponent: (params: IHeaderParams) =>
@ -354,10 +391,19 @@ export const getColumnDef = (column: TableColumn) => {
return tableColumns[column as keyof typeof tableColumns];
};
export const getColumnDefs = (columns: PersistedTableColumn[], useWidth?: boolean) => {
export const getColumnDefs = (
columns: PersistedTableColumn[],
useWidth?: boolean,
type?: 'albumDetail',
) => {
const columnDefs: ColDef[] = [];
for (const column of columns) {
const presetColumn = tableColumns[column.column as keyof typeof tableColumns];
let presetColumn = tableColumns[column.column as keyof typeof tableColumns];
if (type === 'albumDetail' && column.column === TableColumn.TRACK_NUMBER) {
presetColumn = tableColumns['trackNumberDetail' as keyof typeof tableColumns];
}
if (presetColumn) {
columnDefs.push({
...presetColumn,