REFACTOR: `{{avatar}}` and `{{date}}` helpers in vdom templates

REFACTOR: {{avatar}} and {{date}} helpers in vdom templates

This is a step towards a nicer flag display under posts.

diff --git a/app/assets/javascripts/discourse/helpers/node.js.es6 b/app/assets/javascripts/discourse/helpers/node.js.es6
index e73345d..21d4d9d 100644
--- a/app/assets/javascripts/discourse/helpers/node.js.es6
+++ b/app/assets/javascripts/discourse/helpers/node.js.es6
@@ -17,6 +17,11 @@ export function dateNode(dt) {
   }
 }
 
+// TODO: Improve how helpers are registered for vdom compliation
+if (typeof Discourse !== "undefined") {
+  Discourse.__widget_helpers.dateNode = dateNode;
+}
+
 export function numberNode(num, opts) {
   opts = opts || {};
   num = parseInt(num, 10);
diff --git a/app/assets/javascripts/discourse/widgets/actions-summary.js.es6 b/app/assets/javascripts/discourse/widgets/actions-summary.js.es6
index 3206a12..50c6d8a 100644
--- a/app/assets/javascripts/discourse/widgets/actions-summary.js.es6
+++ b/app/assets/javascripts/discourse/widgets/actions-summary.js.es6
@@ -1,9 +1,8 @@
 import { createWidget } from "discourse/widgets/widget";
 import { avatarFor } from "discourse/widgets/post";
-import { iconNode } from "discourse-common/lib/icon-library";
 import { h } from "virtual-dom";
-import { dateNode } from "discourse/helpers/node";
 import { userPath } from "discourse/lib/url";
+import hbs from "discourse/widgets/hbs-compiler";
 
 export function avatarAtts(user) {
   return {
@@ -156,38 +155,19 @@ createWidget("actions-summary-item", {
   }
 });
 
-createWidget("deleted-post", {
-  tagName: "div.post-action.deleted-post",
-
-  html(attrs) {
-    return [
-      iconNode("far-trash-alt"),
-      " ",
-      avatarFor.call(this, "small", {
-        template: attrs.deletedByAvatarTemplate,
-        username: attrs.deletedByUsername
-      }),
-      " ",
-      dateNode(attrs.deleted_at)
-    ];
-  }
-});
-
 export default createWidget("actions-summary", {
   tagName: "section.post-actions",
-
-  html(attrs) {
-    const actionsSummary = attrs.actionsSummary || [];
-    const body = [];
-    actionsSummary.forEach(as => {
-      body.push(this.attach("actions-summary-item", as));
-      body.push(h("div.clearfix"));
-    });
-
-    if (attrs.deleted_at) {
-      body.push(this.attach("deleted-post", attrs));
-    }
-
-    return body;
-  }
+  template: hbs`
+    {{#each attrs.actionsSummary as |as|}}
+      {{attach widget="actions-summary-item" attrs=as}}
+      <div class='clearfix'></div>
+    {{/each}}
+    {{#if attrs.deleted_at}}
+      <div class='post-action deleted-post'>
+        {{d-icon "far-trash-alt"}}
+        {{avatar size="small" template=attrs.deletedByAvatarTemplate username=attrs.deletedByUsername}}
+        {{date attrs.deleted_at}}
+      </div>
+    {{/if}}
+  `
 });
diff --git a/app/assets/javascripts/discourse/widgets/post.js.es6 b/app/assets/javascripts/discourse/widgets/post.js.es6
index 35b86f4..daa19ec 100644
--- a/app/assets/javascripts/discourse/widgets/post.js.es6
+++ b/app/assets/javascripts/discourse/widgets/post.js.es6
@@ -59,6 +59,11 @@ export function avatarFor(wanted, attrs) {
   );
 }
 
+// TODO: Improve how helpers are registered for vdom compliation
+if (typeof Discourse !== "undefined") {
+  Discourse.__widget_helpers.avatar = avatarFor;
+}
+
 createWidget("select-post", {
   tagName: "div.select-posts",
 
diff --git a/lib/javascripts/widget-hbs-compiler.js.es6 b/lib/javascripts/widget-hbs-compiler.js.es6
index 17e963f..a55dfae 100644
--- a/lib/javascripts/widget-hbs-compiler.js.es6
+++ b/lib/javascripts/widget-hbs-compiler.js.es6
@@ -33,6 +33,15 @@ function argValue(arg) {
   }
 }
 
+function useHelper(state, name) {
+  let id = state.helpersUsed[name];
+  if (!id) {
+    id = ++state.helperNumber;
+    state.helpersUsed[name] = id;
+  }
+  return `__h${id}`;
+}
+
 function mustacheValue(node, state) {
   let path = node.path.original;
 
@@ -63,18 +72,30 @@ function mustacheValue(node, state) {
       }
 
       break;
-    case "fa-icon":
+    case "avatar":
+      let template = argValue(node.hash.pairs.find(p => p.key === "template"));
+      let username = argValue(node.hash.pairs.find(p => p.key === "username"));
+      let size = argValue(node.hash.pairs.find(p => p.key === "size"));
+      return `${useHelper(
+        state,
+        "avatar"
+      )}(${size}, { template: ${template}, username: ${username} })`;
+      break;
+    case "date":
+      value = resolve(node.params[0].original);
+      return `${useHelper(state, "dateNode")}(${value})`;
+      break;
     case "d-icon":
-      state.helpersUsed.iconNode = true;
       let icon = node.params[0].value;
-      return `__iN("${icon}")`;
+      return `${useHelper(state, "iconNode")}("${icon}")`;
       break;
     default:
       if (node.escaped) {
         return `${resolve(path)}`;
       } else {
-        state.helpersUsed.rawHtml = true;
-        return `new __rH({ html: '<span>' + ${resolve(path)} + '</span>'})`;
+        return `new ${useHelper(state, "rawHtml")}({ html: '<span>' + ${resolve(
+          path
+        )} + '</span>'})`;
       }
       break;
   }
@@ -86,7 +107,8 @@ class Compiler {
     this.ast = ast;
 
     this.state = {
-      helpersUsed: {}
+      helpersUsed: {},
+      helperNumber: 0
     };
   }
 
@@ -212,12 +234,11 @@ function compile(template) {
   let code = compiler.compile();
 
   let imports = "";
-  if (compiler.state.helpersUsed.iconNode) {
-    imports += "var __iN = Discourse.__widget_helpers.iconNode; ";
-  }
-  if (compiler.state.helpersUsed.rawHtml) {
-    imports += "var __rH = Discourse.__widget_helpers.rawHtml; ";
-  }
+
+  Object.keys(compiler.state.helpersUsed).forEach(h => {
+    let id = compiler.state.helpersUsed[h];
+    imports += `var __h${id} = Discourse.__widget_helpers.${h}; `;
+  });
 
   return `function(attrs, state) { ${imports}var _r = [];\n${code}\nreturn _r; }`;
 }

GitHub sha: dd29af44

1 Like