Skip to content

Commit 603373c

Browse files
committed
Many improvements
mostly stability, bug fixes, and performance - [X] A versioning system, linked with `send-beta-build.yml` and the `links.json` but different file, supporting both git commits (for beta/dev stuff) and github latest releases for normal people use - maybe this isn't needed (note to self, read *all* of the docs first): <https://vite-pwa-org.netlify.app/frameworks/react.html#react> - to migrate?: <https://vite-pwa-org.netlify.app/guide/unregister-service-worker.html#custom-selfdestroying-service-worker> - shouldn't clear cache handle this? ¯\_(ツ)_/¯ - [X] Make loading of songs, playlists, etc on load more staggered so that page doesn't freeze - Added staggered loading with configurable batch sizes - [X] Add more to about menu - [X] Loading a custom iconset tanks performance - Stabilized loadIcon reference with useRef pattern to prevent re-creation - Added cache key tracking to skip redundant fetches when icon set changes - Wrapped Icon component with React.memo to prevent unnecessary re-renders - Removed state updates from inside loadIcon callback - [X] Gapless and crossfade are just broken, they just are - Fixed race condition: added crossfadeInitiatedRef guard to prevent multiple triggers from timeupdate - Fixed event listener cleanup: track and remove ended handler on cancelCrossfade - Fixed handleEnded during crossfade: properly defer to crossfade manager - Removed duplicate ended listener from nextAudio element - Fixed memory leak: revoke blob URLs after crossfade swap - Widened gapless trigger threshold from 50ms to 150ms with setTimeout for precision - Added AudioContext resume check before starting crossfade - Use crossfadeManager.getActiveElement() to determine current element after swap - [X] Load all cover art more lazily and delayed for same reason as above - Added loading="lazy" and decoding="async" to album art images - [X] fix drag and drop of songs not triggering sometimes - [X] Fix share_target so that it supports both importing songs if music files supported by filePickerHelper (as a "polyfill" for file_handlers) and searching/sharing songs if text - Share target already supports both files and text/search queries - [X] Fix sharing so that it just doesn't copy URL - Removed URL from share data for both songs and playlists - [X] Make dragging songs not select song details in song list but also make sure we can select that stuff (prevent conflicts) - [X] Slide out settings menu on close - [X] Prevent/recover from Failed to play song so that HTMLPlayer and system don't get unsynced - (`The play() request was interrupted by a new load request. https://goo.gl/LdLk22`) - [X] fix the UI of the 'Repeat Once' button - [X] Can't move playlist to root unless I have another playlist in root - [X] Center `Empty folder...` - [X] Show SYLT first if both USLT and SYLT are available - [X] fix visualizer missing left padding - [X] prevent esc from closing lyrics overlay - [X] In Home, show `1 Song` rather than `1 Songs` - [X] On mobile, the Persistent Dropdowns are loaded inside the top bar, when they should be overlays
1 parent 29cee15 commit 603373c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1825
-403
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,5 @@ dist
137137

138138
.DS_Store
139139
beta/
140-
.vscode/
140+
.vscode/
141+
dev-dist/

BUGS.md

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,68 +4,29 @@
44
- [ ] Compact mode
55
- [ ] Show Lyrics
66
- [ ] Show Album Art
7-
- [ ] Toggle Lyrics
8-
- [ ] Toggle Visualizer
9-
10-
- [ ] Show SYLT first if both USLT and SYLT are available
11-
12-
- [ ] fix visualizer missing left padding
13-
14-
- [ ] prevent esc from closing lyrics overlay
7+
- [X] Toggle Lyrics
8+
- [X] Toggle Visualizer
159

1610
- [ ] Lessen delays while reordering
1711

