Merge branch 'hub-redesign' of https://github.com/frappe/erpnext into hub-redesign
diff --git a/erpnext/public/js/hub/event_emitter.js b/erpnext/public/js/hub/event_emitter.js
new file mode 100644
index 0000000..1e72881
--- /dev/null
+++ b/erpnext/public/js/hub/event_emitter.js
@@ -0,0 +1,31 @@
+/**
+ * Simple EventEmitter which uses jQuery's event system
+ */
+class EventEmitter {
+    init() {
+        this.jq = jQuery(this);
+    }
+
+    trigger(evt, data) {
+        !this.jq && this.init();
+        this.jq.trigger(evt, data);
+    }
+
+    once(evt, handler) {
+        !this.jq && this.init();
+        this.jq.one(evt, (e, data) => handler(data));
+    }
+
+    on(evt, handler) {
+        !this.jq && this.init();
+        this.jq.bind(evt, (e, data) => handler(data));
+    }
+
+    off(evt, handler) {
+        !this.jq && this.init();
+        this.jq.unbind(evt, (e, data) => handler(data));
+    }
+}
+
+
+export default EventEmitter;
\ No newline at end of file
diff --git a/erpnext/public/js/hub/hub_call.js b/erpnext/public/js/hub/hub_call.js
index 6bc1701..d2eaab3 100644
--- a/erpnext/public/js/hub/hub_call.js
+++ b/erpnext/public/js/hub/hub_call.js
@@ -2,7 +2,7 @@
 frappe.provide('erpnext.hub');
 
 erpnext.hub.cache = {};
-hub.call = function call_hub_method(method, args={}) {
+hub.call = function call_hub_method(method, args={}, setup_cache_invalidation = invalidate_after_5_mins) {
 	return new Promise((resolve, reject) => {
 
 		// cache
@@ -11,12 +11,9 @@
 			resolve(erpnext.hub.cache[key]);
 		}
 
-		// cache invalidation after 5 minutes
-		const timeout = 5 * 60 * 1000;
-
-		setTimeout(() => {
-			delete erpnext.hub.cache[key];
-		}, timeout);
+		// cache invalidation
+		const clear_cache = () => delete erpnext.hub.cache[key];
+		setup_cache_invalidation(clear_cache);
 
 		frappe.call({
 			method: 'erpnext.hub_node.api.call_hub_method',
@@ -42,3 +39,12 @@
 		.fail(reject)
 	});
 }
+
+function invalidate_after_5_mins(clear_cache) {
+	// cache invalidation after 5 minutes
+	const timeout = 5 * 60 * 1000;
+
+	setTimeout(() => {
+		clear_cache();
+	}, timeout);
+}
diff --git a/erpnext/public/js/hub/marketplace.js b/erpnext/public/js/hub/marketplace.js
index 1986cfb..994aebc 100644
--- a/erpnext/public/js/hub/marketplace.js
+++ b/erpnext/public/js/hub/marketplace.js
@@ -17,10 +17,13 @@
 
 // helpers
 import './hub_call';
+import EventEmitter from './event_emitter';
 
 frappe.provide('hub');
 frappe.provide('erpnext.hub');
 
+$.extend(erpnext.hub, EventEmitter.prototype);
+
 erpnext.hub.Marketplace = class Marketplace {
 	constructor({ parent }) {
 		this.$parent = $(parent);
diff --git a/erpnext/public/js/hub/pages/favourites.js b/erpnext/public/js/hub/pages/favourites.js
index 6f2c67c..566f5b0 100644
--- a/erpnext/public/js/hub/pages/favourites.js
+++ b/erpnext/public/js/hub/pages/favourites.js
@@ -26,9 +26,11 @@
 	}
 
 	get_favourites() {
-		return hub.call('get_favourite_items_of_seller', {
-			hub_seller: hub.settings.company_email
-		});
+		return hub.call(
+			'get_favourite_items_of_seller',
+			{ hub_seller: hub.settings.company_email },
+			clear_cache => erpnext.hub.on('action:item_favourite', clear_cache)
+		);
 	}
 
 	render(items) {
diff --git a/erpnext/public/js/hub/pages/item.js b/erpnext/public/js/hub/pages/item.js
index ee43391..a94d584 100644
--- a/erpnext/public/js/hub/pages/item.js
+++ b/erpnext/public/js/hub/pages/item.js
@@ -79,9 +79,10 @@
 		.then(() => {
 			$(favourite_button).html('Saved');
 			frappe.show_alert(__('Saved to <b><a href="#marketplace/favourites">Favourites</a></b>'));
+			erpnext.hub.trigger('action:item_favourite');
 		})
 		.catch(e => {
-			console.log(e);
+			console.error(e);
 		});
 	}