Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the reorder functionality to storage in the side navigation #2665

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions browser/components/DraggableIcon.js
@@ -0,0 +1,8 @@
import React from 'react'
import { SortableHandle } from 'react-sortable-hoc'

const DraggableIcon = SortableHandle(({ className }) => (
<i className={`fa ${className}`} />
))

export default DraggableIcon
6 changes: 1 addition & 5 deletions browser/components/StorageItem.js
Expand Up @@ -6,11 +6,7 @@ import React from 'react'
import styles from './StorageItem.styl'
import CSSModules from 'browser/lib/CSSModules'
import _ from 'lodash'
import { SortableHandle } from 'react-sortable-hoc'

const DraggableIcon = SortableHandle(({ className }) => (
<i className={`fa ${className}`} />
))
import DraggableIcon from './DraggableIcon'

const FolderIcon = ({ className, color, isActive }) => {
const iconStyle = isActive ? 'fa-folder-open-o' : 'fa-folder-o'
Expand Down
1 change: 1 addition & 0 deletions browser/components/StorageItem.styl
Expand Up @@ -78,6 +78,7 @@
.folderList-item-reorder
font-size: 9px
padding: 10px 8px 10px 9px;
margin-left: 20px
color: rgba(147, 147, 149, 0.3)
cursor: ns-resize
&:before
Expand Down
9 changes: 8 additions & 1 deletion browser/main/SideNav/StorageItem.js
Expand Up @@ -7,6 +7,7 @@ import CreateFolderModal from 'browser/main/modals/CreateFolderModal'
import RenameFolderModal from 'browser/main/modals/RenameFolderModal'
import dataApi from 'browser/main/lib/dataApi'
import StorageItemChild from 'browser/components/StorageItem'
import DraggableIcon from 'browser/components/DraggableIcon'
import _ from 'lodash'
import { SortableElement } from 'react-sortable-hoc'
import i18n from 'browser/lib/i18n'
Expand Down Expand Up @@ -381,15 +382,21 @@ class StorageItem extends React.Component {
'$'
)
)
const StyledDraggableIcon = CSSModules(DraggableIcon, styles)

return (
<div styleName={isFolded ? 'root--folded' : 'root'} key={storage.key}>
<div
styleName={isActive ? 'header--active' : 'header'}
onContextMenu={e => this.handleHeaderContextMenu(e)}
>
{!isFolded && (
<StyledDraggableIcon className={styles['header-reorder']} />
)}
<button
styleName='header-toggleButton'
styleName={
isFolded ? 'header-toggleButton--folded' : 'header-toggleButton'
}
onMouseDown={e => this.handleToggleButtonClick(e)}
>
<img
Expand Down
22 changes: 18 additions & 4 deletions browser/main/SideNav/StorageItem.styl
Expand Up @@ -23,10 +23,21 @@
.header-addFolderButton
color #1EC38B

.header-reorder
font-size: 9px
position absolute
padding-left: 10px
padding-right: 10px
width: 10px
color: rgba(147, 147, 149, 0.3)
cursor: ns-resize
&:before
content: "\f142 \f142"

.header-toggleButton
navButtonColor()
position absolute
left 0
left 20px
width 25px
height 25px
padding 0
Expand All @@ -37,12 +48,16 @@
background-color alpha($ui-button-default--hover-backgroundColor, 40%)
color $ui-text-color

.header-toggleButton--folded
composes header-toggleButton
left 0

.header-info
navButtonColor()
display block
width 100%
height 36px
padding-left 25px
padding-left 45px
padding-right 15px
line-height 36px
cursor pointer
Expand Down Expand Up @@ -80,10 +95,9 @@
@extend .root
.header
width 100%
padding-left 5px
.header-info
overflow ellipsis
padding 0 0 0 18px
padding 0 0 0 23px
&:hover .header-info--folded-tooltip
opacity 1
.header-info-path
Expand Down
25 changes: 22 additions & 3 deletions browser/main/SideNav/index.js
Expand Up @@ -18,7 +18,7 @@ import PreferenceButton from './PreferenceButton'
import SearchButton from './SearchButton'
import ListButton from './ListButton'
import TagButton from './TagButton'
import { SortableContainer } from 'react-sortable-hoc'
import { SortableContainer, SortableElement } from 'react-sortable-hoc'
import i18n from 'browser/lib/i18n'
import context from 'browser/lib/context'
import { remote } from 'electron'
Expand Down Expand Up @@ -292,13 +292,21 @@ class SideNav extends React.Component {
}
}

