diff --git a/app/(home-pages)/p/[didOrHandle]/PubListing.tsx b/app/(home-pages)/p/[didOrHandle]/PubListing.tsx
index e95e215e..e948a047 100644
--- a/app/(home-pages)/p/[didOrHandle]/PubListing.tsx
+++ b/app/(home-pages)/p/[didOrHandle]/PubListing.tsx
@@ -8,7 +8,20 @@ import { BaseThemeProvider } from "components/ThemeManager/ThemeProvider";
import { blobRefToSrc } from "src/utils/blobRefToSrc";
import { timeAgo } from "src/utils/timeAgo";
-export const PubListing = (props: PublicationSubscription) => {
+type PubListingProps = Omit<
+ PublicationSubscription,
+ | "publication_subscriptions"
+ | "publication_newsletter_settings"
+ | "documents_in_publications"
+> & {
+ publication_subscriptions?: PublicationSubscription["publication_subscriptions"];
+ publication_newsletter_settings?: PublicationSubscription["publication_newsletter_settings"];
+ documents_in_publications?: PublicationSubscription["documents_in_publications"];
+ showSubscribeButton?: boolean;
+ constrainHeight?: boolean;
+};
+
+export const PubListing = (props: PubListingProps) => {
let record = props.record;
let theme = usePubTheme(record?.theme);
let backgroundImage = record?.theme?.backgroundImage?.image?.ref
@@ -39,15 +52,21 @@ export const PubListing = (props: PublicationSubscription) => {
>
-
{record.name}
+
+ {record.name}
+
{record.description && (
-
+
{record.description}
)}
@@ -55,26 +74,29 @@ export const PubListing = (props: PublicationSubscription) => {
{props.authorProfile?.handle}
-
- Updated{" "}
- {timeAgo(
- props.documents_in_publications?.[0]?.documents?.sort_date ||
- "",
- )}
-
-
-
-
+ {props.documents_in_publications?.[0]?.documents?.sort_date && (
+
+ Updated{" "}
+ {timeAgo(
+ props.documents_in_publications[0].documents.sort_date,
+ )}
+
+ )}
+ {props.showSubscribeButton && (
+
+
+
+ )}
diff --git a/app/(home-pages)/p/[didOrHandle]/subscriptions/SubscriptionsContent.tsx b/app/(home-pages)/p/[didOrHandle]/subscriptions/SubscriptionsContent.tsx
index 917f2dc4..941edcde 100644
--- a/app/(home-pages)/p/[didOrHandle]/subscriptions/SubscriptionsContent.tsx
+++ b/app/(home-pages)/p/[didOrHandle]/subscriptions/SubscriptionsContent.tsx
@@ -83,7 +83,12 @@ export const ProfileSubscriptionsContent = (props: {
{allSubscriptions.map((sub) => (
-
+
))}
{/* Trigger element for loading more subscriptions */}
diff --git a/app/about/Examples.tsx b/app/about/Examples.tsx
index 8d1cb55a..a5795cce 100644
--- a/app/about/Examples.tsx
+++ b/app/about/Examples.tsx
@@ -1,24 +1,84 @@
import { GoToArrow } from "components/Icons/GoToArrow";
+import { supabaseServerClient } from "supabase/serverClient";
+import { normalizePublicationRecord } from "src/utils/normalizeRecords";
+import { PubListing } from "app/(home-pages)/p/[didOrHandle]/PubListing";
+import { idResolver } from "app/(home-pages)/reader/idResolver";
+
+const pubs = [
+ "at://did:plc:e27b62phtzr3sjgs3zwdbctq/site.standard.publication/3lyevoglvs224", // zjsm.fm
+ "at://did:plc:b2p6rujcgpenbtcjposmjuc3/site.standard.publication/3m3axfv5hms24", // Cosmik Labs
+ "at://did:plc:x2xmijn2egk5g67u3cwkddzy/site.standard.publication/3m2avdoogvs2f", // Aaron Ross Powell's Blog
+ "at://did:plc:qsn6ihzsby2lr2r3sl4z3bgf/site.standard.publication/3lunssrkwqc27", // Eva's Garden
+ "at://did:plc:oyvx5qyivlj3s6fnbxpnadba/site.standard.publication/3m3bgolkrvs2c", // Cat Café
+ "at://did:plc:bsxmru6yec5sy42azm7cnrqq/site.standard.publication/3lwk77r5y422r", // leaf litter
+ "at://did:plc:2zmxikig2sj7gqaezl5gntae/site.standard.publication/3lusxwydhqs2i", // How Streamplace Works
+ "at://did:plc:jl5dgp7xb34xfqlpywt7kcp5/site.standard.publication/3lx4aluuanc27", // hyper girl shares hyperlinks
+
+ "at://did:plc:7kjkqt7amna7pih3lcvpf7g2/site.standard.publication/3lptkwbkdws2l", // Dreaming at the Edge of the Apocalypse
+ "at://did:plc:lulmyldiq4sb2ikags5sfb25/site.standard.publication/3lw5apjilmc2v", // microcosm
+ "at://did:plc:mdjhvva6vlrswsj26cftjttd/site.standard.publication/3lwyzotjqu22i", // Connected Places
+];
+
+async function getExamplePub(uri: string) {
+ const { data: publications } = await supabaseServerClient
+ .from("publications")
+ .select("*")
+ .eq("uri", uri)
+ .limit(1);
+ const publication = publications?.[0];
+ if (!publication) return null;
+ const record = normalizePublicationRecord(publication.record);
+ if (!record) return null;
+ const id = await idResolver.did.resolve(publication.identity_did);
+ const authorProfile = id?.alsoKnownAs?.[0]
+ ? { handle: `@${id.alsoKnownAs[0].slice(5)}` }
+ : undefined;
+ return { uri: publication.uri, record, authorProfile };
+}
+
+export const Examples = async () => {
+ const examplePubs = (await Promise.all(pubs.map(getExamplePub))).filter(
+ (pub): pub is NonNullable
=> pub !== null,
+ );
+
+ const pubsWithMargins = examplePubs.map((pub) => ({
+ ...pub,
+ topMargin: `${Math.floor(Math.random() * 6) * 10}px`,
+ }));
+
+ const renderPub = (
+ pub: (typeof pubsWithMargins)[number],
+ keySuffix: string,
+ ) => (
+
+ );
-export const Examples = () => {
return (
-
+
Loved by thousands of writers
-
-
-
-
-
-
{" "}
-
-
-
-
-
+
+
+ {pubsWithMargins.map((pub) => renderPub(pub, "orig"))}
+ {pubsWithMargins.map((pub) => renderPub(pub, "dup"))}
+
);
diff --git a/app/about/Features.tsx b/app/about/Features.tsx
index c27c806f..15f52371 100644
--- a/app/about/Features.tsx
+++ b/app/about/Features.tsx
@@ -4,9 +4,10 @@ export const Features = () => {
return (
<>
-
-
-
+
+ {" "}
+
+
Reach your readers
Send updates via email and on the Atmosphere, a social ecosystem
@@ -14,28 +15,26 @@ export const Features = () => {
-
-
+
+
Express yourself
Custom themes and domains
-
+
-
+
-
Write and read together
+
Write together
- with collaborative editing
+ Collaborative editing
and easy sharing{" "}
-
+
Build your community
-
- Plan events, start conversations, and share everything everywhere
-
+
Events, conversations, and easy sharing
>
diff --git a/app/about/Footer.tsx b/app/about/Footer.tsx
index 9cd2a6ab..15842157 100644
--- a/app/about/Footer.tsx
+++ b/app/about/Footer.tsx
@@ -1,7 +1,7 @@
export const Footer = () => {
return (
-
+
Explore
diff --git a/app/about/LandingCTA.tsx b/app/about/LandingCTA.tsx
index a3228e69..8ef74d27 100644
--- a/app/about/LandingCTA.tsx
+++ b/app/about/LandingCTA.tsx
@@ -34,21 +34,21 @@ export const LandingCTABottom = () => {
<>
Write with Leaflet!
-
+
Get Started!
Pssst… Are you stuck on Substack?
-
+
Join the waitlist for our migration tools!
-
+
Join
diff --git a/app/about/Pricing.tsx b/app/about/Pricing.tsx
index 58c320e4..097340b3 100644
--- a/app/about/Pricing.tsx
+++ b/app/about/Pricing.tsx
@@ -1,17 +1,39 @@
+"use client";
+
import { ButtonPrimary } from "components/Buttons";
+import { ToggleGroup } from "components/ToggleGroup";
+import { useState } from "react";
export const Pricing = () => {
+ let [cadence, setCadence] = useState<"monthly" | "yearly">("yearly");
+
return (
-
-
Pricing
-
-
-
-

-
{" "}
+
+
Pricing
+
+
+
+
+

+
Free
@@ -19,50 +41,85 @@ export const Pricing = () => {
Start Writing
-
+
+ - Fast, powerful text editing
+
+ - Custom Themes and Domains
+
+
+ - Publication Pages and Nav
+
+
- Unlimited Pubs and Posts
- - Atmosphere Subscriptions
- - Custom Themes
- - Custom Domains
+
+
+ - Subscriptions via Atmosphere
+
+
+ - Comments, Quoting, and Sharing
-
-
-

+
+
+
-
$120/year
+
+ {cadence === "yearly" ? "$120/year" : "$12/month"}
+
- Serious publishers need serious tools
+ Serious publishers, serious tools
Get Pro
-
+
- Everything in Free
- - Analytics
+
+
+ -
+
Analytics
+
+ Subscriber count, page views, referrers
+
+
+
+
-
1k Email Subscribers
$5/mo for every additional 1k
+
+
+ -
+
Coming soon!
+
+ Memberships, group publication,
+ social reader, and more
+
+
diff --git a/app/about/about.css b/app/about/about.css
index d2696e6c..c6f4322d 100644
--- a/app/about/about.css
+++ b/app/about/about.css
@@ -28,6 +28,38 @@
.aboutPage p {
@apply text-[1rem] sm:text-[1.25rem] md:text-[1.5rem];
}
+
+.pubAndPostCarousel p {
+ @apply text-[1rem];
+}
+.pubAndPostCarousel h4 {
+ font-weight: 700;
+
+ @apply text-[1.25rem];
+}
+
+.pubAndPostCarouselTrack {
+ animation: pubAndPostCarouselScroll 100s linear infinite;
+}
+
+.pubAndPostCarousel:hover .pubAndPostCarouselTrack {
+ animation-play-state: paused;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .pubAndPostCarouselTrack {
+ animation: none;
+ }
+}
+
+@keyframes pubAndPostCarouselScroll {
+ from {
+ transform: translateX(0);
+ }
+ to {
+ transform: translateX(-50%);
+ }
+}
.aboutCover {
}