|
|
@ -19,12 +19,6 @@ type Todo struct {
|
|
|
|
Completed bool
|
|
|
|
Completed bool
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type ViewModel struct {
|
|
|
|
|
|
|
|
Todos []Todo
|
|
|
|
|
|
|
|
Only string
|
|
|
|
|
|
|
|
Incomplete int
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func filterSlice[T any](xs []T, pred func(T) bool) []T {
|
|
|
|
func filterSlice[T any](xs []T, pred func(T) bool) []T {
|
|
|
|
result := make([]T, 0, len(xs))
|
|
|
|
result := make([]T, 0, len(xs))
|
|
|
|
for _, element := range xs {
|
|
|
|
for _, element := range xs {
|
|
|
@ -48,113 +42,145 @@ func countSlice[T any](xs []T, pred func(T) bool) int {
|
|
|
|
return count
|
|
|
|
return count
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
type server struct {
|
|
|
|
todos := make([]Todo, 0, 10)
|
|
|
|
views *views.Views
|
|
|
|
|
|
|
|
router *http.ServeMux
|
|
|
|
|
|
|
|
todos []Todo
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func newServer() *server {
|
|
|
|
|
|
|
|
srv := &server{
|
|
|
|
|
|
|
|
views: views.New(),
|
|
|
|
|
|
|
|
router: http.NewServeMux(),
|
|
|
|
|
|
|
|
todos: make([]Todo, 0, 10),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
srv.routes()
|
|
|
|
|
|
|
|
|
|
|
|
t := views.New()
|
|
|
|
return srv
|
|
|
|
list := t.Renderer("list.html")
|
|
|
|
}
|
|
|
|
http.Handle("/public/", http.FileServer(http.FS(publicFS)))
|
|
|
|
|
|
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
s.router.ServeHTTP(w, r)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (s *server) AddTodo(t Todo) {
|
|
|
|
|
|
|
|
s.todos = append(s.todos, t)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (s *server) HandleList() http.HandlerFunc {
|
|
|
|
|
|
|
|
type viewModel struct {
|
|
|
|
|
|
|
|
Todos []Todo
|
|
|
|
|
|
|
|
Only string
|
|
|
|
|
|
|
|
Incomplete int
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
list := s.views.Renderer("list.html")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
q := r.URL.Query()
|
|
|
|
q := r.URL.Query()
|
|
|
|
renderTodos := todos
|
|
|
|
renderTodos := s.todos
|
|
|
|
only := q.Get("only")
|
|
|
|
only := q.Get("only")
|
|
|
|
|
|
|
|
|
|
|
|
if only == "active" {
|
|
|
|
if only == "active" {
|
|
|
|
renderTodos = filterSlice(todos, func(todo Todo) bool { return !todo.Completed })
|
|
|
|
renderTodos = filterSlice(s.todos, func(todo Todo) bool { return !todo.Completed })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if only == "completed" {
|
|
|
|
if only == "completed" {
|
|
|
|
renderTodos = filterSlice(todos, func(todo Todo) bool { return todo.Completed })
|
|
|
|
renderTodos = filterSlice(s.todos, func(todo Todo) bool { return todo.Completed })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
incomplete := countSlice(todos, func(todo Todo) bool { return !todo.Completed })
|
|
|
|
incomplete := countSlice(s.todos, func(todo Todo) bool { return !todo.Completed })
|
|
|
|
|
|
|
|
|
|
|
|
vm := ViewModel{
|
|
|
|
vm := viewModel{
|
|
|
|
Todos: renderTodos,
|
|
|
|
Todos: renderTodos,
|
|
|
|
Only: only,
|
|
|
|
Only: only,
|
|
|
|
Incomplete: incomplete,
|
|
|
|
Incomplete: incomplete,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
list(w, r, vm)
|
|
|
|
list(w, r, vm)
|
|
|
|
})
|
|
|
|
|
|
|
|
http.HandleFunc("/todo", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
if r.Method != "POST" {
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (s *server) HandleCreate() http.HandlerFunc {
|
|
|
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
title := r.Form.Get("title")
|
|
|
|
title := r.Form.Get("title")
|
|
|
|
todos = append(todos, Todo{
|
|
|
|
s.AddTodo(Todo{
|
|
|
|
Id: strconv.Itoa(rand.Int()),
|
|
|
|
Id: strconv.Itoa(rand.Int()),
|
|
|
|
Title: title,
|
|
|
|
Title: title,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Location", "/")
|
|
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
|
|
w.WriteHeader(http.StatusSeeOther)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
http.HandleFunc("/toggle", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
if r.Method != "POST" {
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (s *server) HandleToggle() http.HandlerFunc {
|
|
|
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
id := r.Form.Get("id")
|
|
|
|
id := r.Form.Get("id")
|
|
|
|
for i, todo := range todos {
|
|
|
|
for i, todo := range s.todos {
|
|
|
|
if todo.Id == id {
|
|
|
|
if todo.Id == id {
|
|
|
|
todo.Completed = !todo.Completed
|
|
|
|
todo.Completed = !todo.Completed
|
|
|
|
todos[i] = todo
|
|
|
|
s.todos[i] = todo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Location", "/")
|
|
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
|
|
w.WriteHeader(http.StatusSeeOther)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
http.HandleFunc("/destroy", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
func (s *server) HandleDestroy() http.HandlerFunc {
|
|
|
|
if r.Method != "POST" {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
|
id := r.Form.Get("id")
|
|
|
|
return
|
|
|
|
s.todos = filterSlice(s.todos, func(t Todo) bool { return t.Id != id })
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
|
|
func (s *server) HandleClear() http.HandlerFunc {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
return
|
|
|
|
s.todos = filterSlice(s.todos, func(t Todo) bool { return !t.Completed })
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
id := r.Form.Get("id")
|
|
|
|
func (s *server) routes() {
|
|
|
|
todos = filterSlice(todos, func(t Todo) bool { return t.Id != id })
|
|
|
|
s.router.Handle("/public/", http.FileServer(http.FS(publicFS)))
|
|
|
|
|
|
|
|
s.router.HandleFunc("/", s.HandleList())
|
|
|
|
|
|
|
|
s.router.HandleFunc("/todo", parseForm(ensureMethod("POST", s.HandleCreate())))
|
|
|
|
|
|
|
|
s.router.HandleFunc("/toggle", parseForm(ensureMethod("POST", s.HandleToggle())))
|
|
|
|
|
|
|
|
s.router.HandleFunc("/destroy", parseForm(ensureMethod("POST", s.HandleDestroy())))
|
|
|
|
|
|
|
|
s.router.HandleFunc("/clear", ensureMethod("POST", s.HandleClear()))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Location", "/")
|
|
|
|
func run() error {
|
|
|
|
w.WriteHeader(http.StatusSeeOther)
|
|
|
|
srv := newServer()
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
log.Print("Running on 8080")
|
|
|
|
|
|
|
|
return http.ListenAndServe(":8080", srv)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
|
|
|
log.Fatal(run())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
http.HandleFunc("/clear", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
func ensureMethod(method string, next http.Handler) http.HandlerFunc {
|
|
|
|
if r.Method != "POST" {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
if r.Method != method {
|
|
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func parseForm(next http.Handler) http.HandlerFunc {
|
|
|
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
|
|
|
w.Write([]byte("Bad Request"))
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
todos = filterSlice(todos, func(t Todo) bool { return !t.Completed })
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
|
|
|
|
}
|
|
|
|
w.Header().Set("Location", "/")
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusSeeOther)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.Print("Running on 8080")
|
|
|
|
|
|
|
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|