Skip to content

Commit d47f631

Browse files
authored
Merge pull request #15 from soup-bowl/soup-bowl/issue11
Added wantlist
2 parents 661a01d + 3c88ff3 commit d47f631

File tree

8 files changed

+181
-34
lines changed

8 files changed

+181
-34
lines changed

frontend/src/api/discogs.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,48 @@ export const getProfile = async (username: string, password: string): Promise<IP
2828
return response.json()
2929
}
3030

31+
export const getCollectionWants = async (
32+
username: string,
33+
password: string,
34+
onProgress?: (page: number, pages: number) => void
35+
): Promise<IReleases[]> => {
36+
let allReleases: IReleases[] = []
37+
let url: string | undefined = `${API_URL}/users/${username}/wants?per_page=100`
38+
39+
while (url) {
40+
const response = await fetch(url, {
41+
headers: {
42+
"Content-Type": "application/json",
43+
Authorization: `Discogs token=${password}`,
44+
},
45+
})
46+
47+
if (!response.ok) {
48+
throw new Error("Network response was not ok")
49+
}
50+
51+
const data: ICollections = await response.json()
52+
53+
// Cheating a bit - converting the reference to keep the same models.
54+
const transformedData = {
55+
...data,
56+
// @ts-expect-error
57+
releases: data.wants,
58+
}
59+
60+
allReleases = [...allReleases, ...transformedData.releases]
61+
62+
if (onProgress) {
63+
onProgress(data.pagination.page, data.pagination.pages)
64+
}
65+
66+
// Set the url to the next page, or undefined if there are no more pages
67+
url = data.pagination.urls.next
68+
}
69+
70+
return allReleases
71+
}
72+
3173
export const getCollectionReleases = async (
3274
username: string,
3375
password: string,

frontend/src/api/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
export { getCollectionReleases, getMe, getProfile } from "./discogs"
1+
export { getCollectionReleases, getCollectionWants, getMe, getProfile } from "./discogs"
22
export { postVinylQueue } from "./vinyl"
33
export type {
44
ICollections,
5+
IWants,
56
IIdentify,
67
IProfile,
78
IPagination,

frontend/src/api/interface.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ export interface ICollections {
6464
releases: IReleases[]
6565
}
6666

67+
export interface IWants {
68+
pagination: IPagination
69+
wants: IReleases[]
70+
}
71+
6772
export interface IIdentify {
6873
id: number
6974
username: string

frontend/src/components/AlbumGrid.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@ const AlbumGridEntry: React.FC<AlbumProps> = ({ album, index, staticImage = unde
2727
interface CollectionProps {
2828
data: IReleases[]
2929
sort?: "release" | "label" | "artist" | "none"
30+
type: "collection" | "want"
3031
username?: string
3132
onClickAlbum: (album: IReleases) => void
3233
}
3334

34-
const AlbumGrid: React.FC<CollectionProps> = ({ data, sort = "none", username = "", onClickAlbum }) => {
35+
const AlbumGrid: React.FC<CollectionProps> = ({ data, sort = "none", type, username = "", onClickAlbum }) => {
3536
const imageData = useQuery<IVinylResponse | undefined>({
36-
queryKey: [`${username}images`],
37+
queryKey: [`${username}${type}images`],
3738
queryFn: () => postVinylQueue(data?.map((item) => item.basic_information.id) ?? []),
3839
staleTime: 1000 * 60 * 60 * 24, // 24 hours
3940
enabled: data !== undefined,
@@ -49,7 +50,7 @@ const AlbumGrid: React.FC<CollectionProps> = ({ data, sort = "none", username =
4950
break
5051
case "release":
5152
displayData = splitRecordsByYear(data)
52-
labelText = "Collected in "
53+
labelText = `${type === "collection" ? "Collected" : "Wanted"} in `
5354
break
5455
case "label":
5556
displayData = splitRecordsByLabel(data)

frontend/src/components/AlbumList.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { IonAvatar, IonChip, IonIcon, IonItem, IonLabel, IonList, IonText } from "@ionic/react"
1+
import { IonAvatar, IonChip, IonIcon, IonItem, IonLabel, IonList, IonListHeader, IonText } from "@ionic/react"
22
import { disc } from "ionicons/icons"
33
import { useQuery } from "@tanstack/react-query"
44
import { IReleases, IVinylResponse, postVinylQueue } from "../api"
@@ -8,19 +8,26 @@ import "./AlbumList.css"
88
interface Props {
99
data: IReleases[]
1010
username?: string
11+
title?: string
12+
type: "collection" | "want"
1113
onClickAlbum: (album: IReleases) => void
1214
}
1315

14-
const AlbumList: React.FC<Props> = ({ data, username = "", onClickAlbum }) => {
16+
const AlbumList: React.FC<Props> = ({ data, username = "", title = undefined, type, onClickAlbum }) => {
1517
const imageData = useQuery<IVinylResponse | undefined>({
16-
queryKey: [`${username}images`],
18+
queryKey: [`${username}${type}images`],
1719
queryFn: () => postVinylQueue(data?.map((item) => item.basic_information.id) ?? []),
1820
staleTime: 1000 * 60 * 60 * 24, // 24 hours
1921
enabled: data !== undefined,
2022
})
2123

2224
return (
2325
<IonList lines="full">
26+
{title && (
27+
<IonListHeader>
28+
<IonLabel>{title}</IonLabel>
29+
</IonListHeader>
30+
)}
2431
{data.map((album, index) => {
2532
let image = undefined
2633
if (imageData.data) {

frontend/src/modal/ViewAlbumDetails.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ import { findLocalImageById } from "../utils"
1717
interface DisplayProps {
1818
album: IReleases
1919
username?: string
20+
type: "collection" | "want"
2021
open: boolean
2122
onClose: () => void
2223
}
2324

24-
const ViewAlbumDetails: React.FC<DisplayProps> = ({ album, username = "", open, onClose }) => {
25+
const ViewAlbumDetails: React.FC<DisplayProps> = ({ album, username = "", type, open, onClose }) => {
2526
const imageData = useQuery<IVinylResponse | undefined>({
26-
queryKey: [`${username}images`],
27+
queryKey: [`${username}${type}images`],
2728
staleTime: 1000 * 60 * 60 * 24, // 24 hours
2829
enabled: false,
2930
})

frontend/src/pages/Collection.tsx

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,20 @@ import {
55
IonContent,
66
IonHeader,
77
IonIcon,
8+
IonLabel,
89
IonPage,
910
IonRefresher,
1011
IonRefresherContent,
12+
IonSegment,
13+
IonSegmentButton,
1114
IonTitle,
1215
IonToolbar,
1316
RefresherEventDetail,
1417
useIonActionSheet,
1518
} from "@ionic/react"
1619
import { useQuery, useQueryClient } from "@tanstack/react-query"
1720
import { filterOutline, personOutline, pricetagOutline, timeOutline } from "ionicons/icons"
18-
import { IReleases, getCollectionReleases } from "../api"
21+
import { IReleases, getCollectionReleases, getCollectionWants } from "../api"
1922
import { FullpageLoading, AlbumGrid, FullpageInfo } from "../components"
2023
import { ViewAlbumDetails } from "../modal"
2124
import { useAuth } from "../hooks"
@@ -60,6 +63,7 @@ const CollectionPage: React.FC = () => {
6063
const [filter, setFilter] = useState<"release" | "label" | "artist" | "none">("none")
6164
const [modalInfo, setModalInfo] = useState<IReleases | undefined>(undefined)
6265
const [loading, setLoading] = useState<{ page: number; pages: number }>({ page: 0, pages: 0 })
66+
const [viewState, setViewState] = useState<"collection" | "want">("collection")
6367
const betaBanner = import.meta.env.VITE_BETA_BANNER
6468

6569
const [{ username, token }, saveAuth, clearAuth] = useAuth()
@@ -88,27 +92,41 @@ const CollectionPage: React.FC = () => {
8892
)
8993
}
9094

91-
const { data, isLoading, isError } = useQuery<IReleases[]>({
95+
const collectionData = useQuery<IReleases[]>({
9296
queryKey: [`${username}collection`],
9397
queryFn: () =>
9498
getCollectionReleases(username, token ?? "", (page, pages) => setLoading({ page: page, pages: pages })),
9599
staleTime: 1000 * 60 * 60 * 24, // 24 hours
96100
})
97101

102+
const wantData = useQuery<IReleases[]>({
103+
queryKey: [`${username}want`],
104+
queryFn: () =>
105+
getCollectionWants(username, token ?? "", (page, pages) => setLoading({ page: page, pages: pages })),
106+
staleTime: 1000 * 60 * 60 * 24, // 24 hours
107+
})
108+
98109
const handleRefresh = async (event: CustomEvent<RefresherEventDetail>) => {
99-
await queryClient.invalidateQueries({ queryKey: [`${username}collection`, `${username}images`] })
110+
await queryClient.invalidateQueries({
111+
queryKey: [
112+
`${username}collection`,
113+
`${username}want`,
114+
`${username}collectionimages`,
115+
`${username}wantimages`,
116+
],
117+
})
100118
event.detail.complete()
101119
}
102120

103-
if (isLoading) {
121+
if (collectionData.isLoading || wantData.isLoading) {
104122
return (
105123
<IonPage>
106-
<FullpageLoading loadingProgress={loading.page + 1} loadingComplete={loading.pages} />
124+
<FullpageLoading />
107125
</IonPage>
108126
)
109127
}
110128

111-
if (isError) {
129+
if (collectionData.isError || wantData.isError) {
112130
return (
113131
<IonPage>
114132
<FullpageInfo text="An error occurred when loading information." />
@@ -137,7 +155,24 @@ const CollectionPage: React.FC = () => {
137155
<IonIcon slot="icon-only" md={getFilterIcon(filter)}></IonIcon>
138156
</IonButton>
139157
</IonButtons>
140-
<IonTitle>Collection</IonTitle>
158+
<IonSegment
159+
value={viewState}
160+
onIonChange={(e) => {
161+
const selectedValue = e.detail.value
162+
if (selectedValue === "collection" || selectedValue === "want") {
163+
setViewState(selectedValue)
164+
} else {
165+
setViewState("collection")
166+
}
167+
}}
168+
>
169+
<IonSegmentButton value="collection">
170+
<IonLabel>Collection</IonLabel>
171+
</IonSegmentButton>
172+
<IonSegmentButton value="want">
173+
<IonLabel>Wanted</IonLabel>
174+
</IonSegmentButton>
175+
</IonSegment>
141176
</IonToolbar>
142177
{betaBanner && (
143178
<IonToolbar className="beta-banner" color="warning">
@@ -149,11 +184,22 @@ const CollectionPage: React.FC = () => {
149184
<IonRefresher slot="fixed" onIonRefresh={handleRefresh}>
150185
<IonRefresherContent></IonRefresherContent>
151186
</IonRefresher>
152-
{data && (
187+
{viewState === "collection" && collectionData.data && (
188+
<AlbumGrid
189+
data={collectionData.data}
190+
sort={filter}
191+
username={username}
192+
type={viewState}
193+
onClickAlbum={(album) => setModalInfo(album)}
194+
/>
195+
)}
196+
197+
{viewState === "want" && wantData.data && (
153198
<AlbumGrid
154-
data={data}
199+
data={wantData.data}
155200
sort={filter}
156201
username={username}
202+
type={viewState}
157203
onClickAlbum={(album) => setModalInfo(album)}
158204
/>
159205
)}
@@ -162,6 +208,7 @@ const CollectionPage: React.FC = () => {
162208
<ViewAlbumDetails
163209
album={modalInfo}
164210
username={username}
211+
type={viewState}
165212
open={typeof modalInfo !== undefined}
166213
onClose={() => setModalInfo(undefined)}
167214
/>

0 commit comments

Comments
 (0)