Pārlūkot izejas kodu

Introduce List page with add, view, update

Gildas Chabot 4 gadi atpakaļ
vecāks
revīzija
4b01054a11
5 mainītis faili ar 196 papildinājumiem un 8 dzēšanām
  1. 63 2
      collection.go
  2. 19 0
      list.go
  3. 67 6
      pages/pages.go
  4. 24 0
      templates/home.html
  5. 23 0
      templates/list.html

+ 63 - 2
collection.go

@@ -2,16 +2,20 @@ package movies
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
+	"fmt"
5 6
 	"io/ioutil"
6 7
 	"sync"
7 8
 )
8 9
 
9 10
 type Collection struct {
10
-	Movies []*Movie
11
+	Movies   []*Movie
12
+	movieMap map[string]*Movie
13
+
14
+	Lists   []*List
15
+	listMap map[string]*List
11 16
 
12 17
 	mutex      sync.RWMutex
13 18
 	hasChanged bool
14
-	movieMap   map[string]*Movie
15 19
 }
16 20
 
17 21
 func Import(filename string) (*Collection, error) {
@@ -31,6 +35,11 @@ func Import(filename string) (*Collection, error) {
31 35
 		c.movieMap[m.IMDBID] = m
32 36
 	}
33 37
 
38
+	c.listMap = make(map[string]*List)
39
+	for _, l := range c.Lists {
40
+		c.listMap[l.ID] = l
41
+	}
42
+
34 43
 	return &c, nil
35 44
 }
36 45
 
@@ -102,3 +111,55 @@ func (c *Collection) Update(m *Movie) {
102 111
 
103 112
 	c.hasChanged = true
104 113
 }
114
+
115
+func (c *Collection) GetList(listID string) (*List, bool) {
116
+	c.mutex.RLock()
117
+	defer c.mutex.RUnlock()
118
+
119
+	l, ok := c.listMap[listID]
120
+	return l, ok
121
+}
122
+
123
+func (c *Collection) AddList(id, title string) error {
124
+	c.mutex.Lock()
125
+	defer c.mutex.Unlock()
126
+
127
+	c.hasChanged = true
128
+
129
+	for _, present := range c.Lists {
130
+		if id == present.ID {
131
+			return fmt.Errorf("there is already a list with id %q", id)
132
+		}
133
+	}
134
+
135
+	l := &List{
136
+		ID:    id,
137
+		Title: title,
138
+	}
139
+
140
+	c.Lists = append(c.Lists, l)
141
+	c.listMap[l.ID] = l
142
+
143
+	return nil
144
+}
145
+
146
+func (c *Collection) UpdateList(l *List) {
147
+	c.mutex.Lock()
148
+	defer c.mutex.Unlock()
149
+
150
+	c.listMap[l.ID] = l
151
+
152
+	found := false
153
+	for i := range c.Lists {
154
+		if c.Lists[i].ID == l.ID {
155
+			c.Lists[i] = l
156
+			found = true
157
+			break
158
+		}
159
+	}
160
+	if !found {
161
+		c.Lists = append(c.Lists, l)
162
+	}
163
+
164
+	c.hasChanged = true
165
+}

+ 19 - 0
list.go

@@ -0,0 +1,19 @@
1
+package movies
2
+
3
+import "encoding/json"
4
+
5
+type List struct {
6
+	ID          string
7
+	Title       string
8
+	Description string
9
+	Movies      []string // IMDBIDs
10
+}
11
+
12
+func UnmarshalList(b []byte) (*List, error) {
13
+	var l List
14
+	if err := json.Unmarshal(b, &l); err != nil {
15
+		return nil, err
16
+	}
17
+
18
+	return &l, nil
19
+}

+ 67 - 6
pages/pages.go

