a tool for shared writing and social publishing
0

Configure Feed

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

move dashboard pages into new layout, tweak mobile layout

celine (Apr 28, 2026, 1:18 AM EDT) b47bf80d b86d49a6

+317 -255
+23 -16
app/(home-pages)/home/HomeLayout.tsx
··· 19 19 DashboardState, 20 20 useDashboardState, 21 21 } from "components/PageLayouts/DashboardLayout"; 22 + import { DashboardPageLayout } from "components/PageLayouts/DashboardPageLayout"; 22 23 import { Actions } from "./Actions/Actions"; 23 24 import { GetLeafletDataReturnType } from "app/api/rpc/[command]/get_leaflet_data"; 24 25 import { useState } from "react"; ··· 80 81 actions={<Actions />} 81 82 tabs={{ 82 83 home: { 83 - controls: ( 84 - <HomeDashboardControls 85 - defaultDisplay={"grid"} 86 - searchValue={searchValue} 87 - setSearchValueAction={setSearchValue} 88 - hasBackgroundImage={hasBackgroundImage} 89 - hasPubs={hasPubs} 90 - hasArchived={!!hasArchived} 91 - /> 92 - ), 93 84 content: ( 94 - <HomeLeafletList 95 - titles={props.titles} 96 - initialFacts={props.initialFacts} 97 - searchValue={debouncedSearchValue} 98 - /> 85 + <DashboardPageLayout 86 + scrollKey="dashboard-home-home" 87 + pageTitle="Home" 88 + actions={<Actions />} 89 + controls={ 90 + <HomeDashboardControls 91 + defaultDisplay={"grid"} 92 + searchValue={searchValue} 93 + setSearchValueAction={setSearchValue} 94 + hasBackgroundImage={hasBackgroundImage} 95 + hasPubs={hasPubs} 96 + hasArchived={!!hasArchived} 97 + /> 98 + } 99 + showHeader={true} 100 + > 101 + <HomeLeafletList 102 + titles={props.titles} 103 + initialFacts={props.initialFacts} 104 + searchValue={debouncedSearchValue} 105 + /> 106 + </DashboardPageLayout> 99 107 ), 100 108 }, 101 109 }} 102 - pageTitle={"Home"} 103 110 /> 104 111 ); 105 112 };
+1 -4
app/(home-pages)/home/LeafletList/LeafletListItem.tsx
··· 70 70 </div> 71 71 {cardBorderHidden && ( 72 72 <hr 73 - className="last:hidden border-border-light" 74 - style={{ 75 - display: props.isHidden ? "none" : "block", 76 - }} 73 + className={`${props.isHidden ? "hidden" : "block last:hidden"} border-border-light`} 77 74 /> 78 75 )} 79 76 </>
+13 -7
app/(home-pages)/looseleafs/LooseleafsLayout.tsx
··· 1 1 "use client"; 2 2 import { DashboardLayout } from "components/PageLayouts/DashboardLayout"; 3 + import { DashboardPageLayout } from "components/PageLayouts/DashboardPageLayout"; 3 4 import { useState } from "react"; 4 5 import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 5 6 import { Fact, PermissionToken } from "src/replicache"; ··· 37 38 actions={<Actions />} 38 39 tabs={{ 39 40 home: { 40 - controls: null, 41 41 content: ( 42 - <LooseleafList 43 - titles={props.titles} 44 - initialFacts={props.initialFacts} 45 - searchValue={debouncedSearchValue} 46 - /> 42 + <DashboardPageLayout 43 + scrollKey="dashboard-looseleafs-home" 44 + pageTitle="Looseleafs" 45 + actions={<Actions />} 46 + showHeader={false} 47 + > 48 + <LooseleafList 49 + titles={props.titles} 50 + initialFacts={props.initialFacts} 51 + searchValue={debouncedSearchValue} 52 + /> 53 + </DashboardPageLayout> 47 54 ), 48 55 }, 49 56 }} 50 - pageTitle="Looseleafs" 51 57 /> 52 58 ); 53 59 };
+9 -2
app/(home-pages)/notifications/page.tsx
··· 1 1 import { getIdentityData } from "actions/getIdentityData"; 2 2 import { DashboardLayout } from "components/PageLayouts/DashboardLayout"; 3 + import { DashboardPageLayout } from "components/PageLayouts/DashboardPageLayout"; 3 4 import { redirect } from "next/navigation"; 4 5 import { hydrateNotifications } from "src/notifications"; 5 6 import { supabaseServerClient } from "supabase/serverClient"; ··· 15 16 actions={null} 16 17 tabs={{ 17 18 default: { 18 - controls: null, 19 - content: <NotificationContent />, 19 + content: ( 20 + <DashboardPageLayout 21 + scrollKey="dashboard-discover-default" 22 + showHeader={false} 23 + > 24 + <NotificationContent /> 25 + </DashboardPageLayout> 26 + ), 20 27 }, 21 28 }} 22 29 />
+14 -9
app/(home-pages)/p/[didOrHandle]/layout.tsx
··· 5 5 import { ProfileHeader } from "./ProfileHeader"; 6 6 import { ProfileTabs } from "./ProfileTabs"; 7 7 import { DashboardLayout } from "components/PageLayouts/DashboardLayout"; 8 + import { DashboardPageLayout } from "components/PageLayouts/DashboardPageLayout"; 8 9 import { ProfileLayout } from "./ProfileLayout"; 9 10 import { Agent } from "@atproto/api"; 10 11 import { get_profile_data } from "app/api/rpc/[command]/get_profile_data"; ··· 86 87 actions={null} 87 88 tabs={{ 88 89 default: { 89 - controls: null, 90 90 content: ( 91 - <ProfileLayout> 92 - <ProfileHeader 93 - profile={profile} 94 - publications={publications || []} 95 - /> 96 - <ProfileTabs didOrHandle={params.didOrHandle} /> 97 - <>{props.children}</> 98 - </ProfileLayout> 91 + <DashboardPageLayout 92 + scrollKey="dashboard-profile-default" 93 + showHeader={false} 94 + > 95 + <ProfileLayout> 96 + <ProfileHeader 97 + profile={profile} 98 + publications={publications || []} 99 + /> 100 + <ProfileTabs didOrHandle={params.didOrHandle} /> 101 + <>{props.children}</> 102 + </ProfileLayout> 103 + </DashboardPageLayout> 99 104 ), 100 105 }, 101 106 }}
+9 -2
app/(home-pages)/tag/[tag]/page.tsx
··· 1 1 import { DashboardLayout } from "components/PageLayouts/DashboardLayout"; 2 + import { DashboardPageLayout } from "components/PageLayouts/DashboardPageLayout"; 2 3 import { PostListing } from "components/PostListing"; 3 4 import { getDocumentsByTag } from "./getDocumentsByTag"; 4 5 import { Metadata } from "next"; ··· 26 27 actions={null} 27 28 tabs={{ 28 29 default: { 29 - controls: null, 30 - content: <TagContent tag={decodedTag} posts={posts} />, 30 + content: ( 31 + <DashboardPageLayout 32 + scrollKey="dashboard-tag-default" 33 + showHeader={false} 34 + > 35 + <TagContent tag={decodedTag} posts={posts} /> 36 + </DashboardPageLayout> 37 + ), 31 38 }, 32 39 }} 33 40 />
+67 -33
app/lish/[did]/[publication]/dashboard/PublicationDashboard.tsx
··· 10 10 DashboardLayout, 11 11 PublicationDashboardControls, 12 12 } from "components/PageLayouts/DashboardLayout"; 13 + import { DashboardPageLayout } from "components/PageLayouts/DashboardPageLayout"; 13 14 import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 14 15 import { type NormalizedPublication } from "src/utils/normalizeRecords"; 15 16 import { PublicationAnalytics } from "./PublicationAnalytics"; ··· 39 40 [searchValue], 40 41 ); 41 42 43 + const pubUri = publication.uri; 44 + const showPageBackground = !!record.theme?.showPageBackground; 45 + 42 46 return ( 43 47 <DashboardLayout 44 - id={publication.uri} 48 + id={pubUri} 45 49 defaultTab="Drafts" 46 50 tabs={{ 47 51 Drafts: { 48 52 content: ( 49 - <DraftList 50 - searchValue={debouncedSearchValue} 51 - showPageBackground={!!record.theme?.showPageBackground} 52 - /> 53 - ), 54 - controls: ( 55 - <PublicationDashboardControls 56 - defaultDisplay={"list"} 57 - hasBackgroundImage={!!record?.theme?.backgroundImage} 58 - searchValue={searchValue} 59 - setSearchValueAction={setSearchValue} 60 - /> 53 + <DashboardPageLayout 54 + scrollKey={`dashboard-${pubUri}-Drafts`} 55 + pageTitle={record.name} 56 + actions={<Actions publication={pubUri} />} 57 + controls={ 58 + <PublicationDashboardControls 59 + defaultDisplay={"list"} 60 + hasBackgroundImage={!!record?.theme?.backgroundImage} 61 + searchValue={searchValue} 62 + setSearchValueAction={setSearchValue} 63 + /> 64 + } 65 + publication={pubUri} 66 + showHeader={true} 67 + > 68 + <DraftList 69 + searchValue={debouncedSearchValue} 70 + showPageBackground={showPageBackground} 71 + /> 72 + </DashboardPageLayout> 61 73 ), 62 74 }, 63 75 Posts: { 64 76 content: ( 65 - <PublishedPostsList 66 - searchValue={debouncedSearchValue} 67 - showPageBackground={!!record.theme?.showPageBackground} 68 - /> 77 + <DashboardPageLayout 78 + scrollKey={`dashboard-${pubUri}-Posts`} 79 + pageTitle={record.name} 80 + actions={<Actions publication={pubUri} />} 81 + publication={pubUri} 82 + showHeader={false} 83 + > 84 + <PublishedPostsList 85 + searchValue={debouncedSearchValue} 86 + showPageBackground={showPageBackground} 87 + /> 88 + </DashboardPageLayout> 69 89 ), 70 - controls: null, 71 90 }, 72 91 Subs: { 73 92 content: ( 74 - <PublicationSubscribers 75 - showPageBackground={!!record.theme?.showPageBackground} 76 - /> 93 + <DashboardPageLayout 94 + scrollKey={`dashboard-${pubUri}-Subs`} 95 + pageTitle={record.name} 96 + actions={<Actions publication={pubUri} />} 97 + publication={pubUri} 98 + showHeader={false} 99 + > 100 + <PublicationSubscribers showPageBackground={showPageBackground} /> 101 + </DashboardPageLayout> 77 102 ), 78 - controls: null, 79 103 }, 80 104 ...(canSeePro 81 105 ? { 82 106 Analytics: { 83 107 content: ( 84 - <PublicationAnalytics 85 - showPageBackground={!!record.theme?.showPageBackground} 86 - /> 108 + <DashboardPageLayout 109 + scrollKey={`dashboard-${pubUri}-Analytics`} 110 + pageTitle={record.name} 111 + actions={<Actions publication={pubUri} />} 112 + publication={pubUri} 113 + showHeader={false} 114 + > 115 + <PublicationAnalytics showPageBackground={showPageBackground} /> 116 + </DashboardPageLayout> 87 117 ), 88 - controls: null, 89 118 }, 90 119 } 91 120 : {}), 92 121 Settings: { 93 122 icon: <SettingsTiny />, 94 123 content: ( 95 - <SettingsContent 96 - showPageBackground={!!record.theme?.showPageBackground} 97 - /> 124 + <DashboardPageLayout 125 + scrollKey={`dashboard-${pubUri}-Settings`} 126 + pageTitle={record.name} 127 + actions={<Actions publication={pubUri} />} 128 + publication={pubUri} 129 + showHeader={false} 130 + > 131 + <SettingsContent showPageBackground={showPageBackground} /> 132 + </DashboardPageLayout> 98 133 ), 99 - controls: null, 100 134 }, 101 135 }} 102 - actions={<Actions publication={publication.uri} />} 136 + actions={<Actions publication={pubUri} />} 103 137 currentPage="pub" 104 - publication={publication.uri} 105 - pageTitle={record.name} 138 + publication={pubUri} 139 + pubName={record.name} 106 140 /> 107 141 ); 108 142 }
+1 -1
app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx
··· 43 43 }); 44 44 45 45 return ( 46 - <div className="publishedList w-full flex flex-col gap-2 pb-4"> 46 + <div className="publishedList w-full flex flex-col gap-2 pt-3 pb-6"> 47 47 {sortedDocuments.map((doc) => ( 48 48 <PublishedPostItem 49 49 key={doc.uri}
+6 -5
components/ActionBar/ActionButton.tsx
··· 45 45 } 46 46 }, [sidebar, inOpenPopover]); 47 47 48 - let showLabelOnMobile = labelOnMobile !== false && (primary || secondary); 48 + // let showLabelOnMobile = labelOnMobile !== false && (primary || secondary); 49 49 50 50 return ( 51 51 <button ··· 54 54 className={` 55 55 actionButton relative 56 56 rounded-md border 57 + py-0.5 px-1 57 58 flex gap-2 items-start justify-start 58 - sm:w-full sm:max-w-full py-0.5 px-1 59 - w-max 59 + sm:w-full sm:max-w-full ${labelOnMobile ? "w-full" : "w-fit"} 60 60 outline-2 outline-transparent outline-offset-1 61 61 ${smallOnMobile && "sm:text-base text-sm py-0! sm:py-1! sm:h-fit h-6 items-center!"} 62 + 62 63 ${ 63 64 primary 64 65 ? "bg-accent-1 border-accent-1 text-accent-2 sm:hover:outline-accent-contrast focus:outline-accent-1 font-bold" ··· 70 71 `} 71 72 > 72 73 <div 73 - className={`shrink-0 flex flex-row gap-0.5 ${!primary && !secondary && "text-tertiary"}`} 74 + className={`shrink-0 flex flex-row gap-0.5 ${!primary && !secondary && "text-secondary sm:text-tertiary"}`} 74 75 > 75 76 {icon} 76 77 </div> 77 78 <div 78 - className={`flex flex-col ${subtext && "leading-snug"} sm:max-w-full min-w-0 mr-1 ${sidebar.open ? "block" : showLabelOnMobile ? "sm:hidden block" : "hidden"}`} 79 + className={`flex flex-col ${subtext && "leading-snug"} sm:max-w-full min-w-0 mr-1 ${sidebar.open ? "block" : labelOnMobile ? "sm:hidden block" : "hidden"}`} 79 80 style={{ width: "-webkit-fill-available" }} 80 81 > 81 82 <div className="truncate text-left">{label}</div>
+8 -5
components/ActionBar/DesktopNavigation.tsx
··· 58 58 {props.currentPage === "home" && ( 59 59 <> 60 60 <hr className="border-border-light my-1" /> 61 - <PublicationButtons 62 - currentPage={props.currentPage} 63 - currentPubUri={thisPublication?.uri} 64 - /> 61 + <div className="text-tertiary uppercase text-sm px-1 pt-1"> 62 + PUBLICATIONS 63 + </div> 64 + <PublicationButtons currentPage={props.currentPage} /> 65 65 </> 66 66 )} 67 67 ··· 79 79 <NotificationButton current={props.currentPage === "notifications"} /> 80 80 )} 81 81 {identity ? ( 82 - <ProfileButton /> 82 + <> 83 + <hr className="border-border-light my-1" /> 84 + <ProfileButton /> 85 + </> 83 86 ) : ( 84 87 <LoginModal 85 88 asChild
+72 -39
components/ActionBar/MobileNavigation.tsx
··· 11 11 import { ProfileButton } from "./ProfileButton"; 12 12 import { ActionButton } from "./ActionButton"; 13 13 import { AccountSmall } from "components/Icons/AccountSmall"; 14 + import { TabsSmall } from "components/Icons/TabsSmall"; 15 + import { Popover } from "components/Popover"; 16 + import { MenuSmall } from "components/Icons/MenuSmall"; 14 17 15 18 export const MobileNavigation = (props: { 16 19 currentPage: navPages; 17 - currentPublicationUri?: string; 18 - currentProfileDid?: string; 20 + pubName?: string; 21 + tabs?: { [name: string]: { icon?: React.ReactNode } }; 22 + currentTab?: string; 23 + onTabClick?: (tab: string) => void; 24 + onTabHover?: (tab: string) => void; 19 25 }) => { 20 26 let { identity } = useIdentityData(); 21 27 22 - let compactOnMobile = 28 + let isWriterPage = 23 29 props.currentPage === "home" || 24 30 props.currentPage === "looseleafs" || 25 31 props.currentPage === "pub"; 26 32 33 + let tabClassName = 34 + "font-bold text-secondary flex gap-2 items-center grow min-w-0 text-sm h-[34px] px-2 accent-container min-w-0"; 35 + 27 36 return ( 28 37 <div 29 - className={`mobileFooter w-full flex gap-4 px-1 text-secondary grow items-center justify-between`} 38 + className={`mobileFooter w-full flex gap-4 px-1 text-secondary grow items-center justify-between min-w-0`} 30 39 > 31 - <div className="mobileNav flex gap-2 items-center justify-start min-w-0"> 32 - <ReaderButton 33 - compactOnMobile={compactOnMobile} 34 - current={props.currentPage === "reader"} 35 - subs={ 36 - identity?.publication_subscriptions?.length !== 0 && 37 - identity?.publication_subscriptions?.length !== undefined 40 + {props.currentPage === "home" ? ( 41 + <PublicationNavigation currentPage={props.currentPage} /> 42 + ) : props.currentPage === "pub" && 43 + props.tabs && 44 + Object.keys(props.tabs).length > 1 ? ( 45 + <Popover 46 + className="p-1! w-48" 47 + trigger={ 48 + <div className={tabClassName}> 49 + <MenuSmall className="shrink-0" /> 50 + 51 + <div className="truncate w-full min-w-0">{props.pubName}</div> 52 + </div> 38 53 } 39 - /> 40 - <WriterButton 41 - compactOnMobile={compactOnMobile} 42 - currentPage={props.currentPage} 43 - currentPubUri={props.currentPublicationUri} 44 - /> 54 + > 55 + {Object.keys(props.tabs).map((t) => ( 56 + <ActionButton 57 + labelOnMobile 58 + key={t} 59 + icon={props.tabs![t].icon ?? <TabsSmall />} 60 + label={t} 61 + className={ 62 + t === props.currentTab ? "bg-bg-page! border-border-light!" : "" 63 + } 64 + onClick={() => props.onTabClick?.(t)} 65 + onMouseEnter={() => props.onTabHover?.(t)} 66 + onPointerDown={() => props.onTabHover?.(t)} 67 + /> 68 + ))} 69 + </Popover> 70 + ) : ( 71 + <div className="spacer flex-1" /> 72 + )} 45 73 46 - {compactOnMobile && ( 74 + <div className="mobileNav flex grow gap-2 items-center justify-start "> 75 + <WriterButton /> 76 + {isWriterPage && ( 77 + <ReaderButton 78 + subs={ 79 + identity?.publication_subscriptions?.length !== 0 && 80 + identity?.publication_subscriptions?.length !== undefined 81 + } 82 + /> 83 + )} 84 + {identity?.atp_did && ( 85 + <NotificationButton current={props.currentPage === "notifications"} /> 86 + )} 87 + {identity ? ( 47 88 <> 48 - <PublicationNavigation 49 - currentPage={props.currentPage} 50 - currentPubUri={props.currentPublicationUri} 51 - /> 89 + <Separator classname="h-6!" /> 90 + <ProfileButton /> 52 91 </> 92 + ) : ( 93 + <LoginModal 94 + asChild 95 + trigger={ 96 + <ActionButton 97 + secondary 98 + icon={<AccountSmall />} 99 + label="Log In/Sign Up" 100 + /> 101 + } 102 + /> 53 103 )} 54 104 </div> 55 - {identity ? ( 56 - <div className="flex gap-2"> 57 - {identity.atp_did && <NotificationButton />} 58 - <ProfileButton /> 59 - </div> 60 - ) : ( 61 - <LoginModal 62 - asChild 63 - trigger={ 64 - <ActionButton 65 - secondary 66 - icon={<AccountSmall />} 67 - label="Log In/Sign Up" 68 - /> 69 - } 70 - /> 71 - )} 72 105 </div> 73 106 ); 74 107 };
+2 -10
components/ActionBar/NavigationButtons.tsx
··· 41 41 }) => { 42 42 return ( 43 43 <SpeedyLink href={"/home"} className="hover:!no-underline"> 44 - <ActionButton 45 - labelOnMobile={!props.compactOnMobile} 46 - icon={<WriterSmall />} 47 - label="Write" 48 - /> 44 + <ActionButton icon={<WriterSmall />} label="Write" /> 49 45 </SpeedyLink> 50 46 ); 51 47 }; ··· 56 52 }) => { 57 53 return ( 58 54 <SpeedyLink href={"/reader"} className="hover:no-underline!"> 59 - <ActionButton 60 - labelOnMobile={!props.compactOnMobile} 61 - icon={<ReaderUnreadSmall />} 62 - label="Read" 63 - /> 55 + <ActionButton icon={<ReaderUnreadSmall />} label="Read" /> 64 56 </SpeedyLink> 65 57 ); 66 58 };
+1 -1
components/ActionBar/ProfileButton.tsx
··· 48 48 ? record.displayName || record.handle 49 49 : identity?.email || "Account" 50 50 } 51 - className={`w-full`} 51 + className={`font-bold`} 52 52 /> 53 53 } 54 54 >
+2 -8
components/ActionBar/PublicationNavigation.tsx
··· 19 19 import { Menu, MenuItem } from "components/Menu"; 20 20 import { AddTiny } from "components/Icons/AddTiny"; 21 21 22 - export const PublicationNavigation = (props: { 23 - currentPage: navPages; 24 - currentPubUri?: string; 25 - }) => { 22 + export const PublicationNavigation = (props: { currentPage: navPages }) => { 26 23 let { identity } = useIdentityData(); 27 24 28 25 if (!identity) return; ··· 121 118 > 122 119 <HomeButton current={props.currentPage === "home"} /> 123 120 <hr className="my-1 border-border-light" /> 124 - <PublicationButtons 125 - currentPage={props.currentPage} 126 - currentPubUri={props.currentPubUri} 127 - /> 121 + <PublicationButtons currentPage={props.currentPage} /> 128 122 </Popover> 129 123 ); 130 124 };
+17 -18
components/ActionBar/Publications.tsx
··· 23 23 24 24 export const PublicationButtons = (props: { 25 25 currentPage: navPages; 26 - currentPubUri: string | undefined; 27 26 className?: string; 28 - optionClassName?: string; 29 27 }) => { 30 28 let { identity } = useIdentityData(); 31 29 let hasLooseleafs = !!identity?.permission_token_on_homepage.find( ··· 48 46 <SpeedyLink href={`/looseleafs`} className={` hover:no-underline! `}> 49 47 {/*TODO How should i get if this is the current page or not? 50 48 theres not "pub" to check the uri for. Do i need to add it as an option to NavPages? thats kinda annoying*/} 51 - <ActionButton label="Looseleafs" icon={<LooseLeafSmall />} /> 49 + <ActionButton 50 + labelOnMobile 51 + label="Looseleafs" 52 + icon={<LooseLeafSmall />} 53 + /> 52 54 </SpeedyLink> 53 55 <hr className="border-border-light border-dashed my-1" /> 54 56 </> 55 57 )} 56 58 57 59 {identity.publications?.map((d) => { 58 - return ( 59 - <PublicationOption 60 - {...d} 61 - key={d.uri} 62 - record={d.record} 63 - current={d.uri === props.currentPubUri} 64 - /> 65 - ); 60 + return <PublicationOption {...d} key={d.uri} record={d.record} />; 66 61 })} 67 - <Link 62 + <SpeedyLink 68 63 href={"/lish/createPub"} 69 - className={`pubListCreateNew group/new-pub text-tertiary hover:text-accent-contrast flex gap-2 items-center p-1 no-underline! ${props.optionClassName}`} 64 + className={`pubListCreateNew no-underline!`} 70 65 > 71 - <div className="group-hover/new-pub:border-accent-contrast w-6 h-6 border-border-light border-2 border-dashed rounded-full" /> 72 - New Publication 73 - </Link> 66 + <ActionButton 67 + labelOnMobile 68 + icon=<div className="group-hover/new-pub:border-accent-contrast m-0.5 w-5 h-5 border-border border-2 border-dashed rounded-full" /> 69 + label="New" 70 + className="text-tertiary!" 71 + /> 72 + </SpeedyLink> 74 73 </div> 75 74 ); 76 75 }; ··· 79 78 uri: string; 80 79 name: string; 81 80 record: Json; 82 - current?: boolean; 83 81 className?: string; 84 82 }) => { 85 83 let record = normalizePublicationRecord(props.record); ··· 91 89 className={`hover:no-underline! `} 92 90 > 93 91 <ActionButton 92 + labelOnMobile 94 93 label={record.name} 95 94 icon={<PubIcon record={record} uri={props.uri} />} 96 - className={`w-full! ${props.current ? "bg-bg-page! border-border!" : ""} ${props.className}`} 95 + className={` ${props.className}`} 97 96 /> 98 97 </SpeedyLink> 99 98 );
+1 -1
components/ActionBar/Sidebar.tsx
··· 30 30 style={{ height: "-webkit-fill-available" }} 31 31 className={` 32 32 actionSidebar 33 - ${!props.alwaysOpen ? "absolute top-0 left-0 z-10 w-max" : "w-[192px] max-w-[192px]"} 33 + ${!props.alwaysOpen ? "absolute top-0 left-0 z-10 w-max" : "w-56"} 34 34 p-[6px] my-6 35 35 flex flex-col gap-0.5 justify-start border 36 36 rounded-md ${cardBorderHidden ? "light-container" : "frosted-container"}
+1
components/Icons/MenuSmall.tsx
··· 8 8 viewBox="0 0 24 24" 9 9 fill="none" 10 10 xmlns="http://www.w3.org/2000/svg" 11 + {...props} 11 12 > 12 13 <path 13 14 d="M19.9541 16.6455C20.6444 16.6455 21.2039 17.2053 21.2041 17.8955C21.2041 18.5859 20.6445 19.1455 19.9541 19.1455H4.04492C3.35476 19.1453 2.79492 18.5857 2.79492 17.8955C2.79509 17.2054 3.35486 16.6457 4.04492 16.6455H19.9541ZM19.9541 10.75C20.6444 10.75 21.2041 11.3097 21.2041 12C21.2041 12.6904 20.6445 13.25 19.9541 13.25H4.04492C3.35476 13.2498 2.79492 12.6902 2.79492 12C2.79495 11.3098 3.35478 10.7502 4.04492 10.75H19.9541ZM19.9541 4.85449C20.6445 4.85449 21.2041 5.41414 21.2041 6.10449C21.2039 6.79471 20.6444 7.35449 19.9541 7.35449H4.04492C3.35486 7.35426 2.79509 6.79457 2.79492 6.10449C2.79492 5.41428 3.35476 4.85472 4.04492 4.85449H19.9541Z"
+10 -92
components/PageLayouts/DashboardLayout.tsx
··· 1 1 "use client"; 2 - import { useState, createContext, useContext, useEffect } from "react"; 2 + import { useState, createContext, useContext } from "react"; 3 3 import { useSearchParams } from "next/navigation"; 4 - import { Header } from "../PageHeader"; 5 4 import { Footer } from "components/ActionBar/Footer"; 6 5 import { MobileNavigation } from "components/ActionBar/MobileNavigation"; 7 6 import { navPages } from "components/ActionBar/NavigationButtons"; ··· 11 10 import { Checkbox } from "components/Checkbox"; 12 11 import { Separator } from "components/Layout"; 13 12 import { CloseTiny } from "components/Icons/CloseTiny"; 14 - import { MediaContents } from "components/Media"; 15 - import { SortSmall } from "components/Icons/SortSmall"; 16 - import { TabsSmall } from "components/Icons/TabsSmall"; 17 13 import { Input } from "components/Input"; 18 14 import { SearchTiny } from "components/Icons/SearchTiny"; 19 15 import { InterfaceState, useIdentityData } from "components/IdentityProvider"; 20 16 import { updateIdentityInterfaceState } from "actions/updateIdentityInterfaceState"; 21 - import { usePreserveScroll } from "src/hooks/usePreserveScroll"; 22 - import { Tab } from "components/Tab"; 17 + export { PageTitle } from "./DashboardPageLayout"; 23 18 24 19 export type DashboardState = { 25 20 display?: "grid" | "list"; ··· 123 118 T extends { 124 119 [name: string]: { 125 120 content: React.ReactNode; 126 - controls: React.ReactNode; 127 121 icon?: React.ReactNode; 128 122 }; 129 123 }, ··· 133 127 defaultTab: keyof T; 134 128 currentPage: navPages; 135 129 publication?: string; 130 + pubName?: string; 136 131 profileDid?: string; 137 132 actions?: React.ReactNode; 138 - pageTitle?: string; 139 133 onTabHover?: (tabName: string) => void; 140 134 }) { 141 135 const searchParams = useSearchParams(); ··· 153 147 window.history.replaceState(null, "", newUrl); 154 148 }; 155 149 156 - let { content, controls } = props.tabs[tab]; 157 - let { ref } = usePreserveScroll<HTMLDivElement>( 158 - `dashboard-${props.id}-${tab as string}`, 159 - ); 160 - 161 - let [headerState, setHeaderState] = useState<"default" | "controls">( 162 - "default", 163 - ); 150 + let { content } = props.tabs[tab]; 164 151 165 152 return ( 166 153 <DashboardIdContext.Provider value={props.id}> ··· 176 163 onTabClick={(t) => setTabWithUrl(t as keyof T)} 177 164 onTabHover={props.onTabHover} 178 165 /> 179 - <div 180 - className={`w-full h-full flex flex-col gap-2 relative overflow-y-scroll pt-3 pb-3 px-3 sm:pt-8 sm:pb-6 sm:pl-8 sm:pr-4 `} 181 - ref={ref} 182 - id="home-content" 183 - > 184 - {props.pageTitle && ( 185 - <PageTitle pageTitle={props.pageTitle} actions={props.actions} /> 186 - )} 187 - 188 - {!controls && Object.keys(props.tabs).length <= 1 ? null : ( 189 - <Header> 190 - {headerState === "default" ? ( 191 - <> 192 - {Object.keys(props.tabs).length > 1 && ( 193 - <div className="sm:hidden pubDashTabs flex flex-row gap-1"> 194 - {Object.keys(props.tabs).map((t) => ( 195 - <Tab 196 - key={t} 197 - name={t} 198 - icon={props.tabs[t].icon} 199 - selected={t === tab} 200 - onSelect={() => setTabWithUrl(t)} 201 - onMouseEnter={() => props.onTabHover?.(t)} 202 - onPointerDown={() => props.onTabHover?.(t)} 203 - /> 204 - ))} 205 - </div> 206 - )} 207 - {props.publication && controls && ( 208 - <button 209 - className={`sm:hidden block text-tertiary`} 210 - onClick={() => setHeaderState("controls")} 211 - > 212 - <SortSmall /> 213 - </button> 214 - )} 215 - <div 216 - className={`sm:block ${props.publication && "hidden"} grow`} 217 - > 218 - {controls} 219 - </div> 220 - </> 221 - ) : ( 222 - <> 223 - {controls} 224 - <button 225 - className="text-tertiary" 226 - onClick={() => setHeaderState("default")} 227 - > 228 - <TabsSmall /> 229 - </button> 230 - </> 231 - )} 232 - </Header> 233 - )} 234 - {content} 235 - </div> 166 + {content} 236 167 <Footer> 237 168 <MobileNavigation 169 + pubName={props.pubName} 238 170 currentPage={props.currentPage} 239 - currentPublicationUri={props.publication} 240 - currentProfileDid={props.profileDid} 171 + tabs={props.tabs} 172 + currentTab={tab as string} 173 + onTabClick={(t) => setTabWithUrl(t as keyof T)} 174 + onTabHover={props.onTabHover} 241 175 /> 242 176 </Footer> 243 177 </div> 244 178 </DashboardIdContext.Provider> 245 179 ); 246 180 } 247 - 248 - export const PageTitle = (props: { 249 - pageTitle: string; 250 - actions: React.ReactNode; 251 - }) => { 252 - return ( 253 - <MediaContents 254 - mobile={true} 255 - className="flex justify-between items-center px-1 mt-1 -mb-1 w-full " 256 - > 257 - <h4 className="grow truncate">{props.pageTitle}</h4> 258 - <div className="flex flex-row-reverse! gap-1">{props.actions}</div> 259 - {/* <div className="shrink-0 h-6">{props.controls}</div> */} 260 - </MediaContents> 261 - ); 262 - }; 263 181 264 182 export const HomeDashboardControls = (props: { 265 183 searchValue: string;
+51
components/PageLayouts/DashboardPageLayout.tsx
··· 1 + "use client"; 2 + import { Header } from "../PageHeader"; 3 + import { MediaContents } from "components/Media"; 4 + import { usePreserveScroll } from "src/hooks/usePreserveScroll"; 5 + 6 + export const PageTitle = (props: { 7 + pageTitle: string; 8 + actions: React.ReactNode; 9 + }) => { 10 + return ( 11 + <MediaContents 12 + mobile={true} 13 + className="flex justify-between items-center px-1 mt-1 -mb-1 w-full " 14 + > 15 + <h4 className="grow truncate">{props.pageTitle}</h4> 16 + <div className="flex flex-row-reverse! gap-1">{props.actions}</div> 17 + </MediaContents> 18 + ); 19 + }; 20 + 21 + export function DashboardPageLayout(props: { 22 + scrollKey: string; 23 + pageTitle?: string; 24 + actions?: React.ReactNode; 25 + controls?: React.ReactNode; 26 + publication?: string; 27 + showHeader?: boolean; 28 + children: React.ReactNode; 29 + }) { 30 + let { ref } = usePreserveScroll<HTMLDivElement>(props.scrollKey); 31 + 32 + return ( 33 + <div 34 + className={`dashboardPage w-full h-full flex flex-col gap-2 relative overflow-y-scroll pt-3 pb-3 px-3 sm:pt-6 sm:pb-6 sm:pl-8 sm:pr-4`} 35 + ref={ref} 36 + id="home-content" 37 + > 38 + {props.pageTitle && ( 39 + <PageTitle pageTitle={props.pageTitle} actions={props.actions} /> 40 + )} 41 + {props.showHeader && ( 42 + <Header> 43 + <div className={`sm:block ${props.publication && "hidden"} grow`}> 44 + {props.controls} 45 + </div> 46 + </Header> 47 + )} 48 + {props.children} 49 + </div> 50 + ); 51 + }
+9 -2
components/Popover/index.tsx
··· 5 5 import { useEffect, useState } from "react"; 6 6 import { PopoverArrow } from "../Icons/PopoverArrow"; 7 7 import { PopoverOpenContext } from "./PopoverContext"; 8 + import { useCardBorderHidden } from "components/Pages/useCardBorderHidden"; 8 9 export const Popover = (props: { 9 10 trigger: React.ReactNode; 10 11 disabled?: boolean; ··· 28 29 useEffect(() => { 29 30 if (props.open !== undefined) setOpen(props.open); 30 31 }, [props.open]); 32 + let cardBorderHidden = useCardBorderHidden(); 31 33 return ( 32 34 <RadixPopover.Root 33 35 defaultOpen={props.defaultOpen} ··· 38 40 }} 39 41 > 40 42 <PopoverOpenContext value={open}> 41 - <RadixPopover.Trigger disabled={props.disabled} asChild={props.asChild}> 43 + <RadixPopover.Trigger 44 + className="min-w-0" 45 + disabled={props.disabled} 46 + asChild={props.asChild} 47 + > 42 48 {props.trigger} 43 49 </RadixPopover.Trigger> 44 50 <RadixPopover.Portal> 45 51 <NestedCardThemeProvider> 46 52 <RadixPopover.Content 47 53 className={` 48 - z-20 relative bg-bg-page 54 + z-20 relative 49 55 text-primary 50 56 flex flex-col 51 57 px-3 py-2 ··· 53 59 max-h-(--radix-popover-content-available-height) 54 60 border border-border rounded-md shadow-md 55 61 ${props.className} 62 + ${cardBorderHidden ? "light-container" : "bg-bg-page"} 56 63 `} 57 64 side={props.side} 58 65 align={props.align ? props.align : "center"}