a tool for shared writing and social publishing

move publication dashboard to /dashboard page

+54 -121
+36
app/api/rpc/[command]/get_publication_data.ts
··· 1 + import { z } from "zod"; 2 + import { makeRoute } from "../lib"; 3 + import type { Env } from "./route"; 4 + 5 + export type GetPublicationDataReturnType = Awaited< 6 + ReturnType<(typeof get_publication_data)["handler"]> 7 + >; 8 + export const get_publication_data = makeRoute({ 9 + route: "get_publication_data", 10 + input: z.object({ 11 + did: z.string(), 12 + publication_name: z.string(), 13 + }), 14 + handler: async ( 15 + { did, publication_name }, 16 + { supabase }: Pick<Env, "supabase">, 17 + ) => { 18 + let { data: publication } = await supabase 19 + .from("publications") 20 + .select( 21 + `*, 22 + documents_in_publications(documents(*)), 23 + leaflets_in_publications(*, 24 + permission_tokens(*, 25 + permission_token_rights(*), 26 + custom_domain_routes!custom_domain_routes_edit_permission_token_fkey(*) 27 + ) 28 + )`, 29 + ) 30 + .eq("identity_did", did) 31 + .eq("name", publication_name) 32 + .single(); 33 + 34 + return { result: publication }; 35 + }, 36 + });
-97
app/lish/Footer.tsx
··· 1 - "use client"; 2 - import { Menu, MenuItem } from "components/Layout"; 3 - import { useEffect, useState } from "react"; 4 - import { SubscribeButton } from "./Subscribe"; 5 - import Link from "next/link"; 6 - import { ButtonPrimary } from "components/Buttons"; 7 - import { usePublicationRelationship } from "./[handle]/[publication]/usePublicationRelationship"; 8 - import { usePublicationContext } from "components/Providers/PublicationContext"; 9 - import { MoreOptionsTiny } from "components/Icons/MoreOptionsTiny"; 10 - 11 - export const Footer = (props: { pageType: "post" | "pub" }) => { 12 - return ( 13 - <div className="footer w-full bg-bg-page border-0 border-t border-border flex flex-col "> 14 - <ScrollProgress /> 15 - <div className="footerContent w-full min-h-12 h-fit max-w-prose mx-auto px-4 py-2 flex justify-between items-center gap-6"> 16 - {/* <MoreOptionsMenu /> */} 17 - <FooterSubscribeButton pageType={props.pageType} /> 18 - </div> 19 - </div> 20 - ); 21 - }; 22 - 23 - const ScrollProgress = () => { 24 - let [scrollPercent, setScrollPercent] = useState(0); 25 - 26 - useEffect(() => { 27 - let post = document.getElementById("post"); 28 - 29 - let onScroll = () => { 30 - if (!post) return; 31 - let currentScroll = post?.scrollTop; 32 - let totalScroll = post?.scrollHeight - post?.clientHeight; 33 - setScrollPercent((currentScroll / totalScroll) * 100); 34 - }; 35 - post?.addEventListener("scroll", onScroll); 36 - return () => post?.removeEventListener("scroll", onScroll); 37 - }, []); 38 - return ( 39 - <div className="footerScrollProgress w-full h-1 bg-bg-page"> 40 - <div 41 - className={`h-full bg-accent-contrast`} 42 - style={{ width: `${scrollPercent}%` }} 43 - ></div> 44 - </div> 45 - ); 46 - }; 47 - 48 - const FooterSubscribeButton = (props: { pageType: "post" | "pub" }) => { 49 - let [pubHeaderIsVisible, setPubHeaderIsVisible] = useState( 50 - props.pageType === "pub" ? true : false, 51 - ); 52 - let rel = usePublicationRelationship(); 53 - let { publication } = usePublicationContext(); 54 - 55 - useEffect(() => { 56 - let pubHeader = document.getElementById("pub-header"); 57 - if (!pubHeader) return; 58 - let observer = new IntersectionObserver( 59 - (entries) => { 60 - entries.forEach((entry) => { 61 - setPubHeaderIsVisible(entry.isIntersecting); 62 - }); 63 - }, 64 - { threshold: 0 }, 65 - ); 66 - observer.observe(pubHeader); 67 - return () => observer.unobserve(pubHeader); 68 - }, []); 69 - 70 - if (rel?.isSubscribed || pubHeaderIsVisible || !publication) return; 71 - if (rel?.isAuthor) 72 - return ( 73 - <div className="flex gap-2"> 74 - <ButtonPrimary>Write a Draft</ButtonPrimary> 75 - {/* <ShareButton /> */} 76 - </div> 77 - ); 78 - return <SubscribeButton compact publication={publication?.uri} />; 79 - }; 80 - const MoreOptionsMenu = () => { 81 - return ( 82 - <Menu trigger={<MoreOptionsTiny className="footerMoreOptions rotate-90" />}> 83 - <MenuItem onSelect={() => {}}>Log in</MenuItem> 84 - <hr className="border-border-light" /> 85 - 86 - <small className="text-tertiary px-3 leading-none pt-2 font-bold"> 87 - Back to... 88 - </small> 89 - <MenuItem onSelect={() => {}}> 90 - <Link href="./publication">Leaflet Explorers</Link> 91 - </MenuItem> 92 - <MenuItem onSelect={() => {}}> 93 - <Link href="./">Your Feed</Link> 94 - </MenuItem> 95 - </Menu> 96 - ); 97 - };
app/lish/[handle]/[publication]/Actions.tsx app/lish/[handle]/[publication]/dashboard/Actions.tsx
+1 -1
app/lish/[handle]/[publication]/DraftList.tsx app/lish/[handle]/[publication]/dashboard/DraftList.tsx
··· 1 1 "use client"; 2 2 3 - import { usePublicationRelationship } from "./usePublicationRelationship"; 3 + import { usePublicationRelationship } from "../usePublicationRelationship"; 4 4 import { usePublicationContext } from "components/Providers/PublicationContext"; 5 5 import Link from "next/link"; 6 6 import { NewDraftSecondaryButton } from "./NewDraftButton";
app/lish/[handle]/[publication]/NewDraftButton.tsx app/lish/[handle]/[publication]/dashboard/NewDraftButton.tsx
app/lish/[handle]/[publication]/PostsList.tsx

