Repository: kestra-io/kestra
Issue: Inefficient icon management for topology frontend side

It appears that each topology node trigger an http query to get icons from api. 
However, the api returns all icon definition in one document. this is a bloated behavior that we have to centralize in a store
![image](https://user-images.githubusercontent.com/5399780/107669073-a937db80-6c91-11eb-96d1-2c6858948022.png)


Issue comments:
in fact, it's in the store, but the first request display all the component at the same time when the store is not ready. 
I don't find a quick way for this case (only the first load) 
I'll do it :)
I will just do one query at topology load store result  in a vuex store and use something like <img :src="store['icon']" /> which lasyloads image if value change from undefined to a string (or a simple v-if on the img tag otherwise)
👍 just think the image is not used only on this page, and it will be fined not duplicate the behaviour on each page 😁
it's ok now (PR update)

Pull request review comments:

Patch that resolved the issue:
diff --git a/ui/src/App.vue b/ui/src/App.vue
index a3221051de..2a08bc98d9 100644
--- a/ui/src/App.vue
+++ b/ui/src/App.vue
@@ -41,7 +41,7 @@
             }
 
             this.displayApp()
-
+            this.loadGeneralRessources()
             this.onMenuCollapse(localStorage.getItem("menuCollapsed") === "true");
         },
         methods: {
@@ -51,6 +51,9 @@
             displayApp() {
                 document.getElementById("loader-wrapper").style.display = "none";
                 document.getElementById("app-container").style.display = "block";
+            },
+            loadGeneralRessources() {
+                this.$store.dispatch("plugin/icons")
             }
         }
     };
diff --git a/ui/src/components/plugins/TaskIcon.vue b/ui/src/components/plugins/TaskIcon.vue
index ebe436c696..4d04f7bada 100644
--- a/ui/src/components/plugins/TaskIcon.vue
+++ b/ui/src/components/plugins/TaskIcon.vue
@@ -1,5 +1,5 @@
 <template>
-    <div class="wrapper" :class="classes" v-if="ready" :id="uuid">
+    <div class="wrapper" v-if="icon" :class="classes" :id="uuid">
         <div class="icon" :style="styles" :alt="cls" />
         <div v-if="!onlyIcon" class="hover">
             {{ name }}
@@ -27,8 +27,7 @@
         },
         data() {
             return {
-                uuid: Utils.uid(),
-                ready: false
+                uuid: Utils.uid()
             }
         },
         components: {
@@ -36,11 +35,11 @@
         computed: {
             ...mapState("plugin", ["icons"]),
             name() {
-                return this.icons[this.cls] ? this.icons[this.cls].name : this.cls;
+                return this.icon ? this.icon.name : this.cls;
             },
             classes() {
                 return {
-                    "flowable": this.icons[this.cls] ? this.icons[this.cls].flowable : false,
+                    "flowable": this.icon ? this.icon.flowable : false,
                     "only-icon": this.onlyIcon
                 }
             },
@@ -53,17 +52,11 @@
                 return this.name ? this.cls.replace("." + this.name, ".<code>" + this.name + "</code>") : this.cls;
             },
             imageBase64() {
-                return this.icons[this.cls] && this.icons[this.cls].icon ?
-                    this.icons[this.cls].icon :
-                    btoa("<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" aria-hidden=\"true\" focusable=\"false\" width=\"0.75em\" height=\"1em\" style=\"-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);\" preserveAspectRatio=\"xMidYMid meet\" viewBox=\"0 0 384 512\"><path d=\"M288 32H0v448h384V128l-96-96zm64 416H32V64h224l96 96v288z\" fill=\"#333\"/></svg>");
-            }
-        },
-
-        created() {
-            if (this.icons === undefined) {
-                this.$store.dispatch("plugin/icons").then(() => this.ready = true)
-            } else {
-                this.ready = true;
+                const icon = this.icon ? this.icon.icon : undefined;
+                return icon ? icon : btoa("<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" aria-hidden=\"true\" focusable=\"false\" width=\"0.75em\" height=\"1em\" style=\"-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);\" preserveAspectRatio=\"xMidYMid meet\" viewBox=\"0 0 384 512\"><path d=\"M288 32H0v448h384V128l-96-96zm64 416H32V64h224l96 96v288z\" fill=\"#333\"/></svg>");
+            },
+            icon() {
+                return (this.icons || {})[this.cls]
             }
         }
     };


Test patch: