jenkins-bot has submitted this change and it was merged.

Change subject: Add link preview feature to mobile beta mode
......................................................................


Add link preview feature to mobile beta mode

Linkpreview will try to fetch a text extract using the api and, if this
doesn't fail, show the extract as a drawer with a button "Continue to
article". If the query fails, it will redirect the user to the article
directly.

Bug: T113243
Depends-On: I6d8c5b80e70c3d8d1a92a70cc91e1b90d598cb0f
Change-Id: Idbaae9fe2decd89b73e623a25fbd39464c316fb2
---
M .jshintrc
M Popups.hooks.php
M extension.json
M i18n/en.json
M i18n/qqq.json
M resources/ext.popups.core.less
A resources/ext.popups.renderer/LinkPreview.less
A resources/ext.popups.renderer/LinkPreviewDrawer.hogan
A resources/ext.popups.renderer/LinkPreviewDrawer.js
A resources/ext.popups.renderer/mobileRenderer.js
A resources/ext.popups.targets/mobileTarget.js
11 files changed, 427 insertions(+), 37 deletions(-)

Approvals:
  Siebrand: Looks good to me, but someone else must approve
  Jdlrobson: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/.jshintrc b/.jshintrc
index 00765b8..f95586e 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -23,6 +23,7 @@
                "mediaWiki",
                "jQuery",
                "moment",
-               "QUnit"
+               "QUnit",
+               "OO"
        ]
 }
\ No newline at end of file
diff --git a/Popups.hooks.php b/Popups.hooks.php
index 35ebb77..263e161 100644
--- a/Popups.hooks.php
+++ b/Popups.hooks.php
@@ -23,11 +23,9 @@
 class PopupsHooks {
        static function getPreferences( User $user, array &$prefs ){
                global $wgExtensionAssetsPath;
-
                if ( self::getConfig()->get( 'PopupsBetaFeature' ) !== true ) {
                        return;
                }
-
                $prefs['popups'] = array(
                        'label-message' => 'popups-message',
                        'desc-message' => 'popups-desc',
@@ -79,7 +77,6 @@
                                'resources/ext.popups.settings.js',
                        ),
                        'styles' => array(
-                               'resources/ext.popups.core.less',
                                'resources/ext.popups.animation.less',
                                'resources/ext.popups.settings.less',
                        ),
@@ -104,6 +101,50 @@
                        'remoteExtPath' => 'Popups',
                        'localBasePath' => __DIR__,
                ) );
+
+               // if MobileFrontend is installed, register mobile popups 
modules
+               if ( ExtensionRegistry::getInstance()->isLoaded( 
'MobileFrontend' )
+                       && self::getConfig()->get( 'EnablePopupsMobile' )
+               ) {
+                       $mobileBoilerplate = array(
+                               'targets' => array( 'mobile' ),
+                               'remoteExtPath' => 'Popups',
+                               'localBasePath' => __DIR__,
+                       );
+
+                       $rl->register( 'ext.popups.targets.mobileTarget', array(
+                                       'dependencies' => array(
+                                               'ext.popups.core',
+                                               
'ext.popups.renderer.mobileRenderer',
+                                       ),
+                                       'scripts' => array(
+                                               
'resources/ext.popups.targets/mobileTarget.js',
+                                       ),
+                               ) + $mobileBoilerplate
+                       );
+
+                       $rl->register( 'ext.popups.renderer.mobileRenderer', 
array(
+                                       'dependencies' => array(
+                                               'ext.popups.core',
+                                               'mobile.drawers',
+                                       ),
+                                       'scripts' => array(
+                                               
'resources/ext.popups.renderer/mobileRenderer.js',
+                                               
'resources/ext.popups.renderer/LinkPreviewDrawer.js',
+                                       ),
+                                       'templates' => array(
+                                               'LinkPreviewDrawer.hogan' => 
'resources/ext.popups.renderer/LinkPreviewDrawer.hogan',
+                                       ),
+                                       'styles' => array(
+                                               
'resources/ext.popups.renderer/LinkPreview.less',
+                                       ),
+                                       'messages' => array(
+                                               
'popups-mobile-continue-to-page',
+                                               'popups-mobile-dismiss',
+                                       ),
+                               ) + $mobileBoilerplate
+                       );
+               }
 
                return true;
        }
