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