@@ -14,6 +14,7 @@ import (
14 14
 func Router(c *movies.Collection) http.HandlerFunc {
15 15
 	homeHandler := Home(c)
16 16
 	movieHandler := Movie(c)
17
+	listHandler := List(c)
17 18
 
18 19
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
19 20
 		path := r.URL.Path
@@ -24,6 +25,9 @@ func Router(c *movies.Collection) http.HandlerFunc {
24 25
 		case path == "/style.css":
25 26
 			http.ServeFile(w, r, "templates/style.css")
26 27
 			return
28
+		case strings.HasPrefix(path, "/l/"):
29
+			listHandler(w, r)
30
+			return
27 31
 		case strings.HasPrefix(path, "/tt"):
28 32
 			movieHandler(w, r)
29 33
 			return
@@ -42,12 +46,20 @@ func Home(c *movies.Collection) http.HandlerFunc {
42 46
 		var errs []error
43 47
 
44 48
 		if r.Method == "POST" {
45
-			m, err := movies.Unmarshal([]byte(r.FormValue("omdb_json")))
46
-			if err != nil {
47
-				w.WriteHeader(http.StatusBadRequest)
48
-				errs = append(errs, err)
49
-			} else {
50
-				c.Add(m)
49
+			if r.FormValue("omdb_json") != "" {
50
+				m, err := movies.Unmarshal([]byte(r.FormValue("omdb_json")))
51
+				if err != nil {
52
+					w.WriteHeader(http.StatusBadRequest)
53
+					errs = append(errs, err)
54
+				} else {
55
+					c.Add(m)
56
+				}
57
+			}
58
+
59
+			if r.FormValue("list-id") != "" {
60
+				if err := c.AddList(r.FormValue("list-id"), r.FormValue("list-title")); err != nil {
61
+					errs = append(errs, err)
62
+				}
51 63
 			}
52 64
 		}
53 65
 
@@ -118,3 +130,52 @@ func Movie(c *movies.Collection) http.HandlerFunc {
118 130
 		})
119 131
 	})
120 132
 }
133
+
134
+func List(c *movies.Collection) http.HandlerFunc {
135
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
136
+		t, err := template.ParseFiles("templates/list.html")
137
+		if err != nil {
138
+			fmt.Println(err)
139
+			return
140
+		}
141
+
142
+		var errs []error
143
+
144
+		splitted := strings.Split(r.URL.Path, "/")
145
+		if len(splitted) < 3 {
146
+			w.WriteHeader(http.StatusNotFound)
147
+			return
148
+		}
149
+
150
+		listID := splitted[2]
151
+
152
+		l, ok := c.GetList(listID)
153
+		if !ok {
154
+			w.WriteHeader(http.StatusNotFound)
155
+			return
156
+		}
157
+
158
+		if r.Method == "POST" {
159
+			updated, err := movies.UnmarshalList([]byte(r.FormValue("list_json")))
160
+			if err != nil {
161
+				errs = append(errs, err)
162
+			} else if listID != updated.ID {
163
+				errs = append(errs, fmt.Errorf("you cannot change the imdb id."))
164
+			} else {
165
+				l = updated
166
+				c.UpdateList(updated)
167
+			}
168
+		}
169
+
170
+		b, err := json.MarshalIndent(l, "", "  ")
171
+		if err != nil {
172
+			errs = append(errs, err)
173
+		}
174
+
175
+		t.Execute(w, map[string]interface{}{
176
+			"List":     l,
177
+			"ListJSON": string(b),
178
+			"Errors":   errs,
179
+		})
180
+	})
181
+}

+ 24 - 0
templates/home.html

@@ -33,6 +33,19 @@
33 33
             {{ end }}
34 34
         </ul>
35 35
 
36
+        <h2>Lists</h2>
37
+
38
+        <ul class="list-list">
39
+            {{ range $l := .Collection.Lists }}
40
+            <a href="/l/{{ $l.ID }}">
41
+                <li class="list">
42
+                    <h3>{{ $l.Title }} ({{ $l.ID }})</h3>
43
+                    <p>{{ $l.Description }}</p>
44
+                </li>
45
+            </a>
46
+            {{ end }}
47
+        </ul>
48
+
36 49
         <h2>Add a movie</h2>
37 50
         <div>
38 51
             <form method="get">
@@ -48,5 +61,16 @@
48 61
                 </p>
49 62
             </form>
50 63
         </div>
64
+
65
+        <h2>Add a list</h2>
66
+        <div>
67
+            <form method="post" action="/">
68
+                <div>
69
+                    ID: <input type="text" name="list-id" />
70
+                    Title: <input type="text" name="list-title" />
71
+                    <input type="submit" />
72
+                </div>
73
+            </form>
74
+        </div>
51 75
     </body>
52 76
 </html>

+ 23 - 0
templates/list.html

@@ -0,0 +1,23 @@
1
+<html>
2
+    <head>
3
+        <title>gildas.ch</title>
4
+
5
+        <link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
6
+        <link rel="stylesheet" href="/style.css">
7
+    </head>
8
+    <body>
9
+        {{ range $e := .Errors }}
10
+        <div class="error">{{ $e }}</div>
11
+        {{ end }}
12
+
13
+        <h1>{{ .List.Title }}</h1>
14
+
15
+        <p>{{ .List.Description }}</p>
16
+
17
+        <form method="post">
18
+            <p>Update list: <input type="submit" /><br />
19
+                <textarea name="list_json" style="width:90%;height:500px;">{{ .ListJSON }}</textarea>
20
+            </p>
21
+        </form>
22
+    </body>
23
+</html>