diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 9350bbf5..809f3e68 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -284,5 +284,29 @@ "unregisteredUser": { "message": "Number is not registered", "description": "Error message displayed when sending to an unregistered user." + }, + "sync": { + "message": "Contacts", + "description": "Label for contact and group sync settings" + }, + "syncExplanation": { + "message": "Import all Signal groups and contacts from your mobile device.", + "description": "Explanatory text for sync settings" + }, + "lastSynced": { + "message": "Last import at", + "description": "Label for date and time of last sync operation" + }, + "syncNow": { + "message": "Import now", + "description": "Label for a button that syncs contacts and groups from your phone" + }, + "syncing": { + "message": "Importing...", + "description": "Label for a disabled sync button while sync is in progress." + }, + "syncFailed": { + "message": "Import failed. Make sure your computer and your phone are connected to the internet.", + "description": "Informational text displayed if a sync operation times out." } } diff --git a/background.html b/background.html index 1be35442..889ef41c 100644 --- a/background.html +++ b/background.html @@ -382,6 +382,8 @@ + diff --git a/js/background.js b/js/background.js index 03c3d3ef..db16bdcc 100644 --- a/js/background.js +++ b/js/background.js @@ -83,6 +83,10 @@ } }); + window.getSyncRequest = function() { + return new textsecure.SyncRequest(textsecure.messaging, messageReceiver); + }; + function init(firstRun) { window.removeEventListener('online', init); if (!textsecure.registration.isDone()) { return; } @@ -112,6 +116,7 @@ var syncRequest = new textsecure.SyncRequest(textsecure.messaging, messageReceiver); syncRequest.addEventListener('success', function() { console.log('sync successful'); + storage.put('synced_at', Date.now()); window.dispatchEvent(new Event('textsecure:contactsync')); }); syncRequest.addEventListener('timeout', function() { diff --git a/js/views/settings_view.js b/js/views/settings_view.js index 0f1be944..17077b42 100644 --- a/js/views/settings_view.js +++ b/js/views/settings_view.js @@ -23,6 +23,10 @@ setting = 'message'; } this.$('#notification-setting-' + setting).attr('checked','checked'); + if (textsecure.storage.user.getDeviceId() != '1') { + var syncView = new SyncView().render(); + this.$('.content').append(syncView.el); + } }, render_attributes: function() { return { @@ -36,4 +40,59 @@ }; } }); + + var SyncView = Whisper.View.extend({ + templateName: 'syncSettings', + className: 'syncSettings', + events: { + 'click .sync': 'sync' + }, + enable: function() { + this.$('.sync').text(i18n('syncNow')); + this.$('.sync').removeAttr('disabled'); + }, + disable: function() { + this.$('.sync').attr('disabled', 'disabled'); + this.$('.sync').text(i18n('syncing')); + }, + onsuccess: function() { + storage.put('synced_at', Date.now()); + console.log('sync successful'); + this.enable(); + this.render(); + }, + ontimeout: function() { + console.log('sync timed out'); + this.$('.synced_at').hide(); + this.$('.sync_failed').show(); + this.enable(); + }, + sync: function() { + this.$('.sync_failed').hide(); + if (textsecure.storage.user.getDeviceId() != '1') { + this.disable(); + var syncRequest = window.getSyncRequest(); + syncRequest.addEventListener('success', this.onsuccess.bind(this)); + syncRequest.addEventListener('timeout', this.ontimeout.bind(this)); + } else { + console.log("Tried to sync from device 1"); + } + }, + render_attributes: function() { + var attrs = { + sync: i18n('sync'), + syncNow: i18n('syncNow'), + syncExplanation: i18n('syncExplanation'), + syncFailed: i18n('syncFailed') + }; + var date = storage.get('synced_at'); + if (date) { + date = new Date(date); + attrs.lastSynced = i18n('lastSynced'); + attrs.syncDate = date.toLocaleDateString(); + attrs.syncTime = date.toLocaleTimeString(); + } + return attrs; + } + }); })(); diff --git a/stylesheets/_index.scss b/stylesheets/_index.scss index ca02380f..990f3dc6 100644 --- a/stylesheets/_index.scss +++ b/stylesheets/_index.scss @@ -253,3 +253,30 @@ input.search { top: -30px; } } +.syncSettings { + button { + float: right; + border: none; + border-radius: $border-radius; + color: white; + font-weight: bold; + line-height: 36px; + padding: 0 20px; + background: $blue; + margin-left: 20px; + margin-bottom: 20px; + + &[disabled=disabled] { + background: $grey; + } + } + .synced_at { + font-size: small; + color: $grey; + } + .sync_failed { + display: none; + font-size: small; + color: red; + } +} diff --git a/stylesheets/manifest.css b/stylesheets/manifest.css index 8addd03b..f4f23741 100644 --- a/stylesheets/manifest.css +++ b/stylesheets/manifest.css @@ -698,6 +698,27 @@ input.search { border-color: transparent transparent #2eace0 transparent; top: -30px; } +.syncSettings button { + float: right; + border: none; + border-radius: 5px; + color: white; + font-weight: bold; + line-height: 36px; + padding: 0 20px; + background: #2090ea; + margin-left: 20px; + margin-bottom: 20px; } + .syncSettings button[disabled=disabled] { + background: #616161; } +.syncSettings .synced_at { + font-size: small; + color: #616161; } +.syncSettings .sync_failed { + display: none; + font-size: small; + color: red; } + .conversation-title { display: block; line-height: 36px;