FEATURE: get rid of the import a query modal (#127)

FEATURE: get rid of the import a query modal (#127)

This is the new version of #126. Now the pick-files-button moved to core. To import a query into Data Explorer, you need to push the button and then deal with the import modal. Instead, we want just to be triggering a system file picker directly. This PR makes it happen.

diff --git a/assets/javascripts/discourse/components/json-file-uploader.js.es6 b/assets/javascripts/discourse/components/json-file-uploader.js.es6
deleted file mode 100644
index ca124a9..0000000
--- a/assets/javascripts/discourse/components/json-file-uploader.js.es6
+++ /dev/null
@@ -1,113 +0,0 @@
-import {
-  default as computed,
-  observes,
-  on,
-} from "discourse-common/utils/decorators";
-
-export default Ember.Component.extend({
-  fileInput: null,
-  loading: false,
-  expectedRootObjectName: null,
-  hover: 0,
-
-  classNames: ["json-uploader"],
-
-  @on("didInsertElement")
-  _initialize() {
-    const $this = $(this.element);
-    const fileInput = this.element.querySelector("#js-file-input");
-    this.set("fileInput", fileInput);
-
-    $(fileInput).on("change", () => this.fileSelected(fileInput.files));
-
-    $this.on("dragover", (e) => {
-      if (e.preventDefault) {
-        e.preventDefault();
-      }
-      return false;
-    });
-    $this.on("dragenter", (e) => {
-      if (e.preventDefault) {
-        e.preventDefault();
-      }
-      this.set("hover", this.hover + 1);
-      return false;
-    });
-    $this.on("dragleave", (e) => {
-      if (e.preventDefault) {
-        e.preventDefault();
-      }
-      this.set("hover", this.hover - 1);
-      return false;
-    });
-    $this.on("drop", (e) => {
-      if (e.preventDefault) {
-        e.preventDefault();
-      }
-
-      this.set("hover", 0);
-      this.fileSelected(e.dataTransfer.files);
-      return false;
-    });
-  },
-
-  @computed("extension")
-  accept(extension) {
-    return (
-      ".json,application/json,application/x-javascript,text/json" +
-      (extension ? `,${extension}` : "")
-    );
-  },
-
-  @observes("destination", "expectedRootObjectName")
-  setReady() {
-    let parsed;
-    try {
-      parsed = JSON.parse(this.value);
-    } catch (e) {
-      this.set("ready", false);
-      return;
-    }
-
-    const rootObject = parsed[this.expectedRootObjectName];
-
-    if (rootObject !== null && rootObject !== undefined) {
-      this.set("ready", true);
-    } else {
-      this.set("ready", false);
-    }
-  },
-
-  actions: {
-    selectFile() {
-      $(this.fileInput).click();
-    },
-  },
-
-  fileSelected(fileList) {
-    let files = [];
-    for (let i = 0; i < fileList.length; i++) {
-      files[i] = fileList[i];
-    }
-    const fileNameRegex = /\.(json|txt)$/;
-    files = files.filter((file) => {
-      if (fileNameRegex.test(file.name)) {
-        return true;
-      }
-      if (file.type === "text/plain") {
-        return true;
-      }
-      return false;
-    });
-    const firstFile = files[0];
-
-    this.set("loading", true);
-
-    const reader = new FileReader();
-    reader.onload = (evt) => {
-      this.setProperties({ value: evt.target.result, loading: false });
-    };
-
-    reader.readAsText(firstFile);
-  },
-});
diff --git a/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6 b/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6
index a276816..8075026 100644
--- a/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6
@@ -6,6 +6,8 @@ import {
   default as computed,
   observes,
 } from "discourse-common/utils/decorators";
+import I18n from "I18n";
+import { Promise } from "rsvp";
 
 const NoQuery = Query.create({ name: "No queries", fake: true, group_ids: [] });
 
@@ -30,6 +32,11 @@ export default Ember.Controller.extend({
   sortBy: ["last_run_at:desc"],
   sortedQueries: Ember.computed.sort("model", "sortBy"),
 
+  @computed
+  acceptedImportFileTypes() {
+    return ["application/json"];
+  },
+
   @computed("search", "sortBy")
   filteredContent(search) {
     const regexp = new RegExp(search, "i");
@@ -117,6 +124,36 @@ export default Ember.Controller.extend({
       .finally(() => this.set("loading", false));
   },
 
+  async _importQuery(file) {
+    const json = await this._readFileAsTextAsync(file);
+    const query = this._parseQuery(json);
+    const record = this.store.createRecord("query", query);
+    const response = await record.save();
+    return response.target;
+  },
+
+  _parseQuery(json) {
+    const parsed = JSON.parse(json);
+    const query = parsed.query;
+    if (!query || !query.sql) {
+      throw new TypeError();
+    }
+    query.id = 0; // 0 means no Id yet
+    return query;
+  },
+
+  _readFileAsTextAsync(file) {
+    return new Promise((resolve, reject) => {
+      const reader = new FileReader();
+      reader.onload = () => {
+        resolve(reader.result);
+      };
+      reader.onerror = reject;
+
+      reader.readAsText(file);
+    });
+  },
+
   actions: {
     dummy() {},
 
@@ -124,9 +161,27 @@ export default Ember.Controller.extend({
       this.set("hideSchema", false);
     },
 
-    importQuery() {
-      showModal("import-query");
-      this.set("showCreate", false);
+    import(files) {
+      this.set("loading", true);
+      const file = files[0];
+      this._importQuery(file)
+        .then((record) => this.addCreatedRecord(record))
+        .catch((e) => {
+          if (e.jqXHR) {
+            popupAjaxError(e);
+          } else if (e instanceof SyntaxError) {
+            bootbox.alert(I18n.t("explorer.import.unparseable_json"));
+          } else if (e instanceof TypeError) {
+            bootbox.alert(I18n.t("explorer.import.wrong_json"));
+          } else {
+            bootbox.alert(I18n.t("errors.desc.unknown"));
+            // eslint-disable-next-line no-console
+            console.error(e);
+          }
+        })
+        .finally(() => {
+          this.set("loading", false);
+        });
     },
 
     showCreate() {
diff --git a/assets/javascripts/discourse/controllers/import-query.js.es6 b/assets/javascripts/discourse/controllers/import-query.js.es6
deleted file mode 100644
index 8cc3b5f..0000000
--- a/assets/javascripts/discourse/controllers/import-query.js.es6
+++ /dev/null
@@ -1,43 +0,0 @@
-import { default as computed } from "discourse-common/utils/decorators";
-import ModalFunctionality from "discourse/mixins/modal-functionality";
-import { popupAjaxError } from "discourse/lib/ajax-error";
-
-export default Ember.Controller.extend(ModalFunctionality, {
-  notReady: Ember.computed.not("ready"),
-
-  adminPluginsExplorer: Ember.inject.controller(),
-
-  @computed("queryFile")
-  ready(queryFile) {
-    let parsed;
-    try {
-      parsed = JSON.parse(queryFile);
-    } catch (e) {
-      return false;
-    }
-
-    return !!parsed["query"];
-  },
-
-  actions: {
-    doImport() {
-      const object = JSON.parse(this.queryFile).query;
-
-      // Slight fixup before creating object
-      object.id = 0; // 0 means no Id yet
-
-      this.set("loading", true);
-      this.store
-        .createRecord("query", object)
-        .save()
-        .then((query) => {
-          this.send("closeModal");
-          this.set("loading", false);
-
-          const parentController = this.adminPluginsExplorer;
-          parentController.addCreatedRecord(query.target);
-        })
-        .catch(popupAjaxError);
-    },
-  },
-});
diff --git a/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs b/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs
index e200f29..6f50c87 100644
--- a/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs
+++ b/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs
@@ -6,7 +6,12 @@
       <div class="query-list">
         {{text-field value=search placeholderKey="explorer.search_placeholder"}}
         {{d-button action=(action "showCreate") icon="plus" class="no-text btn-right"}}
-        {{d-button action=(action "importQuery") label="explorer.import.label" icon="upload"}}
+        {{pick-files-button
+          class="import-btn"
+          label="explorer.import.label"
+          icon="upload"

[... diff too long, it was truncated ...]

GitHub sha: 4a98cc8af800295ffbf50a8e25a0c4830d947d09

This commit appears in #127 which was approved by martin. It was merged by AndrewPrigorshnev.