Skip to content

Commit f1884d1

Browse files
authored
feat: added disclaimer to group charter (#283)
* feat: added disclaimer to login screen. * feat: added internal modal.
1 parent 4d89694 commit f1884d1

File tree

5 files changed

+3085
-3163
lines changed

5 files changed

+3085
-3163
lines changed
Lines changed: 15 additions & 0 deletions
Loading

platforms/group-charter-manager/src/app/layout.tsx

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,29 @@ import { Inter } from "next/font/google";
33
import "./globals.css";
44
import { Toaster } from "@/components/ui/toaster";
55
import { AuthProvider } from "@/components/auth/auth-provider";
6+
import DisclaimerModal from "@/components/disclaimer-modal";
67

78
const inter = Inter({ subsets: ["latin"] });
89

910
export const metadata: Metadata = {
10-
title: "Group Charter Manager",
11-
description: "Manage your group charters and memberships",
11+
title: "Group Charter Manager",
12+
description: "Manage your group charters and memberships",
1213
};
1314

1415
export default function RootLayout({
15-
children,
16+
children,
1617
}: {
17-
children: React.ReactNode;
18+
children: React.ReactNode;
1819
}) {
19-
return (
20-
<html lang="en">
21-
<body className={inter.className}>
22-
<AuthProvider>
23-
{children}
24-
<Toaster />
25-
</AuthProvider>
26-
</body>
27-
</html>
28-
);
20+
return (
21+
<html lang="en">
22+
<body className={inter.className}>
23+
<AuthProvider>
24+
{children}
25+
<Toaster />
26+
<DisclaimerModal />
27+
</AuthProvider>
28+
</body>
29+
</html>
30+
);
2931
}

platforms/group-charter-manager/src/components/auth/login-screen.tsx

Lines changed: 121 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -5,119 +5,138 @@ import { QRCodeSVG } from "qrcode.react";
55
import { apiClient } from "@/lib/apiClient";
66
import { setAuthToken, setAuthId } from "@/lib/authUtils";
77
import { useRouter } from "next/navigation";
8+
import Image from "next/image";
89

910
export default function LoginScreen() {
10-
const [qrData, setQrData] = useState<string>("");
11-
const [isLoading, setIsLoading] = useState(true);
12-
const [isAuthenticating, setIsAuthenticating] = useState(false);
13-
const router = useRouter();
14-
15-
useEffect(() => {
16-
initializeAuth();
17-
}, []);
18-
19-
const initializeAuth = async () => {
20-
try {
21-
const { data } = await apiClient.get('/api/auth/offer');
22-
setQrData(data.uri);
23-
setIsLoading(false);
24-
25-
// Start watching for authentication
26-
const sessionId = new URL(data.uri).searchParams.get('session');
27-
if (sessionId) {
28-
watchEventStream(sessionId);
29-
}
30-
} catch (error) {
31-
console.error('Failed to get auth offer:', error);
32-
setIsLoading(false);
33-
}
34-
};
11+
const [qrData, setQrData] = useState<string>("");
12+
const [isLoading, setIsLoading] = useState(true);
13+
const [isAuthenticating, setIsAuthenticating] = useState(false);
14+
const router = useRouter();
3515

36-
const watchEventStream = (sessionId: string) => {
37-
const baseUrl = process.env.NEXT_PUBLIC_GROUP_CHARTER_BASE_URL || 'http://localhost:3001';
38-
const sseUrl = `${baseUrl}/api/auth/sessions/${sessionId}`;
39-
const eventSource = new EventSource(sseUrl);
16+
useEffect(() => {
17+
initializeAuth();
18+
}, []);
4019

41-
eventSource.onopen = () => {
42-
console.log('Successfully connected to auth stream.');
43-
};
20+
const initializeAuth = async () => {
21+
try {
22+
const { data } = await apiClient.get("/api/auth/offer");
23+
setQrData(data.uri);
24+
setIsLoading(false);
4425

45-
eventSource.onmessage = (e) => {
46-
const data = JSON.parse(e.data);
47-
console.log('Auth data received:', data);
48-
49-
const { user, token } = data;
50-
51-
// Set authentication data
52-
setAuthId(user.id);
53-
setAuthToken(token);
54-
55-
// Close the event source
56-
eventSource.close();
57-
58-
// Set authenticating state
59-
setIsAuthenticating(true);
60-
61-
// Force a page refresh to trigger AuthProvider re-initialization
62-
window.location.reload();
26+
// Start watching for authentication
27+
const sessionId = new URL(data.uri).searchParams.get("session");
28+
if (sessionId) {
29+
watchEventStream(sessionId);
30+
}
31+
} catch (error) {
32+
console.error("Failed to get auth offer:", error);
33+
setIsLoading(false);
34+
}
6335
};
6436

65-
eventSource.onerror = (error) => {
66-
console.error('EventSource error:', error);
67-
eventSource.close();
37+
const watchEventStream = (sessionId: string) => {
38+
const baseUrl =
39+
process.env.NEXT_PUBLIC_GROUP_CHARTER_BASE_URL ||
40+
"http://localhost:3001";
41+
const sseUrl = `${baseUrl}/api/auth/sessions/${sessionId}`;
42+
const eventSource = new EventSource(sseUrl);
43+
44+
eventSource.onopen = () => {
45+
console.log("Successfully connected to auth stream.");
46+
};
47+
48+
eventSource.onmessage = (e) => {
49+
const data = JSON.parse(e.data);
50+
console.log("Auth data received:", data);
51+
52+
const { user, token } = data;
53+
54+
// Set authentication data
55+
setAuthId(user.id);
56+
setAuthToken(token);
57+
58+
// Close the event source
59+
eventSource.close();
60+
61+
// Set authenticating state
62+
setIsAuthenticating(true);
63+
64+
// Force a page refresh to trigger AuthProvider re-initialization
65+
window.location.reload();
66+
};
67+
68+
eventSource.onerror = (error) => {
69+
console.error("EventSource error:", error);
70+
eventSource.close();
71+
};
6872
};
69-
};
7073

71-
if (isLoading) {
72-
return (
73-
<div className="flex h-screen items-center justify-center">
74-
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900"></div>
75-
</div>
76-
);
77-
}
74+
if (isLoading) {
75+
return (
76+
<div className="flex h-screen items-center justify-center">
77+
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900"></div>
78+
</div>
79+
);
80+
}
81+
82+
if (isAuthenticating) {
83+
return (
84+
<div className="flex h-screen items-center justify-center">
85+
<div className="text-center">
86+
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900 mx-auto mb-4"></div>
87+
<p className="text-lg text-gray-600">Authenticating...</p>
88+
</div>
89+
</div>
90+
);
91+
}
7892

79-
if (isAuthenticating) {
8093
return (
81-
<div className="flex h-screen items-center justify-center">
82-
<div className="text-center">
83-
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900 mx-auto mb-4"></div>
84-
<p className="text-lg text-gray-600">Authenticating...</p>
85-
</div>
86-
</div>
87-
);
88-
}
89-
90-
return (
91-
<div className="flex h-screen items-center justify-center bg-gray-50">
92-
<div className="bg-white p-8 rounded-lg shadow-lg max-w-md w-full">
93-
<div className="text-center mb-8">
94-
<h1 className="text-2xl font-bold text-gray-900 mb-2">
95-
Group Charter Manager
96-
</h1>
97-
<p className="text-gray-600">
98-
Scan the QR code to login with your W3DS identity
99-
</p>
100-
</div>
94+
<div className="flex h-screen items-center justify-center bg-gray-50">
95+
<div className="bg-white p-8 rounded-lg shadow-lg max-w-md w-full">
96+
<div className="text-center mb-8">
97+
<h1 className="text-2xl font-bold text-gray-900 mb-2">
98+
Group Charter Manager
99+
</h1>
100+
<p className="text-gray-600">
101+
Scan the QR code to login with your W3DS identity
102+
</p>
103+
</div>
101104

102-
{qrData && (
103-
<div className="flex justify-center mb-6">
104-
<div className="bg-white p-4 rounded-lg border">
105-
<QRCodeSVG
106-
value={qrData}
107-
size={200}
108-
level="M"
109-
includeMargin={true}
110-
/>
111-
</div>
112-
</div>
113-
)}
105+
{qrData && (
106+
<div className="flex justify-center mb-6">
107+
<div className="bg-white p-4 rounded-lg border">
108+
<QRCodeSVG
109+
value={qrData}
110+
size={200}
111+
level="M"
112+
includeMargin={true}
113+
/>
114+
</div>
115+
</div>
116+
)}
114117

115-
<div className="text-center">
116-
<p className="text-sm text-gray-500">
117-
Use your W3DS wallet to scan this QR code and authenticate
118-
</p>
118+
<div className="text-center">
119+
<p className="text-sm text-gray-500">
120+
Use your W3DS wallet to scan this QR code and
121+
authenticate
122+
</p>
123+
</div>
124+
<p className="p-4 rounded-xl bg-gray-100 text-gray-700 mt-4">
125+
You are entering Group Charter - a group charter management
126+
platform built on the Web 3.0 Data Space (W3DS)
127+
architecture. This system is designed around the principle
128+
of data-platform separation, where all your personal content
129+
is stored in your own sovereign eVault, not on centralised
130+
servers.
131+
</p>
132+
<Image
133+
src="/W3DS.svg"
134+
alt="W3DS Logo"
135+
width={50}
136+
height={20}
137+
className="mx-auto mt-5"
138+
/>
139+
</div>
119140
</div>
120-
</div>
121-
</div>
122-
);
123-
}
141+
);
142+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"use client";
2+
3+
import { Button } from "@/components/ui/button";
4+
import {
5+
Dialog,
6+
DialogContent,
7+
DialogHeader,
8+
DialogTitle,
9+
DialogDescription,
10+
DialogFooter,
11+
} from "@/components/ui/dialog";
12+
import { useState } from "react";
13+
import { useAuth } from "@/components/auth/auth-provider";
14+
15+
export default function DisclaimerModal() {
16+
const { logout } = useAuth();
17+
const [disclaimerAccepted, setDisclaimerAccepted] = useState(false);
18+
return (
19+
<>
20+
{!disclaimerAccepted ? (
21+
<Dialog open>
22+
<DialogContent
23+
className="max-w-lg mx-auto backdrop-blur-md p-6 rounded-lg"
24+
onInteractOutside={() => logout()}
25+
>
26+
<DialogHeader>
27+
<DialogTitle className="text-xl text-center font-bold">
28+
Disclaimer from MetaState Foundation
29+
</DialogTitle>
30+
<DialogDescription asChild>
31+
<div className="flex flex-col gap-2">
32+
<p className="font-bold">⚠️ Please note:</p>
33+
<p>
34+
Group Charter is a{" "}
35+
<b>functional prototype</b>, intended to
36+
showcase <b>interoperability</b> and
37+
core concepts of the W3DS ecosystem.
38+
</p>
39+
<p>
40+
<b>
41+
It is not a production-grade
42+
platform
43+
</b>{" "}
44+
and may lack full reliability,
45+
performance, and security guarantees.
46+
</p>
47+
<p>
48+
We <b>strongly recommend</b> that you
49+
avoid sharing{" "}
50+
<b>sensitive or private content</b>, and
51+
kindly ask for your understanding
52+
regarding any bugs, incomplete features,
53+
or unexpected behaviours.
54+
</p>
55+
<p>
56+
The app is still in development, so we
57+
kindly ask for your understanding
58+
regarding any potential issues. If you
59+
experience issues or have feedback, feel
60+
free to contact us at:
61+
</p>
62+
<a
63+
href="mailto:[email protected]"
64+
className="outline-none"
65+
>
66+
67+
</a>
68+
</div>
69+
</DialogDescription>
70+
</DialogHeader>
71+
<DialogFooter>
72+
<Button
73+
type="button"
74+
onClick={() => {
75+
setDisclaimerAccepted(true);
76+
}}
77+
>
78+
I Understand
79+
</Button>
80+
</DialogFooter>
81+
</DialogContent>
82+
</Dialog>
83+
) : (
84+
<></>
85+
)}
86+
</>
87+
);
88+
}

0 commit comments

Comments
 (0)