Skip to content

Commit

Permalink
feat(App): drag and drop adds file to CreateModal
Browse files Browse the repository at this point in the history
- adjust `CreateDataset` to take a filePath. We pass that filePath through our `validName` function to create a name that will work with Qri. When a filePath is given, we display all the values in the modal that we know about.

Eventually this modal will be removed in favor of desktop making smart assumptions, but before we can do that we need to understand how users can set or change those assumptions, ie the dataset name and default directory locations
  • Loading branch information
ramfox committed Nov 12, 2019
1 parent 2f5a954 commit a90e92b
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 24 deletions.
36 changes: 28 additions & 8 deletions app/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { Action } from 'redux'
import { CSSTransition } from 'react-transition-group'
import { HashRouter as Router } from 'react-router-dom'
import { ipcRenderer } from 'electron'
import path from 'path'

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faFileMedical } from '@fortawesome/free-solid-svg-icons'

// import components
import Toast from './Toast'
Expand All @@ -21,8 +25,9 @@ import RoutesContainer from '../containers/RoutesContainer'
// import models
import { ApiAction } from '../store/api'
import { Modal, ModalType, HideModal } from '../models/modals'
import { Toast as IToast, Selections } from '../models/store'
import { Toast as IToast, Selections, ToastType } from '../models/store'
import { Dataset } from '../models/dataset'
import { ToastTypes } from './chrome/Toast'

export const QRI_CLOUD_ROOT = 'https://qri.cloud'

