(function (context, $) { "use strict"; context.JK = context.JK || {}; context.JK.ClientUpdate = function (app) { var self = this; var EVENTS = context.JK.EVENTS; var logger = context.JK.logger; var ellipsesJiggleTimer = null; var forceShow = false; // manual test helper // updated once a download is started var updateUri = null; var updateSize = 0; app.clientUpdating = false; function cancelUpdate(e) { if ((e.ctrlKey || e.metaKey) && e.keyCode == 78) { logger.debug("update canceled!"); app.layout.closeDialog('client-update'); app.clientUpdating = false; } } // responsible for updating the contents of the update dialog // as well as registering for any event handlers function updateClientUpdateDialog(templateId, options) { options = options || {}; var template = $('#template-' + templateId).html(); var templateHtml = context._.template(template, options, {variable: 'data'}); $('#client_update .dialog-inner').html(templateHtml); $('#client_update').attr('data-mode', templateId); // assign click handlers if (templateId == "update-start") { $('body').on('keyup', cancelUpdate); $("#client_update a.close-application").click(function () { // noop atm return false; }) $("#client_update a.start-update").click(function () { var uri = options.uri.replace('https://', 'http://'); startDownload(uri) return false; }) } else if (templateId == "update-downloading") { $('body').off('keyup', cancelUpdate); $("#client_update a.close-application").click(function () { // noop atm return false; }) } else if (templateId == "update-restarting" || templateId == "update-error" || templateId == "manual-upgrade") { $("#client_update a.open-downloads").click(function () { context.JK.popExternalLink("https://www.jamkazam.com/downloads") return false; }) } if(!app.layout.isDialogShowing('client-update')) { app.layout.showDialog('client-update') } //$('#client_update').show() //$('#client_update_overlay').show() } /***************************************/ /******** CALLBACKS FROM BACKEND *******/ /***************************************/ function clientUpdateDownloadProgress(bytesReceived, bytesTotal, downloadSpeedMegSec, timeRemaining) { // this fires way too many times to leave in. uncomment if debugging update feature //logger.debug("bytesReceived: " + bytesReceived, ", bytesTotal: " + bytesTotal, ", downloadSpeed: " + downloadSpeedMegSec, ", timeRemaining: " + timeRemaining + ", updateSize: " + updateSize); bytesReceived = Number(bytesReceived) bytesTotal = Number(bytesTotal) // bytesTotal from Qt is not trust worthy; trust server's answer instead var progressWidth = ((bytesReceived / updateSize) * 100).toString() + "%"; $('#progress-bar').width(progressWidth) //$("#progressbar_detail").text(parseInt(bytesReceived) + "/" + parseInt(updateSize)) } function clientUpdateDownloadSuccess(updateLocation) { logger.debug("client update downloaded successfully to: " + updateLocation); updateClientUpdateDialog("update-proceeding"); setTimeout(function () { // This method is synchronous, and does alot of work on a mac in particular, hanging the UI. // So, we do a sleep loop to make sure the UI is updated with the last message to the user, before we hang the UI startUpdate(updateLocation); }, 500); } function clientUpdateDownloadFailure(errorMsg) { logger.error("client update download error: " + errorMsg) updateClientUpdateDialog("update-error", {error_msg: "Unable to download client update. Error reason:
" + errorMsg }); } async function clientUpdateLaunchSuccess(userTimeToRead) { if(userTimeToRead === undefined) { userTimeToRead = 1000; // older clients didn't pass this in, and exit very quickly } logger.debug("client update launching in: " + userTimeToRead); // set timer to update countdown var rounded = Math.round(userTimeToRead / 1000); // simple countdown timer var timer = setInterval(function(){ var $countdown = $('#client_update .countdown-secs'); var countdown = parseInt($countdown.text()); if(countdown <= 0) { clearInterval(timer); } else { $countdown.text(countdown - 1); } }, rounded * 1000); updateClientUpdateDialog("update-restarting", {countdown: rounded, os: await context.JK.GetOSAsString()}); } function clientUpdateLaunchFailure(errorMsg) { logger.error("client update launch error: " + errorMsg) updateClientUpdateDialog("update-error", {error_msg: "Unable to launch client updater. Error reason:
" + errorMsg}); } function clientUpdateLaunchStatuses(statuses) { logger.debug("client update launch statuses"); if (statuses) { for (var i = 0; i < statuses.length; i++) { var status = statuses[i]; } } } function clientUpdateLaunchStatusChange(done, status) { logger.debug("client update launch status change. starting=" + done + ", status=" + status); if (!done) { var $ellipses = $('.'); $ellipses.data('count', 1); var $status = $(''); $status.text(status); $status.append($ellipses); $('#client-updater-updating').append($status); ellipsesJiggleTimer = setInterval(function () { var count = $ellipses.data('count'); if (!count) { count = 0; // only the real client sometimes returns undefined for count } count++; if (count > 3) { count = 1; } $ellipses.text(Array(count + 1).join(".")); $ellipses.data('count', count); }, 500); } else { clearInterval(ellipsesJiggleTimer); $('#client-updater-updating span.status').last().css('color', 'gray').find('span.ellipses').text('...'); } } /********************************************/ /******** END: CALLBACKS FROM BACKEND *******/ /********************************************/ // if the current version doesn't not match the server version, attempt to do an upgrade function shouldUpdate(currentVersion, version) { if(forceShow) return true; if (version === undefined || version == null || version == "") { return false; } else { return currentVersion != version; } } async function runCheck(product, version, uri, size, currentVersion) { if (app.clientUpdating) { logger.debug("client is already updating; skipping") return } if(currentVersion === undefined) { currentVersion = await context.jamClient.ClientUpdateVersion(); if (!forceShow && (currentVersion == null || currentVersion.indexOf("Compiled")) > -1) { // this is a developer build; it doesn't make much sense to do an packaged update, so skip logger.debug("skipping client update check because this is a development build ('" + currentVersion + "')") return; } // # strange client oddity: remove quotes, if found, from start and finish of version. if (currentVersion.indexOf('"') == 0 && currentVersion.lastIndexOf('"') == currentVersion.length - 1) { currentVersion = currentVersion.substring(1, currentVersion.length - 1); } } logger.debug("our client version: " + currentVersion + ", server client version: " + version); // test url in lieu of having a configured server with a client-update available if (shouldUpdate(currentVersion, version)) { app.clientUpdating = true; updateUri = uri; updateSize = size; if(context.SessionStore.inSession()) { logger.debug("deferring client update because in session") return; } // test metadata in lieu of having a configured server with a client-update available //updateSize = 10000; //version = "1.2.3" // this will update the client dialog to how it should look when an update is just starting // and show it front-and-center on the screen // XXX HACK if(updateUri && gon.global.manual_override_installer_ends_with && updateUri.endsWith(gon.global.manual_override_installer_ends_with)) { updateClientUpdateDialog("manual-upgrade", { uri: updateUri }) } else { updateClientUpdateDialog("update-start", { uri: updateUri }) } } } // check if updated is needed async function check() { var os = await context.JK.GetOSAsString(); //os = 'Win32' // check kill switch before all other logic if (!gon.check_for_client_updates) { logger.debug("skipping client update because the server is telling us not to") return; } var product = "JamClient" var currentVersion = await context.jamClient.ClientUpdateVersion(); if (!forceShow && (currentVersion == null || currentVersion.indexOf("Compiled")) > -1) { // this is a developer build; it doesn't make much sense to do an packaged update, so skip logger.debug("skipping client update check because this is a development build ('" + currentVersion + "')") return; } // # strange client oddity: remove quotes, if found, from start and finish of version. if (currentVersion.indexOf('"') == 0 && currentVersion.lastIndexOf('"') == currentVersion.length - 1) { currentVersion = currentVersion.substring(1, currentVersion.length - 1); } $.ajax({ type: "GET", url: "/api/versioncheck?product=" + product + "&os=" + os, success: function (response) { runCheck(product, response.version, response.uri, response.size, currentVersion); }, error: function (jqXHR, textStatus, errorThrown) { logger.error("Unable to do a client update check against /api/versioncheck"); } }); } async function startDownload(url) { logger.debug("starting client updater download from: " + url); updateClientUpdateDialog("update-downloading") await context.jamClient.ClientUpdateStartDownload(url, "JK.ClientUpdate.DownloadProgressCallback", "JK.ClientUpdate.DownloadSuccessCallback", "JK.ClientUpdate.DownloadFailureCallback"); } async function startUpdate(updaterFilePath) { logger.debug("starting client update from: " + updaterFilePath) await context.jamClient.ClientUpdateStartUpdate(updaterFilePath, "JK.ClientUpdate.LaunchUpdateSuccessCallback", "JK.ClientUpdate.LaunchUpdateFailureCallback", "JK.ClientUpdate.LaunchUpdateStatusesCallback", "JK.ClientUpdate.LaunchUpdateStatusChangeCallback"); } function initialize() { context.JK.ClientUpdate.DownloadProgressCallback = clientUpdateDownloadProgress; context.JK.ClientUpdate.DownloadSuccessCallback = clientUpdateDownloadSuccess; context.JK.ClientUpdate.DownloadFailureCallback = clientUpdateDownloadFailure; context.JK.ClientUpdate.LaunchUpdateSuccessCallback = clientUpdateLaunchSuccess; context.JK.ClientUpdate.LaunchUpdateFailureCallback = clientUpdateLaunchFailure; context.JK.ClientUpdate.LaunchUpdateStatusesCallback = clientUpdateLaunchStatuses; context.JK.ClientUpdate.LaunchUpdateStatusChangeCallback = clientUpdateLaunchStatusChange; app.bindDialog('client-update', {}); $(document).on(EVENTS.SESSION_ENDED, function(e, data){ if(app.clientUpdating) { updateClientUpdateDialog("update-start", { uri: updateUri}) } }); return self; } // Expose publics this.initialize = initialize; this.check = check; this.runCheck = runCheck; } return this; })(window, jQuery);