1
0
mirror of https://github.com/chinchang/web-maker.git synced 2025-08-02 19:37:29 +02:00

start saving and fix savedItemspane

This commit is contained in:
Kushagra Gour
2018-06-01 10:50:40 +05:30
parent 2c1098d11c
commit ba9ee64f68
3 changed files with 186 additions and 29 deletions

View File

@@ -6,10 +6,11 @@ export default class Header extends Component {
<div class="main-header"> <div class="main-header">
<input <input
type="text" type="text"
id="js-title-input" id="titleInput"
title="Click to edit" title="Click to edit"
class="item-title-input" class="item-title-input"
value="Untitled Work" value="Untitled Work"
onBlur={this.props.titleInputBlurHandler}
/> />
<div class="main-header__btn-wrap flex flex-v-center"> <div class="main-header__btn-wrap flex flex-v-center">
<a <a
@@ -56,9 +57,11 @@ export default class Header extends Component {
</a> </a>
<a <a
id="saveBtn" id="saveBtn"
class="flex flex-v-center hint--rounded hint--bottom-left" class={`flex flex-v-center hint--rounded hint--bottom-left ${
this.props.isSaving ? 'is-loading' : ''
}`}
aria-label="Save current creation (Ctrl/⌘ + S)" aria-label="Save current creation (Ctrl/⌘ + S)"
d-click="onSaveBtnClick" onClick={this.props.saveBtnHandler}
> >
<svg <svg
style="vertical-align:middle;width:14px;height:14px" style="vertical-align:middle;width:14px;height:14px"
@@ -73,7 +76,9 @@ export default class Header extends Component {
</a> </a>
<a <a
id="openItemsBtn" id="openItemsBtn"
class="flex flex-v-center hint--rounded hint--bottom-left" class={`flex flex-v-center hint--rounded hint--bottom-left ${
this.props.isFetchingItems ? 'is-loading' : ''
}`}
aria-label="Open a saved creation (Ctrl/⌘ + O)" aria-label="Open a saved creation (Ctrl/⌘ + O)"
onClick={this.props.openBtnHandler} onClick={this.props.openBtnHandler}
> >

View File

@@ -1,4 +1,5 @@
import { h, Component } from 'preact'; import { h, Component } from 'preact';
import { getHumanDate } from '../utils';
export default class SavedItemPane extends Component { export default class SavedItemPane extends Component {
onCloseIntent() { onCloseIntent() {
@@ -19,7 +20,10 @@ export default class SavedItemPane extends Component {
</button> </button>
<div class="flex flex-v-center" style="justify-content: space-between;"> <div class="flex flex-v-center" style="justify-content: space-between;">
<h3> <h3>
My Library <span id="savedItemCountEl" /> My Library{' '}
<span id="savedItemCountEl">
{this.props.items ? this.props.items.length : 0}
</span>
</h3> </h3>
<div class="main-header__btn-wrap"> <div class="main-header__btn-wrap">
@@ -49,7 +53,38 @@ export default class SavedItemPane extends Component {
placeholder="Search your creations here..." placeholder="Search your creations here..."
/> />
<div id="js-saved-items-wrap" class="saved-items-pane__container" /> <div id="js-saved-items-wrap" class="saved-items-pane__container">
{this.props.items &&
this.props.items.length &&
this.props.items.map(item => (
<div
class="js-saved-item-tile saved-item-tile"
data-item-id={item.id}
>
<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)"
>
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"
>
X
</a>
</div>
<h3 class="saved-item-tile__title">{item.title}</h3>
<span class="saved-item-tile__meta">
Last updated: {getHumanDate(item.updatedOn)}
</span>
</div>
))}
{!(this.props.items && this.props.items.length) && (
<h2 class="opacity--30">Nothing saved here.</h2>
)}
</div>
</div> </div>
); );
} }

View File

@@ -8,12 +8,15 @@ import SavedItemPane from './SavedItemPane.jsx';
import AddLibrary from './AddLibrary.jsx'; import AddLibrary from './AddLibrary.jsx';
import Modal from './Modal.jsx'; import Modal from './Modal.jsx';
import HelpModal from './HelpModal.jsx'; import HelpModal from './HelpModal.jsx';
import { log } from '../utils'; import { log, generateRandomId } from '../utils';
import { itemService } from '../itemService'; import { itemService } from '../itemService';
import '../db'; import '../db';
import Notifications from './Notifications'; import Notifications from './Notifications';
import Settings from './Settings.jsx'; import Settings from './Settings.jsx';
import { modes, cssModes } from '../codeModes'; import { modes, cssModes } from '../codeModes';
import { trackEvent } from '../analytics';
import { deferred } from '../deferred';
import { alertsService } from '../notifications';
if (module.hot) { if (module.hot) {
require('preact/debug'); require('preact/debug');
@@ -150,8 +153,103 @@ export default class App extends Component {
this.setState({ unsavedEditCount: 0 }); this.setState({ unsavedEditCount: 0 });
// saveBtn.classList.remove('is-marked'); // saveBtn.classList.remove('is-marked');
} }
saveBtnClickHandler() {
trackEvent(
'ui',
'saveBtnClick',
this.state.currentItem.id ? 'saved' : 'new'
);
this.saveItem();
}
populateItemsInSavedPane(items) {
const savedItemsPane = $('#js-saved-items-pane');
// TODO: sort desc. by updation date
this.setState({
savedItems: items.sort(function(a, b) {
return b.updatedOn - a.updatedOn;
})
});
this.toggleSavedItemsPane();
// HACK: Set overflow after sometime so that the items can animate without getting cropped.
// setTimeout(() => $('#js-saved-items-wrap').style.overflowY = 'auto', 1000);
}
toggleSavedItemsPane(shouldOpen) {
// const savedItemsPane = $('#js-saved-items-pane');
this.setState({ isSavedItemPaneOpen: !this.state.isSavedItemPaneOpen });
if (this.state.isSavedItemPaneOpen) {
window.searchInput.focus();
} else {
window.searchInput.value = '';
// Give last focused editor, focus again
// if (editorWithFocus) {
// editorWithFocus.focus();
// }
}
document.body.classList[this.state.isSavedItemPaneOpen ? 'add' : 'remove'](
'overlay-visible'
);
}
/**
* Fetches all items from storage
* @param {boolean} shouldSaveGlobally Whether to store the fetched items in global arr for later use.
* @return {promise} Promise.
*/
async fetchItems(shouldSaveGlobally, shouldFetchLocally) {
var d = deferred();
this.state.savedItems = {};
var items = [];
if (window.user && !shouldFetchLocally) {
items = await itemService.getAllItems();
utils.log('got items');
if (shouldSaveGlobally) {
items.forEach(item => {
this.state.savedItems[item.id] = item;
});
}
d.resolve(items);
return d.promise;
}
db.local.get('items', result => {
var itemIds = Object.getOwnPropertyNames(result.items || {});
if (!itemIds.length) {
d.resolve([]);
}
trackEvent('fn', 'fetchItems', itemIds.length);
for (let i = 0; i < itemIds.length; i++) {
/* eslint-disable no-loop-func */
db.local.get(itemIds[i], itemResult => {
if (shouldSaveGlobally) {
this.state.savedItems[itemIds[i]] = itemResult[itemIds[i]];
}
items.push(itemResult[itemIds[i]]);
// Check if we have all items now.
if (itemIds.length === items.length) {
d.resolve(items);
}
});
/* eslint-enable no-loop-func */
}
});
return d.promise;
}
openSavedItemsPane() { openSavedItemsPane() {
this.setState({ isSavedItemPaneOpen: true }); this.setState({
isFetchingItems: true
});
this.fetchItems(true).then(items => {
this.setState({
isFetchingItems: false
});
this.populateItemsInSavedPane(items);
});
// this.setState({ isSavedItemPaneOpen: true });
} }
openAddLibrary() { openAddLibrary() {
this.setState({ isAddLibraryModalOpen: true }); this.setState({ isAddLibraryModalOpen: true });
@@ -168,8 +266,8 @@ export default class App extends Component {
// Ctrl/⌘ + S // Ctrl/⌘ + S
if ((event.ctrlKey || event.metaKey) && event.keyCode === 83) { if ((event.ctrlKey || event.metaKey) && event.keyCode === 83) {
event.preventDefault(); event.preventDefault();
// saveItem(); this.saveItem();
// trackEvent('ui', 'saveItemKeyboardShortcut'); trackEvent('ui', 'saveItemKeyboardShortcut');
} }
// Ctrl/⌘ + Shift + 5 // Ctrl/⌘ + Shift + 5
if ( if (
@@ -179,12 +277,12 @@ export default class App extends Component {
) { ) {
event.preventDefault(); event.preventDefault();
// scope.setPreviewContent(true, true); // scope.setPreviewContent(true, true);
// trackEvent('ui', 'previewKeyboardShortcut'); trackEvent('ui', 'previewKeyboardShortcut');
} else if ((event.ctrlKey || event.metaKey) && event.keyCode === 79) { } else if ((event.ctrlKey || event.metaKey) && event.keyCode === 79) {
// Ctrl/⌘ + O // Ctrl/⌘ + O
event.preventDefault(); event.preventDefault();
this.openSavedItemsPane(); this.openSavedItemsPane();
// trackEvent('ui', 'openCreationKeyboardShortcut'); trackEvent('ui', 'openCreationKeyboardShortcut');
} else if ( } else if (
(event.ctrlKey || event.metaKey) && (event.ctrlKey || event.metaKey) &&
event.shiftKey && event.shiftKey &&
@@ -193,7 +291,7 @@ export default class App extends Component {
// Ctrl/⌘ + Shift + ? // Ctrl/⌘ + Shift + ?
event.preventDefault(); event.preventDefault();
// scope.toggleModal(keyboardShortcutsModal); // scope.toggleModal(keyboardShortcutsModal);
// trackEvent('ui', 'showKeyboardShortcutsShortcut'); trackEvent('ui', 'showKeyboardShortcutsShortcut');
} else if (event.keyCode === 27) { } else if (event.keyCode === 27) {
this.closeAllOverlays(); this.closeAllOverlays();
} }
@@ -302,10 +400,8 @@ export default class App extends Component {
} }
saveCode(key) { saveCode(key) {
// this.currentItem.title = titleInput.value; this.state.currentItem.title = window.titleInput.value;
// currentItem.html = scope.cm.html.getValue();
// currentItem.css = scope.cm.css.getValue();
// currentItem.js = scope.cm.js.getValue();
// currentItem.htmlMode = htmlMode; // currentItem.htmlMode = htmlMode;
// currentItem.cssMode = cssMode; // currentItem.cssMode = cssMode;
// currentItem.jsMode = jsMode; // currentItem.jsMode = jsMode;
@@ -328,11 +424,11 @@ export default class App extends Component {
function onSaveComplete() { function onSaveComplete() {
if (window.user && !navigator.onLine) { if (window.user && !navigator.onLine) {
// alertsService.add( alertsService.add(
// 'Item saved locally. Will save to account when you are online.' 'Item saved locally. Will save to account when you are online.'
// ); );
} else { } else {
// alertsService.add('Item saved.'); alertsService.add('Item saved.');
} }
this.state.unsavedEditCount = 0; this.state.unsavedEditCount = 0;
// saveBtn.classList.remove('is-marked'); // saveBtn.classList.remove('is-marked');
@@ -340,7 +436,7 @@ export default class App extends Component {
return itemService return itemService
.setItem(key || this.state.currentItem.id, this.state.currentItem) .setItem(key || this.state.currentItem.id, this.state.currentItem)
.then(onSaveComplete); .then(onSaveComplete.bind(this));
} }
// Save current item to storage // Save current item to storage
@@ -361,29 +457,43 @@ export default class App extends Component {
} }
trackEvent('ui', LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN, 'local'); trackEvent('ui', LocalStorageKeys.LOGIN_AND_SAVE_MESSAGE_SEEN, 'local');
} }
var isNewItem = !currentItem.id; var isNewItem = !this.state.currentItem.id;
currentItem.id = currentItem.id || 'item-' + utils.generateRandomId(); this.state.currentItem.id =
saveBtn.classList.add('is-loading'); this.state.currentItem.id || 'item-' + generateRandomId();
this.setState({
isSaving: true
});
this.saveCode().then(() => { this.saveCode().then(() => {
saveBtn.classList.remove('is-loading'); this.setState({
isSaving: false
});
// TODO: May be setState with currentItem
// If this is the first save, and auto-saving settings is enabled, // If this is the first save, and auto-saving settings is enabled,
// then start auto-saving from now on. // then start auto-saving from now on.
// This is done in `saveCode()` completion so that the // This is done in `saveCode()` completion so that the
// auto-save notification overrides the `saveCode` function's notification. // auto-save notification overrides the `saveCode` function's notification.
if (!isAutoSavingEnabled && prefs.autoSave) { if (!this.isAutoSavingEnabled && this.state.prefs.autoSave) {
isAutoSavingEnabled = true; this.isAutoSavingEnabled = true;
alertsService.add('Auto-save enabled.'); alertsService.add('Auto-save enabled.');
} }
}); });
// Push into the items hash if its a new item being saved // Push into the items hash if its a new item being saved
if (isNewItem) { if (isNewItem) {
itemService.setItemForUser(currentItem.id); itemService.setItemForUser(this.state.currentItem.id);
} }
} }
onCodeChange(type, code) { onCodeChange(type, code) {
this.state.currentItem[type] = code; this.state.currentItem[type] = code;
} }
titleInputBlurHandler() {
if (this.state.currentItem.id) {
this.saveItem();
trackEvent('ui', 'titleChanged');
}
}
/** /**
* Handles all user triggered preference changes in the UI. * Handles all user triggered preference changes in the UI.
*/ */
@@ -458,7 +568,11 @@ export default class App extends Component {
<MainHeader <MainHeader
externalLibCount={this.state.externalLibCount} externalLibCount={this.state.externalLibCount}
openBtnHandler={this.openSavedItemsPane.bind(this)} openBtnHandler={this.openSavedItemsPane.bind(this)}
saveBtnHandler={this.saveBtnClickHandler.bind(this)}
addLibraryBtnHandler={this.openAddLibrary.bind(this)} addLibraryBtnHandler={this.openAddLibrary.bind(this)}
isFetchingItems={this.state.isFetchingItems}
isSaving={this.state.isSaving}
titleInputBlurHandler={this.titleInputBlurHandler.bind(this)}
/> />
<ContentWrap <ContentWrap
currentItem={this.state.currentItem} currentItem={this.state.currentItem}
@@ -479,6 +593,7 @@ export default class App extends Component {
</div> </div>
<SavedItemPane <SavedItemPane
items={this.state.savedItems}
isOpen={this.state.isSavedItemPaneOpen} isOpen={this.state.isSavedItemPaneOpen}
closeHandler={this.closeSavedItemsPane.bind(this)} closeHandler={this.closeSavedItemsPane.bind(this)}
/> />
@@ -537,6 +652,8 @@ export default class App extends Component {
closeHandler={() => this.setState({ isHelpModalOpen: false })} closeHandler={() => this.setState({ isHelpModalOpen: false })}
/> />
<div class="modal-overlay" />
<svg <svg
version="1.1" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"