a tool for shared writing and social publishing

moved home theme into settings, adjusted menu styles

+259 -106
+121 -8
app/(home-pages)/home/Actions/AccountSettings.tsx
··· 1 1 "use client"; 2 2 3 3 import { ActionButton } from "components/ActionBar/ActionButton"; 4 - import { Menu, MenuItem } from "components/Layout"; 5 4 import { mutate } from "swr"; 6 5 import { AccountSmall } from "components/Icons/AccountSmall"; 7 6 import { LogoutSmall } from "components/Icons/LogoutSmall"; 7 + import { Popover } from "components/Popover"; 8 + import { ArrowRightTiny } from "components/Icons/ArrowRightTiny"; 9 + import { SpeedyLink } from "components/SpeedyLink"; 10 + import { GoBackSmall } from "components/Icons/GoBackSmall"; 11 + import { useState } from "react"; 12 + import { ThemeSetterContent } from "components/ThemeManager/ThemeSetter"; 8 13 9 - // it was going have a popover with a log out button 10 - export const AccountSettings = () => { 14 + export const AccountSettings = (props: { entityID: string }) => { 15 + let [state, setState] = useState<"menu" | "general" | "theme">("menu"); 16 + 11 17 return ( 12 - <Menu 18 + <Popover 13 19 asChild 20 + onOpenChange={() => setState("menu")} 21 + side="right" 22 + align="start" 23 + className={`max-w-xs w-[1000px] ${state === "theme" && "bg-white!"}`} 14 24 trigger={<ActionButton icon=<AccountSmall /> label="Settings" />} 15 25 > 16 - <MenuItem 17 - onSelect={async () => { 26 + {state === "general" ? ( 27 + <GeneralSettings backToMenu={() => setState("menu")} /> 28 + ) : state === "theme" ? ( 29 + <AccountThemeSettings 30 + entityID={props.entityID} 31 + backToMenu={() => setState("menu")} 32 + /> 33 + ) : ( 34 + <SettingsMenu state={state} setState={setState} /> 35 + )} 36 + </Popover> 37 + ); 38 + }; 39 + 40 + const SettingsMenu = (props: { 41 + state: "menu" | "general" | "theme"; 42 + setState: (s: typeof props.state) => void; 43 + }) => { 44 + let menuItemClassName = 45 + "menuItem -mx-[8px] text-left flex items-center justify-between"; 46 + 47 + return ( 48 + <div className="flex flex-col gap-0.5"> 49 + <AccountSettingsHeader state={"menu"} /> 50 + <button 51 + className={menuItemClassName} 52 + type="button" 53 + onClick={() => { 54 + props.setState("general"); 55 + }} 56 + > 57 + Settings 58 + <ArrowRightTiny /> 59 + </button> 60 + <button 61 + className={menuItemClassName} 62 + type="button" 63 + onClick={() => props.setState("theme")} 64 + > 65 + Theme 66 + <ArrowRightTiny /> 67 + </button> 68 + <SpeedyLink 69 + className={menuItemClassName} 70 + href="https://about.leaflet.pub/ 71 + " 72 + > 73 + About Leaflet 74 + <ArrowRightTiny /> 75 + </SpeedyLink> 76 + </div> 77 + ); 78 + }; 79 + 80 + const GeneralSettings = (props: { backToMenu: () => void }) => { 81 + return ( 82 + <div className="flex flex-col gap-0.5"> 83 + <AccountSettingsHeader 84 + state={"general"} 85 + backToMenu={() => props.backToMenu()} 86 + /> 87 + 88 + <button 89 + className="flex gap-2 font-bold" 90 + onClick={async () => { 18 91 await fetch("/api/auth/logout"); 19 92 mutate("identity", null); 20 93 }} 21 94 > 22 95 <LogoutSmall /> 23 96 Logout 24 - </MenuItem> 25 - </Menu> 97 + </button> 98 + </div> 99 + ); 100 + }; 101 + const AccountThemeSettings = (props: { 102 + entityID: string; 103 + backToMenu: () => void; 104 + }) => { 105 + return ( 106 + <div className="flex flex-col gap-0.5"> 107 + <AccountSettingsHeader 108 + state={"theme"} 109 + backToMenu={() => props.backToMenu()} 110 + /> 111 + <ThemeSetterContent entityID={props.entityID} home /> 112 + </div> 113 + ); 114 + }; 115 + export const AccountSettingsHeader = (props: { 116 + state: "menu" | "general" | "theme"; 117 + backToMenu?: () => void; 118 + }) => { 119 + return ( 120 + <div className="flex justify-between font-bold text-secondary bg-border-light -mx-3 -mt-2 px-3 pt-2 pb-1 mb-1"> 121 + {props.state === "menu" 122 + ? "Settings" 123 + : props.state === "general" 124 + ? "General" 125 + : props.state === "theme" 126 + ? "Publication Theme" 127 + : ""} 128 + {props.backToMenu && ( 129 + <button 130 + type="button" 131 + onClick={() => { 132 + props.backToMenu && props.backToMenu(); 133 + }} 134 + > 135 + <GoBackSmall className="text-accent-contrast" /> 136 + </button> 137 + )} 138 + </div> 26 139 ); 27 140 };
+5 -3
app/(home-pages)/home/Actions/Actions.tsx
··· 13 13 return ( 14 14 <> 15 15 <CreateNewLeafletButton /> 16 - {identity ? <AccountSettings /> : <LoginActionButton />} 17 - {/*<HelpPopover noShortcuts />*/} 18 - <ThemePopover entityID={rootEntity} home /> 16 + {identity ? ( 17 + <AccountSettings entityID={rootEntity} /> 18 + ) : ( 19 + <LoginActionButton /> 20 + )} 19 21 <HelpPopover /> 20 22 </> 21 23 );
+20
app/globals.css
··· 400 400 @apply rounded-md; 401 401 } 402 402 403 + .menuItem { 404 + @apply text-secondary; 405 + @apply hover:text-secondary; 406 + @apply data-highlighted:bg-[var(--accent-light)]; 407 + @apply data-highlighted:outline-none; 408 + @apply hover:bg-[var(--accent-light)]; 409 + text-align: left; 410 + font-weight: 800; 411 + padding: 0.25rem 0.5rem; 412 + border-radius: 0.25rem; 413 + outline: none !important; 414 + cursor: pointer; 415 + background-color: transparent; 416 + 417 + :hover { 418 + text-decoration: none !important; 419 + background-color: rgb(var(--accent-light)); 420 + } 421 + } 422 + 403 423 .pwa-padding { 404 424 padding-top: max(calc(env(safe-area-inset-top) - 8px)) !important; 405 425 }
+6 -5
app/lish/[did]/[publication]/dashboard/Actions.tsx
··· 100 100 return ( 101 101 <Popover 102 102 asChild 103 + onOpenChange={() => setState("menu")} 103 104 side={isMobile ? "top" : "right"} 104 105 align={isMobile ? "center" : "start"} 105 106 className={`max-w-xs w-[1000px] ${state === "theme" && "bg-white!"}`} ··· 114 115 > 115 116 {state === "general" ? ( 116 117 <EditPubForm 117 - goBack={() => setState("menu")} 118 + backToMenu={() => setState("menu")} 118 119 loading={loading} 119 120 setLoading={setLoading} 120 121 /> 121 122 ) : state === "theme" ? ( 122 123 <PubThemeSetter 123 - goBack={() => setState("menu")} 124 + backToMenu={() => setState("menu")} 124 125 loading={loading} 125 126 setLoading={setLoading} 126 127 /> ··· 143 144 setLoading: (l: boolean) => void; 144 145 }) => { 145 146 let menuItemClassName = 146 - "hover:bg-[var(--accent-light)] text-secondary hover:no-underline! rounded-md px-2 py-0.5 -mx-[8px] font-bold text-left flex items-center justify-between"; 147 + "menuItem -mx-[8px] text-left flex items-center justify-between"; 147 148 148 149 return ( 149 150 <div className="flex flex-col gap-0.5"> ··· 184 185 185 186 export const PubSettingsHeader = (props: { 186 187 state: "menu" | "general" | "theme"; 187 - goBackAction?: () => void; 188 + backToMenu?: () => void; 188 189 loading: boolean; 189 190 setLoadingAction: (l: boolean) => void; 190 191 }) => { ··· 202 203 <button 203 204 type="button" 204 205 onClick={() => { 205 - props.goBackAction && props.goBackAction(); 206 + props.backToMenu && props.backToMenu(); 206 207 }} 207 208 > 208 209 <GoBackSmall className="text-accent-contrast" />
+2 -2
app/lish/createPub/UpdatePubForm.tsx
··· 23 23 import { PubSettingsHeader } from "../[did]/[publication]/dashboard/Actions"; 24 24 25 25 export const EditPubForm = (props: { 26 - goBack: () => void; 26 + backToMenu: () => void; 27 27 loading: boolean; 28 28 setLoading: (l: boolean) => void; 29 29 }) => { ··· 85 85 <PubSettingsHeader 86 86 loading={props.loading} 87 87 setLoadingAction={props.setLoading} 88 - goBackAction={props.goBack} 88 + backToMenu={props.backToMenu} 89 89 state={"theme"} 90 90 /> 91 91 <div className="flex flex-col gap-3 w-[1000px] max-w-full pb-2">
+3 -8
components/Layout.tsx
··· 45 45 alignOffset={props.alignOffset ? props.alignOffset : undefined} 46 46 sideOffset={4} 47 47 collisionPadding={16} 48 - className={`dropdownMenu z-20 bg-bg-page flex flex-col py-1 gap-0.5 border border-border rounded-md shadow-md ${props.className}`} 48 + className={`dropdownMenu z-20 bg-bg-page flex flex-col p-1 gap-0.5 border border-border rounded-md shadow-md ${props.className}`} 49 49 > 50 50 {props.children} 51 51 <DropdownMenu.Arrow ··· 86 86 props.onSelect(event); 87 87 }} 88 88 className={` 89 - MenuItem 90 - font-bold z-10 py-1 px-3 91 - text-left text-secondary 89 + menuItem 90 + z-10 py-1! px-2! 92 91 flex gap-2 93 - data-highlighted:bg-border-light data-highlighted:text-secondary 94 - hover:bg-border-light hover:text-secondary 95 - outline-hidden 96 - cursor-pointer 97 92 ${props.className} 98 93 `} 99 94 >
+2 -2
components/ThemeManager/PubThemeSetter.tsx
··· 25 25 repeat: number | null; 26 26 }; 27 27 export const PubThemeSetter = (props: { 28 - goBack: () => void; 28 + backToMenu: () => void; 29 29 loading: boolean; 30 30 setLoading: (l: boolean) => void; 31 31 }) => { ··· 95 95 <PubSettingsHeader 96 96 loading={props.loading} 97 97 setLoadingAction={props.setLoading} 98 - goBackAction={props.goBack} 98 + backToMenu={props.backToMenu} 99 99 state={"theme"} 100 100 /> 101 101 </form>
+100 -78
components/ThemeManager/ThemeSetter.tsx
··· 82 82 align={isMobile ? "center" : "start"} 83 83 trigger={<ActionButton icon={<PaintSmall />} label="Theme" />} 84 84 > 85 - <div className="themeSetterContent flex flex-col w-full overflow-y-scroll no-scrollbar"> 86 - <div className="themeBGLeaflet flex"> 87 - <div 88 - className={`bgPicker flex flex-col gap-0 -mb-[6px] z-10 w-full `} 89 - > 90 - <div className="bgPickerBody w-full flex flex-col gap-2 p-2 mt-1 border border-[#CCCCCC] rounded-md"> 91 - <LeafletBGPicker 92 - entityID={props.entityID} 93 - thisPicker={"leaflet"} 94 - openPicker={openPicker} 95 - setOpenPicker={setOpenPicker} 96 - closePicker={() => setOpenPicker("null")} 97 - setValue={set("theme/page-background")} 98 - /> 99 - <PageBackgroundPicker 100 - entityID={props.entityID} 101 - setValue={set("theme/card-background")} 102 - openPicker={openPicker} 103 - setOpenPicker={setOpenPicker} 104 - home={props.home} 105 - /> 106 - <hr className=" border-[#CCCCCC]" /> 107 - <PageBorderHider 108 - entityID={props.entityID} 109 - openPicker={openPicker} 110 - setOpenPicker={setOpenPicker} 111 - /> 112 - </div> 85 + <ThemeSetterContent {...props} /> 86 + </Popover> 87 + </> 88 + ); 89 + }; 90 + 91 + export const ThemeSetterContent = (props: { 92 + entityID: string; 93 + home?: boolean; 94 + }) => { 95 + let { rep } = useReplicache(); 96 + let { data: pub } = useLeafletPublicationData(); 97 + 98 + // I need to get these variables from replicache and then write them to the DB. I also need to parse them into a state that can be used here. 99 + let permission = useEntitySetContext().permissions.write; 100 + let leafletBGImage = useEntity(props.entityID, "theme/background-image"); 101 + let leafletBGRepeat = useEntity( 102 + props.entityID, 103 + "theme/background-image-repeat", 104 + ); 113 105 114 - <SectionArrow 115 - fill="white" 116 - stroke="#CCCCCC" 117 - className="ml-2 -mt-px" 118 - /> 119 - </div> 120 - </div> 106 + let [openPicker, setOpenPicker] = useState<pickers>( 107 + props.home === true ? "leaflet" : "null", 108 + ); 109 + let set = useMemo(() => { 110 + return setColorAttribute(rep, props.entityID); 111 + }, [rep, props.entityID]); 121 112 122 - <div 123 - onClick={(e) => { 124 - e.currentTarget === e.target && setOpenPicker("leaflet"); 125 - }} 126 - style={{ 127 - backgroundImage: leafletBGImage 128 - ? `url(${leafletBGImage.data.src})` 129 - : undefined, 130 - backgroundRepeat: leafletBGRepeat ? "repeat" : "no-repeat", 131 - backgroundPosition: "center", 132 - backgroundSize: !leafletBGRepeat 133 - ? "cover" 134 - : `calc(${leafletBGRepeat.data.value}px / 2 )`, 135 - }} 136 - className={`bg-bg-leaflet px-3 pt-4 pb-0 mb-2 flex flex-col gap-4 rounded-md border border-border`} 137 - > 138 - <PageThemePickers 113 + if (!permission) return null; 114 + if (pub) return null; 115 + return ( 116 + <div className="themeSetterContent flex flex-col w-full overflow-y-scroll no-scrollbar"> 117 + <div className="themeBGLeaflet flex"> 118 + <div className={`bgPicker flex flex-col gap-0 -mb-[6px] z-10 w-full `}> 119 + <div className="bgPickerBody w-full flex flex-col gap-2 p-2 mt-1 border border-[#CCCCCC] rounded-md"> 120 + <LeafletBGPicker 139 121 entityID={props.entityID} 122 + thisPicker={"leaflet"} 140 123 openPicker={openPicker} 141 - setOpenPicker={(pickers) => setOpenPicker(pickers)} 124 + setOpenPicker={setOpenPicker} 125 + closePicker={() => setOpenPicker("null")} 126 + setValue={set("theme/page-background")} 142 127 /> 143 - <div className="flex flex-col -gap-[6px]"> 144 - <div className={`flex flex-col z-10 -mb-[6px] `}> 145 - <AccentPickers 146 - entityID={props.entityID} 147 - openPicker={openPicker} 148 - setOpenPicker={(pickers) => setOpenPicker(pickers)} 149 - /> 150 - <SectionArrow 151 - fill={theme.colors["accent-2"]} 152 - stroke={theme.colors["accent-1"]} 153 - className="ml-2" 154 - /> 155 - </div> 128 + <PageBackgroundPicker 129 + entityID={props.entityID} 130 + setValue={set("theme/card-background")} 131 + openPicker={openPicker} 132 + setOpenPicker={setOpenPicker} 133 + home={props.home} 134 + /> 135 + <hr className=" border-[#CCCCCC]" /> 136 + <PageBorderHider 137 + entityID={props.entityID} 138 + openPicker={openPicker} 139 + setOpenPicker={setOpenPicker} 140 + /> 141 + </div> 156 142 157 - <SampleButton 158 - entityID={props.entityID} 159 - setOpenPicker={setOpenPicker} 160 - /> 161 - </div> 143 + <SectionArrow fill="white" stroke="#CCCCCC" className="ml-2 -mt-px" /> 144 + </div> 145 + </div> 162 146 163 - <SamplePage 164 - setOpenPicker={setOpenPicker} 165 - home={props.home} 147 + <div 148 + onClick={(e) => { 149 + e.currentTarget === e.target && setOpenPicker("leaflet"); 150 + }} 151 + style={{ 152 + backgroundImage: leafletBGImage 153 + ? `url(${leafletBGImage.data.src})` 154 + : undefined, 155 + backgroundRepeat: leafletBGRepeat ? "repeat" : "no-repeat", 156 + backgroundPosition: "center", 157 + backgroundSize: !leafletBGRepeat 158 + ? "cover" 159 + : `calc(${leafletBGRepeat.data.value}px / 2 )`, 160 + }} 161 + className={`bg-bg-leaflet px-3 pt-4 pb-0 mb-2 flex flex-col gap-4 rounded-md border border-border`} 162 + > 163 + <PageThemePickers 164 + entityID={props.entityID} 165 + openPicker={openPicker} 166 + setOpenPicker={(pickers) => setOpenPicker(pickers)} 167 + /> 168 + <div className="flex flex-col -gap-[6px]"> 169 + <div className={`flex flex-col z-10 -mb-[6px] `}> 170 + <AccentPickers 166 171 entityID={props.entityID} 172 + openPicker={openPicker} 173 + setOpenPicker={(pickers) => setOpenPicker(pickers)} 174 + /> 175 + <SectionArrow 176 + fill={theme.colors["accent-2"]} 177 + stroke={theme.colors["accent-1"]} 178 + className="ml-2" 167 179 /> 168 180 </div> 169 - {!props.home && <WatermarkSetter entityID={props.entityID} />} 181 + 182 + <SampleButton 183 + entityID={props.entityID} 184 + setOpenPicker={setOpenPicker} 185 + /> 170 186 </div> 171 - </Popover> 172 - </> 187 + 188 + <SamplePage 189 + setOpenPicker={setOpenPicker} 190 + home={props.home} 191 + entityID={props.entityID} 192 + /> 193 + </div> 194 + {!props.home && <WatermarkSetter entityID={props.entityID} />} 195 + </div> 173 196 ); 174 197 }; 175 - 176 198 function WatermarkSetter(props: { entityID: string }) { 177 199 let { rep } = useReplicache(); 178 200 let checked = useEntity(props.entityID, "theme/page-leaflet-watermark");