18-
- [ ] Slide out settings menu on close
19-
20-
- [ ] Prevent/recover from Failed to play song so that HTMLPlayer and system don't get unsynced
21-
- (`The play() request was interrupted by a new load request. https://goo.gl/LdLk22`)
22-
23-
- [ ] Make loading of songs, playlists, etc on load more staggered so that page doesn't freeze
24-
25-
- [ ] Load all cover art more lazily and delayed for same reason as above
26-
2712
- [ ] Use List Virtualization for song list so that having larger libraries doesn't cause lag on all songs
28-
- in short, loading only visible songs and not showing rest, but smartly loading the top and botton (for ex. predictively loading on scrolling)
13+
- in short, loading only visible songs and not showing rest, but smartly loading the top and bottom (for ex. predictively loading on scrolling)
2914

3015
- [ ] Allow drag and drop to playlist from Home view
3116

32-
- [ ] In Home, show `1 Song` rather than `1 Songs`
33-
3417
- [ ] Fix Help menu
35-
- On Home menu, when pressing Help, use different help guide
36-
- Disable keyboard shortcuts while in help menu to prevent conflict
37-
38-
- [ ] On mobile, the Persistent Dropdowns are loaded inside the top bar, when they should be overlays
18+
- [ ] On Home menu, when pressing Help, use different help guide
19+
- [X] Disable keyboard shortcuts while in help menu to prevent conflict
3920

4021
- [ ] Fix top bar on mobile
4122

42-
- [ ] Add more to about menu
43-
4423
- [ ] The app is using 400 MB RAM by default, but it should use more like 200 MB
4524
- now 290 MB
4625
- around 320-330 MB when playing a song
4726

48-
- [ ] Loading a custom iconset tanks performance
49-
50-
- [ ] Gapless and crossfade are just broken, they just are
51-
52-
- [ ] fix drag and drop of songs not triggering sometimes
53-
54-
- [ ] fix the UI of the 'Repeat Once' button
55-
56-
- [ ] Fix share_target so that it supports both importing songs (as a "polyfill" for file_handlers) and searching/sharing songs
57-
5827
- [ ] fix some themes' visibility issues
5928
- light mode mostly fixed, re-check all
6029

