From 959fef487f33b3824b9370f6904c7b948e363793 Mon Sep 17 00:00:00 2001
From: Kushagra Gour <chinchang457@gmail.com>
Date: Sat, 2 Jun 2018 22:55:44 +0530
Subject: [PATCH] complete functionality for header

---
 webmaker/src/components/ContentWrap.jsx   |  59 +++++----
 webmaker/src/components/Login.jsx         |  12 --
 webmaker/src/components/MainHeader.jsx    |   6 +-
 webmaker/src/components/SavedItemPane.jsx |  14 ++
 webmaker/src/components/app.jsx           | 154 +++++++++++++++++++---
 5 files changed, 183 insertions(+), 62 deletions(-)

diff --git a/webmaker/src/components/ContentWrap.jsx b/webmaker/src/components/ContentWrap.jsx
index 6a9694a..ff3ddbe 100644
--- a/webmaker/src/components/ContentWrap.jsx
+++ b/webmaker/src/components/ContentWrap.jsx
@@ -25,17 +25,29 @@ export default class ContentWrap extends Component {
 
 	onHtmlCodeChange(editor, change) {
 		this.cmCodes.html = editor.getValue();
-		this.props.onCodeChange('html', this.cmCodes.html);
+		this.props.onCodeChange(
+			'html',
+			this.cmCodes.html,
+			change.origin !== 'setValue'
+		);
 		this.onCodeChange(editor, change);
 	}
 	onCssCodeChange(editor, change) {
 		this.cmCodes.css = editor.getValue();
-		this.props.onCodeChange('css', this.cmCodes.css);
+		this.props.onCodeChange(
+			'css',
+			this.cmCodes.css,
+			change.origin !== 'setValue'
+		);
 		this.onCodeChange(editor, change);
 	}
 	onJsCodeChange(editor, change) {
 		this.cmCodes.js = editor.getValue();
-		this.props.onCodeChange('js', this.cmCodes.js);
+		this.props.onCodeChange(
+			'js',
+			this.cmCodes.js,
+			change.origin !== 'setValue'
+		);
 		this.onCodeChange(editor, change);
 	}
 	onCodeChange(editor, change) {
@@ -51,25 +63,11 @@ export default class ContentWrap extends Component {
 					this.setPreviewContent();
 				}
 
-				/* saveBtn.classList.add('is-marked');
-				this.unsavedEditCount += 1;
-				if (
-					this.unsavedEditCount % this.unsavedEditWarningCount === 0 &&
-					this.unsavedEditCount >= this.unsavedEditWarningCount
-				) {
-					saveBtn.classList.add('animated');
-					saveBtn.classList.add('wobble');
-					saveBtn.addEventListener('animationend', () => {
-						saveBtn.classList.remove('animated');
-						saveBtn.classList.remove('wobble');
-					});
-				} */
-
 				// Track when people actually are working.
-				// trackEvent.previewCount = (trackEvent.previewCount || 0) + 1;
-				// if (trackEvent.previewCount === 4) {
-				// trackEvent('fn', 'usingPreview');
-				// }
+				trackEvent.previewCount = (trackEvent.previewCount || 0) + 1;
+				if (trackEvent.previewCount === 4) {
+					trackEvent('fn', 'usingPreview');
+				}
 			}
 		}, this.updateDelay);
 	}