onStorageSortEnd({ oldIndex, newIndex }) {
const { dispatch } = this.props
dataApi.reorderStorage(oldIndex, newIndex).then(data => {
dispatch({ type: 'REORDER_STORAGE', storages: data.storages })
})
}

SideNavComponent(isFolded) {
const { location, data, config, dispatch } = this.props
const { showSearch, searchText } = this.state

const isHomeActive = !!location.pathname.match(/^\/home$/)
const isStarredActive = !!location.pathname.match(/^\/starred$/)
const isTrashedActive = !!location.pathname.match(/^\/trashed$/)
const SortableStorageList = SortableContainer(StorageList)

let component

Expand All @@ -318,11 +326,17 @@ class SideNav extends React.Component {
})
}

let itemIndex = -1

const storageList = storageMap.map((storage, key) => {
const SortableStorageItem = SortableContainer(StorageItem)
const SortableStorageItem = SortableElement(
SortableContainer(StorageItem)
)
itemIndex++
return (
<SortableStorageItem
key={storage.key}
index={itemIndex}
storage={storage}
data={data}
location={location}
Expand Down Expand Up @@ -354,7 +368,12 @@ class SideNav extends React.Component {
)}
/>

<StorageList storageList={storageList} isFolded={isFolded} />
<SortableStorageList
storageList={storageList}
isFolded={isFolded}
onSortEnd={this.onStorageSortEnd.bind(this)}
useDragHandle
/>
<NavToggleButton
isFolded={isFolded}
handleToggleButtonClick={this.handleToggleButtonClick.bind(this)}
Expand Down
1 change: 1 addition & 0 deletions browser/main/lib/dataApi/index.js
Expand Up @@ -4,6 +4,7 @@ const dataApi = {
addStorage: require('./addStorage'),
renameStorage: require('./renameStorage'),
removeStorage: require('./removeStorage'),
reorderStorage: require('./reorderStorage'),
createFolder: require('./createFolder'),
updateFolder: require('./updateFolder'),
deleteFolder: require('./deleteFolder'),
Expand Down
32 changes: 32 additions & 0 deletions browser/main/lib/dataApi/reorderStorage.js
@@ -0,0 +1,32 @@
const _ = require('lodash')
_.move = require('lodash-move').default
const resolveStorageData = require('./resolveStorageData')

/**
* @param {Number} oldIndex
* @param {Number} newIndex
* @return {Object} Storage meta data
*/
function reorderStorage(oldIndex, newIndex) {
let rawStorages
try {
rawStorages = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(rawStorages)) throw new Error('invalid storages')
if (!_.isNumber(oldIndex)) throw new Error('oldIndex must be a number.')
if (!_.isNumber(newIndex)) throw new Error('newIndex must be a number.')
} catch (e) {
console.warn(e)
return Promise.reject(e)
}
return Promise.resolve(rawStorages).then(function moveSaveStorageToLocal() {
const resultStorage = _.move(rawStorages, oldIndex, newIndex)
localStorage.setItem('storages', JSON.stringify(resultStorage))
const resolvedStorage = resultStorage.map(function(storage) {
return resolveStorageData(storage)
})
return Promise.all(resolvedStorage).then(function(storages) {
return Promise.resolve({ storages })
})
})
}
module.exports = reorderStorage
2 changes: 1 addition & 1 deletion browser/main/modals/PreferencesModal/FolderItem.js
Expand Up @@ -320,7 +320,7 @@ class SortableFolderItemComponent extends React.Component {
return (
<div>
<DragHandle />
<StyledFolderItem {...this.props} />
<StyledFolderItem isHeader={false} {...this.props} />
</div>
)
}
Expand Down
4 changes: 2 additions & 2 deletions browser/main/modals/PreferencesModal/FolderItem.styl
@@ -1,15 +1,15 @@
.folderItem
height 35px
box-sizing border-box
padding 2.5px 15px
padding 2.5px 0
display flex
&:hover
background-color darken(white, 3%)

.folderItem-drag-handle
height 35px
border none
padding 0 10px
padding 0 15px 0 20px
line-height 35px
float left
cursor row-resize
Expand Down
23 changes: 23 additions & 0 deletions browser/main/modals/PreferencesModal/Handle.js
@@ -0,0 +1,23 @@
import React from 'react'
import PropTypes from 'prop-types'

class Handle extends React.Component {
render() {
const { isHeader, styles } = this.props
return (
<div
className={
styles[isHeader ? 'header-drag-handle' : 'folderItem-drag-handle']
}
>
<i className='fa fa-reorder' />
</div>
)
}
}

Handle.propTypes = {
isHeader: PropTypes.bool.isRequired
}

export default Handle
6 changes: 6 additions & 0 deletions browser/main/modals/PreferencesModal/StorageItem.js
Expand Up @@ -6,6 +6,8 @@ import consts from 'browser/lib/consts'
import dataApi from 'browser/main/lib/dataApi'
import { store } from 'browser/main/store'
import FolderList from './FolderList'
import Handle from './Handle'
import { SortableHandle } from 'react-sortable-hoc'
import i18n from 'browser/lib/i18n'

const { shell, remote } = require('electron')
Expand Down Expand Up @@ -104,10 +106,14 @@ class StorageItem extends React.Component {

render() {
const { storage, hostBoundingBox } = this.props
const StyledHandle = CSSModules(Handle, this.props.styles)

const SortableStorageHandle = SortableHandle(StyledHandle)

return (
<div styleName='root' key={storage.key}>
<div styleName='header'>
<SortableStorageHandle isHeader />
{this.state.isLabelEditing ? (
<div styleName='header-label--edit'>
<input
Expand Down
11 changes: 10 additions & 1 deletion browser/main/modals/PreferencesModal/StorageItem.styl
Expand Up @@ -5,11 +5,20 @@
.header
height 35px
line-height 30px
padding 0 10px 5px
padding 0 0 5px
box-sizing border-box
border-bottom $default-border
margin-bottom 5px
display flex
line-height 35px

.header-drag-handle
height 35px
border none
padding 0 10px
line-height 35px
float left
cursor row-resize

.header-label
cursor pointer
Expand Down
31 changes: 25 additions & 6 deletions browser/main/modals/PreferencesModal/StoragesTab.js
Expand Up @@ -5,6 +5,7 @@ import styles from './StoragesTab.styl'
import dataApi from 'browser/main/lib/dataApi'
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
import StorageItem from './StorageItem'
import { SortableContainer, SortableElement } from 'react-sortable-hoc'
import i18n from 'browser/lib/i18n'
import { humanFileSize } from 'browser/lib/utils'
import fs from 'fs'
Expand Down Expand Up @@ -45,6 +46,7 @@ class StoragesTab extends React.Component {
attachments: []
}
this.loadAttachmentStorage()
this.onSortEnd = this.onSortEnd.bind(this)
}

loadAttachmentStorage() {
Expand Down Expand Up @@ -94,6 +96,13 @@ class StoragesTab extends React.Component {
.catch(console.error)
}

onSortEnd({ oldIndex, newIndex }) {
const { dispatch } = this.props
dataApi.reorderStorage(oldIndex, newIndex).then(data => {
dispatch({ type: 'REORDER_STORAGE', storages: data.storages })
})
}

renderList() {
const { data, boundingBox } = this.props
const { attachments } = this.state
Expand Down Expand Up @@ -130,23 +139,33 @@ class StoragesTab extends React.Component {
if (!boundingBox) {
return null
}

let index = -1
const SortableStorageItem = SortableElement(StorageItem)
const storageList = data.storageMap.map(storage => {
index++
return (
<StorageItem
<SortableStorageItem
index={index}
key={storage.key}
storage={storage}
hostBoundingBox={boundingBox}
/>
)
})

const ListContent = ({ storageList }) => <div>{storageList}</div>
const SortableListContent = SortableContainer(ListContent)

return (
<div styleName='list'>
<div styleName='header'>{i18n.__('Storage Locations')}</div>
{storageList.length > 0 ? (
storageList
) : (
<div styleName='list-empty'>{i18n.__('No storage found.')}</div>
)}
<SortableListContent
helperClass='sortableItemHelper'
storageList={storageList}
onSortEnd={this.onSortEnd}
useDragHandle
/>
<div styleName='list-control'>
<button
styleName='list-control-addStorageButton'
Expand Down
7 changes: 7 additions & 0 deletions browser/main/store.js
Expand Up @@ -358,6 +358,13 @@ function data(state = defaultDataMap(), action) {
action.storage.isOpen = action.isOpen
state.storageMap.set(action.storage.key, action.storage)
return state
case 'REORDER_STORAGE':
state = Object.assign({}, state)
state.storageMap = new Map()
action.storages.forEach(storage => {
state.storageMap.set(storage.key, storage)
})
return state
}
return state
}
Expand Down