This is a binary file and will not be displayed.

app/lish/[handle]/[publication]/PublicationDashboard.tsx app/lish/[handle]/[publication]/dashboard/PublicationDashboard.tsx
app/lish/[handle]/[publication]/layout.tsx app/lish/[handle]/[publication]/dashboard/layout.tsx
+17 -23
app/lish/[handle]/[publication]/page.tsx app/lish/[handle]/[publication]/dashboard/page.tsx
··· 16 16 import { PubLeafletDocument } from "lexicons/api"; 17 17 import React from "react"; 18 18 import { EditTiny } from "components/Icons/EditTiny"; 19 + import { MoreOptionsVerticalTiny } from "components/Icons/MoreOptionsVerticalTiny"; 20 + import { Menu, MenuItem } from "components/Layout"; 21 + import { get_publication_data } from "app/api/rpc/[command]/get_publication_data"; 19 22 20 23 const idResolver = new IdResolver(); 21 24 ··· 25 28 let did = await idResolver.handle.resolve((await props.params).handle); 26 29 if (!did) return { title: "Publication 404" }; 27 30 28 - let { data: publication } = await supabaseServerClient 29 - .from("publications") 30 - .select( 31 - "*, documents_in_publications(documents(*)), leaflets_in_publications(*, permission_tokens(*, permission_token_rights(*), custom_domain_routes!custom_domain_routes_edit_permission_token_fkey(*) ))", 32 - ) 33 - .eq("identity_did", did) 34 - .eq("name", decodeURIComponent((await props.params).publication)) 35 - .single(); 31 + let { result: publication } = await get_publication_data.handler( 32 + { 33 + did, 34 + publication_name: decodeURIComponent((await props.params).publication), 35 + }, 36 + { supabase: supabaseServerClient }, 37 + ); 36 38 if (!publication) return { title: "404 Publication" }; 37 39 return { title: decodeURIComponent((await props.params).publication) }; 38 40 } ··· 46 48 if (!identity || !identity.atp_did) return <PubNotFound />; 47 49 let did = await idResolver.handle.resolve((await props.params).handle); 48 50 if (!did) return <PubNotFound />; 49 - let { data: publication } = await supabaseServerClient 50 - .from("publications") 51 - .select( 52 - `*, 53 - documents_in_publications(documents(*)), 54 - leaflets_in_publications(*, 55 - permission_tokens(*, 56 - permission_token_rights(*), 57 - custom_domain_routes!custom_domain_routes_edit_permission_token_fkey(*) 58 - ) 59 - )`, 60 - ) 61 - .eq("identity_did", did) 62 - .eq("name", decodeURIComponent((await props.params).publication)) 63 - .single(); 51 + let { result: publication } = await get_publication_data.handler( 52 + { 53 + did, 54 + publication_name: decodeURIComponent((await props.params).publication), 55 + }, 56 + { supabase: supabaseServerClient }, 57 + ); 64 58 if (!publication || identity.atp_did !== publication.identity_did) 65 59 return <PubNotFound />; 66 60