mirror of
				https://github.com/mickael-kerjean/filestash.git
				synced 2025-11-01 02:43:35 +08:00 
			
		
		
		
	feature (prompt): proper prompt system
This commit is contained in:
		| @ -8,48 +8,61 @@ export class ModalPrompt extends React.Component { | |||||||
|     constructor(props){ |     constructor(props){ | ||||||
|         super(props); |         super(props); | ||||||
|         this.state = { |         this.state = { | ||||||
|             appear: false |             appear: false, | ||||||
|  |             value: '' | ||||||
|         }; |         }; | ||||||
|  |         this.onCancel = this.onCancel.bind(this); | ||||||
|  |         this.onSubmit = this.onSubmit.bind(this); | ||||||
|  |         this.onEscapeKeyPress = this.onEscapeKeyPress.bind(this); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     componentDidMount(){ |     componentDidMount(){ | ||||||
|         prompt.subscribe((text, okCallback, cancelCallback, type) => { |         prompt.subscribe((text, okCallback, cancelCallback, type) => { | ||||||
|             console.log("REQUEST FOR PROMPT"); |  | ||||||
|             this.setState({ |             this.setState({ | ||||||
|                 appear: true, |                 appear: true, | ||||||
|  |                 value: '', | ||||||
|                 error: null, |                 error: null, | ||||||
|                 type: type || 'text', |                 type: type || 'text', | ||||||
|                 text: text || '', |                 text: text || '', | ||||||
|                 fns: {ok: okCallback, cancel: cancelCallback} |                 fns: {ok: okCallback, cancel: cancelCallback} | ||||||
|             }); |             }); | ||||||
|         }); |         }); | ||||||
|  |         window.addEventListener('keydown', this.onEscapeKeyPress); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     componentDidUmount(){ | ||||||
|  |         window.removeEventListener('keydown', this.onEscapeKeyPress); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     onCancel(){ |     onCancel(){ | ||||||
|         this.setState({appear: false}); |         this.setState({appear: false}); | ||||||
|         this.state.fns.cancelCallback(); |         this.state.fns.cancel(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     onSubmit(e){ |     onSubmit(e){ | ||||||
|         e && e.preventDefault && e.preventDefault(); |         e && e.preventDefault && e.preventDefault(); | ||||||
|         this.state.fns.okCallback(this.state.value) |         this.state.fns.ok(this.state.value) | ||||||
|             .then(() => this.setState({appear: false})) |             .then(() => this.setState({appear: false})) | ||||||
|             .catch((message) => this.setState({error: message})); |             .catch((message) => this.setState({error: message})); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     onEscapeKeyPress(e){ | ||||||
|  |         if(e.keyCode === 27 && this.state.fns){ this.onCancel(); } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     render() { |     render() { | ||||||
|         return ( |         return ( | ||||||
|             <Modal isActive={this.state.appear} onQuit={this.onCancel.bind(this)}> |             <Modal isActive={this.state.appear} onQuit={this.onCancel}> | ||||||
|               <div className="component_prompt"> |               <div className="component_prompt"> | ||||||
|                 <p className="modal-message"> |                 <p className="modal-message"> | ||||||
|                   {this.state.text} |                   {this.state.text} | ||||||
|                 </p> |                 </p> | ||||||
|                 <form onSubmit={this.onSubmit.bind(this)}> |                 <form onSubmit={this.onSubmit}> | ||||||
|                 <Input autoFocus={true} value={this.state.value} type={this.state.type} autoComplete="new-password" onChange={(e) =>  this.setState({value: e.target.value})} /> |                 <Input autoFocus={true} value={this.state.value} type={this.state.type} autoComplete="new-password" onChange={(e) =>  this.setState({value: e.target.value})} /> | ||||||
|                   <div className="modal-error-message">{this.state.error} </div> |                   <div className="modal-error-message">{this.state.error} </div> | ||||||
|                   <div className="buttons"> |                   <div className="buttons"> | ||||||
|                     <Button type="button" onClick={this.onCancel.bind(this)}>CANCEL</Button> |                     <Button type="button" onClick={this.onCancel}>CANCEL</Button> | ||||||
|                     <Button type="submit" theme="secondary" onClick={this.onSubmit.bind(this)}>OK</Button> |                     <Button type="submit" theme="secondary" onClick={this.onSubmit}>OK</Button> | ||||||
|                   </div> |                   </div> | ||||||
|                 </form> |                 </form> | ||||||
|               </div> |               </div> | ||||||
|  | |||||||
| @ -1,20 +1,15 @@ | |||||||
| import { Observable } from 'rxjs/Observable'; |  | ||||||
|  |  | ||||||
| const Prompt = function (){ | const Prompt = function (){ | ||||||
|     let obs = null; |     let fn = null; | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|         emit: function(text, okCallback, cancelCallbck, type){ |         now: function(text, okCallback, cancelCallback, type){ | ||||||
|             console.log(obs); |             if(!fn){ return window.setTimeout(() => this.now(text, okCallback, cancelCallback, type), 50); } | ||||||
|             obs.emit(text, okCallback, cancelcallBack, type); |             fn(text, okCallback, cancelCallback, type); | ||||||
|         }, |         }, | ||||||
|         subscribe: function(){ |         subscribe: function(_fn){ | ||||||
|             console.log("> SUBSCRIBE") |             fn = _fn; | ||||||
|             return new Observable((_obs) => { |  | ||||||
|                 console.log(_obs); |  | ||||||
|                 obs = _obs; |  | ||||||
|             }); |  | ||||||
|         } |         } | ||||||
|     } |     }; | ||||||
| } | }; | ||||||
|  |  | ||||||
| export const prompt = new Prompt(); | export const prompt = new Prompt(); | ||||||
|  | |||||||
| @ -3,20 +3,32 @@ import PropTypes from 'prop-types'; | |||||||
| import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; | import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; | ||||||
|  |  | ||||||
| import { ModalPrompt } from '../../components/'; | import { ModalPrompt } from '../../components/'; | ||||||
| import { encrypt, decrypt, memory } from '../../helpers/'; | import { encrypt, decrypt, memory, prompt } from '../../helpers/'; | ||||||
|  |  | ||||||
| export class Credentials extends React.Component { | export class Credentials extends React.Component { | ||||||
|     constructor(props){ |     constructor(props){ | ||||||
|         super(props); |         super(props); | ||||||
|         const key = memory.get('credentials_key') || ''; |         const key = memory.get('credentials_key') || ''; // we use a clojure for the "key" because we | ||||||
|  |                                                          // want to persist it in memory, not just in the | ||||||
|  |                                                          // state which is kill whenever react decide | ||||||
|         this.state = { |         this.state = { | ||||||
|             modal_appear: key ? false : this.props.remember_me, |  | ||||||
|             key: key || '', |             key: key || '', | ||||||
|             message: '', |             message: '', | ||||||
|             error: null |             error: null | ||||||
|         }; |         }; | ||||||
|         // we use a clojure for the "key" because we want to persist it in memory |  | ||||||
|         // not just in the state which is kill whenever react decide |         if(this.props.remember_me === true){ | ||||||
|  |             if(key === ""){ | ||||||
|  |                 let raw = window.localStorage.hasOwnProperty('credentials'); | ||||||
|  |                 if(raw){ | ||||||
|  |                     this.promptForExistingPassword(); | ||||||
|  |                 }else{ | ||||||
|  |                     this.promptForNewPassword(); | ||||||
|  |                 } | ||||||
|  |             }else{ | ||||||
|  |                 this.hidrate_credentials(key); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     componentWillReceiveProps(new_props){ |     componentWillReceiveProps(new_props){ | ||||||
| @ -27,25 +39,58 @@ export class Credentials extends React.Component { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if(new_props.remember_me === true && this.props.remember_me === false){ |         if(new_props.remember_me === true && this.props.remember_me === false){ | ||||||
|             this.setState({modal_appear: true}); |             this.promptForNewPassword(); | ||||||
|             this.init(); |  | ||||||
|         }else if(new_props.remember_me === false && this.props.remember_me === true){ |         }else if(new_props.remember_me === false && this.props.remember_me === true){ | ||||||
|             memory.set('credentials_key', ''); |             memory.set('credentials_key', ''); | ||||||
|             this.setState({modal_appear: false, key: ''}); |             this.setState({key: ''}); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     componentDidMount(){ |     promptForExistingPassword(){ | ||||||
|         this.init(); |         prompt.now( | ||||||
|         if(this.state.key) this.onSubmit(this.state.key); |             "Your Master Password", | ||||||
|  |             (key) => { | ||||||
|  |                 if(!key.trim()) return Promise.reject("Password can\'t be empty"); | ||||||
|  |                 this.setState({key: key}); | ||||||
|  |                 memory.set('credentials_key', key); | ||||||
|  |                 return this.hidrate_credentials(key); | ||||||
|  |             }, | ||||||
|  |             () => { | ||||||
|  |                 memory.set('credentials_key', ''); | ||||||
|  |                 this.setState({key: ''}); | ||||||
|  |             }, | ||||||
|  |             'password' | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |     promptForNewPassword(){ | ||||||
|  |         prompt.now( | ||||||
|  |             "Pick a Master Password", | ||||||
|  |             (key) => { | ||||||
|  |                 if(!key.trim()) return Promise.reject("Password can\'t be empty"); | ||||||
|  |                 memory.set('credentials_key', key); | ||||||
|  |                 this.setState({key: key}, () => { | ||||||
|  |                     this.saveCreds(this.props.credentials); | ||||||
|  |                 }); | ||||||
|  |                 return Promise.resolve(); | ||||||
|  |             }, | ||||||
|  |             () => {}, | ||||||
|  |             'password' | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     init(){ |     hidrate_credentials(key){ | ||||||
|         let raw = window.localStorage.hasOwnProperty('credentials'); |         let raw = window.localStorage.getItem('credentials'); | ||||||
|         if(raw){ |         if(raw){ | ||||||
|             this.setState({message: "Your Master Password:"}); |             try{ | ||||||
|  |                 let credentials = decrypt(raw, key); | ||||||
|  |                 this.props.onCredentialsFound(credentials); | ||||||
|  |                 return Promise.resolve(); | ||||||
|  |             }catch(e){ | ||||||
|  |                 return Promise.reject('Incorrect password'); | ||||||
|  |             } | ||||||
|         }else{ |         }else{ | ||||||
|             this.setState({message: "Pick a Master Password:"}); |             this.saveCreds(this.props.credentials); | ||||||
|  |             return Promise.resolve(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -56,52 +101,7 @@ export class Credentials extends React.Component { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     onCancel(should_clear){ |  | ||||||
|         memory.set('credentials_key', ''); |  | ||||||
|         this.setState({modal_appear: false, key: ''}); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     onSubmit(key){ |  | ||||||
|         this.setState({key: key}); |  | ||||||
|         memory.set('credentials_key', key); |  | ||||||
|         /* |  | ||||||
|          * 2 differents use cases: |  | ||||||
|          * - a user is creating a new master password |  | ||||||
|          * - a user want to unlock existing credentials |  | ||||||
|          */ |  | ||||||
|         if(key !== ''){ |  | ||||||
|             let raw = window.localStorage.getItem('credentials'); |  | ||||||
|             if(raw){ |  | ||||||
|                 try{ |  | ||||||
|                     let credentials = decrypt(raw, key); |  | ||||||
|                     this.setState({modal_appear: false}); |  | ||||||
|                     this.props.onCredentialsFound(credentials); |  | ||||||
|                 }catch(e){ |  | ||||||
|                     this.setState({error: 'Incorrect password'}); |  | ||||||
|                 } |  | ||||||
|             }else{ |  | ||||||
|                 this.saveCreds(this.props.credentials); |  | ||||||
|                 this.setState({modal_appear: false}); |  | ||||||
|             } |  | ||||||
|         }else{ |  | ||||||
|             this.setState({error: 'Password can\'t be empty'}); |  | ||||||
|             window.setTimeout(() => this.setState({error: null}), 1500); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     render() { |     render() { | ||||||
|         return ( |         return null; | ||||||
|             <ModalPrompt |  | ||||||
|               type="password" |  | ||||||
|               appear={this.state.modal_appear} |  | ||||||
|               error={this.state.error} |  | ||||||
|               message={this.state.message} |  | ||||||
|               onCancel={this.onCancel.bind(this)} |  | ||||||
|               onSubmit={this.onSubmit.bind(this)} |  | ||||||
|               /> |  | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| Credentials.propTypes = { |  | ||||||
| }; |  | ||||||
|  | |||||||
| @ -100,8 +100,7 @@ export class ExistingThing extends React.Component { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     onDeleteRequest(filename){ |     onDeleteRequest(filename){ | ||||||
|         console.log(prompt); |         prompt.now( | ||||||
|         prompt.emit( |  | ||||||
|             "Confirm by tapping \""+this._confirm_delete_text()+"\"", |             "Confirm by tapping \""+this._confirm_delete_text()+"\"", | ||||||
|             (answer) => { // click on ok |             (answer) => { // click on ok | ||||||
|                 if(answer === this._confirm_delete_text()){ |                 if(answer === this._confirm_delete_text()){ | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ import React from 'react'; | |||||||
| import { BrowserRouter, Route, IndexRoute, Switch } from 'react-router-dom'; | import { BrowserRouter, Route, IndexRoute, Switch } from 'react-router-dom'; | ||||||
| import { NotFoundPage, ConnectPage, HomePage, LogoutPage, FilesPage, ViewerPage } from './pages/'; | import { NotFoundPage, ConnectPage, HomePage, LogoutPage, FilesPage, ViewerPage } from './pages/'; | ||||||
| import { Bundle, URL_HOME, URL_FILES, URL_VIEWER, URL_LOGIN, URL_LOGOUT } from  './helpers/'; | import { Bundle, URL_HOME, URL_FILES, URL_VIEWER, URL_LOGIN, URL_LOGOUT } from  './helpers/'; | ||||||
| import { ModalPrompt, Audio, Video } from './components/'; | import { ModalPrompt, Notification, Audio, Video } from './components/'; | ||||||
|  |  | ||||||
| export default class AppRouter extends React.Component { | export default class AppRouter extends React.Component { | ||||||
|     render() { |     render() { | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Mickael KERJEAN
					Mickael KERJEAN