diff --git a/.gitignore b/.gitignore
index c83ae1043..bcc76c008 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ migrate
.idea/
package-lock.json
custom_modules
+*.iml
diff --git a/actions/activityfeed/loadActivities.js b/actions/activityfeed/loadActivities.js
index c58bf0dc1..b02856d96 100644
--- a/actions/activityfeed/loadActivities.js
+++ b/actions/activityfeed/loadActivities.js
@@ -17,6 +17,8 @@ export default function loadActivities(context, payload, done) {
return;
}
+ context.dispatch('LOAD_ACTIVITIES_LOAD', {loadingIndicator: true});
+
context.service.read('activities.list', payload, {timeout: 20 * 1000}, (err, res) => {
if (err) {
log.error(context, {filepath: __filename});
diff --git a/actions/loadContributors.js b/actions/loadContributors.js
index 0a3050ac6..26293b20a 100644
--- a/actions/loadContributors.js
+++ b/actions/loadContributors.js
@@ -18,7 +18,11 @@ export default function loadContributors(context, payload, done) {
return;
}
- if (!payload.params.language) payload.params.language = context.getStore(TranslationStore).currentLang;
+ context.dispatch('LOAD_CONTRIBUTORS_LOAD', {loadingIndicator: true});
+
+ if (!payload.params.language) {
+ payload.params.language = context.getStore(TranslationStore).currentLang;
+ }
context.service.read('contributors.list', payload, {timeout: 20 * 1000}, (err, res) => {
if (err) {
@@ -30,9 +34,6 @@ export default function loadContributors(context, payload, done) {
// context.dispatch('UPDATE_MODULE_TYPE_SUCCESS', {moduleType: 'contributors'});
}
let pageTitle = shortTitle + ' | Contributors | ' + payload.params.stype + ' | ' + payload.params.sid;
- //context.dispatch('UPDATE_PAGE_TITLE', {
- // pageTitle: pageTitle
- //});
done();
});
}
diff --git a/common.js b/common.js
index c424b8234..a84bfe79d 100644
--- a/common.js
+++ b/common.js
@@ -175,6 +175,42 @@ export default {
return a.substring(0,2).toLowerCase() === b.substring(0,2).toLowerCase();
},
+ equals: (x, y) => {
+ if (x === y) return true;
+ // if both x and y are null or undefined and exactly the same
+
+ if (! (x instanceof Object) || ! (y instanceof Object)) return false;
+ // if they are not strictly equal, they both need to be Objects
+
+ if (x.constructor !== y.constructor) return false;
+ // they must have the exact same prototype chain, the closest we can do is
+ // test there constructor.
+
+ for (let p in x) {
+ if (! x.hasOwnProperty(p)) continue;
+ // other properties were tested using x.constructor === y.constructor
+
+ if (! y.hasOwnProperty(p)) return false;
+ // allows to compare x[p] and y[p] when set to undefined
+
+ if (x[p] === y[p]) continue;
+ // if they have the same strict value or identity then they are equal
+
+ if (typeof(x[p]) !== 'object') return false;
+ // Numbers, Strings, Functions, Booleans must be strictly equal
+
+ if (! exports.default.equals(x[p], y[p])) return false;
+ // Objects and Arrays must be tested recursively
+ }
+
+ for (let p in y) {
+ if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) return false;
+ // allows x[p] to be set to undefined
+ }
+
+ return true;
+ },
+
//ISO6391 language codes from https://pkgstore.datahub.io/core/language-codes/language-codes_csv/data/b65af208b52970a4683fa8fde9af8e9f/language-codes_csv.csv
translationLanguages: [
'de',
diff --git a/components/Deck/ActivityFeedPanel/ActivityFeedPanel.js b/components/Deck/ActivityFeedPanel/ActivityFeedPanel.js
index 495b3a18d..a75c33b28 100644
--- a/components/Deck/ActivityFeedPanel/ActivityFeedPanel.js
+++ b/components/Deck/ActivityFeedPanel/ActivityFeedPanel.js
@@ -34,8 +34,7 @@ class ActivityFeedPanel extends React.Component {
return (
-
Activity Feed
-
+
Activity Feed
{activityDIV}
diff --git a/components/Deck/ActivityFeedPanel/ActivityList.js b/components/Deck/ActivityFeedPanel/ActivityList.js
index 7ed3e30fe..97950ba56 100644
--- a/components/Deck/ActivityFeedPanel/ActivityList.js
+++ b/components/Deck/ActivityFeedPanel/ActivityList.js
@@ -35,6 +35,8 @@ class ActivityList extends React.Component {
// TODO: same as in the ActivityFeedStore; check if there is more elegant way to tell the component that action loadMoreActivities (in the onScroll function) was executed
if (!nextProps.ActivityFeedStore.wasFetch) return;
this.loading = false;
+ let activitiesCount = this.props.ActivityFeedStore.activities.length;
+ console.log('ActivityList.componentWillReceiveProps() [' + 'activitiesCount=' + activitiesCount + ']');
}
render() {
return (
diff --git a/components/Deck/Deck.js b/components/Deck/Deck.js
index 80cabf23f..9e4de87e2 100644
--- a/components/Deck/Deck.js
+++ b/components/Deck/Deck.js
@@ -12,22 +12,49 @@ import SlideEditLeftPanel from './SlideEditLeftPanel/SlideEditLeftPanel';
import ContentPanel from './ContentPanel/ContentPanel';
import NavigationPanel from './NavigationPanel/NavigationPanel';
import ContentModulesPanel from './ContentModulesPanel/ContentModulesPanel';
-//import ActivityFeedPanel from './ActivityFeedPanel/ActivityFeedPanel';
-//import ServiceUnavailable from '../Error/ServiceUnavailable';//NOTE error code has been refactored - this component doesn't exist anymore, code was moved to Error.js in same directory
import InfoPanelInfoView from './InfoPanel/InfoPanelInfoView';
import TranslationStore from '../../stores/TranslationStore';
-import { FormattedMessage, defineMessages } from 'react-intl';
+import ContributorsStore from '../../stores/ContributorsStore';
+import {equals} from '../../common';
class Deck extends React.Component {
- handleExpandClick(){
- this.context.executeAction(hideLeftColumn, {});
- return false;
+ constructor(props) {
+ super(props);
+ this.isLoading = this.isContentUndefined();
}
- handleCollapseClick(){
- this.context.executeAction(restoreDeckPageLayout, {});
- return false;
+
+ // handleExpandClick(){
+ // this.context.executeAction(hideLeftColumn, {});
+ // return false;
+ // }
+ //
+ // handleCollapseClick(){
+ // this.context.executeAction(restoreDeckPageLayout, {});
+ // return false;
+ // }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ let samePropsState = equals(this.props, nextProps);
+ this.isLoading = this.isContentUndefined();
+ // Content should be updated only when properties have changed.
+ return !this.isLoading && !samePropsState;
}
+
+ componentWillReceiveProps(nextProps) {
+ this.isLoading = this.isContentUndefined();
+ }
+
+ componentWillUnmount() {
+ this.props.ContributorsStore.contributors = [];
+ this.isLoading = true;
+ }
+
+ isContentUndefined() {
+ return this.props.ContributorsStore.contributors === undefined
+ || this.props.ContributorsStore.contributors === [];
+ }
+
render() {
const error = this.props.ServiceErrorStore.error;
let status = this.props.DeckPageStore.componentsStatus;
@@ -212,7 +239,7 @@ class Deck extends React.Component {
rightPanel = (
-
+
@@ -251,12 +278,13 @@ Deck.contextTypes = {
executeAction: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
-Deck = connectToStores(Deck, [DeckPageStore, ServiceErrorStore, UserProfileStore, TranslationStore], (context, props) => {
+Deck = connectToStores(Deck, [DeckPageStore, ServiceErrorStore, UserProfileStore, TranslationStore, ContributorsStore], (context, props) => {
return {
DeckPageStore: context.getStore(DeckPageStore).getState(),
ServiceErrorStore: context.getStore(ServiceErrorStore).getState(),
UserProfileStore: context.getStore(UserProfileStore).getState(),
- TranslationStore: context.getStore(TranslationStore).getState()
+ TranslationStore: context.getStore(TranslationStore).getState(),
+ ContributorsStore: context.getStore(ContributorsStore).getState()
};
});
export default Deck;
diff --git a/components/Deck/InfoPanel/InfoPanelInfoView.js b/components/Deck/InfoPanel/InfoPanelInfoView.js
index 2cb9f034c..5f14bc6aa 100644
--- a/components/Deck/InfoPanel/InfoPanelInfoView.js
+++ b/components/Deck/InfoPanel/InfoPanelInfoView.js
@@ -7,6 +7,7 @@ import ActivityFeedPanel from '../ActivityFeedPanel/ActivityFeedPanel';
import ContributorsPanel from '../ContentModulesPanel/ContributorsPanel/ContributorsPanel';
import PresentationsPanel from './PresentationsPanel';
import ActivityFeedStore from '../../../stores/ActivityFeedStore';
+import {getLanguageName, equals} from '../../../common';
import TranslationStore from '../../../stores/TranslationStore';
import PermissionsStore from '../../../stores/PermissionsStore';
import {defineMessages} from 'react-intl';
@@ -16,8 +17,9 @@ import ContentStore from '../../../stores/ContentStore';
class InfoPanelInfoView extends React.Component {
- constructor(props){
+ constructor(props) {
super(props);
+ this.isLoading = true;
this.messages = defineMessages({
});
@@ -26,6 +28,13 @@ class InfoPanelInfoView extends React.Component {
this.resetZoom = this.resetZoom.bind(this);
}
+ shouldComponentUpdate(nextProps, nextState) {
+ const samePropsState = equals(this.props, nextProps);
+ this.isLoading = nextProps.loadingIndicator;
+ // Content should be updated only when properties have changed.
+ return !samePropsState;
+ }
+
zoomIn() {
this.context.executeAction(zoom, { mode: this.props.ContentStore.mode, direction: 'in' });
}
@@ -43,12 +52,13 @@ class InfoPanelInfoView extends React.Component {
let showZoomControls = this.props.ContentStore.selector.stype === 'slide';
let deckId = selector.get('id');
- if (deckId) {
+ if (deckId) {
deckId = deckId.split('-')[0];
}
return (
+ {this.isLoading &&
}
{
showZoomControls &&
@@ -78,8 +88,8 @@ class InfoPanelInfoView extends React.Component {
}
-
+
@@ -110,6 +120,7 @@ InfoPanelInfoView.contextTypes = {
executeAction: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
+
InfoPanelInfoView= connectToStores(InfoPanelInfoView, [ActivityFeedStore, DeckTreeStore, TranslationStore, PermissionsStore, ContentStore], (context, props) => {
return {
ActivityFeedStore: context.getStore(ActivityFeedStore).getState(),
diff --git a/stores/ActivityFeedStore.js b/stores/ActivityFeedStore.js
index db71c784d..428ff1a3b 100644
--- a/stores/ActivityFeedStore.js
+++ b/stores/ActivityFeedStore.js
@@ -11,12 +11,14 @@ class ActivityFeedStore extends BaseStore {
this.selector = {};
this.hasMore = true;
this.presentations = [];
+ this.loadingIndicator = false;
}
updateActivities(payload) {
this.activities = payload.activities;
this.activityType = payload.activityType;
this.selector = payload.selector;
this.hasMore = payload.hasMore;
+ this.loadingIndicator = false;
this.emitChange();
}
loadMoreActivities(payload) {
@@ -29,6 +31,7 @@ class ActivityFeedStore extends BaseStore {
}
updateActivityType(payload) {
this.activityType = payload.activityType;
+ this.loadingIndicator = false;
this.emitChange();
}
// incrementLikes(payload) {
@@ -61,6 +64,7 @@ class ActivityFeedStore extends BaseStore {
// }
addActivity(payload) {
const activity = payload.activity;
+ this.loadingIndicator = false;
if (activity_types_to_display.includes(activity.activity_type) && (this.selector.stype === activity.content_kind && this.selector.sid.split('-')[0] === activity.content_id.split('-')[0] ||
activity.activity_type === 'move' && this.selector.stype === 'deck' && this.selector.sid.split('-')[0] === activity.move_info.source_id.split('-')[0])) {
this.activities.unshift(activity);//add to the beginning
@@ -72,6 +76,7 @@ class ActivityFeedStore extends BaseStore {
}
}
addActivities(payload) {
+ this.loadingIndicator = false;
payload.activities.forEach((activity) => {
if (activity_types_to_display.includes(activity.activity_type) && (this.selector.stype === activity.content_kind && this.selector.sid.split('-')[0] === activity.content_id.split('-')[0])) {
this.activities.unshift(activity);//add to the beginning
@@ -84,6 +89,7 @@ class ActivityFeedStore extends BaseStore {
this.emitChange();
}
addLikeActivity(payload) {
+ this.loadingIndicator = false;
if (payload.selector.stype === 'deck') {
let activity = {
activity_type: 'react',
@@ -105,6 +111,7 @@ class ActivityFeedStore extends BaseStore {
}
}
removeLikeActivity(payload) {
+ this.loadingIndicator = false;
//find like activity and remove it
if (payload.selector.stype === 'deck') {
let i = 0;
@@ -121,6 +128,7 @@ class ActivityFeedStore extends BaseStore {
}
}
updatePresentations(payload) {
+ this.loadingIndicator = false;
// console.log('ActivityFeedStore: updatePresentations', payload);
this.presentations = payload;
this.emitChange();
@@ -145,6 +153,10 @@ class ActivityFeedStore extends BaseStore {
this.hasMore = state.hasMore;
this.presentations = state.presentations;
}
+ loading(payload){
+ this.loadingIndicator = payload.loadingIndicator;
+ this.emitChange();
+ }
}
ActivityFeedStore.storeName = 'ActivityFeedStore';
@@ -159,7 +171,8 @@ ActivityFeedStore.handlers = {
'ADD_ACTIVITIES_SUCCESS': 'addActivities',
'LIKE_ACTIVITY_SUCCESS': 'addLikeActivity',
'DISLIKE_ACTIVITY_SUCCESS': 'removeLikeActivity',
- 'LOAD_PRESENTATIONS_SUCCESS': 'updatePresentations'
+ 'LOAD_PRESENTATIONS_SUCCESS': 'updatePresentations',
+ 'LOAD_ACTIVITIES_LOAD': 'loading'
};
export default ActivityFeedStore;
diff --git a/stores/ContributorsStore.js b/stores/ContributorsStore.js
index 03fd9cf67..17e34d09e 100644
--- a/stores/ContributorsStore.js
+++ b/stores/ContributorsStore.js
@@ -9,6 +9,11 @@ class ContributorsStore extends BaseStore {
this.translators = [];
this.listName = '';
this.selector = {};
+ this.loadingIndicator = false;
+ }
+ loading(payload){
+ this.loadingIndicator = payload.loadingIndicator;
+ this.emitChange();
}
updateContributors(payload) {
this.contributors = this.getContributors(payload.contributors);
@@ -16,6 +21,7 @@ class ContributorsStore extends BaseStore {
this.translators = this.getTranslators(payload.contributors);
this.listName = payload.listName;
this.selector = payload.selector;
+ this.loadingIndicator = false;
this.emitChange();
}
@@ -24,7 +30,8 @@ class ContributorsStore extends BaseStore {
contributors: this.contributors,
creator: this.creator,
translators: this.translators,
- selector: this.selector
+ selector: this.selector,
+ loadingIndicator: this.loadingIndicator
};
}
dehydrate() {
@@ -35,6 +42,7 @@ class ContributorsStore extends BaseStore {
this.creator = state.creator;
this.translators = state.translators;
this.selector = state.selector;
+ this.loadingIndicator = state.loadingIndicator;
}
getBasedonRole(role, list) {
@@ -60,7 +68,8 @@ class ContributorsStore extends BaseStore {
ContributorsStore.storeName = 'ContributorsStore';
ContributorsStore.handlers = {
- 'LOAD_CONTRIBUTORS_SUCCESS': 'updateContributors'
+ 'LOAD_CONTRIBUTORS_SUCCESS': 'updateContributors',
+ 'LOAD_CONTRIBUTORS_LOAD': 'loading'
};
export default ContributorsStore;