From befcd1f74c6123414bb17f54aacc3e31f22aa5a8 Mon Sep 17 00:00:00 2001
From: Kushagra Gour <chinchang457@gmail.com>
Date: Mon, 27 Feb 2017 02:36:28 +0530
Subject: [PATCH] add import/export feat. fixes #59

---
 src/index.html |  17 +++++++
 src/script.js  | 135 +++++++++++++++++++++++++++++++++++++++++++++----
 src/style.css  |  15 ++++--
 3 files changed, 155 insertions(+), 12 deletions(-)

diff --git a/src/index.html b/src/index.html
index ba720ea..f5247bf 100644
--- a/src/index.html
+++ b/src/index.html
@@ -377,7 +377,24 @@
 
 		<div id="js-saved-items-pane" class="saved-items-pane">
 			<button class="btn  saved-items-pane__close-btn" id="js-saved-items-pane-close-btn">X</button>
+			<div class="flex flex-v-center" style="justify-content: space-between;">
+
 			<h3>My Library</h3>
+			<div class="main-header__btn-wrap">
+				<a d-click="exportItems" href="" class="btn btn-icon">
+<svg viewBox="0 0 24 24" style="width:14px;height:14px">
+    <path d="M6,2C4.89,2 4,2.9 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M13,3.5L18.5,9H13M8.93,12.22H16V19.29L13.88,17.17L11.05,20L8.22,17.17L11.05,14.35" />
+</svg>
+Export
+				</a>
+				<a d-click="onImportBtnClicked" href="" class="btn btn-icon">
+<svg viewBox="0 0 24 24" style="width:14px;height:14px">
+    <path d="M6,2C4.89,2 4,2.9 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M13,3.5L18.5,9H13M10.05,11.22L12.88,14.05L15,11.93V19H7.93L10.05,16.88L7.22,14.05" />
+</svg>
+Import
+				</a>
+			</div>
+			</div>
 			<div id="js-saved-items-wrap" class="saved-items-pane__container">
 
 			</div>
diff --git a/src/script.js b/src/script.js
index f006f73..74798b8 100644
--- a/src/script.js
+++ b/src/script.js
@@ -331,13 +331,19 @@ TextareaAutoComplete */
 		isSavedItemsPaneOpen = savedItemsPane.classList.contains('is-open');
 		document.body.classList[isSavedItemsPaneOpen ? 'add' : 'remove']('overlay-visible');
 	}
-	function openSavedItemsPane() {
+
+	/**
+	 * Fetches all items from storage
+	 * @param  {boolean} shouldSaveGlobally Whether to store the fetched items in global arr for later use.
+	 * @return {promise}                    Promise.
+	 */
+	function fetchItems(shouldSaveGlobally) {
+		var d = deferred();
 		chrome.storage.local.get('items', function (result) {
 			var itemIds = Object.getOwnPropertyNames(result.items || {}),
 				items = [];
 			if (!itemIds.length) {
-				populateItemsInSavedPane([]);
-				return;
+				d.resolve([]);
 			}
 
 			savedItems = savedItems || [];
@@ -346,17 +352,26 @@ TextareaAutoComplete */
 
 				/* eslint-disable no-loop-func */
 				chrome.storage.local.get(itemIds[i], function (itemResult) {
-					savedItems[itemIds[i]] = itemResult[itemIds[i]];
+					if (shouldSaveGlobally) {
+						savedItems[itemIds[i]] = itemResult[itemIds[i]];
+					}
 					items.push(itemResult[itemIds[i]]);
 					// Check if we have all items now.
 					if (itemIds.length === items.length) {
-						populateItemsInSavedPane(items);
+						d.resolve(items)
 					}
 				});
 
 				/* eslint-enable no-loop-func */
 			}
 		});
+		return d.promise;
+	}
+
+	function openSavedItemsPane() {
+		fetchItems(true).then(function (items) {
+			populateItemsInSavedPane(items);
+		});
 	}
 
 	function createNewItem() {
@@ -404,6 +419,9 @@ TextareaAutoComplete */
 				createNewItem();
 			}
 		});
+		// Remove from cached list
+		delete savedItems[itemId];
+
 		trackEvent('fn', 'itemRemoved');
 	}
 
@@ -893,22 +911,122 @@ TextareaAutoComplete */
 		}
 	}
 
