tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
289
fork
atom
a tool for shared writing and social publishing
289
fork
atom
overview
issues
28
pulls
pipelines
pulling in the right data, styling adjustments
cozylittle.house
5 months ago
22619219
bd92da54
+154
-105
6 changed files
expand all
collapse all
unified
split
app
discover
PubListing.tsx
home
HomeEmpty
HomeEmpty.tsx
reader
ReaderContent.tsx
page.tsx
components
ActionBar
Publications.tsx
PageHeader.tsx
+3
-13
app/discover/PubListing.tsx
···
1
1
"use client";
2
2
import { AtUri } from "@atproto/syntax";
3
3
+
import { PubIcon } from "components/ActionBar/Publications";
3
4
import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider";
4
5
import { BaseThemeProvider } from "components/ThemeManager/ThemeProvider";
5
6
import { PubLeafletPublication, PubLeafletThemeColor } from "lexicons/api";
···
42
43
px-3 py-3 selected-outline
43
44
hover:outline-accent-contrast hover:border-accent-contrast`}
44
45
>
45
45
-
<div
46
46
-
style={{
47
47
-
backgroundRepeat: "no-repeat",
48
48
-
backgroundPosition: "center",
49
49
-
backgroundSize: "cover",
50
50
-
backgroundImage: record?.icon
51
51
-
? `url(${blobRefToSrc(record.icon?.ref, new AtUri(props.uri).host)})`
52
52
-
: undefined,
53
53
-
}}
54
54
-
className={`w-6 h-6 rounded-full bg-accent-1 text-accent-2 flex place-content-center leading-snug font-bold text-center shrink-0 ${record.theme?.showPageBackground ? "mt-[6px]" : "mt-0.5"}`}
55
55
-
>
56
56
-
{!record?.icon ? record.name.slice(0, 1).toLocaleUpperCase() : null}
57
57
-
</div>
46
46
+
<PubIcon record={record} uri={props.uri} />
47
47
+
58
48
<div
59
49
className={`flex w-full flex-col ${record.theme?.showPageBackground ? "bg-[rgba(var(--bg-page),var(--bg-page-alpha))] px-2 py-1 rounded-lg" : ""}`}
60
50
>
+5
-5
app/home/HomeEmpty/HomeEmpty.tsx
···
66
66
export const PublicationBanner = (props: { small?: boolean }) => {
67
67
return (
68
68
<div
69
69
-
className={`accent-container flex sm:py-2 gap-4 items-center ${props.small ? "items-start p-2 text-sm font-normal" : "items-center p-4"}`}
69
69
+
className={`accent-container flex sm:py-2 items-center ${props.small ? "items-start gap-2 p-2 text-sm font-normal" : "items-center p-4 gap-4"}`}
70
70
>
71
71
{props.small ? (
72
72
-
<PublishSmall className="shrink-0" />
72
72
+
<PublishSmall className="shrink-0 text-accent-contrast" />
73
73
) : (
74
74
<div className="w-[64px] mx-auto">
75
75
<PubListEmptyIllo />
76
76
</div>
77
77
)}
78
78
-
<div className="grow">
78
78
+
<div className={`${props.small ? "pt-[2px]" : ""} grow`}>
79
79
<Link href={"/lish/createPub"} className="font-bold">
80
80
Start a Publication
81
81
</Link>{" "}
···
88
88
export const DiscoverBanner = (props: { small?: boolean }) => {
89
89
return (
90
90
<div
91
91
-
className={`accent-container flex sm:py-2 gap-2 items-center ${props.small ? "items-start p-2 text-sm font-normal" : "items-center p-4"}`}
91
91
+
className={`accent-container flex sm:py-2 items-center ${props.small ? "items-start gap-2 p-2 text-sm font-normal" : "items-center p-4 gap-4"}`}
92
92
>
93
93
{props.small ? (
94
94
<DiscoverSmall className="shrink-0 text-accent-contrast" />
···
97
97
<DiscoverIllo />
98
98
</div>
99
99
)}
100
100
-
<div className="grow">
100
100
+
<div className={`${props.small ? "pt-[2px]" : ""} grow`}>
101
101
<Link href={"/discover"} className="font-bold">
102
102
Explore Publications
103
103
</Link>{" "}
+91
-71
app/reader/ReaderContent.tsx
···
1
1
"use client";
2
2
+
import { AtUri } from "@atproto/api";
3
3
+
import { getPublicationURL } from "app/lish/createPub/getPublicationURL";
4
4
+
import { PubIcon } from "components/ActionBar/Publications";
2
5
import { ShareSmall } from "components/Icons/ShareSmall";
3
6
import { Separator } from "components/Layout";
4
7
import { useCardBorderHidden } from "components/Pages/useCardBorderHidden";
8
8
+
import { SpeedyLink } from "components/SpeedyLink";
9
9
+
import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider";
10
10
+
import { BaseThemeProvider } from "components/ThemeManager/ThemeProvider";
11
11
+
import { PubLeafletDocument, PubLeafletPublication } from "lexicons/api";
5
12
import Link from "next/link";
13
13
+
import { blobRefToSrc } from "src/utils/blobRefToSrc";
14
14
+
import { Json } from "supabase/database.types";
6
15
7
7
-
export const ReaderContent = (props: { root_entity: string }) => {
8
8
-
let cardBorderHidden = useCardBorderHidden(props.root_entity);
16
16
+
export const ReaderContent = (props: {
17
17
+
root_entity: string;
18
18
+
posts: {
19
19
+
publication: {
20
20
+
href: string;
21
21
+
pubRecord: Json;
22
22
+
uri: string;
23
23
+
};
24
24
+
documents: { data: Json; uri: string; indexed_at: string };
25
25
+
}[];
26
26
+
}) => {
9
27
return (
10
28
<div className="flex flex-col gap-3">
11
11
-
{dummyPosts.map((p) => (
12
12
-
<Post {...p} cardBorderHidden={true} />
13
13
-
))}
29
29
+
{props.posts?.map((p) => <Post {...p} />)}
14
30
</div>
15
31
);
16
32
};
17
33
18
34
const Post = (props: {
19
19
-
title: string;
20
20
-
description: string;
21
21
-
date: string;
22
22
-
read: boolean;
23
23
-
author: string;
24
24
-
pub: string;
25
25
-
cardBorderHidden: boolean;
35
35
+
publication: {
36
36
+
pubRecord: Json;
37
37
+
uri: string;
38
38
+
href: string;
39
39
+
};
40
40
+
documents: { data: Json | undefined; uri: string; indexed_at: string };
26
41
}) => {
42
42
+
let pubRecord = props.publication.pubRecord as PubLeafletPublication.Record;
43
43
+
44
44
+
let postRecord = props.documents.data as PubLeafletDocument.Record;
45
45
+
let postUri = new AtUri(props.documents.uri);
46
46
+
47
47
+
let theme = usePubTheme(pubRecord);
48
48
+
let backgroundImage = pubRecord?.theme?.backgroundImage?.image?.ref
49
49
+
? blobRefToSrc(
50
50
+
pubRecord?.theme?.backgroundImage?.image?.ref,
51
51
+
new AtUri(props.publication.uri).host,
52
52
+
)
53
53
+
: null;
54
54
+
55
55
+
let backgroundImageRepeat = pubRecord?.theme?.backgroundImage?.repeat;
56
56
+
let backgroundImageSize = pubRecord?.theme?.backgroundImage?.width || 500;
57
57
+
58
58
+
let showPageBackground = pubRecord.theme?.showPageBackground;
59
59
+
27
60
return (
28
28
-
<div
29
29
-
className={`flex flex-col gap-0 ${props.cardBorderHidden ? "bg-bg-page" : "bg-bg-leaflet"} p-3 rounded-lg border border-border-light`}
30
30
-
>
31
31
-
<div
32
32
-
className={`${props.cardBorderHidden ? "bg-transparent" : "bg-bg-page px-3 py-2"} rounded-md`}
61
61
+
<BaseThemeProvider {...theme} local>
62
62
+
<SpeedyLink
63
63
+
href={`${props.publication.href}/${postUri.rkey}`}
64
64
+
style={{
65
65
+
backgroundImage: `url(${backgroundImage})`,
66
66
+
backgroundRepeat: backgroundImageRepeat ? "repeat" : "no-repeat",
67
67
+
backgroundSize: `${backgroundImageRepeat ? `${backgroundImageSize}px` : "cover"}`,
68
68
+
}}
69
69
+
className={`no-underline! flex flex-row gap-2 w-full
70
70
+
bg-bg-leaflet
71
71
+
border border-border-light rounded-lg
72
72
+
sm:p-2 p-2 selected-outline
73
73
+
hover:outline-accent-contrast hover:border-accent-contrast
74
74
+
`}
33
75
>
34
34
-
<div className="flex justify-between gap-2">
35
35
-
<Link
36
36
-
href={"/"}
37
37
-
className="text-accent-contrast font-bold no-underline text-sm "
38
38
-
>
39
39
-
{props.pub}
40
40
-
</Link>
41
41
-
<button className="text-tertiary">{/*<ShareSmall />*/}</button>
42
42
-
</div>
43
43
-
<h3 className="truncate">{props.title}</h3>
76
76
+
<div
77
77
+
className={`${showPageBackground ? "bg-bg-page " : "bg-transparent"} rounded-md w-full px-[10px] pt-2 pb-2`}
78
78
+
style={{
79
79
+
backgroundColor: showPageBackground
80
80
+
? "rgba(var(--bg-page), var(--bg-page-alpha))"
81
81
+
: "transparent",
82
82
+
}}
83
83
+
>
84
84
+
<div className="flex justify-between gap-2">
85
85
+
<button className="text-tertiary">{/*<ShareSmall />*/}</button>
86
86
+
</div>
87
87
+
<h3 className="text-primary truncate">{postRecord.title}</h3>
44
88
45
45
-
<p className="text-secondary">{props.description}</p>
46
46
-
<div className="flex gap-2 text-sm text-tertiary items-center pt-3">
47
47
-
<div className="flex gap-[6px] items-center">
48
48
-
<div className="bg-test rounded-full h-4 w-4" />
49
49
-
{props.author}
89
89
+
<p className="text-secondary">{postRecord.description}</p>
90
90
+
<div className="flex gap-2 text-sm text-tertiary items-center pt-3">
91
91
+
<Link
92
92
+
href={props.publication.href}
93
93
+
className="text-accent-contrast font-bold no-underline text-sm flex gap-[6px] items-center"
94
94
+
>
95
95
+
<PubIcon small record={pubRecord} uri={props.publication.uri} />
96
96
+
{pubRecord.name}
97
97
+
</Link>
98
98
+
<Separator classname="h-4 !min-h-0" />
99
99
+
NAME HERE
100
100
+
<Separator classname="h-4 !min-h-0" />
101
101
+
{postRecord.publishedAt &&
102
102
+
new Date(postRecord.publishedAt).toLocaleDateString("en-US", {
103
103
+
year: "numeric",
104
104
+
month: "short",
105
105
+
day: "numeric",
106
106
+
})}
50
107
</div>
51
51
-
<Separator classname="h-4 !min-h-0" />
52
52
-
{props.date}
53
108
</div>
54
54
-
</div>
55
55
-
</div>
109
109
+
</SpeedyLink>
110
110
+
</BaseThemeProvider>
56
111
);
57
112
};
58
58
-
59
59
-
let dummyPosts: {
60
60
-
title: string;
61
61
-
description: string;
62
62
-
date: string;
63
63
-
read: boolean;
64
64
-
author: string;
65
65
-
pub: string;
66
66
-
}[] = [
67
67
-
{
68
68
-
title: "First Post",
69
69
-
description: "this is a description",
70
70
-
date: "Oct 2",
71
71
-
read: false,
72
72
-
author: "jared",
73
73
-
pub: "a warm space",
74
74
-
},
75
75
-
{
76
76
-
title: "This is a second Tost",
77
77
-
description: "It has another description, as you can see",
78
78
-
date: "Oct 2",
79
79
-
read: false,
80
80
-
author: "celine",
81
81
-
pub: "Celine's Super Soliloquy",
82
82
-
},
83
83
-
{
84
84
-
title: "A Third Post, A Burnt Toast",
85
85
-
description:
86
86
-
"If the first post is bread, the second is toast, and inevitably the third is a plate of charcoal.",
87
87
-
date: "Oct 2",
88
88
-
read: false,
89
89
-
author: "brendan",
90
90
-
pub: "Scraps",
91
91
-
},
92
92
-
];
+46
-12
app/reader/page.tsx
···
18
18
import { DashboardLayout } from "components/PageLayouts/DashboardLayout";
19
19
import { ReaderContent } from "./ReaderContent";
20
20
import { SubscriptionsContent } from "./SubscriptionsContent";
21
21
+
import { Json } from "supabase/database.types";
22
22
+
import { getPublicationURL } from "app/lish/createPub/getPublicationURL";
23
23
+
import { PubLeafletDocument } from "lexicons/api";
21
24
22
25
export default async function Reader(props: {}) {
23
26
let cookieStore = await cookies();
···
92
95
.select(`publications(*, documents_in_publications(documents(*)))`)
93
96
.eq("identity", auth_res?.atp_did);
94
97
95
95
-
let subbedPubs = publications?.map((pub) => {
96
96
-
let subbedPubsArr: string[] = [];
97
97
-
pub.publications?.name && subbedPubsArr.push(pub.publications?.name);
98
98
-
return subbedPubsArr;
99
99
-
});
98
98
+
// Flatten all posts from all publications into a single array
99
99
+
let posts =
100
100
+
publications?.flatMap((publication) => {
101
101
+
const postsInPub =
102
102
+
publication.publications?.documents_in_publications.filter(
103
103
+
(d) => !!d?.documents,
104
104
+
);
100
105
101
101
-
console.log(subbedPubs);
106
106
+
if (!postsInPub || postsInPub.length === 0) return [];
102
107
108
108
+
return postsInPub
109
109
+
.filter(
110
110
+
(postInPub) =>
111
111
+
postInPub.documents?.data &&
112
112
+
postInPub.documents?.uri &&
113
113
+
postInPub.documents?.indexed_at,
114
114
+
)
115
115
+
.map((postInPub) => ({
116
116
+
publication: {
117
117
+
href: getPublicationURL(publication.publications!),
118
118
+
pubRecord: publication.publications?.record || null,
119
119
+
uri: publication.publications?.uri || "",
120
120
+
},
121
121
+
documents: {
122
122
+
data: postInPub.documents!.data,
123
123
+
uri: postInPub.documents!.uri,
124
124
+
indexed_at: postInPub.documents!.indexed_at,
125
125
+
},
126
126
+
}));
127
127
+
}) || [];
128
128
+
129
129
+
let sortedPosts = posts.sort((a, b) => {
130
130
+
let recordA = a.documents.data as PubLeafletDocument.Record;
131
131
+
let recordB = b.documents.data as PubLeafletDocument.Record;
132
132
+
const dateA = new Date(recordA.publishedAt || 0);
133
133
+
const dateB = new Date(recordB.publishedAt || 0);
134
134
+
return dateB.getTime() - dateA.getTime();
135
135
+
});
103
136
return (
104
137
<ReplicacheProvider
105
138
rootEntity={root_entity}
···
121
154
actions={null}
122
155
tabs={{
123
156
reader: {
124
124
-
controls: <FocusToggle />,
125
125
-
content: <ReaderContent root_entity={root_entity} />,
157
157
+
controls: null,
158
158
+
content: (
159
159
+
<ReaderContent
160
160
+
root_entity={root_entity}
161
161
+
posts={sortedPosts}
162
162
+
/>
163
163
+
),
126
164
},
127
165
subs: {
128
166
controls: null,
···
141
179
</ReplicacheProvider>
142
180
);
143
181
}
144
144
-
145
145
-
const FocusToggle = () => {
146
146
-
return <div className="grow flex justify-end">focus</div>;
147
147
-
};
+9
-3
components/ActionBar/Publications.tsx
···
93
93
export const PubIcon = (props: {
94
94
record: PubLeafletPublication.Record;
95
95
uri: string;
96
96
+
small?: boolean;
97
97
+
className?: string;
96
98
}) => {
97
99
if (!props.record) return;
98
100
···
104
106
backgroundSize: "cover",
105
107
backgroundImage: `url(/api/atproto_images?did=${new AtUri(props.uri).host}&cid=${(props.record.icon?.ref as unknown as { $link: string })["$link"]})`,
106
108
}}
107
107
-
className="w-6 h-6 rounded-full"
109
109
+
className={`${props.small ? "w-5 h-5" : "w-6 h-6"} rounded-full ${props.className}`}
108
110
/>
109
111
) : (
110
110
-
<div className="w-6 h-6 rounded-full bg-accent-1 relative">
111
111
-
<div className="font-bold text-sm absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-accent-2">
112
112
+
<div
113
113
+
className={`${props.small ? "w-5 h-5" : "w-6 h-6"} rounded-full bg-accent-1 relative`}
114
114
+
>
115
115
+
<div
116
116
+
className={`${props.small ? "text-xs" : "text-sm"} font-bold absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-accent-2`}
117
117
+
>
112
118
{props.record?.name.slice(0, 1)}
113
119
</div>
114
120
</div>
-1
components/PageHeader.tsx
···
6
6
cardBorderHidden: boolean;
7
7
}) => {
8
8
let [scrollPos, setScrollPos] = useState(0);
9
9
-
console.log(props.cardBorderHidden);
10
9
11
10
useEffect(() => {
12
11
const homeContent = document.getElementById("home-content");