[gnome-maps/wip/osm-edit: 12/12] osmEdit: WIP, implement OSM account dialog



commit b594f93b815ad4da06e1d1f836fcf6b8d6f49738
Author: Marcus Lundblad <ml update uu se>
Date:   Sat Nov 7 12:17:07 2015 +0100

    osmEdit: WIP, implement OSM account dialog

 data/org.gnome.Maps.data.gresource.xml |    1 +
 data/ui/app-menu.ui                    |    6 +
 data/ui/osm-account-dialog.ui          |  323 ++++++++++++++++++++++++++++++++
 src/application.js                     |    7 +
 src/org.gnome.Maps.src.gresource.xml   |    1 +
 src/osmAccountDialog.js                |  168 +++++++++++++++++
 src/osmEdit.js                         |    7 +
 7 files changed, 513 insertions(+), 0 deletions(-)
---
diff --git a/data/org.gnome.Maps.data.gresource.xml b/data/org.gnome.Maps.data.gresource.xml
index eb68093..04bd08c 100644
--- a/data/org.gnome.Maps.data.gresource.xml
+++ b/data/org.gnome.Maps.data.gresource.xml
@@ -13,6 +13,7 @@
     <file preprocess="xml-stripblanks">ui/main-window.ui</file>
     <file preprocess="xml-stripblanks">ui/map-bubble.ui</file>
     <file preprocess="xml-stripblanks">ui/notification.ui</file>
+    <file preprocess="xml-stripblanks">ui/osm-account-dialog.ui</file>
     <file preprocess="xml-stripblanks">ui/osm-edit-dialog.ui</file>
     <file preprocess="xml-stripblanks">ui/place-bubble.ui</file>
     <file preprocess="xml-stripblanks">ui/place-entry.ui</file>
diff --git a/data/ui/app-menu.ui b/data/ui/app-menu.ui
index a40eb08..1dd897e 100644
--- a/data/ui/app-menu.ui
+++ b/data/ui/app-menu.ui
@@ -3,6 +3,12 @@
   <menu id="app-menu">
     <section>
       <item>
+        <attribute name="action">app.osm-account-setup</attribute>
+        <attribute name="label" translatable="yes">Setup OpenStreetMap Account</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
         <attribute name="action">win.about</attribute>
         <attribute name="label" translatable="yes">About</attribute>
       </item>