61-
- [ ] Fix sharing so that it just doesn't copy URL
62-
63-
- [ ] Can't move playlist to root unless I have another playlist in root
64-
65-
- [ ] Center `Empty folder...`
66-
67-
- [ ] Make dragging songs not select song details in song list but also make sure we can select that stuff (prevent conflicts)
68-
6930
- [ ] proper pitch manipulation
7031
- [PitchShift](https://tonejs.github.io/docs/15.1.22/classes/PitchShift.html)?
7132

Build/public/locales/en/translation.json

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
{
22
"title": "HTMLPlayer",
3+
"update": {
4+
"available": "Update Available",
5+
"description": "A new version of HTMLPlayer is ready to install.",
6+
"now": "Update Now",
7+
"later": "Later",
8+
"installing": "Installing update...",
9+
"installed": "Update installed! Reloading..."
10+
},
311
"common": {
412
"cancel": "Cancel",
513
"close": "Close",
@@ -130,7 +138,11 @@
130138
},
131139
"about": {
132140
"toggleVisualizerShortcut": "Toggle visualizer shortcut - implementation needed",
133-
"description": "A modern music player interface with playlists, song management, and an amazing visualizer."
141+
"description": "A modern music player interface with playlists, song management, and an amazing visualizer.",
142+
"version": "Version",
143+
"privacy": "Privacy Policy",
144+
"terms": "Terms of Service",
145+
"madeWith": "Made with ❤️ using React, TypeScript, and Web Audio API"
134146
},
135147
"metadataWorker": {
136148
"failedToReadAlbumArt": "Failed to read album art",
@@ -199,7 +211,8 @@
199211
"failedToReadFile": "Failed to read file",
200212
"cannotMoveIntoItself": "Cannot move item into itself",
201213
"cannotMoveFolderIntoDescendant": "Cannot move folder into its descendant",
202-
"noFoldersAvailable": "No folders available"
214+
"noFoldersAvailable": "No folders available",
215+
"emptyFolder": "Empty folder"
203216
},
204217
"noResults": {
205218
"search": "No songs found",
@@ -392,7 +405,8 @@
392405
"featuredPlaylists": "Spotlight playlists",
393406
"emptyPlaylists": "Create playlists to pin them here.",
394407
"playPlaylist": "Play",
395-
"playlistCount": "{{count}} songs"
408+
"playlistCount_one": "{{count}} song",
409+
"playlistCount_other": "{{count}} songs"
396410
},
397411
"pip": {
398412
"couldNotAccessStylesheet": "Could not access stylesheet:",

Build/public/locales/fr/translation.json

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
{
22
"title": "HTMLPlayer",
3+
"update": {
4+
"available": "Mise à jour disponible",
5+
"description": "Une nouvelle version de HTMLPlayer est prête à être installée.",
6+
"now": "Mettre à jour",
7+
"later": "Plus tard",
8+
"installing": "Installation de la mise à jour...",
9+
"installed": "Mise à jour installée ! Rechargement..."
10+
},
311
"common": {
412
"cancel": "Annuler",
513
"close": "Fermer",
@@ -129,7 +137,11 @@
129137
},
130138
"about": {
131139
"toggleVisualizerShortcut": "Raccourci pour activer/désactiver le visualiseur - implémentation nécessaire",
132-
"description": "Une interface moderne de lecteur de musique avec playlists, gestion des chansons et un visualiseur incroyable."
140+
"description": "Une interface moderne de lecteur de musique avec playlists, gestion des chansons et un visualiseur incroyable.",
141+
"version": "Version",
142+
"privacy": "Politique de confidentialité",
143+
"terms": "Conditions d'utilisation",
144+
"madeWith": "Fait avec ❤️ en utilisant React, TypeScript et Web Audio API"
133145
},
134146
"metadataWorker": {
135147
"failedToReadAlbumArt": "Échec de lecture de la pochette d'album",
@@ -195,7 +207,8 @@
195207
"failedToReadFile": "Échec de lecture du fichier",
196208
"cannotMoveIntoItself": "Impossible de déplacer l'élément vers lui-même",
197209
"cannotMoveFolderIntoDescendant": "Impossible de déplacer le dossier vers son descendant",
198-
"noFoldersAvailable": "Aucun dossier disponible"
210+
"noFoldersAvailable": "Aucun dossier disponible",
211+
"emptyFolder": "Dossier vide"
199212
},
200213
"noResults": {
201214
"search": "Aucune chanson trouvée",
@@ -364,7 +377,8 @@
364377
"featuredPlaylists": "Playlists à l'honneur",
365378
"emptyPlaylists": "Créez des playlists pour les retrouver ici.",
366379
"playPlaylist": "Lecture",
367-
"playlistCount": "{{count}} titres"
380+
"playlistCount_one": "{{count}} titre",
381+
"playlistCount_other": "{{count}} titres"
368382
},
369383
"pip": {
370384
"couldNotAccessStylesheet": "Impossible d'accéder à la feuille de style :",

Build/src/components/Draggable.module.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,17 @@
141141
.draggableItem[data-dragging="true"] {
142142
opacity: 0.5;
143143
cursor: grabbing;
144+
user-select: none;
145+
}
146+
147+
/* Drag handle, only this area triggers drag */
148+
.dragHandle {
149+
cursor: grab;
150+
user-select: none;
151+
-webkit-user-select: none;
152+
touch-action: none;
153+
}
154+
155+
.dragHandle:active {
156+
cursor: grabbing;
144157
}

Build/src/components/Draggable.tsx

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,15 @@ interface DraggableItemProps {
4646
id: string;
4747
type: "song" | "playlist" | "folder";
4848
data: any;
49-
children: React.ReactNode;
49+
children: React.ReactNode | ((dragHandleProps: DragHandleProps) => React.ReactNode);
5050
disabled?: boolean;
51+
/** If true, only the DragHandle child will initiate drag (allows text selection) */
52+
useDragHandle?: boolean;
53+
}
54+
55+
export interface DragHandleProps {
56+
listeners: ReturnType<typeof useDraggable>["listeners"];
57+
attributes: ReturnType<typeof useDraggable>["attributes"];
5158
}
5259

5360
interface DropZoneProps {
@@ -259,6 +266,7 @@ export const DraggableItem: React.FC<DraggableItemProps> = ({
259266
data,
260267
children,
261268
disabled = false,
269+
useDragHandle = false,
262270
}) => {
263271
const { attributes, listeners, setNodeRef, transform, isDragging } =
264272
useDraggable({
@@ -273,13 +281,53 @@ export const DraggableItem: React.FC<DraggableItemProps> = ({
273281
}
274282
: undefined;
275283

284+
// If using drag handle, pass listeners via render prop or inject into children
285+
const renderChildren = (): React.ReactNode => {
286+
if (useDragHandle && typeof children === "function") {
287+
// Render prop pattern - call the function with drag handle props
288+
return children({ listeners, attributes });
289+
} else if (useDragHandle && typeof children !== "function" && React.isValidElement(children)) {
290+
// Clone children and inject drag listeners for any DragHandle components
291+
return React.Children.map(children, (child) => {
292+
if (React.isValidElement(child)) {
293+
return React.cloneElement(child as React.ReactElement<any>, {
294+
__dragListeners: listeners,
295+
});
296+
}
297+
return child;
298+
});
299+
}
300+
return typeof children === "function" ? null : children;
301+
};
302+
276303
return (
277304
<div
278305
ref={setNodeRef}
279306
style={style}
280307
className={dragStyles.draggableItem}
281308
data-dragging={isDragging ? "true" : "false"}
282-
{...listeners}
309+
{...(useDragHandle ? {} : listeners)}
310+
{...(useDragHandle ? {} : attributes)}
311+
>
312+
{renderChildren()}
313+
</div>
314+
);
315+
};
316+
317+
// Drag handle component - only this element initiates drag when useDragHandle is true
318+
export const DragHandle: React.FC<{
319+
children: React.ReactNode;
320+
className?: string;
321+
listeners?: DragHandleProps["listeners"];
322+
attributes?: DragHandleProps["attributes"];
323+
__dragListeners?: any; // Legacy: Injected by DraggableItem via cloneElement
324+
}> = ({ children, className, listeners, attributes, __dragListeners }) => {
325+
// Use explicit props first, fall back to injected props
326+
const dragListeners = listeners || __dragListeners;
327+
return (
328+
<div
329+
className={`${dragStyles.dragHandle} ${className || ""}`}
330+
{...dragListeners}
283331
{...attributes}
284332
>
285333
{children}

Build/src/components/HelpGuide.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ interface HelpGuideProps {
1111

1212
const { Badge, Close, Navigation } = components;
1313

14-
const TourContext = createContext<{ loaded: boolean }>({ loaded: false });
14+
const TourContext = createContext<{ loaded: boolean; isOpen: boolean }>({ loaded: false, isOpen: false });
1515

1616
export const useTourLoaded = () => useContext(TourContext);
1717

18+
// Re-export useTour for external use (e.g., keyboard shortcuts)
19+
export { useTour };
20+
1821
export const HelpGuideProvider = ({ children }: HelpGuideProps) => {
1922
function useTourStepsConfig() {
2023
const { i18n } = useTranslation();
@@ -60,14 +63,14 @@ export const HelpGuideProvider = ({ children }: HelpGuideProps) => {
6063

6164
if (!tourStepsConfig.length) {
6265
return (
63-
<TourContext.Provider value={{ loaded: false }}>
66+
<TourContext.Provider value={{ loaded: false, isOpen: false }}>
6467
{children}
6568
</TourContext.Provider>
6669
);
6770
}
6871

6972
return (
70-
<TourContext.Provider value={{ loaded: true }}>
73+
<TourContext.Provider value={{ loaded: true, isOpen: false }}>
7174
<TourProvider
7275
steps={steps}
7376
styles={{

0 commit comments

Comments
 (0)