-	scope.onModalSettingsLinkClick = function () {
+	scope.onModalSettingsLinkClick = function onModalSettingsLinkClick() {
 		openSettings();
 		trackEvent('ui', 'onboardSettingsBtnClick');
 	}
 
-	scope.onShowInTabClicked = function () {
+	scope.onShowInTabClicked = function onShowInTabClicked() {
 		onboardDontShowInTabOptionBtn.classList.remove('selected');
 		onboardShowInTabOptionBtn.classList.add('selected');
 		trackEvent('ui', 'onboardShowInTabClick');
 	}
-	scope.onDontShowInTabClicked = function () {
+	scope.onDontShowInTabClicked = function onDontShowInTabClicked() {
 		onboardDontShowInTabOptionBtn.classList.add('selected');
 		onboardShowInTabOptionBtn.classList.remove('selected');
 		trackEvent('ui', 'onboardDontShowInTabClick');
 	}
 
+	scope.exportItems = function exportItems(e) {
+		fetchItems().then(function (items) {
+			utils.log(9, items);
+			var d = new Date();
+			var fileName = [ 'web-maker-export', d.getFullYear(), (d.getMonth() + 1), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds() ].join('-');
+			fileName += '.json';
+			var blob = new Blob([ JSON.stringify(items,false,2) ], { type: "application/json;charset=UTF-8" });
+			var a = document.createElement('a');
+			a.href = window.URL.createObjectURL(blob);
+			a.download = fileName;
+			a.style.display = 'none';
+			document.body.appendChild(a);
+			a.click();
+			a.remove();
+			trackEvent('ui', 'exportBtnClicked');
+		});
+		e.preventDefault();
+	}
+
+	function mergeImportedItems(items) {
+		var existingItemIds = [];
+		var toMergeItems = {};
+		items.forEach((item) => {
+			if (savedItems[item.id]) {
+				// Item already exists
+				existingItemIds.push(item.id);
+			} else {
+				utils.log('merging', item.id);
+				toMergeItems[item.id] = item;
+			}
+		});
+		var mergedItemCount = items.length - existingItemIds.length;
+		if (existingItemIds.length) {
+			var shouldReplace = confirm(existingItemIds.length + ' creations already exist. Do you want to replace them?');
+			if (shouldReplace) {
+				utils.log('shouldreplace', shouldReplace);
+				items.forEach((item) => {
+					toMergeItems[item.id] = item;
+					mergedItemCount = items.length;
+				});
+			}
+		}
+		if (mergedItemCount) {
+			// save new items
+			chrome.storage.local.set(toMergeItems, function () {
+				alertsService.add(mergedItemCount + ' creations imported successfully.');
+			});
+			// Push in new item IDs
+			chrome.storage.local.get({
+				items: {}
+			}, function (result) {
+
+				/* eslint-disable guard-for-in */
+				for (var id in toMergeItems) {
+					result.items[id] = true;
+					chrome.storage.local.set({
+						items: result.items
+					});
+				}
+
+				/* eslint-enable guard-for-in */
+			});
+			alertsService.add(mergedItemCount + ' creations imported successfully.');
+		}
+		// FIXME: Move from here
+		toggleSavedItemsPane(false);
+	}
+
+	function onImportFileChange(e) {
+		var file = e.target.files[0];
+		// if (!f.type.match('image.*')) {
+			// continue;
+		// }
+
+		var reader = new FileReader();
+		reader.onload = function(progressEvent) {
+			var items;
+			try {
+				items = JSON.parse(progressEvent.target.result);
+				utils.log(items);
+				mergeImportedItems(items);
+			} catch (ex) {
+				alert('Oops! Select file is corrupted.')
+			}
+		};
+
+		reader.readAsText(file, 'utf-8');
+	}
+
+	scope.onImportBtnClicked = function exportItems(e) {
+		var input = document.createElement('input');
+		input.type = 'file';
+		input.style.display = 'none';
+		input.accept = 'accept="application/json';
+		document.body.appendChild(input)
+		input.addEventListener('change', onImportFileChange);
+		input.click();
+		e.preventDefault();
+	}
+
 	function saveScreenshot(dataURI) {
 		// convert base64 to raw binary data held in a string
 		// doesn't handle URLEncoded DataURIs
@@ -1098,7 +1216,6 @@ TextareaAutoComplete */
 				toggleSavedItemsPane();
 			}
 			if (e.target.classList.contains('js-saved-item-tile__close-btn')) {
-				utils.log('removing', e.target.parentElement)
 				removeItem(e.target.parentElement.dataset.itemId);
 			}
 		});
diff --git a/src/style.css b/src/style.css
index 2367d36..ebee10a 100644
--- a/src/style.css
+++ b/src/style.css
@@ -258,7 +258,7 @@ li.CodeMirror-hint-active {
 	background-color: rgba(0, 0, 0, 0.5);
 	color: rgba(255, 255, 255, 0.45);
 	border-top: 1px solid rgba(255,255,255,0.14);
-	line-height: 20px;
+	/*line-height: 20px;*/
 }
 .main-header {
 	border: 0;
@@ -266,17 +266,26 @@ li.CodeMirror-hint-active {
 }
 .main-header__btn-wrap > a {
 	font-size: 0.8em;
+	line-height: 20px;
+	height: 20px;
+	letter-spacing: 0.6px;
 	color: #9297B3;
 	border-radius: 3px;
-	border: 1px solid rgba(146, 151, 179, 0.33);
 	margin-left: 10px;
-	padding: 0px 5px;
+	padding: 0px 8px;
+	border: 1px solid rgba(0,0,0,.9);
+	background: linear-gradient(180deg, rgba(0,0,0,0.5) 0, rgba(255,255,255,0.1) 100%);
+	text-shadow: 0px 1px 1px rgba(0,0,0,1);
+	box-shadow: 0 -1px 0px 0 rgba(255,255,255,0.15);
 	text-transform: uppercase;
 }
 .main-header__btn-wrap > a > svg {
 	fill: #9297B3;
 	margin-right: 4px;
 }
+.main-header__btn-wrap > a.is-marked > svg {
+	fill: crimson;
+}
 .main-header__btn-wrap > a:hover {
 	border-color: rgba(146, 151, 179, 0.5);
 }