1
0
mirror of https://github.com/chinchang/web-maker.git synced 2025-07-31 10:40:10 +02:00

removeitem, filtering items, infiniteloop warning ported

This commit is contained in:
Kushagra Gour
2018-06-10 03:28:49 +05:30
parent 80621132a1
commit 3518484568
6 changed files with 140 additions and 76 deletions

View File

@@ -22,11 +22,11 @@ export default class AskToImportModal extends Component {
them anytime on this browser. them anytime on this browser.
</p> </p>
<div class="flex flex-h-end"> <div class="flex flex-h-end">
<button d-click="dontAskToImportAnymore" class="btn"> <button onClick={this.props.dontAskBtnClickHandler} class="btn">
Don't ask me again Don't ask me again
</button> </button>
<button <button
d-click="importCreationsAndSettingsIntoApp" onClick={this.props.importBtnClickHandler}
class="btn btn--primary ml-1" class="btn btn--primary ml-1"
> >
Yes, please import Yes, please import

View File

@@ -17,7 +17,8 @@ export default class ContentWrap extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
isConsoleOpen: false isConsoleOpen: false,
isCssSettingsModalOpen: false
}; };
this.updateTimer = null; this.updateTimer = null;
this.updateDelay = 500; this.updateDelay = 500;
@@ -33,6 +34,7 @@ export default class ContentWrap extends Component {
window.onMessageFromConsole = this.onMessageFromConsole.bind(this); window.onMessageFromConsole = this.onMessageFromConsole.bind(this);
window.previewException = this.previewException.bind(this);
// `clearConsole` is on window because it gets called from inside iframe also. // `clearConsole` is on window because it gets called from inside iframe also.
window.clearConsole = this.clearConsole.bind(this); window.clearConsole = this.clearConsole.bind(this);
} }
@@ -285,14 +287,13 @@ export default class ContentWrap extends Component {
if (!this.cm) { if (!this.cm) {
return; return;
} }
$('#js-html-code').querySelector('.CodeMirror').style.fontSize = $( htmlCodeEl.querySelector(
'#js-css-code' '.CodeMirror'
).querySelector('.CodeMirror').style.fontSize = $( ).style.fontSize = cssCodeEl.querySelector(
'#js-js-code' '.CodeMirror'
).querySelector('.CodeMirror').style.fontSize = `${parseInt( ).style.fontSize = jsCodeEl.querySelector(
prefs.fontSize, '.CodeMirror'
10 ).style.fontSize = `${parseInt(prefs.fontSize, 10)}px`;
)}px`;
window.consoleEl.querySelector('.CodeMirror').style.fontSize = `${parseInt( window.consoleEl.querySelector('.CodeMirror').style.fontSize = `${parseInt(
prefs.fontSize, prefs.fontSize,
10 10
@@ -350,10 +351,11 @@ export default class ContentWrap extends Component {
updateCodeWrapCollapseStates() { updateCodeWrapCollapseStates() {
// This is debounced! // This is debounced!
clearTimeout(this.updateCodeWrapCollapseStates.timeout); clearTimeout(this.updateCodeWrapCollapseStates.timeout);
updateCodeWrapCollapseStates.timeout = setTimeout(function() { this.updateCodeWrapCollapseStates.timeout = setTimeout(() => {
const { currentLayoutMode } = this.props;
const prop = const prop =
currentLayoutMode === 2 || currentLayoutMode === 5 ? 'width' : 'height'; currentLayoutMode === 2 || currentLayoutMode === 5 ? 'width' : 'height';
[htmlCode, cssCode, jsCode].forEach(function(el) { [htmlCodeEl, cssCodeEl, jsCodeEl].forEach(function(el) {
const bounds = el.getBoundingClientRect(); const bounds = el.getBoundingClientRect();
const size = bounds[prop]; const size = bounds[prop];
if (size < 100) { if (size < 100) {
@@ -617,6 +619,11 @@ export default class ContentWrap extends Component {
/* eslint-enable no-param-reassign */ /* eslint-enable no-param-reassign */
} }
previewException(error) {
console.error('Possible infinite loop detected.', error.stack);
this.onMessageFromConsole('Possible infinite loop detected.', error.stack);
}
toggleConsole() { toggleConsole() {
this.setState({ isConsoleOpen: !this.state.isConsoleOpen }); this.setState({ isConsoleOpen: !this.state.isConsoleOpen });
trackEvent('ui', 'consoleToggle'); trackEvent('ui', 'consoleToggle');
@@ -690,14 +697,15 @@ export default class ContentWrap extends Component {
: 'vertical' : 'vertical'
} }
onDragStart={this.codeSplitDragStart.bind(this)} onDragStart={this.codeSplitDragStart.bind(this)}
onDragend={this.codeSplitDragEnd.bind(this)} onDragEnd={this.codeSplitDragEnd.bind(this)}
onSplit={splitInstance => (this.codeSplitInstance = splitInstance)} onSplit={splitInstance => (this.codeSplitInstance = splitInstance)}
> >
<div <div
data-code-wrap-id="0" data-code-wrap-id="0"
id="js-html-code" id="htmlCodeEl"
data-type="html" data-type="html"
class="code-wrap" class="code-wrap"
onTransitionEnd={this.updateCodeWrapCollapseStates.bind(this)}
> >
<div <div
class="js-code-wrap__header code-wrap__header" class="js-code-wrap__header code-wrap__header"
@@ -742,9 +750,10 @@ export default class ContentWrap extends Component {
</div> </div>
<div <div
data-code-wrap-id="1" data-code-wrap-id="1"
id="js-css-code" id="cssCodeEl"
data-type="css" data-type="css"
class="code-wrap" class="code-wrap"
onTransitionEnd={this.updateCodeWrapCollapseStates.bind(this)}
> >
<div <div
class="js-code-wrap__header code-wrap__header" class="js-code-wrap__header code-wrap__header"
@@ -804,9 +813,10 @@ export default class ContentWrap extends Component {
</div> </div>
<div <div
data-code-wrap-id="2" data-code-wrap-id="2"
id="js-js-code" id="jsCodeEl"
data-type="js" data-type="js"
class="code-wrap" class="code-wrap"
onTransitionEnd={this.updateCodeWrapCollapseStates.bind(this)}
> >
<div <div
class="js-code-wrap__header code-wrap__header" class="js-code-wrap__header code-wrap__header"

View File

@@ -5,6 +5,24 @@ import { itemService } from '../itemService';
import { alertsService } from '../notifications'; import { alertsService } from '../notifications';
export default class SavedItemPane extends Component { export default class SavedItemPane extends Component {
constructor(props) {
super(props);
this.items = [];
this.state = {
filteredItems: []
};
}
componentWillUpdate(nextProps) {
if (this.props.items !== nextProps.items) {
this.items = Object.values(nextProps.items);
this.items.sort(function(a, b) {
return b.updatedOn - a.updatedOn;
});
this.setState({
filteredItems: this.items
});
}
}
onCloseIntent() { onCloseIntent() {
this.props.closeHandler(); this.props.closeHandler();
} }
@@ -48,18 +66,15 @@ export default class SavedItemPane extends Component {
} }
if (isEnterKeyPressed && selectedItemElement) { if (isEnterKeyPressed && selectedItemElement) {
const item = this.props.items.filter( const item = this.props.items[selectedItemElement.dataset.itemId];
item => (item.id = selectedItemElement.dataset.itemId) console.log('opening', item);
)[0];
this.props.itemClickHandler(item); this.props.itemClickHandler(item);
} }
// Fork shortcut inside saved creations panel with Ctrl/⌘ + F // Fork shortcut inside saved creations panel with Ctrl/⌘ + F
if (isForkKeyPressed) { if (isForkKeyPressed) {
event.preventDefault(); event.preventDefault();
const item = this.props.items.filter( const item = this.props.items[selectedItemElement.dataset.itemId];
item => (item.id = selectedItemElement.dataset.itemId)
)[0];
this.props.itemForkBtnClickHandler(item); this.props.itemForkBtnClickHandler(item);
trackEvent('ui', 'forkKeyboardShortcut'); trackEvent('ui', 'forkKeyboardShortcut');
} }
@@ -147,6 +162,23 @@ export default class SavedItemPane extends Component {
e.preventDefault(); e.preventDefault();
} }
searchInputHandler(e) {
const text = e.target.value;
let el;
if (!text) {
this.setState({
filteredItems: this.items
});
} else {
this.setState({
filteredItems: this.items.filter(
item => item.title.toLowerCase().indexOf(text) !== -1
)
});
}
trackEvent('ui', 'searchInputType');
}
render() { render() {
return ( return (
<div <div
@@ -162,12 +194,7 @@ export default class SavedItemPane extends Component {
X X
</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 ({this.items.length})</h3>
My Library{' '}
<span id="savedItemCountEl">
{this.props.items ? this.props.items.length : 0}
</span>
</h3>
<div class="main-header__btn-wrap"> <div class="main-header__btn-wrap">
<a <a
@@ -192,42 +219,42 @@ export default class SavedItemPane extends Component {
type="" type=""
id="searchInput" id="searchInput"
class="search-input" class="search-input"
d-input="onSearchInputChange" onInput={this.searchInputHandler.bind(this)}
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.state.filteredItems.length &&
this.props.items.length && !this.items.length && <div class="mt-1">No match found.</div>}
this.props.items.map(item => ( {this.state.filteredItems.map(item => (
<div <div
class="js-saved-item-tile saved-item-tile" class="js-saved-item-tile saved-item-tile"
data-item-id={item.id} data-item-id={item.id}
onClick={this.itemClickHandler.bind(this, item)} onClick={this.itemClickHandler.bind(this, item)}
> >
<div class="saved-item-tile__btns"> <div class="saved-item-tile__btns">
<a <a
class="js-saved-item-tile__fork-btn saved-item-tile__btn hint--left hint--medium" 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)" aria-label="Creates a duplicate of this creation (Ctrl/⌘ + F)"
onClick={this.itemForkBtnClickHandler.bind(this, item)} onClick={this.itemForkBtnClickHandler.bind(this, item)}
> >
Fork<span class="show-when-selected">(Ctrl/ + F)</span> Fork<span class="show-when-selected">(Ctrl/ + F)</span>
</a> </a>
<a <a
class="js-saved-item-tile__remove-btn saved-item-tile__btn hint--left" class="js-saved-item-tile__remove-btn saved-item-tile__btn hint--left"
aria-label="Remove" aria-label="Remove"
onClick={this.itemRemoveBtnClickHandler.bind(this, item)} onClick={this.itemRemoveBtnClickHandler.bind(this, item)}
> >
X X
</a> </a>
</div>
<h3 class="saved-item-tile__title">{item.title}</h3>
<span class="saved-item-tile__meta">
Last updated: {getHumanDate(item.updatedOn)}
</span>
</div> </div>
))} <h3 class="saved-item-tile__title">{item.title}</h3>
{!(this.props.items && this.props.items.length) && ( <span class="saved-item-tile__meta">
Last updated: {getHumanDate(item.updatedOn)}
</span>
</div>
))}
{!this.items.length && (
<h2 class="opacity--30">Nothing saved here.</h2> <h2 class="opacity--30">Nothing saved here.</h2>
)} )}
</div> </div>

View File

@@ -54,12 +54,14 @@ export default class App extends Component {
isSavedItemPaneOpen: false, isSavedItemPaneOpen: false,
isModalOpen: false, isModalOpen: false,
isAddLibraryModalOpen: false, isAddLibraryModalOpen: false,
isSettingsModalOpen: false,
isHelpModalOpen: false, isHelpModalOpen: false,
isNotificationsModalOpen: false, isNotificationsModalOpen: false,
isLoginModalOpen: false, isLoginModalOpen: false,
isProfileModalOpen: false, isProfileModalOpen: false,
isSupportDeveloperModalOpen: false, isSupportDeveloperModalOpen: false,
isKeyboardShortcutsModalOpen: false, isKeyboardShortcutsModalOpen: false,
isAskToImportModalOpen: false,
prefs: {}, prefs: {},
currentItem: { currentItem: {
title: '', title: '',
@@ -269,28 +271,26 @@ export default class App extends Component {
this.setCurrentItem(item).then(() => this.refreshEditor()); this.setCurrentItem(item).then(() => this.refreshEditor());
alertsService.add('Saved item loaded'); alertsService.add('Saved item loaded');
} }
removeItem(itemId) { removeItem(item) {
var answer = confirm( var answer = confirm(`Are you sure you want to delete "${item.title}"?`);
`Are you sure you want to delete "${savedItems[itemId].title}"?`
);
if (!answer) { if (!answer) {
return; return;
} }
// Remove from items list // Remove from items list
itemService.unsetItemForUser(itemId); itemService.unsetItemForUser(item.id);
// Remove individual item too. // Remove individual item too.
itemService.removeItem(itemId).then(() => { itemService.removeItem(item.id).then(() => {
alertsService.add('Item removed.'); alertsService.add('Item removed.', item);
// This item is open in the editor. Lets open a new one. // This item is open in the editor. Lets open a new one.
if (this.state.currentItem.id === itemId) { if (this.state.currentItem.id === item.id) {
this.createNewItem(); this.createNewItem();
} }
}); });
// Remove from cached list // Remove from cached list
delete this.state.savedItems[itemId]; delete this.state.savedItems[item.id];
this.setState({ this.setState({
savedItems: { ...this.state.savedItems } savedItems: { ...this.state.savedItems }
}); });
@@ -327,9 +327,7 @@ export default class App extends Component {
const savedItemsPane = $('#js-saved-items-pane'); const savedItemsPane = $('#js-saved-items-pane');
// TODO: sort desc. by updation date // TODO: sort desc. by updation date
this.setState({ this.setState({
savedItems: items.sort(function(a, b) { savedItems: { ...this.state.savedItems }
return b.updatedOn - a.updatedOn;
})
}); });
this.toggleSavedItemsPane(); this.toggleSavedItemsPane();
@@ -727,8 +725,8 @@ export default class App extends Component {
}, 350); }, 350);
this.toggleSavedItemsPane(); this.toggleSavedItemsPane();
} }
itemRemoveBtnClickHandler(itemId) { itemRemoveBtnClickHandler(item) {
this.removeItem(itemId); this.removeItem(item);
} }
itemForkBtnClickHandler(item) { itemForkBtnClickHandler(item) {
this.toggleSavedItemsPane(); this.toggleSavedItemsPane();
@@ -860,6 +858,27 @@ export default class App extends Component {
this.openSupportDeveloperModal(e); this.openSupportDeveloperModal(e);
} }
/**
* Called from inside ask-to-import-modal
*/
dontAskToImportAnymore(e) {
this.setState({ isAskToImportModalOpen: false });
window.localStorage[LocalStorageKeys.ASKED_TO_IMPORT_CREATIONS] = true;
if (e) {
trackEvent('ui', 'dontAskToImportBtnClick');
}
}
/**
* Called from inside ask-to-import-modal
*/
importCreationsAndSettingsIntoApp() {
this.mergeImportedItems(this.oldSavedItems).then(() => {
trackEvent('fn', 'oldItemsImported');
this.dontAskToImportAnymore();
});
}
render() { render() {
return ( return (
<div> <div>
@@ -1013,6 +1032,10 @@ export default class App extends Component {
show={this.state.isAskToImportModalOpen} show={this.state.isAskToImportModalOpen}
closeHandler={() => this.setState({ isAskToImportModalOpen: false })} closeHandler={() => this.setState({ isAskToImportModalOpen: false })}
oldSavedCreationsCount={this.oldSavedCreationsCount} oldSavedCreationsCount={this.oldSavedCreationsCount}
importBtnClickHandler={this.importCreationsAndSettingsIntoApp.bind(
this
)}
dontAskBtnClickHandler={this.dontAskToImportAnymore.bind(this)}
/> />
<div class="modal-overlay" /> <div class="modal-overlay" />

View File

@@ -41,6 +41,10 @@ import {
} }
}, FAUX_DELAY); }, FAUX_DELAY);
/* eslint-enable consistent-return */ /* eslint-enable consistent-return */
},
remove: (key, cb) => {
window.localStorage.removeItem(key);
setTimeout(() => cb(), FAUX_DELAY);
} }
}; };
const dbLocalAlias = chrome && chrome.storage ? chrome.storage.local : local; const dbLocalAlias = chrome && chrome.storage ? chrome.storage.local : local;

View File

@@ -159,7 +159,7 @@ export const itemService = {
// When not logged in // When not logged in
if (!window.user) { if (!window.user) {
var d = deferred(); var d = deferred();
db.local.remove(id, d.resolve); window.db.local.remove(id, d.resolve);
return d.promise; return d.promise;
} }
const remoteDb = await window.db.getDb(); const remoteDb = await window.db.getDb();
@@ -211,7 +211,7 @@ export const itemService = {
}, },
function (result) { function (result) {
delete result.items[itemId]; delete result.items[itemId];
db.local.set({ window.db.local.set({
items: result.items items: result.items
}); });
} }