diff --git a/app/(app)/(home-pages)/p/[didOrHandle]/comments/page.tsx b/app/(app)/(home-pages)/p/[didOrHandle]/comments/page.tsx
index 146fcc31..079222d7 100644
--- a/app/(app)/(home-pages)/p/[didOrHandle]/comments/page.tsx
+++ b/app/(app)/(home-pages)/p/[didOrHandle]/comments/page.tsx
@@ -36,15 +36,5 @@ async function ProfileCommentsLoader({ did }: { did: string }) {
}
function ProfileCommentsSkeleton() {
- return (
-
- {[0, 1, 2].map((i) => (
-
- ))}
-
- );
+ return null;
}
diff --git a/app/(app)/lish/[did]/[publication]/[rkey]/Interactions/Comments/CommentsSection.tsx b/app/(app)/lish/[did]/[publication]/[rkey]/Interactions/Comments/CommentsSection.tsx
index ec23b672..28d24eef 100644
--- a/app/(app)/lish/[did]/[publication]/[rkey]/Interactions/Comments/CommentsSection.tsx
+++ b/app/(app)/lish/[did]/[publication]/[rkey]/Interactions/Comments/CommentsSection.tsx
@@ -45,15 +45,5 @@ export async function CommentsSection({
}
export function CommentsSkeleton() {
- return (
-
- {[0, 1, 2].map((i) => (
-
- ))}
-
- );
+ return null;
}
diff --git a/app/(app)/lish/[did]/[publication]/dashboard/PublicationSubscribers.tsx b/app/(app)/lish/[did]/[publication]/dashboard/PublicationSubscribers.tsx
index 882690c4..67559b97 100644
--- a/app/(app)/lish/[did]/[publication]/dashboard/PublicationSubscribers.tsx
+++ b/app/(app)/lish/[did]/[publication]/dashboard/PublicationSubscribers.tsx
@@ -1,12 +1,17 @@
"use client";
-import { usePublicationData } from "./PublicationSWRProvider";
import { ButtonPrimary } from "components/Buttons";
import { useSmoker } from "components/Toast";
import { Separator } from "components/Layout";
import { useLocalizedDate } from "src/hooks/useLocalizedDate";
-import { useDashboardState } from "components/PageLayouts/dashboardState";
+import {
+ useDashboardState,
+ useSetDashboardState,
+} from "components/PageLayouts/dashboardState";
import { AtmosphereAccount } from "components/Icons/AtmosphereAccount";
import { EmailTiny } from "components/Icons/EmailTiny";
+import { DashboardPageLayout } from "components/PageLayouts/DashboardPageLayout";
+import { Popover } from "components/Popover";
+import { Checkbox } from "components/Checkbox";
export type SubscriberStatus = "subscribed" | "unconfirmed" | "unsubscribed";
@@ -19,75 +24,15 @@ export type MergedSubscriber = {
status: SubscriberStatus;
};
-export function useMergedSubscribers(): MergedSubscriber[] | null {
- let { data: publication } = usePublicationData();
- let { subscriberStatus } = useDashboardState();
-
- if (!publication) return null;
- // ATProto subscribers have no email lifecycle state — they're just present
- // or absent, so they only count under the "subscribed" status filter.
- let atprotoSubs = subscriberStatus.subscribed
- ? publication.publication?.publication_subscriptions || []
- : [];
- let newsletterEnabled =
- !!publication.publication?.publication_newsletter_settings?.enabled;
- let emailSubs = newsletterEnabled
- ? (publication.publication?.publication_email_subscribers || []).filter(
- (s) => {
- if (s.state === "confirmed") return subscriberStatus.subscribed;
- if (s.state === "pending") return subscriberStatus.unconfirmed;
- if (s.state === "unsubscribed") return subscriberStatus.unsubscribed;
- return false;
- },
- )
- : [];
-
- let byDid = new Map();
- let emailOnly: MergedSubscriber[] = [];
- for (let s of atprotoSubs) {
- let did = s.identities?.atp_did ?? undefined;
- if (!did) continue;
- byDid.set(did, {
- key: `did:${did}`,
- did,
- handle: undefined,
- email: undefined,
- created_at: s.created_at,
- status: "subscribed",
- });
- }
- for (let s of emailSubs) {
- let status: SubscriberStatus =
- s.state === "pending"
- ? "unconfirmed"
- : s.state === "unsubscribed"
- ? "unsubscribed"
- : "subscribed";
- let linkedDid = s.identities?.atp_did ?? undefined;
- let existing = linkedDid ? byDid.get(linkedDid) : undefined;
- if (existing && status === "subscribed") {
- existing.email = s.email;
- continue;
- }
- emailOnly.push({
- key: `email:${s.id}`,
- did: linkedDid,
- handle: undefined,
- email: s.email,
- created_at: s.created_at,
- status,
- });
- }
- return [...byDid.values(), ...emailOnly];
-}
-
-export function SubscribersListView(props: {
+export function PublicationSubscribers(props: {
subscribers: MergedSubscriber[];
publicationShareUrl: string;
- showPageBackground?: boolean;
+ publicationUri: string;
+ showPageBackground: boolean;
}) {
let smoker = useSmoker();
let { subscriberStatus } = useDashboardState();
+ let filtered = props.subscribers.filter((s) => subscriberStatus[s.status]);
let activeStatuses = (
Object.keys(subscriberStatus) as SubscriberStatus[]
@@ -95,89 +40,83 @@ export function SubscribersListView(props: {
let isDefaultStatusFilter =
activeStatuses.length === 1 && activeStatuses[0] === "subscribed";
- let filtered = props.subscribers.filter((s) => subscriberStatus[s.status]);
-
- if (filtered.length === 0) {
- if (!isDefaultStatusFilter) {
- return (
-
-
No subscribers match your filters!
-
- );
- }
- return (
-
-
No subscribers yet
-
Start sharing your publication!
-
{
- e.preventDefault();
- let rect = (e.currentTarget as Element)?.getBoundingClientRect();
- navigator.clipboard.writeText(props.publicationShareUrl);
- smoker({
- position: {
- x: rect ? rect.left + (rect.right - rect.left) / 2 : 0,
- y: rect ? rect.top + 26 : 0,
- },
- text: "Copied Publication URL!",
- });
- }}
- >
- Copy Share Link
-
-
- );
- }
+ let bgStyle = props.showPageBackground
+ ? { backgroundColor: "rgba(var(--bg-page), var(--bg-page-alpha)) " }
+ : { backgroundColor: "transparent" };
+ let bgBorder = props.showPageBackground
+ ? "border-border-light p-2"
+ : "border-transparent";
return (
- }
+ publication={props.publicationUri}
+ showHeader={true}
+ controls={
+
+
+ {filtered.length} Subscriber{filtered.length !== 1 && "s"}
+
+
+
}
>
-
- {filtered
- .sort((a, b) => b.created_at.localeCompare(a.created_at))
- .map((subscriber) => (
-
-
-
-
- ))}
-
-
+ {filtered.length === 0 ? (
+
+ {isDefaultStatusFilter ? (
+ <>
+
No subscribers yet
+
Start sharing your publication!
+
{
+ e.preventDefault();
+ let rect = (
+ e.currentTarget as Element
+ )?.getBoundingClientRect();
+ navigator.clipboard.writeText(props.publicationShareUrl);
+ smoker({
+ position: {
+ x: rect ? rect.left + (rect.right - rect.left) / 2 : 0,
+ y: rect ? rect.top + 26 : 0,
+ },
+ text: "Copied Publication URL!",
+ });
+ }}
+ >
+ Copy Share Link
+
+ >
+ ) : (
+
No subscribers match your filters!
+ )}
+
+ ) : (
+
+
+ {filtered
+ .sort((a, b) => b.created_at.localeCompare(a.created_at))
+ .map((subscriber) => (
+
+
+
+
+ ))}
+
+
+ )}
+
);
}
@@ -245,3 +184,63 @@ function SubscriberDate(props: { createdAt: string }) {
);
}
+
+const SubscriberStatusFilter = () => {
+ let { subscriberStatus } = useDashboardState();
+ let setState = useSetDashboardState();
+ let count = Object.values(subscriberStatus).filter(Boolean).length;
+
+ return (
+
+ Filters {count > 0 && `(${count})`}
+
+ }
+ >
+
+ setState({
+ subscriberStatus: {
+ ...subscriberStatus,
+ subscribed: !!e.target.checked,
+ },
+ })
+ }
+ >
+ Subscribed
+
+
+ setState({
+ subscriberStatus: {
+ ...subscriberStatus,
+ unconfirmed: !!e.target.checked,
+ },
+ })
+ }
+ >
+ Unconfirmed
+
+
+ setState({
+ subscriberStatus: {
+ ...subscriberStatus,
+ unsubscribed: !!e.target.checked,
+ },
+ })
+ }
+ >
+ Unsubscribed
+
+
+ );
+};
diff --git a/app/(app)/lish/[did]/[publication]/dashboard/SubsPageShell.tsx b/app/(app)/lish/[did]/[publication]/dashboard/SubsPageShell.tsx
deleted file mode 100644
index bac567e3..00000000
--- a/app/(app)/lish/[did]/[publication]/dashboard/SubsPageShell.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-"use client";
-
-import { DashboardPageLayout } from "components/PageLayouts/DashboardPageLayout";
-import { Popover } from "components/Popover";
-import { Checkbox } from "components/Checkbox";
-import {
- useDashboardState,
- useSetDashboardState,
-} from "components/PageLayouts/dashboardState";
-import { usePublicationData } from "./PublicationSWRProvider";
-import { useMergedSubscribers } from "./PublicationSubscribers";
-
-export function SubsPageShell({ children }: { children: React.ReactNode }) {
- let { data } = usePublicationData();
- let pubUri = data?.publication?.uri || "";
- let subscribers = useMergedSubscribers();
- let count = subscribers?.length ?? 0;
-
- return (
- }
- publication={pubUri}
- showHeader={true}
- controls={
-
-
- {count} Subscriber{count !== 1 && "s"}
-
-
-
- }
- >
- {children}
-
- );
-}
-
-const SubscriberStatusFilter = () => {
- let { subscriberStatus } = useDashboardState();
- let setState = useSetDashboardState();
- let count = Object.values(subscriberStatus).filter(Boolean).length;
-
- return (
-
- Filters {count > 0 && `(${count})`}
-
- }
- >
-
- setState({
- subscriberStatus: {
- ...subscriberStatus,
- subscribed: !!e.target.checked,
- },
- })
- }
- >
- Subscribed
-
-
- setState({
- subscriberStatus: {
- ...subscriberStatus,
- unconfirmed: !!e.target.checked,
- },
- })
- }
- >
- Unconfirmed
-
-
- setState({
- subscriberStatus: {
- ...subscriberStatus,
- unsubscribed: !!e.target.checked,
- },
- })
- }
- >
- Unsubscribed
-
-
- );
-};
diff --git a/app/(app)/lish/[did]/[publication]/dashboard/SubscribersList.tsx b/app/(app)/lish/[did]/[publication]/dashboard/SubscribersList.tsx
deleted file mode 100644
index c2a39b11..00000000
--- a/app/(app)/lish/[did]/[publication]/dashboard/SubscribersList.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import { supabaseServerClient } from "supabase/serverClient";
-import { getProfiles } from "src/identity";
-import { get_publication_data } from "app/api/rpc/[command]/get_publication_data";
-import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL";
-import { normalizePublicationRecord } from "src/utils/normalizeRecords";
-import {
- SubscribersListView,
- type MergedSubscriber,
- type SubscriberStatus,
-} from "./PublicationSubscribers";
-
-export async function SubscribersList({
- did,
- publication,
-}: {
- did: string;
- publication: string;
-}) {
- const { result } = await get_publication_data.handler(
- { did, publication_name: publication },
- { supabase: supabaseServerClient },
- );
- const pub = result.publication;
- if (!pub) {
- return (
-
- );
- }
- const record = normalizePublicationRecord(pub.record);
- const showPageBackground = !!record?.theme?.showPageBackground;
-
- const atprotoSubs = pub.publication_subscriptions || [];
- const newsletterEnabled = !!pub.publication_newsletter_settings?.enabled;
- const emailSubs = newsletterEnabled
- ? pub.publication_email_subscribers || []
- : [];
-
- const dids = new Set();
- for (const s of atprotoSubs) {
- const d = s.identities?.atp_did ?? undefined;
- if (d) dids.add(d);
- }
- for (const s of emailSubs) {
- if (s.identities?.atp_did) dids.add(s.identities.atp_did);
- }
-
- const profiles = await getProfiles(Array.from(dids));
-
- const byDid = new Map();
- const emailOnly: MergedSubscriber[] = [];
-
- for (const s of atprotoSubs) {
- const d = s.identities?.atp_did ?? undefined;
- if (!d) continue;
- const p = profiles.get(d);
- byDid.set(d, {
- key: `did:${d}`,
- did: d,
- handle: p?.handle ?? undefined,
- email: undefined,
- created_at: s.created_at,
- status: "subscribed",
- });
- }
-
- for (const s of emailSubs) {
- const status: SubscriberStatus =
- s.state === "pending"
- ? "unconfirmed"
- : s.state === "unsubscribed"
- ? "unsubscribed"
- : "subscribed";
- const linkedDid = s.identities?.atp_did ?? undefined;
- const existing = linkedDid ? byDid.get(linkedDid) : undefined;
- if (existing && status === "subscribed") {
- existing.email = s.email;
- continue;
- }
- const p = linkedDid ? profiles.get(linkedDid) : null;
- emailOnly.push({
- key: `email:${s.id}`,
- did: linkedDid,
- handle: p?.handle ?? undefined,
- email: s.email,
- created_at: s.created_at,
- status,
- });
- }
-
- const subscribers = [...byDid.values(), ...emailOnly];
-
- return (
-
- );
-}
diff --git a/app/(app)/lish/[did]/[publication]/dashboard/subs/page.tsx b/app/(app)/lish/[did]/[publication]/dashboard/subs/page.tsx
index 64f5d027..c3b45bb7 100644
--- a/app/(app)/lish/[did]/[publication]/dashboard/subs/page.tsx
+++ b/app/(app)/lish/[did]/[publication]/dashboard/subs/page.tsx
@@ -1,6 +1,13 @@
-import { Suspense } from "react";
-import { SubsPageShell } from "../SubsPageShell";
-import { SubscribersList } from "../SubscribersList";
+import { supabaseServerClient } from "supabase/serverClient";
+import { getProfiles } from "src/identity";
+import { get_publication_data } from "app/api/rpc/[command]/get_publication_data";
+import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL";
+import { normalizePublicationRecord } from "src/utils/normalizeRecords";
+import {
+ PublicationSubscribers,
+ type MergedSubscriber,
+ type SubscriberStatus,
+} from "../PublicationSubscribers";
export default async function SubsPage(props: {
params: Promise<{ did: string; publication: string }>;
@@ -9,11 +16,85 @@ export default async function SubsPage(props: {
const did = decodeURIComponent(params.did);
const publication = decodeURIComponent(params.publication);
+ const { result } = await get_publication_data.handler(
+ { did, publication_name: publication },
+ { supabase: supabaseServerClient },
+ );
+ const pub = result.publication;
+ if (!pub) {
+ return (
+
+ );
+ }
+
+ const record = normalizePublicationRecord(pub.record);
+ const showPageBackground = !!record?.theme?.showPageBackground;
+ const atprotoSubs = pub.publication_subscriptions || [];
+ const newsletterEnabled = !!pub.publication_newsletter_settings?.enabled;
+ const emailSubs = newsletterEnabled
+ ? pub.publication_email_subscribers || []
+ : [];
+
+ const dids = new Set();
+ for (const s of atprotoSubs) {
+ if (s.identities?.atp_did) dids.add(s.identities.atp_did);
+ }
+ for (const s of emailSubs) {
+ if (s.identities?.atp_did) dids.add(s.identities.atp_did);
+ }
+
+ const profiles = await getProfiles(Array.from(dids));
+
+ const byDid = new Map();
+ const emailOnly: MergedSubscriber[] = [];
+
+ for (const s of atprotoSubs) {
+ const d = s.identities?.atp_did ?? undefined;
+ if (!d) continue;
+ byDid.set(d, {
+ key: `did:${d}`,
+ did: d,
+ handle: profiles.get(d)?.handle ?? undefined,
+ email: undefined,
+ created_at: s.created_at,
+ status: "subscribed",
+ });
+ }
+
+ for (const s of emailSubs) {
+ const status: SubscriberStatus =
+ s.state === "pending"
+ ? "unconfirmed"
+ : s.state === "unsubscribed"
+ ? "unsubscribed"
+ : "subscribed";
+ const linkedDid = s.identities?.atp_did ?? undefined;
+ const existing = linkedDid ? byDid.get(linkedDid) : undefined;
+ if (existing && status === "subscribed") {
+ existing.email = s.email;
+ continue;
+ }
+ emailOnly.push({
+ key: `email:${s.id}`,
+ did: linkedDid,
+ handle: linkedDid ? (profiles.get(linkedDid)?.handle ?? undefined) : undefined,
+ email: s.email,
+ created_at: s.created_at,
+ status,
+ });
+ }
+
return (
-
-
-
-
-
+
);
}