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
29
pulls
pipelines
added contributor section in settings for pubs
cozylittle.house
3 months ago
70297d58
f3e2a86d
+264
2 changed files
expand all
collapse all
unified
split
app
lish
[did]
[publication]
dashboard
publicationSettings
PublicationContributors.tsx
PublicationSettings.tsx
+135
app/lish/[did]/[publication]/dashboard/publicationSettings/PublicationContributors.tsx
···
1
1
+
"use client";
2
2
+
import { MoreOptionsVerticalTiny } from "components/Icons/MoreOptionsVerticalTiny";
3
3
+
import { PubSettingsHeader } from "./PublicationSettings";
4
4
+
import { theme } from "tailwind.config";
5
5
+
import { Popover } from "components/Popover";
6
6
+
import { DeleteSmall } from "components/Icons/DeleteSmall";
7
7
+
import { useState } from "react";
8
8
+
import { Input } from "components/Input";
9
9
+
import { ButtonPrimary } from "components/Buttons";
10
10
+
11
11
+
export const PubContributorManager = (props: {
12
12
+
backToMenuAction: () => void;
13
13
+
}) => {
14
14
+
let contributors = [
15
15
+
{ name: "celine", status: "pending" },
16
16
+
{ name: "jared", status: "accepted" },
17
17
+
{ name: "brendan", status: "accepted" },
18
18
+
];
19
19
+
return (
20
20
+
<div>
21
21
+
<PubSettingsHeader
22
22
+
state={"contributors"}
23
23
+
backToMenuAction={props.backToMenuAction}
24
24
+
>
25
25
+
Contributors
26
26
+
</PubSettingsHeader>
27
27
+
{contributors.length === 0 ? (
28
28
+
<PubContributorsEmpty />
29
29
+
) : (
30
30
+
<PubContributorsContent contributors={contributors} />
31
31
+
)}
32
32
+
</div>
33
33
+
);
34
34
+
};
35
35
+
36
36
+
const PubContributorsEmpty = () => {
37
37
+
return (
38
38
+
<div className="flex flex-col gap-2 justify-center accent-container text-sm text-center sm:p-4 p-3 mb-1 mt-3">
39
39
+
<PubContibutorEmptyIllo />
40
40
+
<div className="font-bold">
41
41
+
Contributors can make drafts and publish in this publication!{" "}
42
42
+
</div>
43
43
+
<div className="text-secondary">
44
44
+
They can't change publication settings or edit other people's work
45
45
+
though.
46
46
+
</div>
47
47
+
{/*TODO ADD THE INVITE FLOW*/}
48
48
+
</div>
49
49
+
);
50
50
+
};
51
51
+
52
52
+
const PubContributorsContent = (props: {
53
53
+
contributors: { name: string; status: string }[];
54
54
+
}) => {
55
55
+
let [inputValue, setInputValue] = useState("");
56
56
+
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">
68
68
+
<div className="font-bold text-tertiary text-sm">
69
69
+
Add a New Contributor
70
70
+
</div>
71
71
+
<div className="input-with-border flex gap-1">
72
72
+
<div className="text-tertiary">@</div>
73
73
+
<Input
74
74
+
placeholder="search bluesky handles"
75
75
+
className="w-full outline-none!"
76
76
+
value={inputValue}
77
77
+
onChange={(e) => setInputValue(e.currentTarget.value)}
78
78
+
/>
79
79
+
</div>
80
80
+
</div>
81
81
+
{props.contributors.map((contributor) => {
82
82
+
return (
83
83
+
<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
+
{contributor.status === "pending" && (
87
87
+
<div className="text-sm text-tertiary italic">Invited</div>
88
88
+
)}
89
89
+
</div>
90
90
+
91
91
+
<PubContributorOptions />
92
92
+
</div>
93
93
+
);
94
94
+
})}
95
95
+
</div>
96
96
+
);
97
97
+
};
98
98
+
99
99
+
const PubContibutor = () => {
100
100
+
return <div></div>;
101
101
+
};
102
102
+
103
103
+
const PubContributorOptions = () => {
104
104
+
return (
105
105
+
<Popover trigger={<MoreOptionsVerticalTiny />}>
106
106
+
<div className="menuItem flex gap-2 -mx-2! -my-1">
107
107
+
<DeleteSmall /> Remove Contributor
108
108
+
</div>
109
109
+
</Popover>
110
110
+
);
111
111
+
};
112
112
+
113
113
+
const PubContibutorEmptyIllo = () => {
114
114
+
return (
115
115
+
<svg
116
116
+
width="52"
117
117
+
height="40"
118
118
+
viewBox="0 0 52 40"
119
119
+
fill="none"
120
120
+
xmlns="http://www.w3.org/2000/svg"
121
121
+
className="mx-auto"
122
122
+
>
123
123
+
<path
124
124
+
d="M0.0229335 14.6446C0.63646 8.1375 3.12717 6.38968 8.1976 4.40031C15.1643 1.66694 18.0542 5.27071 26.6984 11.4523C35.3425 17.6339 41.9359 12.8676 49.0059 20.9395C56.0759 29.0114 47.3939 36.9226 41.1227 39.2592C34.9882 41.5448 31.2246 37.1191 23.468 35.7604C15.7114 34.4016 -0.693068 22.2386 0.0229335 14.6446Z"
125
125
+
fill={theme.colors["accent-2"]}
126
126
+
/>
127
127
+
<path
128
128
+
fillRule="evenodd"
129
129
+
clipRule="evenodd"
130
130
+
d="M15.2537 16.6617C15.9514 16.604 16.4699 15.9951 16.4118 15.3018C16.3536 14.6085 15.7409 14.0933 15.0431 14.1511C12.4026 14.3698 9.81158 15.8192 7.89676 17.9171C5.97188 20.0259 4.64404 22.8821 4.64404 26.0347C4.64404 32.643 10.0355 38 16.6863 38C23.337 38 28.7285 32.643 28.7285 26.0347C28.7285 25.4713 28.7108 24.9476 28.6648 24.4471C29.1811 24.458 29.8511 24.4646 30.7221 24.4646H30.7442L30.7663 24.4638C37.3656 24.2349 42.644 18.8481 42.644 12.2357C42.644 5.47811 37.1307 0 30.3297 0C26.906 0 23.8067 1.38988 21.5762 3.62962C21.0837 4.12413 21.0879 4.92169 21.5856 5.41102C22.0833 5.90036 22.886 5.89617 23.3784 5.40166C25.1523 3.6205 27.6104 2.51933 30.3297 2.51933C35.7304 2.51933 40.1085 6.8695 40.1085 12.2357C40.1085 17.4789 35.9283 21.7526 30.6993 21.9453C29.3933 21.9451 28.5863 21.9295 28.0939 21.9105C27.7793 21.0871 27.3374 20.286 26.7523 19.3945C26.3698 18.8118 25.5843 18.6475 24.9979 19.0275C24.4114 19.4075 24.246 20.188 24.6285 20.7707C25.2962 21.7879 25.6688 22.5431 25.8875 23.2916C26.0143 23.7256 26.0966 24.1823 26.1435 24.7178C25.7065 24.6255 25.2338 24.626 24.7455 24.7415C23.0664 25.1389 22.2224 26.37 21.7321 27.7207C21.4946 28.3751 21.836 29.097 22.4946 29.333C23.1532 29.569 23.8797 29.2299 24.1173 28.5754C24.461 27.6286 24.8268 27.3121 25.333 27.1923C25.5356 27.1444 25.686 27.1871 25.7936 27.2578C25.9106 27.3347 26.0024 27.4622 26.0351 27.6208C26.0393 27.6411 26.0439 27.6612 26.049 27.681C25.2651 32.1131 21.3715 35.4807 16.6863 35.4807C11.4359 35.4807 7.17957 31.2516 7.17957 26.0347C7.17957 23.5939 8.21102 21.3229 9.77493 19.6095C11.3489 17.8851 13.3762 16.8172 15.2537 16.6617ZM12.8254 27.9333C13.1051 27.3836 13.4291 27.0736 13.7429 26.9096C14.0501 26.7489 14.4507 26.6737 14.9916 26.7711C15.3341 26.8327 15.5628 27.0166 15.746 27.323C15.9477 27.6603 16.0581 28.1023 16.0913 28.4906C16.1504 29.1838 16.764 29.6981 17.4616 29.6393C18.1593 29.5805 18.6769 28.9709 18.6177 28.2777C18.5637 27.6441 18.3824 26.7997 17.9258 26.0361C17.4507 25.2416 16.6547 24.5102 15.4438 24.2921C14.4313 24.1098 13.4422 24.2199 12.562 24.6801C11.6884 25.137 11.0274 25.8835 10.5627 26.7964C10.2468 27.4172 10.4972 28.175 11.122 28.4889C11.7468 28.8029 12.5095 28.5541 12.8254 27.9333ZM23.0683 15.3492C22.716 15.5988 22.3606 15.7116 22.0884 15.704C21.295 15.682 19.8534 14.6003 19.9217 12.1693C19.99 9.73829 21.4901 8.73827 22.2836 8.76028C23.077 8.7823 24.5186 9.86395 24.4503 12.295C24.4429 12.5561 24.4191 12.8007 24.3813 13.0293C24.1443 12.3038 23.4584 11.7792 22.6492 11.7792C21.6437 11.7792 20.8285 12.5892 20.8285 13.5883C20.8285 14.5874 21.6437 15.3974 22.6492 15.3974C22.7934 15.3974 22.9337 15.3807 23.0683 15.3492ZM26.5624 12.3536C26.4759 15.4301 24.4464 17.8697 22.0294 17.8026C19.6124 17.7356 17.7231 15.1872 17.8096 12.1107C17.896 9.03422 19.9255 6.5946 22.3425 6.66166C23.8206 6.70267 25.1013 7.67154 25.8535 9.12164C26.5934 8.91877 27.345 8.91534 28.0962 9.07137C28.8275 7.38253 30.2637 6.19705 31.9806 6.0923C34.6001 5.93248 36.8809 8.34904 37.075 11.4898C37.2691 14.6306 35.3029 17.3063 32.6834 17.4661C30.0639 17.6259 27.7831 15.2094 27.589 12.0686C27.578 11.89 27.5739 11.7129 27.5766 11.5377C27.2325 11.4677 26.8802 11.4585 26.5397 11.5474C26.5622 11.8111 26.5701 12.0802 26.5624 12.3536ZM32.554 15.3706C33.6551 15.3034 35.1125 13.9877 34.9661 11.6185C34.8197 9.24927 33.2113 8.12061 32.1101 8.18779C31.0089 8.25498 29.5515 9.57068 29.698 11.9399C29.7116 12.161 29.738 12.3712 29.7755 12.5707C30.0485 11.914 30.6995 11.4518 31.459 11.4518C32.4646 11.4518 33.2798 12.2618 33.2798 13.2609C33.2798 14.26 32.4646 15.07 31.459 15.07C31.4429 15.07 31.4269 15.0698 31.4109 15.0694C31.8009 15.2921 32.2032 15.392 32.554 15.3706Z"
131
131
+
fill={theme.colors["accent-1"]}
132
132
+
/>
133
133
+
</svg>
134
134
+
);
135
135
+
};
+129
app/lish/[did]/[publication]/dashboard/publicationSettings/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
+
import { PubContributorManager } from "./PublicationContributors";
16
16
+
17
17
+
type state = "menu" | "general" | "theme" | "contributors";
18
18
+
19
19
+
export function PublicationSettingsButton(props: { publication: string }) {
20
20
+
let isMobile = useIsMobile();
21
21
+
let [state, setState] = useState<state>("menu");
22
22
+
let [loading, setLoading] = useState(false);
23
23
+
24
24
+
return (
25
25
+
<Popover
26
26
+
asChild
27
27
+
onOpenChange={() => setState("menu")}
28
28
+
side={isMobile ? "top" : "right"}
29
29
+
align={isMobile ? "center" : "start"}
30
30
+
className={`max-w-xs w-[1000px] ${state === "theme" && "bg-white!"}`}
31
31
+
arrowFill={theme.colors["border-light"]}
32
32
+
trigger={
33
33
+
<ActionButton
34
34
+
id="pub-settings-button"
35
35
+
icon=<SettingsSmall />
36
36
+
label="Settings"
37
37
+
/>
38
38
+
}
39
39
+
>
40
40
+
{state === "general" ? (
41
41
+
<EditPubForm
42
42
+
backToMenuAction={() => setState("menu")}
43
43
+
loading={loading}
44
44
+
setLoadingAction={setLoading}
45
45
+
/>
46
46
+
) : state === "theme" ? (
47
47
+
<PubThemeSetter
48
48
+
backToMenu={() => setState("menu")}
49
49
+
loading={loading}
50
50
+
setLoading={setLoading}
51
51
+
/>
52
52
+
) : state === "contributors" ? (
53
53
+
<PubContributorManager backToMenuAction={() => setState("menu")} />
54
54
+
) : (
55
55
+
<PubSettingsMenu state={state} setState={setState} />
56
56
+
)}
57
57
+
</Popover>
58
58
+
);
59
59
+
}
60
60
+
61
61
+
const PubSettingsMenu = (props: {
62
62
+
state: "menu" | "general" | "theme" | "contributors";
63
63
+
setState: (s: state) => void;
64
64
+
}) => {
65
65
+
return (
66
66
+
<div className="flex flex-col gap-0.5">
67
67
+
<PubSettingsHeader state={"menu"}>Settings</PubSettingsHeader>
68
68
+
69
69
+
<PubSettingsMenuItem setState={() => props.setState("general")}>
70
70
+
Publication Settings
71
71
+
</PubSettingsMenuItem>
72
72
+
73
73
+
<PubSettingsMenuItem setState={() => props.setState("theme")}>
74
74
+
Publication Theme
75
75
+
</PubSettingsMenuItem>
76
76
+
77
77
+
<PubSettingsMenuItem setState={() => props.setState("contributors")}>
78
78
+
Contributors
79
79
+
</PubSettingsMenuItem>
80
80
+
</div>
81
81
+
);
82
82
+
};
83
83
+
84
84
+
const PubSettingsMenuItem = (props: {
85
85
+
children: React.ReactNode;
86
86
+
setState: () => void;
87
87
+
}) => {
88
88
+
return (
89
89
+
<button
90
90
+
className="menuItem -mx-[8px] text-left flex items-center justify-between hover:no-underline!"
91
91
+
type="button"
92
92
+
onClick={() => props.setState()}
93
93
+
>
94
94
+
{props.children} <ArrowRightTiny />
95
95
+
</button>
96
96
+
);
97
97
+
};
98
98
+
99
99
+
export const PubSettingsHeader = (props: {
100
100
+
state: state;
101
101
+
backToMenuAction?: () => void;
102
102
+
loading?: boolean;
103
103
+
noCTA?: boolean;
104
104
+
children: React.ReactNode;
105
105
+
}) => {
106
106
+
return (
107
107
+
<div className="flex justify-between font-bold text-secondary bg-border-light -mx-3 -mt-2 px-3 py-2 mb-1">
108
108
+
{props.children}
109
109
+
110
110
+
<div className="flex gap-2">
111
111
+
{props.state !== "menu" && (
112
112
+
<button
113
113
+
type="button"
114
114
+
onClick={() => {
115
115
+
props.backToMenuAction && props.backToMenuAction();
116
116
+
}}
117
117
+
>
118
118
+
<GoBackSmall className="text-accent-contrast" />
119
119
+
</button>
120
120
+
)}
121
121
+
{props.loading !== undefined && (
122
122
+
<ButtonPrimary compact type="submit">
123
123
+
{props.loading ? <DotLoader /> : "Update"}
124
124
+
</ButtonPrimary>
125
125
+
)}
126
126
+
</div>
127
127
+
</div>
128
128
+
);
129
129
+
};