tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
a tool for shared writing and social publishing
284
fork
atom
overview
issues
27
pulls
pipelines
Added a page title to the home layout
cozylittle.house
1 week ago
2cb5eff0
12b52565
+139
-32
7 changed files
expand all
collapse all
unified
split
app
(home-pages)
home
HomeLayout.tsx
lish
[did]
[publication]
dashboard
PublicationDashboard.tsx
components
ActionBar
ActionButton.tsx
DesktopNavigation.tsx
NavigationButtons.tsx
Publications.tsx
PageLayouts
DashboardLayout.tsx
+64
app/(home-pages)/home/HomeLayout.tsx
···
18
18
DashboardLayout,
19
19
DashboardState,
20
20
useDashboardState,
21
21
+
PageTitle,
21
22
} from "components/PageLayouts/DashboardLayout";
22
23
import { Actions } from "./Actions/Actions";
23
24
import { GetLeafletDataReturnType } from "app/api/rpc/[command]/get_leaflet_data";
···
28
29
HomeEmptyState,
29
30
PublicationBanner,
30
31
} from "./HomeEmpty/HomeEmpty";
32
32
+
import { Popover } from "components/Popover";
33
33
+
import { PubIcon, PublicationButtons } from "components/ActionBar/Publications";
34
34
+
import { normalizePublicationRecord } from "src/utils/normalizeRecords";
35
35
+
import { ButtonPrimary } from "components/Buttons";
36
36
+
import { LooseLeafSmall } from "components/Icons/LooseleafSmall";
37
37
+
import { HomeButton } from "components/ActionBar/NavigationButtons";
31
38
32
39
export type Leaflet = {
33
40
added_at: string;
···
76
83
(leaflet) => leaflet.archived === true,
77
84
).length > 0;
78
85
86
86
+
function getPubIcons() {
87
87
+
let hasLooseleafs = !!identity?.permission_token_on_homepage.find(
88
88
+
(f) =>
89
89
+
f.permission_tokens.leaflets_to_documents &&
90
90
+
f.permission_tokens.leaflets_to_documents[0]?.document,
91
91
+
);
92
92
+
93
93
+
if (identity && identity.publications.length >= 1) {
94
94
+
return (
95
95
+
<div className="flex gap-1">
96
96
+
{identity.publications.map((pub, index) => {
97
97
+
if (index <= 3)
98
98
+
return (
99
99
+
<PubIcon
100
100
+
key={pub.uri}
101
101
+
record={normalizePublicationRecord(pub.record)}
102
102
+
uri={pub.uri}
103
103
+
/>
104
104
+
);
105
105
+
})}
106
106
+
</div>
107
107
+
);
108
108
+
}
109
109
+
if (identity && hasLooseleafs) {
110
110
+
return (
111
111
+
<div className="bg-bg-leaflet rounded-full ">
112
112
+
<LooseLeafSmall className="scale-[75%]" />
113
113
+
</div>
114
114
+
);
115
115
+
} else
116
116
+
return (
117
117
+
<ButtonPrimary compact className="text-sm!">
118
118
+
Create a Publication!
119
119
+
</ButtonPrimary>
120
120
+
);
121
121
+
}
122
122
+
79
123
return (
80
124
<DashboardLayout
81
125
id="home"
···
103
147
),
104
148
},
105
149
}}
150
150
+
pageTitle={
151
151
+
<PageTitle
152
152
+
pageTitle={"Home"}
153
153
+
controls={
154
154
+
<Popover
155
155
+
trigger={<div>{getPubIcons()}</div>}
156
156
+
className="pt-1 px-2!"
157
157
+
>
158
158
+
<HomeButton current className="flex-row-reverse! justify-end!" />
159
159
+
<hr className="my-1 border-border-light" />
160
160
+
<PublicationButtons
161
161
+
currentPage={"home"}
162
162
+
currentPubUri={undefined}
163
163
+
className="justify-end!"
164
164
+
optionClassName=" flex-row-reverse!"
165
165
+
/>
166
166
+
</Popover>
167
167
+
}
168
168
+
/>
169
169
+
}
106
170
/>
107
171
);
108
172
};
+18
app/lish/[did]/[publication]/dashboard/PublicationDashboard.tsx
···
8
8
import { PublicationSubscribers } from "./PublicationSubscribers";
9
9
import {
10
10
DashboardLayout,
11
11
+
PageTitle,
11
12
PublicationDashboardControls,
12
13
} from "components/PageLayouts/DashboardLayout";
13
14
import { useDebouncedEffect } from "src/hooks/useDebouncedEffect";
14
15
import { type NormalizedPublication } from "src/utils/normalizeRecords";
16
16
+
import { PublicationButtons } from "components/ActionBar/Publications";
17
17
+
import { Popover } from "components/Popover";
15
18
16
19
export default function PublicationDashboard({
17
20
publication,
···
76
79
actions={<Actions publication={publication.uri} />}
77
80
currentPage="pub"
78
81
publication={publication.uri}
82
82
+
pageTitle={
83
83
+
<PageTitle
84
84
+
pageTitle={record.name}
85
85
+
controls={
86
86
+
<Popover trigger={<div>pubs</div>} className="pt-1 px-2!">
87
87
+
<PublicationButtons
88
88
+
currentPage={"pub"}
89
89
+
currentPubUri={publication.uri}
90
90
+
className="justify-end!"
91
91
+
optionClassName=" flex-row-reverse!"
92
92
+
/>
93
93
+
</Popover>
94
94
+
}
95
95
+
/>
96
96
+
}
79
97
/>
80
98
);
81
99
}
+1
-1
components/ActionBar/ActionButton.tsx
···
71
71
>
72
72
<div className="shrink-0 flex flex-row gap-0.5">{icon}</div>
73
73
<div
74
74
-
className={`flex flex-col pr-1 ${subtext && "leading-snug"} max-w-full min-w-0 ${sidebar.open ? "block" : showLabelOnMobile ? "sm:hidden block" : "hidden"}`}
74
74
+
className={`flex flex-col ${subtext && "leading-snug"} max-w-full min-w-0 ${sidebar.open ? "block" : showLabelOnMobile ? "sm:hidden block" : "hidden"}`}
75
75
>
76
76
<div className="truncate text-left">{label}</div>
77
77
{subtext && (
+8
-4
components/ActionBar/DesktopNavigation.tsx
···
8
8
} from "./NavigationButtons";
9
9
import { PublicationButtons } from "./Publications";
10
10
import { Sidebar } from "./Sidebar";
11
11
+
import { LoginActionButton, LoginButton } from "components/LoginButton";
11
12
12
13
export const DesktopNavigation = (props: {
13
14
currentPage: navPages;
···
24
25
props.currentPage === "pub";
25
26
return (
26
27
<div className="flex flex-col gap-3">
27
27
-
{identity?.atp_did && (
28
28
-
<Sidebar alwaysOpen>
28
28
+
<Sidebar alwaysOpen>
29
29
+
{identity?.atp_did ? (
29
30
<NotificationButton current={props.currentPage === "notifications"} />
30
30
-
</Sidebar>
31
31
-
)}
31
31
+
) : (
32
32
+
<LoginActionButton />
33
33
+
)}
34
34
+
</Sidebar>
35
35
+
32
36
<Sidebar alwaysOpen>
33
37
<ReaderButton
34
38
current={props.currentPage === "reader"}
+11
-19
components/ActionBar/NavigationButtons.tsx
···
23
23
| "notifications"
24
24
| "looseleafs"
25
25
| "tag"
26
26
-
| "profile";
26
26
+
| "profile"
27
27
+
| "discover";
27
28
28
28
-
export const HomeButton = (props: { current?: boolean }) => {
29
29
+
export const HomeButton = (props: {
30
30
+
current?: boolean;
31
31
+
className?: string;
32
32
+
}) => {
29
33
return (
30
34
<SpeedyLink href={"/home"} className="hover:!no-underline">
31
35
<ActionButton
32
36
nav
33
37
icon={<HomeSmall />}
34
34
-
label="Writer Home"
35
35
-
className={props.current ? "bg-bg-page! border-border-light!" : ""}
38
38
+
label="Write"
39
39
+
className={`${props.current ? "bg-bg-page! border-border-light!" : ""} ${props.className}`}
36
40
/>
37
41
</SpeedyLink>
38
42
);
···
82
86
nav
83
87
labelOnMobile={true}
84
88
icon={<WriterSmall />}
85
85
-
label=<div className="flex flex-row gap-1">
86
86
-
Writer
87
87
-
{current && (
88
88
-
<>
89
89
-
<Divider /> {currentIcon}
90
90
-
</>
91
91
-
)}
92
92
-
</div>
89
89
+
label=<div className="flex flex-row gap-1">Write</div>
93
90
className={current ? "bg-bg-page! border-border-light!" : ""}
94
91
/>
95
92
) : (
···
99
96
icon={
100
97
<>
101
98
<WriterSmall />
102
102
-
{current && (
103
103
-
<>
104
104
-
<Divider /> {currentIcon}
105
105
-
</>
106
106
-
)}
107
99
</>
108
100
}
109
101
label=<div className="flex flex-row gap-1">Writer</div>
···
116
108
<ActionButton
117
109
nav
118
110
icon={<HomeSmall />}
119
119
-
label="Writer Home"
111
111
+
label="Write"
120
112
className={
121
113
props.currentPage === "home"
122
114
? "bg-bg-page! border-border-light!"
···
145
137
nav
146
138
labelOnMobile={props.compactOnMobile}
147
139
icon={<ReaderUnreadSmall />}
148
148
-
label="Reader"
140
140
+
label="Read"
149
141
className={props.current ? "bg-bg-page! border-border-light!" : ""}
150
142
/>
151
143
</SpeedyLink>
+16
-7
components/ActionBar/Publications.tsx
···
20
20
import { useState } from "react";
21
21
import { LooseLeafSmall } from "components/Icons/LooseleafSmall";
22
22
import type { navPages } from "./NavigationButtons";
23
23
+
import { AddTiny } from "components/Icons/AddTiny";
23
24
24
25
export const PublicationButtons = (props: {
25
26
currentPage: navPages;
26
27
currentPubUri: string | undefined;
28
28
+
className?: string;
29
29
+
optionClassName?: string;
27
30
}) => {
28
31
let { identity } = useIdentityData();
29
32
let hasLooseleafs = !!identity?.permission_token_on_homepage.find(
···
38
41
return <PubListEmpty />;
39
42
40
43
return (
41
41
-
<div className="pubListWrapper w-full flex flex-col sm:bg-transparent sm:border-0">
44
44
+
<div
45
45
+
className={`pubListWrapper w-full flex flex-col sm:bg-transparent sm:border-0 ${props.className}`}
46
46
+
>
42
47
{hasLooseleafs && (
43
48
<>
44
49
<SpeedyLink
45
50
href={`/looseleafs`}
46
46
-
className="flex gap-2 items-start text-secondary font-bold hover:no-underline! hover:text-accent-contrast w-full"
51
51
+
className={`flex gap-2 items-start text-secondary font-bold hover:no-underline! hover:text-accent-contrast w-full `}
47
52
>
48
53
{/*TODO How should i get if this is the current page or not?
49
54
theres not "pub" to check the uri for. Do i need to add it as an option to NavPages? thats kinda annoying*/}
···
51
56
label="Looseleafs"
52
57
icon={<LooseLeafSmall />}
53
58
nav
54
54
-
className={
59
59
+
className={`${
55
60
props.currentPage === "looseleafs"
56
61
? "bg-bg-page! border-border!"
57
62
: ""
58
63
}
64
64
+
${props.optionClassName}`}
59
65
/>
60
66
</SpeedyLink>
61
67
</>
···
68
74
key={d.uri}
69
75
record={d.record}
70
76
current={d.uri === props.currentPubUri}
77
77
+
className={props.optionClassName}
71
78
/>
72
79
);
73
80
})}
74
81
<Link
75
82
href={"/lish/createPub"}
76
76
-
className="pubListCreateNew text-accent-contrast text-sm place-self-end hover:text-accent-contrast"
83
83
+
className={`pubListCreateNew group/new-pub text-tertiary hover:text-accent-contrast flex gap-2 items-center p-1 no-underline! ${props.optionClassName}`}
77
84
>
78
78
-
New
85
85
+
<div className="group-hover/new-pub:border-accent-contrast w-6 h-6 border-border-light border-2 border-dashed rounded-full" />
86
86
+
New Publication
79
87
</Link>
80
88
</div>
81
89
);
···
86
94
name: string;
87
95
record: Json;
88
96
current?: boolean;
97
97
+
className?: string;
89
98
}) => {
90
99
let record = normalizePublicationRecord(props.record);
91
100
if (!record) return;
···
93
102
return (
94
103
<SpeedyLink
95
104
href={`${getBasePublicationURL(props)}/dashboard`}
96
96
-
className="flex gap-2 items-start text-secondary font-bold hover:no-underline! hover:text-accent-contrast w-full"
105
105
+
className={`flex gap-2 items-start text-secondary font-bold hover:no-underline! hover:text-accent-contrast w-full `}
97
106
>
98
107
<ActionButton
99
108
label={record.name}
100
109
icon={<PubIcon record={record} uri={props.uri} />}
101
110
nav
102
102
-
className={props.current ? "bg-bg-page! border-border!" : ""}
111
111
+
className={`${props.current ? "bg-bg-page! border-border!" : ""} ${props.className}`}
103
112
/>
104
113
</SpeedyLink>
105
114
);
+21
-1
components/PageLayouts/DashboardLayout.tsx
···
27
27
import { ExternalLinkTiny } from "components/Icons/ExternalLinkTiny";
28
28
import { usePreserveScroll } from "src/hooks/usePreserveScroll";
29
29
import { Tab } from "components/Tab";
30
30
+
import { PubIcon, PublicationButtons } from "components/ActionBar/Publications";
30
31
31
32
export type DashboardState = {
32
33
display?: "grid" | "list";
···
141
142
publication?: string;
142
143
profileDid?: string;
143
144
actions: React.ReactNode;
145
145
+
pageTitle?: React.ReactNode;
144
146
}) {
145
147
const searchParams = useSearchParams();
146
148
const tabParam = searchParams.get("tab");
···
167
169
let [headerState, setHeaderState] = useState<"default" | "controls">(
168
170
"default",
169
171
);
172
172
+
170
173
return (
171
174
<DashboardIdContext.Provider value={props.id}>
172
175
<div
···
186
189
ref={ref}
187
190
id="home-content"
188
191
>
192
192
+
{props.pageTitle}
193
193
+
189
194
{Object.keys(props.tabs).length <= 1 && !controls ? null : (
190
195
<>
191
196
<Header>
···
256
261
);
257
262
}
258
263
264
264
+
export const PageTitle = (props: {
265
265
+
pageTitle: string;
266
266
+
controls: React.ReactNode;
267
267
+
}) => {
268
268
+
return (
269
269
+
<MediaContents
270
270
+
mobile={true}
271
271
+
className="flex justify-between items-center px-1 mt-1 -mb-1 w-full "
272
272
+
>
273
273
+
<h4 className="grow truncate">{props.pageTitle}</h4>
274
274
+
<div className="shrink-0 h-6">{props.controls}</div>
275
275
+
</MediaContents>
276
276
+
);
277
277
+
};
278
278
+
259
279
export const HomeDashboardControls = (props: {
260
280
searchValue: string;
261
281
setSearchValueAction: (searchValue: string) => void;
···
449
469
className={`dashboardSearchInput
450
470
appearance-none! outline-hidden!
451
471
w-full min-w-0 text-primary relative pl-7 pr-1 -my-px
452
452
-
border rounded-md border-transparent focus-within:border-border
472
472
+
border rounded-md border-border-light focus-within:border-border
453
473
bg-transparent ${props.hasBackgroundImage ? "focus-within:bg-bg-page" : "focus-within:bg-bg-leaflet"} `}
454
474
type="text"
455
475
id="pubName"