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