Your music, beautifully tracked. All yours. (coming soon) teal.fm
teal-fm atproto

nits (weird error in login, unneeded import)

+149 -5
+148 -3
apps/amethyst/app/(tabs)/(stamp)/stamp/index.tsx
··· 4 4 ReleaseSelections, 5 5 searchMusicbrainz, 6 6 SearchParams, 7 - SearchResult, 7 + SearchResultProps, 8 8 } from "../../../../lib/oldStamp"; 9 - import { ScrollView, TextInput, View, Text } from "react-native"; 9 + import { 10 + ScrollView, 11 + TextInput, 12 + View, 13 + Text, 14 + TouchableOpacity, 15 + Image, 16 + Modal, 17 + } from "react-native"; 10 18 import { Stack, useRouter } from "expo-router"; 11 19 import { Button } from "@/components/ui/button"; 12 20 import { FlatList } from "react-native"; 13 - import { ChevronRight } from "lucide-react-native"; 21 + import { Check, ChevronRight } from "lucide-react-native"; 22 + import { Icon } from "@/lib/icons/iconWithClassName"; 14 23 15 24 export default function StepOne() { 16 25 const router = useRouter(); ··· 143 152 </ScrollView> 144 153 ); 145 154 } 155 + 156 + export function SearchResult({ 157 + result, 158 + onSelectTrack, 159 + isSelected, 160 + selectedRelease, 161 + onReleaseSelect, 162 + }: SearchResultProps) { 163 + const [showReleaseModal, setShowReleaseModal] = useState<boolean>(false); 164 + 165 + const currentRelease = selectedRelease || result.releases?.[0]; 166 + 167 + return ( 168 + <TouchableOpacity 169 + onPress={() => { 170 + onSelectTrack( 171 + isSelected 172 + ? null 173 + : { 174 + ...result, 175 + selectedRelease: currentRelease, // Pass the selected release with the track 176 + }, 177 + ); 178 + }} 179 + className={`p-4 mb-2 rounded-lg ${ 180 + isSelected ? "bg-primary/20" : "bg-secondary/10" 181 + }`} 182 + > 183 + <View className="flex-row justify-between items-center gap-2"> 184 + <Image 185 + className="w-16 h-16 rounded-lg bg-gray-500/50" 186 + source={{ 187 + uri: `https://coverartarchive.org/release/${currentRelease?.id}/front-250`, 188 + }} 189 + /> 190 + <View className="flex-1"> 191 + <Text className="font-bold text-sm">{result.title}</Text> 192 + <Text className="text-sm text-gray-600"> 193 + {result["artist-credit"]?.[0]?.artist?.name ?? "Unknown Artist"} 194 + </Text> 195 + 196 + {/* Release Selector Button */} 197 + {result.releases && result.releases?.length > 0 && ( 198 + <TouchableOpacity 199 + onPress={() => setShowReleaseModal(true)} 200 + className="p-1 bg-secondary/10 rounded-lg flex md:flex-row items-start md:gap-1" 201 + > 202 + <Text className="text-sm text-gray-500">Release:</Text> 203 + <Text className="text-sm" numberOfLines={1}> 204 + {currentRelease?.title} 205 + {currentRelease?.date ? ` (${currentRelease.date})` : ""} 206 + {currentRelease?.country ? ` - ${currentRelease.country}` : ""} 207 + </Text> 208 + </TouchableOpacity> 209 + )} 210 + </View> 211 + {/* Existing icons */} 212 + {/* <Link href={`https://musicbrainz.org/recording/${result.id}`}> 213 + <View className="bg-primary/40 rounded-full p-1"> 214 + <Icon icon={Brain} size={20} /> 215 + </View> 216 + </Link> */} 217 + {isSelected ? ( 218 + <View className="bg-primary rounded-full p-1"> 219 + <Icon icon={Check} size={20} /> 220 + </View> 221 + ) : ( 222 + <View className="border-2 border-secondary rounded-full p-3"></View> 223 + )} 224 + </View> 225 + 226 + {/* Release Selection Modal */} 227 + <Modal 228 + visible={showReleaseModal} 229 + transparent={true} 230 + animationType="slide" 231 + onRequestClose={() => setShowReleaseModal(false)} 232 + > 233 + <View className="flex-1 justify-end bg-black/50"> 234 + <View className="bg-background rounded-t-3xl"> 235 + <View className="p-4 border-b border-gray-200"> 236 + <Text className="text-lg font-bold text-center"> 237 + Select Release 238 + </Text> 239 + <TouchableOpacity 240 + className="absolute right-4 top-4" 241 + onPress={() => setShowReleaseModal(false)} 242 + > 243 + <Text className="text-primary">Done</Text> 244 + </TouchableOpacity> 245 + </View> 246 + 247 + <ScrollView className="max-h-[50vh]"> 248 + {result.releases?.map((release) => ( 249 + <TouchableOpacity 250 + key={release.id} 251 + className={`p-4 border-b border-gray-100 ${ 252 + selectedRelease?.id === release.id ? "bg-primary/10" : "" 253 + }`} 254 + onPress={() => { 255 + onReleaseSelect(result.id, release); 256 + setShowReleaseModal(false); 257 + }} 258 + > 259 + <Text className="font-medium">{release.title}</Text> 260 + <View className="flex-row gap-2"> 261 + {release.date && ( 262 + <Text className="text-sm text-gray-500"> 263 + {release.date} 264 + </Text> 265 + )} 266 + {release.country && ( 267 + <Text className="text-sm text-gray-500"> 268 + {release.country} 269 + </Text> 270 + )} 271 + {release.status && ( 272 + <Text className="text-sm text-gray-500"> 273 + {release.status} 274 + </Text> 275 + )} 276 + </View> 277 + {release.disambiguation && ( 278 + <Text className="text-sm text-gray-400 italic"> 279 + {release.disambiguation} 280 + </Text> 281 + )} 282 + </TouchableOpacity> 283 + ))} 284 + </ScrollView> 285 + </View> 286 + </View> 287 + </Modal> 288 + </TouchableOpacity> 289 + ); 290 + }
+1 -1
apps/amethyst/app/auth/login.tsx
··· 32 32 let redirUrl = await getLoginUrl(handle.replace("@", "")); 33 33 if (!redirUrl) { 34 34 // TODO: better error handling lulw 35 - throw new Error("Does not resolve to a DID"); 35 + throw new Error("Could not get login url. "); 36 36 } 37 37 setIsRedirecting(true); 38 38 if (Platform.OS === "web") {
-1
apps/amethyst/app/auth/options.tsx
··· 3 3 import { Text } from "../../components/ui/text"; 4 4 import { Button } from "../../components/ui/button"; 5 5 import React from "react"; 6 - import { FontAwesome6 } from "@expo/vector-icons"; 7 6 8 7 export default function AuthOptions() { 9 8 return (