@@ -137,6 +178,22 @@
        }
 
        /**
+        * Handler for MobileFrontend's BeforePageDisplay hook, which is only 
called in mobile mode.
+        *
+        * @param OutputPage &$out,
+        * @param Skin &$skin
+        */
+       public static function onBeforePageDisplayMobile( OutputPage &$out, 
Skin &$skin ) {
+               // enable mobile link preview in mobile beta and if the beta 
feature is enabled
+               if (
+                       self::getConfig()->get( 'EnablePopupsMobile' ) &&
+                       MobileContext::singleton()->isBetaGroupMember()
+               ) {
+                       $out->addModules( 'ext.popups.targets.mobileTarget' );
+               }
+       }
+
+       /**
         * @param array &$testModules
         * @param ResourceLoader $resourceLoader
         * @return bool
diff --git a/extension.json b/extension.json
index 6c30318..720557b 100644
--- a/extension.json
+++ b/extension.json
@@ -29,6 +29,9 @@
                ],
                "ResourceLoaderGetConfigVars": [
                        "PopupsHooks::onResourceLoaderGetConfigVars"
+               ],
+               "BeforePageDisplayMobile": [
+                       "PopupsHooks::onBeforePageDisplayMobile"
                ]
        },
        "MessagesDirs": {
@@ -42,7 +45,11 @@
        "config": {
                "@PopupsBetaFeature": "@var bool: Whether the extension should 
be enabled as an opt-in beta feature. If true, the BetaFeatures extension must 
be installed. False by default.",
                "PopupsBetaFeature": false,
-               "PopupsSurveyLink": false
+               "PopupsSurveyLink": false,
+               "EnablePopupsMobile": false
+       },
+       "DefaultUserOptions": {
+               "popupsmobile": "1"
        },
        "ResourceModules": {
                "ext.popups.core": {
@@ -58,6 +65,9 @@
                        "targets": [
                                "desktop",
                                "mobile"
+                       ],
+                       "styles": [
+                               "resources/ext.popups.core.less"
                        ]
                },
                "ext.popups.targets.desktopTarget": {
diff --git a/i18n/en.json b/i18n/en.json
index d1c30d2..4a83ce1 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -19,5 +19,8 @@
        "popups-settings-cancel": "Cancel",
        "popups-settings-help": "You can turn previews back on using a link in 
the footer of the page.",
        "popups-settings-enable": "Enable previews",
-       "popups-send-feedback": "Send Feedback (external link)"
+       "popups-send-feedback": "Send Feedback (external link)",
+       "popups-mobile-continue-to-page": "Continue to page",
+       "popups-mobile-dismiss": "Dismiss",
+       "popups-mobile-message": "Hovercards (mobile)"
 }
diff --git a/i18n/qqq.json b/i18n/qqq.json
index f0d21b1..1b3873d 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -24,5 +24,8 @@
        "popups-settings-cancel": "Cancel button for the setting's 
dialog\n{{Identical|Cancel}}",
        "popups-settings-help": "Help text explaining how to re-enable 
previews",
        "popups-settings-enable": "Link on the footer to enable hovercards if 
its disabled.\n\nSee also:\n* {{msg-mw|Popups-settings-option-off}}",
-       "popups-send-feedback": "Tooltip for the send feedback icon on the 
hovercard. Should mention that its an external link.\n{{Identical|Send 
feedback}}"
+       "popups-send-feedback": "Tooltip for the send feedback icon on the 
hovercard. Should mention that its an external link.\n{{Identical|Send 
feedback}}",
+       "popups-mobile-continue-to-page": "Button label visible in the link 
preview drawer. When clicked, the brwoser redirects to the target page.",
+       "popups-mobile-dismiss": "Label of button that dismisses the link 
preview overlay.",
+       "popups-mobile-message": "Name shown in user preference for the mobile 
hovercards feature of this extension"
 }
diff --git a/resources/ext.popups.core.less b/resources/ext.popups.core.less
index 9618ea7..43eddef 100644
--- a/resources/ext.popups.core.less
+++ b/resources/ext.popups.core.less
@@ -1,4 +1,5 @@
 @import "mediawiki.mixins.less";
+@import "mediawiki.ui/variables";
 
 /* Code adapted from Yair Rand's NavPopupsRestyled.js
  * https://en.wikipedia.org/wiki/User:Yair_rand/NavPopupsRestyled.js
@@ -29,6 +30,36 @@
        bottom: -@size;
        /* @noflip */
        left: @left;
