Skip to content

Commit a03da22

Browse files
authored
Feat: add a top-right share-button on the repo page (#152)
1 parent aa68a9e commit a03da22

File tree

13 files changed

+592
-92
lines changed

13 files changed

+592
-92
lines changed

api/src/resolver.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ import {
1111
createRepo,
1212
deletePod,
1313
deleteRepo,
14+
deleteCollaborator,
1415
myRepos,
1516
myCollabRepos,
17+
getVisibility,
18+
updateVisibility,
1619
addCollaborator,
1720
pod,
1821
repo,
@@ -50,7 +53,9 @@ export const resolvers = {
5053
repo,
5154
pod,
5255
listAllRuntimes,
56+
getVisibility,
5357
myCollabRepos,
58+
5459
...(process.env.RUNTIME_SPAWNER === "k8s"
5560
? {
5661
infoRuntime: infoRuntime_k8s,
@@ -72,6 +77,8 @@ export const resolvers = {
7277
updatePod,
7378
deletePod,
7479
addCollaborator,
80+
updateVisibility,
81+
deleteCollaborator,
7582
...(process.env.RUNTIME_SPAWNER === "k8s"
7683
? {
7784
spawnRuntime: spawnRuntime_k8s,

api/src/resolver_repo.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,41 @@ export async function createRepo(_, { id, name, isPublic }, { userId }) {
133133
return repo;
134134
}
135135

136+
export async function getVisibility(_, { repoId }, { userId }) {
137+
if (!userId) throw Error("Unauthenticated");
138+
const repo = await prisma.repo.findFirst({
139+
where: {
140+
id: repoId,
141+
owner: { id: userId || "undefined" }
142+
},
143+
include: {
144+
collaborators: true,
145+
},
146+
});
147+
if (!repo) throw Error("Repo not found");
148+
return {collaborators: repo.collaborators, isPublic: repo.public};
149+
}
150+
151+
export async function updateVisibility(_, { repoId, isPublic }, { userId }) {
152+
if (!userId) throw Error("Unauthenticated");
153+
const repo = await prisma.repo.findFirst({
154+
where: {
155+
id: repoId,
156+
owner: { id: userId || "undefined" }
157+
},
158+
});
159+
if (!repo) throw Error("Repo not found");
160+
await prisma.repo.update({
161+
where: {
162+
id: repoId,
163+
},
164+
data: {
165+
public: isPublic,
166+
},
167+
});
168+
return true;
169+
}
170+
136171
export async function updateRepo(_, { id, name }, { userId }) {
137172
if (!userId) throw Error("Unauthenticated");
138173
const repo = await prisma.repo.findFirst({
@@ -223,6 +258,28 @@ export async function addCollaborator(_, { repoId, email }, { userId }) {
223258
return true;
224259
}
225260

261+
export async function deleteCollaborator(_, { repoId, collaboratorId }, { userId }) {
262+
if(!userId) throw new Error("Not authenticated.")
263+
// 1. find the repo
264+
const repo = await prisma.repo.findFirst({
265+
where: {
266+
id: repoId,
267+
owner: { id: userId },
268+
},
269+
});
270+
// 2. delete the user from the repo
271+
if (!repo) throw new Error("Repo not found or you are not the owner.");
272+
const res = await prisma.repo.update({
273+
where: {
274+
id: repoId,
275+
},
276+
data: {
277+
collaborators: { disconnect: { id: collaboratorId } },
278+
}
279+
})
280+
return true;
281+
}
282+
226283
export async function addPod(_, { repoId, parent, index, input }, { userId }) {
227284
// make sure the repo is writable by this user
228285
if (!userId) throw new Error("Not authenticated.");

api/src/typedefs.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ export const typeDefs = gql`
1414
lastname: String!
1515
}
1616
17+
type Visibility {
18+
collaborators: [User]
19+
isPublic: Boolean
20+
}
21+
1722
type Repo {
1823
id: ID!
1924
name: String
@@ -92,6 +97,7 @@ export const typeDefs = gql`
9297
pod(id: ID!): Pod
9398
myRepos: [Repo]
9499
activeSessions: [String]
100+
getVisibility(repoId: String): Visibility
95101
listAllRuntimes: [RuntimeInfo]
96102
myCollabRepos: [Repo]
97103
infoRuntime(sessionId: String!): RuntimeInfo
@@ -118,6 +124,8 @@ export const typeDefs = gql`
118124
clearPod: Boolean
119125
spawnRuntime(sessionId: String): Boolean
120126
killRuntime(sessionId: String!): Boolean
127+
updateVisibility(repoId: String, isPublic: Boolean): Boolean
121128
addCollaborator(repoId: String, email: String): Boolean
129+
deleteCollaborator(repoId: String, collaboratorId: String): Boolean
122130
}
123131
`;

ui/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"net": "^1.0.2",
2828
"notistack": "^2.0.8",
2929
"react": "^18.2.0",
30+
"react-copy-to-clipboard": "^5.1.0",
3031
"react-dom": "^18.2.0",
3132
"react-icons": "^4.6.0",
3233
"react-monaco-editor": "^0.50.1",
@@ -85,6 +86,7 @@
8586
]
8687
},
8788
"devDependencies": {
89+
"@types/react-copy-to-clipboard": "^5.0.4",
8890
"@types/react-resizable": "^3.0.3",
8991
"babel-plugin-named-exports-order": "^0.0.2",
9092
"prop-types": "^15.8.1",

ui/src/components/Canvas.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -739,12 +739,12 @@ export function Canvas() {
739739
// const pods = useStore(store, (state) => state.pods);
740740
const getPod = useStore(store, (state) => state.getPod);
741741
const nodesMap = useStore(store, (state) => state.ydoc.getMap<Node>("pods"));
742-
const [showShareDialog, setShowShareDialog] = useState(false);
743742
const repoId = useStore(store, (state) => state.repoId);
744-
const repoName = useStore(store, (state) => state.repoName);
745743
const role = useStore(store, (state) => state.role);
746744
const provider = useStore(store, (state) => state.provider);
747745
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
746+
const shareOpen = useStore(store, (state) => state.shareOpen);
747+
const setShareOpen = useStore(store, (state) => state.setShareOpen);
748748

749749
const getRealNodes = useCallback(
750750
(id: string, level: number) => {
@@ -1163,16 +1163,11 @@ export function Canvas() {
11631163
addCode={() => addNode(client.x, client.y, "code")}
11641164
addScope={() => addNode(client.x, client.y, "scope")}
11651165
onShareClick={() => {
1166-
setShowShareDialog(true);
1166+
setShareOpen(true);
11671167
}}
11681168
/>
11691169
)}
1170-
<ShareProjDialog
1171-
open={showShareDialog}
1172-
onClose={() => setShowShareDialog(false)}
1173-
title={repoName || ""}
1174-
id={repoId || ""}
1175-
/>
1170+
{shareOpen && <ShareProjDialog open={shareOpen} id={repoId || ""} />}
11761171
</Box>
11771172
</Box>
11781173
);

ui/src/components/CanvasContextMenu.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import React, { useContext } from "react";
99
import CodeIcon from "@mui/icons-material/Code";
1010
import PostAddIcon from "@mui/icons-material/PostAdd";
1111
import FormatListNumberedIcon from "@mui/icons-material/FormatListNumbered";
12-
import ShareOutlinedIcon from "@mui/icons-material/ShareOutlined";
1312

1413
const paneMenuStyle = (left, top) => {
1514
return {
@@ -68,14 +67,6 @@ export function CanvasContextMenu(props) {
6867
{showLineNumbers ? "Hide " : "Show "} Line Numbers
6968
</ListItemText>
7069
</MenuItem>
71-
{role === RoleType.OWNER && (
72-
<MenuItem onClick={props.onShareClick} sx={ItemStyle}>
73-
<ListItemIcon>
74-
<ShareOutlinedIcon />
75-
</ListItemIcon>
76-
<ListItemText> Share with Collaborators </ListItemText>
77-
</MenuItem>
78-
)}
7970
</MenuList>
8071
</Box>
8172
);

ui/src/components/Header.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ type HeaderProps = {
3030
drawerWidth?: number;
3131
currentPage?: string | null;
3232
breadcrumbItem?: React.ReactNode;
33+
shareButton?: React.ReactNode;
3334
};
3435

3536
export const Header: React.FC<HeaderProps> = ({
3637
open = false,
3738
drawerWidth = 0,
3839
currentPage = null,
3940
breadcrumbItem = null,
41+
shareButton = null,
4042
}) => {
4143
const [anchorElNav, setAnchorElNav] = useState(null);
4244
const [anchorElUser, setAnchorElUser] = useState(null);
@@ -94,6 +96,16 @@ export const Header: React.FC<HeaderProps> = ({
9496
{breadcrumbItem}
9597
</Breadcrumbs>
9698

99+
<Box
100+
sx={{
101+
display: { xs: "none", md: "flex" },
102+
alignItems: "center",
103+
paddingRight: "10px",
104+
}}
105+
>
106+
{shareButton}
107+
</Box>
108+
97109
{/* The navigation on desktop */}
98110
<Box
99111
sx={{

0 commit comments

Comments
 (0)