tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
a tool for shared writing and social publishing
276
fork
atom
overview
issues
25
pulls
pipelines
Compare changes
Choose any two refs to compare.
base:
update/wider-page
update/thread-viewer
update/looseleafs
update/delete-leaflets
update/delete-blocks
test/unknown-marks
refactor/standard.site
refactor/shared-home-layout
main
fix/profile-popover
fix/bottom-scroll-margin-on-docs
fix/accent-contrast-on-page
feature/thread-viewer
feature/tags
feature/small-text
feature/set-page-width
feature/reader
feature/publish-leaflets
feature/pub-contributors
feature/profiles
feature/post-options
feature/page-blocks
feature/notifications
feature/hard_breaks
feature/fonts
feature/follow-via-email
feature/bsky-follows-feed
feature/backdate
feature/atp-polls
feature/atp-canvas-blocks
feature/at-mentions
debug/datetime
no tags found
compare:
update/wider-page
update/thread-viewer
update/looseleafs
update/delete-leaflets
update/delete-blocks
test/unknown-marks
refactor/standard.site
refactor/shared-home-layout
main
fix/profile-popover
fix/bottom-scroll-margin-on-docs
fix/accent-contrast-on-page
feature/thread-viewer
feature/tags
feature/small-text
feature/set-page-width
feature/reader
feature/publish-leaflets
feature/pub-contributors
feature/profiles
feature/post-options
feature/page-blocks
feature/notifications
feature/hard_breaks
feature/fonts
feature/follow-via-email
feature/bsky-follows-feed
feature/backdate
feature/atp-polls
feature/atp-canvas-blocks
feature/at-mentions
debug/datetime
no tags found
go
+134
-357
31 changed files
expand all
collapse all
unified
split
actions
publishToPublication.ts
app
[leaflet_id]
actions
PublishButton.tsx
lish
[did]
[publication]
[rkey]
CanvasPage.tsx
Interactions
Comments
index.tsx
InteractionDrawer.tsx
Interactions.tsx
LinearDocumentPage.tsx
PostHeader
PostHeader.tsx
PostPages.tsx
PostPrevNextButtons.tsx
QuoteHandler.tsx
getPostPageData.ts
dashboard
PublishedPostsLists.tsx
settings
PostOptions.tsx
PublicationSettings.tsx
page.tsx
createPub
CreatePubForm.tsx
UpdatePubForm.tsx
updatePublication.ts
components
Canvas.tsx
InteractionsPreview.tsx
Pages
PublicationMetadata.tsx
PostListing.tsx
ThemeManager
PublicationThemeProvider.tsx
ThemeProvider.tsx
ThemeSetter.tsx
themeUtils.ts
lexicons
api
lexicons.ts
types
pub
leaflet
publication.ts
pub
leaflet
publication.json
src
publication.ts
-2
actions/publishToPublication.ts
···
784
784
root_entity,
785
785
"theme/background-image-repeat",
786
786
)?.[0];
787
787
-
let pageWidth = scan.eav(root_entity, "theme/page-width")?.[0];
788
787
789
788
let theme: PubLeafletPublication.Theme = {
790
789
showPageBackground: showPageBackground ?? true,
791
790
};
792
791
793
793
-
if (pageWidth) theme.pageWidth = pageWidth.data.value;
794
792
if (pageBackground)
795
793
theme.backgroundColor = ColorToRGBA(parseColor(`hsba(${pageBackground})`));
796
794
if (cardBackground)
+2
-2
app/[leaflet_id]/actions/PublishButton.tsx
···
136
136
content: (
137
137
<div>
138
138
{pub.doc ? "Updated! " : "Published! "}
139
139
-
<SpeedyLink className="underline" href={docUrl}>
140
140
-
See Published Post
139
139
+
<SpeedyLink className="underline font-bold" href={docUrl}>
140
140
+
See Post
141
141
</SpeedyLink>
142
142
</div>
143
143
),
+1
-6
app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx
···
202
202
isSubpage: boolean | undefined;
203
203
data: PostPageData;
204
204
profile: ProfileViewDetailed;
205
205
-
preferences: {
206
206
-
showComments?: boolean;
207
207
-
showMentions?: boolean;
208
208
-
showPrevNext?: boolean;
209
209
-
};
205
205
+
preferences: { showComments?: boolean };
210
206
quotesCount: number | undefined;
211
207
commentsCount: number | undefined;
212
208
}) => {
···
217
213
quotesCount={props.quotesCount || 0}
218
214
commentsCount={props.commentsCount || 0}
219
215
showComments={props.preferences.showComments}
220
220
-
showMentions={props.preferences.showMentions}
221
216
pageId={props.pageId}
222
217
/>
223
218
{!props.isSubpage && (
+1
-4
app/lish/[did]/[publication]/[rkey]/Interactions/Comments/index.tsx
···
51
51
}, []);
52
52
53
53
return (
54
54
-
<div
55
55
-
id={"commentsDrawer"}
56
56
-
className="flex flex-col gap-2 relative text-sm text-secondary"
57
57
-
>
54
54
+
<div id={"commentsDrawer"} className="flex flex-col gap-2 relative">
58
55
<div className="w-full flex justify-between text-secondary font-bold">
59
56
Comments
60
57
<button
+1
-2
app/lish/[did]/[publication]/[rkey]/Interactions/InteractionDrawer.tsx
···
9
9
import { decodeQuotePosition } from "../quotePosition";
10
10
11
11
export const InteractionDrawer = (props: {
12
12
-
showPageBackground: boolean | undefined;
13
12
document_uri: string;
14
13
quotesAndMentions: { uri: string; link?: string }[];
15
14
comments: Comment[];
···
39
38
<div className="snap-center h-full flex z-10 shrink-0 w-[calc(var(--page-width-units)-6px)] sm:w-[calc(var(--page-width-units))]">
40
39
<div
41
40
id="interaction-drawer"
42
42
-
className={`opaque-container h-full w-full px-3 sm:px-4 pt-2 sm:pt-3 pb-6 overflow-scroll -ml-[1px] ${props.showPageBackground ? "rounded-l-none! rounded-r-lg!" : "rounded-lg! sm:mx-2"}`}
41
41
+
className="opaque-container rounded-l-none! rounded-r-lg! h-full w-full px-3 sm:px-4 pt-2 sm:pt-3 pb-6 overflow-scroll -ml-[1px] "
43
42
>
44
43
{drawer.drawer === "quotes" ? (
45
44
<Quotes {...props} quotesAndMentions={filteredQuotesAndMentions} />
+44
-68
app/lish/[did]/[publication]/[rkey]/Interactions/Interactions.tsx
···
108
108
commentsCount: number;
109
109
className?: string;
110
110
showComments?: boolean;
111
111
-
showMentions?: boolean;
112
111
pageId?: string;
113
112
}) => {
114
113
const data = useContext(PostPageContext);
···
132
131
<div className={`flex gap-2 text-tertiary text-sm ${props.className}`}>
133
132
{tagCount > 0 && <TagPopover tags={tags} tagCount={tagCount} />}
134
133
135
135
-
{props.quotesCount === 0 || props.showMentions === false ? null : (
134
134
+
{props.quotesCount > 0 && (
136
135
<button
137
136
className="flex w-fit gap-2 items-center"
138
137
onClick={() => {
···
169
168
commentsCount: number;
170
169
className?: string;
171
170
showComments?: boolean;
172
172
-
showMentions?: boolean;
173
171
pageId?: string;
174
172
}) => {
175
173
const data = useContext(PostPageContext);
···
191
189
const tags = (data?.data as any)?.tags as string[] | undefined;
192
190
const tagCount = tags?.length || 0;
193
191
194
194
-
let noInteractions = !props.showComments && !props.showMentions;
195
195
-
196
192
let subscribed =
197
193
identity?.atp_did &&
198
194
publication?.publication_subscriptions &&
···
233
229
<TagList tags={tags} className="mb-3" />
234
230
</>
235
231
)}
236
236
-
237
232
<hr className="border-border-light mb-3 " />
238
238
-
239
233
<div className="flex gap-2 justify-between">
240
240
-
{noInteractions ? (
241
241
-
<div />
242
242
-
) : (
243
243
-
<>
244
244
-
<div className="flex gap-2">
245
245
-
{props.quotesCount === 0 ||
246
246
-
props.showMentions === false ? null : (
247
247
-
<button
248
248
-
className="flex w-fit gap-2 items-center px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"
249
249
-
onClick={() => {
250
250
-
if (!drawerOpen || drawer !== "quotes")
251
251
-
openInteractionDrawer(
252
252
-
"quotes",
253
253
-
document_uri,
254
254
-
props.pageId,
255
255
-
);
256
256
-
else
257
257
-
setInteractionState(document_uri, { drawerOpen: false });
258
258
-
}}
259
259
-
onMouseEnter={handleQuotePrefetch}
260
260
-
onTouchStart={handleQuotePrefetch}
261
261
-
aria-label="Post quotes"
262
262
-
>
263
263
-
<QuoteTiny aria-hidden /> {props.quotesCount}{" "}
264
264
-
<span
265
265
-
aria-hidden
266
266
-
>{`Mention${props.quotesCount === 1 ? "" : "s"}`}</span>
267
267
-
</button>
268
268
-
)}
269
269
-
{props.showComments === false ? null : (
270
270
-
<button
271
271
-
className="flex gap-2 items-center w-fit px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"
272
272
-
onClick={() => {
273
273
-
if (
274
274
-
!drawerOpen ||
275
275
-
drawer !== "comments" ||
276
276
-
pageId !== props.pageId
277
277
-
)
278
278
-
openInteractionDrawer(
279
279
-
"comments",
280
280
-
document_uri,
281
281
-
props.pageId,
282
282
-
);
283
283
-
else
284
284
-
setInteractionState(document_uri, { drawerOpen: false });
285
285
-
}}
286
286
-
aria-label="Post comments"
287
287
-
>
288
288
-
<CommentTiny aria-hidden />{" "}
289
289
-
{props.commentsCount > 0 ? (
290
290
-
<span aria-hidden>
291
291
-
{`${props.commentsCount} Comment${props.commentsCount === 1 ? "" : "s"}`}
292
292
-
</span>
293
293
-
) : (
294
294
-
"Comment"
295
295
-
)}
296
296
-
</button>
234
234
+
<div className="flex gap-2">
235
235
+
{props.quotesCount > 0 && (
236
236
+
<button
237
237
+
className="flex w-fit gap-2 items-center px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"
238
238
+
onClick={() => {
239
239
+
if (!drawerOpen || drawer !== "quotes")
240
240
+
openInteractionDrawer("quotes", document_uri, props.pageId);
241
241
+
else setInteractionState(document_uri, { drawerOpen: false });
242
242
+
}}
243
243
+
onMouseEnter={handleQuotePrefetch}
244
244
+
onTouchStart={handleQuotePrefetch}
245
245
+
aria-label="Post quotes"
246
246
+
>
247
247
+
<QuoteTiny aria-hidden /> {props.quotesCount}{" "}
248
248
+
<span
249
249
+
aria-hidden
250
250
+
>{`Mention${props.quotesCount === 1 ? "" : "s"}`}</span>
251
251
+
</button>
252
252
+
)}
253
253
+
{props.showComments === false ? null : (
254
254
+
<button
255
255
+
className="flex gap-2 items-center w-fit px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"
256
256
+
onClick={() => {
257
257
+
if (
258
258
+
!drawerOpen ||
259
259
+
drawer !== "comments" ||
260
260
+
pageId !== props.pageId
261
261
+
)
262
262
+
openInteractionDrawer("comments", document_uri, props.pageId);
263
263
+
else setInteractionState(document_uri, { drawerOpen: false });
264
264
+
}}
265
265
+
aria-label="Post comments"
266
266
+
>
267
267
+
<CommentTiny aria-hidden />{" "}
268
268
+
{props.commentsCount > 0 ? (
269
269
+
<span aria-hidden>
270
270
+
{`${props.commentsCount} Comment${props.commentsCount === 1 ? "" : "s"}`}
271
271
+
</span>
272
272
+
) : (
273
273
+
"Comment"
297
274
)}
298
298
-
</div>
299
299
-
</>
300
300
-
)}
301
301
-
275
275
+
</button>
276
276
+
)}
277
277
+
</div>
302
278
<EditButton document={data} />
303
279
{subscribed && publication && (
304
280
<ManageSubscription
+2
-7
app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx
···
14
14
ExpandedInteractions,
15
15
getCommentCount,
16
16
getQuoteCount,
17
17
+
Interactions,
17
18
} from "./Interactions/Interactions";
18
19
import { PostContent } from "./PostContent";
19
20
import { PostHeader } from "./PostHeader/PostHeader";
···
24
25
import { decodeQuotePosition } from "./quotePosition";
25
26
import { PollData } from "./fetchPollData";
26
27
import { SharedPageProps } from "./PostPages";
27
27
-
import { PostPrevNextButtons } from "./PostPrevNextButtons";
28
28
29
29
export function LinearDocumentPage({
30
30
blocks,
···
56
56
57
57
const isSubpage = !!pageId;
58
58
59
59
-
console.log("prev/next?: " + preferences.showPrevNext);
60
60
-
61
59
return (
62
60
<>
63
61
<PageWrapper
···
85
83
did={did}
86
84
prerenderedCodeBlocks={prerenderedCodeBlocks}
87
85
/>
88
88
-
<PostPrevNextButtons
89
89
-
showPrevNext={preferences.showPrevNext && !isSubpage}
90
90
-
/>
86
86
+
91
87
<ExpandedInteractions
92
88
pageId={pageId}
93
89
showComments={preferences.showComments}
94
94
-
showMentions={preferences.showMentions}
95
90
commentsCount={getCommentCount(document, pageId) || 0}
96
91
quotesCount={getQuoteCount(document, pageId) || 0}
97
92
/>
+1
-2
app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx
···
23
23
export function PostHeader(props: {
24
24
data: PostPageData;
25
25
profile: ProfileViewDetailed;
26
26
-
preferences: { showComments?: boolean; showMentions?: boolean };
26
26
+
preferences: { showComments?: boolean };
27
27
}) {
28
28
let { identity } = useIdentityData();
29
29
let document = props.data;
···
91
91
</div>
92
92
<Interactions
93
93
showComments={props.preferences.showComments}
94
94
-
showMentions={props.preferences.showMentions}
95
94
quotesCount={getQuoteCount(document) || 0}
96
95
commentsCount={getCommentCount(document) || 0}
97
96
/>
+4
-22
app/lish/[did]/[publication]/[rkey]/PostPages.tsx
···
147
147
document: PostPageData;
148
148
did: string;
149
149
profile: ProfileViewDetailed;
150
150
-
preferences: {
151
151
-
showComments?: boolean;
152
152
-
showMentions?: boolean;
153
153
-
showPrevNext?: boolean;
154
154
-
};
150
150
+
preferences: { showComments?: boolean };
155
151
pubRecord?: PubLeafletPublication.Record;
156
152
theme?: PubLeafletPublication.Theme | null;
157
153
prerenderedCodeBlocks?: Map<string, string>;
···
210
206
did: string;
211
207
prerenderedCodeBlocks?: Map<string, string>;
212
208
bskyPostData: AppBskyFeedDefs.PostView[];
213
213
-
preferences: {
214
214
-
showComments?: boolean;
215
215
-
showMentions?: boolean;
216
216
-
showPrevNext?: boolean;
217
217
-
};
209
209
+
preferences: { showComments?: boolean };
218
210
pollData: PollData[];
219
211
}) {
220
212
let drawer = useDrawerOpen(document_uri);
···
269
261
270
262
{drawer && !drawer.pageId && (
271
263
<InteractionDrawer
272
272
-
showPageBackground={pubRecord?.theme?.showPageBackground}
273
264
document_uri={document.uri}
274
265
comments={
275
266
pubRecord?.preferences?.showComments === false
276
267
? []
277
268
: document.comments_on_documents
278
269
}
279
279
-
quotesAndMentions={
280
280
-
pubRecord?.preferences?.showMentions === false
281
281
-
? []
282
282
-
: quotesAndMentions
283
283
-
}
270
270
+
quotesAndMentions={quotesAndMentions}
284
271
did={did}
285
272
/>
286
273
)}
···
360
347
/>
361
348
{drawer && drawer.pageId === page.id && (
362
349
<InteractionDrawer
363
363
-
showPageBackground={pubRecord?.theme?.showPageBackground}
364
350
pageId={page.id}
365
351
document_uri={document.uri}
366
352
comments={
···
368
354
? []
369
355
: document.comments_on_documents
370
356
}
371
371
-
quotesAndMentions={
372
372
-
pubRecord?.preferences?.showMentions === false
373
373
-
? []
374
374
-
: quotesAndMentions
375
375
-
}
357
357
+
quotesAndMentions={quotesAndMentions}
376
358
did={did}
377
359
/>
378
360
)}
-58
app/lish/[did]/[publication]/[rkey]/PostPrevNextButtons.tsx
···
1
1
-
"use client";
2
2
-
import { PubLeafletDocument } from "lexicons/api";
3
3
-
import { usePublicationData } from "../dashboard/PublicationSWRProvider";
4
4
-
import { getPublicationURL } from "app/lish/createPub/getPublicationURL";
5
5
-
import { AtUri } from "@atproto/api";
6
6
-
import { useParams } from "next/navigation";
7
7
-
import { getPostPageData } from "./getPostPageData";
8
8
-
import { PostPageContext } from "./PostPageContext";
9
9
-
import { useContext } from "react";
10
10
-
import { SpeedyLink } from "components/SpeedyLink";
11
11
-
import { ArrowRightTiny } from "components/Icons/ArrowRightTiny";
12
12
-
13
13
-
export const PostPrevNextButtons = (props: {
14
14
-
showPrevNext: boolean | undefined;
15
15
-
}) => {
16
16
-
let postData = useContext(PostPageContext);
17
17
-
let pub = postData?.documents_in_publications[0]?.publications;
18
18
-
19
19
-
if (!props.showPrevNext || !pub || !postData) return;
20
20
-
21
21
-
function getPostLink(uri: string) {
22
22
-
return pub && uri
23
23
-
? `${getPublicationURL(pub)}/${new AtUri(uri).rkey}`
24
24
-
: "leaflet.pub/not-found";
25
25
-
}
26
26
-
let prevPost = postData?.prevNext?.prev;
27
27
-
let nextPost = postData?.prevNext?.next;
28
28
-
29
29
-
return (
30
30
-
<div className="flex flex-col gap-1 w-full px-3 sm:px-4 pb-2 pt-2">
31
31
-
{/*<hr className="border-border-light" />*/}
32
32
-
<div className="flex justify-between w-full gap-8 ">
33
33
-
{nextPost ? (
34
34
-
<SpeedyLink
35
35
-
href={getPostLink(nextPost.uri)}
36
36
-
className="flex gap-1 items-center truncate min-w-0 basis-1/2"
37
37
-
>
38
38
-
<ArrowRightTiny className="rotate-180 shrink-0" />
39
39
-
<div className="min-w-0 truncate">{nextPost.title}</div>
40
40
-
</SpeedyLink>
41
41
-
) : (
42
42
-
<div />
43
43
-
)}
44
44
-
{prevPost ? (
45
45
-
<SpeedyLink
46
46
-
href={getPostLink(prevPost.uri)}
47
47
-
className="flex gap-1 items-center truncate min-w-0 basis-1/2 justify-end"
48
48
-
>
49
49
-
<div className="min-w-0 truncate">{prevPost.title}</div>
50
50
-
<ArrowRightTiny className="shrink-0" />
51
51
-
</SpeedyLink>
52
52
-
) : (
53
53
-
<div />
54
54
-
)}
55
55
-
</div>
56
56
-
</div>
57
57
-
);
58
58
-
};
+2
-3
app/lish/[did]/[publication]/[rkey]/QuoteHandler.tsx
···
186
186
<BlueskyLinkTiny className="shrink-0" />
187
187
Bluesky
188
188
</a>
189
189
-
<Separator classname="h-4!" />
189
189
+
<Separator classname="h-4" />
190
190
<button
191
191
id="copy-quote-link"
192
192
className="flex gap-1 items-center hover:font-bold px-1"
···
211
211
</button>
212
212
{pubRecord?.preferences?.showComments !== false && identity?.atp_did && (
213
213
<>
214
214
-
<Separator classname="h-4! " />
215
215
-
214
214
+
<Separator classname="h-4" />
216
215
<button
217
216
className="flex gap-1 items-center hover:font-bold px-1"
218
217
onClick={() => {
+1
-58
app/lish/[did]/[publication]/[rkey]/getPostPageData.ts
···
10
10
data,
11
11
uri,
12
12
comments_on_documents(*, bsky_profiles(*)),
13
13
-
documents_in_publications(publications(*,
14
14
-
documents_in_publications(documents(uri, data)),
15
15
-
publication_subscriptions(*))
16
16
-
),
13
13
+
documents_in_publications(publications(*, publication_subscriptions(*))),
17
14
document_mentions_in_bsky(*),
18
15
leaflets_in_publications(*)
19
16
`,
···
54
51
?.record as PubLeafletPublication.Record
55
52
)?.theme || (document?.data as PubLeafletDocument.Record)?.theme;
56
53
57
57
-
// Calculate prev/next documents from the fetched publication documents
58
58
-
let prevNext:
59
59
-
| {
60
60
-
prev?: { uri: string; title: string };
61
61
-
next?: { uri: string; title: string };
62
62
-
}
63
63
-
| undefined;
64
64
-
65
65
-
const currentPublishedAt = (document.data as PubLeafletDocument.Record)
66
66
-
?.publishedAt;
67
67
-
const allDocs =
68
68
-
document.documents_in_publications[0]?.publications
69
69
-
?.documents_in_publications;
70
70
-
71
71
-
if (currentPublishedAt && allDocs) {
72
72
-
// Filter and sort documents by publishedAt
73
73
-
const sortedDocs = allDocs
74
74
-
.map((dip) => ({
75
75
-
uri: dip?.documents?.uri,
76
76
-
title: (dip?.documents?.data as PubLeafletDocument.Record).title,
77
77
-
publishedAt: (dip?.documents?.data as PubLeafletDocument.Record)
78
78
-
.publishedAt,
79
79
-
}))
80
80
-
.filter((doc) => doc.publishedAt) // Only include docs with publishedAt
81
81
-
.sort(
82
82
-
(a, b) =>
83
83
-
new Date(a.publishedAt!).getTime() -
84
84
-
new Date(b.publishedAt!).getTime(),
85
85
-
);
86
86
-
87
87
-
// Find current document index
88
88
-
const currentIndex = sortedDocs.findIndex((doc) => doc.uri === uri);
89
89
-
90
90
-
if (currentIndex !== -1) {
91
91
-
prevNext = {
92
92
-
prev:
93
93
-
currentIndex > 0
94
94
-
? {
95
95
-
uri: sortedDocs[currentIndex - 1].uri || "",
96
96
-
title: sortedDocs[currentIndex - 1].title,
97
97
-
}
98
98
-
: undefined,
99
99
-
next:
100
100
-
currentIndex < sortedDocs.length - 1
101
101
-
? {
102
102
-
uri: sortedDocs[currentIndex + 1].uri || "",
103
103
-
title: sortedDocs[currentIndex + 1].title,
104
104
-
}
105
105
-
: undefined,
106
106
-
};
107
107
-
}
108
108
-
}
109
109
-
110
54
return {
111
55
...document,
112
56
quotesAndMentions,
113
57
theme,
114
114
-
prevNext,
115
58
};
116
59
}
117
60
-1
app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx
···
140
140
commentsCount={comments}
141
141
tags={tags}
142
142
showComments={pubRecord?.preferences?.showComments}
143
143
-
showMentions={pubRecord?.preferences?.showMentions}
144
143
postUrl={`${getPublicationURL(publication)}/${uri.rkey}`}
145
144
/>
146
145
</div>
+25
-31
app/lish/[did]/[publication]/dashboard/settings/PostOptions.tsx
···
22
22
? true
23
23
: record.preferences.showComments,
24
24
);
25
25
-
let [showMentions, setShowMentions] = useState(
26
26
-
record?.preferences?.showMentions === undefined
27
27
-
? true
28
28
-
: record.preferences.showMentions,
29
29
-
);
30
30
-
let [showPrevNext, setShowPrevNext] = useState(
31
31
-
record?.preferences?.showPrevNext === undefined
32
32
-
? true
33
33
-
: record.preferences.showPrevNext,
34
34
-
);
25
25
+
let [showMentions, setShowMentions] = useState(true);
26
26
+
let [showPrevNext, setShowPrevNext] = useState(true);
35
27
36
28
let toast = useToaster();
37
29
return (
38
30
<form
39
31
onSubmit={async (e) => {
40
40
-
if (!pubData) return;
41
41
-
e.preventDefault();
42
42
-
props.setLoading(true);
43
43
-
let data = await updatePublication({
44
44
-
name: record.name,
45
45
-
uri: pubData.uri,
46
46
-
preferences: {
47
47
-
showInDiscover:
48
48
-
record?.preferences?.showInDiscover === undefined
49
49
-
? true
50
50
-
: record.preferences.showInDiscover,
51
51
-
showComments: showComments,
52
52
-
showMentions: showMentions,
53
53
-
showPrevNext: showPrevNext,
54
54
-
},
55
55
-
});
56
56
-
toast({ type: "success", content: <strong>Posts Updated!</strong> });
57
57
-
console.log(record.preferences?.showPrevNext);
58
58
-
props.setLoading(false);
59
59
-
mutate("publication-data");
32
32
+
// if (!pubData) return;
33
33
+
// e.preventDefault();
34
34
+
// props.setLoading(true);
35
35
+
// let data = await updatePublication({
36
36
+
// uri: pubData.uri,
37
37
+
// name: nameValue,
38
38
+
// description: descriptionValue,
39
39
+
// iconFile: iconFile,
40
40
+
// preferences: {
41
41
+
// showInDiscover: showInDiscover,
42
42
+
// showComments: showComments,
43
43
+
// },
44
44
+
// });
45
45
+
// toast({ type: "success", content: "Posts Updated!" });
46
46
+
// props.setLoading(false);
47
47
+
// mutate("publication-data");
60
48
}}
61
49
className="text-primary flex flex-col"
62
50
>
···
69
57
Post Options
70
58
</PubSettingsHeader>
71
59
<h4 className="mb-1">Layout</h4>
60
60
+
{/*<div>Max Post Width</div>*/}
72
61
<Toggle
73
62
toggle={showPrevNext}
74
63
onToggle={() => {
75
64
setShowPrevNext(!showPrevNext);
76
65
}}
77
66
>
78
78
-
<div className="font-bold">Show Prev/Next Buttons</div>
67
67
+
<div className="flex flex-col justify-start">
68
68
+
<div className="font-bold">Show Prev/Next Buttons</div>
69
69
+
<div className="text-tertiary text-sm leading-tight">
70
70
+
Show buttons that navigate to the previous and next posts
71
71
+
</div>
72
72
+
</div>
79
73
</Toggle>
80
74
<hr className="my-2 border-border-light" />
81
75
<h4 className="mb-1">Interactions</h4>
+2
-2
app/lish/[did]/[publication]/dashboard/settings/PublicationSettings.tsx
···
103
103
Theme and Layout
104
104
<ArrowRightTiny />
105
105
</button>
106
106
-
<button
106
106
+
{/*<button
107
107
className={menuItemClassName}
108
108
type="button"
109
109
onClick={() => props.setState("post-options")}
110
110
>
111
111
Post Options
112
112
<ArrowRightTiny />
113
113
-
</button>
113
113
+
</button>*/}
114
114
</div>
115
115
);
116
116
};
-1
app/lish/[did]/[publication]/page.tsx
···
172
172
tags={tags}
173
173
postUrl={`${getPublicationURL(publication)}/${uri.rkey}`}
174
174
showComments={record?.preferences?.showComments}
175
175
-
showMentions={record?.preferences?.showMentions}
176
175
/>
177
176
</div>
178
177
</div>
+2
-9
app/lish/createPub/CreatePubForm.tsx
···
53
53
description: descriptionValue,
54
54
iconFile: logoFile,
55
55
subdomain: domainValue,
56
56
-
preferences: {
57
57
-
showInDiscover,
58
58
-
showComments: true,
59
59
-
showMentions: true,
60
60
-
showPrevNext: false,
61
61
-
},
56
56
+
preferences: { showInDiscover, showComments: true },
62
57
});
63
58
64
59
if (!result.success) {
···
73
68
setTimeout(() => {
74
69
setFormState("normal");
75
70
if (result.publication)
76
76
-
router.push(
77
77
-
`${getBasePublicationURL(result.publication)}/dashboard`,
78
78
-
);
71
71
+
router.push(`${getBasePublicationURL(result.publication)}/dashboard`);
79
72
}, 500);
80
73
}}
81
74
>
+14
-19
app/lish/createPub/UpdatePubForm.tsx
···
21
21
import { Checkbox } from "components/Checkbox";
22
22
import type { GetDomainConfigResponseBody } from "@vercel/sdk/esm/models/getdomainconfigop";
23
23
import { PubSettingsHeader } from "../[did]/[publication]/dashboard/settings/PublicationSettings";
24
24
-
import { Toggle } from "components/Toggle";
25
24
26
25
export const EditPubForm = (props: {
27
26
backToMenuAction: () => void;
···
44
43
? true
45
44
: record.preferences.showComments,
46
45
);
47
47
-
let showMentions =
48
48
-
record?.preferences?.showMentions === undefined
49
49
-
? true
50
50
-
: record.preferences.showMentions;
51
51
-
let showPrevNext =
52
52
-
record?.preferences?.showPrevNext === undefined
53
53
-
? true
54
54
-
: record.preferences.showPrevNext;
55
55
-
56
46
let [descriptionValue, setDescriptionValue] = useState(
57
47
record?.description || "",
58
48
);
···
84
74
preferences: {
85
75
showInDiscover: showInDiscover,
86
76
showComments: showComments,
87
87
-
showMentions: showMentions,
88
88
-
showPrevNext: showPrevNext,
89
77
},
90
78
});
91
79
toast({ type: "success", content: "Updated!" });
···
102
90
General Settings
103
91
</PubSettingsHeader>
104
92
<div className="flex flex-col gap-3 w-[1000px] max-w-full pb-2">
105
105
-
<div className="flex items-center justify-between gap-2 mt-2 ">
93
93
+
<div className="flex items-center justify-between gap-2 ">
106
94
<p className="pl-0.5 pb-0.5 text-tertiary italic text-sm font-bold">
107
95
Logo <span className="font-normal">(optional)</span>
108
96
</p>
···
172
160
<CustomDomainForm />
173
161
<hr className="border-border-light" />
174
162
175
175
-
<Toggle
176
176
-
toggle={showInDiscover}
177
177
-
onToggle={() => setShowInDiscover(!showInDiscover)}
163
163
+
<Checkbox
164
164
+
checked={showInDiscover}
165
165
+
onChange={(e) => setShowInDiscover(e.target.checked)}
178
166
>
179
179
-
<div className=" pt-0.5 flex flex-col text-sm text-tertiary ">
167
167
+
<div className=" pt-0.5 flex flex-col text-sm italic text-tertiary ">
180
168
<p className="font-bold">
181
169
Show In{" "}
182
170
<a href="/discover" target="_blank">
···
191
179
page. You can change this at any time!
192
180
</p>
193
181
</div>
194
194
-
</Toggle>
182
182
+
</Checkbox>
195
183
196
196
-
184
184
+
<Checkbox
185
185
+
checked={showComments}
186
186
+
onChange={(e) => setShowComments(e.target.checked)}
187
187
+
>
188
188
+
<div className=" pt-0.5 flex flex-col text-sm italic text-tertiary ">
189
189
+
<p className="font-bold">Show comments on posts</p>
190
190
+
</div>
191
191
+
</Checkbox>
197
192
</div>
198
193
</form>
199
194
);
+2
-2
app/lish/createPub/updatePublication.ts
···
25
25
}: {
26
26
uri: string;
27
27
name: string;
28
28
-
description?: string;
29
29
-
iconFile?: File | null;
28
28
+
description: string;
29
29
+
iconFile: File | null;
30
30
preferences?: Omit<PubLeafletPublication.Preferences, "$type">;
31
31
}): Promise<UpdatePublicationResult> {
32
32
let identity = await getIdentityData();
+3
-6
components/Canvas.tsx
···
170
170
171
171
let pubRecord = pub.publications.record as PubLeafletPublication.Record;
172
172
let showComments = pubRecord.preferences?.showComments;
173
173
-
let showMentions = pubRecord.preferences?.showMentions;
174
173
175
174
return (
176
175
<div className="flex flex-row gap-3 items-center absolute top-6 right-3 sm:top-4 sm:right-4 bg-bg-page border-border-light rounded-md px-2 py-1 h-fit z-20">
···
179
178
<CommentTiny className="text-border" /> โ
180
179
</div>
181
180
)}
182
182
-
{showComments && (
183
183
-
<div className="flex gap-1 text-tertiary items-center">
184
184
-
<QuoteTiny className="text-border" /> โ
185
185
-
</div>
186
186
-
)}
181
181
+
<div className="flex gap-1 text-tertiary items-center">
182
182
+
<QuoteTiny className="text-border" /> โ
183
183
+
</div>
187
184
188
185
{!props.isSubpage && (
189
186
<>
+2
-4
components/InteractionsPreview.tsx
···
14
14
tags?: string[];
15
15
postUrl: string;
16
16
showComments: boolean | undefined;
17
17
-
showMentions: boolean | undefined;
18
18
-
19
17
share?: boolean;
20
18
}) => {
21
19
let smoker = useSmoker();
22
20
let interactionsAvailable =
23
23
-
(props.quotesCount > 0 && props.showMentions !== false) ||
21
21
+
props.quotesCount > 0 ||
24
22
(props.showComments !== false && props.commentsCount > 0);
25
23
26
24
const tagsCount = props.tags?.length || 0;
···
38
36
</>
39
37
)}
40
38
41
41
-
{props.showMentions === false || props.quotesCount === 0 ? null : (
39
39
+
{props.quotesCount === 0 ? null : (
42
40
<SpeedyLink
43
41
aria-label="Post quotes"
44
42
href={`${props.postUrl}?interactionDrawer=quotes`}
+3
-5
components/Pages/PublicationMetadata.tsx
···
121
121
<Separator classname="h-4!" />
122
122
</>
123
123
)}
124
124
-
{pubRecord?.preferences?.showMentions && (
125
125
-
<div className="flex gap-1 items-center">
126
126
-
<QuoteTiny />โ
127
127
-
</div>
128
128
-
)}
124
124
+
<div className="flex gap-1 items-center">
125
125
+
<QuoteTiny />โ
126
126
+
</div>
129
127
{pubRecord?.preferences?.showComments && (
130
128
<div className="flex gap-1 items-center">
131
129
<CommentTiny />โ
-1
components/PostListing.tsx
···
97
97
commentsCount={comments}
98
98
tags={tags}
99
99
showComments={pubRecord?.preferences?.showComments}
100
100
-
showMentions={pubRecord?.preferences?.showMentions}
101
100
share
102
101
/>
103
102
</div>
+7
-7
components/ThemeManager/PublicationThemeProvider.tsx
···
2
2
import { useMemo, useState } from "react";
3
3
import { parseColor } from "react-aria-components";
4
4
import { useEntity } from "src/replicache";
5
5
-
import { getColorDifference } from "./themeUtils";
5
5
+
import { getColorContrast } from "./themeUtils";
6
6
import { useColorAttribute, colorToString } from "./useColorAttribute";
7
7
import { BaseThemeProvider, CardBorderHiddenContext } from "./ThemeProvider";
8
8
import { PubLeafletPublication, PubLeafletThemeColor } from "lexicons/api";
···
174
174
let newAccentContrast;
175
175
let sortedAccents = [newTheme.accent1, newTheme.accent2].sort((a, b) => {
176
176
return (
177
177
-
getColorDifference(
177
177
+
getColorContrast(
178
178
colorToString(b, "rgb"),
179
179
colorToString(
180
180
showPageBackground ? newTheme.bgPage : newTheme.bgLeaflet,
181
181
"rgb",
182
182
),
183
183
) -
184
184
-
getColorDifference(
184
184
+
getColorContrast(
185
185
colorToString(a, "rgb"),
186
186
colorToString(
187
187
showPageBackground ? newTheme.bgPage : newTheme.bgLeaflet,
···
191
191
);
192
192
});
193
193
if (
194
194
-
getColorDifference(
194
194
+
getColorContrast(
195
195
colorToString(sortedAccents[0], "rgb"),
196
196
colorToString(newTheme.primary, "rgb"),
197
197
-
) < 0.15 &&
198
198
-
getColorDifference(
197
197
+
) < 30 &&
198
198
+
getColorContrast(
199
199
colorToString(sortedAccents[1], "rgb"),
200
200
colorToString(
201
201
showPageBackground ? newTheme.bgPage : newTheme.bgLeaflet,
202
202
"rgb",
203
203
),
204
204
-
) > 0.08
204
204
+
) > 12
205
205
) {
206
206
newAccentContrast = sortedAccents[1];
207
207
} else newAccentContrast = sortedAccents[0];
+9
-9
components/ThemeManager/ThemeProvider.tsx
···
22
22
PublicationThemeProvider,
23
23
} from "./PublicationThemeProvider";
24
24
import { PubLeafletPublication } from "lexicons/api";
25
25
-
import { getColorDifference } from "./themeUtils";
25
25
+
import { getColorContrast } from "./themeUtils";
26
26
27
27
// define a function to set an Aria Color to a CSS Variable in RGB
28
28
function setCSSVariableToColor(
···
140
140
//sorting the accents by contrast on background
141
141
let sortedAccents = [accent1, accent2].sort((a, b) => {
142
142
return (
143
143
-
getColorDifference(
143
143
+
getColorContrast(
144
144
colorToString(b, "rgb"),
145
145
colorToString(showPageBackground ? bgPage : bgLeaflet, "rgb"),
146
146
) -
147
147
-
getColorDifference(
147
147
+
getColorContrast(
148
148
colorToString(a, "rgb"),
149
149
colorToString(showPageBackground ? bgPage : bgLeaflet, "rgb"),
150
150
)
···
156
156
// then use the not contrasty option
157
157
158
158
if (
159
159
-
getColorDifference(
159
159
+
getColorContrast(
160
160
colorToString(sortedAccents[0], "rgb"),
161
161
colorToString(primary, "rgb"),
162
162
-
) < 0.15 &&
163
163
-
getColorDifference(
162
162
+
) < 30 &&
163
163
+
getColorContrast(
164
164
colorToString(sortedAccents[1], "rgb"),
165
165
colorToString(showPageBackground ? bgPage : bgLeaflet, "rgb"),
166
166
-
) > 0.08
166
166
+
) > 12
167
167
) {
168
168
accentContrast = sortedAccents[1];
169
169
} else accentContrast = sortedAccents[0];
···
286
286
bgPage && accent1 && accent2
287
287
? [accent1, accent2].sort((a, b) => {
288
288
return (
289
289
-
getColorDifference(
289
289
+
getColorContrast(
290
290
colorToString(b, "rgb"),
291
291
colorToString(bgPage, "rgb"),
292
292
) -
293
293
-
getColorDifference(
293
293
+
getColorContrast(
294
294
colorToString(a, "rgb"),
295
295
colorToString(bgPage, "rgb"),
296
296
)
+3
-2
components/ThemeManager/ThemeSetter.tsx
···
1
1
"use client";
2
2
import { Popover } from "components/Popover";
3
3
+
import { theme } from "../../tailwind.config";
3
4
4
5
import { Color } from "react-aria-components";
5
6
···
165
166
setOpenPicker={(pickers) => setOpenPicker(pickers)}
166
167
/>
167
168
<SectionArrow
168
168
-
fill="rgb(var(--accent-2))"
169
169
-
stroke="rgb(var(--accent-1))"
169
169
+
fill={theme.colors["accent-2"]}
170
170
+
stroke={theme.colors["accent-1"]}
170
171
className="ml-2"
171
172
/>
172
173
</div>
+3
-4
components/ThemeManager/themeUtils.ts
···
1
1
-
import { parse, ColorSpace, sRGB, distance, OKLab } from "colorjs.io/fn";
1
1
+
import { parse, contrastLstar, ColorSpace, sRGB } from "colorjs.io/fn";
2
2
3
3
// define the color defaults for everything
4
4
export const ThemeDefaults = {
···
17
17
};
18
18
19
19
// used to calculate the contrast between page and accent1, accent2, and determin which is higher contrast
20
20
-
export function getColorDifference(color1: string, color2: string) {
20
20
+
export function getColorContrast(color1: string, color2: string) {
21
21
ColorSpace.register(sRGB);
22
22
-
ColorSpace.register(OKLab);
23
22
24
23
let parsedColor1 = parse(`rgb(${color1})`);
25
24
let parsedColor2 = parse(`rgb(${color2})`);
26
25
27
27
-
return distance(parsedColor1, parsedColor2, "oklab");
26
26
+
return contrastLstar(parsedColor1, parsedColor2);
28
27
}
-8
lexicons/api/lexicons.ts
···
1810
1810
type: 'boolean',
1811
1811
default: true,
1812
1812
},
1813
1813
-
showMentions: {
1814
1814
-
type: 'boolean',
1815
1815
-
default: true,
1816
1816
-
},
1817
1817
-
showPrevNext: {
1818
1818
-
type: 'boolean',
1819
1819
-
default: false,
1820
1820
-
},
1821
1813
},
1822
1814
},
1823
1815
theme: {
-2
lexicons/api/types/pub/leaflet/publication.ts
···
37
37
$type?: 'pub.leaflet.publication#preferences'
38
38
showInDiscover: boolean
39
39
showComments: boolean
40
40
-
showMentions: boolean
41
41
-
showPrevNext: boolean
42
40
}
43
41
44
42
const hashPreferences = 'preferences'
-8
lexicons/pub/leaflet/publication.json
···
51
51
"showComments": {
52
52
"type": "boolean",
53
53
"default": true
54
54
-
},
55
55
-
"showMentions": {
56
56
-
"type": "boolean",
57
57
-
"default": true
58
58
-
},
59
59
-
"showPrevNext": {
60
60
-
"type": "boolean",
61
61
-
"default": false
62
54
}
63
55
}
64
56
},
-2
lexicons/src/publication.ts
···
27
27
properties: {
28
28
showInDiscover: { type: "boolean", default: true },
29
29
showComments: { type: "boolean", default: true },
30
30
-
showMentions: { type: "boolean", default: true },
31
31
-
showPrevNext: { type: "boolean", default: false },
32
30
},
33
31
},
34
32
theme: {