diff --git a/dashboard-ui/apiclient/connectionmanager.js b/dashboard-ui/apiclient/connectionmanager.js index f5afddf75..fcae17cac 100644 --- a/dashboard-ui/apiclient/connectionmanager.js +++ b/dashboard-ui/apiclient/connectionmanager.js @@ -880,7 +880,7 @@ var tests = []; if (server.LastConnectionMode != null) { - tests.push(server.LastConnectionMode); + //tests.push(server.LastConnectionMode); } if (tests.indexOf(MediaBrowser.ConnectionMode.Manual) == -1) { tests.push(MediaBrowser.ConnectionMode.Manual); } if (tests.indexOf(MediaBrowser.ConnectionMode.Local) == -1) { tests.push(MediaBrowser.ConnectionMode.Local); } diff --git a/dashboard-ui/bower_components/iron-behaviors/.bower.json b/dashboard-ui/bower_components/iron-behaviors/.bower.json index 5d7b926af..f49935188 100644 --- a/dashboard-ui/bower_components/iron-behaviors/.bower.json +++ b/dashboard-ui/bower_components/iron-behaviors/.bower.json @@ -27,14 +27,14 @@ "web-component-tester": "*", "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" }, - "homepage": "https://github.com/polymerelements/iron-behaviors", + "homepage": "https://github.com/PolymerElements/iron-behaviors", "_release": "1.0.8", "_resolution": { "type": "version", "tag": "v1.0.8", "commit": "663ad706b43989f4961d945b8116cf4db346532f" }, - "_source": "git://github.com/polymerelements/iron-behaviors.git", + "_source": "git://github.com/PolymerElements/iron-behaviors.git", "_target": "^1.0.0", - "_originalSource": "polymerelements/iron-behaviors" + "_originalSource": "PolymerElements/iron-behaviors" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json b/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json index 42470f3ae..73b5a092b 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json +++ b/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json @@ -1,6 +1,6 @@ { "name": "iron-overlay-behavior", - "version": "1.0.8", + "version": "1.0.9", "license": "http://polymer.github.io/LICENSE.txt", "description": "Provides a behavior for making an element an overlay", "private": true, @@ -35,11 +35,11 @@ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" }, "homepage": "https://github.com/polymerelements/iron-overlay-behavior", - "_release": "1.0.8", + "_release": "1.0.9", "_resolution": { "type": "version", - "tag": "v1.0.8", - "commit": "cf25fe1ff2f585fa84190537bf62b94eb1579aad" + "tag": "v1.0.9", + "commit": "87f7763d323fffa07357a08777ad831b7c2c2fb8" }, "_source": "git://github.com/polymerelements/iron-overlay-behavior.git", "_target": "^1.0.0", diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/bower.json b/dashboard-ui/bower_components/iron-overlay-behavior/bower.json index 4deb8d2b3..30a3f04d9 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/bower.json +++ b/dashboard-ui/bower_components/iron-overlay-behavior/bower.json @@ -1,6 +1,6 @@ { "name": "iron-overlay-behavior", - "version": "1.0.8", + "version": "1.0.9", "license": "http://polymer.github.io/LICENSE.txt", "description": "Provides a behavior for making an element an overlay", "private": true, diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html b/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html index df52db1a5..837759f7e 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html +++ b/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html @@ -28,7 +28,8 @@ intent. Closing generally implies that the user acknowledged the content on the it will cancel whenever the user taps outside it or presses the escape key. This behavior is configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties. `close()` should be called explicitly by the implementer when the user interacts with a control -in the overlay element. +in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled' +event. Call `preventDefault` on this event to prevent the overlay from closing. ### Positioning @@ -199,6 +200,11 @@ context. You should place this element as a child of `` whenever possible. * Cancels the overlay. */ cancel: function() { + var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelable: true}); + if (cancelEvent.defaultPrevented) { + return; + } + this.opened = false; this._setCanceled(true); }, diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html b/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html index 8fd17eadf..a352a4b18 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html +++ b/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html @@ -180,6 +180,15 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN }); test('cancel an overlay by clicking outside', function(done) { + runAfterOpen(overlay, function() { + overlay.addEventListener('iron-overlay-canceled', function(event) { + done(); + }); + Polymer.Base.fire.call(document, 'click'); + }); + }); + + test('close an overlay by clicking outside', function(done) { runAfterOpen(overlay, function() { overlay.addEventListener('iron-overlay-closed', function(event) { assert.isTrue(event.detail.canceled, 'overlay is canceled'); @@ -189,7 +198,35 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN }); }); + test('cancel event can be prevented', function(done) { + runAfterOpen(overlay, function() { + overlay.addEventListener('iron-overlay-canceled', function(event) { + event.preventDefault(); + }); + var closedListener = function(event) { + throw new Error('iron-overlay-closed should not fire'); + }; + overlay.addEventListener('iron-overlay-closed', closedListener); + Polymer.Base.fire.call(document, 'click'); + setTimeout(function() { + overlay.removeEventListener('iron-overlay-closed', closedListener); + done(); + }, 10); + }); + }); + test('cancel an overlay with esc key', function(done) { + runAfterOpen(overlay, function() { + overlay.addEventListener('iron-overlay-canceled', function(event) { + done(); + }); + fireEvent('keydown', { + keyCode: 27 + }, document); + }); + }); + + test('close an overlay with esc key', function(done) { runAfterOpen(overlay, function() { overlay.addEventListener('iron-overlay-closed', function(event) { assert.isTrue(event.detail.canceled, 'overlay is canceled'); diff --git a/dashboard-ui/components/imagedownloader/imagedownloader.js b/dashboard-ui/components/imagedownloader/imagedownloader.js index 71f34e056..5edbff50c 100644 --- a/dashboard-ui/components/imagedownloader/imagedownloader.js +++ b/dashboard-ui/components/imagedownloader/imagedownloader.js @@ -269,22 +269,7 @@ currentItemId = itemId; currentItemType = itemType; - var dlg = document.createElement('paper-dialog'); - - dlg.setAttribute('with-backdrop', 'with-backdrop'); - dlg.setAttribute('role', 'alertdialog'); - - // without this safari will scroll the background instead of the dialog contents - // but not needed here since this is already on top of an existing dialog - // dlg.setAttribute('modal', 'modal'); - - // seeing max call stack size exceeded in the debugger with this - dlg.setAttribute('noAutoFocus', 'noAutoFocus'); - dlg.entryAnimation = 'scale-up-animation'; - dlg.exitAnimation = 'fade-out-animation'; - dlg.classList.add('fullscreen-editor-paper-dialog'); - dlg.classList.add('ui-body-b'); - dlg.classList.add('smoothScrollY'); + var dlg = PaperDialogHelper.createDialog(); var html = ''; html += '

'; diff --git a/dashboard-ui/components/imageeditor/imageeditor.js b/dashboard-ui/components/imageeditor/imageeditor.js index 4e3ff0f02..d372d81de 100644 --- a/dashboard-ui/components/imageeditor/imageeditor.js +++ b/dashboard-ui/components/imageeditor/imageeditor.js @@ -230,19 +230,7 @@ ApiClient.getItem(Dashboard.getCurrentUserId(), itemId).done(function (item) { - var dlg = document.createElement('paper-dialog'); - - dlg.setAttribute('with-backdrop', 'with-backdrop'); - dlg.setAttribute('role', 'alertdialog'); - // without this safari will scroll the background instead of the dialog contents - dlg.setAttribute('modal', 'modal'); - // seeing max call stack size exceeded in the debugger with this - dlg.setAttribute('noAutoFocus', 'noAutoFocus'); - dlg.entryAnimation = 'scale-up-animation'; - dlg.exitAnimation = 'fade-out-animation'; - dlg.classList.add('fullscreen-editor-paper-dialog'); - dlg.classList.add('ui-body-b'); - dlg.classList.add('smoothScrollY'); + var dlg = PaperDialogHelper.createDialog(); var html = ''; html += '

'; diff --git a/dashboard-ui/components/imageuploader/imageuploader.js b/dashboard-ui/components/imageuploader/imageuploader.js index 29f45b347..981e41b8f 100644 --- a/dashboard-ui/components/imageuploader/imageuploader.js +++ b/dashboard-ui/components/imageuploader/imageuploader.js @@ -132,22 +132,7 @@ currentItemId = itemId; - var dlg = document.createElement('paper-dialog'); - - dlg.setAttribute('with-backdrop', 'with-backdrop'); - dlg.setAttribute('role', 'alertdialog'); - - // without this safari will scroll the background instead of the dialog contents - // but not needed here since this is already on top of an existing dialog - // dlg.setAttribute('modal', 'modal'); - - // seeing max call stack size exceeded in the debugger with this - dlg.setAttribute('noAutoFocus', 'noAutoFocus'); - dlg.entryAnimation = 'scale-up-animation'; - dlg.exitAnimation = 'fade-out-animation'; - dlg.classList.add('fullscreen-editor-paper-dialog'); - dlg.classList.add('ui-body-b'); - dlg.classList.add('smoothScrollY'); + var dlg = PaperDialogHelper.createDialog(); var html = ''; html += '

'; diff --git a/dashboard-ui/components/paperdialoghelper.js b/dashboard-ui/components/paperdialoghelper.js index 166bb1ae9..6b198d1d5 100644 --- a/dashboard-ui/components/paperdialoghelper.js +++ b/dashboard-ui/components/paperdialoghelper.js @@ -66,15 +66,41 @@ function close(dlg) { if (enableHashChange()) { - history.back(); + + if (dlg.opened) { + history.back(); + } + } else { dlg.close(); } } + function createDialog() { + var dlg = document.createElement('paper-dialog'); + + dlg.setAttribute('with-backdrop', 'with-backdrop'); + dlg.setAttribute('role', 'alertdialog'); + + // without this safari will scroll the background instead of the dialog contents + // but not needed here since this is already on top of an existing dialog + dlg.setAttribute('modal', 'modal'); + + // seeing max call stack size exceeded in the debugger with this + dlg.setAttribute('noAutoFocus', 'noAutoFocus'); + dlg.entryAnimation = 'scale-up-animation'; + dlg.exitAnimation = 'fade-out-animation'; + dlg.classList.add('fullscreen-editor-paper-dialog'); + dlg.classList.add('ui-body-b'); + dlg.classList.add('smoothScrollY'); + + return dlg; + } + globalScope.PaperDialogHelper = { openWithHash: openWithHash, - close: close + close: close, + createDialog: createDialog }; })(this); \ No newline at end of file diff --git a/dashboard-ui/components/prompt.js b/dashboard-ui/components/prompt.js new file mode 100644 index 000000000..19ba0763c --- /dev/null +++ b/dashboard-ui/components/prompt.js @@ -0,0 +1,10 @@ +define([], function () { + return function (options) { + + var result = prompt(options.text, options.defaultText || ''); + + if (options.callback) { + options.callback(result); + } + }; +}); \ No newline at end of file diff --git a/dashboard-ui/components/subtitleeditor/subtitleeditor.js b/dashboard-ui/components/subtitleeditor/subtitleeditor.js index da7774d72..45882a72d 100644 --- a/dashboard-ui/components/subtitleeditor/subtitleeditor.js +++ b/dashboard-ui/components/subtitleeditor/subtitleeditor.js @@ -335,19 +335,7 @@ ApiClient.getItem(Dashboard.getCurrentUserId(), itemId).done(function (item) { - var dlg = document.createElement('paper-dialog'); - - dlg.setAttribute('with-backdrop', 'with-backdrop'); - dlg.setAttribute('role', 'alertdialog'); - // without this safari will scroll the background instead of the dialog contents - dlg.setAttribute('modal', 'modal'); - // seeing max call stack size exceeded in the debugger with this - dlg.setAttribute('noAutoFocus', 'noAutoFocus'); - dlg.entryAnimation = 'scale-up-animation'; - dlg.exitAnimation = 'fade-out-animation'; - dlg.classList.add('fullscreen-editor-paper-dialog'); - dlg.classList.add('ui-body-b'); - dlg.classList.add('smoothScrollY'); + var dlg = PaperDialogHelper.createDialog(); var html = ''; html += '

'; diff --git a/dashboard-ui/cordova/android/iap.js b/dashboard-ui/cordova/android/iap.js index 82038233f..7f3b1bb2b 100644 --- a/dashboard-ui/cordova/android/iap.js +++ b/dashboard-ui/cordova/android/iap.js @@ -1,8 +1,16 @@ (function () { - var unlockId = "com.mb.android.unlock"; var updatedProducts = []; + function getStoreFeatureId(feature) { + + if (feature == 'embypremieremonthly') { + return "emby.supporter.monthly"; + } + + return "com.mb.android.unlock"; + } + function updateProductInfo(id, owned, price) { updatedProducts = updatedProducts.filter(function (r) { @@ -20,7 +28,10 @@ Events.trigger(IapManager, 'productupdated', [product]); } - function getProduct(id) { + function getProduct(feature) { + + var id = getStoreFeatureId(feature); + var products = updatedProducts.filter(function (r) { return r.id == id; }); @@ -28,13 +39,14 @@ return products.length ? products[0] : null; } - function isPurchaseAvailable(id) { + function isPurchaseAvailable(feature) { return NativeIapManager.isStoreAvailable(); } - function beginPurchase(id) { - return MainActivity.beginPurchase(id); + function beginPurchase(feature, email) { + var id = getStoreFeatureId(feature); + return MainActivity.beginPurchase(id, email); } function onPurchaseComplete(result) { @@ -45,7 +57,31 @@ } function refreshPurchases() { - NativeIapManager.isPurchased(unlockId, "window.IapManager.updateProduct"); + NativeIapManager.isPurchased(getStoreFeatureId("") + "|" + getStoreFeatureId("embypremieremonthly"), "window.IapManager.updateProduct"); + //NativeIapManager.isPurchased(getStoreFeatureId("embypremieremonthly"), "window.IapManager.updateProduct"); + } + + function getSubscriptionOptions() { + var deferred = DeferredBuilder.Deferred(); + + var options = []; + + options.push({ + feature: 'embypremieremonthly', + buttonText: 'EmbyPremiereMonthlyWithPrice' + }); + + options = options.filter(function (o) { + return getProduct(o.feature) != null; + + }).map(function (o) { + + o.buttonText = Globalize.translate(o.buttonText, o.price); + return o; + }); + + deferred.resolveWith(null, [options]); + return deferred.promise(); } window.IapManager = { @@ -53,7 +89,9 @@ getProductInfo: getProduct, updateProduct: updateProductInfo, beginPurchase: beginPurchase, - onPurchaseComplete: onPurchaseComplete + onPurchaseComplete: onPurchaseComplete, + getStoreFeatureId: getStoreFeatureId, + getSubscriptionOptions: getSubscriptionOptions }; refreshPurchases(); diff --git a/dashboard-ui/cordova/iap.js b/dashboard-ui/cordova/iap.js index 616d4022a..e7b07398c 100644 --- a/dashboard-ui/cordova/iap.js +++ b/dashboard-ui/cordova/iap.js @@ -1,10 +1,16 @@ (function () { - var unlockAlias = "premium features"; - var unlockAppProductId = 'appunlock'; - var updatedProducts = []; + function getStoreFeatureId(feature) { + + if (feature == 'embypremieremonthly') { + return 'emby.subscription.monthly'; + } + + return 'appunlock'; + } + function updateProductInfo(product) { updatedProducts = updatedProducts.filter(function (r) { @@ -16,17 +22,9 @@ Events.trigger(IapManager, 'productupdated', [product]); } - function normalizeId(id) { + function getProduct(feature) { - // This is what i named it in itunes - id = id.replace('premiumunlock', 'appunlock'); - - return id; - } - - function getProduct(id) { - - id = normalizeId(id); + var id = getStoreFeatureId(feature); var products = updatedProducts.filter(function (r) { return r.id == id; @@ -35,19 +33,19 @@ return products.length ? products[0] : null; } - function isPurchaseAvailable(id) { - var product = getProduct(id); + function isPurchaseAvailable(feature) { + + var product = getProduct(feature); return product != null && product.valid /*&& product.canPurchase*/; } - function beginPurchase(id) { - id = normalizeId(id); + function beginPurchase(feature, email) { + var id = getStoreFeatureId(feature); store.order(id); } function restorePurchase(id) { - id = normalizeId(id); store.refresh(); } @@ -74,6 +72,36 @@ //callback(false, "Impossible to proceed with validation"); } + function initProduct(id, alias, type) { + + store.register({ + id: id, + alias: alias, + type: type + }); + + // When purchase of the full version is approved, + // show some logs and finish the transaction. + store.when(id).approved(function (order) { + order.finish(); + }); + + store.when(id).verified(function (p) { + p.finish(); + }); + + // The play button can only be accessed when the user + // owns the full version. + store.when(id).updated(function (product) { + + if (product.loaded && product.valid && product.state == store.APPROVED) { + Logger.log('finishing previously created transaction'); + product.finish(); + } + updateProductInfo(product); + }); + } + function initializeStore() { // Let's set a pretty high verbosity level, so that we see a lot of stuff @@ -82,35 +110,8 @@ store.validator = validateProduct; - // iOS - store.register({ - id: unlockAppProductId, - alias: unlockAlias, - type: store.NON_CONSUMABLE - }); - - // When purchase of the full version is approved, - // show some logs and finish the transaction. - store.when(unlockAppProductId).approved(function (order) { - log('You just unlocked the FULL VERSION!'); - order.finish(); - }); - - store.when(unlockAppProductId).verified(function (p) { - log("verified"); - p.finish(); - }); - - // The play button can only be accessed when the user - // owns the full version. - store.when(unlockAppProductId).updated(function (product) { - - if (product.loaded && product.valid && product.state == store.APPROVED) { - Logger.log('finishing previously created transaction'); - product.finish(); - } - updateProductInfo(product); - }); + initProduct(getStoreFeatureId(""), "premium features", store.NON_CONSUMABLE); + initProduct(getStoreFeatureId("embypremieremonthly"), "emby premiere monthly", store.PAID_SUBSCRIPTION); // When every goes as expected, it's time to celebrate! // The "ready" event should be welcomed with music and fireworks, @@ -125,11 +126,36 @@ store.refresh(); } + function getSubscriptionOptions() { + var deferred = DeferredBuilder.Deferred(); + + var options = []; + + options.push({ + feature: 'embypremieremonthly', + buttonText: 'EmbyPremiereMonthlyWithPrice' + }); + + options = options.filter(function (o) { + return getProduct(o.feature) != null; + + }).map(function (o) { + + o.buttonText = Globalize.translate(o.buttonText, o.price); + return o; + }); + + deferred.resolveWith(null, [options]); + return deferred.promise(); + } + window.IapManager = { isPurchaseAvailable: isPurchaseAvailable, getProductInfo: getProduct, beginPurchase: beginPurchase, - restorePurchase: restorePurchase + restorePurchase: restorePurchase, + getStoreFeatureId: getStoreFeatureId, + getSubscriptionOptions: getSubscriptionOptions }; initializeStore(); diff --git a/dashboard-ui/cordova/ios/backgroundfetch.js b/dashboard-ui/cordova/ios/backgroundfetch.js index 102eae1cf..d7f4c41fd 100644 --- a/dashboard-ui/cordova/ios/backgroundfetch.js +++ b/dashboard-ui/cordova/ios/backgroundfetch.js @@ -4,11 +4,11 @@ function onDeviceReady() { - var fetcher = window.BackgroundFetch; + //var fetcher = window.BackgroundFetch; - fetcher.configure(onBackgroundFetch, onBackgroundFetchFailed, { - stopOnTerminate: false // <-- false is default - }); + //fetcher.configure(onBackgroundFetch, onBackgroundFetchFailed, { + // stopOnTerminate: false // <-- false is default + //}); } function onSyncFinish() { @@ -86,7 +86,7 @@ }); }); - pageClassOn('pageshow', "page", function () { + pageClassOn('pageshow', "libraryPage", function () { if (!Dashboard.getCurrentUserId()) { return; diff --git a/dashboard-ui/cordova/localassetmanager.js b/dashboard-ui/cordova/localassetmanager.js index d1dd0be3a..32f3f8bc2 100644 --- a/dashboard-ui/cordova/localassetmanager.js +++ b/dashboard-ui/cordova/localassetmanager.js @@ -523,7 +523,7 @@ function downloadFile(url, localPath, enableBackground, enableNewDownloads) { if (!enableBackground) { - return downloadWithFileTransfer(url, localPath); + return downloadWithFileTransfer(url, localPath, enableBackground); } var deferred = DeferredBuilder.Deferred(); diff --git a/dashboard-ui/cordova/prompt.js b/dashboard-ui/cordova/prompt.js new file mode 100644 index 000000000..f924d6230 --- /dev/null +++ b/dashboard-ui/cordova/prompt.js @@ -0,0 +1,17 @@ +define([], function () { + return function (options) { + + var callback = function (result) { + + if (result.buttonIndex == 1) { + options.callback(result.input1); + } else { + options.callback(null); + } + }; + + var buttonLabels = [Globalize.translate('ButtonOk'), Globalize.translate('ButtonCancel')]; + + navigator.notification.prompt(options.text, callback, options.title, buttonLabels, options.defaultText || ''); + }; +}); \ No newline at end of file diff --git a/dashboard-ui/cordova/registrationservices.js b/dashboard-ui/cordova/registrationservices.js index ba3e97053..cc72ba8a6 100644 --- a/dashboard-ui/cordova/registrationservices.js +++ b/dashboard-ui/cordova/registrationservices.js @@ -1,86 +1,56 @@ (function () { - function isAndroid() { - - return $.browser.android; - } - - function getPremiumUnlockFeatureId() { - - if (isAndroid()) { - return "com.mb.android.unlock"; - } - - return 'appunlock'; - } - - function validatePlayback(deferred) { - - // Don't require validation on android - if (isAndroid()) { - deferred.resolve(); - return; - } - - validateFeature(getPremiumUnlockFeatureId(), deferred); - } - - function validateLiveTV(deferred) { - - validateFeature(getPremiumUnlockFeatureId(), deferred); - } - function validateServerManagement(deferred) { deferred.resolve(); } - function getRegistrationInfo(feature, enableSupporterUnlock) { + function getRegistrationInfo(feature) { - if (!enableSupporterUnlock) { - var deferred = $.Deferred(); - deferred.resolveWith(null, [{}]); - return deferred.promise(); - } return ConnectionManager.getRegistrationInfo(feature, ApiClient); } var validatedFeatures = []; - function validateFeature(id, deferred) { + function validateFeature(feature, deferred) { - if (validatedFeatures.indexOf(id) != -1) { + var id = IapManager.getStoreFeatureId(feature); + + if (validatedFeatures.indexOf(feature) != -1) { deferred.resolve(); return; } - var info = IapManager.getProductInfo(id) || {}; + var info = IapManager.getProductInfo(feature) || {}; if (info.owned) { notifyServer(id); - validatedFeatures.push(id); + validatedFeatures.push(feature); deferred.resolve(); return; } var productInfo = { - enableSupporterUnlock: true, - enableAppUnlock: IapManager.isPurchaseAvailable(id), + enableAppUnlock: IapManager.isPurchaseAvailable(feature), id: id, - price: info.price + price: info.price, + feature: feature }; - var prefix = isAndroid() ? 'android' : 'ios'; + var prefix = $.browser.android ? 'android' : 'ios'; // Get supporter status - getRegistrationInfo(prefix + 'appunlock', productInfo.enableSupporterUnlock).done(function (registrationInfo) { + getRegistrationInfo(prefix + 'appunlock').done(function (registrationInfo) { if (registrationInfo.IsRegistered) { - validatedFeatures.push(id); + validatedFeatures.push(feature); deferred.resolve(); return; } - showInAppPurchaseInfo(productInfo, registrationInfo, deferred); + IapManager.getSubscriptionOptions().done(function (subscriptionOptions) { + + showInAppPurchaseInfo(productInfo, subscriptionOptions, registrationInfo, deferred); + }); }).fail(function () { deferred.reject(); @@ -114,42 +84,32 @@ }); } - function getInAppPurchaseElement(info) { + function getInAppPurchaseElement(info, subscriptionOptions) { - cancelInAppPurchase(); + var dlg = PaperDialogHelper.createDialog(); var html = ''; - html += '
'; - html += '
'; + html += '

'; + html += ''; + html += '
' + Globalize.translate('HeaderUnlockApp') + '
'; + html += '

'; + html += '
'; - html += '
'; - - html += '

' + Globalize.translate('HeaderUnlockApp') + '

'; - + html += '
'; html += '

'; - var showSupporterInfo = info.enableSupporterUnlock && !$.browser.safari; - - if (showSupporterInfo && info.enableAppUnlock) { + if (info.enableAppUnlock) { html += Globalize.translate('MessageUnlockAppWithPurchaseOrSupporter'); } - else if (showSupporterInfo) { + else { html += Globalize.translate('MessageUnlockAppWithSupporter'); - } else if (info.enableAppUnlock) { - html += Globalize.translate('MessageUnlockAppWithPurchase'); - } else { - html += ''; - html += Globalize.translate('MessagePaymentServicesUnavailable'); - html += ''; } html += '

'; - if (showSupporterInfo) { - html += '

'; - html += Globalize.translate('MessageToValidateSupporter'); - html += '

'; - } + html += '

'; + html += Globalize.translate('MessageToValidateSupporter'); + html += '

'; if (info.enableAppUnlock) { @@ -158,76 +118,118 @@ unlockText = Globalize.translate('ButtonUnlockPrice', info.price); } html += '

'; - html += '' + unlockText + ''; + html += '' + unlockText + ''; html += '

'; - - if (IapManager.restorePurchase) { - html += '

'; - html += '' + Globalize.translate('ButtonRestorePreviousPurchase') + ''; - html += '

'; - } } - html += '

'; - html += '' + Globalize.translate('ButtonCancel') + ''; - html += '

'; + for (var i = 0, length = subscriptionOptions.length; i < length; i++) { + html += '

'; + html += ''; + html += subscriptionOptions[i].buttonText; + html += ''; + html += '

'; + } + + if (IapManager.restorePurchase) { + html += '

'; + html += '' + Globalize.translate('ButtonRestorePreviousPurchase') + ''; + html += '

'; + } + + html += '
'; html += '
'; - html += '
'; - html += '
'; + dlg.innerHTML = html; + document.body.appendChild(dlg); - $(document.body).append(html); + // init dlg content here - return $('.inAppPurchaseOverlay'); + PaperDialogHelper.openWithHash(dlg, 'iap'); + + $('.btnCloseDialog', dlg).on('click', function () { + + PaperDialogHelper.close(dlg); + }); + + dlg.classList.add('inAppPurchaseOverlay'); + + return dlg; } function cancelInAppPurchase() { - $('.inAppPurchaseOverlay').remove(); + var elem = document.querySelector('.inAppPurchaseOverlay'); + if (elem) { + PaperDialogHelper.close(elem); + } } - var currentDisplayingProductInfo = null; + var currentDisplayingProductInfos = []; var currentDisplayingDeferred = null; + var isCancelled = true; function clearCurrentDisplayingInfo() { - currentDisplayingProductInfo = null; + currentDisplayingProductInfos = []; currentDisplayingDeferred = null; } - function showInAppPurchaseInfo(info, serverRegistrationInfo, deferred) { + function showInAppPurchaseInfo(info, subscriptionOptions, serverRegistrationInfo, deferred) { - var elem = getInAppPurchaseElement(info); + require(['components/paperdialoghelper'], function () { - currentDisplayingProductInfo = info; - currentDisplayingDeferred = deferred; - - $('.btnAppUnlock', elem).on('click', function () { - - IapManager.beginPurchase(info.id); - }); - - $('.btnRestorePurchase', elem).on('click', function () { - - IapManager.restorePurchase(info.id); - }); - - $('.btnCancel', elem).on('click', function () { - - clearCurrentDisplayingInfo(); cancelInAppPurchase(); + isCancelled = true; - deferred.reject(); - }); - $('.btnSignInSupporter', elem).on('click', function () { + var elem = getInAppPurchaseElement(info, subscriptionOptions); - clearCurrentDisplayingInfo(); + // clone + currentDisplayingProductInfos = subscriptionOptions.slice(0); + currentDisplayingProductInfos.push(info); - Dashboard.alert({ - message: Globalize.translate('MessagePleaseSignInLocalNetwork'), - callback: function () { + currentDisplayingDeferred = deferred; + + $('.btnPurchase', elem).on('click', function () { + + isCancelled = false; + + if (this.getAttribute('data-email') == 'true') { + promptForEmail(this.getAttribute('data-feature')); + } else { + IapManager.beginPurchase(this.getAttribute('data-feature')); + } + }); + + $('.btnRestorePurchase', elem).on('click', function () { + + isCancelled = false; + IapManager.restorePurchase(info.feature); + }); + + $(elem).on('iron-overlay-closed', function () { + + if (isCancelled) { + clearCurrentDisplayingInfo(); cancelInAppPurchase(); - Dashboard.logout(); + + deferred.reject(); + } + }); + }); + } + + function promptForEmail(feature) { + + require(['prompt'], function (prompt) { + + prompt({ + text: Globalize.translate('TextPleaseEnterYourEmailAddressForSubscription'), + title: Globalize.translate('HeaderEmailAddress'), + callback: function(email) { + + if (email) { + IapManager.beginPurchase(this.getAttribute('data-feature'), email); + } } }); }); @@ -235,12 +237,15 @@ function onProductUpdated(e, product) { - var currentInfo = currentDisplayingProductInfo; var deferred = currentDisplayingDeferred; - if (currentInfo && deferred) { - if (product.owned && product.id == currentInfo.id) { + if (deferred && product.owned) { + if (currentDisplayingProductInfos.filter(function (p) { + + return product.id == p.id; + + }).length) { clearCurrentDisplayingInfo(); cancelInAppPurchase(); deferred.resolve(); @@ -305,11 +310,9 @@ var deferred = DeferredBuilder.Deferred(); if (name == 'playback') { - validatePlayback(deferred); + validateFeature(name, deferred); } else if (name == 'livetv') { - validateLiveTV(deferred); - } else if (name == 'manageserver') { - validateServerManagement(deferred); + validateFeature(name, deferred); } else if (name == 'sync') { validateSync(deferred); } else { @@ -324,7 +327,7 @@ Events.on(IapManager, 'productupdated', onProductUpdated); } - if (isAndroid()) { + if ($.browser.android) { requirejs(['cordova/android/iap'], onIapManagerLoaded); } else { requirejs(['cordova/iap'], onIapManagerLoaded); diff --git a/dashboard-ui/scripts/globalize.js b/dashboard-ui/scripts/globalize.js index 36956b657..e469e5d39 100644 --- a/dashboard-ui/scripts/globalize.js +++ b/dashboard-ui/scripts/globalize.js @@ -26,8 +26,9 @@ } else { var url = getUrl(name, culture); + var requestUrl = url + "?v=" + window.dashboardVersion; - $.getJSON(url).done(function (dictionary) { + $.getJSON(requestUrl).done(function (dictionary) { dictionaries[url] = dictionary; deferred.resolve(); diff --git a/dashboard-ui/scripts/itemdetailpage.js b/dashboard-ui/scripts/itemdetailpage.js index 0e8721254..55f899492 100644 --- a/dashboard-ui/scripts/itemdetailpage.js +++ b/dashboard-ui/scripts/itemdetailpage.js @@ -45,6 +45,7 @@ getPromise().done(function (item) { reloadFromItem(page, item); + window.scrollTo(0, 0); }); } diff --git a/dashboard-ui/scripts/librarymenu.js b/dashboard-ui/scripts/librarymenu.js index b7d9e90e0..dd820bbad 100644 --- a/dashboard-ui/scripts/librarymenu.js +++ b/dashboard-ui/scripts/librarymenu.js @@ -548,13 +548,7 @@ closeMainDrawer(); - requirejs(["scripts/registrationservices"], function () { - - RegistrationServices.validateFeature('manageserver').done(function () { - Dashboard.navigate('dashboard.html'); - - }); - }); + Dashboard.navigate('dashboard.html'); } function getTopParentId() { diff --git a/dashboard-ui/scripts/mysync.js b/dashboard-ui/scripts/mysync.js index b84997a84..930e4efcf 100644 --- a/dashboard-ui/scripts/mysync.js +++ b/dashboard-ui/scripts/mysync.js @@ -6,8 +6,6 @@ if (LocalSync.isSupported()) { - page.querySelector('.localSyncStatus').classList.remove('hide'); - var status = LocalSync.getSyncStatus(); page.querySelector('.labelSyncStatus').innerHTML = Globalize.translate('LabelLocalSyncStatusValue', status); @@ -20,9 +18,6 @@ page.querySelector('.btnSyncNow').classList.remove('hide'); } - } else { - page.querySelector('.localSyncStatus').classList.add('hide'); - page.querySelector('.syncSpinner').active = false; } }); } @@ -47,7 +42,19 @@ syncNow(page); }); - }).on('pageshow', "#mySyncActivityPage", function () { + require(['localsync'], function () { + + if (LocalSync.isSupported()) { + + page.querySelector('.localSyncStatus').classList.remove('hide'); + + } else { + page.querySelector('.localSyncStatus').classList.add('hide'); + page.querySelector('.syncSpinner').active = false; + } + }); + + }).on('pagebeforeshow', "#mySyncActivityPage", function () { var page = this; diff --git a/dashboard-ui/scripts/site.js b/dashboard-ui/scripts/site.js index ece9ce084..a310d5e25 100644 --- a/dashboard-ui/scripts/site.js +++ b/dashboard-ui/scripts/site.js @@ -100,6 +100,12 @@ var Dashboard = { return; } + // Don't bounce if the failure is in a sync service + if (url.indexOf('/sync') != -1) { + Dashboard.hideLoadingMsg(); + return; + } + // Bounce to the login screen, but not if a password entry fails, obviously if (url.indexOf('/password') == -1 && url.indexOf('/authenticate') == -1 && @@ -238,6 +244,8 @@ var Dashboard = { importCss: function (url) { + url += "?v=" + window.dashboardVersion; + if (!Dashboard.importedCss) { Dashboard.importedCss = []; } @@ -2019,12 +2027,20 @@ var AppInfo = {}; urlArgs += new Date().getTime(); } + var paths = { + velocity: "bower_components/velocity/velocity.min" + }; + + if (Dashboard.isRunningInCordova()) { + paths.prompt = "cordova/prompt"; + } else { + paths.prompt = "components/prompt"; + } + requirejs.config({ urlArgs: urlArgs, - paths: { - "velocity": "bower_components/velocity/velocity.min" - } + paths: paths }); // Required since jQuery is loaded before requireJs diff --git a/dashboard-ui/strings/javascript/javascript.json b/dashboard-ui/strings/javascript/javascript.json index 66c80d34f..20d690e0b 100644 --- a/dashboard-ui/strings/javascript/javascript.json +++ b/dashboard-ui/strings/javascript/javascript.json @@ -788,11 +788,10 @@ "TabScenes": "Scenes", "HeaderUnlockApp": "Unlock App", "MessageUnlockAppWithPurchase": "Unlock the full features of the app with a small one-time purchase.", - "MessageUnlockAppWithPurchaseOrSupporter": "Unlock the full features of the app with a small one-time purchase, or by signing in with an active Emby Supporter Membership.", - "MessageUnlockAppWithSupporter": "Unlock the full features of the app by signing in with an active Emby Supporter Membership.", - "MessageToValidateSupporter": "If you have an active Emby Supporter Membership, simply sign into the app using your Wifi connection within your home network.", + "MessageUnlockAppWithPurchaseOrSupporter": "Unlock the full features of the app with a small one-time purchase, or with an active Emby Premiere subscription.", + "MessageUnlockAppWithSupporter": "Unlock the full features of the app by signing in with an active Emby Premiere subscription.", + "MessageToValidateSupporter": "If you have an active Emby Premiere subscription, simply sign into the app using your Wifi connection within your home network.", "MessagePaymentServicesUnavailable": "Payment services are currently unavailable. Please try again later.", - "ButtonUnlockWithSupporter": "Sign in with Emby Supporter Membership", "MessagePleaseSignInLocalNetwork": "Before proceeding, please ensure that you're connected to your local network using a Wifi or LAN connection.", "ButtonUnlockWithPurchase": "Unlock with Purchase", "ButtonUnlockPrice": "Unlock {0}", @@ -902,6 +901,9 @@ "HeaderEmbyForAndroidHasMoved": "Emby for Android has moved!", "MessageEmbyForAndroidHasMoved": "Emby for Android has moved to a new home in the app store. Please consider checking out the new app. You may continue to use this app for as long as you wish.", "HeaderNextUp": "Next Up", - "HeaderLatestMovies": "Latest Movies", - "HeaderLatestEpisodes": "Latest Episodes" + "HeaderLatestMovies": "Latest Movies", + "HeaderLatestEpisodes": "Latest Episodes", + "EmbyPremiereMonthlyWithPrice": "Emby Premiere Monthly {0}", + "HeaderEmailAddress": "E-Mail Address", + "TextPleaseEnterYourEmailAddressForSubscription": "Please enter your e-mail address." } diff --git a/dashboard-ui/themes/ios.css b/dashboard-ui/themes/ios.css index b2c0ee89d..fe7a7c7ce 100644 --- a/dashboard-ui/themes/ios.css +++ b/dashboard-ui/themes/ios.css @@ -233,3 +233,6 @@ paper-tab { .nowPlayingPage { padding-top: 50px !important; } +.localSyncStatus .labelSyncStatus { + display: none !important; +} \ No newline at end of file diff --git a/dashboard-ui/vulcanize-out.html b/dashboard-ui/vulcanize-out.html index c55ad223c..dd41df9f4 100644 --- a/dashboard-ui/vulcanize-out.html +++ b/dashboard-ui/vulcanize-out.html @@ -9835,7 +9835,8 @@ intent. Closing generally implies that the user acknowledged the content on the it will cancel whenever the user taps outside it or presses the escape key. This behavior is configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties. `close()` should be called explicitly by the implementer when the user interacts with a control -in the overlay element. +in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled' +event. Call `preventDefault` on this event to prevent the overlay from closing. ### Positioning @@ -10006,6 +10007,11 @@ context. You should place this element as a child of `` whenever possible. * Cancels the overlay. */ cancel: function() { + var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelable: true}); + if (cancelEvent.defaultPrevented) { + return; + } + this.opened = false; this._setCanceled(true); },