Skip to content

Instantly share code, notes, and snippets.

@loganpowell
Forked from puppybits/BankItem-v0.js
Last active October 16, 2018 19:03
Show Gist options
  • Save loganpowell/087fe422ebc90706dbf7d1644540050c to your computer and use it in GitHub Desktop.
Save loganpowell/087fe422ebc90706dbf7d1644540050c to your computer and use it in GitHub Desktop.
ClojureScript & React sample code
/* next add the state to ask security questions */
render: function(){
// Now add the security challenge state
var item = null;
switch(this.state.bankState){
case "CONNECTED":
break;
case "SECURITY":
// create a new element to show questions and get answers
item = el('BankItemSecurity', {
bank: this.props.bank,
challenges: this.state.securityChallenge,
onAnswered: this.state.securityPass
});
break;
case "AUTHENTICATION":
// create a new element to ask for username/password
item = el('BankItemAuthentication', {
bank:this.props.bank,
onConnect:this._link
});
break;
}
// creates the HTML to show the view
return el('div', null,
el('h2', null, bank.name),
item);
},
_link: function(username, password){
// when clojurescript is ready for the user/pass, it will call this function
var authenticate = function(request){
// return a promise that resolves right away with the user/pass
return new Promise(function(resolve){
resolve({username:username, password:password});
});
};
// if clojurescript needs to ask security questions, it will call this function
var challenge = function(request){
// get the questions and return a promise to clojurescript
return new Promise(function(resolve, reject){
this.setState({
state: "SECURITY",
securityChallenge: request.questions,
securityPass: resolve, // once the user gives the anwser, the resolve will send it to cljs
securityFail: reject
});
});
};
// call clojurescript and pass in the auth and chanllege and get a promise
this.context.triforce
.bank_link(authenticate, challenge)
.then(function(response){
// when clojurescript is successfully connected the bank, set the state and render the view
this.setState({
accounts: response,
state: "CONNECTED"
});
})
.catch(function(err){
this.setState({
securityChallenge: {},
securityPass: noop,
securityFail: noop});
});
}
/* at last we'll add the Connected state */
// when this element starts, we'll add a callback to clojurescript to know when anything changes on the accounts
componentWillMount: function(){
// simple filter function so we only get accounts for this bank
var myAccountsFilter = function(account){
return account.bankId === this.props.bank.id;
};
// whenever anything changes on any account, it will trigger this callback
var onChanged = function(accounts){
// if there are accounts for this bank then we know it's connected
var newState = {accounts: accounts};
if (accounts.length > 0){
newState.state = "CONNECTED";
}
// set the state and trigger the render function
this.setState({accounts: newState});
};
// pass our callback into clojurescript
this.context.triforce.accounts(myAccountsFilter, onChanged);
},
render: function(){
// when an account is checked/unchecked, send it to clojurescript
var changeActive = function(accountId, isActive){
// we could use the promise it returns, but it's better to just let the
// callback we created in the componentWillMount handle it
this.context.triforce.bank_account_active(accountId, isActive);
};
// Add the last state of connected to show all the accounts
var item = null;
switch(this.state.bankState){
case "CONNECTED":
// create a new element to show accounts
item = el('BankItemConnected', {
bank: this.props.bank,
accounts: this.state.accounts,
onActiveChange: changeActive
});
break;
case "SECURITY":
item = el('BankItemSecurity', {
bank: this.props.bank,
challenges: this.state.securityChallenge,
onAnswered: this.state.securityPass
});
break;
case "AUTHENTICATION":
item = el('BankItemAuthentication', {
bank:this.props.bank,
onConnect:this._link
});
break;
}
// creates the HTML to show the view
return el('div', null,
el('h2', null, bank.name),
item);
}
createView({
contextTypes: {
triforce: React.PropTypes.object.isRequired
},
getDefaultProps: function(){
return {bank: {}};
},
getInitialState: function(){
return {
state: "AUTHENTICATION",
accounts: [],
securityChallenge: {},
securityPass: noop,
securityFail: noop};
},
componentWillMount: function(){
var myAccountsFilter = function(account){
return account.bankId === this.props.bank.id;
};
var onChanged = function(accounts){
var newState = {accounts: accounts};
if (accounts.length > 0){
newState.state = "CONNECTED";
}
this.setState({accounts: newState});
};
this.context.triforce.accounts(myAccountsFilter, onChanged);
},
render: function(){
var changeActive = function(accountId, isActive){
this.context.triforce.bank_account_active(accountId, isActive);
};
var item = null;
switch(this.state.bankState){
case "CONNECTED":
item = el('BankItemConnected', {
bank: this.props.bank,
accounts: this.state.accounts,
onActiveChange: changeActive
});
break;
case "SECURITY":
item = el('BankItemSecurity', {
bank: this.props.bank,
challenges: this.state.securityChallenge,
onAnswered: this.state.securityPass
});
break;
case "AUTHENTICATION":
item = el('BankItemAuthentication', {
bank:this.props.bank,
onConnect:this._link
});
break;
}
return el('div', null,
el('h2', null, bank.name),
item);
},
_link: function(username, password){
var authenticate = function(request){
return new Promise(function(resolve){
resolve({username:username, password:password});
});
};
var challenge = function(request){
return new Promise(function(resolve, reject){
this.setState({
state: "SECURITY",
securityChallenge: request.questions,
securityPass: resolve,
securityFail: reject
});
});
};
this.context.triforce
.bank_link(authenticate, challenge)
.then(function(response){
this.setState({
accounts: response,
state: "CONNECTED"
});
})
.catch(function(err){
this.setState({
securityChallenge: {},
securityPass: noop,
securityFail: noop});
});
}
});
/* Create a basic class to show a list of banks */
var el = React.createElement;
var createView = React.createClass;
var noop = function(){};
createView({
// if the parent element doesn't pass a selectedIds, then use this instead
getDefaultProps: function(){
return {selectedIds: [1, 10]};
},
// when first created have an empty array that will hold the bank objects
getInitialState: function(){
return {banks: []};
},
// anytime the state changes (via this.setState()) it triggers this render
render: function(){
// this will create the HTML that will show the selected banks
return el('div', null,
el('h1', null, "Bank Link @ " + this.props.route.path),
el('a', {href:"/install"}, "Download App"));
}
});
/* connect the view to clojurescript to show the list of banks */
// triforce is our clojurescript library. It initializes once and "smart" components ask to use it like this:
contextTypes: {
triforce: React.PropTypes.object.isRequired
},
// componentWillMount is only called once, when this element is first created
componentWillMount: function(){
// get the list of banks from clojurescript, which returns a promise
this.context.triforce.bank_list().then(function(banks){
// when the promise completes, filter out only the banks selected
var selectedBanks = banks.filter(function(bank){
return this.props.selectedIds.indexOf(bank.id) > -1;
});
// tell React to change the state to have the complete bank objects
this.setState({banks: selectedBanks});
});
},
// anytime the state changes (via this.setState()) it triggers render
render: function(){
// for each bank object, create an element called BankItem
var bankItems = this.state.banks.map(function(bank, index){
// BankItem is the class we'll talk about next
return el('BankItem', {key:index, bank:bank});
});
// this will create the HTML that will show the selected banks
return el('div', null,
el('h1', null, "Bank Link @ " + this.props.route.path),
bankItems,
el('a', {href:"/install"}, "Download App"));
}
var el = React.createElement;
var createView = React.createClass;
createView({
contextTypes: {
triforce: React.PropTypes.object.isRequired,
},
getDefaultProps: function(){
return {selectedIds: [1, 10]};
},
getInitialState: function(){
return {banks: []};
},
componentWillMount: function(){
this.context.triforce.bank_list().then(function(banks){
var selectedBanks = banks.filter(function(bank){
return this.props.selectedIds.indexOf(bank.id) > -1;
});
this.setState({banks: selectedBanks});
});
},
render: function(){
var bankItems = this.state.banks.map(function(bank, index){
return el('BankItem', {key:index, bank:bank});
});
return el('div', null,
el('h1', null, "Bank Link @ " + this.props.route.path),
bankItems,
el('a', {href:"/install"}, "Download App"));
}
});
/* Basic element to show the state of a single bank connection */
createView({
contextTypes: {
triforce: React.PropTypes.object.isRequired
},
getDefaultProps: function(){
return {bank: {}};
},
getInitialState: function(){
return {
state: "AUTHENTICATION",
accounts: [],
securityChallenge: {},
securityPass: noop,
securityFail: noop};
},
render: function(){
// There are 3 possible states to bank linking,
// 1. asking for user/pass
// 2. needs additional security questions
// 3. connected bank and can choose to have account active or not
var item = null;
switch(this.state.bankState){
case "CONNECTED":
break;
case "SECURITY":
break;
case "AUTHENTICATION":
// create a new element to ask for username/password
item = el('BankItemAuthentication', {
bank:this.props.bank,
onConnect:this._link
});
break;
}
// creates the HTML to show the view
return el('div', null,
el('h2', null, bank.name),
item);
},
_link: function(username, password){
// when clojurescript is ready for the user/pass, it will call this function
var authenticate = function(request){
// return a promise that resolves right away with the user/pass
return new Promise(function(resolve){
resolve({username:username, password:password});
});
};
// call clojurescript and pass in the auth and chanllege and get a promise
this.context.triforce
.bank_link(authenticate)
.then(function(response){
// when clojurescript is successfully connected the bank, set the state and render the view
this.setState({
accounts: response,
state: "CONNECTED"
});
})
.catch(function(err){
this.setState({
securityChallenge: {},
securityPass: noop,
securityFail: noop});
});
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment