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:
@@ -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
|
||||||
|
@@ -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"
|
||||||
|
@@ -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>
|
||||||
|
@@ -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" />
|
||||||
|
@@ -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;
|
||||||
|
@@ -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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user