Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
468 lines
14 KiB
JavaScript
468 lines
14 KiB
JavaScript
/* global BalanceView, TopUpLayoutView, CostControl, ConfigManager,
|
|
ViewManager, debug, MozActivity, getTopUpTimeout, LazyLoader,
|
|
sendBalanceThresholdNotification
|
|
*/
|
|
|
|
/*
|
|
* The balance tab is in charge of show balance details and allows the use
|
|
* to manually update or top up via USSD or code.
|
|
*
|
|
* It has four areas of drawing: balance, top up dialog, top up countdown and
|
|
* error area. The update is done by demand when the application is shown or
|
|
* when the user taps on the update button. Furthermore, the balance update
|
|
* can be triggered by a successful top up request.
|
|
*/
|
|
'use strict';
|
|
|
|
var BalanceTab = (function() {
|
|
|
|
var view, updateButton;
|
|
var topUpUSSD, topUp, topUpDialog, topUpCodeInput, sendCode, countdownSpan;
|
|
var balanceView, topUpLayoutView;
|
|
var costcontrol, initialized;
|
|
|
|
function showTopUpWithCode(evt) {
|
|
topUpWithCode();
|
|
}
|
|
|
|
function setupTab() {
|
|
if (initialized) {
|
|
return;
|
|
}
|
|
|
|
CostControl.getInstance(function _onCostControl(instance) {
|
|
costcontrol = instance;
|
|
|
|
// we need to load topUpDialog to get elements from inside
|
|
// it should be a separate function run on startup of the module
|
|
// after HTML is generated
|
|
topUpDialog = document.getElementById('topup-dialog');
|
|
var vm = new ViewManager(); // XXX: used to eagerly load top up dialog
|
|
vm.loadPanel(topUpDialog);
|
|
|
|
// HTML entities
|
|
view = document.getElementById('balance-tab');
|
|
updateButton = document.getElementById('balance-tab-update-button');
|
|
sendCode = document.getElementById('topup-send-button');
|
|
topUpUSSD = document.getElementById('balance-tab-topup-ussd-button');
|
|
topUp = document.getElementById('balance-tab-topup-button');
|
|
topUpCodeInput = document.getElementById('topup-code-input');
|
|
countdownSpan = document.getElementById('top-up-countdown');
|
|
|
|
// Subviews
|
|
topUpLayoutView = new TopUpLayoutView(topUpUSSD, topUp);
|
|
var balanceConfig = ConfigManager.configuration.balance;
|
|
balanceView = new BalanceView(
|
|
document.getElementById('balance-tab-credit'),
|
|
document.getElementById('balance-tab-time'),
|
|
balanceConfig ? balanceConfig.minimum_delay : undefined
|
|
);
|
|
|
|
window.addEventListener('localized', localize);
|
|
|
|
// Configure updates
|
|
document.addEventListener('visibilitychange', updateWhenVisible);
|
|
updateButton.addEventListener('click', lockAndUpdateUI);
|
|
ConfigManager.observe('lowLimit', toogleLimits, true);
|
|
ConfigManager.observe('lowLimitThreshold', resetNotification, true);
|
|
ConfigManager.observe('lastBalance', onBalance, true);
|
|
ConfigManager.observe('errors', onBalanceTimeout, true);
|
|
|
|
// Configure top up
|
|
topUpUSSD.addEventListener('click', topUpWithUSSD);
|
|
topUp.addEventListener('click', showTopUpWithCode);
|
|
topUpCodeInput.addEventListener('input', toogleSend);
|
|
sendCode.addEventListener('click', requestTopUp);
|
|
ConfigManager.observe('waitingForTopUp', onConfirmation, true);
|
|
ConfigManager.observe('errors', onTopUpErrors, true);
|
|
toogleSend();
|
|
|
|
updateUI();
|
|
initialized = true;
|
|
});
|
|
}
|
|
|
|
function localize() {
|
|
if (initialized) {
|
|
updateUI();
|
|
}
|
|
}
|
|
|
|
function finalize() {
|
|
if (!initialized) {
|
|
return;
|
|
}
|
|
|
|
document.removeEventListener('visibilitychange', updateWhenVisible);
|
|
updateButton.removeEventListener('click', lockAndUpdateUI);
|
|
ConfigManager.removeObserver('lowLimit', toogleLimits);
|
|
ConfigManager.removeObserver('lowLimitThreshold', resetNotification);
|
|
ConfigManager.removeObserver('lastBalance', onBalance);
|
|
ConfigManager.removeObserver('errors', onBalanceTimeout);
|
|
|
|
topUpUSSD.removeEventListener('click', topUpWithUSSD);
|
|
topUp.removeEventListener('click', showTopUpWithCode);
|
|
topUpCodeInput.removeEventListener('input', toogleSend);
|
|
sendCode.removeEventListener('click', requestTopUp);
|
|
ConfigManager.removeObserver('waitingForTopUp', onConfirmation);
|
|
ConfigManager.removeObserver('errors', onTopUpErrors);
|
|
|
|
topUpLayoutSet = false;
|
|
initialized = false;
|
|
}
|
|
|
|
// BALANCE ACTIONS
|
|
|
|
// On showing the application
|
|
function updateWhenVisible() {
|
|
if (!document.hidden && initialized) {
|
|
updateUI();
|
|
} else {
|
|
clearInterval(topUpCountdown);
|
|
}
|
|
}
|
|
|
|
// On tapping update
|
|
function lockAndUpdateUI() {
|
|
updateButton.disabled = true;
|
|
updateUI(true);
|
|
}
|
|
|
|
// On changing low limit alert
|
|
function toogleLimits(isEnabled, old, key, settings) {
|
|
updateBalance(settings.lastBalance,
|
|
isEnabled && settings.lowLimitThreshold);
|
|
if (isEnabled) {
|
|
sendBalanceThresholdNotification(settings.lastBalance, settings, false);
|
|
updateBalance(settings.lastBalance, settings.lowLimitThreshold);
|
|
}
|
|
}
|
|
|
|
// On changing the threshold for low limit
|
|
function resetNotification() {
|
|
ConfigManager.setOption({ 'lowLimitNotified': false });
|
|
ConfigManager.requestAll(function _onSettings(configuration, settings) {
|
|
updateBalance(settings.lastBalance, settings.lowLimitThreshold);
|
|
sendBalanceThresholdNotification(settings.lastBalance, settings, false);
|
|
});
|
|
}
|
|
|
|
// On balance update received
|
|
function onBalance(balance, old, key, settings) {
|
|
debug('Balance received:', balance);
|
|
setBalanceMode();
|
|
updateBalance(balance, settings.lowLimit && settings.lowLimitThreshold);
|
|
debug('Balance updated!');
|
|
}
|
|
|
|
// On balance timeout
|
|
function onBalanceTimeout(errors) {
|
|
if (!errors.BALANCE_TIMEOUT) {
|
|
return;
|
|
}
|
|
debug('Balance timeout!');
|
|
|
|
setBalanceMode('warning');
|
|
setError('balance_error');
|
|
|
|
// Error handled, disabling
|
|
errors.BALANCE_TIMEOUT = false;
|
|
ConfigManager.setOption({errors: errors});
|
|
}
|
|
|
|
// TOP UP ACTIONS
|
|
|
|
// On tapping Top Up and Pay
|
|
function topUpWithUSSD() {
|
|
var dialing;
|
|
dialing = new MozActivity({
|
|
name: 'dial',
|
|
data: {
|
|
type: 'webtelephony/number',
|
|
number: ConfigManager.configuration.topup.ussd_destination
|
|
}
|
|
});
|
|
}
|
|
|
|
// On tapping Top Up with code
|
|
function topUpWithCode(lastWasError) {
|
|
window.location.hash = '##topup-dialog';
|
|
if (lastWasError) {
|
|
setTopUpMode('incorrect_code');
|
|
}
|
|
|
|
topUpCodeInput.focus();
|
|
}
|
|
|
|
// On typing the code
|
|
function toogleSend(evt) {
|
|
sendCode.disabled = !topUpCodeInput.value.trim();
|
|
}
|
|
|
|
// On tapping send code button
|
|
var DIALOG_TIMEOUT = 10 * 1000; // 10s
|
|
function requestTopUp() {
|
|
sendCode.disabled = true;
|
|
|
|
var code = topUpCodeInput.value.trim();
|
|
var requestObj = { type: 'topup', data: code };
|
|
costcontrol.request(requestObj, function _onTopUpRequest(result) {
|
|
debug(result);
|
|
sendCode.disabled = false;
|
|
|
|
var status = result.status;
|
|
if (status === 'success' || status === 'in_progress') {
|
|
setTopUpMode('in_progress');
|
|
setTimeout(closeTopUp, DIALOG_TIMEOUT);
|
|
|
|
} else if (status === 'error') {
|
|
setTopUpMode(result.details);
|
|
}
|
|
});
|
|
}
|
|
|
|
function closeTopUp() {
|
|
// Remove from hash
|
|
if (isTopUpShown()) {
|
|
window.location.hash = '#';
|
|
}
|
|
}
|
|
|
|
function isTopUpShown() {
|
|
return window.location.hash.split('#')[2] === topUpDialog.id;
|
|
}
|
|
|
|
// On confirmation SMS for top up received
|
|
function onConfirmation(isWaiting) {
|
|
if (isWaiting !== null) {
|
|
return;
|
|
}
|
|
debug('TopUp confirmed!');
|
|
setTopUpMode('default');
|
|
topUpCodeInput.value = '';
|
|
updateUI();
|
|
}
|
|
|
|
// On top up timeout or incorrect code
|
|
function onTopUpErrors(errors) {
|
|
debug('ERRORS:', errors);
|
|
|
|
var mode;
|
|
if (errors.TOPUP_TIMEOUT) {
|
|
errors.TOPUP_TIMEOUT = false;
|
|
mode = 'topup_timeout';
|
|
}
|
|
if (errors.INCORRECT_TOPUP_CODE) {
|
|
errors.INCORRECT_TOPUP_CODE = false;
|
|
mode = 'incorrect_code';
|
|
}
|
|
debug('Most important error: ', mode);
|
|
|
|
// XXX: Very important to call this only if something has changed. If not,
|
|
// infinite events are triggered.
|
|
if (mode) {
|
|
setTopUpMode(mode);
|
|
ConfigManager.setOption({errors: errors});
|
|
}
|
|
}
|
|
|
|
// USER INTERFACE
|
|
var topUpLayoutSet = false;
|
|
function updateUI(force) {
|
|
ConfigManager.requestAll(function _onSettings(configuration, settings) {
|
|
|
|
if (!topUpLayoutSet) {
|
|
topUpLayoutView.setupLayout(configuration.topup);
|
|
topUpLayoutSet = true;
|
|
}
|
|
|
|
resetTopUpCountdown();
|
|
updateBalance(settings.lastBalance,
|
|
settings.lowLimit && settings.lowLimitThreshold);
|
|
|
|
var requestObj = { type: 'balance', force: !!force };
|
|
costcontrol.request(requestObj, function _onRequest(result) {
|
|
debug(result);
|
|
updateButton.disabled = false;
|
|
var status = result.status;
|
|
var balance = result.data;
|
|
var isError = status === 'error' && result.details !== 'minimum_delay';
|
|
var isUpdating = status !== 'error';
|
|
if (isError) {
|
|
setBalanceMode('warning');
|
|
setError(result.details);
|
|
} else {
|
|
setBalanceMode(isUpdating ? 'updating' : 'default');
|
|
setError();
|
|
}
|
|
updateBalance(balance,
|
|
settings.lowLimit && settings.lowLimitThreshold);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Update the balance in balance view
|
|
function updateBalance(balance, limit) {
|
|
|
|
if (!balance) {
|
|
debug('Balance not available');
|
|
balanceView.update();
|
|
return;
|
|
}
|
|
|
|
var isUpdating = view.classList.contains('updating');
|
|
balanceView.update(balance, isUpdating);
|
|
|
|
// Limits: reaching zero / low limit
|
|
if (balance.balance === 0) {
|
|
view.classList.add('no-credit');
|
|
|
|
} else {
|
|
view.classList.remove('no-credit');
|
|
|
|
if (limit && balance.balance < limit) {
|
|
view.classList.add('low-credit');
|
|
} else {
|
|
view.classList.remove('low-credit');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set warning / updating modes
|
|
var lastBalanceMode;
|
|
function setBalanceMode(mode) {
|
|
if (mode === lastBalanceMode) {
|
|
return;
|
|
}
|
|
|
|
lastBalanceMode = mode;
|
|
view.classList.remove('updating');
|
|
view.classList.remove('warning');
|
|
|
|
if (mode === 'warning') {
|
|
view.classList.add('warning');
|
|
}
|
|
|
|
if (mode === 'updating') {
|
|
view.classList.add('updating');
|
|
}
|
|
}
|
|
|
|
// Control info messages in the top up dialog as well as the top up countdown
|
|
// and error messages in the error area
|
|
var lastTopUpMode;
|
|
function setTopUpMode(mode) {
|
|
if (lastTopUpMode === mode) {
|
|
return;
|
|
}
|
|
|
|
lastTopUpMode = mode;
|
|
|
|
// Messages in top up dialog
|
|
var isShown;
|
|
var defaultMessage = document.getElementById('topup-code-explanation');
|
|
var inprogressMessage = document.getElementById('topup-in-progress');
|
|
var errorMessage = document.getElementById('topup-error');
|
|
var incorrectCodeMessage = document.getElementById('topup-incorrect-code');
|
|
|
|
isShown = (mode === 'default' || mode === 'topup_timeout');
|
|
defaultMessage.hidden = !isShown;
|
|
|
|
isShown = (mode === 'in_progress');
|
|
inprogressMessage.hidden = !isShown;
|
|
|
|
isShown = (mode === 'request_fail' || mode === 'no_service');
|
|
errorMessage.hidden = !isShown;
|
|
|
|
isShown = (mode === 'incorrect_code');
|
|
incorrectCodeMessage.hidden = !isShown;
|
|
|
|
// Count down
|
|
var countdownArea = document.getElementById('cost-control-topup-countdown');
|
|
isShown = (mode === 'in_progress');
|
|
countdownArea.hidden = !isShown;
|
|
if (isShown) {
|
|
resetTopUpCountdown();
|
|
}
|
|
|
|
// Messages in error message area
|
|
if (mode === 'topup_timeout') {
|
|
setError(mode);
|
|
}
|
|
}
|
|
|
|
var topUpCountdown, countdown;
|
|
function resetTopUpCountdown() {
|
|
getTopUpTimeout(function(timeout) {
|
|
if (!timeout) {
|
|
return;
|
|
}
|
|
countdown = Math.floor((timeout.getTime() - Date.now()) / 1000);
|
|
if (countdown < 0) {
|
|
return;
|
|
}
|
|
clearInterval(topUpCountdown);
|
|
topUpCountdown = setInterval(function _updateCountdown() {
|
|
var minutes = Math.floor(countdown / 60);
|
|
var seconds = (countdown % 60).toFixed(0);
|
|
var padding = '';
|
|
if (seconds < 10) {
|
|
padding = '0';
|
|
}
|
|
countdownSpan.textContent = (minutes + ' : ' + padding + seconds);
|
|
if (countdown > 0) {
|
|
countdown -= 1;
|
|
}
|
|
}, 1000);
|
|
});
|
|
}
|
|
|
|
var ERRORS = {
|
|
'airplane_mode': { priority: 1, string: 'airplane-mode-error-message2' },
|
|
'no_service': { priority: 2, string: 'no-coverage-error-message2' },
|
|
'no_coverage': { priority: 2, string: 'no-coverage-error-message' },
|
|
'topup_timeout': { priority: 3, string: 'top-up-timed-out2' },
|
|
'balance_error': { priority: 4, string: 'balance-error-message2' },
|
|
'non_free_in_roaming': { priority: 4, string: 'on-roaming-message2' }
|
|
};
|
|
var currentError = '';
|
|
|
|
// Decide which error should be shown taking in count error priorities
|
|
function setError(error) {
|
|
// Ignore showing message if the message is not registered
|
|
if (error && !ERRORS[error]) {
|
|
return;
|
|
}
|
|
|
|
debug('Error mode:', error ? error : 'no error');
|
|
var messageArea = document.getElementById('cost-control-message-area');
|
|
var message = document.getElementById('error-message-placeholder');
|
|
|
|
if (!error) {
|
|
messageArea.hidden = true;
|
|
message.removeAttribute('data-l10n-id');
|
|
message.textContent = '';
|
|
} else {
|
|
messageArea.hidden = false;
|
|
var curPriority = currentError ? ERRORS[currentError].priority : 0;
|
|
var newPriority = ERRORS[error] ? ERRORS[error].priority : 0;
|
|
if (newPriority >= curPriority) {
|
|
message.setAttribute('data-l10n-id', ERRORS[error].string);
|
|
currentError = error;
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
topUpWithCode: topUpWithCode,
|
|
initialize: function() {
|
|
var SCRIPTS_NEEDED = [
|
|
'js/views/TopUpLayoutView.js',
|
|
'js/views/BalanceView.js'
|
|
];
|
|
LazyLoader.load(SCRIPTS_NEEDED, setupTab);
|
|
},
|
|
finalize: finalize
|
|
};
|
|
}());
|
|
BalanceTab.initialize();
|