a tool for shared writing and social publishing
0

Configure Feed

Select the types of activity you want to include in your feed.

small fixes

Jared Pereira (Apr 15, 2026, 11:57 PM EDT) 6f73f5b0 3e7901bd

+34 -90
-1
app/globals.css
··· 621 621 overflow: hidden; 622 622 transition: 623 623 max-height 200ms ease, 624 - max-image 200ms ease, 625 624 opacity 200ms ease; 626 625 background: rgba(var(--bg-page), var(--bg-page-alpha)); 627 626 border-radius: 4px;
-1
components/Combobox.tsx
··· 24 24 trigger?: React.ReactNode; 25 25 triggerClassName?: string; 26 26 className?: string; 27 - width?: string; 28 27 results: string[]; 29 28 onSelect?: () => void; 30 29 onOpenChange?: (open: boolean) => void;
+28 -9
components/LoginButton.tsx
··· 40 40 noEmailLogin={props.noEmailLogin} 41 41 redirectRoute={props.redirectRoute} 42 42 open={open} 43 + onSuccess={() => setOpen(false)} 43 44 /> 44 45 </Modal> 45 46 ); ··· 50 51 noEmailLogin?: boolean; 51 52 redirectRoute?: string; 52 53 open?: boolean; 54 + onSuccess?: () => void; 53 55 }) => { 54 56 let identityData = useIdentityData(); 55 - if (identityData.identity) return null; 56 57 let [state, setState] = useState< 57 58 "log in" | "sign up" | "email log in" | "email confirm" 58 59 >("log in"); ··· 61 62 let [loading, setLoading] = useState(false); 62 63 let toaster = useToaster(); 63 64 65 + if (identityData.identity) return null; 66 + 64 67 const handleEmailSubmit = async () => { 65 68 setLoading(true); 66 - const id = await requestAuthEmailToken(loginEmail); 67 - setLoading(false); 68 - setTokenId(id); 69 - setState("email confirm"); 69 + try { 70 + const id = await requestAuthEmailToken(loginEmail); 71 + setTokenId(id); 72 + setState("email confirm"); 73 + } catch (e) { 74 + toaster({ 75 + content: ( 76 + <div className="font-bold">Could not send email — please try again</div> 77 + ), 78 + type: "error", 79 + }); 80 + } finally { 81 + setLoading(false); 82 + } 70 83 }; 71 84 72 85 const handleCodeSubmit = async (code: string) => { ··· 79 92 content: <div className="font-bold">Incorrect code!</div>, 80 93 type: "error", 81 94 }); 82 - } else { 83 - const localLeaflets = getHomeDocs(); 84 - await loginWithEmailToken(localLeaflets.filter((l) => !l.hidden)); 85 - mutate("identity"); 95 + return; 86 96 } 97 + const localLeaflets = getHomeDocs(); 98 + await loginWithEmailToken(localLeaflets.filter((l) => !l.hidden)); 99 + mutate("identity"); 100 + toaster({ 101 + content: <div className="font-bold">Logged in! Welcome!</div>, 102 + type: "success", 103 + }); 104 + setLoading(false); 105 + props.onSuccess?.(); 87 106 }; 88 107 89 108 return (
-75
app/login/LoginForm.tsx
··· 1 - "use client"; 2 - import { 3 - confirmEmailAuthToken, 4 - requestAuthEmailToken, 5 - } from "actions/emailAuth"; 6 - import { loginWithEmailToken } from "actions/login"; 7 - import { ActionAfterSignIn } from "app/api/oauth/[route]/afterSignInActions"; 8 - import { getHomeDocs } from "app/(home-pages)/home/storage"; 9 - import { ButtonPrimary } from "components/Buttons"; 10 - import { ArrowRightTiny } from "components/Icons/ArrowRightTiny"; 11 - import { BlueskySmall } from "components/Icons/BlueskySmall"; 12 - import { Input } from "components/Input"; 13 - import { useSmoker, useToaster } from "components/Toast"; 14 - import React, { useState } from "react"; 15 - import { mutate } from "swr"; 16 - import { BlueskyTiny } from "components/Icons/BlueskyTiny"; 17 - 18 - export function BlueskyLogin(props: { 19 - redirectRoute?: string; 20 - action?: ActionAfterSignIn; 21 - compact?: boolean; 22 - }) { 23 - const [signingWithHandle, setSigningWithHandle] = useState(false); 24 - const [handle, setHandle] = useState(""); 25 - 26 - return ( 27 - <form action={`/api/oauth/login`} method="GET"> 28 - <input 29 - type="hidden" 30 - name="redirect_url" 31 - value={props.redirectRoute || "/"} 32 - /> 33 - {props.action && ( 34 - <input 35 - type="hidden" 36 - name="action" 37 - value={JSON.stringify(props.action)} 38 - /> 39 - )} 40 - {signingWithHandle ? ( 41 - <div className="w-full flex gap-1"> 42 - <Input 43 - type="text" 44 - name="handle" 45 - id="handle" 46 - placeholder="you.bsky.social" 47 - value={handle} 48 - className="input-with-border" 49 - onChange={(e) => setHandle(e.target.value)} 50 - required 51 - /> 52 - <ButtonPrimary type="submit">Sign In</ButtonPrimary> 53 - </div> 54 - ) : ( 55 - <div className="flex flex-col justify-center"> 56 - <ButtonPrimary 57 - fullWidth={!props.compact} 58 - compact={props.compact} 59 - className={`${props.compact ? "mx-auto text-sm" : "py-2"}`} 60 - > 61 - {props.compact ? <BlueskyTiny /> : <BlueskySmall />} 62 - {props.compact ? "Link" : "Log In/Sign Up with"} Bluesky 63 - </ButtonPrimary> 64 - <button 65 - type="button" 66 - className={`${props.compact ? "text-xs mt-0.5" : "text-sm mt-[6px]"} text-accent-contrast place-self-center`} 67 - onClick={() => setSigningWithHandle(true)} 68 - > 69 - use an ATProto handle 70 - </button> 71 - </div> 72 - )} 73 - </form> 74 - ); 75 - }
-1
components/ActionBar/Publications.tsx
··· 14 14 import { SpeedyLink } from "components/SpeedyLink"; 15 15 import { PublishSmall } from "components/Icons/PublishSmall"; 16 16 import { Popover } from "components/Popover"; 17 - import { BlueskyLogin } from "app/login/LoginForm"; 18 17 import { ButtonPrimary, ButtonSecondary } from "components/Buttons"; 19 18 import { useIsMobile } from "src/hooks/isMobile"; 20 19 import { useState } from "react";
+4 -1
components/Subscribe/HandleInput.tsx
··· 2 2 import { Agent } from "@atproto/api"; 3 3 import { Input } from "components/Input"; 4 4 import { Combobox, ComboboxResult } from "components/Combobox"; 5 - import { useState } from "react"; 5 + import { useRef, useState } from "react"; 6 6 import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 7 7 import { DotLoader } from "components/utils/DotLoader"; 8 8 import { theme } from "tailwind.config"; ··· 27 27 let [suggestions, setSuggestions] = useState<ActorSuggestion[]>([]); 28 28 let [dropdownOpen, setDropdownOpen] = useState(false); 29 29 let [highlighted, setHighlighted] = useState<string | undefined>(undefined); 30 + let requestIdRef = useRef(0); 30 31 31 32 useDebouncedEffect( 32 33 async () => { ··· 35 36 setDropdownOpen(false); 36 37 return; 37 38 } 39 + const requestId = ++requestIdRef.current; 38 40 const agent = new Agent("https://public.api.bsky.app"); 39 41 const result = await agent.searchActorsTypeahead({ 40 42 q: handleValue, 41 43 limit: 8, 42 44 }); 45 + if (requestId !== requestIdRef.current) return; 43 46 const actors = result.data.actors.map((actor) => ({ 44 47 handle: actor.handle, 45 48 did: actor.did,
+2 -2
app/lish/[did]/[publication]/[rkey]/PostPubInfo.tsx
··· 4 4 newsletterMode: true, 5 5 user: { 6 6 loggedIn: true, 7 - email: "thisiscelinepark@gmail.com", 8 - handle: "cozylittle.house", 7 + email: "example@example.com", 8 + handle: "example.handle", 9 9 subscribed: false, 10 10 }, 11 11 };