VulpesOS/assets/webapps/mobile-wallet.gaiamobile.org/js/pin/pin_view.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

274 lines
7.7 KiB
JavaScript

'use strict';
/* globals addMixin, ObserverSubjectMixin, DebugMixin */
/* exported PinView */
(function(exports) {
// number of pin input attempts which should
// trigger the pin warning display
const PIN_ATTEMPTS_WARNING = 2;
const PIN_INCORRECT_TIMEOUT = 1000; //ms
const PIN_DEFAULT_LEN = 4;
const PIN_REQ_DEF = 'request-default-pin';
const PIN_REQ_BEFORE_CHANGE = 'request-pin-before-change';
const PIN_REQ_PUK = 'request-puk';
const PIN_REQ_NEW_PIN = 'request-new-pin';
const PIN_REQ_REPEAT_NEW_PIN = 'request-repeat-new-pin';
const PIN_ENTRY_TEXT_MAP = {};
PIN_ENTRY_TEXT_MAP[PIN_REQ_DEF] = {
entry: 'Please enter your PIN',
error: 'Incorrect PIN'
};
PIN_ENTRY_TEXT_MAP[PIN_REQ_BEFORE_CHANGE] = {
entry: 'Please enter current PIN',
error: 'Incorrect PIN'
},
PIN_ENTRY_TEXT_MAP[PIN_REQ_PUK] = {
entry: 'Please enter your PUK',
error: 'Incorrect PUK'
};
PIN_ENTRY_TEXT_MAP[PIN_REQ_NEW_PIN] = {
entry: 'Please enter new PIN',
error: 'New PIN mismatch',
};
PIN_ENTRY_TEXT_MAP[PIN_REQ_REPEAT_NEW_PIN] = {
entry: 'Please confirm new PIN',
error: ''
};
var PinView = function(id, defPinLen, templateId) {
this._id = id;
this._defaultPinLen = defPinLen || PIN_DEFAULT_LEN;
this._maxPinLen = this._defaultPinLen;
addMixin(this, ObserverSubjectMixin);
addMixin(this, DebugMixin);
if(templateId) {
this._el = document.querySelector('#' + templateId).cloneNode(true);
this._el.id = this._id;
} else {
this._el = document.querySelector('#' + this._id);
}
this._init();
};
PinView.prototype = {
REQUEST_PIN: PIN_REQ_DEF,
REQUEST_BEFORE_CHANGE: PIN_REQ_BEFORE_CHANGE,
REQUEST_PUK: PIN_REQ_PUK,
REQUEST_NEW_PIN: PIN_REQ_NEW_PIN,
REQUEST_CONFIRM: PIN_REQ_REPEAT_NEW_PIN,
_el: null,
_id: null,
_visible: false,
_pinInputEl: null,
_inputedPin: '',
// we have a default PIN length but we can do one time request for PINs
// of different length (i.e. PUK)
_defaultPinLen: 0,
_maxPinLen: 0,
_inputBlocked: false,
isVisible() {
return this._visible;
},
show: function() {
this._visible = true;
this._el.classList.remove('hide');
this._clearPin();
},
hide: function() {
this._visible = false;
this._el.classList.add('hide');
},
_init: function() {
this._pinInputEl = this._el.querySelector('.pin-input');
var keyHandler = this._keyHandler.bind(this);
var keypad = this._el.querySelector('.pin-keypad');
keypad.addEventListener('touchstart', keyHandler, true);
this._updatePinInput();
},
_keyHandler: function(event) {
event.preventDefault();
event.stopPropagation();
var key = event.target.dataset.value;
if(!key || this._inputBlocked) {
return;
}
switch(key) {
case 'clear': this._clearPin(); break;
case 'backspace': this._backspacePin(); break;
default: this._appendPin(key);
}
},
_clearPin: function() {
this._inputedPin = '';
this._updatePinInput();
},
_backspacePin: function() {
if(!this._inputedPin.length) {
return;
}
this._inputedPin = this._inputedPin.slice(0, -1);
this._updatePinInput();
},
_appendPin: function(digit) {
if(this._inputedPin.length >= this._maxPinLen) {
return;
}
this._inputedPin += digit;
this._updatePinInput();
if(this._inputedPin.length === this._maxPinLen) {
if(this._maxPinLen !== this._defaultPinLen) {
this._maxPinLen = this._defaultPinLen;
}
this._notify({ action: 'pin-inputed', pin: this._inputedPin });
}
},
_updatePinInput: function() {
var inputedDigits = this._inputedPin.length;
var fragment = document.createDocumentFragment();
for(var i = 0; i < this._maxPinLen; i++) {
var pinDigit = document.createElement('div');
if(i < inputedDigits) {
pinDigit.classList.add('pin-digit-entered');
}
fragment.appendChild(pinDigit);
}
this._pinInputEl.innerHTML = '';
this._pinInputEl.appendChild(fragment);
},
_updatePinInputTexts: function _updatePinInputTexts(reqType) {
reqType = (reqType in PIN_ENTRY_TEXT_MAP) ? reqType : PIN_REQ_DEF;
this._el.querySelector('.pin-entry-text')
.textContent = PIN_ENTRY_TEXT_MAP[reqType].entry;
this._el.querySelector('.pin-entry-error-text')
.textContent = PIN_ENTRY_TEXT_MAP[reqType].error;
},
requestPin: function(attemptsLeft, showPinIncorrect, reqType, pinLen) {
this.debug('requestPIN, tries: ' + attemptsLeft + ', type: ' + reqType);
this._maxPinLen = pinLen || this._maxPinLen;
this._clearPin();
this._updatePinInputTexts(reqType);
this._showByClass('pin-unblocked');
if(showPinIncorrect) {
this._showPinIncorrect();
if(reqType !== PIN_REQ_NEW_PIN) {
this._showWarning(attemptsLeft);
}
} else if(attemptsLeft <= PIN_ATTEMPTS_WARNING) {
this._showWarning(attemptsLeft);
} else {
this._clearWarning();
}
},
cancelPinRequest: function() {
this._maxPinLen = this._defaultPinLen;
this._notify({});
},
blockPin: function() {
window.navigator.vibrate([200, 100, 200, 100, 200]);
this._showByClass('pin-blocked');
return new Promise((resolve, reject) => {
var unblockBtn = this._el.querySelector('.pin-unblock-btn');
var onclick = () => {
unblockBtn.removeEventListener('click', onclick);
resolve();
};
unblockBtn.addEventListener('click', onclick);
});
},
blockPuk: function() {
window.navigator.vibrate([200, 100, 200, 100, 200]);
this._showByClass('puk-blocked');
return new Promise(()=>{});
},
showEnableFailed: function(tries, defaultPin) {
window.navigator.vibrate([200, 100, 200, 100, 200]);
this._showByClass('pin-enable-error');
this._el.querySelector('.pin-default-val').textContent = defaultPin;
if(tries || tries === 0) {
this._el.querySelector('.pin-enable-warning').classList.remove('hide');
var att = (tries === 1) ? ' entry attempt' : ' entry attempts';
this._el.querySelector('.pin-enable-attempt').textContent = tries + att;
} else {
this._el.querySelector('.pin-enable-warning').classList.add('hide');
}
},
_showByClass: function(clss) {
var nodesList = this._el.querySelectorAll('article');
Array.prototype.slice.call(nodesList).forEach(el => {
if(el.classList.contains(clss)) {
el.classList.remove('hide');
} else {
el.classList.add('hide');
}
});
},
_showWarning: function(attemptsLeft) {
this._el.querySelector('.pin-warning').classList.remove('hide');
var warning = attemptsLeft + ' attempt';
warning = (attemptsLeft !== 1) ? (warning + 's') : warning;
warning += ' remaining';
this._el.querySelector('.pin-attempts').textContent = warning;
},
_togglePinIncorrect: function(show) {
this._inputBlocked = show;
this._el.querySelector('.pin-entry').classList.toggle('hide');
this._el.querySelector('.pin-incorrect').classList.toggle('hide');
},
_showPinIncorrect: function() {
window.navigator.vibrate([200, 100, 200]);
this._togglePinIncorrect(true);
setTimeout(() => this._togglePinIncorrect(false), PIN_INCORRECT_TIMEOUT);
},
_clearWarning: function() {
this._el.querySelector('.pin-warning').classList.add('hide');
}
};
exports.PinView = PinView;
}((typeof exports === 'undefined') ? window : exports));