tangled
alpha
login
or
join now
teal.fm
/
teal
110
fork
atom
Your music, beautifully tracked. All yours. (coming soon)
teal.fm
teal-fm
atproto
110
fork
atom
overview
issues
pulls
pipelines
finally type musicbrainz responses and functions
Natalie
1 year ago
49efc503
e623280d
+84
-23
1 changed file
expand all
collapse all
unified
split
apps
amethyst
app
(tabs)
stamp.tsx
+84
-23
apps/amethyst/app/(tabs)/stamp.tsx
···
5
5
TouchableOpacity,
6
6
FlatList,
7
7
Image,
8
8
-
Alert,
9
8
Modal,
10
9
} from "react-native";
11
10
import { useState } from "react";
···
18
17
import { Link, Stack } from "expo-router";
19
18
import React from "react";
20
19
21
21
-
async function searchMusicbrainz(searchParams: {
20
20
+
// MusicBrainz API Types
21
21
+
interface MusicBrainzArtistCredit {
22
22
+
artist: {
23
23
+
id: string;
24
24
+
name: string;
25
25
+
"sort-name"?: string;
26
26
+
};
27
27
+
joinphrase?: string;
28
28
+
name: string;
29
29
+
}
30
30
+
31
31
+
interface MusicBrainzRelease {
32
32
+
id: string;
33
33
+
title: string;
34
34
+
status?: string;
35
35
+
date?: string;
36
36
+
country?: string;
37
37
+
disambiguation?: string;
38
38
+
"track-count"?: number;
39
39
+
}
40
40
+
41
41
+
interface MusicBrainzRecording {
42
42
+
id: string;
43
43
+
title: string;
44
44
+
length?: number;
45
45
+
isrcs?: string[];
46
46
+
"artist-credit"?: MusicBrainzArtistCredit[];
47
47
+
releases?: MusicBrainzRelease[];
48
48
+
selectedRelease?: MusicBrainzRelease; // Added for UI state
49
49
+
}
50
50
+
51
51
+
interface SearchParams {
22
52
track?: string;
23
53
artist?: string;
24
24
-
}) {
54
54
+
release?: string;
55
55
+
}
56
56
+
57
57
+
interface SearchResultProps {
58
58
+
result: MusicBrainzRecording;
59
59
+
onSelectTrack: (track: MusicBrainzRecording | null) => void;
60
60
+
isSelected: boolean;
61
61
+
selectedRelease: MusicBrainzRelease | null;
62
62
+
onReleaseSelect: (trackId: string, release: MusicBrainzRelease) => void;
63
63
+
}
64
64
+
65
65
+
interface PlayRecord {
66
66
+
trackName: string;
67
67
+
recordingMbId?: string;
68
68
+
duration?: number;
69
69
+
artistName: string;
70
70
+
artistMbIds?: string[];
71
71
+
releaseName?: string;
72
72
+
releaseMbId?: string;
73
73
+
isrc?: string;
74
74
+
originUrl: string;
75
75
+
musicServiceBaseDomain: string;
76
76
+
submissionClientAgent: string;
77
77
+
playedTime: string;
78
78
+
}
79
79
+
80
80
+
interface ReleaseSelections {
81
81
+
[key: string]: MusicBrainzRelease;
82
82
+
}
83
83
+
84
84
+
async function searchMusicbrainz(
85
85
+
searchParams: SearchParams,
86
86
+
): Promise<MusicBrainzRecording[]> {
25
87
try {
26
26
-
const queryParts = [];
88
88
+
const queryParts: string[] = [];
27
89
if (searchParams.track)
28
90
queryParts.push(`release title:"${searchParams.track}"`);
29
91
if (searchParams.artist)
···
44
106
}
45
107
}
46
108
47
47
-
const SearchResult = ({
109
109
+
const SearchResult: React.FC<SearchResultProps> = ({
48
110
result,
49
111
onSelectTrack,
50
112
isSelected,
51
113
selectedRelease,
52
114
onReleaseSelect,
53
115
}) => {
54
54
-
const [showReleaseModal, setShowReleaseModal] = useState(false);
116
116
+
const [showReleaseModal, setShowReleaseModal] = useState<boolean>(false);
55
117
56
118
const currentRelease = selectedRelease || result.releases?.[0];
57
119
···
85
147
</Text>
86
148
87
149
{/* Release Selector Button */}
88
88
-
{result.releases?.length > 0 && (
150
150
+
{result.releases && result.releases?.length > 0 && (
89
151
<TouchableOpacity
90
152
onPress={() => setShowReleaseModal(true)}
91
153
className="p-1 bg-secondary/10 rounded-lg flex md:flex-row items-start md:gap-1"
···
182
244
183
245
export default function TabTwoScreen() {
184
246
const agent = useStore((state) => state.pdsAgent);
185
185
-
const [searchFields, setSearchFields] = useState({
247
247
+
const [searchFields, setSearchFields] = useState<SearchParams>({
186
248
track: "",
187
249
artist: "",
188
250
release: "",
189
251
});
190
190
-
const [searchResults, setSearchResults] = useState([]);
191
191
-
const [selectedTrack, setSelectedTrack] = useState(null);
192
192
-
const [selectedRelease, setSelectedRelease] = useState(null);
193
193
-
const [isLoading, setIsLoading] = useState(false);
194
194
-
const [isSubmitting, setIsSubmitting] = useState(false);
195
195
-
196
196
-
const [releaseSelections, setReleaseSelections] = useState({});
252
252
+
const [searchResults, setSearchResults] = useState<MusicBrainzRecording[]>(
253
253
+
[],
254
254
+
);
255
255
+
const [selectedTrack, setSelectedTrack] =
256
256
+
useState<MusicBrainzRecording | null>(null);
257
257
+
const [isLoading, setIsLoading] = useState<boolean>(false);
258
258
+
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
259
259
+
const [releaseSelections, setReleaseSelections] = useState<ReleaseSelections>(
260
260
+
{},
261
261
+
);
197
262
198
198
-
const handleTrackSelect = (track) => {
263
263
+
const handleTrackSelect = (track: MusicBrainzRecording | null): void => {
199
264
setSelectedTrack(track);
200
200
-
// Reset selected release when track is deselected
201
201
-
if (!track) {
202
202
-
setSelectedRelease(null);
203
203
-
}
204
265
};
205
266
206
206
-
const handleSearch = async () => {
267
267
+
const handleSearch = async (): Promise<void> => {
207
268
if (!searchFields.track && !searchFields.artist && !searchFields.release) {
208
269
return;
209
270
}
···
215
276
setIsLoading(false);
216
277
};
217
278
218
218
-
const createPlayRecord = (result) => {
279
279
+
const createPlayRecord = (result: MusicBrainzRecording): PlayRecord => {
219
280
return {
220
281
trackName: result.title ?? "Unknown Title",
221
282
recordingMbId: result.id ?? undefined,
···
235
296
};
236
297
};
237
298
238
238
-
const submitPlay = async () => {
299
299
+
const submitPlay = async (): Promise<void> => {
239
300
if (!selectedTrack) return;
240
301
241
302
setIsSubmitting(true);