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
adjustments to the invite contributor flow
cozylittle.house
3 months ago
252c1f2d
1bad1760
+49
-162
7 changed files
expand all
collapse all
unified
split
app
lish
[did]
[publication]
dashboard
Actions.tsx
PublicationSettings.tsx
publicationSettings
PublicationContributors.tsx
PublicationSettings.tsx
createPub
UpdatePubForm.tsx
components
Icons
PopoverArrow.tsx
ThemeManager
PubThemeSetter.tsx
+1
-1
app/lish/[did]/[publication]/dashboard/Actions.tsx
···
1
1
"use client";
2
2
3
3
import { NewDraftActionButton } from "./NewDraftButton";
4
4
-
import { PublicationSettingsButton } from "./PublicationSettings";
4
4
+
import { PublicationSettingsButton } from "./publicationSettings/PublicationSettings";
5
5
import { ActionButton } from "components/ActionBar/ActionButton";
6
6
import { ShareSmall } from "components/Icons/ShareSmall";
7
7
import { Menu } from "components/Layout";
-132
app/lish/[did]/[publication]/dashboard/PublicationSettings.tsx
···
1
1
-
"use client";
2
2
-
3
3
-
import { ActionButton } from "components/ActionBar/ActionButton";
4
4
-
import { Popover } from "components/Popover";
5
5
-
import { SettingsSmall } from "components/Icons/SettingsSmall";
6
6
-
import { EditPubForm } from "app/lish/createPub/UpdatePubForm";
7
7
-
import { PubThemeSetter } from "components/ThemeManager/PubThemeSetter";
8
8
-
import { useIsMobile } from "src/hooks/isMobile";
9
9
-
import { useState } from "react";
10
10
-
import { GoBackSmall } from "components/Icons/GoBackSmall";
11
11
-
import { theme } from "tailwind.config";
12
12
-
import { ButtonPrimary } from "components/Buttons";
13
13
-
import { DotLoader } from "components/utils/DotLoader";
14
14
-
import { ArrowRightTiny } from "components/Icons/ArrowRightTiny";
15
15
-
16
16
-
export function PublicationSettingsButton(props: { publication: string }) {
17
17
-
let isMobile = useIsMobile();
18
18
-
let [state, setState] = useState<"menu" | "general" | "theme">("menu");
19
19
-
let [loading, setLoading] = useState(false);
20
20
-
21
21
-
return (
22
22
-
<Popover
23
23
-
asChild
24
24
-
onOpenChange={() => setState("menu")}
25
25
-
side={isMobile ? "top" : "right"}
26
26
-
align={isMobile ? "center" : "start"}
27
27
-
className={`max-w-xs w-[1000px] ${state === "theme" && "bg-white!"}`}
28
28
-
arrowFill={theme.colors["border-light"]}
29
29
-
trigger={
30
30
-
<ActionButton
31
31
-
id="pub-settings-button"
32
32
-
icon=<SettingsSmall />
33
33
-
label="Settings"
34
34
-
/>
35
35
-
}
36
36
-
>
37
37
-
{state === "general" ? (
38
38
-
<EditPubForm
39
39
-
backToMenuAction={() => setState("menu")}
40
40
-
loading={loading}
41
41
-
setLoadingAction={setLoading}
42
42
-
/>
43
43
-
) : state === "theme" ? (
44
44
-
<PubThemeSetter
45
45
-
backToMenu={() => setState("menu")}
46
46
-
loading={loading}
47
47
-
setLoading={setLoading}
48
48
-
/>
49
49
-
) : (
50
50
-
<PubSettingsMenu
51
51
-
state={state}
52
52
-
setState={setState}
53
53
-
loading={loading}
54
54
-
setLoading={setLoading}
55
55
-
/>
56
56
-
)}
57
57
-
</Popover>
58
58
-
);
59
59
-
}
60
60
-
61
61
-
const PubSettingsMenu = (props: {
62
62
-
state: "menu" | "general" | "theme";
63
63
-
setState: (s: typeof props.state) => void;
64
64
-
loading: boolean;
65
65
-
setLoading: (l: boolean) => void;
66
66
-
}) => {
67
67
-
let menuItemClassName =
68
68
-
"menuItem -mx-[8px] text-left flex items-center justify-between hover:no-underline!";
69
69
-
70
70
-
return (
71
71
-
<div className="flex flex-col gap-0.5">
72
72
-
<PubSettingsHeader
73
73
-
loading={props.loading}
74
74
-
setLoadingAction={props.setLoading}
75
75
-
state={"menu"}
76
76
-
/>
77
77
-
<button
78
78
-
className={menuItemClassName}
79
79
-
type="button"
80
80
-
onClick={() => {
81
81
-
props.setState("general");
82
82
-
}}
83
83
-
>
84
84
-
Publication Settings
85
85
-
<ArrowRightTiny />
86
86
-
</button>
87
87
-
<button
88
88
-
className={menuItemClassName}
89
89
-
type="button"
90
90
-
onClick={() => props.setState("theme")}
91
91
-
>
92
92
-
Publication Theme
93
93
-
<ArrowRightTiny />
94
94
-
</button>
95
95
-
</div>
96
96
-
);
97
97
-
};
98
98
-
99
99
-
export const PubSettingsHeader = (props: {
100
100
-
state: "menu" | "general" | "theme";
101
101
-
backToMenuAction?: () => void;
102
102
-
loading: boolean;
103
103
-
setLoadingAction: (l: boolean) => void;
104
104
-
}) => {
105
105
-
return (
106
106
-
<div className="flex justify-between font-bold text-secondary bg-border-light -mx-3 -mt-2 px-3 py-2 mb-1">
107
107
-
{props.state === "menu"
108
108
-
? "Settings"
109
109
-
: props.state === "general"
110
110
-
? "General"
111
111
-
: props.state === "theme"
112
112
-
? "Publication Theme"
113
113
-
: ""}
114
114
-
{props.state !== "menu" && (
115
115
-
<div className="flex gap-2">
116
116
-
<button
117
117
-
type="button"
118
118
-
onClick={() => {
119
119
-
props.backToMenuAction && props.backToMenuAction();
120
120
-
}}
121
121
-
>
122
122
-
<GoBackSmall className="text-accent-contrast" />
123
123
-
</button>
124
124
-
125
125
-
<ButtonPrimary compact type="submit">
126
126
-
{props.loading ? <DotLoader /> : "Update"}
127
127
-
</ButtonPrimary>
128
128
-
</div>
129
129
-
)}
130
130
-
</div>
131
131
-
);
132
132
-
};
+38
-20
app/lish/[did]/[publication]/dashboard/publicationSettings/PublicationContributors.tsx
···
7
7
import { useState } from "react";
8
8
import { Input } from "components/Input";
9
9
import { ButtonPrimary } from "components/Buttons";
10
10
+
import { PopoverArrow } from "components/Icons/PopoverArrow";
11
11
+
import { useSmoker } from "components/Toast";
10
12
11
13
export const PubContributorManager = (props: {
12
14
backToMenuAction: () => void;
···
54
56
}) => {
55
57
let [inputValue, setInputValue] = useState("");
56
58
return (
57
57
-
<div className="flex flex-col gap-1 ">
58
58
-
<div className="flex flex-col gap-1 py-2">
59
59
-
<div className="flex flex-col gap-0.5 text-sm text-tertiary ">
60
60
-
<ButtonPrimary fullWidth compact className=" ">
61
61
-
Get Contributor Invite Link
62
62
-
</ButtonPrimary>
63
63
-
Only added contributors can access
64
64
-
</div>
65
65
-
</div>
66
66
-
<hr className="border-border-light" />
67
67
-
<div className="flex flex-col gap-0.5 my-2">
59
59
+
<div className="flex flex-col gap-1 pt-2">
60
60
+
<div className="flex flex-col gap-0.5">
68
61
<div className="font-bold text-tertiary text-sm">
69
62
Add a New Contributor
70
63
</div>
···
72
65
<div className="text-tertiary">@</div>
73
66
<Input
74
67
placeholder="search bluesky handles"
75
75
-
className="w-full outline-none!"
68
68
+
className="w-full outline-none! text-primary"
76
69
value={inputValue}
77
70
onChange={(e) => setInputValue(e.currentTarget.value)}
78
71
/>
79
72
</div>
80
73
</div>
74
74
+
75
75
+
<div className="flex flex-col gap-1 text-sm text-secondary text-center mb-2 mt-1 p-2 accent-container">
76
76
+
<ButtonPrimary fullWidth compact onClick={(e) => getContributorLink(e)}>
77
77
+
Copy Accept Invite Link
78
78
+
</ButtonPrimary>
79
79
+
Send this link to contributors so they can access this publication
80
80
+
</div>
81
81
+
<hr className="border-border-light" />
82
82
+
81
83
{props.contributors.map((contributor) => {
82
84
return (
83
85
<div className="flex justify-between items-center">
84
84
-
<div className="flex gap-2 items-baseline">
85
85
-
<div className="font-bold text-secondary">{contributor.name}</div>
86
86
+
<div className="font-bold text-secondary truncate grow">
87
87
+
{contributor.name}
88
88
+
</div>
89
89
+
90
90
+
<div className="flex gap-2 items-center">
86
91
{contributor.status === "pending" && (
87
87
-
<div className="text-sm text-tertiary italic">Invited</div>
92
92
+
<button
93
93
+
className="text-sm text-accent-contrast italic w-max shrink-0"
94
94
+
onClick={(e) => getContributorLink(e)}
95
95
+
>
96
96
+
Resend Invite
97
97
+
</button>
88
98
)}
99
99
+
100
100
+
<PubContributorOptions />
89
101
</div>
90
90
-
91
91
-
<PubContributorOptions />
92
102
</div>
93
103
);
94
104
})}
···
96
106
);
97
107
};
98
108
99
99
-
const PubContibutor = () => {
100
100
-
return <div></div>;
101
101
-
};
109
109
+
function getContributorLink(e: React.MouseEvent) {
110
110
+
let smoker = useSmoker();
111
111
+
112
112
+
smoker({
113
113
+
text: "Copied Invite Link!",
114
114
+
position: {
115
115
+
y: e.clientY,
116
116
+
x: e.clientX,
117
117
+
},
118
118
+
});
119
119
+
}
102
120
103
121
const PubContributorOptions = () => {
104
122
return (
+1
-2
app/lish/[did]/[publication]/dashboard/publicationSettings/PublicationSettings.tsx
···
1
1
"use client";
2
2
-
3
2
import { ActionButton } from "components/ActionBar/ActionButton";
4
3
import { Popover } from "components/Popover";
5
4
import { SettingsSmall } from "components/Icons/SettingsSmall";
···
27
26
onOpenChange={() => setState("menu")}
28
27
side={isMobile ? "top" : "right"}
29
28
align={isMobile ? "center" : "start"}
30
30
-
className={`max-w-xs w-[1000px] ${state === "theme" && "bg-white!"}`}
29
29
+
className={`text-secondary max-w-xs w-[1000px] ${state === "theme" && "bg-white!"}`}
31
30
arrowFill={theme.colors["border-light"]}
32
31
trigger={
33
32
<ActionButton
+4
-3
app/lish/createPub/UpdatePubForm.tsx
···
20
20
import Link from "next/link";
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/PublicationSettings";
23
23
+
import { PubSettingsHeader } from "../[did]/[publication]/dashboard/publicationSettings/PublicationSettings";
24
24
25
25
export const EditPubForm = (props: {
26
26
backToMenuAction: () => void;
···
84
84
>
85
85
<PubSettingsHeader
86
86
loading={props.loading}
87
87
-
setLoadingAction={props.setLoadingAction}
88
87
backToMenuAction={props.backToMenuAction}
89
88
state={"theme"}
90
90
-
/>
89
89
+
>
90
90
+
Publication Settings
91
91
+
</PubSettingsHeader>
91
92
<div className="flex flex-col gap-3 w-[1000px] max-w-full pb-2">
92
93
<div className="flex items-center justify-between gap-2 ">
93
94
<p className="pl-0.5 pb-0.5 text-tertiary italic text-sm font-bold">
+1
-1
components/Icons/PopoverArrow.tsx
···
11
11
height="8"
12
12
viewBox="0 0 16 8"
13
13
fill="none"
14
14
-
className="-mt-px"
14
14
+
className={`-mt-px ${props.className}`}
15
15
xmlns="http://www.w3.org/2000/svg"
16
16
>
17
17
<path
+4
-3
components/ThemeManager/PubThemeSetter.tsx
···
15
15
import { BackgroundPicker } from "./PubPickers/PubBackgroundPickers";
16
16
import { PubAccentPickers } from "./PubPickers/PubAcccentPickers";
17
17
import { Separator } from "components/Layout";
18
18
-
import { PubSettingsHeader } from "app/lish/[did]/[publication]/dashboard/PublicationSettings";
18
18
+
import { PubSettingsHeader } from "app/lish/[did]/[publication]/dashboard/publicationSettings/PublicationSettings";
19
19
20
20
export type ImageState = {
21
21
src: string;
···
92
92
>
93
93
<PubSettingsHeader
94
94
loading={props.loading}
95
95
-
setLoadingAction={props.setLoading}
96
95
backToMenuAction={props.backToMenu}
97
96
state={"theme"}
98
98
-
/>
97
97
+
>
98
98
+
Publication Theme
99
99
+
</PubSettingsHeader>
99
100
</form>
100
101
101
102
<div className="themeSetterContent flex flex-col w-full overflow-y-scroll -mb-2 ">