From 5d4d9935cffa9a7cb8fe2a294e45959b43390ef2 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 14 Nov 2016 18:46:52 +0100 Subject: likes funktionieren diff --git a/assets_src/css/list.css b/assets_src/css/list.css index 51b2514..a3e6fcf 100644 --- a/assets_src/css/list.css +++ b/assets_src/css/list.css @@ -2,6 +2,10 @@ color: rgb(166, 166, 166); } +.liked { + background-color: green; +} + .list-entry { height: 80px; padding: 8px; diff --git a/assets_src/js/lib/ajax.js b/assets_src/js/lib/ajax.js index b189a92..71e3764 100644 --- a/assets_src/js/lib/ajax.js +++ b/assets_src/js/lib/ajax.js @@ -61,3 +61,6 @@ export async function post(url, body, options) { export async function del(url, body, options) { return request('DELETE', url, body, options); } +export async function put(url, body, options) { + return request('PUT', url, body, options); +} diff --git a/assets_src/js/lib/dom.js b/assets_src/js/lib/dom.js index 3c2f4d9..82f5dd2 100644 --- a/assets_src/js/lib/dom.js +++ b/assets_src/js/lib/dom.js @@ -27,6 +27,22 @@ export function withClass(className) { return arr; } +export function firstChild(el, fn) { + if (!el) { + return null; + } + + for (const child of el.childNodes) { + if (child.nodeName === '#text') { + continue; + } + if (fn(child)) { + return child; + } + } + return null; +} + export function next(el, fn) { if (!el) { return null; diff --git a/assets_src/js/like.js b/assets_src/js/like.js index 03b15a1..feae345 100644 --- a/assets_src/js/like.js +++ b/assets_src/js/like.js @@ -11,12 +11,81 @@ async function updateLikeCount(el) { const count = await ajax.get(`/api/likes/count?id=${contentId}&type=${type}`, {}); - el.textContent = count; + dom.firstChild(el, e => e.classList.contains('like-count')).textContent = count; +} + +async function isLikedByCurrentUser(el) { + const type = parseInt(el.getAttribute('content-type'), 10); + const contentId = parseInt(el.getAttribute('content-id'), 10); + const userId = parseInt(el.getAttribute('update-with'), 10); + + if (isNaN(type) || isNaN(contentId) || isNaN(userId)) { + return; + } + + const b = await ajax.get(`/api/likes/liked_by?id=${contentId}&type=${type}&user=${userId}`, {}); + + console.log(b); + return b === 'true'; +} + +async function updateLikeByCurrentUser(el, to) { + const type = parseInt(el.getAttribute('content-type'), 10); + const contentId = parseInt(el.getAttribute('content-id'), 10); + const userId = parseInt(el.getAttribute('update-with'), 10); + + if (isNaN(type) || isNaN(contentId) || isNaN(userId)) { + return; + } + + updateLikeClass(el, to); + + if (to) { + await ajax.put('/api/likes', `id=${contentId}&type=${type}&user=${userId}`, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + } + }); + } else { + await ajax.del('/api/likes', `id=${contentId}&type=${type}&user=${userId}`, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + } + }); + } + const cont = dom.firstChild(el, e => e.classList.contains('like-count')); + + cont.textContent = parseInt(cont.textContent, 10) + (to ? 1 : -1); +} + +function updateLikeClass(el, to) { + el.classList.toggle('liked', to); } dom.ready(() => { - dom.withClass('like-count') - .forEach(el => { + dom.withClass('like-div') + .forEach(async el => { updateLikeCount(el); + + const userId = parseInt(el.getAttribute('update-with'), 10); + + let liked = await isLikedByCurrentUser(el); + updateLikeClass(el, liked); + if (!isNaN(userId)) { + el.addEventListener('click', async () => { + liked = !liked; + await updateLikeByCurrentUser(el, liked); + }); + const cap = dom.firstChild(el, e => e.classList.contains('like-caption')); + if (cap) { + el.addEventListener('mouseover', () => { + cap.textContent = `${liked ? 'nicht mehr ' : ''}geil finden`; + }); + el.addEventListener('mouseout', () => cap.textContent = 'Finden das geil'); + } + } else { + el.classList.add('disabled'); + } + }); }); diff --git a/modules/likes/likes.go b/modules/likes/likes.go index 949cba2..4d69d7b 100644 --- a/modules/likes/likes.go +++ b/modules/likes/likes.go @@ -2,11 +2,14 @@ package likes import ( "fmt" + "io/ioutil" "log" "net/http" + "net/url" "strconv" "fagott.pw/charakterin" + "fagott.pw/grilist/cache" "fagott.pw/grilist/grilist" "fagott.pw/grilist/modules/lists" @@ -15,6 +18,7 @@ import ( type Module struct { g *grilist.Grilist + c *cache.Cache lists *lists.Module } @@ -33,7 +37,11 @@ func (m *Module) Init(g *grilist.Grilist) { log.Fatal("tags: lists module not found") } m.lists = gm.(*lists.Module) + m.c = cache.New() m.g.Router.GET("/api/likes/count", m.getLikeCount) + m.g.Router.GET("/api/likes/liked_by", m.isLikedBy) + m.g.Router.PUT("/api/likes", m.addLike) + m.g.Router.DELETE("/api/likes", m.removeLike) } func (m *Module) Interface() interface{} { @@ -68,3 +76,115 @@ func (m *Module) getLikeCount(w http.ResponseWriter, r *http.Request, p httprout w.WriteHeader(200) w.Write([]byte(fmt.Sprintf("%d", res))) } +func (m *Module) isLikedBy(w http.ResponseWriter, r *http.Request, p httprouter.Params) { + params := r.URL.Query() + + contentId, err := strconv.Atoi(params.Get("id")) + if err != nil { + http.Error(w, "invalid content id", http.StatusBadRequest) + return + } + contentType, err := strconv.Atoi(params.Get("type")) + if err != nil { + http.Error(w, "invalid content type", http.StatusBadRequest) + return + } + userId, err := strconv.Atoi(params.Get("user")) + if err != nil { + http.Error(w, "invalid user id", http.StatusBadRequest) + return + } + + var res int + if err := m.g.DB.QueryRow(`SELECT COUNT(*) FROM grilist.likes WHERE content = $1 AND type = $2 AND "user" = $3`, contentId, contentType, userId).Scan(&res); err != nil { + http.Error(w, "pq error", http.StatusInternalServerError) + log.Printf("error getting like by user: %s", err) + return + } + + w.WriteHeader(200) + w.Write([]byte(fmt.Sprintf("%t", res > 0))) +} +func (m *Module) addLike(w http.ResponseWriter, r *http.Request, p httprouter.Params) { + params, err := readBody(r) + if err != nil { + log.Println(err) + http.Error(w, "invalid body", http.StatusBadRequest) + return + } + + contentId, err := strconv.Atoi(params.Get("id")) + if err != nil { + log.Println(err) + http.Error(w, "invalid content id", http.StatusBadRequest) + return + } + contentType, err := strconv.Atoi(params.Get("type")) + if err != nil { + log.Println(err) + http.Error(w, "invalid content type", http.StatusBadRequest) + return + } + userId, err := strconv.Atoi(params.Get("user")) + if err != nil { + http.Error(w, "invalid user id", http.StatusBadRequest) + return + } + + _, err = m.g.DB.Exec(`INSERT INTO grilist.likes(content, "user", type) SELECT $1, $2, $3 WHERE NOT EXISTS (SELECT * FROM grilist.likes WHERE content = $1 AND "user" = $2 AND type = $3)`, contentId, userId, contentType) + if err != nil { + http.Error(w, "pq error", http.StatusInternalServerError) + log.Printf("error add like: %s", err) + return + } + + w.WriteHeader(200) +} + +func (m *Module) removeLike(w http.ResponseWriter, r *http.Request, p httprouter.Params) { + params, err := readBody(r) + if err != nil { + http.Error(w, "invalid body", http.StatusBadRequest) + return + } + + contentId, err := strconv.Atoi(params.Get("id")) + if err != nil { + http.Error(w, "invalid content id", http.StatusBadRequest) + return + } + contentType, err := strconv.Atoi(params.Get("type")) + if err != nil { + http.Error(w, "invalid content type", http.StatusBadRequest) + return + } + userId, err := strconv.Atoi(params.Get("user")) + if err != nil { + http.Error(w, "invalid user id", http.StatusBadRequest) + return + } + + _, err = m.g.DB.Exec(`DELETE FROM grilist.likes WHERE content = $1 AND "user" = $2 AND type = $3`, contentId, userId, contentType) + if err != nil { + http.Error(w, "pq error", http.StatusInternalServerError) + log.Printf("error add like: %s", err) + return + } + + w.WriteHeader(200) +} + +func readBody(r *http.Request) (url.Values, error) { + defer r.Body.Close() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + + values, err := url.ParseQuery(string(data)) + if err != nil { + return nil, err + } + + return values, nil +} diff --git a/views/includes/like.html b/views/includes/like.html index 226aaff..bfb9576 100644 --- a/views/includes/like.html +++ b/views/includes/like.html @@ -1,3 +1,5 @@ {{ define "like" }} - Likes +
+ +
{{ end }} diff --git a/views/pages/list.html b/views/pages/list.html index f884a63..b1ec236 100644 --- a/views/pages/list.html +++ b/views/pages/list.html @@ -25,7 +25,7 @@ {{ end }}
{{ $list.Description }}

- {{ template "like" (map "ContentType" 0 "ContentID" $list.ID) }} + {{ template "like" (map "ContentType" 0 "ContentID" $list.ID "User" $user) }}
    -- cgit v0.10.1