~pepe/good-place

5bdc8799f01e6d88445fba276a5b5b141d638f34 — Josef Pospíšil 1 year, 9 months ago 621f20b
Use sock for notification and hot reload
M good-place/acts.janet => good-place/acts.janet +6 -5
@@ 29,10 29,11 @@
    (forever
      (def lc (notify-modify t))
      (cocoon/emerge
        (make-act {:watch
                   (fn [&] [(clear-module lc) RenderAll])
                   :effect
                   (fn [&] (print "Modified " lc))})))))
        (make-act
          {:watch
           (fn [&] [(clear-module lc) RenderAll])
           :effect
           (fn [&] (print "Modified " lc))})))))

(define-watch Monitor [&]
  [content/Monitor


@@ 57,7 58,7 @@
(define-watch Development [&]
  [Present
   SetDev
   (web/start
   (web/open
     {:render-content-file content/render-file
      :render-blog-file blog/markup-and-render-file
      :render-all RenderAll

M good-place/acts/blog.janet => good-place/acts/blog.janet +2 -2
@@ 30,7 30,7 @@
  (make-watch
    (fn [_ e _]
      (def {:site-title st :dev dev :markups ms
            :files {:blog bfiles}} e)
            :files {:blog bfiles} :trevor t} e)
      (def m (require file))
      (def {:template {'render-dict {:value rt}}
            :content ct} m)


@@ 43,7 43,7 @@
                    :site-title st
                    :css (process-css e)
                    :file file
                    :dev dev})
                    :dev dev :trevor t})
            index?
            (merge {:markups ms
                    :posts (filter |(not (index? $)) bfiles)}))))

M good-place/acts/content.janet => good-place/acts/content.janet +4 -4
@@ 1,8 1,8 @@
(use shawn/act shawn/acts)
(import /mendoza/markup)
(import /mendoza/render)
(use shawn/act)
(use shawn/acts)
(import shawn/cocoon)
(use /good-place/sock/acts)

(use ../utils)
(use ./shared)


@@ 10,14 10,14 @@
(defn render-file [file]
  (make-watch
    (fn [_ e _]
      (def {:site-title st :dev dev :pkgs-list pl} e)
      (def {:site-title st :dev dev :pkgs-list pl :trevor t} e)
      (def [m fc] [(require file) @""])
      (with-dyns [:out fc]
        ((get-in m [:template 'render-dict :value])
          (merge m
                 {:content (render/render (m :content) @"")
                  :site-title st :css (process-css e)
                  :file file :dev dev})))
                  :file file :dev dev :trevor t})))
      [(clear-module file) (save-content file fc)])
    (. "render-" file)))


M good-place/acts/shared.janet => good-place/acts/shared.janet +16 -4
@@ 1,6 1,7 @@
(use shawn/acts)
(use spork)
(use ../utils)
(use /good-place/sock/acts)

(defn copy-file [file ftype]
  (make-effect


@@ 20,17 21,28 @@
    (fn [_ e] (put-in e [:files dir] files))
    (. "save-" dir)))

(defn save-content [file content]
(defn log-render [of nf]
  (make-effect (fn [&] (print "Rendered " of " to " nf))
               "log-render"))

(defn spit-file [file content]
  (make-effect
    (fn [_ _ _]
      (def dir (path/dirname file))
      (if (not (os/stat dir)) (os/mkdir dir))
      (spit file content))
    (. "spit-file-" file)))

(defn save-content [file content]
  (make-watch
    (fn [_ {:content c :public p} _]
      (def nf
        (->> file
             (string/replace c p)
             mdz->html))
      (def dir (path/dirname nf))
      (if (not (os/stat dir)) (os/mkdir dir))
      (spit nf content)
      (print "Rendered " file " to " nf))
      [(spit-file nf content) (log-render file nf)
       (alert {:action :rendered :name nf})])
    (. "save-content-" file)))

(defn log [& msgs]

M good-place/sock/acts.janet => good-place/sock/acts.janet +19 -16
@@ 9,29 9,20 @@
(import ../sock/app)
(use ../utils)

(defn type-path [sck]
  ~[:socks ,(sck :type)])
(defn type-path [sck] [:socks (sck :type)])

(defn add [sck]
(defn add-one [sck]
  (make-update
    (fn [_ e]
      (tour ~[,(type-path sck) (add ,sck)] e)
      e)
      ((guide ;(type-path sck) (add sck)) e))
    "add-sock"))

(defn remove [sck]
(defn remove-one [sck]
  (make-update
    (fn [_ e]
      (tour ~[,(type-path sck) (remove ,sck)] e)
      e)
      ((guide ;(type-path sck) (remove sck)) e))
    "remove-sock"))

(defn alert [msg]
  (make-effect
    (fn [_ {:socks {:alert ss}} _]
      (each s ss
        (:write s (trevor/text (json/encode {:msg msg})))))))

(defn html [t p a]
  (make-effect
    (fn [_ {:socks ss} _]


@@ 39,6 30,18 @@
        (:write s (trevor/text (page* p a)))))
    (. "html-" t "-" p)))

(defn alert [action]
  (make-watch
    (fn [_ {:socks {:alert ss}} _]
      (seq [s :in ss]
        (def name
          (peg/replace
            ~(* (thru "/")) "" (action :name)))
        (def msg (. name " was " (action :action)))
        (html :alert "notification"
              {:msg msg :location name})))
    (. alert "-" action)))

