a tool for shared writing and social publishing
0
fork

Configure Feed

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

WIP publication dashboard

+71 -81
-41
app/lish/[handle]/[publication]/CallToActionButton.tsx
··· 1 - "use client"; 2 - 3 - import { SubscribeButton } from "app/lish/Subscribe"; 4 - import { usePublicationRelationship } from "./usePublicationRelationship"; 5 - import { usePublicationContext } from "components/Providers/PublicationContext"; 6 - import { NewDraftButton } from "./NewDraftButton"; 7 - import { Menu, MenuItem } from "components/Layout"; 8 - import { useIdentityData } from "components/IdentityProvider"; 9 - import { unsubscribeFromPublication } from "actions/unsubscribeFromPublication"; 10 - import { MoreOptionsTiny } from "components/Icons/MoreOptionsTiny"; 11 - 12 - export function CallToActionButton() { 13 - let rel = usePublicationRelationship(); 14 - let { publication } = usePublicationContext(); 15 - if (!publication) return null; 16 - if (rel?.isAuthor) return <NewDraftButton publication={publication.uri} />; 17 - if (rel?.isSubscribed) 18 - return ( 19 - <div className="flex gap-2"> 20 - <div className="font-bold">You're Subscribed!</div> 21 - <ManageSubscriptionMenu publication_uri={publication.uri} /> 22 - </div> 23 - ); 24 - return <SubscribeButton publication={publication.uri} />; 25 - } 26 - 27 - const ManageSubscriptionMenu = (props: { publication_uri: string }) => { 28 - let { mutate } = useIdentityData(); 29 - return ( 30 - <Menu trigger={<MoreOptionsTiny className="rotate-90" />}> 31 - <MenuItem 32 - onSelect={async () => { 33 - await unsubscribeFromPublication(props.publication_uri); 34 - mutate(); 35 - }} 36 - > 37 - Unsub! 38 - </MenuItem> 39 - </Menu> 40 - ); 41 - };
+4 -2
app/lish/[handle]/[publication]/DraftList.tsx
··· 4 4 import { usePublicationRelationship } from "./usePublicationRelationship"; 5 5 import { usePublicationContext } from "components/Providers/PublicationContext"; 6 6 import Link from "next/link"; 7 + import { NewDraftSecondaryButton } from "./NewDraftButton"; 7 8 8 9 export function DraftList(props: { 10 + publication: string; 9 11 drafts: { id: string; initialFacts: Fact<any>[]; root_entity: string }[]; 10 12 }) { 11 13 let rel = usePublicationRelationship(); ··· 13 15 if (!publication) return null; 14 16 if (!rel?.isAuthor) return null; 15 17 return ( 16 - <div className=""> 17 - <h2>drafts</h2> 18 + <div className="flex flex-col gap-2"> 19 + <NewDraftSecondaryButton publication={props.publication} /> 18 20 {props.drafts.map((d) => { 19 21 return ( 20 22 <ReplicacheProvider
+33 -12
app/lish/[handle]/[publication]/NewDraftButton.tsx
··· 1 1 "use client"; 2 2 import { createPublicationDraft } from "actions/createPublicationDraft"; 3 - import { ButtonPrimary } from "components/Buttons"; 3 + import { ActionButton } from "components/ActionBar/ActionButton"; 4 + import { ButtonSecondary } from "components/Buttons"; 5 + import { AddTiny } from "components/Icons/AddTiny"; 4 6 import { useRouter } from "next/navigation"; 5 7 6 - export function NewDraftButton(props: { publication: string }) { 8 + export function NewDraftActionButton(props: { publication: string }) { 7 9 let router = useRouter(); 10 + 8 11 return ( 9 - <div className="flex gap-2"> 10 - <ButtonPrimary 11 - onClick={async () => { 12 - let newLeaflet = await createPublicationDraft(props.publication); 13 - router.push(`/${newLeaflet}`); 14 - }} 15 - > 16 - New Draft 17 - </ButtonPrimary> 18 - </div> 12 + <ActionButton 13 + id="new-leaflet-button" 14 + primary 15 + onClick={async () => { 16 + let newLeaflet = await createPublicationDraft(props.publication); 17 + router.push(`/${newLeaflet}`); 18 + }} 19 + icon=<AddTiny className="m-1 shrink-0" /> 20 + label="New Draft" 21 + /> 22 + ); 23 + } 24 + 25 + export function NewDraftSecondaryButton(props: { publication: string }) { 26 + let router = useRouter(); 27 + 28 + return ( 29 + <ButtonSecondary 30 + fullWidth 31 + id="new-leaflet-button" 32 + onClick={async () => { 33 + let newLeaflet = await createPublicationDraft(props.publication); 34 + router.push(`/${newLeaflet}`); 35 + }} 36 + > 37 + <AddTiny className="m-1 shrink-0" /> 38 + <span>New Post</span> 39 + </ButtonSecondary> 19 40 ); 20 41 }
+12 -4
app/lish/[handle]/[publication]/PublicationDashboard.tsx
··· 11 11 let content = props.tabs[tab]; 12 12 return ( 13 13 <div className="w-full flex flex-col items-stretch"> 14 - <div className="flex flex-row w-full justify-between border-b border-secondary text-secondary"> 14 + <div className="flex flex-row w-full justify-between border-b border-border text-secondary"> 15 15 <div>{props.name}</div> 16 16 <div className="flex flex-row gap-2"> 17 17 {Object.keys(props.tabs).map((t) => ( 18 - <Tab name={t} selected={t === tab} onSelect={() => setTab(t)} /> 18 + <Tab 19 + key={t} 20 + name={t} 21 + selected={t === tab} 22 + onSelect={() => setTab(t)} 23 + /> 19 24 ))} 20 25 </div> 21 26 </div> 22 - <div>{content}</div> 27 + <div className="pt-4">{content}</div> 23 28 </div> 24 29 ); 25 30 } 26 31 27 32 function Tab(props: { name: string; selected: boolean; onSelect: () => void }) { 28 33 return ( 29 - <div className="border" onClick={() => props.onSelect()}> 34 + <div 35 + className={`border bg-bg-page border-b-0 px-2 pt-1 rounded-t-md border-border ${props.selected ? "text-accent-1 font-bold -mb-[1px]" : ""}`} 36 + onClick={() => props.onSelect()} 37 + > 30 38 {props.name} 31 39 </div> 32 40 );
+22 -22
app/lish/[handle]/[publication]/page.tsx
··· 9 9 import { Media } from "components/Media"; 10 10 import { Footer } from "components/ActionBar/Footer"; 11 11 import { PublicationDashboard } from "./PublicationDashboard"; 12 - import { AddTiny } from "components/Icons/AddTiny"; 13 12 import { DraftList } from "./DraftList"; 13 + import { NewDraftActionButton } from "./NewDraftButton"; 14 + import { use } from "react"; 15 + import { IdentityContext } from "components/IdentityProvider"; 16 + import { getIdentityData } from "actions/getIdentityData"; 14 17 15 18 const idResolver = new IdResolver(); 16 19 ··· 32 35 return { title: decodeURIComponent((await props.params).publication) }; 33 36 } 34 37 38 + //This is the admin dashboard of the publication 35 39 export default async function Publication(props: { 36 40 params: Promise<{ publication: string; handle: string }>; 37 41 }) { 42 + let identity = await getIdentityData(); 43 + if (!identity || !identity.atp_did) return <PubNotFound />; 38 44 let did = await idResolver.handle.resolve((await props.params).handle); 39 45 if (!did) return <PubNotFound />; 40 46 let { data: publication } = await supabaseServerClient 41 47 .from("publications") 42 48 .select( 43 - "*, documents_in_publications(documents(*)), leaflets_in_publications(*, permission_tokens(*, permission_token_rights(*), custom_domain_routes!custom_domain_routes_edit_permission_token_fkey(*) ))", 49 + `*, 50 + documents_in_publications(documents(*)), 51 + leaflets_in_publications(*, 52 + permission_tokens(*, 53 + permission_token_rights(*), 54 + custom_domain_routes!custom_domain_routes_edit_permission_token_fkey(*) 55 + ) 56 + )`, 44 57 ) 45 58 .eq("identity_did", did) 46 59 .eq("name", decodeURIComponent((await props.params).publication)) 47 60 .single(); 48 - if (!publication) return <PubNotFound />; 61 + if (!publication || identity.atp_did !== publication.identity_did) 62 + return <PubNotFound />; 49 63 50 64 let all_facts = await supabaseServerClient.rpc("get_facts_for_roots", { 51 65 max_depth: 2, ··· 69 83 return ( 70 84 <div className="relative max-w-screen-lg w-full h-full mx-auto flex sm:flex-row flex-col sm:items-stretch sm:px-6"> 71 85 <Sidebar className="mt-6 p-2"> 72 - <ActionButton 73 - id="new-leaflet-button" 74 - primary 75 - icon=<AddTiny className="m-1 shrink-0" /> 76 - label="Create Draft" 77 - /> 86 + <Actions publication={publication.uri} /> 78 87 </Sidebar> 79 88 <div className={`h-full overflow-y-scroll pl-8 pt-8 w-full`}> 80 89 <PublicationDashboard ··· 82 91 tabs={{ 83 92 Drafts: ( 84 93 <DraftList 94 + publication={publication.uri} 85 95 drafts={publication.leaflets_in_publications.map((d) => ({ 86 96 ...d.permission_tokens!, 87 97 initialFacts: facts[d.permission_tokens?.root_entity!], ··· 95 105 </div> 96 106 <Media mobile> 97 107 <Footer> 98 - <ActionButton 99 - id="new-leaflet-button" 100 - primary 101 - icon=<AddTiny className="m-1 shrink-0" /> 102 - label="New Doc" 103 - /> 108 + <Actions publication={publication.uri} /> 104 109 </Footer> 105 110 </Media> 106 111 </div> ··· 115 120 return <div>ain't no pub here</div>; 116 121 }; 117 122 118 - const Actions = () => { 123 + const Actions = (props: { publication: string }) => { 119 124 return ( 120 125 <> 121 - <ActionButton 122 - id="new-leaflet-button" 123 - primary 124 - icon=<AddTiny className="m-1 shrink-0" /> 125 - label="Create Draft" 126 - /> 126 + <NewDraftActionButton publication={props.publication} /> 127 127 </> 128 128 ); 129 129 };