This commit is contained in:
Mo Bitar
2020-04-17 16:41:51 -05:00
parent 8050c9cccd
commit 851269200b
15 changed files with 521 additions and 487 deletions

View File

@@ -0,0 +1,36 @@
.sk-modal-background(ng-click="ctrl.cancel()")
#privileges-modal.sk-modal-content
.sn-component
.sk-panel
.sk-panel-header
.sk-panel-header-title Authentication Required
a.close-button.info(
ng-if="ctrl.cancelable"
ng-click="ctrl.cancel()"
) Cancel
.sk-panel-content
.sk-panel-section
div(ng-repeat="type in ctrl.state.types")
.sk-p.sk-bold.sk-panel-row
strong {{ctrl.promptForChallenge(type)}}
.sk-panel-row
input.sk-input.contrast(
ng-model="ctrl.state.values[type].value"
should-focus="$index == 0"
sn-autofocus="true"
sn-enter="ctrl.submit()" ,
ng-change="ctrl.onTextValueChange(type)"
type="password"
)
.sk-panel-row
label.sk-label.danger(
ng-if="ctrl.state.values[type].invalid"
) Invalid authentication. Please try again.
.sk-panel-row
.sk-panel-footer.extra-padding
.sk-button.info.big.block.bold(
ng-click="ctrl.submit()",
ng-class="{'info' : !ctrl.state.processing, 'neutral': ctrl.state.processing}"
ng-disabled="ctrl.state.processing"
)
.sk-label {{ctrl.state.processing ? 'Generating Keys...' : 'Submit'}}

View File

@@ -0,0 +1,169 @@
import { WebApplication } from '@/ui_models/application';
import template from './challenge-modal.pug';
import {
ChallengeType,
ChallengeValue,
removeFromArray,
Challenge,
ChallengeOrchestrator
} from 'snjs';
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
import { WebDirective } from '@/types';
type InputValue = {
value: string
invalid: boolean
}
type ChallengeModalScope = {
application: WebApplication
challenge: Challenge
orchestrator: ChallengeOrchestrator
}
type Values = Record<ChallengeType, InputValue>
type ChallengeModalState = {
types: ChallengeType[]
values: Partial<Values>
processing: boolean
}
class ChallengeModalCtrl extends PureViewCtrl implements ChallengeModalScope {
private $element: JQLite
private processingTypes: ChallengeType[] = []
application!: WebApplication
challenge!: Challenge
orchestrator!: ChallengeOrchestrator
/* @ngInject */
constructor(
$element: JQLite,
$timeout: ng.ITimeoutService
) {
super($timeout);
this.$element = $element;
}
getState() {
return this.state as ChallengeModalState;
}
$onInit() {
super.$onInit();
const values = {} as Values;
const types = this.challenge.types;
for (const type of types) {
values[type] = {
value: '',
invalid: false
};
}
this.setState({
types: types,
values: values,
processing: false
});
this.orchestrator.setCallbacks(
(value) => {
this.getState().values[value.type]!.invalid = false;
removeFromArray(this.processingTypes, value.type);
this.reloadProcessingStatus();
},
(value) => {
this.getState().values[value.type]!.invalid = true;
removeFromArray(this.processingTypes, value.type);
this.reloadProcessingStatus();
},
() => {
this.dismiss();
},
);
}
deinit() {
(this.application as any) = undefined;
(this.orchestrator as any) = undefined;
(this.challenge as any) = undefined;
super.deinit();
}
reloadProcessingStatus() {
this.setState({
processing: this.processingTypes.length > 0
});
}
promptForChallenge(challenge: ChallengeType) {
if (challenge === ChallengeType.LocalPasscode) {
return 'Enter your application passcode';
} else {
return 'Enter your account password';
}
}
cancel() {
// if (!this.cancelable) {
// return;
// }
this.dismiss();
}
onTextValueChange(challenge: ChallengeType) {
const values = this.getState().values;
values[challenge]!.invalid = false;
this.setState({ values });
}
validate() {
const failed = [];
for (const type of this.getState().types) {
const value = this.getState().values[type];
if (!value || value.value.length === 0) {
this.getState().values[type]!.invalid = true;
}
}
return failed.length === 0;
}
async submit() {
if (!this.validate()) {
return;
}
await this.setState({ processing: true });
const values = [];
for (const key of Object.keys(this.getState().values)) {
const type = Number(key) as ChallengeType;
if (this.getState().values[type]!.invalid) {
continue;
}
const rawValue = this.getState().values[type]!.value;
const value = new ChallengeValue(type, rawValue);
values.push(value);
}
this.processingTypes = values.map((v) => v.type);
this.orchestrator.submitValues(values);
}
dismiss() {
const elem = this.$element;
const scope = elem.scope();
scope.$destroy();
elem.remove();
}
}
export class ChallengeModal extends WebDirective {
constructor() {
super();
this.restrict = 'E';
this.template = template;
this.controller = ChallengeModalCtrl;
this.controllerAs = 'ctrl';
this.bindToController = true;
this.scope = {
challenge: '=',
orchestrator: '=',
application: '='
};
}
}