+}
+
+.mwe-popups-icon {
+       display: inline-block;
+       vertical-align: middle;
+       cursor: pointer;
+       margin: 0;
+       margin-top: -5px;
+       height: 24px;
+       width: 30px;
+       border-radius: 2px;
+       background-position: center center;
+       background-repeat: no-repeat;
+       background-size: 24px 24px;
+
+       &:hover {
+               background-color: @colorGray14;
+       }
+
+       &:active {
+               background-color: @colorGray12;
+       }
+}
+
+.mwe-popups-settings-icon {
+       .background-image-svg( "images/cog.svg", "images/cog.png" );
+}
+
+.mwe-popups-survey-icon {
+       .background-image-svg( "images/horn-ltr.svg", "images/horn-ltr.png" );
 }
 
 .mwe-popups {
@@ -97,36 +128,6 @@
 
                > div.mwe-popups-timestamp-recent {
                        color: #00af89;
-               }
-
-               .mwe-popups-icon {
-                       display: inline-block;
-                       vertical-align: middle;
-                       cursor: pointer;
-                       margin: 0;
-                       margin-top: -5px;
-                       height: 24px;
-                       width: 30px;
-                       border-radius: 2px;
-                       background-position: center center;
-                       background-repeat: no-repeat;
-                       background-size: 24px 24px;
-
-                       &:hover {
-                               background-color: #eee;
-                       }
-
-                       &:active {
-                               background-color: #ccc;
-                       }
-               }
-
-               .mwe-popups-settings-icon {
-                       .background-image-svg( "images/cog.svg", 
"images/cog.png" );
-               }
-
-               .mwe-popups-survey-icon {
-                       .background-image-svg( "images/horn-ltr.svg", 
"images/horn-ltr.png" );
                }
        }
 
