Florianschmidtwelzow has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/260578

Change subject: [WIP/MobileFrontend] Add link preview feature to mobile beta 
mode
......................................................................

[WIP/MobileFrontend] 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.

ToDo:
 * There should be an option to disable this feature (maybe in mobile
   settings, or in the drawer)

Bug: T113243
Change-Id: Idbaae9fe2decd89b73e623a25fbd39464c316fb2
---
M Popups.hooks.php
M extension.json
M i18n/en.json
M i18n/qqq.json
A resources/ext.popups.mobilelinkpreview.init/initLinkPreview.js
A resources/ext.popups.mobilelinkpreview/LinkPreview.less
A resources/ext.popups.mobilelinkpreview/LinkPreviewDrawer.hogan
A resources/ext.popups.mobilelinkpreview/LinkPreviewDrawer.js
8 files changed, 349 insertions(+), 2 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Popups 
refs/changes/78/260578/1

diff --git a/Popups.hooks.php b/Popups.hooks.php
index 2743b02..b02f597 100644
--- a/Popups.hooks.php
+++ b/Popups.hooks.php
@@ -66,6 +66,8 @@
         * @return bool
         */
        public static function onResourceLoaderRegisterModules( ResourceLoader 
$rl ) {
+               global $wgMFResourceFileModuleBoilerplate;
+
                $moduleDependencies = array(
                        'mediawiki.api',
                        'mediawiki.RegExp',
@@ -119,6 +121,49 @@
                        'localBasePath' => __DIR__,
                ) );
 
+               // if MobileFrontend is installed, register mobile popups 
modules
+               // FIXME: Use ExtensionRegistry::getInstance()->isLoaded( 
'MobileFrontend' ) once T88057
+               // is fixed
+               if ( class_exists( 'MobileFrontendHooks' ) && 
MobileContext::singleton()->isBetaGroupMember() ) {
+                       $rl->register( 'ext.popups.mobilelinkpreview.init', 
array(
+                                       'dependencies' => array(
+                                               'mobile.context',
+                                               'mobile.settings',
+                                       ),
+                                       'scripts' => array(
+                                               
'resources/ext.popups.mobilelinkpreview.init/initLinkPreview.js',
+                                       ),
+                                       'targets' => array( 'mobile' ),
+                                       'remoteExtPath' => 'Popups',
+                                       'localBasePath' => __DIR__,
+                               )
+                       );
+
+                       $rl->register( 'ext.popups.mobilelinkpreview', array(
+                                       'dependencies' => array(
+                                               
'ext.popups.mobilelinkpreview.init',
+                                               'mobile.drawers',
+                                       ),
+                                       'scripts' => array(
+                                               
'resources/ext.popups.mobilelinkpreview/LinkPreviewDrawer.js'
+                                       ),
+                                       'templates' => array(
+                                               'LinkPreviewDrawer.hogan' => 
'resources/ext.popups.mobilelinkpreview/LinkPreviewDrawer.hogan',
+                                       ),
+                                       'styles' => array(
+                                               
'resources/ext.popups.mobilelinkpreview/LinkPreview.less',
+                                       ),
+                                       'messages' => array(
+                                               
'popups-mobile-continue-to-article',
+                                               'popups-mobile-dismiss',
+                                       ),
+                                       'targets' => array( 'mobile' ),
+                                       'remoteExtPath' => 'Popups',
+                                       'localBasePath' => __DIR__,
+                               )
+                       );
+               }
+
                return true;
        }
 
