mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-11-01 10:56:31 +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