mirror of
				https://github.com/mickael-kerjean/filestash.git
				synced 2025-10-31 18:16:00 +08:00 
			
		
		
		
	feature (prompt): proper prompt system
This commit is contained in:
		| @ -8,48 +8,61 @@ export class ModalPrompt extends React.Component { | ||||
|     constructor(props){ | ||||
|         super(props); | ||||
|         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(){ | ||||
|         prompt.subscribe((text, okCallback, cancelCallback, type) => { | ||||
|             console.log("REQUEST FOR PROMPT"); | ||||
|             this.setState({ | ||||
|                 appear: true, | ||||
|                 value: '', | ||||
|                 error: null, | ||||
|                 type: type || 'text', | ||||
|                 text: text || '', | ||||
|                 fns: {ok: okCallback, cancel: cancelCallback} | ||||
|             }); | ||||
|         }); | ||||
|         window.addEventListener('keydown', this.onEscapeKeyPress); | ||||
|     } | ||||
|  | ||||
|     componentDidUmount(){ | ||||
|         window.removeEventListener('keydown', this.onEscapeKeyPress); | ||||
|     } | ||||
|  | ||||
|     onCancel(){ | ||||
|         this.setState({appear: false}); | ||||
|         this.state.fns.cancelCallback(); | ||||
|         this.state.fns.cancel(); | ||||
|     } | ||||
|  | ||||
|     onSubmit(e){ | ||||
|         e && e.preventDefault && e.preventDefault(); | ||||
|         this.state.fns.okCallback(this.state.value) | ||||
|         this.state.fns.ok(this.state.value) | ||||
|             .then(() => this.setState({appear: false})) | ||||
|             .catch((message) => this.setState({error: message})); | ||||
|     } | ||||
|  | ||||
|     onEscapeKeyPress(e){ | ||||
|         if(e.keyCode === 27 && this.state.fns){ this.onCancel(); } | ||||
|     } | ||||
|  | ||||
|     render() { | ||||
|         return ( | ||||
|             <Modal isActive={this.state.appear} onQuit={this.onCancel.bind(this)}> | ||||
|             <Modal isActive={this.state.appear} onQuit={this.onCancel}> | ||||
|               <div className="component_prompt"> | ||||
|                 <p className="modal-message"> | ||||
|                   {this.state.text} | ||||
|                 </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})} /> | ||||
|                   <div className="modal-error-message">{this.state.error} </div> | ||||
|                   <div className="buttons"> | ||||
|                     <Button type="button" onClick={this.onCancel.bind(this)}>CANCEL</Button> | ||||
|                     <Button type="submit" theme="secondary" onClick={this.onSubmit.bind(this)}>OK</Button> | ||||
|                     <Button type="button" onClick={this.onCancel}>CANCEL</Button> | ||||
|                     <Button type="submit" theme="secondary" onClick={this.onSubmit}>OK</Button> | ||||
|                   </div> | ||||
|                 </form> | ||||
|               </div> | ||||
|  | ||||
| @ -1,20 +1,15 @@ | ||||
| import { Observable } from 'rxjs/Observable'; | ||||
|  | ||||
| const Prompt = function (){ | ||||
|     let obs = null; | ||||
|     let fn = null; | ||||
|  | ||||
|     return { | ||||
|         emit: function(text, okCallback, cancelCallbck, type){ | ||||
|             console.log(obs); | ||||
|             obs.emit(text, okCallback, cancelcallBack, type); | ||||
|         now: function(text, okCallback, cancelCallback, type){ | ||||
|             if(!fn){ return window.setTimeout(() => this.now(text, okCallback, cancelCallback, type), 50); } | ||||
|             fn(text, okCallback, cancelCallback, type); | ||||
|         }, | ||||
|         subscribe: function(){ | ||||
|             console.log("> SUBSCRIBE") | ||||
|             return new Observable((_obs) => { | ||||
|                 console.log(_obs); | ||||
|                 obs = _obs; | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|         subscribe: function(_fn){ | ||||
|             fn = _fn; | ||||
|         } | ||||
|     }; | ||||
| }; | ||||
|  | ||||
| export const prompt = new Prompt(); | ||||
|  | ||||
| @ -3,20 +3,32 @@ import PropTypes from 'prop-types'; | ||||
| import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; | ||||
|  | ||||
| import { ModalPrompt } from '../../components/'; | ||||
| import { encrypt, decrypt, memory } from '../../helpers/'; | ||||
| import { encrypt, decrypt, memory, prompt } from '../../helpers/'; | ||||
|  | ||||
| export class Credentials extends React.Component { | ||||
|     constructor(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 = { | ||||
|             modal_appear: key ? false : this.props.remember_me, | ||||
|             key: key || '', | ||||
|             message: '', | ||||
|             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){ | ||||
| @ -27,25 +39,58 @@ export class Credentials extends React.Component { | ||||
|         } | ||||
|  | ||||
|         if(new_props.remember_me === true && this.props.remember_me === false){ | ||||
|             this.setState({modal_appear: true}); | ||||
|             this.init(); | ||||
|             this.promptForNewPassword(); | ||||
|         }else if(new_props.remember_me === false && this.props.remember_me === true){ | ||||
|             memory.set('credentials_key', ''); | ||||
|             this.setState({modal_appear: false, key: ''}); | ||||
|             this.setState({key: ''}); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     componentDidMount(){ | ||||
|         this.init(); | ||||
|         if(this.state.key) this.onSubmit(this.state.key); | ||||
|     promptForExistingPassword(){ | ||||
|         prompt.now( | ||||
|             "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(){ | ||||
|         let raw = window.localStorage.hasOwnProperty('credentials'); | ||||
|     hidrate_credentials(key){ | ||||
|         let raw = window.localStorage.getItem('credentials'); | ||||
|         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{ | ||||
|             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() { | ||||
|         return ( | ||||
|             <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)} | ||||
|               /> | ||||
|         ); | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
|  | ||||
| Credentials.propTypes = { | ||||
| }; | ||||
|  | ||||
| @ -100,8 +100,7 @@ export class ExistingThing extends React.Component { | ||||
|     } | ||||
|  | ||||
|     onDeleteRequest(filename){ | ||||
|         console.log(prompt); | ||||
|         prompt.emit( | ||||
|         prompt.now( | ||||
|             "Confirm by tapping \""+this._confirm_delete_text()+"\"", | ||||
|             (answer) => { // click on ok | ||||
|                 if(answer === this._confirm_delete_text()){ | ||||
|  | ||||
| @ -2,7 +2,7 @@ import React from 'react'; | ||||
| import { BrowserRouter, Route, IndexRoute, Switch } from 'react-router-dom'; | ||||
| import { NotFoundPage, ConnectPage, HomePage, LogoutPage, FilesPage, ViewerPage } from './pages/'; | ||||
| 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 { | ||||
|     render() { | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Mickael KERJEAN
					Mickael KERJEAN