Phuedx has uploaded a new change for review.
https://gerrit.wikimedia.org/r/321851
Change subject: WIP: Add footer link change listener
......................................................................
WIP: Add footer link change listener
TODO:
* Integration tests.
* Documentation (in doc/change_listener.md).
Change-Id: Ibe6934058327c7f02f7d8092e74a667a5a1c600a
---
M extension.json
M resources/ext.popups/boot.js
A resources/ext.popups/changeListener.js
A resources/ext.popups/footerLinkChangeListener.js
M resources/ext.popups/index.js
A tests/qunit/ext.popups/changeListener.test.js
6 files changed, 190 insertions(+), 2 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Popups
refs/changes/51/321851/1
diff --git a/extension.json b/extension.json
index a7330c9..1562708 100644
--- a/extension.json
+++ b/extension.json
@@ -64,6 +64,8 @@
"resources/ext.popups/processLinks.js",
"resources/ext.popups/gateway.js",
"resources/ext.popups/reducers.js",
+ "resources/ext.popups/changeListener.js",
+
"resources/ext.popups/footerLinkChangeListener.js",
"resources/ext.popups/boot.js"
],
"templates": {
diff --git a/resources/ext.popups/boot.js b/resources/ext.popups/boot.js
index 00d770a..a6787d0 100644
--- a/resources/ext.popups/boot.js
+++ b/resources/ext.popups/boot.js
@@ -1,4 +1,4 @@
-( function ( mw, Redux, ReduxThunk ) {
+( function ( mw, Redux, ReduxThunk, $ ) {
var BLACKLISTED_LINKS = [
'.extiw',
'.image',
@@ -22,6 +22,23 @@
mw.user,
userSettings
);
+ }
+
+ /**
+ * Subscribes the registered change listeners to the
+ * [store](http://redux.js.org/docs/api/Store.html#store).
+ *
+ * Change listeners are registered by setting a property on
+ * `mw.popups.changeListeners`.
+ *
+ * @param {Store} store
+ */
+ function registerChangeListeners( store ) {
+ $.each( mw.popups.changeListeners, function ( _,
changeListenerFactory ) {
+ var changeListener = changeListenerFactory();
+
+ store.subscribe( mw.popups.createChangeListener(
changeListener, store ) );
+ } );
}
/**
@@ -53,6 +70,7 @@
) )
);
actions = createBoundActions( store );
+ registerChangeListeners( store );
actions.boot(
isUserInCondition(),
@@ -81,4 +99,4 @@
} );
} );
-}( mediaWiki, Redux, ReduxThunk ) );
+}( mediaWiki, Redux, ReduxThunk, jQuery ) );
diff --git a/resources/ext.popups/changeListener.js
b/resources/ext.popups/changeListener.js
new file mode 100644
index 0000000..5dcaed4
--- /dev/null
+++ b/resources/ext.popups/changeListener.js
@@ -0,0 +1,44 @@
+( function ( mw ) {
+
+ /**
+ * @typedef {Function} ext.popups.ChangeListener
+ * @param {Object} prevState The previous state
+ * @param {Object} state The current state
+ */
+
+ /**
+ * Creates a change listener, which is bound, **but not subscribed**,
to the
+ * [store](http://redux.js.org/docs/api/Store.html).
+ *
+ * A change listener is a function that is only invoked when the state
in the
+ * [store](http://redux.js.org/docs/api/Store.html) changes. N.B. that
there
+ * may not be a 1:1 correspondence with actions being dispatched to the
store
+ * and the state in the store changing.
+ *
+ * See
[Store#subscribe](http://redux.js.org/docs/api/Store.html#subscribe)
+ * for more information about what change listeners may and may not do.
+ *
+ * @param {ext.popups.ChangeListener} callback
+ * @param {Store} store
+ * @return {Function} A function that should be passed to
+ *
[Store#subscribe]([Store#subscribe](http://redux.js.org/docs/api/Store.html#subscribe)
+ */
+ mw.popups.createChangeListener = function ( callback, store ) {
+ // This function is based on the example in [the documentation
for
+ //
Store#subscribe](http://redux.js.org/docs/api/Store.html#subscribe),
+ // which was written by Dan Abramov.
+
+ var state;
+
+ return function () {
+ var prevState = state;
+
+ state = store.getState();
+
+ if ( prevState !== state ) {
+ callback( prevState, state );
+ }
+ };
+ };
+
+}( mediaWiki ) );
diff --git a/resources/ext.popups/footerLinkChangeListener.js
b/resources/ext.popups/footerLinkChangeListener.js
new file mode 100644
index 0000000..c5df9fc
--- /dev/null
+++ b/resources/ext.popups/footerLinkChangeListener.js
@@ -0,0 +1,63 @@
+( function ( mw, $ ) {
+
+ /**
+ * Creates the link element and appends it to the footer element.
+ *
+ * The following elements are considered to be the footer element
(highest
+ * priority to lowest):
+ *
+ * # `#footer-places`
+ * # `#f-list`
+ * # The parent element of `#footer li`, which is either an `ol` or
`ul`.
+ *
+ * @return {jQuery} The link element
+ */
+ function createFooterLink() {
+ var $link = $( '<li>' ).append(
+ $( '<a>' )
+ .attr( 'href', '#' )
+ .text( mw.message(
'popups-settings-enable' ).text() )
+ ),
+ $footer;
+
+ // As yet, we don't know whether the link should be visible.
+ $link.hide();
+
+ // From
https://en.wikipedia.org/wiki/MediaWiki:Gadget-ReferenceTooltips.js,
+ // which was written by Yair rand
<https://en.wikipedia.org/wiki/User:Yair_rand>.
+ $footer = $( '#footer-places, #f-list' );
+
+ if ( $footer.length === 0 ) {
+ $footer = $( '#footer li' ).parent();
+ }
+
+ $footer.append( $link );
+
+ return $link;
+ }
+
+ /**
+ * Creates the footer link change listener.
+ *
+ * When `state.preview.enabled` is truthy, then the "Enable previews"
link is
+ * shown in the footer menu; otherwise, the link is hidden.
+ *
+ * @return {Function}
+ */
+ mw.popups.changeListeners.footerLink = function () {
+ var $footerLink;
+
+ return function ( prevState, state ) {
+ if ( $footerLink === undefined ) {
+ $footerLink = createFooterLink();
+ }
+
+ if ( state.preview.enabled ) {
+ $footerLink.hide();
+ } else {
+ $footerLink.show();
+ }
+ };
+ };
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/ext.popups/index.js b/resources/ext.popups/index.js
index 378fd81..9e3a872 100644
--- a/resources/ext.popups/index.js
+++ b/resources/ext.popups/index.js
@@ -2,4 +2,15 @@
mw.popups = {};
+ /**
+ * Unlike action creators and reducers, change listeners are more
complex and
+ * won't be defined in just one file. Create the
`mw.popups.changeListeners`
+ * namespace here to avoid repeating the following:
+ *
+ * ```js
+ * mw.popups.changeListeners = mw.popups.changeListeners || {};
+ * ```
+ */
+ mw.popups.changeListeners = {};
+
}( mediaWiki ) );
diff --git a/tests/qunit/ext.popups/changeListener.test.js
b/tests/qunit/ext.popups/changeListener.test.js
new file mode 100644
index 0000000..077fbf3
--- /dev/null
+++ b/tests/qunit/ext.popups/changeListener.test.js
@@ -0,0 +1,50 @@
+( function ( mw ) {
+
+ var stubStore = ( function () {
+ var state;
+
+ return {
+ getState: function () {
+ return state;
+ },
+ setState: function ( value ) {
+ state = value;
+ }
+ };
+ }() );
+
+ QUnit.module( 'ext.popups/changeListener' );
+
+ QUnit.test( 'it should only call the callback when the state has
changed', function ( assert ) {
+ var spy = this.sandbox.spy(),
+ changeListener = mw.popups.createChangeListener( spy,
stubStore );
+
+ assert.expect( 4 );
+
+ stubStore.setState( {} );
+
+ changeListener();
+ changeListener();
+
+ assert.strictEqual( spy.callCount, 1 );
+ assert.ok( spy.calledWith(
+ undefined, // The initial internal state of the change
listener.
+ {}
+ ) );
+
+ stubStore.setState( {
+ foo: 'bar'
+ } );
+
+ changeListener();
+
+ assert.strictEqual( spy.callCount, 2 );
+ assert.ok( spy.calledWith(
+ {},
+ {
+ foo: 'bar'
+ }
+ ) );
+ } );
+
+}( mediaWiki ) );
--
To view, visit https://gerrit.wikimedia.org/r/321851
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ibe6934058327c7f02f7d8092e74a667a5a1c600a
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Popups
Gerrit-Branch: mpga
Gerrit-Owner: Phuedx <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits