Skip to content

Commit

Permalink
feat: add router-based onboarding flow, user menu
Browse files Browse the repository at this point in the history
  • Loading branch information
chriswhong committed Aug 28, 2019
1 parent b022d1b commit f891790
Show file tree
Hide file tree
Showing 28 changed files with 531 additions and 377 deletions.
6 changes: 3 additions & 3 deletions app/actions/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function fetchWorkingDataset (): ApiActionThunk {
const action = {
type: 'dataset',
[CALL_API]: {
endpoint: 'dataset',
endpoint: '',
method: 'GET',
params: { fsi: isLinked },
segments: {
Expand Down Expand Up @@ -128,7 +128,7 @@ export function fetchCommitDataset (): ApiActionThunk {
const response = await dispatch({
type: 'commitdataset',
[CALL_API]: {
endpoint: 'dataset',
endpoint: '',
method: 'GET',
segments: {
peername: selections.peername,
Expand Down Expand Up @@ -386,7 +386,7 @@ export function initDataset (filepath: string, name: string, format: string): Ap
const action = {
type: 'init',
[CALL_API]: {
endpoint: 'init',
endpoint: 'init/',
method: 'POST',
params: {
filepath,
Expand Down
23 changes: 20 additions & 3 deletions app/actions/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function fetchSession (): ApiActionThunk {
const action = {
type: 'session',
[CALL_API]: {
endpoint: 'session',
endpoint: 'me',
method: 'GET',
map: (data: Record<string, any>): Session => {
return data as Session
Expand All @@ -20,9 +20,9 @@ export function fetchSession (): ApiActionThunk {
export function signup (username: string, email: string, password: string): ApiActionThunk {
return async (dispatch) => {
const action = {
type: 'signin',
type: 'signup',
[CALL_API]: {
endpoint: 'signin',
endpoint: 'registry/profile/new',
method: 'POST',
body: {
username,
Expand All @@ -34,3 +34,20 @@ export function signup (username: string, email: string, password: string): ApiA
return dispatch(action)
}
}

export function signin (username: string, password: string): ApiActionThunk {
return async (dispatch) => {
const action = {
type: 'signin',
[CALL_API]: {
endpoint: 'registry/profile/prove',
method: 'POST',
body: {
username,
password
}
}
}
return dispatch(action)
}
}
24 changes: 20 additions & 4 deletions app/actions/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import {
UI_TOGGLE_DATASET_LIST,
UI_SET_SIDEBAR_WIDTH,
UI_ACCEPT_TOS,
UI_SET_PEERNAME,
UI_SET_QRI_CLOUD_AUTHENTICATED,
UI_OPEN_TOAST,
UI_CLOSE_TOAST,
UI_SET_API_CONNECTION
UI_SET_API_CONNECTION,
UI_SET_MODAL,
UI_SIGNOUT
} from '../reducers/ui'

import { ToastType } from '../models/store'
import { Modal } from '../models/modals'

export const toggleDatasetList = () => {
return {
Expand All @@ -31,9 +34,9 @@ export const acceptTOS = () => {
}
}

export const setHasSignedUp = () => {
export const setQriCloudAuthenticated = () => {
return {
type: UI_SET_PEERNAME
type: UI_SET_QRI_CLOUD_AUTHENTICATED
}
}

Expand All @@ -50,9 +53,22 @@ export const closeToast = () => {
}
}

export const setModal = (modal: Modal) => {
return {
type: UI_SET_MODAL,
payload: modal
}
}

export const setApiConnection = (status: number) => {
return {
type: UI_SET_API_CONNECTION,
status
}
}

export const signout = () => {
return {
type: UI_SIGNOUT
}
}
101 changes: 35 additions & 66 deletions app/components/App.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import * as React from 'react'
import { Action } from 'redux'
import { CSSTransition } from 'react-transition-group'
import { HashRouter as Router } from 'react-router-dom'
import RoutesContainer from '../containers/RoutesContainer'

// import components
import Toast from './Toast'
import Onboard from './Onboard'
import AppError from './AppError'
import AppLoading from './AppLoading'
import NoDatasets from './NoDatasets'
import CreateDataset from './modals/CreateDataset'
import AddDataset from './modals/AddDataset'
import DatasetContainer from '../containers/DatasetContainer'

// import models
import { ApiAction } from '../store/api'
Expand All @@ -23,22 +22,23 @@ export interface AppProps {
sessionID: string
apiConnection?: number
hasAcceptedTOS: boolean
hasSignedUp: boolean
hasSignedIn: boolean
qriCloudAuthenticated: boolean
toast: IToast
modal: Modal
children: JSX.Element[] | JSX.Element
fetchSession: () => Promise<ApiAction>
fetchMyDatasets: (page?: number, pageSize?: number) => Promise<ApiAction>
addDataset: (peername: string, name: string) => Promise<ApiAction>
setWorkingDataset: (peername: string, name: string, isLinked: boolean) => Promise<ApiAction>
initDataset: (path: string, name: string, format: string) => Promise<ApiAction>
acceptTOS: () => Action
setHasSignedUp: () => Action
setHasSignedIn: () => Action
setQriCloudAuthenticated: () => Action
signup: (username: string, email: string, password: string) => Promise<ApiAction>
signin: (username: string, password: string) => Promise<ApiAction>
closeToast: () => Action
setApiConnection: (status: number) => Action
pingApi: () => Promise<ApiAction>
setModal: (modal: Modal) => Action
}

interface AppState {
Expand All @@ -57,7 +57,6 @@ export default class App extends React.Component<AppProps, AppState> {

this.setModal = this.setModal.bind(this)
this.renderModal = this.renderModal.bind(this)
this.renderNoDatasets = this.renderNoDatasets.bind(this)
this.renderAppLoading = this.renderAppLoading.bind(this)
this.renderAppError = this.renderAppError.bind(this)
}
Expand Down Expand Up @@ -87,12 +86,10 @@ export default class App extends React.Component<AppProps, AppState> {
}

private renderModal (): JSX.Element | null {
// Hide any dialogs while we're displaying an error
// if (errors) {
// return null
// }
const Modal = this.state.currentModal
const { modal, setModal } = this.props
const Modal = modal

if (!Modal) return null
return (
<div >
<CSSTransition
Expand All @@ -104,7 +101,7 @@ export default class App extends React.Component<AppProps, AppState> {
>
<CreateDataset
onSubmit={this.props.initDataset}
onDismissed={() => this.setState({ currentModal: NoModal })}
onDismissed={async () => setModal(NoModal)}
setWorkingDataset={this.props.setWorkingDataset}
fetchMyDatasets={this.props.fetchMyDatasets}
/>
Expand All @@ -118,7 +115,7 @@ export default class App extends React.Component<AppProps, AppState> {
>
<AddDataset
onSubmit={this.props.addDataset}
onDismissed={() => this.setState({ currentModal: NoModal })}
onDismissed={async () => setModal(NoModal)}
setWorkingDataset={this.props.setWorkingDataset}
fetchMyDatasets={this.props.fetchMyDatasets}
/>
Expand All @@ -127,20 +124,6 @@ export default class App extends React.Component<AppProps, AppState> {
)
}

private renderNoDatasets () {
return (
<CSSTransition
in={!this.props.hasDatasets}
classNames="fade"
component="div"
timeout={1000}
unmountOnExit
>
< NoDatasets setModal={this.setModal}/>
</CSSTransition>
)
}

private renderAppLoading () {
return (
<CSSTransition
Expand Down Expand Up @@ -175,44 +158,30 @@ export default class App extends React.Component<AppProps, AppState> {

render () {
const {
hasSignedUp,
hasSignedIn,
hasAcceptedTOS,
acceptTOS,
signup,
signin,
toast,
closeToast,
setHasSignedUp,
setHasSignedIn
closeToast
} = this.props
return (<div style={{
height: '100%',
position: 'relative',
overflow: 'hidden'
}}>
{this.renderAppLoading()}
{this.renderAppError()}
{this.renderModal()}
<Onboard
hasAcceptedTOS={hasAcceptedTOS}
hasSignedUp={hasSignedUp}
hasSignedIn={hasSignedIn}
setHasSignedUp={setHasSignedUp}
setHasSignedIn={setHasSignedIn}
signup={signup}
signin={signin}
acceptTOS={acceptTOS}
/>
{this.renderNoDatasets()}
{ this.props.hasDatasets && <DatasetContainer setModal={this.setModal}/> }
<Toast
type={toast.type}
message={toast.message}
isVisible={toast.visible}
timeout={3000}
onClose={closeToast}
/>
</div>)

return (
<div style={{
height: '100%',
position: 'relative',
overflow: 'hidden'
}}>
{this.renderAppLoading()}
{this.renderAppError()}
{this.renderModal()}
<Router>
<RoutesContainer />
</Router>
<Toast
type={toast.type}
message={toast.message}
isVisible={toast.visible}
timeout={3000}
onClose={closeToast}
/>
</div>
)
}
}
26 changes: 18 additions & 8 deletions app/components/ComponentList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as React from 'react'
import { Action } from 'redux'
import classNames from 'classnames'
import { DatasetStatus, ComponentType } from '../models/store'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faTags, faArchive, faTh, IconDefinition } from '@fortawesome/free-solid-svg-icons'

interface StatusDotProps {
status: string | undefined
Expand Down Expand Up @@ -34,6 +36,7 @@ export const StatusDot: React.FunctionComponent<StatusDotProps> = (props) => {
interface FileRowProps {
name: string
displayName: string
icon?: IconDefinition
filename?: string
selected?: boolean
status?: string
Expand All @@ -56,6 +59,9 @@ export const FileRow: React.FunctionComponent<FileRowProps> = (props) => (
}}
data-tip={props.tooltip}
>
{props.icon && (<div className='icon-column'>
<FontAwesomeIcon icon={props.icon} size='sm'/>
</div>)}
<div className='text-column'>
<div className='text'>{props.displayName}</div>
<div className='subtext'>{props.filename}</div>
Expand All @@ -80,23 +86,25 @@ const components = [
{
name: 'meta',
displayName: 'Meta',
tooltip: 'title, description, tags, etc'
tooltip: 'title, description, tags, etc',
icon: faTags
},
{
name: 'body',
displayName: 'Body',
tooltip: "the dataset's content"
tooltip: 'the data',
icon: faArchive
},
{
name: 'schema',
displayName: 'Schema',
tooltip: 'the structure of the dataset'
tooltip: 'the structure of the dataset',
icon: faTh
}
]

export const getComponentDisplayName = (name: string) => {
const match = components.find(d => d.name === name)
return match && match.displayName
export const getComponentDisplayProps = (name: string) => {
return components.filter(d => d.name === name)[0]
}

const ComponentList: React.FunctionComponent<ComponentListProps> = (props: ComponentListProps) => {
Expand All @@ -111,10 +119,10 @@ const ComponentList: React.FunctionComponent<ComponentListProps> = (props: Compo
return (
<div>
<div className='sidebar-list-item sidebar-list-item-text sidebar-list-header'>
Dataset Components
Dataset Components
</div>
{
components.map(({ name, displayName, tooltip }) => {
components.map(({ name, displayName, tooltip, icon }) => {
if (status[name]) {
const { filepath, status: fileStatus } = status[name]
let filename
Expand All @@ -129,6 +137,7 @@ const ComponentList: React.FunctionComponent<ComponentListProps> = (props: Compo
key={name}
displayName={displayName}
name={name}
icon={icon}
filename={isLinked ? filename : ''}
status={fileStatus}
selected={selectedComponent === name}
Expand All @@ -143,6 +152,7 @@ const ComponentList: React.FunctionComponent<ComponentListProps> = (props: Compo
key={name}
displayName={displayName}
name={displayName}
icon={icon}
disabled={true}
tooltip={tooltip}
/>
Expand Down
Loading

0 comments on commit f891790

Please sign in to comment.