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