(defn start []
  (make-act
    {:watch


@@ 46,8 49,8 @@
       (def {:trevor {:host host :port port}} e)
       (cocoon/give
         (chidi/start
           (app/handler {:add add
                         :remove remove})
           (app/handler {:add add-one
                         :remove remove-one})
           host port
           app/supervisor
           trevor/on-connection)))

M good-place/web/acts.janet => good-place/web/acts.janet +7 -9
@@ 1,18 1,16 @@
(use shawn/act)
(import shawn/cocoon)
(import chidi)
(import ../web)
(use shawn/act shawn/cocoon chidi)
(use /good-place/web)

(defn start [acts]
(defn open [acts]
  (make-act
    {:watch
     (fn [_ e _]
       (def {:chidi {:host host :port port}} e)
       (cocoon/give
         (chidi/start
           (web/handler e acts)
       (give
         (start
           (handler e acts)
           host port
           web/supervisor)))
           supervisor)))
     :effect
     (fn [_ {:chidi {:host host :port port}} _]
       (print "Chidi Present on " host ":" port))}

M good-place/web/init.janet => good-place/web/init.janet +4 -2
@@ 53,6 53,7 @@
    (layout @{:title (string "Editing " file)
              :site-title st
              :css (process-css envelope)
              :dev true
              :content
              (page edit
                    @{:file-content


@@ 65,11 66,12 @@
                      :file-name file})})))

(defn save [list-content]
  (fn [{:body {"file-name" fnm "file-content" fc}}]
  (fn [req]
    (def {:body {"file-name" fnm "file-content" fc} :headers {"HX-Request" hx}} req)
    (spit fnm (string/replace-all "\r\n" "\n" fc))
    (emerge list-content)
    (ev/sleep 0.01) # give monitor time to catch up
    (see-other (vf fnm))))
    (if hx (no-content) (see-other (vf fnm)))))

(defn pkgs [get-pkgs]
  (fn [&]

M templates/edit.temple => templates/edit.temple +2 -2
@@ 3,11 3,11 @@
(def fnm (args :file-name))
(def new? (= fnm new-file-name))
%}
<form style="max-width: 45rem" action="/__dashboard/save" method="POST">
<form hx-post="/__dashboard/save" style="max-width: 45rem" action="/__dashboard/save" method="POST">
  <textarea name="file-content" cols="80" rows="20">{- (args :file-content) -}</textarea>
  {- (if new? `<label for="name">File name</label>`) -}
  <input type={- (if new? (. `"text" placeholder="` fnm `"`) `"hidden"`) -}
    name="file-name" value="{- (unless new? fnm) -}"/>
  <input type="submit" value="Save" />
  <a class="button" href="/__dashboard">Cancel</a>
  <a class="button" href="/__dashboard">Back to Dashboard</a>
</form>

M templates/foot.temple => templates/foot.temple +29 -1
@@ 2,7 2,35 @@
        <hstack align-x="center">Good Place 2021 All rights rejected</hstack>
      </vstack>
    </vstack>
    <script src="https://unpkg.com/htmx.org@1.3.3" integrity="sha384-QrlPmoLqMVfnV4lzjmvamY0Sv/Am8ca1W7veO++Sp6PiIGixqkD+0xZ955Nc03qO" crossorigin="anonymous"></script>
    {% (when (args :dev) %}
    <script src="https://unpkg.com/htmx.org@1.6.1"
      integrity="sha384-tvG/2mnCFmGQzYC1Oh3qxQ7CkQ9kMzYjWZSNtrRZygHPDDqottzEJsqS4oUVodhW"
      crossorigin="anonymous"></script>
    <script src="https://unpkg.com/hyperscript.org@0.9.0"></script>
    <script type="text/hyperscript">
      def checkNotifications()
        if Notification.permission == "denied"
          show <#notifications p.disabled />
        else if Notification.permission == "default"
          show <#notifications button />
        else
          show <#notifications p.enabled />
        end
      end

      def requestNotification()
        if Notification.permission == "denied"
          call alert('You have denied the permission to Notification, please remove the block')
        else if Notification.permission == "default"
          set permission to Notification.requestPermission()
          hide <#notifications button />
          call checkNotifications()
        else
          js new Notification("Notifications enabled!") end
        end
      end
    </script>
    {% ) %}
  </body>
</html>


M templates/head.temple => templates/head.temple +7 -2
@@ 1,3 1,5 @@
{$ (use marble) $}
{% (def d (args :dev))(def t (args :trevor)) %}
<!DOCTYPE html>
<html lang="en">
  <head>


@@ 15,14 17,17 @@
        <img class="logo" src="/logo.svg" />
        <span>{{ (args :site-title) }}</span>
      </a>
      {% (when (args :dev) %}
      {% (when d %}
      <input id="bmenub" type="checkbox" class="show">
      <label for="bmenub" class="burger pseudo button">menu</label>
      <div class="menu">
        <button class="pseudo" _="on click call requestNotification()">Notify</button>
        <a href="/__dashboard/edit?file={- (args :file) -}" class="pseudo button">Edit</a>
        <a href="/__dashboard" class="pseudo button">Dashboard</a>
      </div>
      {% ) %}
    </nav>
    <vstack spacing="xl" id="page">
    <vstack spacing="xl" id="page"
      {- (when (and t d) (. `hx-ws="connect:ws://` (t :host) ":" (t :port) `/"`)) -}>
      <script id="notification"></script>
      <vstack align-x="center">

A templates/notification.temple => templates/notification.temple +4 -0
@@ 0,0 1,4 @@
<script id="notification">
  new Notification('{{ (args :msg) }} redirecting there in one sec')
  setTimeout(() => {window.location = '/{{ (args :location) }}'}, 1000)
</script>