const React = require('react'); const { connect } = require('react-redux'); const shared = require('lib/components/shared/side-menu-shared.js'); const { Synchronizer } = require('lib/synchronizer.js'); const { BaseModel } = require('lib/base-model.js'); const { Folder } = require('lib/models/folder.js'); const { Note } = require('lib/models/note.js'); const { Tag } = require('lib/models/tag.js'); const { _ } = require('lib/locale.js'); const { themeStyle } = require('../theme.js'); const { bridge } = require('electron').remote.require('./bridge'); const Menu = bridge().Menu; const MenuItem = bridge().MenuItem; class SideBarComponent extends React.Component { style() { const theme = themeStyle(this.props.theme); const itemHeight = 25; let style = { root: { backgroundColor: theme.backgroundColor2, }, listItem: { height: itemHeight, fontFamily: theme.fontFamily, fontSize: theme.fontSize, textDecoration: 'none', boxSizing: 'border-box', color: theme.color2, paddingLeft: 14, display: 'flex', alignItems: 'center', cursor: 'default', opacity: 0.8, }, listItemSelected: { backgroundColor: theme.selectedColor2, }, conflictFolder: { color: theme.colorError2, fontWeight: 'bold', }, header: { height: itemHeight * 1.8, fontFamily: theme.fontFamily, fontSize: theme.fontSize * 1.3, textDecoration: 'none', boxSizing: 'border-box', color: theme.color2, paddingLeft: 8, display: 'flex', alignItems: 'center', }, button: { padding: 6, fontFamily: theme.fontFamily, fontSize: theme.fontSize, textDecoration: 'none', boxSizing: 'border-box', color: theme.color2, display: 'flex', alignItems: 'center', justifyContent: 'center', border: "1px solid rgba(255,255,255,0.2)", marginTop: 10, marginLeft: 5, marginRight: 5, cursor: 'default', }, syncReport: { fontFamily: theme.fontFamily, fontSize: Math.round(theme.fontSize * .9), color: theme.color2, opacity: .5, display: 'flex', alignItems: 'left', justifyContent: 'top', flexDirection: 'column', marginTop: 10, marginLeft: 5, marginRight: 5, minHeight: 70, }, }; return style; } itemContextMenu(event) { const itemId = event.target.getAttribute('data-id'); if (itemId === Folder.conflictFolderId()) return; const itemType = Number(event.target.getAttribute('data-type')); if (!itemId || !itemType) throw new Error('No data on element'); let deleteMessage = ''; if (itemType === BaseModel.TYPE_FOLDER) { deleteMessage = _('Delete notebook?'); } else if (itemType === BaseModel.TYPE_TAG) { deleteMessage = _('Remove this tag from all the notes?'); } else if (itemType === BaseModel.TYPE_SEARCH) { deleteMessage = _('Remove this search from the sidebar?'); } const menu = new Menu(); menu.append(new MenuItem({label: _('Delete'), click: async () => { const ok = bridge().showConfirmMessageBox(deleteMessage); if (!ok) return; if (itemType === BaseModel.TYPE_FOLDER) { await Folder.delete(itemId); } else if (itemType === BaseModel.TYPE_TAG) { await Tag.untagAll(itemId); } else if (itemType === BaseModel.TYPE_SEARCH) { this.props.dispatch({ type: 'SEARCH_DELETE', id: itemId, }); } }})) if (itemType === BaseModel.TYPE_FOLDER) { menu.append(new MenuItem({label: _('Rename'), click: async () => { this.props.dispatch({ type: 'WINDOW_COMMAND', name: 'renameNotebook', id: itemId, }); }})) } menu.popup(bridge().window()); } folderItem_click(folder) { this.props.dispatch({ type: 'FOLDER_SELECT', id: folder ? folder.id : null, }); } tagItem_click(tag) { this.props.dispatch({ type: 'TAG_SELECT', id: tag ? tag.id : null, }); } searchItem_click(search) { this.props.dispatch({ type: 'SEARCH_SELECT', id: search ? search.id : null, }); } async sync_click() { await shared.synchronize_press(this); } folderItem(folder, selected) { let style = Object.assign({}, this.style().listItem); if (selected) style = Object.assign(style, this.style().listItemSelected); if (folder.id === Folder.conflictFolderId()) style = Object.assign(style, this.style().conflictFolder); const onDragOver = (event, folder) => { if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') >= 0) event.preventDefault(); } const onDrop = async (event, folder) => { if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') < 0) return; event.preventDefault(); const noteIds = JSON.parse(event.dataTransfer.getData('text/x-jop-note-ids')); for (let i = 0; i < noteIds.length; i++) { await Note.moveToFolder(noteIds[i], folder.id); } } return { onDragOver(event, folder) } } onDrop={(event) => { onDrop(event, folder) } } href="#" data-id={folder.id} data-type={BaseModel.TYPE_FOLDER} onContextMenu={(event) => this.itemContextMenu(event)} key={folder.id} style={style} onClick={() => {this.folderItem_click(folder)}}>{folder.title} } tagItem(tag, selected) { let style = Object.assign({}, this.style().listItem); if (selected) style = Object.assign(style, this.style().listItemSelected); return this.itemContextMenu(event)} key={tag.id} style={style} onClick={() => {this.tagItem_click(tag)}}>{tag.title} } searchItem(search, selected) { let style = Object.assign({}, this.style().listItem); if (selected) style = Object.assign(style, this.style().listItemSelected); return this.itemContextMenu(event)} key={search.id} style={style} onClick={() => {this.searchItem_click(search)}}>{search.title} } makeDivider(key) { return
} makeHeader(key, label, iconName) { const style = this.style().header; const icon = return
{icon}{label}
} synchronizeButton(type) { const style = this.style().button; const iconName = type === 'sync' ? 'fa-refresh' : 'fa-times'; const label = type === 'sync' ? _('Synchronise') : _('Cancel'); const icon = return {this.sync_click()}}>{icon}{label} } render() { const theme = themeStyle(this.props.theme); const style = Object.assign({}, this.style().root, this.props.style, { overflowX: 'hidden', overflowY: 'auto', }); let items = []; items.push(this.makeHeader('folderHeader', _('Notebooks'), 'fa-folder-o')); if (this.props.folders.length) { const folderItems = shared.renderFolders(this.props, this.folderItem.bind(this)); items = items.concat(folderItems); } items.push(this.makeHeader('tagHeader', _('Tags'), 'fa-tags')); if (this.props.tags.length) { const tagItems = shared.renderTags(this.props, this.tagItem.bind(this)); items.push(
{tagItems}
); } if (this.props.searches.length) { items.push(this.makeHeader('searchHeader', _('Searches'), 'fa-search')); const searchItems = shared.renderSearches(this.props, this.searchItem.bind(this)); items.push(
{searchItems}
); } let lines = Synchronizer.reportToLines(this.props.syncReport); const syncReportText = []; for (let i = 0; i < lines.length; i++) { syncReportText.push(
{lines[i]}
); } items.push(this.synchronizeButton(this.props.syncStarted ? 'cancel' : 'sync')); items.push(
{syncReportText}
); return (
{items}
); } } const mapStateToProps = (state) => { return { folders: state.folders, tags: state.tags, searches: state.searches, syncStarted: state.syncStarted, syncReport: state.syncReport, selectedFolderId: state.selectedFolderId, selectedTagId: state.selectedTagId, selectedSearchId: state.selectedSearchId, notesParentType: state.notesParentType, locale: state.settings.locale, theme: state.settings.theme, }; }; const SideBar = connect(mapStateToProps)(SideBarComponent); module.exports = { SideBar };