From 74add41ccddadcee4e29201b3c4566be5efceb3c Mon Sep 17 00:00:00 2001 From: Jeremy Dormitzer Date: Fri, 12 Jan 2018 13:29:12 -0500 Subject: [PATCH] First pass at sidebar / rendering logic --- ext/manifest.json | 15 +++++-- ext/sidebar.html | 12 ++++++ project.clj | 8 ++-- src/looped_in/background.cljs | 72 ++++++++++++++-------------------- src/looped_in/hackernews.cljs | 39 +++++++++++++++++++ src/looped_in/popup.cljs | 4 -- src/looped_in/sidebar.cljs | 73 +++++++++++++++++++++++++++++++++++ 7 files changed, 168 insertions(+), 55 deletions(-) create mode 100644 ext/sidebar.html create mode 100644 src/looped_in/hackernews.cljs delete mode 100644 src/looped_in/popup.cljs create mode 100644 src/looped_in/sidebar.cljs diff --git a/ext/manifest.json b/ext/manifest.json index b89cf1f..2b5e443 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -12,7 +12,8 @@ "background": { "scripts": [ "js/browser-polyfill.min.js", - "js/generated/out/cljs_base.js", + "js/generated/out/goog/base.js", + "js/generated/out/cljs_deps.js", "js/generated/background.js" ] }, @@ -20,7 +21,15 @@ "default_icon": "icons/icon48.png", "default_title": "Looped In" }, + "sidebar_action": { + "default_icon": { + "16": "icons/icon16.png", + "48": "icons/icon48.png" + }, + "default_title": "Looped In", + "default_panel": "sidebar.html" + }, "permissions": ["tabs", "https://hn.algolia.com/*"], - "content_security_policy": "script-src 'self'; object-src 'self'; connect-src 'self' https://hn.algolia.com", - "web_accessible_resources": ["js/*.map", "js/generated/*.map"] + "content_security_policy": "script-src 'self' 'unsafe-eval' 'sha256-CfMPE8ys/ylJ5D9qKG0a9/UejrcczMr4/EmFCbVbgcc=' 'sha256-XsBu2nEJnS7x/Izq1v7dzy3Ze5myJMHvg4zukh/R1Yk=' 'sha256-AUCho1UyOpYFSZDg8EM9SYlysRIrUpQKZ7iE9CFSYfU=' 'sha256-vLlTaRFN8A2FPadIx711FwK9Ytd6LXkAzuEYAdC0D1k='; object-src 'self'; connect-src 'self' https://hn.algolia.com", + "web_accessible_resources": ["js/*.map", "js/generated/*.map", "js/generated/out/*"] } diff --git a/ext/sidebar.html b/ext/sidebar.html new file mode 100644 index 0000000..9df9eab --- /dev/null +++ b/ext/sidebar.html @@ -0,0 +1,12 @@ + + + + + + + + + +
+ + diff --git a/project.clj b/project.clj index 0363001..a6dcc5b 100644 --- a/project.clj +++ b/project.clj @@ -10,13 +10,13 @@ [cljs-ajax "0.7.3"]] :plugins [[lein-cljsbuild "1.1.7"]] :cljsbuild {:builds [{:source-paths ["src"] - :compiler {:optimizations :simple + :compiler {:optimizations :none :pretty-print true :source-map true :output-dir "ext/js/generated/out" :modules {:background {:output-to "ext/js/generated/background.js" :entries #{"looped-in.background"}} - :popup - {:output-to "ext/js/generated/popup.js" - :entries #{"looped-in.popup"}}}}}]}) + :sidebar + {:output-to "ext/js/generated/sidebar.js" + :entries #{"looped-in.sidebar"}}}}}]}) diff --git a/src/looped_in/background.cljs b/src/looped_in/background.cljs index d373021..087db90 100644 --- a/src/looped_in/background.cljs +++ b/src/looped_in/background.cljs @@ -1,46 +1,14 @@ (ns looped-in.background (:require [clojure.core.match :refer [match]] - [cljs.core.async :refer [go go-loop chan close! >! promise promise->channel]])) (enable-console-print!) -(defn fetch-submission - "Fetches submissions from Hacker News by `url`" - [url] - (let [response-chan (chan)] - (GET "https://hn.algolia.com/api/v1/search" - {:params {"query" url - "hitsPerPage" 1000 - "restrictSearchableAttributes" "url"} - :handler (fn [res] (go (>! response-chan res))) - :error-handler (fn [err] - (log/error (str "Error fetching HN stories for " url ":") err) - (close! response-chan))}) - response-chan)) - -(defn fetch-item - "Fetches items from Hacker News by `id`" - [id] - (let [response-chan (chan)] - (GET (str "https://hn.algolia.com/api/v1/items/" id) - {:handler (fn [res] (go (>! response-chan res))) - :error-handler (fn [err] - (log/error (str "Error fetching item " id ":") err) - (close! response-chan))}) - response-chan)) - -(defn fetch-items-for-hits [hits] - (let [chans (map (fn [hit] - (fetch-item (hit "objectID"))) - hits)] - (go-loop [[channel & rest] chans - acc []] - (if (nil? channel) - acc - (recur rest (conj acc ( js/browser (.-tabs) (.query #js {:active true :currentWindow true}) @@ -79,24 +45,42 @@ (first) (.-url)) hits (-> url - (fetch-submission) + (hn/fetch-submission) (promise (go @object-ids)) + x (log/error "Unknown popup message type" x))) + +(defn handle-browser-action [tab] + (-> js/browser (.-sidebarAction) (.open))) (-> js/browser (.-tabs) (.-onActivated) - (.addListener handle-update)) + (.addListener handle-tab-update)) (-> js/browser (.-tabs) (.-onUpdated) - (.addListener handle-update)) + (.addListener handle-tab-update)) + +(-> js/browser + (.-runtime) + (.-onMessage) + (.addListener handle-message)) + +(-> js/browser + (.-browserAction) + (.-onClicked) + (.addListener handle-browser-action)) ;; Application logic: ;; 1. Event comes in (new url) diff --git a/src/looped_in/hackernews.cljs b/src/looped_in/hackernews.cljs new file mode 100644 index 0000000..4580169 --- /dev/null +++ b/src/looped_in/hackernews.cljs @@ -0,0 +1,39 @@ +(ns looped-in.hackernews + (:require [cljs.core.async :refer [go go-loop >! chan close!]] + [ajax.core :refer [GET]] + [looped-in.logging :as log])) + +(defn fetch-submission + "Fetches submissions from Hacker News by `url`" + [url] + (let [response-chan (chan)] + (GET "https://hn.algolia.com/api/v1/search" + {:params {"query" url + "hitsPerPage" 1000 + "restrictSearchableAttributes" "url"} + :handler (fn [res] (go (>! response-chan res))) + :error-handler (fn [err] + (log/error (str "Error fetching HN stories for " url ":") err) + (close! response-chan))}) + response-chan)) + +(defn fetch-item + "Fetches items from Hacker News by `id`" + [id] + (let [response-chan (chan)] + (GET (str "https://hn.algolia.com/api/v1/items/" id) + {:handler (fn [res] (go (>! response-chan res))) + :error-handler (fn [err] + (log/error (str "Error fetching item " id ":") err) + (close! response-chan))}) + response-chan)) + +(defn fetch-items [ids] + (let [chans (map (fn [id] + (fetch-item id)) + ids)] + (go-loop [[channel & rest] chans + acc []] + (if (nil? channel) + acc + (recur rest (conj acc (channel]]) + (:import (goog.ui Zippy))) + +(defn log [& args] + (let [bg (-> js/browser (.-extension) (.getBackgroundPage))] + (apply (-> bg (.-console) (.-log)) "[Looped In]" (map clj->js args)))) + +(defn comment-dom [{:strs [text author children]}] + (let [$text (dom/createDom "div" + #js {:class "commentText"} + (dom/safeHtmlToNode (Sanitizer/sanitize text))) + $author (dom/createDom "div" + #js {:class "commentAuthor"} + author)] + (if (> (count children) 0) + (let [$toggle (dom/createDom "div" + #js {:class "commentToggle"} + "") + $children (apply dom/createDom + "div" + #js {:class "commentChildren"} + (clj->js (map comment-dom children)))] + (Zippy. $toggle $children) + (dom/createDom "div" + #js {:class "comment"} + $text + $author + $toggle + $children)) + (dom/createDom "div" + #js {:class "comment"} + $text + $author)))) + +(defn comments-dom [comments] + (clj->js + (apply dom/createDom + "div" + #js {:class "comments"} + (map comment-dom comments)))) + +(defn story-dom [story] + (let [$title (dom/createDom "div" + #js {:class "storyTitle"} + (story "title")) + $comments (comments-dom (filter #(= "comment" (% "type")) (story "children")))] + (Zippy. $title $comments) + (dom/createDom "div" + #js {:class "story"} + $title + $comments))) + +(defn render-items [items] + (let [stories (filter #(= "story" (% "type")) items) + $stories (clj->js (map story-dom stories)) + $storiesContainer (dom/getElement "storiesContainer")] + (log items) + (dom/append $storiesContainer $stories))) + +(go (-> js/browser + (.-runtime) + (.sendMessage #js {:type "popupOpened"}) + (promise->channel) + (