Implement session invites

This commit is contained in:
Pijus Kamandulis
2024-10-12 00:04:36 +03:00
parent 9b45d372fa
commit 1463c05731
16 changed files with 795 additions and 1 deletions

View File

@@ -1,4 +1,4 @@
import { Client, Account, Databases } from 'appwrite';
import { Client, Account, Databases, Functions } from 'appwrite';
export const client = new Client();
@@ -10,7 +10,10 @@ export { ID } from 'appwrite';
export const account = new Account(client);
export const databases = new Databases(client);
export const functions = new Functions(client);
export const DATABASE_ID = import.meta.env.VITE_APPWRITE_DATABASE_ID;
export const ESTIMATION_SESSION_COLLECTION_ID = import.meta.env
.VITE_APPWRITE_ESTIMATION_SESSION_COLLECTION_ID;
export const SESSION_INVITE_FUNCTION_ID = import.meta.env
.VITE_SESSION_INVITE_FUNCTION_ID;

View File

@@ -0,0 +1,88 @@
import { ExecutionMethod } from 'appwrite';
import { functions, SESSION_INVITE_FUNCTION_ID } from '../appwrite';
type SessionInviteInfo =
| {
success: true;
id: string;
name: string;
}
| {
success: false;
message: string;
};
type JoinSessionResponse =
| {
success: true;
}
| {
success: false;
message: string;
};
const getInviteInfo = async (sessionId: string): Promise<SessionInviteInfo> => {
const result = await functions.createExecution(
SESSION_INVITE_FUNCTION_ID,
undefined,
false,
`/?action=get-info&estimationId=${sessionId}`,
ExecutionMethod.GET,
{},
);
if (result.status === 'failed') {
return {
success: false,
message: result.errors ?? 'Failed to get estimation session info',
};
}
const responseBody = JSON.parse(result.responseBody);
if (responseBody.message) {
return {
success: false,
message: responseBody.message,
};
}
const { id, name } = responseBody.result;
return {
success: true,
id,
name,
};
};
const joinSession = async (sessionId: string): Promise<JoinSessionResponse> => {
const result = await functions.createExecution(
SESSION_INVITE_FUNCTION_ID,
undefined,
false,
`/?action=join&estimationId=${sessionId}`,
ExecutionMethod.GET,
{},
);
if (result.status === 'failed') {
return {
success: false,
message: result.errors ?? 'Failed to join session',
};
}
const responseBody = JSON.parse(result.responseBody);
if (responseBody.message) {
return {
success: false,
message: responseBody.message,
};
}
return {
success: true,
};
};
export { getInviteInfo, joinSession };
export type { SessionInviteInfo };

View File

@@ -17,6 +17,7 @@ import { EstimationContextProvider } from './lib/context/estimation';
import Estimation from './pages/Estimation/Estimation';
import Header from './components/Header';
import Profile from './pages/Profile';
import Join from './pages/Join';
interface RouterContext {
userContext: UserContextType;
@@ -68,6 +69,12 @@ const profileRoute = createRoute({
getParentRoute: () => authenticatedRoute,
});
const joinRoute = createRoute({
path: 'join/$sessionId',
component: Join,
getParentRoute: () => authenticatedRoute,
});
const estimationSessionRoute = createRoute({
path: 'estimate/session/$sessionId',
component: Estimation,
@@ -77,6 +84,7 @@ const estimationSessionRoute = createRoute({
const router = createRouter({
routeTree: rootRoute.addChildren([
authenticatedRoute.addChildren([
joinRoute,
indexRoute,
profileRoute,
estimationSessionRoute,

79
src/pages/Join.tsx Normal file
View File

@@ -0,0 +1,79 @@
import { useEffect, useState } from 'react';
import {
getInviteInfo,
joinSession,
SessionInviteInfo,
} from '../lib/functions/estimationSessionInvite';
import { getRouteApi } from '@tanstack/react-router';
const route = getRouteApi('/_authenticated/join/$sessionId');
const Join = () => {
const navigate = route.useNavigate();
const { sessionId } = route.useParams();
const [sessionInfo, setSessionInfo] = useState<SessionInviteInfo>();
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
getInviteInfo(sessionId).then((sessionInfo) => {
setSessionInfo(sessionInfo);
setIsLoading(false);
});
}, [sessionId]);
const handleAccept = async () => {
setIsLoading(true);
await joinSession(sessionId);
navigate({
to: '/estimate/session/$sessionId',
params: {
sessionId: sessionId,
},
});
};
const handleReturnHome = () => {
navigate({
to: '/',
});
};
if (!sessionInfo || isLoading) {
// TODO: add loader
return <p>Loading...</p>;
}
if (!sessionInfo.success) {
return <p>{sessionInfo.message}</p>;
}
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-gray-50 transition-colors dark:bg-nero-900">
<div className="max-w-lg rounded-lg bg-white p-8 text-center shadow-lg dark:bg-nero-800">
<h1 className="mb-4 text-2xl font-semibold text-gray-900 dark:text-gray-100">
You have been invited to join a new estimation session!
</h1>
<p className="mb-6 text-lg text-gray-700 dark:text-gray-300">
Session Name: <strong>{sessionInfo.name}</strong>
</p>
<div className="flex justify-center gap-4">
<button
onClick={handleAccept}
className="rounded-md bg-indigo-600 px-6 py-2 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-indigo-500"
>
Accept
</button>
<button
onClick={handleReturnHome}
className="rounded-md bg-gray-300 px-6 py-2 text-sm font-semibold text-gray-900 shadow-sm transition-colors hover:bg-gray-200 dark:bg-nero-700 dark:text-gray-100 dark:hover:bg-gray-600"
>
Return to Home
</button>
</div>
</div>
</div>
);
};
export default Join;

1
src/vite-env.d.ts vendored
View File

@@ -5,6 +5,7 @@ interface ImportMetaEnv {
readonly VITE_APPWRITE_PROJECT_ID: string;
readonly VITE_APPWRITE_DATABASE_ID: string;
readonly VITE_APPWRITE_ESTIMATION_SESSION_COLLECTION_ID: string;
readonly VITE_SESSION_INVITE_FUNCTION_ID: string;
}
interface ImportMeta {