Compare commits

...

13 Commits

Author SHA1 Message Date
jeffvli 3675146f1f Fix opacity mask for unsynced lyrics container 2023-10-07 19:58:04 -07:00
jeffvli 946f4ff306 Bump to v0.4.1 2023-10-07 19:06:30 -07:00
jeffvli 277669c413 Fix album detail table customizations 2023-10-07 18:11:02 -07:00
jeffvli 49b6478b72 Fix table row actions button on album detail and play queue 2023-10-07 17:32:59 -07:00
jeffvli ca39409cc3 Respect order of set-queue function (fix race condition) 2023-10-07 16:46:23 -07:00
jeffvli cca6fa21db Adjust scrobble duration to check in ms 2023-10-05 22:11:48 -07:00
jeffvli 5e1059870c Fix second song on startup not playing 2023-10-05 21:54:11 -07:00
Kendall Garner 6bac172bbe fix scrobble durations (#269)
* fix scrobble durations

* Fix scrobble condition on last song in queue, normalize ms

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
2023-10-05 21:45:47 -07:00
Kendall Garner 118a9f73d1 fix unsynced lyrics (#279) 2023-10-04 22:02:42 -07:00
jeffvli c464be8cea Fix quit functionality (#184) 2023-09-27 02:37:03 -07:00
jeffvli 3bbe696f4c Update react-router and add useTransition support 2023-09-25 16:13:27 -07:00
jeffvli f7cacd2b73 Remove page fade in transition 2023-09-25 16:12:51 -07:00
jeffvli 62794623a3 Fix tracks list refresh on search 2023-09-25 15:57:48 -07:00
18 changed files with 183 additions and 101 deletions
+31 -31
View File
@@ -1,12 +1,12 @@
{
"name": "feishin",
"version": "0.4.0",
"version": "0.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "feishin",
"version": "0.4.0",
"version": "0.4.1",
"hasInstallScript": true,
"license": "GPL-3.0",
"dependencies": {
@@ -59,8 +59,8 @@
"react-i18next": "^11.16.7",
"react-icons": "^4.10.1",
"react-player": "^2.11.0",
"react-router": "^6.5.0",
"react-router-dom": "^6.5.0",
"react-router": "^6.16.0",
"react-router-dom": "^6.16.0",
"react-simple-img": "^3.0.0",
"react-virtualized-auto-sizer": "^1.0.17",
"react-window": "^1.8.9",
@@ -4336,11 +4336,11 @@
}
},
"node_modules/@remix-run/router": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.1.0.tgz",
"integrity": "sha512-rGl+jH/7x1KBCQScz9p54p0dtPLNeKGb3e0wD2H5/oZj41bwQUnXdzbj2TbUAFhvD7cp9EyEQA4dEgpUFa1O7Q==",
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.9.0.tgz",
"integrity": "sha512-bV63itrKBC0zdT27qYm6SDZHlkXwFL1xMBuhkn+X7l0+IIhNaH5wuuvZKp6eKhCD4KFhujhfhCT1YxXW6esUIA==",
"engines": {
"node": ">=14"
"node": ">=14.0.0"
}
},
"node_modules/@sindresorhus/is": {
@@ -17055,29 +17055,29 @@
}
},
"node_modules/react-router": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.5.0.tgz",
"integrity": "sha512-fqqUSU0NC0tSX0sZbyuxzuAzvGqbjiZItBQnyicWlOUmzhAU8YuLgRbaCL2hf3sJdtRy4LP/WBrWtARkMvdGPQ==",
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.16.0.tgz",
"integrity": "sha512-VT4Mmc4jj5YyjpOi5jOf0I+TYzGpvzERy4ckNSvSh2RArv8LLoCxlsZ2D+tc7zgjxcY34oTz2hZaeX5RVprKqA==",
"dependencies": {
"@remix-run/router": "1.1.0"
"@remix-run/router": "1.9.0"
},
"engines": {
"node": ">=14"
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.5.0.tgz",
"integrity": "sha512-/XzRc5fq80gW1ctiIGilyKFZC/j4kfe75uivMsTChFbkvrK4ZrF3P3cGIc1f/SSkQ4JiJozPrf+AwUHHWVehVg==",
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.16.0.tgz",
"integrity": "sha512-aTfBLv3mk/gaKLxgRDUPbPw+s4Y/O+ma3rEN1u8EgEpLpPe6gNjIsWt9rxushMHHMb7mSwxRGdGlGdvmFsyPIg==",
"dependencies": {
"@remix-run/router": "1.1.0",
"react-router": "6.5.0"
"@remix-run/router": "1.9.0",
"react-router": "6.16.0"
},
"engines": {
"node": ">=14"
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
@@ -24366,9 +24366,9 @@
}
},
"@remix-run/router": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.1.0.tgz",
"integrity": "sha512-rGl+jH/7x1KBCQScz9p54p0dtPLNeKGb3e0wD2H5/oZj41bwQUnXdzbj2TbUAFhvD7cp9EyEQA4dEgpUFa1O7Q=="
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.9.0.tgz",
"integrity": "sha512-bV63itrKBC0zdT27qYm6SDZHlkXwFL1xMBuhkn+X7l0+IIhNaH5wuuvZKp6eKhCD4KFhujhfhCT1YxXW6esUIA=="
},
"@sindresorhus/is": {
"version": "4.6.0",
@@ -33977,20 +33977,20 @@
}
},
"react-router": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.5.0.tgz",
"integrity": "sha512-fqqUSU0NC0tSX0sZbyuxzuAzvGqbjiZItBQnyicWlOUmzhAU8YuLgRbaCL2hf3sJdtRy4LP/WBrWtARkMvdGPQ==",
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.16.0.tgz",
"integrity": "sha512-VT4Mmc4jj5YyjpOi5jOf0I+TYzGpvzERy4ckNSvSh2RArv8LLoCxlsZ2D+tc7zgjxcY34oTz2hZaeX5RVprKqA==",
"requires": {
"@remix-run/router": "1.1.0"
"@remix-run/router": "1.9.0"
}
},
"react-router-dom": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.5.0.tgz",
"integrity": "sha512-/XzRc5fq80gW1ctiIGilyKFZC/j4kfe75uivMsTChFbkvrK4ZrF3P3cGIc1f/SSkQ4JiJozPrf+AwUHHWVehVg==",
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.16.0.tgz",
"integrity": "sha512-aTfBLv3mk/gaKLxgRDUPbPw+s4Y/O+ma3rEN1u8EgEpLpPe6gNjIsWt9rxushMHHMb7mSwxRGdGlGdvmFsyPIg==",
"requires": {
"@remix-run/router": "1.1.0",
"react-router": "6.5.0"
"@remix-run/router": "1.9.0",
"react-router": "6.16.0"
}
},
"react-shallow-renderer": {
+3 -3
View File
@@ -2,7 +2,7 @@
"name": "feishin",
"productName": "Feishin",
"description": "Feishin music server",
"version": "0.4.0",
"version": "0.4.1",
"scripts": {
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\" \"npm run build:remote\"",
"build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts",
@@ -305,8 +305,8 @@
"react-i18next": "^11.16.7",
"react-icons": "^4.10.1",
"react-player": "^2.11.0",
"react-router": "^6.5.0",
"react-router-dom": "^6.5.0",
"react-router": "^6.16.0",
"react-router-dom": "^6.16.0",
"react-simple-img": "^3.0.0",
"react-virtualized-auto-sizer": "^1.0.17",
"react-window": "^1.8.9",
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "feishin",
"version": "0.4.0",
"version": "0.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "feishin",
"version": "0.4.0",
"version": "0.4.1",
"hasInstallScript": true,
"license": "GPL-3.0",
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "feishin",
"version": "0.4.0",
"version": "0.4.1",
"description": "",
"main": "./dist/main/main.js",
"author": {
+5 -7
View File
@@ -112,18 +112,16 @@ ipcMain.on('player-set-queue', async (_event, data: PlayerData, pause?: boolean)
try {
if (data.queue.current) {
getMpvInstance()
await getMpvInstance()
?.load(data.queue.current.streamUrl, 'replace')
.then(() => {
// eslint-disable-next-line promise/always-return
if (data.queue.next) {
getMpvInstance()?.load(data.queue.next.streamUrl, 'append');
}
})
.catch((err) => {
console.log('MPV failed to load song', err);
getMpvInstance()?.play();
});
if (data.queue.next) {
await getMpvInstance()?.load(data.queue.next.streamUrl, 'append');
}
}
} catch (err) {
console.error(err);
+5
View File
@@ -259,6 +259,11 @@ const createWindow = async () => {
mainWindow?.close();
});
ipcMain.on('window-quit', () => {
mainWindow?.close();
app.exit();
});
ipcMain.on('app-restart', () => {
// Fix for .AppImage
if (process.env.APPIMAGE) {
+8
View File
@@ -3,16 +3,23 @@ import { ipcRenderer } from 'electron';
const exit = () => {
ipcRenderer.send('window-close');
};
const maximize = () => {
ipcRenderer.send('window-maximize');
};
const minimize = () => {
ipcRenderer.send('window-minimize');
};
const unmaximize = () => {
ipcRenderer.send('window-unmaximize');
};
const quit = () => {
ipcRenderer.send('window-quit');
};
const devtools = () => {
ipcRenderer.send('window-dev-tools');
};
@@ -22,5 +29,6 @@ export const browser = {
exit,
maximize,
minimize,
quit,
unmaximize,
};
@@ -934,7 +934,7 @@ const getLyrics = async (args: LyricsArgs): Promise<LyricsResponse> => {
}
if (res.body.Lyrics.length > 0 && res.body.Lyrics[0].Start === undefined) {
return res.body.Lyrics[0].Text;
return res.body.Lyrics.map((lyric) => lyric.Text).join('\n');
}
return res.body.Lyrics.map((lyric) => [lyric.Start! / 1e4, lyric.Text]);
@@ -87,6 +87,7 @@ export const GENRE_TABLE_COLUMNS = [
];
interface TableConfigDropdownProps {
// tableRef?: MutableRefObject<AgGridReactType<any> | null>;
type: TableType;
}
@@ -12,9 +12,9 @@ import { AlbumListSort, LibraryItem, QueueSong, SortOrder } from '/@/renderer/ap
import { Button, Popover } from '/@/renderer/components';
import { MemoizedSwiperGridCarousel } from '/@/renderer/components/grid-carousel';
import {
getColumnDefs,
TableConfigDropdown,
VirtualTable,
getColumnDefs,
} from '/@/renderer/components/virtual-table';
import { FullWidthDiscCell } from '/@/renderer/components/virtual-table/cells/full-width-disc-cell';
import { useCurrentSongRowStyles } from '/@/renderer/components/virtual-table/hooks/use-current-song-row-styles';
@@ -34,7 +34,11 @@ import { LibraryBackgroundOverlay } from '/@/renderer/features/shared/components
import { useContainerQuery } from '/@/renderer/hooks';
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer } from '/@/renderer/store';
import { usePlayButtonBehavior, useTableSettings } from '/@/renderer/store/settings.store';
import {
usePlayButtonBehavior,
useSettingsStoreActions,
useTableSettings,
} from '/@/renderer/store/settings.store';
import { Play } from '/@/renderer/types';
const isFullWidthRow = (node: RowNode) => {
@@ -65,16 +69,20 @@ export const AlbumDetailContent = ({ tableRef, background }: AlbumDetailContentP
const cq = useContainerQuery();
const handlePlayQueueAdd = usePlayQueueAdd();
const tableConfig = useTableSettings('albumDetail');
const { setTable } = useSettingsStoreActions();
const columnDefs = useMemo(() => getColumnDefs(tableConfig.columns), [tableConfig.columns]);
const getRowHeight = useCallback((params: RowHeightParams) => {
if (isFullWidthRow(params.node)) {
return 45;
}
const getRowHeight = useCallback(
(params: RowHeightParams) => {
if (isFullWidthRow(params.node)) {
return 45;
}
return 60;
}, []);
return tableConfig.rowHeight;
},
[tableConfig.rowHeight],
);
const songsRowData = useMemo(() => {
if (!detailQuery.data?.songs) {
@@ -216,7 +224,7 @@ export const AlbumDetailContent = ({ tableRef, background }: AlbumDetailContentP
});
};
const handleContextMenu = useHandleTableContextMenu(LibraryItem.SONG, SONG_CONTEXT_MENU_ITEMS);
const onCellContextMenu = useHandleTableContextMenu(LibraryItem.SONG, SONG_CONTEXT_MENU_ITEMS);
const handleRowDoubleClick = (e: RowDoubleClickedEvent<QueueSong>) => {
if (!e.data || e.node.isFullWidthCell()) return;
@@ -266,6 +274,32 @@ export const AlbumDetailContent = ({ tableRef, background }: AlbumDetailContentP
ALBUM_CONTEXT_MENU_ITEMS,
);
const onColumnMoved = useCallback(() => {
const { columnApi } = tableRef?.current || {};
const columnsOrder = columnApi?.getAllGridColumns();
if (!columnsOrder) return;
const columnsInSettings = tableConfig.columns;
const updatedColumns = [];
for (const column of columnsOrder) {
const columnInSettings = columnsInSettings.find(
(c) => c.column === column.getColDef().colId,
);
if (columnInSettings) {
updatedColumns.push({
...columnInSettings,
...(!tableConfig.autoFit && {
width: column.getActualWidth(),
}),
});
}
}
setTable('albumDetail', { ...tableConfig, columns: updatedColumns });
}, [setTable, tableConfig, tableRef]);
const { rowClassRules } = useCurrentSongRowStyles({ tableRef });
return (
@@ -352,6 +386,7 @@ export const AlbumDetailContent = ({ tableRef, background }: AlbumDetailContentP
)}
<Box style={{ minHeight: '300px' }}>
<VirtualTable
key={`table-${tableConfig.rowHeight}`}
ref={tableRef}
autoHeight
stickyHeader
@@ -360,6 +395,9 @@ export const AlbumDetailContent = ({ tableRef, background }: AlbumDetailContentP
suppressRowDrag
autoFitColumns={tableConfig.autoFit}
columnDefs={columnDefs}
context={{
onCellContextMenu,
}}
enableCellChangeFlash={false}
fullWidthCellRenderer={FullWidthDiscCell}
getRowHeight={getRowHeight}
@@ -374,7 +412,8 @@ export const AlbumDetailContent = ({ tableRef, background }: AlbumDetailContentP
rowClassRules={rowClassRules}
rowData={songsRowData}
rowSelection="multiple"
onCellContextMenu={handleContextMenu}
onCellContextMenu={onCellContextMenu}
onColumnMoved={onColumnMoved}
onRowDoubleClicked={handleRowDoubleClick}
/>
</Box>
@@ -18,6 +18,14 @@ const UnsynchronizedLyricsContainer = styled.div<{ $gap: number }>`
overflow: scroll;
transform: translateY(-2rem);
-webkit-mask-image: linear-gradient(
180deg,
transparent 5%,
rgb(0 0 0 / 100%) 20%,
rgb(0 0 0 / 100%) 85%,
transparent 95%
);
mask-image: linear-gradient(
180deg,
transparent 5%,
@@ -206,7 +206,7 @@ export const PlayQueue = forwardRef(({ type }: QueueProps, ref: Ref<any>) => {
}
}, [currentSong, previousSong, tableConfig.followCurrentSong]);
const handleContextMenu = useHandleTableContextMenu(LibraryItem.SONG, QUEUE_CONTEXT_MENU_ITEMS);
const onCellContextMenu = useHandleTableContextMenu(LibraryItem.SONG, QUEUE_CONTEXT_MENU_ITEMS);
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
@@ -218,6 +218,9 @@ export const PlayQueue = forwardRef(({ type }: QueueProps, ref: Ref<any>) => {
rowDragMultiRow
autoFitColumns={tableConfig.autoFit}
columnDefs={columnDefs}
context={{
onCellContextMenu,
}}
deselectOnClickOutside={type === 'fullScreen'}
getRowId={(data) => data.data.uniqueId}
rowBuffer={50}
@@ -225,7 +228,7 @@ export const PlayQueue = forwardRef(({ type }: QueueProps, ref: Ref<any>) => {
rowData={queue}
rowHeight={tableConfig.rowHeight || 40}
suppressCellFocus={type === 'fullScreen'}
onCellContextMenu={handleContextMenu}
onCellContextMenu={onCellContextMenu}
onCellDoubleClicked={handleDoubleClick}
onColumnMoved={handleColumnChange}
onColumnResized={debouncedColumnChange}
@@ -34,20 +34,21 @@ Progress Events (Jellyfin only):
*/
const checkScrobbleConditions = (args: {
scrobbleAtDuration: number;
scrobbleAtDurationMs: number;
scrobbleAtPercentage: number;
songCompletedDuration: number;
songDuration: number;
songCompletedDurationMs: number;
songDurationMs: number;
}) => {
const { scrobbleAtDuration, scrobbleAtPercentage, songCompletedDuration, songDuration } = args;
const percentageOfSongCompleted = songDuration
? (songCompletedDuration / songDuration) * 100
const { scrobbleAtDurationMs, scrobbleAtPercentage, songCompletedDurationMs, songDurationMs } =
args;
const percentageOfSongCompleted = songDurationMs
? (songCompletedDurationMs / songDurationMs) * 100
: 0;
return (
percentageOfSongCompleted >= scrobbleAtPercentage ||
songCompletedDuration >= scrobbleAtDuration
);
const shouldScrobbleBasedOnPercetange = percentageOfSongCompleted >= scrobbleAtPercentage;
const shouldScrobbleBasedOnDuration = songCompletedDurationMs >= scrobbleAtDurationMs;
return shouldScrobbleBasedOnPercetange || shouldScrobbleBasedOnDuration;
};
export const useScrobble = () => {
@@ -97,15 +98,15 @@ export const useScrobble = () => {
// const currentSong = current[0] as QueueSong | undefined;
const previousSong = previous[0] as QueueSong;
const previousSongTime = previous[1] as number;
const previousSongTimeSec = previous[1] as number;
// Send completion scrobble when song changes and a previous song exists
if (previousSong?.id) {
const shouldSubmitScrobble = checkScrobbleConditions({
scrobbleAtDuration: scrobbleSettings?.scrobbleAtDuration,
scrobbleAtDurationMs: (scrobbleSettings?.scrobbleAtDuration ?? 0) * 1000,
scrobbleAtPercentage: scrobbleSettings?.scrobbleAtPercentage,
songCompletedDuration: previousSongTime,
songDuration: previousSong.duration,
songCompletedDurationMs: previousSongTimeSec * 1000,
songDurationMs: previousSong.duration,
});
if (
@@ -114,7 +115,7 @@ export const useScrobble = () => {
) {
const position =
previousSong?.serverType === ServerType.JELLYFIN
? previousSongTime * 1e7
? previousSongTimeSec * 1e7
: undefined;
sendScrobble.mutate({
@@ -168,7 +169,10 @@ export const useScrobble = () => {
);
const handleScrobbleFromStatusChange = useCallback(
(status: PlayerStatus | undefined) => {
(
current: (PlayerStatus | number | undefined)[],
previous: (PlayerStatus | number | undefined)[],
) => {
if (!isScrobbleEnabled) return;
const currentSong = usePlayerStore.getState().current.song;
@@ -180,8 +184,11 @@ export const useScrobble = () => {
? usePlayerStore.getState().current.time * 1e7
: undefined;
const currentStatus = current[0] as PlayerStatus;
const currentTimeSec = current[1] as number;
// Whenever the player is restarted, send a 'start' scrobble
if (status === PlayerStatus.PLAYING) {
if (currentStatus === PlayerStatus.PLAYING) {
sendScrobble.mutate({
query: {
event: 'unpause',
@@ -194,7 +201,7 @@ export const useScrobble = () => {
if (currentSong?.serverType === ServerType.JELLYFIN) {
progressIntervalId.current = setInterval(() => {
const currentTime = usePlayerStore.getState().current.time;
const currentTime = currentTimeSec;
handleScrobbleFromSeek(currentTime);
}, 10000);
}
@@ -215,12 +222,17 @@ export const useScrobble = () => {
clearInterval(progressIntervalId.current as ReturnType<typeof setInterval>);
}
} else {
const isLastTrackInQueue = usePlayerStore.getState().actions.checkIsLastTrack();
const previousTimeSec = previous[1] as number;
// If not already scrobbled, send a 'submission' scrobble if conditions are met
const shouldSubmitScrobble = checkScrobbleConditions({
scrobbleAtDuration: scrobbleSettings?.scrobbleAtDuration,
scrobbleAtDurationMs: (scrobbleSettings?.scrobbleAtDuration ?? 0) * 1000,
scrobbleAtPercentage: scrobbleSettings?.scrobbleAtPercentage,
songCompletedDuration: usePlayerStore.getState().current.time,
songDuration: currentSong.duration,
// If scrobbling the last song in the queue, use the previous time instead of the current time since otherwise time value will be 0
songCompletedDurationMs:
(isLastTrackInQueue ? previousTimeSec : currentTimeSec) * 1000,
songDurationMs: currentSong.duration,
});
if (!isCurrentSongScrobbled && shouldSubmitScrobble) {
@@ -261,10 +273,10 @@ export const useScrobble = () => {
currentSong?.serverType === ServerType.JELLYFIN ? currentTime * 1e7 : undefined;
const shouldSubmitScrobble = checkScrobbleConditions({
scrobbleAtDuration: scrobbleSettings?.scrobbleAtDuration,
scrobbleAtDurationMs: (scrobbleSettings?.scrobbleAtDuration ?? 0) * 1000,
scrobbleAtPercentage: scrobbleSettings?.scrobbleAtPercentage,
songCompletedDuration: currentTime,
songDuration: currentSong.duration,
songCompletedDurationMs: currentTime,
songDurationMs: currentSong.duration,
});
if (!isCurrentSongScrobbled && shouldSubmitScrobble) {
@@ -313,8 +325,11 @@ export const useScrobble = () => {
);
const unsubStatusChange = usePlayerStore.subscribe(
(state) => state.current.status,
(state) => [state.current.status, state.current.time],
handleScrobbleFromStatusChange,
{
equalityFn: (a, b) => (a[0] as PlayerStatus) === (b[0] as PlayerStatus),
},
);
return () => {
@@ -7,23 +7,23 @@ interface AnimatedPageProps {
children: ReactNode;
}
const variants = {
animate: { opacity: 1 },
exit: { opacity: 0 },
initial: { opacity: 0 },
};
// const variants = {
// animate: { opacity: 1 },
// exit: { opacity: 0 },
// initial: { opacity: 0 },
// };
export const AnimatedPage = forwardRef(
({ children }: AnimatedPageProps, ref: Ref<HTMLDivElement>) => {
return (
<motion.main
ref={ref}
animate="animate"
// animate="animate"
className={styles.animatedPage}
exit="exit"
initial="initial"
transition={{ duration: 0.3, ease: 'easeIn' }}
variants={variants}
// exit="exit"
// initial="initial"
// transition={{ duration: 0.3, ease: 'easeIn' }}
// variants={variants}
>
{children}
</motion.main>
@@ -29,7 +29,7 @@ export const SongListHeader = ({ gridRef, title, itemCount, tableRef }: SongList
const { display, filter } = useListStoreByKey({ key: pageKey });
const cq = useContainerQuery();
const { handleRefreshTable } = useListFilterRefresh({
const { handleRefreshTable, handleRefreshGrid } = useListFilterRefresh({
itemType: LibraryItem.SONG,
server,
});
@@ -51,7 +51,7 @@ export const SongListHeader = ({ gridRef, title, itemCount, tableRef }: SongList
handleRefreshTable(tableRef, filterWithCustom);
setTablePagination({ data: { currentPage: 0 }, key: pageKey });
} else {
// handleRefreshGrid(gridRef, filterWithCustom);
handleRefreshGrid(gridRef, filterWithCustom);
}
}, 500);
@@ -91,7 +91,7 @@ export const AppMenu = () => {
};
const handleQuit = () => {
browser?.exit();
browser?.quit();
};
return (
+5 -6
View File
@@ -1,10 +1,8 @@
import isElectron from 'is-electron';
import { lazy, Suspense } from 'react';
import {
Route,
createRoutesFromElements,
RouterProvider,
createBrowserRouter,
createHashRouter,
} from 'react-router-dom';
import { AppRoute } from './routes';
@@ -68,10 +66,8 @@ const RouteErrorBoundary = lazy(
() => import('/@/renderer/features/action-required/components/route-error-boundary'),
);
const dynamicRouter = isElectron() ? createHashRouter : createBrowserRouter;
export const AppRouter = () => {
const router = dynamicRouter(
const router = createHashRouter(
createRoutesFromElements(
<>
<Route element={<TitlebarOutlet />}>
@@ -198,7 +194,10 @@ export const AppRouter = () => {
return (
<Suspense fallback={<></>}>
<RouterProvider router={router} />
<RouterProvider
future={{ v7_startTransition: true }}
router={router}
/>
</Suspense>
);
};
+6
View File
@@ -196,6 +196,7 @@ export interface SettingsSlice extends SettingsState {
reset: () => void;
setSettings: (data: Partial<SettingsState>) => void;
setSidebarItems: (items: SidebarItemType[]) => void;
setTable: (type: TableType, data: DataTableProps) => void;
};
}
@@ -493,6 +494,11 @@ export const useSettingsStore = create<SettingsSlice>()(
state.general.sidebarItems = items;
});
},
setTable: (type: TableType, data: DataTableProps) => {
set((state) => {
state.tables[type] = data;
});
},
},
...initialState,
})),