Browse Source

Add Movie page

Gildas Chabot 4 years ago
parent
commit
c2931b4cd2
6 changed files with 187 additions and 49 deletions
  1. 1 1
      cmd/movies/main.go
  2. 34 0
      collection.go
  3. 71 0
      pages/pages.go
  4. 13 48
      templates/list.html
  5. 27 0
      templates/movie.html
  6. 41 0
      templates/style.css

+ 1 - 1
cmd/movies/main.go

@@ -28,7 +28,7 @@ func main() {
28 28
 		collection = &movies.Collection{}
29 29
 	}
30 30
 
31
-	http.Handle("/", pages.List(collection))
31
+	http.Handle("/", pages.Router(collection))
32 32
 	http.Handle("/add-movie", api.AddMovieHandler(collection))
33 33
 
34 34
 	port := "8080"

+ 34 - 0
collection.go

@@ -11,6 +11,7 @@ type Collection struct {
11 11
 
12 12
 	mutex      sync.RWMutex
13 13
 	hasChanged bool
14
+	movieMap   map[string]*Movie
14 15
 }
15 16
 
16 17
 func Import(filename string) (*Collection, error) {
@@ -24,8 +25,10 @@ func Import(filename string) (*Collection, error) {
24 25
 		return nil, err
25 26
 	}
26 27
 
28
+	c.movieMap = make(map[string]*Movie)
27 29
 	for _, m := range c.Movies {
28 30
 		m.FillFromOMDB()
31
+		c.movieMap[m.IMDBID] = m
29 32
 	}
30 33
 
31 34
 	return &c, nil
@@ -60,9 +63,40 @@ func (c *Collection) Add(m *Movie) {
60 63
 	}
61 64
 
62 65
 	c.Movies = append(c.Movies, m)
66
+	c.movieMap[m.IMDBID] = m
67
+
63 68
 	c.hasChanged = true
64 69
 }
65 70
 
66 71
 func (c *Collection) HasChanged() bool {
67 72
 	return c.hasChanged
68 73
 }
74
+
75
+func (c *Collection) Get(imdbID string) (*Movie, bool) {
76
+	c.mutex.RLock()
77
+	defer c.mutex.RUnlock()
78
+
79
+	m, ok := c.movieMap[imdbID]
80
+	return m, ok
81
+}
82
+
83
+func (c *Collection) Update(m *Movie) {
84
+	c.mutex.Lock()
85
+	defer c.mutex.Unlock()
86
+
87
+	c.movieMap[m.IMDBID] = m
88
+
89
+	found := false
90
+	for i := range c.Movies {
91
+		if c.Movies[i].IMDBID == m.IMDBID {
92
+			c.Movies[i] = m
93
+			found = true
94
+			break
95
+		}
96
+	}
97
+	if !found {
98
+		c.Movies = append(c.Movies, m)
99
+	}
100
+
101
+	c.hasChanged = true
102
+}

+ 71 - 0
pages/pages.go

@@ -1,14 +1,36 @@
1 1
 package pages
2 2
 
3 3
 import (
4
+	"encoding/json"
4 5
 	"fmt"
5 6
 	"html/template"
6 7
 	"net/http"
8
+	"strings"
7 9
 
8 10
 	"gogs.gildas.ch/gildas/movies"
9 11
 	"gogs.gildas.ch/gildas/movies/omdb"
10 12
 )
11 13
 
14
+func Router(c *movies.Collection) http.HandlerFunc {
15
+	listHandler := List(c)
16
+	movieHandler := Movie(c)
17
+
18
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
19
+		path := r.URL.Path
20
+		switch {
21
+		case path == "/":
22
+			listHandler(w, r)
23
+			return
24
+		case path == "/style.css":
25
+			http.ServeFile(w, r, "templates/style.css")
26
+			return
27
+		case strings.HasPrefix(path, "/tt"):
28
+			movieHandler(w, r)
29
+			return
30
+		}
31
+	})
32
+}
33
+
12 34
 func List(c *movies.Collection) http.HandlerFunc {
13 35
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
14 36
 		t, err := template.ParseFiles("templates/list.html")
@@ -47,3 +69,52 @@ func List(c *movies.Collection) http.HandlerFunc {
47 69
 		})
48 70
 	})
49 71
 }
72
+
73
+func Movie(c *movies.Collection) http.HandlerFunc {
74
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
75
+		t, err := template.ParseFiles("templates/movie.html")
76
+		if err != nil {
77
+			fmt.Println(err)
78
+			return
79
+		}
80
+
81
+		var errs []error
82
+
83
+		splitted := strings.Split(r.URL.Path, "/")
84
+		if len(splitted) < 2 {
85
+			w.WriteHeader(http.StatusNotFound)
86
+			return
87
+		}
88
+
89
+		imdbID := splitted[1]
90
+
91
+		m, ok := c.Get(imdbID)
92
+		if !ok {
93
+			w.WriteHeader(http.StatusNotFound)
94
+			return
95
+		}
96
+
97
+		if r.Method == "POST" {
98
+			updated, err := movies.Unmarshal([]byte(r.FormValue("movie_json")))
99
+			if err != nil {
100
+				errs = append(errs, err)
101
+			} else if imdbID != updated.IMDBID {
102
+				errs = append(errs, fmt.Errorf("you cannot change the imdb id."))
103
+			} else {
104
+				m = updated
105
+				c.Update(updated)
106
+			}
107
+		}
108
+
109
+		b, err := json.MarshalIndent(m, "", "  ")
110
+		if err != nil {
111
+			errs = append(errs, err)
112
+		}
113
+
114
+		t.Execute(w, map[string]interface{}{
115
+			"Movie":     m,
116
+			"MovieJSON": string(b),
117
+			"Errors":    errs,
118
+		})
119
+	})
120
+}

