a tool for shared writing and social publishing
0

Configure Feed

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

pretty up the subscribe mode button

celine (May 8, 2026, 4:44 PM EDT) 0aef66ca 51ff9e11

+77 -70
+1 -1
app/lish/[did]/[publication]/UpgradeModal.tsx
··· 98 98 <div 99 99 className={`${props.compact ? `text-sm` : "text-base"} leading-snug text-tertiary pt-2`} 100 100 > 101 - Analytics for all your pubs! <br /> Emails and membership coming soon. 101 + Analytics and Emails for all your pubs! <br /> Membership coming soon. 102 102 </div> 103 103 </div> 104 104 );
+5 -6
components/Icons/EmailTiny.tsx
··· 1 1 import { Props } from "./Props"; 2 + 2 3 export const EmailTiny = (props: Props) => { 3 4 return ( 4 5 <svg 5 - width="24" 6 - height="24" 7 - viewBox="0 0 24 24" 6 + width="16" 7 + height="16" 8 + viewBox="0 0 16 16" 8 9 fill="none" 9 10 xmlns="http://www.w3.org/2000/svg" 10 11 {...props} 11 12 > 12 13 <path 13 - fillRule="evenodd" 14 - clipRule="evenodd" 15 - d="M8.26788 2.26052C8.76343 1.73099 9.53423 1.56198 10.2059 1.83559L11.7491 2.46429L16.6025 2.20414C17.4868 2.15673 18.2463 2.82619 18.3102 3.7095L18.4144 5.14935L20.0677 6.01329C20.6659 6.32586 21.0921 6.89045 21.229 7.55135L23.019 16.1987C23.2697 17.4099 22.4958 18.5964 21.2863 18.8551L5.44248 22.2436C4.22165 22.5047 3.02167 21.7219 2.76862 20.4994L0.980561 11.8611C0.845583 11.209 1.0064 10.5306 1.41972 10.0085L3.56607 7.29716L3.40145 4.5493C3.34755 3.64946 4.0364 2.8777 4.93657 2.82945L7.88327 2.6715L8.26788 2.26052ZM18.6163 7.93856L18.5417 6.90833L19.373 7.34272C19.5724 7.44691 19.7145 7.63511 19.7601 7.85541L21.5501 16.5028C21.6337 16.9065 21.3757 17.302 20.9725 17.3882L5.12877 20.7767C4.72183 20.8638 4.32183 20.6028 4.23748 20.1953L2.44942 11.557C2.40443 11.3397 2.45804 11.1135 2.59581 10.9395L3.70066 9.54382L3.80512 11.2874L3.64416 11.2345C3.31624 11.1267 2.96303 11.3051 2.85524 11.6331C2.74745 11.961 2.9259 12.3142 3.25381 12.422L12.2571 15.3814L19.2176 8.99821C19.472 8.76491 19.4891 8.36956 19.2558 8.11516C19.0886 7.93292 18.8384 7.87245 18.6163 7.93856ZM17.4353 8.93663L17.0635 3.79974C17.0487 3.5959 16.8735 3.4414 16.6694 3.45234L5.00347 4.07766C4.79574 4.0888 4.63678 4.26689 4.64922 4.47455L5.08251 11.7073L11.9518 13.9653L17.4353 8.93663Z" 14 + d="M10.2968 1.38656C11.8028 0.80632 13.494 1.56199 14.0663 3.07099L15.6289 7.19072C16.1004 8.43493 15.5479 9.48369 14.9401 10.5127C14.704 10.9123 14.3405 11.2228 13.9088 11.3933L5.77871 14.6014C4.27234 15.182 2.58108 14.4256 2.00909 12.916L0.376959 8.79207C-0.311673 7.05234 0.893538 5.01138 2.51263 4.38692L10.2968 1.38656ZM12.7699 3.56314C12.4701 2.77272 11.5834 2.37608 10.7946 2.68007L3.01153 5.68126C2.22925 5.98309 1.8366 6.85946 2.1335 7.64358L3.6961 11.7633C3.99584 12.5538 4.88156 12.9504 5.67038 12.6465L13.4554 9.64503C14.2374 9.34305 14.6292 8.46681 14.3325 7.68286L12.7699 3.56314ZM11.6941 3.08949C11.975 3.12161 12.1765 3.37631 12.1446 3.65718C12.1068 3.9877 11.9114 4.67708 11.654 5.39907C11.3917 6.13504 11.0397 6.97653 10.6613 7.61556C10.1369 8.50088 9.10711 8.5289 8.36595 8.23365C7.26493 7.79507 5.99091 7.3727 4.38605 7.11939C4.1821 7.08727 4.00762 7.10478 3.88416 7.15439C3.77387 7.19877 3.69513 7.27044 3.64773 7.39444C3.54663 7.65849 3.25068 7.79062 2.98654 7.68966C2.72237 7.58865 2.59036 7.29267 2.69131 7.02848C3.02311 6.16072 3.90148 6.00603 4.54572 6.10756C6.24683 6.37607 7.59633 6.82477 8.74486 7.28228C9.24839 7.48286 9.622 7.36111 9.7805 7.0938C10.1105 6.53641 10.436 5.76436 10.6889 5.05475C10.9465 4.33209 11.1045 3.74232 11.1275 3.54089C11.1597 3.26006 11.4133 3.05764 11.6941 3.08949Z" 16 15 fill="currentColor" 17 16 /> 18 17 </svg>
+1 -1
components/Icons/PopoverArrow.tsx
··· 11 11 height="8" 12 12 viewBox="0 0 16 8" 13 13 fill="none" 14 - className="" 14 + className="-mt-[1px]" 15 15 xmlns="http://www.w3.org/2000/svg" 16 16 > 17 17 <path
+1 -1
components/IdentityProvider.tsx
··· 10 10 dashboards: { [id: string]: DashboardState | undefined }; 11 11 }; 12 12 export type Identity = Awaited<ReturnType<typeof getIdentityData>>; 13 - let IdentityContext = createContext({ 13 + export let IdentityContext = createContext({ 14 14 identity: null as Identity, 15 15 mutate: (() => {}) as KeyedMutator<Identity>, 16 16 });
+1
components/Menu.tsx
··· 81 81 }) => { 82 82 return ( 83 83 <DropdownMenu.RadioGroup 84 + className="flex flex-col gap-0.5" 84 85 value={props.value} 85 86 onValueChange={props.onValueChange} 86 87 >
+2 -4
components/Subscribe/HandleSubscribe.tsx
··· 37 37 { name: "Graze", logo: "/logos/graze.svg" }, 38 38 ]; 39 39 40 - import { Separator } from "components/Layout"; 41 - 42 40 export const SubscribeWithHandle = (props: { 43 41 autoFocus?: boolean; 44 42 publicationUri: string; ··· 79 77 80 78 if (props.user.loggedIn && props.user.handle) { 81 79 return ( 82 - <div className="flex flex-col gap-2"> 80 + <div className="flex flex-col gap-2 w-max mx-auto"> 83 81 <div className="flex items-center gap-2"> 84 - {props.leading} 85 82 <ButtonPrimary 86 83 className="mx-auto max-w-full grow" 87 84 disabled={subscribing} ··· 117 114 <DotLoader /> 118 115 ) : ( 119 116 <> 117 + {props.leading} 120 118 <span className="shrink-0">Subscribe as</span> 121 119 <span className="flex gap-1 items-center max-w-full grow min-w-0"> 122 120 <Avatar
+66 -57
components/Subscribe/SubscribeButton.tsx
··· 19 19 } from "actions/publications/subscribeEmail"; 20 20 21 21 import { useViewerSubscription } from "./viewerSubscription"; 22 + import { Separator } from "components/Layout"; 23 + import { ArrowDownTiny } from "components/Icons/ArrowDownTiny"; 22 24 23 25 type SubscribeMode = "email" | "atproto"; 24 26 ··· 276 278 }} 277 279 > 278 280 {confirmState === "success" ? ( 279 - <EmailSubscribeSuccess email={email} handle={user.handle} /> 280 - ) : ( 281 - <EmailConfirm 282 - autoFocus 283 - loading={confirming} 284 - onBack={() => setConfirmOpen(false)} 285 - emailValue={email} 286 - onSubmit={async (code) => { 287 - if (confirming) return; 288 - setConfirming(true); 289 - let res = await confirmPublicationEmailSubscription( 290 - props.publicationUri, 291 - email, 292 - code, 293 - linkToCurrent, 294 - ); 295 - setConfirming(false); 296 - if (!res.ok) { 297 - toaster({ 298 - type: "error", 299 - content: ERROR_MESSAGES[res.error], 300 - }); 301 - return; 302 - } 303 - setConfirmState("success"); 304 - router.refresh(); 305 - }} 306 - /> 307 - )} 281 + <EmailSubscribeSuccess email={email} handle={user.handle} /> 282 + ) : ( 283 + <EmailConfirm 284 + autoFocus 285 + loading={confirming} 286 + onBack={() => setConfirmOpen(false)} 287 + emailValue={email} 288 + onSubmit={async (code) => { 289 + if (confirming) return; 290 + setConfirming(true); 291 + let res = await confirmPublicationEmailSubscription( 292 + props.publicationUri, 293 + email, 294 + code, 295 + linkToCurrent, 296 + ); 297 + setConfirming(false); 298 + if (!res.ok) { 299 + toaster({ 300 + type: "error", 301 + content: ERROR_MESSAGES[res.error], 302 + }); 303 + return; 304 + } 305 + setConfirmState("success"); 306 + router.refresh(); 307 + }} 308 + /> 309 + )} 308 310 </Modal> 309 311 </> 310 312 )} ··· 317 319 onChange: (mode: SubscribeMode) => void; 318 320 }) => { 319 321 return ( 320 - <Menu 321 - align="start" 322 - asChild 323 - trigger={ 324 - <button 325 - type="button" 326 - aria-label="Choose subscribe method" 327 - className="text-tertiary hover:text-accent-contrast flex items-center" 328 - > 329 - {props.mode === "email" ? <EmailTiny /> : <AtmosphereAccount />} 330 - </button> 331 - } 332 - > 333 - <RadioMenuGroup 334 - value={props.mode} 335 - onValueChange={(v) => props.onChange(v as SubscribeMode)} 322 + <div className="flex gap-1"> 323 + <Menu 324 + align="start" 325 + asChild 326 + trigger={ 327 + <button 328 + type="button" 329 + aria-label="Choose subscribe method" 330 + className="text-inherit flex items-center gap-1" 331 + > 332 + {props.mode === "email" ? <EmailTiny /> : <AtmosphereAccount />} 333 + <ArrowDownTiny className="scale-90" /> 334 + </button> 335 + } 336 336 > 337 - <RadioMenuItem value="email" selected={props.mode === "email"}> 338 - <span className="flex items-center gap-2"> 339 - <EmailTiny /> Email 340 - </span> 341 - </RadioMenuItem> 342 - <RadioMenuItem value="atproto" selected={props.mode === "atproto"}> 343 - <span className="flex items-center gap-2"> 344 - <AtmosphereAccount /> Atmosphere handle 345 - </span> 346 - </RadioMenuItem> 347 - </RadioMenuGroup> 348 - </Menu> 337 + <div className="text-tertiary text-sm px-1 pt-0.5 -mb-0.5"> 338 + Subscribe via… 339 + </div> 340 + <RadioMenuGroup 341 + value={props.mode} 342 + onValueChange={(v) => props.onChange(v as SubscribeMode)} 343 + > 344 + <RadioMenuItem value="email" selected={props.mode === "email"}> 345 + <span className="flex items-center gap-2 shrink-0"> 346 + <EmailTiny /> Email 347 + </span> 348 + </RadioMenuItem> 349 + <RadioMenuItem value="atproto" selected={props.mode === "atproto"}> 350 + <span className="flex items-center gap-2"> 351 + <AtmosphereAccount /> Atmosphere 352 + </span> 353 + </RadioMenuItem> 354 + </RadioMenuGroup> 355 + </Menu> 356 + <Separator classname="h-5! " /> 357 + </div> 349 358 ); 350 359 }; 351 360