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

things people didn't ask for

- added titles to the `stamp/submit` and `stamp/success` routes
- added a chevron to the releases dropdown (I didn't know you could click on it as a dropdown so I feel like thats a good hint)
- oauth wasn't working for me so i fixed it lol

authored by mmatt.net and committed by

Natalie B. 95c64815 01581828

+57 -42
+30 -24
apps/amethyst/app/(tabs)/(stamp)/stamp/index.tsx
··· 1 + import { Button } from "@/components/ui/button"; 2 + import { Icon } from "@/lib/icons/iconWithClassName"; 3 + import { Stack, useRouter } from "expo-router"; 4 + import { Check, ChevronDown, ChevronRight } from "lucide-react-native"; 1 5 import React, { useState } from "react"; 2 6 import { 7 + FlatList, 8 + Image, 9 + Modal, 10 + ScrollView, 11 + Text, 12 + TextInput, 13 + TouchableOpacity, 14 + View, 15 + } from "react-native"; 16 + import { 3 17 MusicBrainzRecording, 4 18 ReleaseSelections, 5 19 searchMusicbrainz, 6 20 SearchParams, 7 21 SearchResultProps, 8 22 } from "../../../../lib/oldStamp"; 9 - import { 10 - ScrollView, 11 - TextInput, 12 - View, 13 - Text, 14 - TouchableOpacity, 15 - Image, 16 - Modal, 17 - } from "react-native"; 18 - import { Stack, useRouter } from "expo-router"; 19 - import { Button } from "@/components/ui/button"; 20 - import { FlatList } from "react-native"; 21 - import { Check, ChevronRight } from "lucide-react-native"; 22 - import { Icon } from "@/lib/icons/iconWithClassName"; 23 23 24 24 export default function StepOne() { 25 25 const router = useRouter(); ··· 32 32 release: "", 33 33 }); 34 34 const [searchResults, setSearchResults] = useState<MusicBrainzRecording[]>( 35 - [], 35 + [] 36 36 ); 37 37 const [isLoading, setIsLoading] = useState<boolean>(false); 38 38 const [releaseSelections, setReleaseSelections] = useState<ReleaseSelections>( 39 - {}, 39 + {} 40 40 ); 41 41 42 42 const handleSearch = async (): Promise<void> => { ··· 173 173 : { 174 174 ...result, 175 175 selectedRelease: currentRelease, // Pass the selected release with the track 176 - }, 176 + } 177 177 ); 178 178 }} 179 179 className={`p-4 mb-2 rounded-lg ${ ··· 197 197 {result.releases && result.releases?.length > 0 && ( 198 198 <TouchableOpacity 199 199 onPress={() => setShowReleaseModal(true)} 200 - className="p-1 bg-secondary/10 rounded-lg flex md:flex-row items-start md:gap-1" 200 + className="p-1 bg-secondary/10 rounded-lg flex md:flex-row items-start md:items-center justify-between md:gap-1" 201 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> 202 + <View className="flex md:flex-row items-start gap-1"> 203 + <Text className="text-sm text-gray-500">Release:</Text> 204 + <Text className="text-sm" numberOfLines={1}> 205 + {currentRelease?.title} 206 + {currentRelease?.date ? ` (${currentRelease.date})` : ""} 207 + {currentRelease?.country 208 + ? ` - ${currentRelease.country}` 209 + : ""} 210 + </Text> 211 + </View> 212 + {/* the chevron looks odd in the other layout so I'm just hiding it for now. -mm */} 213 + <ChevronDown className="md:ml-1 md:block hidden w-6 h-6" /> 208 214 </TouchableOpacity> 209 215 )} 210 216 </View>
+18 -14
apps/amethyst/app/(tabs)/(stamp)/stamp/submit.tsx
··· 1 - import { useLocalSearchParams, useRouter } from "expo-router"; 2 - import { View, Text } from "react-native"; 3 - import { 4 - MusicBrainzRecording, 5 - PlaySubmittedData, 6 - } from "../../../../lib/oldStamp"; 1 + import VerticalPlayView from "@/components/play/verticalPlayView"; 2 + import { Button } from "@/components/ui/button"; 3 + import { useStore } from "@/stores/mainStore"; 4 + import { ComAtprotoRepoCreateRecord } from "@atproto/api"; 7 5 import { 8 - validateRecord, 9 6 Record as PlayRecord, 7 + validateRecord, 10 8 } from "@teal/lexicons/src/types/fm/teal/alpha/feed/play"; 11 - import { Button } from "@/components/ui/button"; 9 + import { Stack, useLocalSearchParams, useRouter } from "expo-router"; 12 10 import { useState } from "react"; 13 - import VerticalPlayView from "@/components/play/verticalPlayView"; 14 - import { Switch } from "react-native"; 15 - import { useStore } from "@/stores/mainStore"; 16 - import { ComAtprotoRepoCreateRecord } from "@atproto/api"; 11 + import { Switch, Text, View } from "react-native"; 12 + import { 13 + MusicBrainzRecording, 14 + PlaySubmittedData, 15 + } from "../../../../lib/oldStamp"; 17 16 18 17 const createPlayRecord = (result: MusicBrainzRecording): PlayRecord => { 19 18 let artistNames: string[] = []; ··· 48 47 const { track } = useLocalSearchParams(); 49 48 50 49 const selectedTrack: MusicBrainzRecording | null = JSON.parse( 51 - track as string, 50 + track as string 52 51 ); 53 52 54 53 const [isSubmitting, setIsSubmitting] = useState<boolean>(false); ··· 75 74 collection: "fm.teal.alpha.feed.play", 76 75 rkey: undefined, 77 76 record, 78 - }, 77 + } 79 78 ); 80 79 if (!res || res.success === false) { 81 80 throw new Error("Failed to submit play!"); ··· 99 98 100 99 return ( 101 100 <View className="flex-1 p-4 bg-background items-center h-screen-safe"> 101 + <Stack.Screen 102 + options={{ 103 + title: "Submit Stamp", 104 + }} 105 + /> 102 106 <View className="flex justify-between align-middle gap-4 max-w-screen-md w-screen min-h-full px-4"> 103 107 <Text className="font-bold text-lg">Submit Play</Text> 104 108 <VerticalPlayView
+7 -2
apps/amethyst/app/(tabs)/(stamp)/stamp/success.tsx
··· 1 1 import { ExternalLink } from "@/components/ExternalLink"; 2 2 import { PlaySubmittedData } from "@/lib/oldStamp"; 3 - import { useLocalSearchParams } from "expo-router"; 3 + import { Stack, useLocalSearchParams } from "expo-router"; 4 4 import { Check, ExternalLinkIcon } from "lucide-react-native"; 5 - import { View, Text } from "react-native"; 5 + import { Text, View } from "react-native"; 6 6 7 7 export default function StepThree() { 8 8 const { submittedData } = useLocalSearchParams(); 9 9 const responseData: PlaySubmittedData = JSON.parse(submittedData as string); 10 10 return ( 11 11 <View className="flex-1 p-4 bg-background items-center h-screen-safe"> 12 + <Stack.Screen 13 + options={{ 14 + title: "Play Successfully Submitted", 15 + }} 16 + /> 12 17 <View className="flex justify-center items-center gap-2 max-w-screen-md w-screen min-h-full px-4"> 13 18 <Check size={48} className="text-green-600 dark:text-green-400" /> 14 19 <Text className="text-xl">Play Submitted!</Text>
+2 -2
apps/amethyst/lib/atp/oauth.tsx
··· 12 12 >; 13 13 14 14 export default function createOAuthClient( 15 - baseUrl: string, 15 + baseUrl: string 16 16 ): AquareumOAuthClient { 17 17 if (!baseUrl) { 18 18 throw new Error("baseUrl is required"); ··· 44 44 console.log("Our client base uri is ", hostname); 45 45 meta = { 46 46 client_id: 47 - hostname === "localhost" 47 + hostname === "127.0.0.1" 48 48 ? `http://localhost?${queryParams.toString()}` 49 49 : `https://${hostname}/client-metadata.json`, 50 50 redirect_uris: [redirect as any],