Skip to content

Commit

Permalink
feat: persist player state across browser reloads
Browse files Browse the repository at this point in the history
  • Loading branch information
paulschwoerer committed Oct 20, 2021
1 parent 00dea69 commit c6c7859
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 1 deletion.
2 changes: 2 additions & 0 deletions web/src/modules/auth/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export function AuthProvider({

const logout = async () => {
await makeApiPostRequest('auth/logout');

localStorage.clear();
setUser(null);
};

Expand Down
46 changes: 46 additions & 0 deletions web/src/modules/player/PlayerProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
updateMediaSessionPlayState,
updateMediaSessionPositionState,
} from './mediaSession';
import { LocalStoragePlayerPersistor } from './persistence/LocalStoragePlayerPersistor';
import {
loadAlbumSongsFromAPI,
loadArtistSongsFromAPI,
Expand Down Expand Up @@ -211,6 +212,51 @@ export function PlayerProvider({
}
}, [canSkipPrevious, hasPassedSkipThreshold, setSeek]);

useEffect(() => {
const persistor = new LocalStoragePlayerPersistor();

persistor
.retrieve()
.then(state => {
if (state !== null) {
dispatch({
type: 'setState',
state: {
...state,
...state.settings,
},
});
}
})
.catch(e =>
console.error(`could not retrieve persisted player state: ${e}`),
);
}, []);

useEffect(() => {
const persistor = new LocalStoragePlayerPersistor();

window.onbeforeunload = () => {
persistor
.persist({
current,
history,
queue,
settings: {
isMuted,
repeatMode,
shuffle,
volume,
},
})
.catch(e => `could not persist player state: ${e}`);
};

return () => {
window.onbeforeunload = null;
};
}, [current, history, isMuted, queue, repeatMode, shuffle, volume]);

useEffect(() => {
updateMediaSessionPlayState(playbackState);
}, [playbackState]);
Expand Down
39 changes: 39 additions & 0 deletions web/src/modules/player/persistence/LocalStoragePlayerPersistor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { PlayerPersistor, PlayerPersistState } from './PlayerPersistor';

const LOCAL_STORAGE_KEY = 'lp_player_state';

export class LocalStoragePlayerPersistor implements PlayerPersistor {
clear(): Promise<void> {
localStorage.removeItem(LOCAL_STORAGE_KEY);

return Promise.resolve();
}

persist(state: PlayerPersistState): Promise<void> {
const content = JSON.stringify(state);

try {
localStorage.setItem(LOCAL_STORAGE_KEY, content);

return Promise.resolve();
} catch (e) {
return Promise.reject(e);
}
}

retrieve(): Promise<PlayerPersistState | null> {
try {
const content = localStorage.getItem(LOCAL_STORAGE_KEY);

if (content === null) {
return Promise.resolve(null);
}

const state = JSON.parse(content) as PlayerPersistState;

return Promise.resolve(state);
} catch (e) {
return Promise.reject(e);
}
}
}
19 changes: 19 additions & 0 deletions web/src/modules/player/persistence/PlayerPersistor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { PlayerRepeatMode, QueueItem } from '../types';

export type PlayerPersistState = {
queue: QueueItem[];
history: QueueItem[];
current: QueueItem | null;
settings: {
volume: number;
isMuted: boolean;
shuffle: boolean;
repeatMode: PlayerRepeatMode;
};
};

export interface PlayerPersistor {
persist(state: PlayerPersistState): Promise<void>;
retrieve(): Promise<PlayerPersistState | null>;
clear(): Promise<void>;
}
11 changes: 10 additions & 1 deletion web/src/modules/player/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ type Action =
| { type: 'removeQueueItem'; index: number }
| { type: 'goToQueueIndex'; index: number }
| { type: 'playbackError' }
| { type: 'setBuffered'; to: number };
| { type: 'setBuffered'; to: number }
| { type: 'setState'; state: Partial<State> };

export function playerReducer(state: State, action: Action): State {
switch (action.type) {
Expand Down Expand Up @@ -226,6 +227,14 @@ export function playerReducer(state: State, action: Action): State {
bufferedTo: to,
};
}
case 'setState': {
const { state: newState } = action;

return {
...state,
...newState,
};
}
}
}

Expand Down

0 comments on commit c6c7859

Please sign in to comment.