@@ -292,17 +290,26 @@ export default class ContentWrap extends Component {
 		this.codeInPreview.js = currentCode.js;
 	}
 	componentDidUpdate() {
+		this.refreshEditor();
+		// this.setPreviewContent(true);
+		// console.log('componentdidupdate', this.props.currentItem);
+	}
+	componentDidMount() {
+		this.props.onRef(this);
+	}
+	refreshEditor() {
+		console.log('ContentWrap refresh editor');
 		this.cmCodes.html = this.props.currentItem.html;
 		this.cmCodes.css = this.props.currentItem.css;
 		this.cmCodes.js = this.props.currentItem.js;
 		this.cm.html.setValue(this.cmCodes.html || '');
 		this.cm.css.setValue(this.cmCodes.css || '');
 		this.cm.js.setValue(this.cmCodes.js || '');
-		// this.setPreviewContent(true);
-		// console.log('componentdidupdate', this.props.currentItem);
-	}
-	componentDidMount() {
-		this.props.onRef(this);
+		this.cm.html.refresh();
+		this.cm.css.refresh();
+		this.cm.js.refresh();
+
+		this.setPreviewContent(true);
 	}
 	applyCodemirrorSettings(prefs) {
 		if (!this.cm) {
diff --git a/webmaker/src/components/Login.jsx b/webmaker/src/components/Login.jsx
index 5b73786..fe46299 100644
--- a/webmaker/src/components/Login.jsx
+++ b/webmaker/src/components/Login.jsx
@@ -9,18 +9,6 @@ export default class Login extends Component {
 		trackEvent('ui', 'loginProviderClick', provider);
 		auth.login(provider);
 	}
-	logout(e) {
-		if (this.unsavedEditCount) {
-			var shouldDiscard = confirm(
-				'You have unsaved changes. Do you still want to logout?'
-			);
-			if (!shouldDiscard) {
-				return;
-			}
-		}
-		trackEvent('fn', 'loggedOut');
-		auth.logout();
-	}
 	render() {
 		return (
 			<div>
diff --git a/webmaker/src/components/MainHeader.jsx b/webmaker/src/components/MainHeader.jsx
index 8dae8cb..9cf420a 100644
--- a/webmaker/src/components/MainHeader.jsx
+++ b/webmaker/src/components/MainHeader.jsx
@@ -9,7 +9,7 @@ export default class Header extends Component {
 					id="titleInput"
 					title="Click to edit"
 					class="item-title-input"
-					value="Untitled Work"
+					value={this.props.title}
 					onBlur={this.props.titleInputBlurHandler}
 				/>
 				<div class="main-header__btn-wrap  flex  flex-v-center">
@@ -46,7 +46,7 @@ export default class Header extends Component {
 					<a
 						class="flex  flex-v-center hint--rounded hint--bottom-left"
 						aria-label="Start a new creation"
-						d-click="onNewBtnClick"
+						onClick={this.props.newBtnHandler}
 					>
 						<svg
 							style="vertical-align:middle;width:14px;height:14px"
@@ -59,7 +59,7 @@ export default class Header extends Component {
 						id="saveBtn"
 						class={`flex  flex-v-center hint--rounded hint--bottom-left ${
 							this.props.isSaving ? 'is-loading' : ''
-						}`}
+						} ${this.props.unsavedEditCount ? 'is-marked' : 0}`}
 						aria-label="Save current creation (Ctrl/⌘ + S)"
 						onClick={this.props.saveBtnHandler}
 					>
diff --git a/webmaker/src/components/SavedItemPane.jsx b/webmaker/src/components/SavedItemPane.jsx
index a666516..e65bd82 100644
--- a/webmaker/src/components/SavedItemPane.jsx
+++ b/webmaker/src/components/SavedItemPane.jsx
@@ -5,6 +5,17 @@ export default class SavedItemPane extends Component {
 	onCloseIntent() {
 		this.props.closeHandler();
 	}
+	itemClickHandler(item) {
+		this.props.itemClickHandler(item);
+	}
+	itemRemoveBtnClickHandler(item, e) {
+		e.stopPropagation();
+		this.props.itemRemoveBtnClickHandler(item);
+	}
+	itemForkBtnClickHandler(item, e) {
+		e.stopPropagation();
+		this.props.itemForkBtnClickHandler(item);
+	}
 	render() {
 		return (
 			<div
@@ -60,17 +71,20 @@ export default class SavedItemPane extends Component {
 							<div
 								class="js-saved-item-tile saved-item-tile"
 								data-item-id={item.id}
+								onClick={this.itemClickHandler.bind(this, item)}
 							>
 								<div class="saved-item-tile__btns">
 									<a
 										class="js-saved-item-tile__fork-btn  saved-item-tile__btn hint--left hint--medium"
 										aria-label="Creates a duplicate of this creation (Ctrl/⌘ + F)"
+										onClick={this.itemForkBtnClickHandler.bind(this, item)}
 									>
 										Fork<span class="show-when-selected">(Ctrl/⌘ + F)</span>
 									</a>
 									<a
 										class="js-saved-item-tile__remove-btn  saved-item-tile__btn hint--left"
 										aria-label="Remove"
+										onClick={this.itemRemoveBtnClickHandler.bind(this, item)}
 									>
 										X
 									</a>
diff --git a/webmaker/src/components/app.jsx b/webmaker/src/components/app.jsx
index 31d5fc8..525ba24 100644
--- a/webmaker/src/components/app.jsx
+++ b/webmaker/src/components/app.jsx
@@ -21,6 +21,7 @@ import { alertsService } from '../notifications';
 import firebase from 'firebase/app';
 import 'firebase/auth';
 import Profile from './Profile';
+import { auth } from '../auth';
 
 if (module.hot) {
 	require('preact/debug');
@@ -30,6 +31,7 @@ const LocalStorageKeys = {
 	LOGIN_AND_SAVE_MESSAGE_SEEN: 'loginAndsaveMessageSeen',
 	ASKED_TO_IMPORT_CREATIONS: 'askedToImportCreations'
 };
+const UNSAVED_WARNING_COUNT = 15;
 
 export default class App extends Component {
 	constructor() {
@@ -71,7 +73,8 @@ export default class App extends Component {
 			preserveConsoleLogs: true,
 			lightVersion: false,
 			lineWrap: true,
-			infiniteLoopTimeout: 1000
+			infiniteLoopTimeout: 1000,
+			layoutMode: 2
 		};
 		this.prefs = {};
 
@@ -131,7 +134,7 @@ export default class App extends Component {
 			},
 			result => {
 				// this.toggleLayout(result.layoutMode);
-				this.prefs.layoutMode = result.layoutMode;
+				this.state.prefs.layoutMode = result.layoutMode;
 				if (result.code) {
 					lastCode = result.code;
 				}
@@ -174,7 +177,31 @@ export default class App extends Component {
 		}
 	}
 
-	refreshEditor() {}
+	refreshEditor() {
+		this.toggleLayout(
+			this.state.currentItem.layoutMode || this.state.prefs.layoutMode
+		);
+		// this.contentWrap.refreshEditor();
+	}
+	// Creates a new item with passed item's contents
+	forkItem(sourceItem) {
+		if (this.state.unsavedEditCount) {
+			var shouldDiscard = confirm(
+				'You have unsaved changes in your current work. Do you want to discard unsaved changes and continue?'
+			);
+			if (!shouldDiscard) {
+				return;
+			}
+		}
+		const fork = JSON.parse(JSON.stringify(sourceItem));
+		delete fork.id;
+		fork.title = '(Forked) ' + sourceItem.title;
+		fork.updatedOn = Date.now();
+		this.setCurrentItem(fork);
+		this.refreshEditor();
+		alertsService.add(`"${sourceItem.title}" was forked`);
+		trackEvent('fn', 'itemForked');
+	}
 	createNewItem() {
 		var d = new Date();
 		this.setCurrentItem({
@@ -196,8 +223,43 @@ export default class App extends Component {
 		this.refreshEditor();
 		alertsService.add('New item created');
 	}
+	openItem(item) {
+		// console.log(itemId, this.state.savedItems)
+
+		this.setCurrentItem(item);
+		this.refreshEditor();
+		alertsService.add('Saved item loaded');
+	}
+	removeItem(itemId) {
+		var answer = confirm(
+			`Are you sure you want to delete "${savedItems[itemId].title}"?`
+		);
+		if (!answer) {
+			return;
+		}
+
+		// Remove from items list
+		itemService.unsetItemForUser(itemId);
+
+		// Remove individual item too.
+		itemService.removeItem(itemId).then(() => {
+			alertsService.add('Item removed.');
+			// This item is open in the editor. Lets open a new one.
+			if (this.state.currentItem.id === itemId) {
+				this.createNewItem();
+			}
+		});
+
+		// Remove from cached list
+		delete this.state.savedItems[itemId];
+		this.setState({
+			savedItems: { ...this.state.savedItems }
+		});
+
+		trackEvent('fn', 'itemRemoved');
+	}
 	setCurrentItem(item) {
-		this.state.currentItem = item;
+		this.setState({ currentItem: item });
 		log('Current Item set', item);
 
 		// Reset auto-saving flag
@@ -373,8 +435,8 @@ export default class App extends Component {
 				d.getSeconds()
 			].join('-');
 
-			if (currentItem.title) {
-				fileName = currentItem.title;
+			if (this.state.currentItem.title) {
+				fileName = this.state.currentItem.title;
 			}
 			fileName += '.html';
 
@@ -440,8 +502,8 @@ export default class App extends Component {
 	}
 
 	layoutBtnClickHandler(layoutId) {
-		// saveSetting('layoutMode', mode);
-		trackEvent('ui', 'toggleLayoutClick', mode);
+		this.saveSetting('layoutMode', layoutId);
+		trackEvent('ui', 'toggleLayoutClick', layoutId);
 		this.toggleLayout(layoutId);
 	}
 	saveSetting(setting, value) {
@@ -454,8 +516,6 @@ export default class App extends Component {
 	}
 
 	saveCode(key) {
-		this.state.currentItem.title = window.titleInput.value;
-
 		// currentItem.htmlMode = htmlMode;
 		// currentItem.cssMode = cssMode;
 		// currentItem.jsMode = jsMode;
@@ -466,10 +526,6 @@ export default class App extends Component {
 		}
 		this.state.currentItem.updatedOn = Date.now();
 		this.state.currentItem.layoutMode = this.state.currentLayoutMode;
-		// this.state.currentItem.externalLibs = {
-		// 	js: externalJsTextarea.value,
-		// 	css: externalCssTextarea.value
-		// };
 
 		// currentItem.sizes = getCodePaneSizes();
 		// currentItem.mainSizes = getMainPaneSizes();
@@ -537,11 +593,28 @@ export default class App extends Component {
 			itemService.setItemForUser(this.state.currentItem.id);
 		}
 	}
-	onCodeChange(type, code) {
+	onCodeChange(type, code, isUserChange) {
 		this.state.currentItem[type] = code;
+		if (isUserChange) {
+			this.setState({ unsavedEditCount: this.state.unsavedEditCount + 1 });
+
+			if (
+				this.state.unsavedEditCount % UNSAVED_WARNING_COUNT === 0 &&
+				this.state.unsavedEditCount >= UNSAVED_WARNING_COUNT
+			) {
+				window.saveBtn.classList.add('animated');
+				window.saveBtn.classList.add('wobble');
+				window.saveBtn.addEventListener('animationend', () => {
+					window.saveBtn.classList.remove('animated');
+					window.saveBtn.classList.remove('wobble');
+				});
+			}
+		}
 	}
 
-	titleInputBlurHandler() {
+	titleInputBlurHandler(e) {
+		this.state.currentItem.title = e.target.value;
+
 		if (this.state.currentItem.id) {
 			this.saveItem();
 			trackEvent('ui', 'titleChanged');
@@ -622,9 +695,8 @@ export default class App extends Component {
 		this.setState({ isProfileModalOpen: true });
 	}
 
-	logout(e) {
-		e.preventDefault();
-		if (unsavedEditCount) {
+	logout() {
+		if (this.state.unsavedEditCount) {
 			var shouldDiscard = confirm(
 				'You have unsaved changes. Do you still want to logout?'
 			);
@@ -633,7 +705,36 @@ export default class App extends Component {
 			}
 		}
 		trackEvent('fn', 'loggedOut');
-		window.logout();
+		auth.logout();
+	}
+
+	itemClickHandler(item) {
+		setTimeout(() => {
+			this.openItem(item);
+		}, 350);
+		this.toggleSavedItemsPane();
+	}
+	itemRemoveBtnClickHandler(itemId) {
+		this.removeItem(itemId);
+	}
+	itemForkBtnClickHandler(item) {
+		this.toggleSavedItemsPane();
+		setTimeout(() => {
+			this.forkItem(item);
+		}, 350);
+	}
+	newBtnClickHandler() {
+		trackEvent('ui', 'newBtnClick');
+		if (this.state.unsavedEditCount) {
+			var shouldDiscard = confirm(
+				'You have unsaved changes. Do you still want to create something new?'
+			);
+			if (shouldDiscard) {
+				this.createNewItem();
+			}
+		} else {
+			this.createNewItem();
+		}
 	}
 
 	render() {
@@ -643,19 +744,24 @@ export default class App extends Component {
 					<MainHeader
 						externalLibCount={this.state.externalLibCount}
 						openBtnHandler={this.openSavedItemsPane.bind(this)}
+						newBtnHandler={this.newBtnClickHandler.bind(this)}
 						saveBtnHandler={this.saveBtnClickHandler.bind(this)}
 						loginBtnHandler={this.loginBtnClickHandler.bind(this)}
 						profileBtnHandler={this.profileBtnClickHandler.bind(this)}
 						addLibraryBtnHandler={this.openAddLibrary.bind(this)}
 						isFetchingItems={this.state.isFetchingItems}
 						isSaving={this.state.isSaving}
+						title={this.state.currentItem.title}
 						titleInputBlurHandler={this.titleInputBlurHandler.bind(this)}
 						user={this.state.user}
+						unsavedEditCount={this.state.unsavedEditCount}
 					/>
 					<ContentWrap
+						currentLayoutMode={this.state.currentLayoutMode}
 						currentItem={this.state.currentItem}
 						onCodeChange={this.onCodeChange.bind(this)}
 						onRef={comp => (this.contentWrap = comp)}
+						prefs={this.state.prefs}
 					/>
 					<div class="global-console-container" id="globalConsoleContainerEl" />
 					<Footer
@@ -674,6 +780,9 @@ export default class App extends Component {
 					items={this.state.savedItems}
 					isOpen={this.state.isSavedItemPaneOpen}
 					closeHandler={this.closeSavedItemsPane.bind(this)}
+					itemClickHandler={this.itemClickHandler.bind(this)}
+					itemRemoveBtnClickHandler={this.itemRemoveBtnClickHandler.bind(this)}
+					itemForkBtnClickHandler={this.itemForkBtnClickHandler.bind(this)}
 				/>
 				<div class="alerts-container" id="js-alerts-container" />
 				<form
@@ -735,7 +844,10 @@ export default class App extends Component {
 					show={this.state.isProfileModalOpen}
 					closeHandler={() => this.setState({ isProfileModalOpen: false })}
 				>
-					<Profile user={this.state.user} />
+					<Profile
+						user={this.state.user}
+						logoutBtnHandler={this.logout.bind(this)}
+					/>
 				</Modal>
 				<HelpModal
 					show={this.state.isHelpModalOpen}