···11-import { Link } from 'expo-router';
22-import * as WebBrowser from 'expo-web-browser';
33-import React from 'react';
44-import { Platform } from 'react-native';
11+import { Link } from "expo-router";
22+import * as WebBrowser from "expo-web-browser";
33+import React from "react";
44+import { Platform } from "react-native";
5566export function ExternalLink(
77- props: Omit<React.ComponentProps<typeof Link>, 'href'> & { href: string }
77+ props: Omit<React.ComponentProps<typeof Link>, "href"> & { href: string },
88) {
99 return (
1010 <Link
1111 target="_blank"
1212 {...props}
1313- // @ts-expect-error: External URLs are not typed.
1413 href={props.href}
1514 onPress={(e) => {
1616- if (Platform.OS !== 'web') {
1515+ if (Platform.OS !== "web") {
1716 // Prevent the default behavior of linking to the default browser on native.
1817 e.preventDefault();
1918 // Open the link in an in-app browser.
···11-import { View, Image, Text } from "react-native";
11+import { View, Image } from "react-native";
22+33+import { Text } from "@/components/ui/text";
2435export default function VerticalPlayView({
46 releaseMbid,
+14
apps/amethyst/components/toggleTheme.tsx
···11+import { useColorScheme } from "nativewind";
22+import { Text } from "@/components/ui/text";
33+44+export default function ToggleTheme() {
55+ const { colorScheme, setColorScheme } = useColorScheme();
66+77+ return (
88+ <Text
99+ onPress={() => setColorScheme(colorScheme === "light" ? "dark" : "light")}
1010+ >
1111+ {`The color scheme is ${colorScheme}`}
1212+ </Text>
1313+ );
1414+}
···11-export { useColorScheme } from 'react-native';
-8
apps/amethyst/components/useColorScheme.web.ts
···11-// NOTE: The default React Native styling doesn't support server rendering.
22-// Server rendered styles should not change between the first render of the HTML
33-// and the first render on the client. Typically, web developers will use CSS media queries
44-// to render different styles on the client and server, these aren't directly supported in React Native
55-// but can be achieved using a styling library like Nativewind.
66-export function useColorScheme() {
77- return 'light';
88-}
···1212>;
13131414export default function createOAuthClient(
1515- baseUrl: string
1515+ baseUrl: string,
1616+ pdsBaseUrl: string,
1617): AquareumOAuthClient {
1718 if (!baseUrl) {
1819 throw new Error("baseUrl is required");
···6061 };
6162 clientMetadataSchema.parse(meta);
6263 return new ReactNativeOAuthClient({
6363- handleResolver: "https://bsky.social", // backend instances should use a DNS based resolver
6464+ handleResolver: "https://" + pdsBaseUrl, // backend instances should use a DNS based resolver
6465 responseMode: "query", // or "fragment" (frontend only) or "form_post" (backend only)
65666667 // These must be the same metadata as the one exposed on the
+70-62
apps/amethyst/lib/atp/pid.ts
···5566// resolve pid
77export const isDid = (did: string) => {
88- // is this a did? regex
99- return did.match(/^did:[a-z]+:[\S\s]+/)
1010-}
88+ // is this a did? regex
99+ return did.match(/^did:[a-z]+:[\S\s]+/);
1010+};
11111212-export const resolveHandle = async (handle: string, resolverAppViewUrl: string = "https://public.api.bsky.app"): Promise<string> => {
1313- const url = resolverAppViewUrl + `/xrpc/com.atproto.identity.resolveHandle` + `?handle=${handle}`;
1212+export const resolveHandle = async (
1313+ handle: string,
1414+ resolverAppViewUrl: string = "https://public.api.bsky.app",
1515+): Promise<string> => {
1616+ const url =
1717+ resolverAppViewUrl +
1818+ `/xrpc/com.atproto.identity.resolveHandle` +
1919+ `?handle=${handle}`;
14201515- const response = await fetch(url);
1616- if (response.status === 400) {
1717- throw new Error(`domain handle not found`);
1818- } else if (!response.ok) {
1919- throw new Error(`directory is unreachable`);
2020- }
2121+ const response = await fetch(url);
2222+ if (response.status === 400) {
2323+ throw new Error(`domain handle not found`);
2424+ } else if (!response.ok) {
2525+ throw new Error(`directory is unreachable`);
2626+ }
21272222- const json = (await response.json())
2323- return json.did;
2828+ const json = await response.json();
2929+ return json.did;
2430};
25312626-2727-2832export const getDidDocument = async (did: string) => {
2929- const colon_index = did.indexOf(':', 4);
3333+ const colon_index = did.indexOf(":", 4);
30343131- const type = did.slice(4, colon_index);
3232- const ident = did.slice(colon_index + 1);
3535+ const type = did.slice(4, colon_index);
3636+ const ident = did.slice(colon_index + 1);
33373434- // get a did:plc
3535- if (type === 'plc') {
3636- const res = await fetch("https://plc.directory/" + did)
3838+ // get a did:plc
3939+ if (type === "plc") {
4040+ const res = await fetch("https://plc.directory/" + did);
37413838- if (res.status === 400) {
3939- throw new Error(`domain handle not found`);
4040- } else if (!res.ok) {
4141- throw new Error(`directory is unreachable`);
4242- }
4343-4444- const json = (await res.json())
4545- return json;
4646- } else if (type === "web") {
4747- if (ident.match(/^([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(?:\.[a-zA-Z]{2,}))$/)) {
4848- throw new Error(`invalid domain handle`);
4949- }
5050- const res = await fetch(`https://${ident}/.well-known/did.json`);
4242+ if (res.status === 400) {
4343+ throw new Error(`domain handle not found`);
4444+ } else if (!res.ok) {
4545+ throw new Error(`directory is unreachable`);
4646+ }
51475252- if (res.status === 400) {
5353- throw new Error(`domain handle not found`);
5454- }
5555- else if (!res.ok) {
5656- throw new Error(`directory is unreachable`);
5757- }
4848+ const json = await res.json();
4949+ return json;
5050+ } else if (type === "web") {
5151+ if (
5252+ !ident.match(/^([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(?:\.[a-zA-Z]{2,}))$/)
5353+ ) {
5454+ throw new Error(`invalid domain handle`);
5555+ }
5656+ const res = await fetch(`https://${ident}/.well-known/did.json`);
58575959- const json = await res.json();
6060- return json;
5858+ if (res.status === 400) {
5959+ throw new Error(`domain handle not found`);
6060+ } else if (!res.ok) {
6161+ throw new Error(`directory is unreachable`);
6162 }
6262-6363-6363+6464+ const json = await res.json();
6565+ return json;
6666+ }
6467};
65686666-export const resolveFromIdentity = async(identity: string, resolverAppViewUrl: string = "https://public.api.bsky.app") => {
6767- let did: string
6868- // is this a did? regex
6969- if (isDid(identity)) {
7070- did = identity
7171- } else {
7272- did = await resolveHandle(identity, resolverAppViewUrl)
7373- }
6969+export const resolveFromIdentity = async (
7070+ identity: string,
7171+ resolverAppViewUrl: string = "https://public.api.bsky.app",
7272+) => {
7373+ let did: string;
7474+ // is this a did? regex
7575+ if (isDid(identity)) {
7676+ did = identity;
7777+ } else {
7878+ did = await resolveHandle(identity, resolverAppViewUrl);
7979+ }
74807575- let doc = await getDidDocument(did)
7676- let pds = getPdsEndpoint(doc)
8181+ let doc = await getDidDocument(did);
8282+ let pds = getPdsEndpoint(doc);
77837878- if (!pds) {
7979- throw new Error("account doesn't have PDS endpoint?")
8080- }
8484+ if (!pds) {
8585+ throw new Error("account doesn't have PDS endpoint?");
8686+ }
81878282- return {
8383- did,doc,identity,
8484- pds: new URL(pds),
8585- }
8686-}8888+ return {
8989+ did,
9090+ doc,
9191+ identity,
9292+ pds: new URL(pds),
9393+ };
9494+};
+22-4
apps/amethyst/lib/oldStamp.tsx
···6060): Promise<MusicBrainzRecording[]> {
6161 try {
6262 const queryParts: string[] = [];
6363- if (searchParams.track)
6464- queryParts.push(`release title:"${searchParams.track}"`);
6565- if (searchParams.artist)
6666- queryParts.push(`AND artist:"${searchParams.artist}"`);
6363+ if (searchParams.track) {
6464+ queryParts.push(`title:"${searchParams.track}"`);
6565+ }
6666+6767+ if (searchParams.artist) {
6868+ queryParts.push(`artist:"${searchParams.artist}"`);
6969+ }
7070+7171+ if (searchParams.release) {
7272+ queryParts.push(`release:"${searchParams.release}"`);
7373+ }
7474+67756876 const query = queryParts.join(" AND ");
6977···7179 `https://musicbrainz.org/ws/2/recording?query=${encodeURIComponent(
7280 query,
7381 )}&fmt=json`,
8282+ {
8383+ headers: {
8484+ "User-Agent": "tealtracker/0.0.1",
8585+ },
8686+ },
7487 );
8888+8989+ if (!res.ok) {
9090+ throw new Error(`MusicBrainz API returned ${res.status}`);
9191+ }
9292+7593 const data = await res.json();
7694 return data.recordings || [];
7795 } catch (error) {
···1414 if cf_pages_url.startswith('https://'):
1515 cf_pages_url = cf_pages_url[8:]
16161717+ if os.environ.get('CF_PAGES_BRANCH') == 'main':
1818+ # trim pages url if we are building for prod
1919+ # TODO: remove this once we have a non-pages-dev url
2020+ cf_pages_url.split('.')[1:]
2121+1722 # Path to metadata file
1823 metadata_path_pre = 'assets/client-metadata.json'
1924 metadata_path = 'dist/client-metadata.json'