Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 0 additions & 38 deletions client/src/App.css

This file was deleted.

1 change: 0 additions & 1 deletion client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';
import './App.css';
import TodoService from './services/TodoService';
import TodoList from './components/TodoList';
import TodoListViewModel from './viewmodels/TodoListViewModel';
Expand Down
25 changes: 18 additions & 7 deletions client/src/components/TodoItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@ import TodoItemViewModel from '../viewmodels/TodoItemViewModel';

type TodoItemProps = {
viewModel: TodoItemViewModel,
deleteItem: () => void
}

export default observer(
({viewModel, deleteItem} : TodoItemProps) => {
({viewModel} : TodoItemProps) => {

const [isEditing, setIsEditing] = useState(false);
const [content, setContent] = useState(viewModel.content);
const [draftContent, setDraftContent] = useState('');

function onCheckboxChanged(e: React.FormEvent<HTMLInputElement>) {
viewModel.setIsDone(e.currentTarget.checked);
}

function onInputChanged(e: React.FormEvent<HTMLInputElement>) {
setContent(e.currentTarget.value);
setDraftContent(e.currentTarget.value);
}

function onInputKeyUp(e: React.KeyboardEvent<HTMLInputElement>) {
Expand All @@ -28,10 +27,19 @@ export default observer(
}

function update() {
viewModel.updateContent(content);
viewModel.updateContent(draftContent);
setIsEditing(false);
}

function deleteItem() {
viewModel.deleteTodoItem();
}

function startDrafting() {
setDraftContent(viewModel.content);
setIsEditing(true);
}

return (
<li>
{viewModel.isAwaiting
Expand All @@ -40,11 +48,14 @@ export default observer(
{viewModel.isAwaiting
? <span>Loading...</span>
: isEditing
? <input onChange={onInputChanged} onKeyUp={onInputKeyUp} value={content}/>
: <span onClick={() => setIsEditing(true)}>{content}</span>}
? <input onChange={onInputChanged} onKeyUp={onInputKeyUp} value={draftContent}/>
: <span onClick={startDrafting}>{viewModel.content}</span>}
{isEditing
? <button onClick={update}>Done</button>
: <button onClick={deleteItem}>Delete</button>}
{viewModel.didRequestFail
? <span>{viewModel.failReason}</span>
: null}
</li>
);
}
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/TodoList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default observer(
<div>
<h1>Todo Items: {viewModel.todosCount}</h1>
<ol>
{viewModel.todoItems?.map(item => <TodoItem key={item.id} viewModel={item} deleteItem={async () => await viewModel.deleteTodoItem(item.id)}/>)}
{viewModel.todoItems?.map(item => <TodoItem key={item.id} viewModel={item}/>)}
</ol>
<div>
<input type="text" value={newItemText} onChange={onInputChanged}/>
Expand Down
1 change: 0 additions & 1 deletion client/src/services/TodoService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import TodoItem from '../models/TodoItem';
import ServerResponseInterface from './ServerResponseInterface';
import { Server } from 'http';

const url = "http://localhost:4963/todos";

Expand Down
16 changes: 15 additions & 1 deletion client/src/viewmodels/TodoItemViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export default class TodoItemViewModel extends PromiseAwareViewModelBase {

constructor(
private service: TodoService,
model : TodoItem
model : TodoItem,
private remove: () => void
) {
super();
this.id = model.id;
Expand Down Expand Up @@ -62,6 +63,19 @@ export default class TodoItemViewModel extends PromiseAwareViewModelBase {
});
}

public async deleteTodoItem() {
await this.runWithAwareness(async () => {
var response = await this.service.deleteTodo(this.id);

if (response.didFail) {
this.didRequestFail = true;
this.failReason = response.failReason;
} else {
this.remove()
}
});
}

//#endregion

}
33 changes: 12 additions & 21 deletions client/src/viewmodels/TodoListViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export default class TodoListViewModel extends PromiseAwareViewModelBase {
this.didRequestFail = true;
this.failReason = response.failReason;
} else {
this.todoItems = (response.data as Array<TodoItem>).map(item => new TodoItemViewModel(this.service, item));
this.todoItems = (response.data!)
.map(item => new TodoItemViewModel(this.service, item, () => this.remove(item.id)));
}
});
}
Expand All @@ -50,31 +51,21 @@ export default class TodoListViewModel extends PromiseAwareViewModelBase {
this.didRequestFail = true;
this.failReason = response.failReason;
} else {
this.todoItems.push(
new TodoItemViewModel(
this.service,
new TodoItem(
response.data as number,
content,
false
)
)
);
this.insert(new TodoItemViewModel(
this.service,
new TodoItem(response.data!, content, false),
() => this.remove(response.data!)
));
}
});
}

public async deleteTodoItem(id: number) {
await this.runWithAwareness(async () => {
var response = await this.service.deleteTodo(id);
public insert(item: TodoItemViewModel) {
this.todoItems.push(item);
}

if (response.didFail) {
this.didRequestFail = true;
this.failReason = response.failReason;
} else {
this.todoItems = this.todoItems.filter(item => item.id !== id);
}
});
public remove(id: number) {
this.todoItems = this.todoItems.filter(item => item.id !== id);
}

//#endregion
Expand Down