diff --git a/resources/ext.popups.renderer/LinkPreview.less 
b/resources/ext.popups.renderer/LinkPreview.less
new file mode 100644
index 0000000..44f6677
--- /dev/null
+++ b/resources/ext.popups.renderer/LinkPreview.less
@@ -0,0 +1,52 @@
+@import "minerva.variables";
+
+.drawer.linkpreview {
+       position: fixed;
+       background-color: white;
+       text-align: left;
+       padding: 0 15px 20px;
+
+       &.loading {
+               padding: 10px;
+       }
+}
+
+.linkpreview-overlay {
+       position: fixed;
+       top: 0;
+       left: 0;
+       height: 100%;
+       width: 100%;
+       z-index: @z-indexOverlay;
+       background-color: rgba(0,0,0,0.1);
+       background-attachment: fixed;
+}
+
+.linkpreview-title {
+       font-family: Georgia;
+       font-size: 22px;
+       margin-top: 20px;
+       line-height: 1;
+}
+
+.linkpreview-content {
+       font-size: 15px;
+       margin-top: 20px;
+}
+
+.linkpreview-actions {
+       margin-top: 20px;
+       text-align: right;
+
+       a {
+               margin-bottom: 0 !important;
+               margin-top: 0;
+       }
+}
+
+@media all and (min-width: @deviceWidthTablet) {
+       .drawer.linkpreview {
+               padding-left: 30px;
+               padding-right: 30px;
+       }
+}
diff --git a/resources/ext.popups.renderer/LinkPreviewDrawer.hogan 
b/resources/ext.popups.renderer/LinkPreviewDrawer.hogan
new file mode 100644
index 0000000..5899505
--- /dev/null
+++ b/resources/ext.popups.renderer/LinkPreviewDrawer.hogan
@@ -0,0 +1,6 @@
+<div class="linkpreview">
+       <div class="linkpreview-title"></div>
+       <div class="linkpreview-content"></div>
+       <div 
class="linkpreview-actions">{{#dismissButton}}{{>Button}}{{/dismissButton}}{{#continueButton}}{{>Button}}{{/continueButton}}</div>
+</div>
+{{{spinner}}}
diff --git a/resources/ext.popups.renderer/LinkPreviewDrawer.js 
b/resources/ext.popups.renderer/LinkPreviewDrawer.js
new file mode 100644
index 0000000..9babc49
--- /dev/null
+++ b/resources/ext.popups.renderer/LinkPreviewDrawer.js
@@ -0,0 +1,211 @@
+( function ( mw, M, $, OO ) {
+       var Drawer = M.require( 'mobile.drawers/Drawer' ),
+               Button = M.require( 'mobile.startup/Button' ),
+               icons = M.require( 'mobile.startup/icons' );
+
+       /**
+        * Drawer for the link preview feature on a mobile device
+        *
+        * @class LinkPreviewDrawer
+        * @extends Drawer
+        */
+       function LinkPreviewDrawer() {
+               Drawer.apply( this, arguments );
+       }
+
+       OO.mfExtend( LinkPreviewDrawer, Drawer, {
+               /**
+                * @cfg {Object} defaults Default options hash.
+                * @cfg {string} defaults.spinner html of spinner icon
+                * @cfg {Object} defaults.continueButton HTML of the continue 
button.
+                */
+               defaults: {
+                       spinner: icons.spinner().toHtmlString(),
+                       continueButton: new Button( {
+                               progressive: true,
+                               label: mw.msg( 'popups-mobile-continue-to-page' 
),
+                               additionalClassNames: 'linkpreview-continue'
+                       } ).options,
+                       dismissButton: new Button( {
+                               label: mw.msg( 'popups-mobile-dismiss' ),
+                               additionalClassNames: 'linkpreview-dismiss'
+                       } ).options
+               },
+
+               /**
+                * @inheritdoc
+                */
+               template: mw.template.get( 
'ext.popups.renderer.mobileRenderer', 'LinkPreviewDrawer.hogan' ),
+
+               /**
+                * @inheritdoc
+                */
+               templatePartials: {
+                       Button: Button.prototype.template
+               },
+
+               /**
+                * @inheritdoc
+                */
+               events: $.extend( {}, Drawer.prototype.events, {
+                       'click .linkpreview-continue, .linkpreview-title': 
'onContinueClick',
+                       'click .linkpreview-dismiss': 'hide'
+               } ),
+
+               /**
+                * @inheritdoc
+                */
+               className: 'drawer linkpreview',
+
+               /**
+                * @inheritdoc
+                */
+               closeOnScroll: false,
+
+               /**
+                * Cache for the api queries.
+                *
+                * @property {Object}
+                */
+               cache: {},
+
+               /**
+                * @inheritdoc
+                */
+               postRender: function () {
+                       var $linkpreviewOverlay;
+
+                       Drawer.prototype.postRender.apply( this, arguments );
+
+                       $linkpreviewOverlay = $( '<div>' )
+                               .addClass( 'linkpreview-overlay hidden' );
+                       this.$el.before( $linkpreviewOverlay );
+                       this.$linkpreview = this.$( '.linkpreview' );
+                       this.$spinner = this.$( '.spinner' );
+               },
+
+               /**
+                * @inheritdoc
+                */
+               show: function () {
+                       $( '.linkpreview-overlay' ).removeClass( 'hidden' );
+                       Drawer.prototype.show.apply( this, arguments );
+               },
+
+               /**
+                * @inheritdoc
+                */
+               hide: function () {
+                       Drawer.prototype.hide.apply( this, arguments );
+                       $( '.linkpreview-overlay' ).addClass( 'hidden' );
+               },
+
+               /**
+                * Show the drawer with the initial spinner (and hide the 
content,
+                * that was (maybe) already added.
+                *
+                * @return {Object} this
+                */
+               showWithSpinner: function () {
+                       this.$spinner.removeClass( 'hidden' );
+                       this.$el.addClass( 'loading' );
+                       this.$linkpreview.addClass( 'hidden' );
+                       this.show();
+
+                       return this;
+               },
+
+               /**
+                * Hide the spinner (if any) and show the content of the drawer.
+                *
+                * @return {Object} this
+                */
+               showContent: function () {
+                       this.$spinner.addClass( 'hidden' );
+                       this.$el.removeClass( 'loading' );
+                       this.$linkpreview.removeClass( 'hidden' );
+
+                       return this;
+               },
+
+               /**
+                * Load content from a given title, show it or redirect to this
+                * title if something went wrong.
+                *
+                * @param {string} title The target title
+                */
+               loadNew: function ( title ) {
+                       var self = this;
+
+                       this.showWithSpinner();
+                       this.title = title;
+
+                       if ( !this.cache[ title ] ) {
+                               mw.popups.api.get( {
+                                       action: 'query',
+                                       titles: title,
+                                       prop: 'extracts',
+                                       explaintext: true,
+                                       exintro: true,
+                                       exchars: 140,
+                                       formatversion: 2
+                               } ).done( function ( result ) {
+                                       var data;
+
+                                       if ( result.query.pages[ 0 ] ) {
+                                               data = result.query.pages[ 0 ];
+                                               self
+                                                       .setTitle( data.title )
+                                                       .setContent( 
data.extract )
+                                                       .showContent();
+
+                                               self.cache[ title ] = data;
+                                       } else {
+                                               self.onContinueClick();
+                                       }
+                               } ).fail( function () {
+                                       self.onContinueClick();
+                               } );
+                       } else {
+                               self
+                                       .setTitle( this.cache[ title ].title )
+                                       .setContent( this.cache[ title 
].extract )
+                                       .showContent();
+                       }
+               },
+
+               /**
+                * Replace the current visible title with the given one.
+                *
+                * @param {string} title The new title (HTML isn't supported)
+                * @return {Object} this
+                */
+               setTitle: function ( title ) {
+                       this.$( '.linkpreview-title' ).text( title );
+
+                       return this;
+               },
+
+               /**
+                * Replace the current visible content with the given one.
+                *
+                * @param {string} content The new content (HTML isn't 
supported)
+                * @return {Object} this
+                */
+               setContent: function ( content ) {
+                       this.$( '.linkpreview-content' ).text( content );
+
+                       return this;
+               },
+
+               /**
+                * Handle the click on the "continue to article" button and 
redirect to the
+                * title (this.title).
+                */
+               onContinueClick: function () {
+                       window.location.href = mw.util.getUrl( this.title );
+               }
+       } );
+
+       M.define( 'ext.popups.mobilelinkpreview/LinkPreviewDrawer', 
LinkPreviewDrawer );
+}( mediaWiki, mediaWiki.mobileFrontend, jQuery, OO ) );
diff --git a/resources/ext.popups.renderer/mobileRenderer.js 
b/resources/ext.popups.renderer/mobileRenderer.js
new file mode 100644
index 0000000..805d761
--- /dev/null
+++ b/resources/ext.popups.renderer/mobileRenderer.js
@@ -0,0 +1,31 @@
+( function ( $, mw, M ) {
+       /**
+        * @class mw.popups.render
+        * @singleton
+        */
+       mw.popups.render = {};
+
+       /**
+        * Render a new LinkPreviewDrawer
+        *
+        * @method render
+        * @param {Object} link
+        * @param {Object} event
+        */
+       mw.popups.render.render = function ( link, event ) {
+               var LinkPreviewDrawer = M.require( 
'ext.popups.mobilelinkpreview/LinkPreviewDrawer' );
+
+               // Ignore if its meant to call a function
+               // TODO: Remove this when adding reference popups
+               if ( link.attr( 'href' ) === '#' ) {
+                       return;
+               }
+
+               if ( !mw.popups.$popup ) {
+                       mw.popups.$popup = new LinkPreviewDrawer();
+               }
+               mw.popups.$popup.loadNew( event.target.title );
+               event.preventDefault();
+       };
+
+} )( jQuery, mediaWiki, mediaWiki.mobileFrontend );
diff --git a/resources/ext.popups.targets/mobileTarget.js 
b/resources/ext.popups.targets/mobileTarget.js
new file mode 100644
index 0000000..a7fadb4
--- /dev/null
+++ b/resources/ext.popups.targets/mobileTarget.js
@@ -0,0 +1,15 @@
+( function ( $, mw ) {
+       // FIXME: There should be a way to turn this off
+       mw.popups.enabled = true;
+
+       /**
+        * Triggers when a popup should be rendered.
+        */
+       mw.popups.triggers = 'click';
+
+       mw.hook( 'wikipage.content' ).add( function ( $content ) {
+               mw.popups.$content = $content;
+               mw.popups.setupTriggers( mw.popups.selectPopupElements() );
+       } );
+
+} )( jQuery, mediaWiki );

-- 
To view, visit https://gerrit.wikimedia.org/r/260578
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Idbaae9fe2decd89b73e623a25fbd39464c316fb2
Gerrit-PatchSet: 15
Gerrit-Project: mediawiki/extensions/Popups
Gerrit-Branch: master
Gerrit-Owner: Florianschmidtwelzow <florian.schmidt.stargatewis...@gmail.com>
Gerrit-Reviewer: Florianschmidtwelzow <florian.schmidt.stargatewis...@gmail.com>
Gerrit-Reviewer: Jdlrobson <jrob...@wikimedia.org>
Gerrit-Reviewer: Siebrand <siebr...@kitano.nl>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to