@@ -3,15 +3,23 @@ import { Todo } from "../../shared/src";
3
3
import TodoItem from "./components/TodoItem" ;
4
4
import CircleProgress from "./components/CircleProgress" ;
5
5
import { getTodos , createTodo , updateTodo , deleteTodo } from "./api/todoApi" ;
6
+ import socketService from "./services/socketService" ;
6
7
7
8
function App ( ) {
8
9
const [ todos , setTodos ] = useState < Todo [ ] > ( [ ] ) ;
9
10
const [ input , setInput ] = useState ( "" ) ;
10
11
const [ loading , setLoading ] = useState ( true ) ;
11
12
const [ error , setError ] = useState < string | null > ( null ) ;
13
+ const [ onlineUsers , setOnlineUsers ] = useState < number > ( 1 ) ;
12
14
13
- // Fetch todos on component mount
15
+ // Calculate progress
16
+ const totalTodos = todos . length ;
17
+ const completedTodos = todos . filter ( todo => todo . completed ) . length ;
18
+ const progressPercentage = totalTodos === 0 ? 0 : ( completedTodos / totalTodos ) * 100 ;
19
+
20
+ // Connect to WebSocket on component mount
14
21
useEffect ( ( ) => {
22
+ // Initial fetch of todos
15
23
const fetchTodos = async ( ) => {
16
24
try {
17
25
setLoading ( true ) ;
@@ -27,14 +35,32 @@ function App() {
27
35
} ;
28
36
29
37
fetchTodos ( ) ;
38
+
39
+ // Connect to WebSocket
40
+ socketService . connect ( ) ;
41
+
42
+ // Listen for todo updates
43
+ const unsubscribe = socketService . on ( 'todos:update' , ( updatedTodos : Todo [ ] ) => {
44
+ setTodos ( updatedTodos ) ;
45
+ } ) ;
46
+
47
+ // Listen for user count updates
48
+ socketService . on ( 'users:count' , ( count : number ) => {
49
+ setOnlineUsers ( count ) ;
50
+ } ) ;
51
+
52
+ // Cleanup on unmount
53
+ return ( ) => {
54
+ unsubscribe ( ) ;
55
+ socketService . disconnect ( ) ;
56
+ } ;
30
57
} , [ ] ) ;
31
58
32
59
const handleSubmit = async ( e : React . FormEvent < HTMLFormElement > ) => {
33
60
e . preventDefault ( ) ;
34
61
if ( input . trim ( ) ) {
35
62
try {
36
- const newTodo = await createTodo ( input . trim ( ) ) ;
37
- setTodos ( ( prev ) => [ ...prev , newTodo ] ) ;
63
+ await createTodo ( input . trim ( ) ) ;
38
64
setInput ( "" ) ;
39
65
} catch ( err ) {
40
66
setError ( "Failed to create todo. Please try again." ) ;
@@ -48,8 +74,7 @@ function App() {
48
74
const todoToUpdate = todos . find ( todo => todo . id === id ) ;
49
75
if ( ! todoToUpdate ) return ;
50
76
51
- const updatedTodo = await updateTodo ( id , { completed : ! todoToUpdate . completed } ) ;
52
- setTodos ( todos . map ( todo => todo . id === id ? updatedTodo : todo ) ) ;
77
+ await updateTodo ( id , { completed : ! todoToUpdate . completed } ) ;
53
78
} catch ( err ) {
54
79
setError ( "Failed to update todo. Please try again." ) ;
55
80
console . error ( err ) ;
@@ -59,24 +84,18 @@ function App() {
59
84
const removeTodo = async ( id : string ) => {
60
85
try {
61
86
await deleteTodo ( id ) ;
62
- setTodos ( todos . filter ( todo => todo . id !== id ) ) ;
63
87
} catch ( err ) {
64
88
setError ( "Failed to delete todo. Please try again." ) ;
65
89
console . error ( err ) ;
66
90
}
67
91
} ;
68
92
69
- // Calculate stats
70
- const totalTodos = todos . length ;
71
- const completedTodos = todos . filter ( todo => todo . completed ) . length ;
72
- const progressPercentage = totalTodos === 0 ? 0 : ( completedTodos / totalTodos ) * 100 ;
73
-
74
93
return (
75
94
< div className = "min-h-screen bg-zinc-900 py-8" >
76
95
{ /* Hero Section */ }
77
96
< div className = "max-w-md mx-auto bg-zinc-800 rounded-xl shadow-lg overflow-hidden mb-6" >
78
97
< div className = "p-6" >
79
- < h1 className = "text2xl font-bold text-center text-white mb-6" > The world's most complex todo app</ h1 >
98
+ < h1 className = "text-2xl font-bold text-center text-white mb-6" > The world's most complex todo app</ h1 >
80
99
81
100
< div className = "flex items-center justify-between" >
82
101
< div className = "space-y-2" >
@@ -91,6 +110,10 @@ function App() {
91
110
? "All done! 🎉"
92
111
: `${ totalTodos - completedTodos } remaining` }
93
112
</ p >
113
+ < div className = "text-xs text-zinc-500 mt-2" >
114
+ < span className = "inline-block w-2 h-2 bg-green-500 rounded-full mr-1" > </ span >
115
+ { onlineUsers } user{ onlineUsers !== 1 ? 's' : '' } online
116
+ </ div >
94
117
</ div >
95
118
96
119
< CircleProgress percentage = { progressPercentage } />
@@ -150,4 +173,4 @@ function App() {
150
173
) ;
151
174
}
152
175
153
- export default App ;
176
+ export default App ;
0 commit comments