+ 13 - 48
templates/list.html

@@ -3,44 +3,7 @@
3 3
         <title>gildas.ch</title>
4 4
 
5 5
         <link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
6
-        <style>
7
-         body {
8
-             font-family: 'Roboto', sans-serif;
9
-         }
10
-         h2 {
11
-             font-size: 1.2em;
12
-             color: #757575;
13
-         }
14
-         ul {
15
-             list-style-type: none;
16
-             display: flex;
17
-             flex-wrap: wrap;
18
-             padding: 0;
19
-         }
20
-         li {
21
-             width: 240px;
22
-             height: 400px;
23
-             display: grid;
24
-             grid-template-rows: 320px 80px;
25
-             box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
26
-             margin: 10px;
27
-         }
28
-         li .poster {
29
-             justify-self: center;
30
-             align-self: center;
31
-         }
32
-         li .metadata {
33
-             padding: 10px;
34
-         }
35
-         li img {
36
-             max-width: 240px;
37
-             max-height: 320px;
38
-         }
39
-         h3 {
40
-             margin-bottom: 5px;
41
-             margin-top: 5px;
42
-         }
43
-        </style>
6
+        <link rel="stylesheet" href="/style.css">
44 7
     </head>
45 8
     <body>
46 9
         {{ range $e := .Errors }}
@@ -49,17 +12,19 @@
49 12
 
50 13
         <h2>Movies</h2>
51 14
 
52
-        <ul>
15
+        <ul class="movie-list">
53 16
             {{ range $m := .Collection.Movies }}
54
-            <li>
55
-                <div class="poster">
56
-                    <img src="{{ $m.Poster }}" title="{{ $m.Title }}" alt="{{ $m.Title }}" />
57
-                </div>
58
-                <div class="metadata">
59
-                    <h3>{{ $m.Title }}</h3>
60
-                    <div>{{ $m.Director }}</div>
61
-                </div>
62
-            </li>
17
+            <a href="/{{ $m.IMDBID }}">
18
+                <li>
19
+                    <div class="poster">
20
+                        <img src="{{ $m.Poster }}" title="{{ $m.Title }}" alt="{{ $m.Title }}" />
21
+                    </div>
22
+                    <div class="metadata">
23
+                        <h3>{{ $m.Title }}</h3>
24
+                        <div>{{ $m.Director }}</div>
25
+                    </div>
26
+                </li>
27
+            </a>
63 28
             {{ end }}
64 29
         </ul>
65 30
 

+ 27 - 0
templates/movie.html

@@ -0,0 +1,27 @@
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
+        <div class="poster">
14
+            <img src="{{ .Movie.Poster }}" title="{{ .Movie.Title }}" alt="{{ .Movie.Title }}" />
15
+        </div>
16
+        <div class="metadata">
17
+            <h3>{{ .Movie.Title }}</h3>
18
+            <div>{{ .Movie.Director }}</div>
19
+        </div>
20
+
21
+        <form method="post">
22
+            <p>Update movie: <input type="submit" /><br />
23
+                <textarea name="movie_json" style="width:90%;height:500px;">{{ .MovieJSON }}</textarea>
24
+            </p>
25
+        </form>
26
+    </body>
27
+</html>

+ 41 - 0
templates/style.css

@@ -0,0 +1,41 @@
1
+body {
2
+    font-family: 'Roboto', sans-serif;
3
+}
4
+h2 {
5
+    font-size: 1.2em;
6
+    color: #757575;
7
+}
8
+h3 {
9
+    margin-bottom: 5px;
10
+    margin-top: 5px;
11
+}
12
+
13
+ul.movie-list {
14
+    list-style-type: none;
15
+    display: flex;
16
+    flex-wrap: wrap;
17
+    padding: 0;
18
+}
19
+ul.movie-list li {
20
+    width: 240px;
21
+    height: 400px;
22
+    display: grid;
23
+    grid-template-rows: 320px 80px;
24
+    box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
25
+    margin: 10px;
26
+}
27
+ul.movie-list li .poster {
28
+    justify-self: center;
29
+    align-self: center;
30
+}
31
+ul.movie-list li .metadata {
32
+    padding: 10px;
33
+}
34
+ul.movie-list li img {
35
+    max-width: 240px;
36
+    max-height: 320px;
37
+}
38
+ul.movie-list li a {
39
+    color: black;
40
+    text-decoration: none;
41
+}