Skip to content

Commit d22b178

Browse files
committed
ENG-1917: List groups
1 parent 7816e18 commit d22b178

7 files changed

Lines changed: 177 additions & 4 deletions

File tree

apps/obsidian/src/components/AdminPanelSettings.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@ export const AdminPanelSettings = () => {
6565
new Notice("Failed to connect to the database", 3000);
6666
return;
6767
}
68-
if (data) window.open(`${nextRoot()}/auth/token?t=${data}&url=/`, "_blank");
68+
if (data)
69+
window.open(
70+
`${nextRoot()}auth/token?t=${data}&url=/auth/group`,
71+
"_blank",
72+
);
6973
};
7074

7175
return (

apps/roam/src/components/settings/AdminPanel.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,11 @@ const FeatureFlagsTab = (): React.ReactElement => {
325325
});
326326
return;
327327
}
328-
if (data) window.open(`${nextRoot()}/auth/token?t=${data}&url=/`, "_blank");
328+
if (data)
329+
window.open(
330+
`${nextRoot()}auth/token?t=${data}&url=/auth/group`,
331+
"_blank",
332+
);
329333
};
330334

331335
return (
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { ListGroups } from "~/components/auth/ListGroups";
2+
import { Suspense } from "react";
3+
4+
const Page = () => (
5+
<main>
6+
<div className="mx-auto max-w-6xl space-y-12 px-6 py-12">
7+
<Suspense fallback={<>Loading</>}>
8+
<ListGroups />
9+
</Suspense>
10+
</div>
11+
</main>
12+
);
13+
14+
export default Page;
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
"use client";
2+
3+
import { createClient } from "~/utils/supabase/client";
4+
import { getSessionUserData } from "~/utils/supabase/account";
5+
import { useState, useEffect } from "react";
6+
import { Tables } from "@repo/database/dbTypes";
7+
import useInternalError from "~/utils/internalError";
8+
9+
type GroupData = Tables<"my_groups">;
10+
11+
export const ListGroups = () => {
12+
const [groupData, setGroupData] = useState<GroupData[] | null>(null);
13+
const [adminData, setAdminData] = useState<Record<string, boolean>>({});
14+
const [userName, setUserName] = useState<string | null>(null);
15+
const [error, setError] = useState<string | null>(null);
16+
const internalError = useInternalError();
17+
18+
useEffect(() => {
19+
const getGroups = async () => {
20+
try {
21+
const client = createClient();
22+
const userData = await getSessionUserData(client);
23+
if (!userData) {
24+
const userMessage = "Not logged in.\nPlease log in from application.";
25+
setError(userMessage);
26+
return;
27+
}
28+
const { name, type, id } = userData;
29+
if (type === "anonymous") setUserName("Space " + name);
30+
else if (type === "group") setUserName("group " + name);
31+
else if (type === "person") setUserName(name);
32+
const groupResponse = await client.from("my_groups").select();
33+
if (groupResponse.error) {
34+
const userMessage = "Could not access DiscourseGraphs";
35+
setError(userMessage);
36+
internalError({
37+
error: groupResponse.error,
38+
});
39+
return;
40+
}
41+
setGroupData(groupResponse.data);
42+
const membershipReq = await client
43+
.from("group_membership")
44+
.select("group_id,admin")
45+
.eq("member_id", id);
46+
if (membershipReq.error) {
47+
const userMessage = "Could not access DiscourseGraphs";
48+
setError(userMessage);
49+
internalError({
50+
error: membershipReq.error,
51+
});
52+
return;
53+
}
54+
setAdminData(
55+
Object.fromEntries(
56+
// eslint-disable-next-line @typescript-eslint/naming-convention
57+
membershipReq.data.map(({ group_id, admin }) => [
58+
group_id,
59+
admin || false,
60+
]),
61+
),
62+
);
63+
} catch (error) {
64+
const userMessage = "Unknown error occurred";
65+
setError(userMessage);
66+
internalError({
67+
error,
68+
});
69+
}
70+
};
71+
void getGroups();
72+
}, [internalError]);
73+
74+
return (
75+
<div>
76+
<div className="text-right text-sm">
77+
{userName ? <p>Logged in as {userName}</p> : ""}
78+
</div>
79+
<div>
80+
{error ? (
81+
"Error: " + error
82+
) : groupData === null ? (
83+
"loading"
84+
) : groupData.length === 0 ? (
85+
<p>You are not part of any group.</p>
86+
) : (
87+
<>
88+
<p>Your groups:</p>
89+
<ul className="list-inside list-disc space-y-2">
90+
{groupData.map((d) => (
91+
<li key={d.id}>
92+
{adminData[d.id || ""] ? (
93+
<a href={"group/" + d.id!}>{d.name}</a>
94+
) : (
95+
d.name
96+
)}
97+
</li>
98+
))}
99+
</ul>
100+
</>
101+
)}
102+
</div>
103+
</div>
104+
);
105+
};

apps/website/app/components/auth/LoginWithToken.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export const LoginWithToken = () => {
7575
router.replace(url);
7676
}
7777
} catch (error) {
78-
setError("Unkown error while logging you in.");
78+
setError("Unknown error while logging you in.");
7979
internalError({ error, type: "token-login-exception" });
8080
} finally {
8181
setDone(true);

apps/website/app/utils/supabase/account.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,51 @@
1+
import type { Database } from "@repo/database/dbTypes";
12
import type { DGSupabaseClient } from "@repo/database/lib/client";
23

4+
type AgentType = Database["public"]["Enums"]["AgentType"] | "group";
5+
6+
export const getSessionUserData = async (
7+
client: DGSupabaseClient,
8+
): Promise<{
9+
id: string;
10+
name: string;
11+
type: AgentType;
12+
email?: string;
13+
} | null> => {
14+
const session = await client.auth.getSession();
15+
if (!session?.data?.session?.user) return null;
16+
const { id, email } = session.data.session.user;
17+
if (email) {
18+
const [name, host] = email.split("@") as [string, string];
19+
if (host === "database.discoursegraphs.com" && name.endsWith("-anon")) {
20+
const parts = name.split("-");
21+
const spaceId = Number.parseInt(parts[1]!);
22+
if (Number.isNaN(spaceId)) return null;
23+
const spaceReq = await client
24+
.from("Space")
25+
.select("name")
26+
.eq("id", spaceId)
27+
.maybeSingle();
28+
if (spaceReq.error || !spaceReq.data) {
29+
return null;
30+
}
31+
return { name: spaceReq.data.name, id, type: "anonymous", email };
32+
}
33+
if (host === "groups.discoursegraphs.com") {
34+
return { name, id, email, type: "group" };
35+
}
36+
}
37+
const accountReq = await client
38+
.from("PlatformAccount")
39+
.select("name")
40+
.eq("dg_account", session.data.session.user.id)
41+
.eq("agent_type", "person")
42+
.maybeSingle();
43+
if (accountReq.error || !accountReq.data) {
44+
return null;
45+
}
46+
return { id, name: accountReq.data.name, type: "person", email };
47+
};
48+
349
export const createGroup = async (
450
client: DGSupabaseClient,
551
name: string,

packages/utils/src/execContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const nextRoot = (): string => {
55
process.env.NEXT_API_ROOT !== undefined &&
66
process.env.NEXT_API_ROOT !== ""
77
)
8-
return process.env.NEXT_API_ROOT.split("/").slice(0, 3).join("/");
8+
return process.env.NEXT_API_ROOT.split("/").slice(0, 3).join("/") + "/";
99
return IS_DEV ? "http://localhost:3000/" : "https://discoursegraphs.com/";
1010
};
1111

0 commit comments

Comments
 (0)