Expand All @@ -43,6 +48,7 @@ export interface AppProps {
datasetDirPath: string
qriCloudAuthenticated: boolean
toast: IToast
openToast: (type: ToastType, message: string) => Action
modal: Modal
workingDataset: Dataset
exportPath: string
Expand Down Expand Up @@ -171,6 +177,7 @@ class App extends React.Component<AppProps, AppState> {
onSubmit={this.props.initDataset}
onDismissed={async () => setModal(noModalObject)}
setDatasetDirPath={this.props.setDatasetDirPath}
filePath={modal.bodyPath ? modal.bodyPath : ''}
/>
)
break
Expand Down Expand Up @@ -269,30 +276,41 @@ class App extends React.Component<AppProps, AppState> {
event.stopPropagation()
event.preventDefault()
this.setState({ showDragDrop: true })
console.log('enter')
}}
onDragOver={(event) => {
event.stopPropagation()
event.preventDefault()
this.setState({ showDragDrop: true })
console.log('over')
}}
onDragLeave={(event) => {
event.stopPropagation()
event.preventDefault()
this.setState({ showDragDrop: false })
console.log('leave')
}}
onDrop={(event) => {
this.setState({ showDragDrop: false })
console.log(event.dataTransfer.files[0].name)
event.preventDefault()
console.log('drop')
const ext = path.extname(event.dataTransfer.files[0].path)
this.props.closeToast()
if (!(ext === '.csv' || ext === '.json')) {
// open toast for 1 second
this.props.openToast(ToastTypes.error, 'unsupported file format: only json and csv supported')
setTimeout(() => this.props.closeToast(), 2500)
return
}
this.props.setModal({
type: ModalType.CreateDataset,
bodyPath: event.dataTransfer.files[0].path
})
}}
className='drag-drop'
id='drag-drop'
>
DRAG AND DROP!
<div className="inner">
<div className="spacer">Create a new dataset!</div>
<div className="icon"><FontAwesomeIcon size="5x" icon={faFileMedical} /></div>
</div>
<div className="footer">You can import csv and json files</div>
</div>
)
}
Expand All @@ -311,7 +329,9 @@ class App extends React.Component<AppProps, AppState> {
return (
<div className='drag'
onDragEnter={() => {
this.setState({ showDragDrop: true })
if (this.props.modal.type === ModalType.NoModal) {
this.setState({ showDragDrop: true })
}
}}
style={{
height: '100%',
Expand Down
31 changes: 17 additions & 14 deletions app/components/modals/CreateDataset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,29 @@ interface CreateDatasetProps {
onSubmit: (path: string, name: string, dir: string, mkdir: string) => Promise<ApiAction>
datasetDirPath: string
setDatasetDirPath: (path: string) => Action
filePath: string
}

const CreateDataset: React.FunctionComponent<CreateDatasetProps> = (props) => {
const {
onDismissed,
onSubmit,
datasetDirPath: persistedDatasetDirPath,
setDatasetDirPath: saveDatasetDirPath
setDatasetDirPath: saveDatasetDirPath,
filePath: givenFilePath
} = props
const [datasetName, setDatasetName] = React.useState('')

const validName = (name: string): string => {
// cast name to meet our specification
// make lower case, snakecase, and remove invalid characters
let coercedName = changeCase.lowerCase(name)
coercedName = changeCase.snakeCase(name)
return coercedName.replace(/^[^a-z0-9_]+$/g, '')
}

const [datasetDirPath, setDatasetDirPath] = React.useState(persistedDatasetDirPath)
const [filePath, setFilePath] = React.useState('')
const [filePath, setFilePath] = React.useState(givenFilePath)
const [datasetName, setDatasetName] = React.useState(validName(path.basename(filePath, path.extname(filePath))))

const [dismissable, setDismissable] = React.useState(true)
const [buttonDisabled, setButtonDisabled] = React.useState(true)
Expand Down Expand Up @@ -78,16 +89,6 @@ const CreateDataset: React.FunctionComponent<CreateDatasetProps> = (props) => {
}
}

const tryToSetValidName = (name: string) => {
// cast name to meet our specification
// make lower case, snakecase, and remove invalid characters
let coercedName = changeCase.lowerCase(name)
coercedName = changeCase.snakeCase(name)
coercedName = coercedName.replace(/^[^a-z0-9_]+$/g, '')

setDatasetName(coercedName)
}

const showFilePicker = () => {
const window = remote.getCurrentWindow()
const filePath: string[] | undefined = remote.dialog.showOpenDialog(window, {
Expand All @@ -102,7 +103,9 @@ const CreateDataset: React.FunctionComponent<CreateDatasetProps> = (props) => {
const basename = path.basename(selectedPath)
const name = basename.split('.')[0]

name && datasetName === '' && tryToSetValidName(name)
if (name && datasetName === '') {
setDatasetName(validName(name))
}

setFilePath(selectedPath)
const isDataset = isQriDataset(selectedPath)
Expand Down
2 changes: 2 additions & 0 deletions app/containers/AppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import {
acceptTOS,
setQriCloudAuthenticated,
openToast,
closeToast,
setModal,
setDatasetDirPath,
Expand Down Expand Up @@ -66,6 +67,7 @@ const AppContainer = connect(
addDataset: addDatasetAndFetch,
initDataset: initDatasetAndFetch,
linkDataset: linkDatasetAndFetch,
openToast,
closeToast,
pingApi,
setWorkingDataset,
Expand Down
4 changes: 3 additions & 1 deletion app/reducers/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ipcRenderer } from 'electron'
import store from '../utils/localStore'
import { apiActionTypes } from '../utils/actionType'
import { SAVE_SUCC, SAVE_FAIL } from '../reducers/mutations'
import { ModalType } from '../models/modals'

export const UI_TOGGLE_DATASET_LIST = 'UI_TOGGLE_DATASET_LIST'
export const UI_SET_SIDEBAR_WIDTH = 'UI_SET_SIDEBAR_WIDTH'
Expand Down Expand Up @@ -49,7 +50,8 @@ const initialState = {
blockMenus: true,
hideCommitNudge: store().getItem(hideCommitNudge) === 'true',
datasetDirPath: store().getItem('datasetDirPath') || '',
exportPath: store().getItem('exportPath') || ''
exportPath: store().getItem('exportPath') || '',
modal: { type: ModalType.NoModal }
}

// send an event to electron to block menus on first load
Expand Down
24 changes: 23 additions & 1 deletion app/scss/_drag-drop.scss
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
.drag-drop {
background: rgba($color: #000000, $alpha: .6);
background: rgba($color: #ffffff, $alpha: .8);
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
top: 0;
right: 0;
left: 0;
bottom: 0;

.inner {
width: 80%;
height: 70%;
border: .5rem dashed rgba($color: #000000, $alpha: .4);
border-radius: 5px;
display: flex;
flex-direction: column;
justify-content:center;
align-items: center;

.icon {
color: rgba($color: #000000, $alpha: .4);
}
}
.footer {
position: relative;
bottom: -5%;
background: white;
color: rgba($color: #000000, $alpha: 1);
}
}

0 comments on commit a90e92b

Please sign in to comment.