-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
27 changed files
with
572 additions
and
224 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { useCallback, useEffect, useState } from 'react' | ||
|
||
import { useDispatch, useSelector } from 'react-redux' | ||
|
||
import { Status } from 'models/Status' | ||
|
||
import { accountActions } from '../store/account' | ||
import { | ||
savedCollectionsActions, | ||
savedCollectionsSelectors | ||
} from '../store/saved-collections' | ||
|
||
const { fetchSavedPlaylists } = accountActions | ||
const { fetchCollections } = savedCollectionsActions | ||
|
||
const { | ||
getAccountAlbums, | ||
getSavedAlbumsState, | ||
getFetchedAlbumsWithDetails, | ||
getAccountPlaylists | ||
} = savedCollectionsSelectors | ||
|
||
const DEFAULT_PAGE_SIZE = 50 | ||
|
||
export function useSavedAlbums() { | ||
return useSelector(getAccountAlbums) | ||
} | ||
|
||
/* TODO: Handle filtering | ||
* Option 1: This hook takes the list of album ids to fetch and computes the unfetched | ||
* based on that. | ||
* Option 2: Bake filter into selectors which drive this. Downside: Can't use this in multiple places... | ||
*/ | ||
type UseSavedAlbumDetailsConfig = { | ||
pageSize?: number | ||
} | ||
export function useSavedAlbumsDetails({ | ||
pageSize = DEFAULT_PAGE_SIZE | ||
}: UseSavedAlbumDetailsConfig) { | ||
const dispatch = useDispatch() | ||
const [hasFetched, setHasFetched] = useState(false) | ||
const { unfetched: unfetchedAlbums, fetched: albumsWithDetails } = | ||
useSelector(getFetchedAlbumsWithDetails) | ||
const { status } = useSelector(getSavedAlbumsState) | ||
|
||
const fetchMore = useCallback(() => { | ||
if (status === Status.LOADING || unfetchedAlbums.length === 0) { | ||
return | ||
} | ||
const ids = unfetchedAlbums | ||
.slice(0, Math.min(pageSize, unfetchedAlbums.length)) | ||
.map((c) => c.id) | ||
dispatch(fetchCollections({ type: 'albums', ids })) | ||
setHasFetched(true) | ||
}, [status, unfetchedAlbums, pageSize, dispatch, setHasFetched]) | ||
|
||
// Fetch first page if we don't have any items fetched yet | ||
// Needs to wait for at least some albums to be fetchable | ||
useEffect(() => { | ||
if ( | ||
!hasFetched && | ||
// TODO: This check should change once InfiniteScroll is implemented | ||
status !== Status.LOADING /* && | ||
unfetchedAlbums.length > 0 && | ||
albumsWithDetails.length === 0 */ | ||
) { | ||
fetchMore() | ||
} | ||
}, [albumsWithDetails, status, hasFetched, unfetchedAlbums, fetchMore]) | ||
|
||
return { data: albumsWithDetails, status, fetchMore } | ||
} | ||
|
||
export function useSavedPlaylists() { | ||
return useSelector(getAccountPlaylists) | ||
} | ||
|
||
export function useSavedPlaylistsDetails() { | ||
const dispatch = useDispatch() | ||
|
||
useEffect(() => { | ||
dispatch(fetchSavedPlaylists()) | ||
}, [dispatch]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export { | ||
default as savedCollectionsReducer, | ||
actions as savedCollectionsActions | ||
} from './slice' | ||
export * as savedCollectionsSelectors from './selectors' | ||
export * from './types' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { createSelector } from '@reduxjs/toolkit' | ||
|
||
import { getUsers } from 'store/cache/users/selectors' | ||
|
||
import { AccountCollection } from '../account' | ||
import { getAccountStatus } from '../account/selectors' | ||
import { getCollections } from '../cache/collections/selectors' | ||
import { CommonState } from '../commonStore' | ||
|
||
import { CollectionWithOwner } from './types' | ||
|
||
const getAccountCollections = (state: CommonState) => state.account.collections | ||
export const getSavedAlbumsState = (state: CommonState) => | ||
state.savedCollections.albums | ||
export const getSavedPlaylistsState = (state: CommonState) => | ||
state.savedCollections.playlists | ||
|
||
export const getAccountAlbums = createSelector( | ||
[getAccountCollections, getAccountStatus], | ||
(collections, status) => ({ | ||
status, | ||
data: Object.values(collections).filter((c) => c.is_album) | ||
}) | ||
) | ||
|
||
type GetAlbumsWithDetailsResult = { | ||
fetched: CollectionWithOwner[] | ||
unfetched: AccountCollection[] | ||
} | ||
/** Returns a mapped list of albums for which we have fetched full details */ | ||
export const getFetchedAlbumsWithDetails = createSelector( | ||
[getAccountAlbums, getCollections, getUsers], | ||
(albums, collections, users) => { | ||
// TODO: Might want to read status, what happens for failed loads of parts of the collection? | ||
return albums.data.reduce<GetAlbumsWithDetailsResult>( | ||
(acc, cur) => { | ||
const collectionMetadata = collections[cur.id] | ||
if (collectionMetadata) { | ||
const ownerHandle = | ||
users[collectionMetadata.playlist_owner_id]?.handle ?? '' | ||
acc.fetched.push({ ...collections[cur.id], ownerHandle }) | ||
} else { | ||
acc.unfetched.push(cur) | ||
} | ||
return acc | ||
}, | ||
{ fetched: [], unfetched: [] } | ||
) | ||
} | ||
) | ||
|
||
export const getAccountPlaylists = createSelector( | ||
[getAccountCollections, getAccountStatus], | ||
(collections, status) => ({ | ||
status, | ||
data: Object.values(collections).filter((c) => !c.is_album) | ||
}) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { createSlice, PayloadAction } from '@reduxjs/toolkit' | ||
|
||
import { ID } from '../../models/Identifiers' | ||
import { Status } from '../../models/Status' | ||
|
||
import { CollectionType } from './types' | ||
|
||
type FetchCollectionsPayload = { | ||
type: CollectionType | ||
ids: ID[] | ||
} | ||
|
||
type FetchCollectionsSucceededPayload = { | ||
type: CollectionType | ||
} | ||
|
||
type FetchCollectionsFailedPayload = { | ||
type: CollectionType | ||
} | ||
|
||
export type CollectionListState = { | ||
status: Status | ||
} | ||
|
||
export type SavedCollectionsState = { | ||
albums: CollectionListState | ||
playlists: CollectionListState | ||
} | ||
|
||
const initialState: SavedCollectionsState = { | ||
albums: { | ||
status: Status.IDLE | ||
}, | ||
playlists: { | ||
status: Status.IDLE | ||
} | ||
} | ||
|
||
const slice = createSlice({ | ||
name: 'saved-collections', | ||
initialState, | ||
reducers: { | ||
fetchCollections: ( | ||
state, | ||
action: PayloadAction<FetchCollectionsPayload> | ||
) => { | ||
state[action.payload.type].status = Status.LOADING | ||
}, | ||
fetchCollectionsSucceeded: ( | ||
state, | ||
action: PayloadAction<FetchCollectionsSucceededPayload> | ||
) => { | ||
const list = state[action.payload.type] | ||
list.status = Status.SUCCESS | ||
}, | ||
fetchCollectionsFailed: ( | ||
state, | ||
action: PayloadAction<FetchCollectionsFailedPayload> | ||
) => { | ||
state[action.payload.type].status = Status.ERROR | ||
} | ||
} | ||
}) | ||
|
||
export const { | ||
fetchCollections, | ||
fetchCollectionsSucceeded, | ||
fetchCollectionsFailed | ||
} = slice.actions | ||
|
||
export const actions = slice.actions | ||
export default slice.reducer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Collection } from '../../models/Collection' | ||
|
||
export type CollectionType = 'albums' | 'playlists' | ||
|
||
export type CollectionWithOwner = Collection & { | ||
ownerHandle: string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.