From 85d4f9797dbaacb53892ebd9c9b19ed610a94ad9 Mon Sep 17 00:00:00 2001 From: antelle Date: Sun, 16 Apr 2017 18:05:58 +0200 Subject: [PATCH] fix #298: support cloud storages in iOS homescreen apps --- app/scripts/app.js | 9 +++---- app/scripts/comp/auth-receiver.js | 13 ++++++++-- app/scripts/storage/storage-base.js | 38 ++++++++++++++++++++-------- app/scripts/util/feature-detector.js | 3 ++- app/styles/areas/_settings.scss | 1 + release-notes.md | 1 + 6 files changed, 46 insertions(+), 19 deletions(-) diff --git a/app/scripts/app.js b/app/scripts/app.js index d54a22b5..38e74e2b 100644 --- a/app/scripts/app.js +++ b/app/scripts/app.js @@ -15,14 +15,15 @@ const ExportApi = require('./comp/export-api'); const SettingsManager = require('./comp/settings-manager'); const PluginManager = require('./plugins/plugin-manager'); const Launcher = require('./comp/launcher'); +const FeatureDetector = require('./util/feature-detector'); const KdbxwebInit = require('./util/kdbxweb-init'); const Locale = require('./util/locale'); const ready = Launcher && Launcher.ready || $; ready(() => { - if (isPopup()) { - return AuthReceiver.receive(); + if (FeatureDetector.isPopup && AuthReceiver.receive()) { + return; } loadMixins(); @@ -34,10 +35,6 @@ ready(() => { .then(loadRemoteConfig) .then(showApp); - function isPopup() { - return (window.parent !== window.top) || window.opener; - } - function loadMixins() { require('./mixins/view'); require('./helpers'); diff --git a/app/scripts/comp/auth-receiver.js b/app/scripts/comp/auth-receiver.js index b8ba8536..eb1a6045 100644 --- a/app/scripts/comp/auth-receiver.js +++ b/app/scripts/comp/auth-receiver.js @@ -1,9 +1,18 @@ +const FeatureDetector = require('../util/feature-detector'); +const Storage = require('../storage'); + const AuthReceiver = { receive: function() { const opener = window.opener || window.parent; const message = this.urlArgsToMessage(window.location.href); - opener.postMessage(message, window.location.origin); - window.close(); + if (FeatureDetector.isStandalone) { + Storage[sessionStorage.authStorage].handleOAuthReturnMessage(message); + return false; + } else { + opener.postMessage(message, window.location.origin); + window.close(); + return true; + } }, urlArgsToMessage: function(url) { diff --git a/app/scripts/storage/storage-base.js b/app/scripts/storage/storage-base.js index 37fdeb3c..82b67b4e 100644 --- a/app/scripts/storage/storage-base.js +++ b/app/scripts/storage/storage-base.js @@ -3,6 +3,7 @@ const Logger = require('../util/logger'); const AppSettingsModel = require('../models/app-settings-model'); const RuntimeDataModel = require('../models/runtime-data-model'); const Links = require('../const/links'); +const FeatureDetector = require('../util/feature-detector'); const MaxRequestRetries = 3; @@ -32,6 +33,12 @@ _.extend(StorageBase.prototype, { } } this.logger = new Logger('storage-' + this.name); + if (this._oauthReturnMessage) { + this.logger.debug('OAuth return message', this._oauthReturnMessage); + this._oauthProcessReturn(this._oauthReturnMessage, _.noop); + delete this._oauthReturnMessage; + delete sessionStorage.authStorage; + } return this; }, @@ -39,6 +46,10 @@ _.extend(StorageBase.prototype, { this.enabled = enabled; }, + handleOAuthReturnMessage(message) { + this._oauthReturnMessage = message; + }, + _xhr: function(config) { const xhr = new XMLHttpRequest(); if (config.responseType) { @@ -104,6 +115,9 @@ _.extend(StorageBase.prototype, { location: 'yes' }; settings = Object.keys(settings).map(key => key + '=' + settings[key]).join(','); + if (FeatureDetector.isStandalone) { + sessionStorage.authStorage = this.name; + } return window.open(url, title, settings); }, @@ -147,21 +161,25 @@ _.extend(StorageBase.prototype, { } Backbone.off('popup-closed', popupClosed); window.removeEventListener('message', windowMessage); - const token = this._oauthMsgToToken(e.data); - if (token.error) { - this.logger.error('OAuth error', token.error, token.errorDescription); - callback(token.error); - } else { - this._oauthToken = token; - this.runtimeData.set(this.name + 'OAuthToken', token); - this.logger.debug('OAuth token received'); - callback(); - } + this._oauthProcessReturn(e.data, callback); }; Backbone.on('popup-closed', popupClosed); window.addEventListener('message', windowMessage); }, + _oauthProcessReturn: function(message, callback) { + const token = this._oauthMsgToToken(message); + if (token.error) { + this.logger.error('OAuth error', token.error, token.errorDescription); + callback(token.error); + } else { + this._oauthToken = token; + this.runtimeData.set(this.name + 'OAuthToken', token); + this.logger.debug('OAuth token received'); + callback(); + } + }, + _oauthMsgToToken: function(data) { if (data.error || !data.token_type) { return { error: data.error || 'no token', errorDescription: data.error_description }; diff --git a/app/scripts/util/feature-detector.js b/app/scripts/util/feature-detector.js index d7ee4a16..8aeefbfe 100644 --- a/app/scripts/util/feature-detector.js +++ b/app/scripts/util/feature-detector.js @@ -6,7 +6,8 @@ const FeatureDetector = { isWindows: navigator.platform.indexOf('Win') >= 0, isiOS: /iPad|iPhone|iPod/i.test(navigator.userAgent), isMobile: MobileRegex.test(navigator.userAgent) || screen.width < MinDesktopScreenWidth, - + isPopup: !!((window.parent !== window.top) || window.opener), + isStandalone: !!(navigator.standalone || window.matchMedia('(display-mode: standalone)').matches), isBeta: window.location.href.toLowerCase().indexOf('beta.') > 0, actionShortcutSymbol: function(formatting) { diff --git a/app/styles/areas/_settings.scss b/app/styles/areas/_settings.scss index 8e04a790..8b0e29a8 100644 --- a/app/styles/areas/_settings.scss +++ b/app/styles/areas/_settings.scss @@ -137,6 +137,7 @@ &__logs { @include user-select(text); margin-top: $base-padding-v; + word-break: break-all; &-log { margin: 0; white-space: pre-wrap; diff --git a/release-notes.md b/release-notes.md index 7f53b995..8173ce5a 100644 --- a/release-notes.md +++ b/release-notes.md @@ -4,6 +4,7 @@ Release notes `+` plugins `*` translations are available only as plugins `*` Dropbox API V2 +`+` support cloud providers in iOS homescreen apps `+` mobile field editing improvements `+` file path hint in recent files list `+` cacheConfigSettings config option