Cleanup estimation sessions list context
This commit is contained in:
parent
e34c8524b7
commit
f4d3005acd
|
@ -1,5 +1,5 @@
|
|||
import { useForm } from '@tanstack/react-form';
|
||||
import { useEstimationSessions } from '../../lib/context/estimationSession';
|
||||
import { useEstimationsList } from '../../lib/context/estimationsList';
|
||||
import { useUser } from '../../lib/context/user';
|
||||
import Input from '../Input';
|
||||
import Button from '../Button';
|
||||
|
@ -12,13 +12,13 @@ const CreateEstimationSessionForm: React.FC<
|
|||
CreateEstimationSessionFormProps
|
||||
> = ({ onCreated }) => {
|
||||
const user = useUser();
|
||||
const estimationSessions = useEstimationSessions();
|
||||
const estimationsList = useEstimationsList();
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
await estimationSessions?.add({
|
||||
await estimationsList?.add({
|
||||
name: value.name,
|
||||
userId: user.current?.$id,
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
import { DatabaseModels, EntityModels } from '../types';
|
||||
import { useUser } from './user';
|
||||
import { EstimationSessionTicket } from '../types/entityModels';
|
||||
import { mapDatabaseToEntity } from '../mappers/estimationSession';
|
||||
|
||||
interface EstimationContextType {
|
||||
setSessionId: (sessionId: string) => void;
|
||||
|
@ -32,43 +33,6 @@ export const useEstimationContext = () => {
|
|||
return useContext(EstimationContext);
|
||||
};
|
||||
|
||||
const mapEstimationSession = (
|
||||
data: DatabaseModels.EstimationSession,
|
||||
{ userId }: { userId?: string },
|
||||
) => {
|
||||
const sessionState: EntityModels.SessionState = data.sessionState
|
||||
? JSON.parse(data.sessionState)
|
||||
: {
|
||||
votes: [],
|
||||
};
|
||||
|
||||
const tickets = data.tickets
|
||||
? data.tickets.map<EntityModels.EstimationSessionTicket>((ticket) =>
|
||||
JSON.parse(ticket),
|
||||
)
|
||||
: [];
|
||||
|
||||
const result: EntityModels.EstimationSession = {
|
||||
id: data.$id,
|
||||
name: data.name,
|
||||
userId: data.userId,
|
||||
tickets,
|
||||
sessionState: {
|
||||
...sessionState,
|
||||
currentPlayerVote: sessionState.votes.find((x) => x.userId === userId)
|
||||
?.estimate,
|
||||
currentTicket: tickets.find((x) => x.id === sessionState.currentTicketId),
|
||||
},
|
||||
};
|
||||
|
||||
console.log({
|
||||
result,
|
||||
userId,
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const EstimationContextProvider = (props: PropsWithChildren) => {
|
||||
const [sessionId, setSessionId] = useState('');
|
||||
const [currentSessionData, setCurrentSessionData] =
|
||||
|
@ -89,7 +53,7 @@ export const EstimationContextProvider = (props: PropsWithChildren) => {
|
|||
)
|
||||
.then((payload) => {
|
||||
const userId = userData?.$id ?? ''; // TODO: Not sure if this is the user id or session
|
||||
setCurrentSessionData(mapEstimationSession(payload, { userId }));
|
||||
setCurrentSessionData(mapDatabaseToEntity(payload, { userId }));
|
||||
});
|
||||
|
||||
return client.subscribe<DatabaseModels.EstimationSession>(
|
||||
|
@ -98,7 +62,7 @@ export const EstimationContextProvider = (props: PropsWithChildren) => {
|
|||
],
|
||||
({ payload }) => {
|
||||
const userId = userData?.$id ?? ''; // TODO: Not sure if this is the user id or session
|
||||
setCurrentSessionData(mapEstimationSession(payload, { userId }));
|
||||
setCurrentSessionData(mapDatabaseToEntity(payload, { userId }));
|
||||
},
|
||||
);
|
||||
}, [sessionId, userData]);
|
||||
|
|
|
@ -1,258 +0,0 @@
|
|||
import {
|
||||
createContext,
|
||||
PropsWithChildren,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
client,
|
||||
DATABASE_ID,
|
||||
databases,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
} from '../appwrite';
|
||||
import { ID, Models, Query } from 'appwrite';
|
||||
|
||||
interface EstimationSessionType extends Models.Document {
|
||||
userId: string;
|
||||
name: string;
|
||||
tickets: string[];
|
||||
sessionState: string;
|
||||
}
|
||||
|
||||
interface EstimationSessionTicket {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface SessionStateType {
|
||||
currentTicketId: string;
|
||||
votesRevealed: boolean;
|
||||
votes: {
|
||||
userId: string;
|
||||
estimate: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
interface EstimationSessionsContextType {
|
||||
current: EstimationSessionType[];
|
||||
add: (
|
||||
estimationSession: Omit<EstimationSessionType, keyof Models.Document>,
|
||||
) => Promise<void>;
|
||||
remove: (id: string) => Promise<void>;
|
||||
addTicket: (
|
||||
sessionId: string,
|
||||
ticket: Omit<EstimationSessionTicket, 'id'>,
|
||||
) => Promise<void>;
|
||||
getTickets: (sessionId: string) => EstimationSessionTicket[];
|
||||
selectTicket: (sessionId: string, ticketId: string) => Promise<void>;
|
||||
getState: (sessionId: string) => SessionStateType;
|
||||
voteEstimate: (
|
||||
sessionId: string,
|
||||
ticketId: string,
|
||||
estimate: number,
|
||||
userId: string,
|
||||
) => Promise<void>;
|
||||
revealVotes: (sessionId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const EstimationSessionsContext = createContext<
|
||||
EstimationSessionsContextType | undefined
|
||||
>(undefined);
|
||||
|
||||
export function useEstimationSessions() {
|
||||
return useContext(EstimationSessionsContext);
|
||||
}
|
||||
|
||||
export function EstimationSessionProvider(props: PropsWithChildren) {
|
||||
const [estimationSessions, setEstimationSessions] = useState<
|
||||
EstimationSessionType[]
|
||||
>([]);
|
||||
|
||||
const add = async (
|
||||
estimationSession: Omit<EstimationSessionType, keyof Models.Document>,
|
||||
) => {
|
||||
const response = await databases.createDocument<EstimationSessionType>(
|
||||
DATABASE_ID,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
ID.unique(),
|
||||
estimationSession,
|
||||
);
|
||||
setEstimationSessions((estimationSessions) =>
|
||||
[response, ...estimationSessions].slice(0, 10),
|
||||
);
|
||||
};
|
||||
|
||||
const remove = async (id: string) => {
|
||||
await databases.deleteDocument(
|
||||
DATABASE_ID,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
id,
|
||||
);
|
||||
setEstimationSessions((estimationSessions) =>
|
||||
estimationSessions.filter(
|
||||
(estimationSession) => estimationSession.$id !== id,
|
||||
),
|
||||
);
|
||||
await init();
|
||||
};
|
||||
|
||||
const addTicket = async (
|
||||
sessionId: string,
|
||||
ticket: Omit<EstimationSessionTicket, 'id'>,
|
||||
) => {
|
||||
const currentSession = estimationSessions.find((x) => x.$id === sessionId);
|
||||
const response = await databases.updateDocument<EstimationSessionType>(
|
||||
DATABASE_ID,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
sessionId,
|
||||
{
|
||||
tickets: currentSession?.tickets.concat([
|
||||
JSON.stringify({
|
||||
...ticket,
|
||||
id: crypto.randomUUID(),
|
||||
}),
|
||||
]),
|
||||
},
|
||||
);
|
||||
setEstimationSessions((estimationSessions) =>
|
||||
estimationSessions
|
||||
.filter((x) => x.$id != sessionId)
|
||||
.concat([response])
|
||||
.slice(0, 10),
|
||||
);
|
||||
};
|
||||
|
||||
const getTickets = (sessionId: string) => {
|
||||
return (
|
||||
estimationSessions
|
||||
.find((x) => x.$id === sessionId)
|
||||
?.tickets.map<EstimationSessionTicket>((x) => JSON.parse(x)) ?? []
|
||||
);
|
||||
};
|
||||
|
||||
const selectTicket = async (sessionId: string, ticketId: string) => {
|
||||
const response = await databases.updateDocument<EstimationSessionType>(
|
||||
DATABASE_ID,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
sessionId,
|
||||
{
|
||||
sessionState: JSON.stringify({
|
||||
currentTicketId: ticketId,
|
||||
}),
|
||||
},
|
||||
);
|
||||
setEstimationSessions((estimationSessions) =>
|
||||
estimationSessions
|
||||
.filter((x) => x.$id != sessionId)
|
||||
.concat([response])
|
||||
.slice(0, 10),
|
||||
);
|
||||
};
|
||||
|
||||
const getState = (sessionId: string): SessionStateType => {
|
||||
return JSON.parse(
|
||||
estimationSessions.find((x) => x.$id === sessionId)?.sessionState ?? '{}',
|
||||
);
|
||||
};
|
||||
|
||||
const voteEstimate = async (
|
||||
sessionId: string,
|
||||
ticketId: string,
|
||||
estimate: number,
|
||||
userId: string,
|
||||
) => {
|
||||
const currentState = getState(sessionId);
|
||||
const newVotes = (currentState.votes ?? [])
|
||||
.filter((x) => x.userId !== userId)
|
||||
.concat([
|
||||
{
|
||||
estimate: estimate,
|
||||
userId: userId,
|
||||
},
|
||||
]);
|
||||
const response = await databases.updateDocument<EstimationSessionType>(
|
||||
DATABASE_ID,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
sessionId,
|
||||
{
|
||||
sessionState: JSON.stringify({
|
||||
currentTicketId: ticketId,
|
||||
votes: newVotes,
|
||||
}),
|
||||
},
|
||||
);
|
||||
setEstimationSessions((estimationSessions) =>
|
||||
estimationSessions
|
||||
.filter((x) => x.$id != sessionId)
|
||||
.concat([response])
|
||||
.slice(0, 10),
|
||||
);
|
||||
};
|
||||
|
||||
const revealVotes = async (sessionId: string) => {
|
||||
const currentState = getState(sessionId);
|
||||
const response = await databases.updateDocument<EstimationSessionType>(
|
||||
DATABASE_ID,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
sessionId,
|
||||
{
|
||||
sessionState: JSON.stringify({
|
||||
...currentState,
|
||||
VotesRevealed: true,
|
||||
}),
|
||||
},
|
||||
);
|
||||
setEstimationSessions((estimationSessions) =>
|
||||
estimationSessions
|
||||
.filter((x) => x.$id != sessionId)
|
||||
.concat([response])
|
||||
.slice(0, 10),
|
||||
);
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
const response = await databases.listDocuments<EstimationSessionType>(
|
||||
DATABASE_ID,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
[Query.orderDesc('$createdAt'), Query.limit(10)],
|
||||
);
|
||||
setEstimationSessions(response.documents);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
|
||||
return client.subscribe<EstimationSessionType>(
|
||||
[
|
||||
`databases.${DATABASE_ID}.collections.${ESTIMATION_SESSION_COLLECTION_ID}.documents`,
|
||||
],
|
||||
(payload) => {
|
||||
setEstimationSessions((estimationSessions) =>
|
||||
estimationSessions
|
||||
.filter((x) => x.$id != payload.payload.$id)
|
||||
.concat([payload.payload])
|
||||
.slice(0, 10),
|
||||
);
|
||||
},
|
||||
);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<EstimationSessionsContext.Provider
|
||||
value={{
|
||||
current: estimationSessions,
|
||||
add,
|
||||
remove,
|
||||
addTicket,
|
||||
getTickets,
|
||||
selectTicket,
|
||||
getState,
|
||||
voteEstimate,
|
||||
revealVotes,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</EstimationSessionsContext.Provider>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
import {
|
||||
createContext,
|
||||
PropsWithChildren,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
client,
|
||||
DATABASE_ID,
|
||||
databases,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
} from '../appwrite';
|
||||
import { ID, Models, Query } from 'appwrite';
|
||||
import { EntityModels } from '../types';
|
||||
import { mapDatabaseToEntity } from '../mappers/estimationSession';
|
||||
|
||||
interface EstimationSessionType extends Models.Document {
|
||||
userId: string;
|
||||
name: string;
|
||||
tickets: string[];
|
||||
sessionState: string;
|
||||
}
|
||||
|
||||
interface EstimationsListContextType {
|
||||
current: EntityModels.EstimationSession[];
|
||||
add: (
|
||||
estimationSession: Omit<EstimationSessionType, keyof Models.Document>,
|
||||
) => Promise<void>;
|
||||
remove: (id: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const EstimationsListContext = createContext<
|
||||
EstimationsListContextType | undefined
|
||||
>(undefined);
|
||||
|
||||
export function useEstimationsList() {
|
||||
return useContext(EstimationsListContext);
|
||||
}
|
||||
|
||||
export function EstimationsListContextProvider(props: PropsWithChildren) {
|
||||
const [estimationSessions, setEstimationSessions] = useState<
|
||||
EntityModels.EstimationSession[]
|
||||
>([]);
|
||||
|
||||
const add = async (
|
||||
estimationSession: Omit<EstimationSessionType, keyof Models.Document>,
|
||||
) => {
|
||||
const response = await databases.createDocument<EstimationSessionType>(
|
||||
DATABASE_ID,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
ID.unique(),
|
||||
estimationSession,
|
||||
);
|
||||
setEstimationSessions((estimationSessions) =>
|
||||
[mapDatabaseToEntity(response, {}), ...estimationSessions].slice(0, 10),
|
||||
);
|
||||
};
|
||||
|
||||
const remove = async (id: string) => {
|
||||
await databases.deleteDocument(
|
||||
DATABASE_ID,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
id,
|
||||
);
|
||||
setEstimationSessions((estimationSessions) =>
|
||||
estimationSessions.filter(
|
||||
(estimationSession) => estimationSession.id !== id,
|
||||
),
|
||||
);
|
||||
await init();
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
const response = await databases.listDocuments<EstimationSessionType>(
|
||||
DATABASE_ID,
|
||||
ESTIMATION_SESSION_COLLECTION_ID,
|
||||
[Query.orderDesc('$createdAt'), Query.limit(10)],
|
||||
);
|
||||
setEstimationSessions(
|
||||
response.documents.map((document) => mapDatabaseToEntity(document, {})),
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
|
||||
return client.subscribe<EstimationSessionType>(
|
||||
[
|
||||
`databases.${DATABASE_ID}.collections.${ESTIMATION_SESSION_COLLECTION_ID}.documents`,
|
||||
],
|
||||
(payload) => {
|
||||
setEstimationSessions((estimationSessions) =>
|
||||
estimationSessions
|
||||
.filter((x) => x.id != payload.payload.$id)
|
||||
.concat([mapDatabaseToEntity(payload.payload, {})])
|
||||
.slice(0, 10),
|
||||
);
|
||||
},
|
||||
);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<EstimationsListContext.Provider
|
||||
value={{
|
||||
current: estimationSessions,
|
||||
add,
|
||||
remove,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</EstimationsListContext.Provider>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import { DatabaseModels, EntityModels } from '../types';
|
||||
|
||||
export const mapDatabaseToEntity = (
|
||||
data: DatabaseModels.EstimationSession,
|
||||
{ userId }: { userId?: string },
|
||||
) => {
|
||||
const sessionState: EntityModels.SessionState = data.sessionState
|
||||
? JSON.parse(data.sessionState)
|
||||
: {
|
||||
votes: [],
|
||||
};
|
||||
|
||||
const tickets = data.tickets
|
||||
? data.tickets.map<EntityModels.EstimationSessionTicket>((ticket) =>
|
||||
JSON.parse(ticket),
|
||||
)
|
||||
: [];
|
||||
|
||||
const result: EntityModels.EstimationSession = {
|
||||
id: data.$id,
|
||||
name: data.name,
|
||||
userId: data.userId,
|
||||
tickets,
|
||||
sessionState: {
|
||||
...sessionState,
|
||||
currentPlayerVote: sessionState.votes.find((x) => x.userId === userId)
|
||||
?.estimate,
|
||||
currentTicket: tickets.find((x) => x.id === sessionState.currentTicketId),
|
||||
},
|
||||
};
|
||||
|
||||
return result;
|
||||
};
|
|
@ -12,7 +12,7 @@ import {
|
|||
import Home from './pages/Home';
|
||||
import { UserContextType, UserProvider, useUser } from './lib/context/user';
|
||||
import Login from './pages/Login';
|
||||
import { EstimationSessionProvider } from './lib/context/estimationSession';
|
||||
import { EstimationsListContextProvider } from './lib/context/estimationsList';
|
||||
import { EstimationContextProvider } from './lib/context/estimation';
|
||||
import Estimation from './pages/Estimation/Estimation';
|
||||
import Header from './components/Header';
|
||||
|
@ -115,12 +115,12 @@ const InnerApp = () => {
|
|||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<UserProvider>
|
||||
<EstimationSessionProvider>
|
||||
<EstimationsListContextProvider>
|
||||
<EstimationContextProvider>
|
||||
{/* TODO: Move ctx providers to layout */}
|
||||
<InnerApp />
|
||||
</EstimationContextProvider>
|
||||
</EstimationSessionProvider>
|
||||
</EstimationsListContextProvider>
|
||||
</UserProvider>
|
||||
</StrictMode>,
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import './Home.css';
|
||||
import { getRouteApi, Link } from '@tanstack/react-router';
|
||||
import { useUser } from '../lib/context/user';
|
||||
import { useEstimationSessions } from '../lib/context/estimationSession';
|
||||
import { useEstimationsList } from '../lib/context/estimationsList';
|
||||
import {
|
||||
Card,
|
||||
CreateEstimationSessionForm,
|
||||
|
@ -15,7 +15,7 @@ const route = getRouteApi('/_authenticated/');
|
|||
function Home() {
|
||||
const user = useUser();
|
||||
const navigate = route.useNavigate();
|
||||
const estimationSessions = useEstimationSessions();
|
||||
const estimationsList = useEstimationsList();
|
||||
const [isDrawerOpen, setDrawerOpen] = useState(false);
|
||||
|
||||
return (
|
||||
|
@ -33,16 +33,16 @@ function Home() {
|
|||
<p>Estimation sessions</p>
|
||||
<GridList
|
||||
colNum={2}
|
||||
items={estimationSessions?.current ?? []}
|
||||
items={estimationsList?.current ?? []}
|
||||
itemComponent={({ item }) => (
|
||||
<Card
|
||||
key={item.$id}
|
||||
key={item.id}
|
||||
title={item.name}
|
||||
description={item.$id}
|
||||
description={item.id}
|
||||
onClick={() => {
|
||||
navigate({
|
||||
to: '/estimate/session/$sessionId',
|
||||
params: { sessionId: item.$id },
|
||||
params: { sessionId: item.id },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
|
Loading…
Reference in New Issue