diff --git a/data/ui/osm-account-dialog.ui b/data/ui/osm-account-dialog.ui
new file mode 100644
index 0000000..c60a6b3
--- /dev/null
+++ b/data/ui/osm-account-dialog.ui
@@ -0,0 +1,323 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.12"/>
+  <template class="Gjs_OSMAccountDialog" parent="GtkDialog">
+    <property name="can_focus">False</property>
+    <property name="type">popup</property>
+    <property name="type_hint">dialog</property>
+    <property name="width_request">500</property>
+    <property name="title" translatable="yes">OpenStreetMap Account</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="contentArea">
+        <child>
+          <object class="GtkStack" id="stack">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkGrid">
+                <property name="visible">True</property>
+                <property name="row-spacing">10</property>
+                <property name="margin">20</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Sign 
in to edit maps&lt;/span&gt;</property>
+                    <property name="use_markup">True</property>
+                    <property name="visible">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="halign">GTK_ALIGN_CENTER</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">Help to improve the map, using an
+OpenStreetMap account.</property>
+                    <property name="visible">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="halign">GTK_ALIGN_CENTER</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkGrid">
+                    <property name="visible">True</property>
+                    <property name="column-spacing">10</property>
+                    <property name="row-spacing">10</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Email</property>
+                        <property name="halign">GTK_ALIGN_END</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="emailEntry">
+                        <property name="visible">True</property>
+                        <property name="hexpand">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">0</property>
+                        <property name="width">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Password</property>
+                        <property name="halign">GTK_ALIGN_END</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="passwordEntry">
+                        <property name="visible">True</property>
+                        <property name="hexpand">True</property>
+                        <property name="input-purpose">GTK_INPUT_PURPOSE_PASSWORD</property>
+                        <property name="visibility">False</property>
+                        <property name="caps-lock-warning">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">1</property>
+                        <property name="width">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinner" id="signInSpinner">
+                        <property name="visible">False</property>
+                        <property name="height_request">16</property>
+                        <property name="width_request">16</property>
+                        <property name="can_focus">False</property>
+                        <property name="active">True</property>
+                        <property name="halign">GTK_ALIGN_END</property>
+                        <property name="hexpand">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="signInButton">
+                        <property name="visible">True</property>
+                        <property name="halign">GTK_ALIGN_END</property>
+                        <property name="label" translatable="yes">Sign In</property>
+                        <property name="sensitive">False</property>
+                        <style>
+                          <class name="suggested-action"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">2</property>
+                        <property name="top_attach">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLinkButton" id="signUpLinkButton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">Don't have an account?</property>
+                    <property name="uri">https://www.openstreetmap.org/user/new?referer=gnome-maps</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="resetPasswordLabel">
+                    <property name="visible">False</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes"
+                      comments="The label should contain the link to the OSM reset password page with a 
translated title">Sorry, that didn't work. Please try again, or visit
+&lt;a href="https://www.openstreetmap.org/user/forgot-password"&gt;OpenStreetMap&lt;/a&gt; to reset your 
password.</property>
+                    <property name="use-markup">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">4</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="verificationFailedLabel">
+                    <property name="visible">False</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">The verification code didn't match, please try 
again.</property>
+                    <property name="use-markup">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">5</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">sign-in</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkGrid" id="verifyGrid">
+                <property name="visible">True</property>
+                <property name="row-spacing">10</property>
+                <property name="margin">20</property>
+                <child>
+                  <object class="WebKitWebView" id="verifyView">
+                    <property name="visible">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkGrid">
+                    <property name="visible">True</property>
+                    <property name="column-spacing">10</property>
+                    <property name="row-spacing">10</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Enter verification code shown 
above</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="verificationEntry">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="verifyButton">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="label" translatable="yes">Verify</property>
+                        <property name="hexpand">False</property>
+                        <property name="halign">GTK_ALIGN_END</property>
+                        <style>
+                          <class name="suggested-action"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">verify</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkGrid">
+                <property name="visible">True</property>
+                <property name="row-spacing">10</property>
+                <property name="margin">20</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">&lt;span weight="bold" 
size="x-large"&gt;Signed In&lt;/span&gt;</property>
+                    <property name="use_markup">True</property>
+                    <property name="visible">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="halign">GTK_ALIGN_CENTER</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="label" translatable="yes">Your OpenStreetMap account active.</property>
+                    <property name="visible">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="halign">GTK_ALIGN_CENTER</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="icon-name">avatar-default-symbolic</property>
+                    <property name="pixel-size">64</property>
+                    <property name="opacity">0.33</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="signedInUserLabel">
+                    <property name="visible">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="signOutButton">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Sign Out</property>
+                    <property name="halign">GTK_ALIGN_CENTER</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">4</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">logged-in</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/application.js b/src/application.js
index d21826d..552d60b 100644
--- a/src/application.js
+++ b/src/application.js
@@ -149,6 +149,10 @@ const Application = new Lang.Class({
         this._mainWindow.destroy();
     },
 
+    _onOsmAccountSetupActivate: function() {
+        osmEdit.showAccountDialog(this._mainWindow);
+    },
+
     _addContacts: function() {
         contactStore.get_contacts().forEach(function(contact) {
             contact.geocode(function() {
@@ -207,6 +211,9 @@ const Application = new Lang.Class({
             'show-contact': {
                 paramType: 's',
                 onActivate: this._onShowContactActivate.bind(this)
+            },
+            'osm-account-setup': {
+                onActivate: this._onOsmAccountSetupActivate.bind(this)
             }
         });
 
diff --git a/src/org.gnome.Maps.src.gresource.xml b/src/org.gnome.Maps.src.gresource.xml
index 98fae43..65b83ba 100644
--- a/src/org.gnome.Maps.src.gresource.xml
+++ b/src/org.gnome.Maps.src.gresource.xml
@@ -28,6 +28,7 @@
     <file>mapWalker.js</file>
     <file>notification.js</file>
     <file>notificationManager.js</file>
+    <file>osmAccountDialog.js</file>
     <file>osmConnection.js</file>
     <file>osmEdit.js</file>
     <file>osmEditDialog.js</file>
diff --git a/src/osmAccountDialog.js b/src/osmAccountDialog.js
new file mode 100644
index 0000000..1a97867
--- /dev/null
+++ b/src/osmAccountDialog.js
@@ -0,0 +1,168 @@
+/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
+/* vim: set et ts=4 sw=4: */
+/*
+ * Copyright (c) 2015 Marcus Lundblad
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Marcus Lundblad <ml update uu se>
+ */
+
+const GObject = imports.gi.GObject;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const WebKit = imports.gi.WebKit;
+
+const Application = imports.application;
+
+const OSMAccountDialog = new Lang.Class({
+    Name: 'OSMAccountDialog',
+    Extends: Gtk.Dialog,
+    Template: 'resource:///org/gnome/Maps/ui/osm-account-dialog.ui',
+    InternalChildren: ['stack',
+                       'emailEntry',
+                       'passwordEntry',
+                       'signInButton',
+                       'signInSpinner',
+                       'signUpLinkButton',
+                       'resetPasswordLabel',
+                       'verifyView',
+                       'verificationEntry',
+                       'verifyButton',
+                       'verificationFailedLabel',
+                       'signedInUserLabel',
+                       'signOutButton'],
+
+    _init: function(params) {
+        /* This is a construct-only property and cannot be set by GtkBuilder */
+        params.use_header_bar = true;
+        GObject.type_ensure(WebKit.WebView);
+
+        this.parent(params);
+
+        this._emailEntry.connect('changed',
+                                 this._onCredentialsChanged.bind(this));
+        this._passwordEntry.connect('changed',
+                                    this._onCredentialsChanged.bind(this));
+        this._signInButton.connect('clicked',
+                                   this._onSignInButtonClicked.bind(this));
+        this._verifyButton.connect('clicked',
+                                   this._onVerifyButtonClicked.bind(this));
+        this._verificationEntry.connect('changed',
+                                        this._onVerificationEntryChanged.bind(this));
+        this._signOutButton.connect('clicked',
+                                    this._onSignOutButtonClicked.bind(this));
+
+        /* if the user is logged in, show the logged-in view */
+        if (Application.osmEdit.isSignedIn) {
+            this._signedInUserLabel.label = Application.osmEdit.username;
+            this._stack.visible_child_name = 'logged-in';
+        }
+    },
+
+    _onCredentialsChanged: function() {
+        let email = this._emailEntry.text;
+        let password = this._passwordEntry.text;
+
+        /* make sign in button sensitive if credential have been entered
+            TODO: should we try to validate email addresses? */
+        this._signInButton.sensitive =
+            email && email.length > 0 && password && password.length > 0;
+    },
+
+    _onSignInButtonClicked: function() {
+        /* turn on signing in spinner and desensisize credential entries */
+        this._signInSpinner.visible = true;
+        this._signInButton.sensitive = false;
+        this._emailEntry.sensitive = false;
+        this._passwordEntry.sensitive = false;
+        this._signUpLinkButton.visible = false;
+
+        Application.osmEdit.performOAuthSignIn(this._emailEntry.text,
+                                               this._passwordEntry.text,
+                                               this._onOAuthSignInPerformed.bind(this));
+    },
+
+    _onOAuthSignInPerformed: function(success, verificationPage) {
+        if (success) {
+            /* switch to the verification view and show the verification
+               page */
+            this._verifyView.load_html_string(verificationPage,
+                                              'https://www.openstreetmap.org/');
+            this._stack.visible_child_name = 'verify';
+        } else {
+            /* clear password entry */
+            this._passwordEntry.text = '';
+            /* show the password reset link */
+            this._resetPasswordLabel.visible = true;
+        }
+
+        this._signInSpinner.visible = false;
+        /* re-sensisize credential entries */
+        this._emailEntry.sensitive = true;
+        this._passwordEntry.sensitive = true;
+    },
+
+    _onVerifyButtonClicked: function() {
+        /* allow copying the leading space between the "The verification is"
+           label and the code */
+        let verificationCode = this._verificationEntry.text.trim();
+
+        /* Since the text shown on OSM's OAuth authorization verification form
+           is a bit unclear with a trailing period after the verification code,
+           let's strip that off if the user copied that over. */
+        if (verificationCode.charAt(verificationCode.length - 1) === '.') {
+            verificationCode =
+                verificationCode.substring(0, verificationCode.length - 1);
+        }
+
+        Application.osmEdit.requestOAuthAccessToken(verificationCode,
+                                                    this._onOAuthAccessTokenRequested.bind(this));
+    },
+
+    _onVerificationEntryChanged: function() {
+        this._verifyButton.sensitive =
+            this._verificationEntry.text &&
+            this._verificationEntry.text.length > 0;
+    },
+
+    _onOAuthAccessTokenRequested: function(success) {
+        if (success) {
+            /* update the username label */
+            this._signedInUserLabel.label = Application.osmEdit.username;
+            /* switch to the logged in view and reset the state in case
+               the user signs out and start over again */
+            this._resetPasswordLabel.visible = false;
+            this._verificationFailedLabel = false;
+            this._signUpLinkButton.visible = true;
+            this._stack.visible_child_name = 'logged-in';
+        } else {
+            /* switch back to the sign-in view, and show a label indicating
+               that verification failed */
+            this._resetPasswordLabel.visible = false;
+            this._signUpLinkButton.visible = false;
+            this._verificationFailedLabel.visible = true;
+            this._signInButton.sensitive = true;
+            this._stack.visible_child_name = 'sign-in';
+        }
+        /* reset verification code entry */
+        this._verificationEntry.text = '';
+    },
+
+    _onSignOutButtonClicked: function() {
+        Application.osmEdit.signOut();
+        this._stack.visible_child_name = 'sign-in';
+    }
+});
diff --git a/src/osmEdit.js b/src/osmEdit.js
index 5f93ce0..6821009 100644
--- a/src/osmEdit.js
+++ b/src/osmEdit.js
@@ -56,6 +56,13 @@ const OSMEdit = new Lang.Class({
         return response;
     },
 
+    showAccountDialog: function(parentWindow) {
+        let dialog = new OSMAccountDialog.OSMAccountDialog({transient_for: parentWindow });
+        let response = dialog.run();
+        dialog.destroy();
+        return response;
+    },
+
     fetchObject: function(place, callback, cancellable) {
         let osmType = Utils.osmTypeToString(place.osmType);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]