@@ -151,6 +196,19 @@
        }
 
        /**
+        * 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
+               if ( MobileContext::singleton()->isBetaGroupMember() ) {
+                       $out->addModules( 'ext.popups.mobilelinkpreview.init' );
+               }
+       }
+
+       /**
         * @param array &$testModules
         * @param ResourceLoader $resourceLoader
         * @return bool
diff --git a/extension.json b/extension.json
index 56111f6..a2a0e26 100644
--- a/extension.json
+++ b/extension.json
@@ -32,6 +32,9 @@
                ],
                "ResourceLoaderGetConfigVars": [
                        "PopupsHooks::onResourceLoaderGetConfigVars"
+               ],
+               "BeforePageDisplayMobile": [
+                       "PopupsHooks::onBeforePageDisplayMobile"
                ]
        },
        "MessagesDirs": {
diff --git a/i18n/en.json b/i18n/en.json
index d1c30d2..5edc08a 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -19,5 +19,7 @@
        "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-article": "Continue to article",
+       "popups-mobile-dismiss": "Dismiss"
 }
diff --git a/i18n/qqq.json b/i18n/qqq.json
index f0d21b1..fe39d56 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -24,5 +24,7 @@
        "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-articl": "Button label visible in the link 
preview drawer. When clicked, the brwoser redirects to the target article.",
+       "popups-mobile-dismiss": "Label of button that dismisses the link 
preview overlay."
 }
diff --git a/resources/ext.popups.mobilelinkpreview.init/initLinkPreview.js 
b/resources/ext.popups.mobilelinkpreview.init/initLinkPreview.js
new file mode 100644
index 0000000..9875cd0
--- /dev/null
+++ b/resources/ext.popups.mobilelinkpreview.init/initLinkPreview.js
@@ -0,0 +1,22 @@
+( function ( M, $ ) {
+       var context = M.require( 'mobile.context/context' ),
+               settings = M.require( 'mobile.settings/settings' );
+
+       if ( !settings.get( 'mf-disable-linkpreview' ) ) {
+               mw.loader.using( 'ext.popups.mobilelinkpreview' ).done( 
function () {
+                       var LinkPreviewDrawer = M.require( 
'ext.popups.mobilelinkpreview/LinkPreviewDrawer' ),
+                               drawer;
+
+                       // show linkpreview only for internal and existing 
links. All others should work
+                       // like "normal" links (redirecting to the target)"
+                       $( '#bodyContent a:not(.new .external)' ).on( 'click', 
function ( ev ) {
+                               if ( !drawer ) {
+                                       drawer = new LinkPreviewDrawer();
+                               }
+                               drawer.loadNew( ev.target.title );
+
+                               return false;
+                       } );
+               } );
+       }
+}( mw.mobileFrontend, jQuery ) );
diff --git a/resources/ext.popups.mobilelinkpreview/LinkPreview.less 
b/resources/ext.popups.mobilelinkpreview/LinkPreview.less
new file mode 100644
index 0000000..fcb6cc1
--- /dev/null
+++ b/resources/ext.popups.mobilelinkpreview/LinkPreview.less
@@ -0,0 +1,51 @@
+@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;
+
+       .mw-ui-button {
+               margin-bottom: 0 !important;
+       }
+}
+
+@media all and (min-width: @wgMFDeviceWidthTablet) {
+       .drawer.linkpreview {
+               padding-left: 30px;
+               padding-right: 30px;
+       }
+}
diff --git a/resources/ext.popups.mobilelinkpreview/LinkPreviewDrawer.hogan 
b/resources/ext.popups.mobilelinkpreview/LinkPreviewDrawer.hogan
new file mode 100644
index 0000000..5899505
--- /dev/null
+++ b/resources/ext.popups.mobilelinkpreview/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.mobilelinkpreview/LinkPreviewDrawer.js 
b/resources/ext.popups.mobilelinkpreview/LinkPreviewDrawer.js
new file mode 100644
index 0000000..1320c4c
--- /dev/null
+++ b/resources/ext.popups.mobilelinkpreview/LinkPreviewDrawer.js
@@ -0,0 +1,203 @@
+( function ( M, $ ) {
+       var LinkPreviewDrawer,
+               Drawer = M.require( 'mobile.drawers/Drawer' ),
+               Button = M.require( 'mobile.startup/Button' ),
+               icons = M.require( 'mobile.startup/icons' );
+
+       LinkPreviewDrawer = Drawer.extend( {
+               /**
+                * @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-article' ),
+                               additionalClassNames: 'linkpreview-continue'
+                       } ).options,
+                       dismissButton: new Button( {
+                               label: mw.msg( 'popups-mobile-dismiss' ),
+                               additionalClassNames: 'linkpreview-dismiss'
+                       } ).options
+               },
+
+               /**
+                * @inheritdoc
+                */
+               template: mw.template.get( 'ext.popups.mobilelinkpreview', 
'LinkPreviewDrawer.hogan' ),
+
+               /**
+                * @inheritdoc
+                */
+               templatePartials: {
+                       Button: Button.prototype.template
+               },
+
+               /**
+                * @inheritdoc
+                */
+               events: $.extend( {}, Drawer.prototype.events, {
+                       'click .linkpreview-continue': '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' );
+                       this.api = new mw.Api();
+               },
+
+               /**
+                * @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] ) {
+                               this.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 );
+}( mw.mobileFrontend, jQuery ) );

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Idbaae9fe2decd89b73e623a25fbd39464c316fb2
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Popups
Gerrit-Branch: master
Gerrit-Owner: Florianschmidtwelzow <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to