Refined the design a bit
This commit is contained in:
parent
62630d63f2
commit
ce3d351769
@ -16,20 +16,16 @@ public class TodosController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<ActionResult<Core.Entities.Todo>> CreateTodo([FromBody] CreateTodoRequest request)
|
public async Task<ActionResult<Core.Entities.Todo>> CreateTodo([FromBody] CreateTodoRequest request) =>
|
||||||
{
|
Ok(await _todoRepository.CreateTodoAsync(request.Title));
|
||||||
var todo = await _todoRepository.CreateTodoAsync(request.Title);
|
|
||||||
|
|
||||||
return Ok(todo);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<IEnumerable<Core.Entities.Todo>>> GetTodos()
|
public async Task<ActionResult<IEnumerable<Core.Entities.Todo>>> GetTodos() =>
|
||||||
{
|
Ok(await _todoRepository.GetTodosAsync());
|
||||||
var todos = await _todoRepository.GetTodosAsync();
|
|
||||||
|
|
||||||
return Ok(todos);
|
[HttpGet]
|
||||||
}
|
public async Task<ActionResult<IEnumerable<Core.Entities.Todo>>> GetNotDoneTodos() =>
|
||||||
|
Ok(await _todoRepository.GetNotDoneTodos());
|
||||||
|
|
||||||
public record CreateTodoRequest
|
public record CreateTodoRequest
|
||||||
{
|
{
|
||||||
|
@ -18,36 +18,47 @@ namespace Todo.Api.Hubs
|
|||||||
{
|
{
|
||||||
var _ = await _todoRepository.CreateTodoAsync(todoTitle);
|
var _ = await _todoRepository.CreateTodoAsync(todoTitle);
|
||||||
|
|
||||||
var todos = await _todoRepository.GetTodosAsync();
|
var todos = await _todoRepository.GetNotDoneTodos();
|
||||||
var serializedTodos =
|
var serializedTodos =
|
||||||
JsonSerializer.Serialize(todos
|
JsonSerializer.Serialize(todos
|
||||||
.Select(t => new TodoResponse { Id = t.Id, Title = t.Title })
|
.Select(t => new TodoResponse { Id = t.Id, Title = t.Title })
|
||||||
.ToList());
|
.ToList());
|
||||||
|
|
||||||
await Clients.Caller.SendAsync("todos", serializedTodos);
|
await Clients.Caller.SendAsync("getInboxTodos", serializedTodos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateTodo(string todoId, bool todoStatus)
|
public async Task UpdateTodo(string todoId, bool todoStatus)
|
||||||
{
|
{
|
||||||
await _todoRepository.UpdateTodoStatus(todoId, todoStatus);
|
await _todoRepository.UpdateTodoStatus(todoId, todoStatus);
|
||||||
|
|
||||||
var todos = await _todoRepository.GetTodosAsync();
|
var todos = await _todoRepository.GetNotDoneTodos();
|
||||||
var serializedTodos =
|
var serializedTodos =
|
||||||
JsonSerializer.Serialize(todos
|
JsonSerializer.Serialize(todos
|
||||||
.Select(t => new TodoResponse { Id = t.Id, Title = t.Title, Status = t.Status })
|
.Select(t => new TodoResponse { Id = t.Id, Title = t.Title, Status = t.Status })
|
||||||
.ToList());
|
.ToList());
|
||||||
|
|
||||||
await Clients.Caller.SendAsync("todos", serializedTodos);
|
await Clients.Caller.SendAsync("getInboxTodos", serializedTodos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GetTodos()
|
public async Task GetTodos()
|
||||||
{
|
{
|
||||||
var todos = await _todoRepository.GetTodosAsync();
|
var todos = await _todoRepository.GetTodosAsync();
|
||||||
var serializedTodos = JsonSerializer.Serialize(todos
|
var serializedTodos = JsonSerializer.Serialize(todos
|
||||||
.Select(t => new TodoResponse { Id = t.Id, Title = t.Title, Status = t.Status})
|
.Select(t => new TodoResponse { Id = t.Id, Title = t.Title, Status = t.Status })
|
||||||
.ToList());
|
.ToList());
|
||||||
|
|
||||||
await Clients.Caller.SendAsync("todos", serializedTodos);
|
await Clients.Caller.SendAsync("todos", serializedTodos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task GetInboxTodos()
|
||||||
|
{
|
||||||
|
var todos = await _todoRepository.GetNotDoneTodos();
|
||||||
|
var serializedTodos = JsonSerializer.Serialize(todos
|
||||||
|
.Select(t => new TodoResponse { Id = t.Id, Title = t.Title, Status = t.Status })
|
||||||
|
.ToList());
|
||||||
|
|
||||||
|
await Clients.Caller.SendAsync("getInboxTodos", serializedTodos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,4 +5,5 @@ public interface ITodoRepository
|
|||||||
Task<Entities.Todo> CreateTodoAsync(string title);
|
Task<Entities.Todo> CreateTodoAsync(string title);
|
||||||
Task<IEnumerable<Entities.Todo>> GetTodosAsync();
|
Task<IEnumerable<Entities.Todo>> GetTodosAsync();
|
||||||
Task UpdateTodoStatus(string todoId, bool todoStatus);
|
Task UpdateTodoStatus(string todoId, bool todoStatus);
|
||||||
|
Task<IEnumerable<Entities.Todo>> GetNotDoneTodos();
|
||||||
}
|
}
|
@ -20,7 +20,7 @@ public class TodoRepository : ITodoRepository
|
|||||||
{
|
{
|
||||||
var todo = new MongoTodo() { Title = title };
|
var todo = new MongoTodo() { Title = title };
|
||||||
await _todosCollection.InsertOneAsync(todo);
|
await _todosCollection.InsertOneAsync(todo);
|
||||||
return new Core.Entities.Todo() { Id = todo.Id, Title = todo.Title };
|
return new Core.Entities.Todo() { Id = todo.Id, Title = todo.Title, Status = false};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Core.Entities.Todo>> GetTodosAsync()
|
public async Task<IEnumerable<Core.Entities.Todo>> GetTodosAsync()
|
||||||
@ -38,4 +38,10 @@ public class TodoRepository : ITodoRepository
|
|||||||
.UpdateOneAsync(t => t.Id == todoId,
|
.UpdateOneAsync(t => t.Id == todoId,
|
||||||
Builders<MongoTodo>.Update.Set(t => t.Status, todoStatus));
|
Builders<MongoTodo>.Update.Set(t => t.Status, todoStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Core.Entities.Todo>> GetNotDoneTodos()
|
||||||
|
{
|
||||||
|
var todos = await GetTodosAsync();
|
||||||
|
return todos.Where(t => t.Status == false);
|
||||||
|
}
|
||||||
}
|
}
|
13
src/client/.idea/runConfigurations/dev.xml
Normal file
13
src/client/.idea/runConfigurations/dev.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="dev" type="js.build_tools.npm" nameIsGenerated="true">
|
||||||
|
<package-json value="$PROJECT_DIR$/package.json" />
|
||||||
|
<command value="run" />
|
||||||
|
<scripts>
|
||||||
|
<script value="dev" />
|
||||||
|
</scripts>
|
||||||
|
<node-interpreter value="project" />
|
||||||
|
<package-manager value="yarn" />
|
||||||
|
<envs />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
@ -2,9 +2,11 @@ import { useState } from "react";
|
|||||||
import { CollapsedAddTodo } from "@src/components/todos/collapsed/collapsedAddTodo";
|
import { CollapsedAddTodo } from "@src/components/todos/collapsed/collapsedAddTodo";
|
||||||
import { AddTodoForm } from "@src/components/todos/collapsed/addTodoForm";
|
import { AddTodoForm } from "@src/components/todos/collapsed/addTodoForm";
|
||||||
import { CollapsedState } from "@src/components/todos/collapsed/collapsedState";
|
import { CollapsedState } from "@src/components/todos/collapsed/collapsedState";
|
||||||
import {HubConnection} from "@microsoft/signalr";
|
import { useCreateTodo } from "@src/presentation/hooks/socketHooks";
|
||||||
|
|
||||||
|
export function AddTodo(props: {}) {
|
||||||
|
const { createTodo } = useCreateTodo();
|
||||||
|
|
||||||
export function AddTodo(props: {conn: HubConnection}) {
|
|
||||||
const [collapsed, setCollapsed] = useState<CollapsedState>(
|
const [collapsed, setCollapsed] = useState<CollapsedState>(
|
||||||
CollapsedState.collapsed
|
CollapsedState.collapsed
|
||||||
);
|
);
|
||||||
@ -20,7 +22,7 @@ export function AddTodo(props: {conn: HubConnection}) {
|
|||||||
return (
|
return (
|
||||||
<AddTodoForm
|
<AddTodoForm
|
||||||
onAdd={(todoName) => {
|
onAdd={(todoName) => {
|
||||||
props.conn.invoke("CreateTodo", todoName).catch(console.error)
|
createTodo(todoName);
|
||||||
}}
|
}}
|
||||||
onClose={() => setCollapsed(CollapsedState.collapsed)}
|
onClose={() => setCollapsed(CollapsedState.collapsed)}
|
||||||
/>
|
/>
|
||||||
|
@ -7,29 +7,44 @@ export const AddTodoForm: FC<{
|
|||||||
const [todoName, setTodoName] = useState("");
|
const [todoName, setTodoName] = useState("");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 space-y-1">
|
<form
|
||||||
<div className="todo-input-form py-2 px-4 bg-gray-900 border border-gray-600">
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onAdd(todoName);
|
||||||
|
setTodoName("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="py-2 space-y-3">
|
||||||
|
<div className="todo-input-form py-2 px-4 bg-gray-800 border border-gray-500 rounded-lg">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Todo name"
|
placeholder="Todo name"
|
||||||
className="text-sm"
|
className="text-sm"
|
||||||
|
autoFocus
|
||||||
value={todoName}
|
value={todoName}
|
||||||
|
tabIndex={1}
|
||||||
onChange={(e) => setTodoName(e.target.value)}
|
onChange={(e) => setTodoName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-x-2">
|
<div className="space-x-2">
|
||||||
<button
|
<button
|
||||||
|
type="submit"
|
||||||
|
tabIndex={2}
|
||||||
disabled={!todoName}
|
disabled={!todoName}
|
||||||
onClick={() => {
|
className="base-button dark:bg-accent-500 disabled:bg-accent-800 active:bg-accent-400"
|
||||||
onAdd(todoName);
|
|
||||||
setTodoName("");
|
|
||||||
}}
|
|
||||||
className="disabled:text-gray-800 transition"
|
|
||||||
>
|
>
|
||||||
Add todo
|
Add todo
|
||||||
</button>
|
</button>
|
||||||
<button onClick={onClose}>Cancel</button>
|
<button
|
||||||
|
tabIndex={3}
|
||||||
|
type="button"
|
||||||
|
className="base-button"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
|
||||||
export const CollapsedAddTodo: FC<{ onClick: () => void }> = ({ onClick }) => {
|
export const CollapsedAddTodo: FC<{ onClick: () => void }> = ({ onClick }) => {
|
||||||
return <div onClick={onClick}>Add todo</div>;
|
return (
|
||||||
|
<div
|
||||||
|
className="cursor-pointer select-none dark:text-gray-400"
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
Add todo
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -11,7 +11,7 @@ export const TodoCheckmark: FC<TodoCheckmarkProps> = (props) => (
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
props.updateTodo({ ...props.todo, status: !props.todo.status })
|
props.updateTodo({ ...props.todo, status: !props.todo.status })
|
||||||
}
|
}
|
||||||
className={`h-5 w-5 rounded-full border dark:border-gray-700 ${
|
className={`h-5 w-5 rounded-full border dark:border-gray-500 ${
|
||||||
props.todo.status === StatusState.done
|
props.todo.status === StatusState.done
|
||||||
? "dark:bg-gray-700"
|
? "dark:bg-gray-700"
|
||||||
: "hover:dark:bg-gray-600"
|
: "hover:dark:bg-gray-600"
|
||||||
|
@ -8,7 +8,7 @@ interface TodoItemProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const TodoItem: FC<TodoItemProps> = (props) => (
|
export const TodoItem: FC<TodoItemProps> = (props) => (
|
||||||
<div className="py-3 border-b border-gray-300 dark:border-gray-700">
|
<div className="py-3 border-b border-gray-300 dark:border-gray-600">
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<TodoCheckmark {...props} />
|
<TodoCheckmark {...props} />
|
||||||
<span className="pb-1">{props.todo.title}</span>
|
<span className="pb-1">{props.todo.title}</span>
|
||||||
|
@ -1,24 +1,28 @@
|
|||||||
import { Todo } from "@src/core/entities/todo";
|
import { Todo } from "@src/core/entities/todo";
|
||||||
import { TodoItem } from "@src/components/todos/todoItem";
|
import { TodoItem } from "@src/components/todos/todoItem";
|
||||||
import { AddTodo } from "@src/components/todos/addTodo";
|
import { AddTodo } from "@src/components/todos/addTodo";
|
||||||
import { HubConnection } from "@microsoft/signalr";
|
import { useUpdateTodoState } from "@src/presentation/hooks/socketHooks";
|
||||||
|
|
||||||
export const TodoList = (props: { todos: Todo[]; conn: HubConnection }) => (
|
export const TodoList = (props: { todos: Todo[]; hideDone: boolean }) => {
|
||||||
|
const { updateTodoState } = useUpdateTodoState();
|
||||||
|
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
<ul id="inbox">
|
<ul id="inbox">
|
||||||
{props.todos.map((t, i) => (
|
{props.todos
|
||||||
|
.filter((t) => t.status == false)
|
||||||
|
.map((t, i) => (
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
<TodoItem
|
<TodoItem
|
||||||
todo={t}
|
todo={t}
|
||||||
updateTodo={(todo) => {
|
updateTodo={(todo) => {
|
||||||
props.conn
|
updateTodoState(todo.id, todo.status);
|
||||||
.invoke("UpdateTodo", todo.id, todo.status)
|
|
||||||
.catch(console.error);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<AddTodo conn={props.conn} />
|
<AddTodo />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
type NotDone = false;
|
type NotDone = false;
|
||||||
type Done = true;
|
type Done = true;
|
||||||
type StatusState = NotDone | Done;
|
export type StatusState = NotDone | Done;
|
||||||
export const StatusState: { done: Done; notDone: NotDone } = {
|
export const StatusState: { done: Done; notDone: NotDone } = {
|
||||||
done: true,
|
done: true,
|
||||||
notDone: false,
|
notDone: false,
|
||||||
@ -11,3 +11,19 @@ export interface Todo {
|
|||||||
title: string;
|
title: string;
|
||||||
status: StatusState;
|
status: StatusState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const asTodo = (item: Todo): Todo => {
|
||||||
|
if (!item.id) {
|
||||||
|
throw new Error("Validation failed: id is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item.title) {
|
||||||
|
throw new Error("Validation failed: title is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!item.status) {
|
||||||
|
throw new Error("Validation failed: status is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
};
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { AppProps } from "next/app";
|
import { AppProps } from "next/app";
|
||||||
|
|
||||||
import "@src/styles/tailwind.css";
|
import "@src/styles/tailwind.css";
|
||||||
|
import SocketProvider from "@src/presentation/contexts/SocketContext";
|
||||||
|
|
||||||
const MyApp = ({ Component, pageProps }: AppProps) => (
|
const MyApp = ({ Component, pageProps }: AppProps) => (
|
||||||
|
<SocketProvider>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
|
</SocketProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default MyApp;
|
export default MyApp;
|
||||||
|
@ -1,34 +1,15 @@
|
|||||||
import { useEffect, useState } from "react";
|
|
||||||
import { PageHeading } from "@src/components/common/headings/pageHeading";
|
import { PageHeading } from "@src/components/common/headings/pageHeading";
|
||||||
import { TodoList } from "@src/components/todos";
|
import { TodoList } from "@src/components/todos";
|
||||||
import * as signalR from "@microsoft/signalr";
|
import { useSelectInboxTodos } from "@src/presentation/hooks/socketHooks";
|
||||||
import { HubConnection } from "@microsoft/signalr";
|
|
||||||
import { Todo } from "@src/core/entities/todo";
|
|
||||||
|
|
||||||
const HomePage = () => {
|
const HomePage = () => {
|
||||||
const [conn, setConn] = useState<HubConnection>();
|
const { todos, loading } = useSelectInboxTodos();
|
||||||
const [todos, setTodos] = useState<Todo[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const connection = new signalR.HubConnectionBuilder()
|
|
||||||
.withUrl("http://localhost:5000/hubs/todo")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
connection.on("todos", (todos) => {
|
|
||||||
const parsedTodos = JSON.parse(todos);
|
|
||||||
setTodos(parsedTodos);
|
|
||||||
});
|
|
||||||
|
|
||||||
connection.start().then(() => connection.invoke("GetTodos"));
|
|
||||||
|
|
||||||
setConn(connection);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="py-8 px-14 space-y-6">
|
<main className="py-8 px-14 space-y-6">
|
||||||
<section className="space-y-2">
|
<section className="space-y-2 mx-auto max-w-4xl">
|
||||||
<PageHeading title="Inbox" />
|
<PageHeading title="Inbox" />
|
||||||
<TodoList todos={todos} conn={conn} />
|
<TodoList todos={loading ? [] : todos} hideDone />
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
85
src/client/src/presentation/contexts/SocketContext.tsx
Normal file
85
src/client/src/presentation/contexts/SocketContext.tsx
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { createContext, FC, useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
HubConnection,
|
||||||
|
HubConnectionBuilder,
|
||||||
|
LogLevel,
|
||||||
|
} from "@microsoft/signalr";
|
||||||
|
import { asTodo, StatusState, Todo } from "@src/core/entities/todo";
|
||||||
|
|
||||||
|
interface SocketContextProps {
|
||||||
|
conn: HubConnection;
|
||||||
|
todos: Todo[];
|
||||||
|
inboxTodos: Todo[];
|
||||||
|
getTodos: () => void;
|
||||||
|
getInboxTodos: () => void;
|
||||||
|
createTodo: (todoName: string) => void;
|
||||||
|
updateTodo: (todoId: string, todoStatus: StatusState) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SocketContext = createContext<SocketContextProps>({
|
||||||
|
conn: void 0,
|
||||||
|
todos: [],
|
||||||
|
inboxTodos: [],
|
||||||
|
getTodos: () => {},
|
||||||
|
getInboxTodos: () => {},
|
||||||
|
createTodo: (todoName) => {},
|
||||||
|
updateTodo: (todoId, todoStatus) => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SocketProvider: FC = (props) => {
|
||||||
|
const [conn, setConn] = useState<HubConnection>();
|
||||||
|
const [todos, setTodos] = useState<Todo[]>([]);
|
||||||
|
const [inboxTodos, setInboxTodos] = useState<Todo[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const connection = new HubConnectionBuilder()
|
||||||
|
.withUrl("http://localhost:5000/hubs/todo")
|
||||||
|
.withAutomaticReconnect()
|
||||||
|
.configureLogging(LogLevel.Information)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
connection.on("getTodos", (rawTodos) => {
|
||||||
|
const newTodos = JSON.parse(rawTodos) as Todo[];
|
||||||
|
const validatedTodos = newTodos.map(asTodo);
|
||||||
|
setTodos(validatedTodos);
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on("getInboxTodos", (rawTodos) => {
|
||||||
|
const newTodos = JSON.parse(rawTodos) as Todo[];
|
||||||
|
const validatedTodos = newTodos
|
||||||
|
.map(asTodo)
|
||||||
|
.filter((t) => t.status == false);
|
||||||
|
setInboxTodos(validatedTodos);
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.start().then(() => {
|
||||||
|
setConn(connection);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SocketContext.Provider
|
||||||
|
value={{
|
||||||
|
conn,
|
||||||
|
todos,
|
||||||
|
inboxTodos,
|
||||||
|
getTodos: () => {
|
||||||
|
conn.invoke("GetTodos").catch(console.error);
|
||||||
|
},
|
||||||
|
getInboxTodos: () => {
|
||||||
|
conn.invoke("GetInboxTodos").catch(console.error);
|
||||||
|
},
|
||||||
|
createTodo: (todoName) => {
|
||||||
|
conn.invoke("CreateTodo", todoName).catch(console.error);
|
||||||
|
},
|
||||||
|
updateTodo: (todoId, todoStatus) => {
|
||||||
|
conn.invoke("UpdateTodo", todoId, todoStatus).catch(console.error);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{conn ? props.children : "loading"}
|
||||||
|
</SocketContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SocketProvider;
|
36
src/client/src/presentation/hooks/socketHooks.tsx
Normal file
36
src/client/src/presentation/hooks/socketHooks.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { useContext, useEffect } from "react";
|
||||||
|
import { SocketContext } from "@src/presentation/contexts/SocketContext";
|
||||||
|
import { StatusState } from "@src/core/entities/todo";
|
||||||
|
|
||||||
|
export const useSelectInboxTodos = () => {
|
||||||
|
const socketContext = useContext(SocketContext);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socketContext.getInboxTodos();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
todos: socketContext.inboxTodos,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCreateTodo = () => {
|
||||||
|
const socketContext = useContext(SocketContext);
|
||||||
|
|
||||||
|
return {
|
||||||
|
createTodo: (todoName: string) => {
|
||||||
|
socketContext.createTodo(todoName);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateTodoState = () => {
|
||||||
|
const socketContext = useContext(SocketContext);
|
||||||
|
|
||||||
|
return {
|
||||||
|
updateTodoState: (todoId: string, todoState: StatusState) => {
|
||||||
|
socketContext.updateTodo(todoId, todoState);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@ -2,7 +2,7 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
@apply shadow-none placeholder-gray-300 dark:placeholder-gray-700 border-none outline-none;
|
@apply shadow-none placeholder-gray-50 dark:placeholder-gray-500 border-none outline-none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12,10 +12,26 @@ input {
|
|||||||
|
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
.base-button {
|
||||||
|
@apply py-2
|
||||||
|
px-4
|
||||||
|
rounded-lg
|
||||||
|
transition
|
||||||
|
bg-gray-700
|
||||||
|
disabled:bg-gray-800
|
||||||
|
active:bg-gray-600
|
||||||
|
border
|
||||||
|
border-gray-500
|
||||||
|
disabled:border-gray-600
|
||||||
|
text-sm
|
||||||
|
font-semibold
|
||||||
|
disabled:text-gray-300
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@apply dark:text-gray-100
|
@apply dark:text-gray-50
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply h-screen dark:bg-black
|
@apply h-screen dark:bg-gray-700
|
||||||
}
|
}
|
@ -5,7 +5,34 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
darkMode: "media", // or 'media' or 'class'
|
darkMode: "media", // or 'media' or 'class'
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {
|
||||||
|
colors: {
|
||||||
|
gray: {
|
||||||
|
50: "#eaeaea",
|
||||||
|
100: "#d4d4d5",
|
||||||
|
200: "#bfbfc0",
|
||||||
|
300: "#a9aaab",
|
||||||
|
400: "#949596",
|
||||||
|
500: "#535557",
|
||||||
|
600: "#3d3f42",
|
||||||
|
700: "#282A2D",
|
||||||
|
800: "#1c1d1f",
|
||||||
|
900: "#141517",
|
||||||
|
},
|
||||||
|
accent: {
|
||||||
|
50: "#fcf6e7",
|
||||||
|
100: "#faeccf",
|
||||||
|
200: "#f5d99e",
|
||||||
|
300: "#efc66e",
|
||||||
|
400: "#eab33d",
|
||||||
|
500: "#e5a00d",
|
||||||
|
600: "#b7800a",
|
||||||
|
700: "#896008",
|
||||||
|
800: "#5c4005",
|
||||||
|
900: "#2e2003",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
extend: {},
|
extend: {},
|
||||||
|
Loading…
Reference in New Issue
Block a user