<commit_before>import RcModule from '../../lib/RcModule';
import proxify from '../../lib/proxy/proxify';
import { Module } from '../../lib/di';
import actionTypes from './actionTypes';
import getUserGuideReducer, { getGuidesReducer } from './getUserGuideReducer';

/**
 * Support localization
 */
const SUPPORTED_LOCALES = {
  'en-US': 'en-US',
  'fr-CA': 'fr-CA',
};

@Module({
  deps: [
    'Auth',
    'Locale',
    'Storage',
    'Webphone',
    'RolesAndPermissions',
    { dep: 'UserGuideOptions', optional: true }
  ]
})
export default class UserGuide extends RcModule {
  constructor({
    auth,
    locale,
    storage,
    webphone,
    rolesAndPermissions,
    ...options
  }) {
    super({
      actionTypes,
      ...options
    });
    this._auth = auth;
    this._locale = locale;
    this._storage = storage;
    this._webphone = webphone;
    this._rolesAndPermissions = rolesAndPermissions;
    this._reducer = getUserGuideReducer(this.actionTypes);

    this._context = options.context;

    this._storageKey = 'userGuide';
    this._guideReducer = getGuidesReducer(this.actionTypes);
    this._storage.registerReducer({
      key: this._storageKey,
      reducer: this._guideReducer,
    });
  }

  initialize() {
    this.store.subscribe(() => this._onStateChange());
  }

  async _onStateChange() {
    if (
      this.pending &&
      this._auth.ready &&
      this._locale.ready &&
      this._storage.ready &&
      this._rolesAndPermissions.ready &&
      this._auth.loggedIn
    ) {
      this.store.dispatch({
        type: this.actionTypes.initSuccess,
      });
      await this.initUserGuide();
    } else if (
      this.ready && (
        !this._auth.ready ||
        !this._locale.ready ||
        !this._storage.ready ||
        !this._rolesAndPermissions.ready
      )
    ) {
      this.store.dispatch({
        type: this.actionTypes.resetSuccess
      });
    }
    // When there is an incoming call,
    // the guide should be dismissed
    if (
      this._webphone.ready &&
      this._webphone.ringSession &&
      this._webphone.ringSession !== this._lastRingSession
    ) {
      this._lastRingSession = this._webphone.ringSession;
      this.dismiss();
    }
  }

  /**
   * Using webpack `require.context` to load guides files.
   * Image files will be ordered by file name ascendingly.
   * @return {Map<String, Array<URI>>}
   */
  resolveGuides() {
    if (this._context && typeof this._context === 'function') {
      const locales = Object.keys(SUPPORTED_LOCALES);
      return this._context.keys().sort()
        .map(key => this._context(key))
        .reduce((prev, curr) => {
          locales.forEach((locale) => {
            if (!prev[locale]) prev[locale] = [];
            if (curr.includes(locale)) {
              prev[locale].push(curr);
            }
          });
          return prev;
        }, {});
    }
    return {};
  }

  @proxify
  dismiss() {
    this.updateCarousel({ curIdx: 0, entered: false, playing: false });
  }

  @proxify
  async loadGuides(guides) {
    if (guides) {
      this.store.dispatch({
        type: this.actionTypes.loadGuides,
        guides
      });
    }
  }

  @proxify
  async updateCarousel({ curIdx, entered, playing }) {
    this.store.dispatch({
      type: this.actionTypes.updateCarousel,
      curIdx,
      entered,
      playing
    });
  }

  @proxify
  async initUserGuide() {
    if (!this._rolesAndPermissions.hasUserGuidePermission) return;
    // eslint-disable-next-line
    const prevGuides = this.allGuides;
    const guides = this.resolveGuides();
    // Determine if it needs to be displayed when first log in,
    // the principles behind this is to use webpack's file hash,
    // i.e. if any of the guide files is changed, the file name hash
    // will be changed as well, in this case, it will be displayed.
    await this.loadGuides(guides);
    if (JSON.stringify(guides) !== JSON.stringify(prevGuides)) {
      await this.start();
    }
  }

  @proxify
  async start() {
    // Start guides only when images are ready
    if (this.guides.length > 0) {
      this.store.dispatch({
        type: this.actionTypes.updateCarousel,
        curIdx: 0,
        entered: true,
        playing: true,
      });
    }
  }

  get guides() {
    if (!this._locale.ready) return [];
    if (this.allGuides) {
      return this.allGuides[this._locale.currentLocale] ||
        this.allGuides[SUPPORTED_LOCALES['en-US']] || [];
    }
    return [];
  }

  get allGuides() {
    if (!this._storage.ready) return [];
    return this._storage.getItem(this._storageKey);
  }

  get carouselState() {
    return this.state.carouselState;
  }

  get started() {
    return this.carouselState.entered && this.carouselState.playing;
  }

  get status() {
    return this.state.status;
  }
}
<commit_msg>bugfix: user guide module cannot fall back to en-US automatically (#787)<commit_after>import RcModule from '../../lib/RcModule';
import proxify from '../../lib/proxy/proxify';
import { Module } from '../../lib/di';
import actionTypes from './actionTypes';
import getUserGuideReducer, { getGuidesReducer } from './getUserGuideReducer';

/**
 * Support localization
 */
const SUPPORTED_LOCALES = {
  'en-US': 'en-US',
  'fr-CA': 'fr-CA',
};

@Module({
  deps: [
    'Auth',
    'Locale',
    'Storage',
    'Webphone',
    'RolesAndPermissions',
    { dep: 'UserGuideOptions', optional: true }
  ]
})
export default class UserGuide extends RcModule {
  constructor({
    auth,
    locale,
    storage,
    webphone,
    rolesAndPermissions,
    ...options
  }) {
    super({
      actionTypes,
      ...options
    });
    this._auth = auth;
    this._locale = locale;
    this._storage = storage;
    this._webphone = webphone;
    this._rolesAndPermissions = rolesAndPermissions;
    this._reducer = getUserGuideReducer(this.actionTypes);

    this._context = options.context;

    this._storageKey = 'userGuide';
    this._guideReducer = getGuidesReducer(this.actionTypes);
    this._storage.registerReducer({
      key: this._storageKey,
      reducer: this._guideReducer,
    });
  }

  initialize() {
    this.store.subscribe(() => this._onStateChange());
  }

  async _onStateChange() {
    if (
      this.pending &&
      this._auth.ready &&
      this._locale.ready &&
      this._storage.ready &&
      this._rolesAndPermissions.ready &&
      this._auth.loggedIn
    ) {
      this.store.dispatch({
        type: this.actionTypes.initSuccess,
      });
      await this.initUserGuide();
    } else if (
      this.ready && (
        !this._auth.ready ||
        !this._locale.ready ||
        !this._storage.ready ||
        !this._rolesAndPermissions.ready
      )
    ) {
      this.store.dispatch({
        type: this.actionTypes.resetSuccess
      });
    }
    // When there is an incoming call,
    // the guide should be dismissed
    if (
      this._webphone.ready &&
      this._webphone.ringSession &&
      this._webphone.ringSession !== this._lastRingSession
    ) {
      this._lastRingSession = this._webphone.ringSession;
      this.dismiss();
    }
  }

  /**
   * Using webpack `require.context` to load guides files.
   * Image files will be ordered by file name ascendingly.
   * @return {Map<String, Array<URI>>}
   */
  resolveGuides() {
    if (this._context && typeof this._context === 'function') {
      const locales = Object.keys(SUPPORTED_LOCALES);
      return this._context.keys().sort()
        .map(key => this._context(key))
        .reduce((prev, curr) => {
          locales.forEach((locale) => {
            if (!prev[locale]) prev[locale] = [];
            if (curr.includes(locale)) {
              prev[locale].push(curr);
            }
          });
          return prev;
        }, {});
    }
    return {};
  }

  @proxify
  dismiss() {
    this.updateCarousel({ curIdx: 0, entered: false, playing: false });
  }

  @proxify
  async loadGuides(guides) {
    if (guides) {
      this.store.dispatch({
        type: this.actionTypes.loadGuides,
        guides
      });
    }
  }

  @proxify
  async updateCarousel({ curIdx, entered, playing }) {
    this.store.dispatch({
      type: this.actionTypes.updateCarousel,
      curIdx,
      entered,
      playing
    });
  }

  @proxify
  async initUserGuide() {
    if (!this._rolesAndPermissions.hasUserGuidePermission) return;
    // eslint-disable-next-line
    const prevGuides = this.allGuides;
    const guides = this.resolveGuides();
    // Determine if it needs to be displayed when first log in,
    // the principles behind this is to use webpack's file hash,
    // i.e. if any of the guide files is changed, the file name hash
    // will be changed as well, in this case, it will be displayed.
    await this.loadGuides(guides);
    if (JSON.stringify(guides) !== JSON.stringify(prevGuides)) {
      await this.start();
    }
  }

  @proxify
  async start() {
    // Start guides only when images are ready
    if (this.guides.length > 0) {
      this.store.dispatch({
        type: this.actionTypes.updateCarousel,
        curIdx: 0,
        entered: true,
        playing: true,
      });
    }
  }

  get guides() {
    if (!this._locale.ready) return [];
    if (this.allGuides) {
      const currentGuides = this.allGuides[this._locale.currentLocale];
      if (currentGuides && currentGuides.length > 0) return currentGuides;
      return this.allGuides[SUPPORTED_LOCALES['en-US']] || [];
    }
    return [];
  }

  get allGuides() {
    if (!this._storage.ready) return null;
    return this._storage.getItem(this._storageKey);
  }

  get carouselState() {
    return this.state.carouselState;
  }

  get started() {
    return this.carouselState.entered && this.carouselState.playing;
  }

  get status() {
    return this.state.status;
  }
}
<|endoftext|>


<commit_before>/*
Copyright 2018, 2019 New Vector Ltd
Copyright 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import Modal from '../../../Modal';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import SettingsStore from "../../../settings/SettingsStore";

export default class LogoutDialog extends React.Component {
    defaultProps = {
        onFinished: function() {},
    };

    constructor() {
        super();
        this._onSettingsLinkClick = this._onSettingsLinkClick.bind(this);
        this._onExportE2eKeysClicked = this._onExportE2eKeysClicked.bind(this);
        this._onFinished = this._onFinished.bind(this);
        this._onSetRecoveryMethodClick = this._onSetRecoveryMethodClick.bind(this);
        this._onLogoutConfirm = this._onLogoutConfirm.bind(this);

        const lowBandwidth = SettingsStore.getValue("lowBandwidth");
        const shouldLoadBackupStatus = !lowBandwidth && !MatrixClientPeg.get().getKeyBackupEnabled();

        this.state = {
            shouldLoadBackupStatus: shouldLoadBackupStatus,
            loading: shouldLoadBackupStatus,
            backupInfo: null,
            error: null,
        };

        if (shouldLoadBackupStatus) {
            this._loadBackupStatus();
        }
    }

    async _loadBackupStatus() {
        try {
            const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
            this.setState({
                loading: false,
                backupInfo,
            });
        } catch (e) {
            console.log("Unable to fetch key backup status", e);
            this.setState({
                loading: false,
                error: e,
            });
        }
    }

    _onSettingsLinkClick() {
        // close dialog
        this.props.onFinished();
    }

    _onExportE2eKeysClicked() {
        Modal.createTrackedDialogAsync('Export E2E Keys', '',
            import('../../../async-components/views/dialogs/ExportE2eKeysDialog'),
            {
                matrixClient: MatrixClientPeg.get(),
            },
        );
    }

    _onFinished(confirmed) {
        if (confirmed) {
            dis.dispatch({action: 'logout'});
        }
        // close dialog
        this.props.onFinished();
    }

    _onSetRecoveryMethodClick() {
        if (this.state.backupInfo) {
            // A key backup exists for this account, but the creating device is not
            // verified, so restore the backup which will give us the keys from it and
            // allow us to trust it (ie. upload keys to it)
            const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
            Modal.createTrackedDialog(
                'Restore Backup', '', RestoreKeyBackupDialog, null, null,
                /* priority = */ false, /* static = */ true,
            );
        } else {
            Modal.createTrackedDialogAsync("Key Backup", "Key Backup",
                import("../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog"),
                null, null, /* priority = */ false, /* static = */ true,
            );
        }

        // close dialog
        this.props.onFinished();
    }

    _onLogoutConfirm() {
        dis.dispatch({action: 'logout'});

        // close dialog
        this.props.onFinished();
    }

    render() {
        if (this.state.shouldLoadBackupStatus) {
            const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');

            const description =

{_t(
                    "Encrypted messages are secured with end-to-end encryption. " +
                    "Only you and the recipient(s) have the keys to read these messages.",
                )}

{_t("Back up your keys before signing out to avoid losing them.")}

;

            let dialogContent;
            if (this.state.loading) {
                const Spinner = sdk.getComponent('views.elements.Spinner');

                dialogContent = <Spinner />;
            } else {
                const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
                let setupButtonCaption;
                if (this.state.backupInfo) {
                    setupButtonCaption = _t("Connect this session to Key Backup");
                } else {
                    // if there's an error fetching the backup info, we'll just assume there's
                    // no backup for the purpose of the button caption
                    setupButtonCaption = _t("Start using Key Backup");
                }

                dialogContent =

                        { description }

                    <DialogButtons primaryButton={setupButtonCaption}
                        hasCancel={false}
                        onPrimaryButtonClick={this._onSetRecoveryMethodClick}
                        focus={true}
                    >
                        <button onClick={this._onLogoutConfirm}>
                            {_t("I don't want my encrypted messages")}
                        </button>
                    </DialogButtons>

                        {_t("Advanced")}

<button onClick={this._onExportE2eKeysClicked}>
                            {_t("Manually export keys")}
                        </button>

;
            }
            // Not quite a standard question dialog as the primary button cancels
            // the action and does something else instead, whilst non-default button
            // confirms the action.
            return (<BaseDialog
                title={_t("You'll lose access to your encrypted messages")}
                contentId='mx_Dialog_content'
                hasCancel={true}
                onFinished={this._onFinished}
            >
                {dialogContent}
            </BaseDialog>);
        } else {
            const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog');
            return (<QuestionDialog
                hasCancelButton={true}
                title={_t("Sign out")}
                description={_t(
                    "Are you sure you want to sign out?",
                )}
                button={_t("Sign out")}
                onFinished={this._onFinished}
            />);
        }
    }
}
<commit_msg>Fix logout when Olm failed to load.<commit_after>/*
Copyright 2018, 2019 New Vector Ltd
Copyright 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import Modal from '../../../Modal';
import * as sdk from '../../../index';
import dis from '../../../dispatcher';
import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';

export default class LogoutDialog extends React.Component {
    defaultProps = {
        onFinished: function() {},
    };

    constructor() {
        super();
        this._onSettingsLinkClick = this._onSettingsLinkClick.bind(this);
        this._onExportE2eKeysClicked = this._onExportE2eKeysClicked.bind(this);
        this._onFinished = this._onFinished.bind(this);
        this._onSetRecoveryMethodClick = this._onSetRecoveryMethodClick.bind(this);
        this._onLogoutConfirm = this._onLogoutConfirm.bind(this);

        const cli = MatrixClientPeg.get();
        const shouldLoadBackupStatus = cli.isCryptoEnabled() && !cli.getKeyBackupEnabled();

        this.state = {
            shouldLoadBackupStatus: shouldLoadBackupStatus,
            loading: shouldLoadBackupStatus,
            backupInfo: null,
            error: null,
        };

        if (shouldLoadBackupStatus) {
            this._loadBackupStatus();
        }
    }

    async _loadBackupStatus() {
        try {
            const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
            this.setState({
                loading: false,
                backupInfo,
            });
        } catch (e) {
            console.log("Unable to fetch key backup status", e);
            this.setState({
                loading: false,
                error: e,
            });
        }
    }

    _onSettingsLinkClick() {
        // close dialog
        this.props.onFinished();
    }

    _onExportE2eKeysClicked() {
        Modal.createTrackedDialogAsync('Export E2E Keys', '',
            import('../../../async-components/views/dialogs/ExportE2eKeysDialog'),
            {
                matrixClient: MatrixClientPeg.get(),
            },
        );
    }

    _onFinished(confirmed) {
        if (confirmed) {
            dis.dispatch({action: 'logout'});
        }
        // close dialog
        this.props.onFinished();
    }

    _onSetRecoveryMethodClick() {
        if (this.state.backupInfo) {
            // A key backup exists for this account, but the creating device is not
            // verified, so restore the backup which will give us the keys from it and
            // allow us to trust it (ie. upload keys to it)
            const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
            Modal.createTrackedDialog(
                'Restore Backup', '', RestoreKeyBackupDialog, null, null,
                /* priority = */ false, /* static = */ true,
            );
        } else {
            Modal.createTrackedDialogAsync("Key Backup", "Key Backup",
                import("../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog"),
                null, null, /* priority = */ false, /* static = */ true,
            );
        }

        // close dialog
        this.props.onFinished();
    }

    _onLogoutConfirm() {
        dis.dispatch({action: 'logout'});

        // close dialog
        this.props.onFinished();
    }

    render() {
        if (this.state.shouldLoadBackupStatus) {
            const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');

            const description =

{_t(
                    "Encrypted messages are secured with end-to-end encryption. " +
                    "Only you and the recipient(s) have the keys to read these messages.",
                )}

{_t("Back up your keys before signing out to avoid losing them.")}

;

            let dialogContent;
            if (this.state.loading) {
                const Spinner = sdk.getComponent('views.elements.Spinner');

                dialogContent = <Spinner />;
            } else {
                const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
                let setupButtonCaption;
                if (this.state.backupInfo) {
                    setupButtonCaption = _t("Connect this session to Key Backup");
                } else {
                    // if there's an error fetching the backup info, we'll just assume there's
                    // no backup for the purpose of the button caption
                    setupButtonCaption = _t("Start using Key Backup");
                }

                dialogContent =

                        { description }

                    <DialogButtons primaryButton={setupButtonCaption}
                        hasCancel={false}
                        onPrimaryButtonClick={this._onSetRecoveryMethodClick}
                        focus={true}
                    >
                        <button onClick={this._onLogoutConfirm}>
                            {_t("I don't want my encrypted messages")}
                        </button>
                    </DialogButtons>

                        {_t("Advanced")}

<button onClick={this._onExportE2eKeysClicked}>
                            {_t("Manually export keys")}
                        </button>

;
            }
            // Not quite a standard question dialog as the primary button cancels
            // the action and does something else instead, whilst non-default button
            // confirms the action.
            return (<BaseDialog
                title={_t("You'll lose access to your encrypted messages")}
                contentId='mx_Dialog_content'
                hasCancel={true}
                onFinished={this._onFinished}
            >
                {dialogContent}
            </BaseDialog>);
        } else {
            const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog');
            return (<QuestionDialog
                hasCancelButton={true}
                title={_t("Sign out")}
                description={_t(
                    "Are you sure you want to sign out?",
                )}
                button={_t("Sign out")}
                onFinished={this._onFinished}
            />);
        }
    }
}
<|endoftext|>


<commit_before>/*
 * This script rely on
    "sgmap-mes-aides-api": "sgmap/mes-aides-api#replicate",
 */
var config = require('../../backend');
var expect = require('expect');
var _ = require('lodash');

var mongoose = require('mongoose');

// Setup mongoose
var AcceptanceTest = mongoose.model('AcceptanceTest');
var LegacySituation = mongoose.model('LegacySituation');
var Situation = mongoose.model('Situation');

var legacyMapping = require('sgmap-mes-aides-api/lib/simulation/openfisca/mapping');

var migration = require('../../backend/lib/migrations/initial');
var mapping = require('../../backend/lib/openfisca/mapping');

function deepDiffRight(left, right) {
  // if they are equals, return undefined
  if (_.isEqual(left, right)) { return; }
  // form now on, we can assure that `left` and `right` are not equals (equivalents)

  // if `left` and `right` are primitives, value changed
  // if `left` is a primitive and `right` an object, value has been replaced with an object
  // if `left` is an object and `right` a primitive, value has been replaced with a primitive
  // use `_.copy` to prevent object referrence issues
  if (! _.isObject(left) || ! _.isObject(right)) { return _.cloneDeep(right); }

  // make `result` the same type as `right`
  var result = _.isArray(right) ? [] : {};

  // since we know that both are objects,
  // iterate on `right` to see what changed or what's new in `right`
  _.forEach(right, function (value, key) {
    // recursion
    var diff = deepDiffRight(left[key], right[key]);
    // since the function returns undefined when `left` and `right` are equals,
    // only assing non-undefined values to result
    if (! _.isUndefined(diff)) {
        result[key] = diff;
    }
  });

  //console.log(result, (! result.length), (! Object.keys(result).length));
  if ((! result.length) && (! Object.keys(result).length)) { return; }

  return result;
}

function processSituations(err, retrievedTests) {
    if (err) {
        console.log(err);
        process.exit(1);
    }
    var tests = retrievedTests.slice(0, 2000);
    var done = 0;
    function processTests() {
        if(done == tests.length) {
            process.exit();
        }
        var test = tests[done];
        var testId = test._id;
        var situationId = test.scenario && test.scenario.situationId || (test.id || test._id || '').toString();

        LegacySituation.findById(situationId)
        .then(function(dbLegacySituation) {
            if (! dbLegacySituation) {
                console.log('var Err_testID_ = \'' + testId + '\'; // Null');
                done += 1;
                return processTests();
            }
            var dbSituationJSON = JSON.stringify(dbLegacySituation.toObject(), null, 2);
            var situation = dbLegacySituation.toObject();
            migration.persistedSituationPretransformationUpdate(situation);

            var legacyOpenfiscaRequest = legacyMapping.buildOpenFiscaRequest(_.cloneDeep(situation));

            var frontSituation = migration.migratePersistedSituation(situation);
            Situation.create(frontSituation, function(err, dbSituation) {
                if (err) {
                    console.log('var Err_testID_ = \'' + testId + '\'; // Situation.create ' + frontSituation._id );
                    console.log(JSON.stringify(err, null, 2));
                    process.exit(1);
                }

                var generatedSituation = frontSituation;
                var persistedSituation = _.omit(dbSituation.toObject(), '__v');

                var diff01 = deepDiffRight(generatedSituation, persistedSituation);
                var diff02 = deepDiffRight(persistedSituation, generatedSituation);
                if (/*true || //*/
                    diff01 || diff02) {
                    var structure0 = [diff01, diff02];
                    console.log('var testID_db_ = \'' + testId + '\' //  ' + frontSituation._id );
                    console.log(JSON.stringify(structure0, null, 2));
                    console.log(JSON.stringify([generatedSituation, persistedSituation], null, 2));

                    process.exit(1);
                }

                var newOpenfiscaRequest = mapping.buildOpenFiscaRequest(_.cloneDeep(persistedSituation));

                legacyOpenfiscaRequest.variables.sort();

                var diff1 = deepDiffRight(legacyOpenfiscaRequest, newOpenfiscaRequest);
                var diff2 = deepDiffRight(newOpenfiscaRequest, legacyOpenfiscaRequest);
                if (/*true || //*/
                    diff1 || diff2) {
                    var structure = [diff1, diff2];

                    console.log('var testID_req_ = \'' + testId + '\'; // Situation.create ' + frontSituation._id );
                    console.log(JSON.stringify(structure, null, 2));
                    console.log(JSON.stringify([newOpenfiscaRequest, legacyOpenfiscaRequest], null, 2));
                    process.exit(1);
                } else {
                    console.log('var situation_' + situationId + '_' + done + ' = \'ok\';');
                }

                done += 1;

                processTests();
            });
        }).catch(function(err) {
            console.log('var Err_testID_ = \'' + testId + '\'; // LegacySituation.findById');
            console.log(err);
            console.log(JSON.stringify(err, null, 2));
            process.exit(1);
        });
    }
    processTests();
}

function migrateTestSituations() {
    AcceptanceTest.find({
        // _id: '54e4c0bffb31b85d4074fe15', // taux incapacite
        // _id: '54915ffadf990f5b0286cf20', // salaire_imposable were double counted
        // _id: '5422c2d95be58c0200a07585', // Refactor backend - anneeDeReference proxy ressources - missing computed ressources
        // _id: '53d79982f6aa390200a6cd08', // Refactor backend - anneeDeReference proxy ressources
        // _id: '546c9783b7fb60347d0f3b24', // Ids missing in individus
        // _id: '53d21ee5159e330200810b87', // ObjectId versus String comparison issue
        // _id: '53d7b76df6aa390200a6cd5c', // Add firstName
        // _id: '540d69a1aa90150200440ee9', // remove rejected test - Caution will hang
        // _id: '582b06f467831ca264e93def', // Situation 582b06e667831ca264e93de5 set individus.2.specificSituations = []
        // _id: '53d220ce159e330200810b8e', // Persisted situation error
        // _id: '588f2778db011fdc7a230a12', // habite_chez_parents missing in migration mapping
        // _id: '5639eb3ceeab276a0422b307', // reverted interrupted aide_logement
        // _id: '54f07c45246e5054751ccfe3', // situation.logement.isChambre
        // _id: '54d8e0821fe4e8cf448e2bf3', // rfr diff
        // _id: '53d24d0ae40a7902009c3377', // Broken diff function
        // _id: '55b8cbb1d28906b46b50376a', // Uninferable ressourcesYearMoins2Captured = true from ressources
        // _id: '5540d71646bea9643939530f', // Unknown properties autresRevenusTns and caAutresRevenusTns
        // _id: '559e4fe4fa617858013d9a39', // rounding loyer
        // _id: '54e4c0bffb31b85d4074fe15', // tauxIncapacite presence
        // _id: '545a57f8ad58ab46561f56ec', // Ressources moved from individu to group entity
        state: { '$nin': ['unclaimed', 'rejected'] }
    }, {}, { sort: '_id' }, processSituations);
}

function migrateRecentSituations() {
    LegacySituation.find({
        _id: { '$nin': [
            '5999dc194866593317f4b006',
            '599983ae4866593317f44818',
            '599971c44866593317f43044', // ASI partially interrupted, new version has the correct behavior
            '599961ef4866593317f41fb2', // aide_logement partially interrupted, new version has the correct behavior
            '59991f1e4866593317f3e9d4', // ass partially interrupted, new version has the correct behavior
            '599877fd4866593317f3b120', // aide_logement partially interrupted, new version has the correct behavior
            '599808db4866593317f337e8', // aide_logement and paje_base partially interrupted, new version has the correct behavior
            '5998001e4866593317f32e31', // aide_logement partially interrupted, new version has the correct behavior
            '5997642e4866593317f2f2e5', // aide_logement partially interrupted, new version has the correct behavior
            '59971f104866593317f2b3de' // paje_clca partially interrupted, new version has the correct behavior
            ],
            '$lte': '59971f104866593317f2b3de',
            //'$in': ['5998d6d04866593317f3e45f']
        }, // Dodgy situation ?
        // _id: '5998d6d04866593317f3e45f', //
        // _id: '599a898d4866593317f4fd61',
        // _id: '5999fc6e4866593317f4cb26', // legacy API bug: 'Fix not interrupted ressources for famille'
    }, {}, {
        limit: 10000,
        sort: { dateDeValeur: -1 },
    }, processSituations);
}

Situation.deleteMany({}, function(err) {
    if (err) {
        console.log('ERROR:');
        console.log(err);
        process.exit(1);
    }
    //migrateTestSituations();
    migrateRecentSituations();
});
<commit_msg>Improve diff function used in migration validation<commit_after>/*
 * This script rely on
    "sgmap-mes-aides-api": "sgmap/mes-aides-api#replicate",
 */
var config = require('../../backend');
var expect = require('expect');
var _ = require('lodash');

var mongoose = require('mongoose');

// Setup mongoose
var AcceptanceTest = mongoose.model('AcceptanceTest');
var LegacySituation = mongoose.model('LegacySituation');
var Situation = mongoose.model('Situation');

var legacyMapping = require('sgmap-mes-aides-api/lib/simulation/openfisca/mapping');

var migration = require('../../backend/lib/migrations/initial');
var mapping = require('../../backend/lib/openfisca/mapping');

function deepDiffRight(left, right) {
  // if they are equals, return undefined
  if (_.isEqual(left, right)) { return; }
  if (_.isNumber(left) && _.isNumber(right) && Math.abs(right-left)<0.000001) { return; }
  // form now on, we can assure that `left` and `right` are not equals (equivalents)

  // if `left` and `right` are primitives, value changed
  // if `left` is a primitive and `right` an object, value has been replaced with an object
  // if `left` is an object and `right` a primitive, value has been replaced with a primitive
  // use `_.copy` to prevent object referrence issues
  if (! _.isObject(left) || ! _.isObject(right)) { return _.cloneDeep(right); }

  // make `result` the same type as `right`
  var result = _.isArray(right) ? [] : {};

  // since we know that both are objects,
  // iterate on `right` to see what changed or what's new in `right`
  _.forEach(right, function (value, key) {
    // recursion
    var diff = deepDiffRight(left[key], right[key]);
    // since the function returns undefined when `left` and `right` are equals,
    // only assing non-undefined values to result
    if (! _.isUndefined(diff)) {
        result[key] = diff;
    }
  });

  //console.log(result, (! result.length), (! Object.keys(result).length));
  if ((! result.length) && (! Object.keys(result).length)) { return; }

  return result;
}

function processSituations(err, retrievedTests) {
    if (err) {
        console.log(err);
        process.exit(1);
    }
    var tests = retrievedTests.slice(0, 2000);
    var done = 0;
    function processTests() {
        if(done == tests.length) {
            process.exit();
        }
        var test = tests[done];
        var testId = test._id;
        var situationId = test.scenario && test.scenario.situationId || (test.id || test._id || '').toString();

        LegacySituation.findById(situationId)
        .then(function(dbLegacySituation) {
            if (! dbLegacySituation) {
                console.log('var Err_testID_ = \'' + testId + '\'; // Null');
                done += 1;
                return processTests();
            }
            var dbSituationJSON = JSON.stringify(dbLegacySituation.toObject(), null, 2);
            var situation = dbLegacySituation.toObject();
            migration.persistedSituationPretransformationUpdate(situation);

            var legacyOpenfiscaRequest = legacyMapping.buildOpenFiscaRequest(_.cloneDeep(situation));

            var frontSituation = migration.migratePersistedSituation(situation);
            Situation.create(frontSituation, function(err, dbSituation) {
                if (err) {
                    console.log('var Err_testID_ = \'' + testId + '\'; // Situation.create ' + frontSituation._id );
                    console.log(JSON.stringify(err, null, 2));
                    process.exit(1);
                }

                var generatedSituation = frontSituation;
                var persistedSituation = _.omit(dbSituation.toObject(), '__v');

                var diff01 = deepDiffRight(generatedSituation, persistedSituation);
                var diff02 = deepDiffRight(persistedSituation, generatedSituation);
                if (/*true || //*/
                    diff01 || diff02) {
                    var structure0 = [diff01, diff02];
                    console.log('var testID_db_ = \'' + testId + '\' //  ' + frontSituation._id );
                    console.log(JSON.stringify(structure0, null, 2));
                    console.log(JSON.stringify([generatedSituation, persistedSituation], null, 2));

                    process.exit(1);
                }

                var newOpenfiscaRequest = mapping.buildOpenFiscaRequest(_.cloneDeep(persistedSituation));

                legacyOpenfiscaRequest.variables.sort();

                var diff1 = deepDiffRight(legacyOpenfiscaRequest, newOpenfiscaRequest);
                var diff2 = deepDiffRight(newOpenfiscaRequest, legacyOpenfiscaRequest);
                if (/*true || //*/
                    diff1 || diff2) {
                    var structure = [diff1, diff2];

                    console.log('var testID_req_ = \'' + testId + '\'; // Situation.create ' + frontSituation._id );
                    console.log(JSON.stringify(structure, null, 2));
                    console.log(JSON.stringify([newOpenfiscaRequest, legacyOpenfiscaRequest], null, 2));
                    process.exit(1);
                } else {
                    console.log('var situation_' + situationId + '_' + done + ' = \'ok\';');
                }

                done += 1;

                processTests();
            });
        }).catch(function(err) {
            console.log('var Err_testID_ = \'' + testId + '\'; // LegacySituation.findById');
            console.log(err);
            console.log(JSON.stringify(err, null, 2));
            process.exit(1);
        });
    }
    processTests();
}

function migrateTestSituations() {
    AcceptanceTest.find({
        // _id: '54e4c0bffb31b85d4074fe15', // taux incapacite
        // _id: '54915ffadf990f5b0286cf20', // salaire_imposable were double counted
        // _id: '5422c2d95be58c0200a07585', // Refactor backend - anneeDeReference proxy ressources - missing computed ressources
        // _id: '53d79982f6aa390200a6cd08', // Refactor backend - anneeDeReference proxy ressources
        // _id: '546c9783b7fb60347d0f3b24', // Ids missing in individus
        // _id: '53d21ee5159e330200810b87', // ObjectId versus String comparison issue
        // _id: '53d7b76df6aa390200a6cd5c', // Add firstName
        // _id: '540d69a1aa90150200440ee9', // remove rejected test - Caution will hang
        // _id: '582b06f467831ca264e93def', // Situation 582b06e667831ca264e93de5 set individus.2.specificSituations = []
        // _id: '53d220ce159e330200810b8e', // Persisted situation error
        // _id: '588f2778db011fdc7a230a12', // habite_chez_parents missing in migration mapping
        // _id: '5639eb3ceeab276a0422b307', // reverted interrupted aide_logement
        // _id: '54f07c45246e5054751ccfe3', // situation.logement.isChambre
        // _id: '54d8e0821fe4e8cf448e2bf3', // rfr diff
        // _id: '53d24d0ae40a7902009c3377', // Broken diff function
        // _id: '55b8cbb1d28906b46b50376a', // Uninferable ressourcesYearMoins2Captured = true from ressources
        // _id: '5540d71646bea9643939530f', // Unknown properties autresRevenusTns and caAutresRevenusTns
        // _id: '559e4fe4fa617858013d9a39', // rounding loyer
        // _id: '54e4c0bffb31b85d4074fe15', // tauxIncapacite presence
        // _id: '545a57f8ad58ab46561f56ec', // Ressources moved from individu to group entity
        state: { '$nin': ['unclaimed', 'rejected'] }
    }, {}, { sort: '_id' }, processSituations);
}

function migrateRecentSituations() {
    LegacySituation.find({
        _id: { '$nin': [
            '5999dc194866593317f4b006',
            '599983ae4866593317f44818',
            '599971c44866593317f43044', // ASI partially interrupted, new version has the correct behavior
            '599961ef4866593317f41fb2', // aide_logement partially interrupted, new version has the correct behavior
            '59991f1e4866593317f3e9d4', // ass partially interrupted, new version has the correct behavior
            '599877fd4866593317f3b120', // aide_logement partially interrupted, new version has the correct behavior
            '599808db4866593317f337e8', // aide_logement and paje_base partially interrupted, new version has the correct behavior
            '5998001e4866593317f32e31', // aide_logement partially interrupted, new version has the correct behavior
            '5997642e4866593317f2f2e5', // aide_logement partially interrupted, new version has the correct behavior
            '59971f104866593317f2b3de' // paje_clca partially interrupted, new version has the correct behavior
            ],
            '$lte': '59971f104866593317f2b3de',
            //'$in': ['5998d6d04866593317f3e45f']
        }, // Dodgy situation ?
        // _id: '5998d6d04866593317f3e45f', //
        // _id: '599a898d4866593317f4fd61',
        // _id: '5999fc6e4866593317f4cb26', // legacy API bug: 'Fix not interrupted ressources for famille'
    }, {}, {
        limit: 10000,
        sort: { dateDeValeur: -1 },
    }, processSituations);
}

Situation.deleteMany({}, function(err) {
    if (err) {
        console.log('ERROR:');
        console.log(err);
        process.exit(1);
    }
    //migrateTestSituations();
    migrateRecentSituations();
});
<|endoftext|>


<commit_before>// LICENSE : MIT
"use strict";
var assert = require("power-assert");
var createFormatter = require("../");
describe("textlint-formatter-test", function () {
    describe("createFormatter", function () {
        it("should return formatter function", function () {
            var formatter = createFormatter({
                formatterName: "stylish"
            });
            assert(typeof formatter === "function");
        });
        context("formatter", function () {
            it("should return output text", function () {
                var formatter = createFormatter({
                    formatterName: "stylish"
                });
                var output = formatter([
                    {
                        filePath: "./myfile.js",
                        messages: [
                            {
                                ruleId: "semi",
                                line: 1,
                                column: 23,
                                message: "Expected a semicolon."
                            }
                        ]
                    }
                ]);
                assert(output.length > 0);
            });
        });
        it("run all formatter", function () {
            var formatterNames = [
                "checkstyle",
                "compact",
                "jslint-xml",
                "junit",
                "pretty-error",
                "stylish",
                "tap",
                "json"
            ];
            formatterNames.forEach(function (name) {
                var formatter = createFormatter({
                    formatterName: name
                });
                var output = formatter([
                    {
                        filePath: __dirname + "/fixtures/myfile.js",
                        messages: [
                            {
                                ruleId: "semi",
                                line: 1,
                                column: 0,
                                message: "0 pattern."
                            },
                            {
                                ruleId: "semi",
                                line: 2,
                                column: 26,
                                message: "Expected a semicolon."
                            },
                            {
                                ruleId: "semi",
                                line: 1,
                                column: 21,
                                message: "Expected a semicolon."
                            },
                            {
                                ruleId: "semi",
                                line: 2,
                                column: 26,
                                message: "Expected a semicolon."
                            }
                        ]
                    }
                ]);
                assert(output.length > 0);
                console.log(output);
                console.log("==================");
            });
        });
    });

});<commit_msg>fix(test): column number start with 1<commit_after>// LICENSE : MIT
"use strict";
var assert = require("power-assert");
var createFormatter = require("../");
describe("textlint-formatter-test", function () {
    describe("createFormatter", function () {
        it("should return formatter function", function () {
            var formatter = createFormatter({
                formatterName: "stylish"
            });
            assert(typeof formatter === "function");
        });
        context("formatter", function () {
            it("should return output text", function () {
                var formatter = createFormatter({
                    formatterName: "stylish"
                });
                var output = formatter([
                    {
                        filePath: "./myfile.js",
                        messages: [
                            {
                                ruleId: "semi",
                                line: 1,
                                column: 23,
                                message: "Expected a semicolon."
                            }
                        ]
                    }
                ]);
                assert(output.length > 0);
            });
        });
        it("run all formatter", function () {
            var formatterNames = [
                "checkstyle",
                "compact",
                "jslint-xml",
                "junit",
                "pretty-error",
                "stylish",
                "tap",
                "json"
            ];
            formatterNames.forEach(function (name) {
                var formatter = createFormatter({
                    formatterName: name
                });
                var output = formatter([
                    {
                        filePath: __dirname + "/fixtures/myfile.js",
                        messages: [
                            {
                                ruleId: "semi",
                                line: 1,
                                column: 1,
                                message: "0 pattern."
                            },
                            {
                                ruleId: "semi",
                                line: 2,
                                column: 26,
                                message: "Expected a semicolon."
                            },
                            {
                                ruleId: "semi",
                                line: 1,
                                column: 21,
                                message: "Expected a semicolon."
                            },
                            {
                                ruleId: "semi",
                                line: 2,
                                column: 26,
                                message: "Expected a semicolon."
                            }
                        ]
                    }
                ]);
                assert(output.length > 0);
                console.log(output);
                console.log("==================");
            });
        });
    });

});<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

import React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
import dis from '../../../dispatcher';
import classNames from 'classnames';
import { _t } from '../../../languageHandler';

const MessageCase = Object.freeze({
    NotLoggedIn: "NotLoggedIn",
    Joining: "Joining",
    Loading: "Loading",
    Rejecting: "Rejecting",
    Kicked: "Kicked",
    Banned: "Banned",
    OtherThreePIDError: "OtherThreePIDError",
    InvitedEmailMismatch: "InvitedEmailMismatch",
    Invite: "Invite",
    ViewingRoom: "ViewingRoom",
    RoomNotFound: "RoomNotFound",
    OtherError: "OtherError",
});

module.exports = React.createClass({
    displayName: 'RoomPreviewBar',

    propTypes: {
        onJoinClick: PropTypes.func,
        onRejectClick: PropTypes.func,
        onForgetClick: PropTypes.func,
        onSignInClick: PropTypes.func,
        onSignUpClick: PropTypes.func,

        // if inviterName is specified, the preview bar will shown an invite to the room.
        // You should also specify onRejectClick if specifiying inviterName
        inviterName: PropTypes.string,

        // If invited by 3rd party invite, the email address the invite was sent to
        invitedEmail: PropTypes.string,

        // A standard client/server API error object. If supplied, indicates that the
        // caller was unable to fetch details about the room for the given reason.
        error: PropTypes.object,

        canPreview: PropTypes.bool,
        room: PropTypes.object,

        // When a spinner is present, a spinnerState can be specified to indicate the
        // purpose of the spinner.
        spinner: PropTypes.bool,
        spinnerState: PropTypes.oneOf(["joining"]),

        // The alias that was used to access this room, if appropriate
        // If given, this will be how the room is referred to (eg.
        // in error messages).
        roomAlias: PropTypes.string,
    },

    getDefaultProps: function() {
        return {
            onJoinClick: function() {},
        };
    },

    getInitialState: function() {
        return {
            busy: false,
        };
    },

    componentWillMount: function() {
        // If this is an invite and we've been told what email
        // address was invited, fetch the user's list of Threepids
        // so we can check them against the one that was invited
        if (this.props.inviterName && this.props.invitedEmail) {
            this.setState({busy: true});
            MatrixClientPeg.get().lookupThreePid(
                'email', this.props.invitedEmail,
            ).finally(() => {
                this.setState({busy: false});
            }).done((result) => {
                this.setState({invitedEmailMxid: result.mxid});
            }, (err) => {
                this.setState({threePidFetchError: err});
            });
        }
    },

    _getMessageCase() {
        const isGuest = MatrixClientPeg.get().isGuest();

        if (isGuest) {
            return MessageCase.NotLoggedIn;
        }

        const myMember = this.props.room &&
            this.props.room.getMember(MatrixClientPeg.get().getUserId());

        if (myMember) {
            if (myMember.isKicked()) {
                return MessageCase.Kicked;
            } else if (myMember.membership === "ban") {
                return MessageCase.Banned;
            }
        }

        if (this.props.joining) {
            return MessageCase.Joining;
        } else if (this.props.rejecting) {
            return MessageCase.Rejecting;
        } else if (this.props.loading) {
            return MessageCase.Loading;
        }

        if (this.props.inviterName) {
            if (this.props.invitedEmail) {
                if (this.state.threePidFetchError) {
                    return MessageCase.OtherThreePIDError;
                } else if (this.state.invitedEmailMxid != MatrixClientPeg.get().credentials.userId) {
                    return MessageCase.InvitedEmailMismatch;
                }
            }
            return MessageCase.Invite;
        } else if (this.props.error) {
            if (this.props.error.errcode == 'M_NOT_FOUND') {
                return MessageCase.RoomNotFound;
            } else {
                return MessageCase.OtherError;
            }
        } else {
            return MessageCase.ViewingRoom;
        }
    },

    _getKickOrBanInfo() {
        const myMember = this.props.room ?
            this.props.room.getMember(MatrixClientPeg.get().getUserId()) :
            null;
        if (!myMember) {
            return {};
        }
        const kickerMember = this.props.room.currentState.getMember(
            myMember.events.member.getSender(),
        );
        const memberName = kickerMember ?
            kickerMember.name : myMember.events.member.getSender();
        const reason = myMember.events.member.getContent().reason;
        return {memberName, reason};
    },

    _joinRule: function() {
        const room = this.props.room;
        if (room) {
            const joinRules = room.currentState.getStateEvents('m.room.join_rules', '');
            if (joinRules) {
                return joinRules.getContent().join_rule;
            }
        }
    },

    _roomName: function() {
        return this.props.room ? this.props.room.name : (this.props.room_alias || _t("This room"));
    },

    onLoginClick: function() {
        dis.dispatch({ action: 'start_login' });
    },

    onRegisterClick: function() {
        dis.dispatch({ action: 'start_registration' });
    },

    render: function() {
        let showSpinner = false;
        let darkStyle = false;
        let title;
        let subTitle;
        let primaryActionHandler;
        let primaryActionLabel;
        let secondaryActionHandler;
        let secondaryActionLabel;

        switch (this._getMessageCase()) {
            case MessageCase.Joining: {
                title = _t("Joining room …");
                showSpinner = true;
                break;
            }
            case MessageCase.Loading: {
                title = _t("Loading …");
                showSpinner = true;
                break;
            }
            case MessageCase.Rejecting: {
                title = _t("Rejecting invite …");
                showSpinner = true;
                break;
            }
            case MessageCase.NotLoggedIn: {
                darkStyle = true;
                title = _t("Join the conversation with an account");
                primaryActionLabel = _t("Sign Up");
                primaryActionHandler = this.onRegisterClick;
                secondaryActionLabel = _t("Sign In");
                secondaryActionHandler = this.onLoginClick;
                break;
            }
            case MessageCase.Kicked: {
                const {memberName, reason} = this._getKickOrBanInfo();
                title = _t("You were kicked from this room by %(memberName)s", {memberName});
                subTitle = _t("Reason: %(reason)s", {reason});
                primaryActionLabel = _t("Re-join");
                primaryActionHandler = this.props.onJoinClick;
                secondaryActionLabel = _t("Forget this room");
                secondaryActionHandler = this.props.onForgetClick;
                break;
            }
            case MessageCase.Banned: {
                const {memberName, reason} = this._getKickOrBanInfo();
                title = _t("You were banned from this room by %(memberName)s", {memberName});
                subTitle = _t("Reason: %(reason)s", {reason});
                primaryActionLabel = _t("Forget this room");
                primaryActionHandler = this.props.onForgetClick;
                break;
            }
            case MessageCase.OtherThreePIDError: {
                title = _t("Something went wrong with your invite to this room");
                const joinRule = this._joinRule();
                const errCodeMessage = _t("%(errcode)s was returned while trying to valide your invite. You could try to pass this information on to a room admin.",
                    {errcode: this.state.threePidFetchError.errcode},
                );
                switch (joinRule) {
                    case "invite":
                        subTitle = [
                            _t("You can only join it with a working invite."),
                            errCodeMessage,
                        ];
                        break;
                    case "public":
                        subTitle = _t("You can still join it because this is a public room.");
                        primaryActionLabel = _t("Join the discussion");
                        primaryActionHandler = this.props.onJoinClick;
                        break;
                    default:
                        subTitle = errCodeMessage;
                        primaryActionLabel = _t("Try to join anyway");
                        primaryActionHandler = this.props.onJoinClick;
                        break;
                }
                break;
            }
            case MessageCase.InvitedEmailMismatch: {
                title = _t("The room invite wasn't sent to your account");
                const joinRule = this._joinRule();
                if (joinRule === "public") {
                    subTitle = _t("You can still join it because this is a public room.");
                    primaryActionLabel = _t("Join the discussion");
                    primaryActionHandler = this.props.onJoinClick;
                } else {
                    subTitle = _t("Sign in with a different account, ask for another invite, or add the e-mail address %(email)s to this account.", {email: this.props.invitedEmail});
                    if (joinRule !== "invite") {
                        primaryActionLabel = _t("Try to join anyway");
                        primaryActionHandler = this.props.onJoinClick;
                    }
                }
                break;
            }
            case MessageCase.Invite: {
                if (this.props.canPreview) {
                    title = _t("%(memberName)s invited you to this room", {memberName: this.props.inviterName});
                } else {
                    title = _t("Do you want to join this room?");
                    subTitle = _t("%(memberName)s invited you", {memberName: this.props.inviterName});
                }
                primaryActionLabel = _t("Accept");
                primaryActionHandler = this.props.onJoinClick;
                secondaryActionLabel = _t("Reject");
                secondaryActionHandler = this.props.onRejectClick;
                break;
            }
            case MessageCase.ViewingRoom: {
                if (this.props.canPreview) {
                    title = _t("You're previewing this room. Want to join it?");
                } else {
                    title = _t("This room can't be previewed. Do you want to join it?");
                }
                primaryActionLabel = _t("Join the discussion");
                primaryActionHandler = this.props.onJoinClick;
                break;
            }
            case MessageCase.RoomNotFound: {
                title = _t("%(roomName)s does not exist.", {roomName: this._roomName()});
                subTitle = _t("This room doesn't exist. Are you sure you're at the right place?");
                break;
            }
            case MessageCase.OtherError: {
                title = _t("%(roomName)s is not accessible at this time.", {roomName: this._roomName()});
                subTitle = [
                    _t("Try again later, or ask a room admin to check if you have access."),
                    _t("%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please <issueLink>submit a bug report</issueLink>.",
                        { errcode: this.props.error.errcode },
                        { issueLink: label => { label } },
                    ),
                ];
                break;
            }
        }

        const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
        const Spinner = sdk.getComponent('elements.Spinner');

        let subTitleElements;
        if (subTitle) {
            if (!Array.isArray(subTitle)) {
                subTitle = [subTitle];
            }
            subTitleElements = subTitle.map((t, i) => <p key={`subTitle${i}`}>{t}
);
        }

        let titleElement;
        if (showSpinner) {
            titleElement =
<Spinner />{ title }
;
        } else {
            titleElement =
{ title }
;
        }

        let primaryButton;
        if (primaryActionHandler) {
            primaryButton = (
                <AccessibleButton kind="primary" onClick={primaryActionHandler}>
                    { primaryActionLabel }
                </AccessibleButton>
            );
        }

        let secondaryButton;
        if (secondaryActionHandler) {
            secondaryButton = (
                <AccessibleButton kind="secondary" onClick={secondaryActionHandler}>
                    { secondaryActionLabel }
                </AccessibleButton>
            );
        }

        const classes = classNames("mx_RoomPreviewBar", "dark-panel", {
            "mx_RoomPreviewBar_panel": this.props.canPreview,
            "mx_RoomPreviewBar_dialog": !this.props.canPreview,
            "mx_RoomPreviewBar_dark": darkStyle,
        });

        return (

                    { titleElement }
                    { subTitleElements }

                    { secondaryButton }
                    { primaryButton }

        );
    },
});
<commit_msg>also return Joining when internally busy looking something up<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

import React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
import dis from '../../../dispatcher';
import classNames from 'classnames';
import { _t } from '../../../languageHandler';

const MessageCase = Object.freeze({
    NotLoggedIn: "NotLoggedIn",
    Joining: "Joining",
    Loading: "Loading",
    Rejecting: "Rejecting",
    Kicked: "Kicked",
    Banned: "Banned",
    OtherThreePIDError: "OtherThreePIDError",
    InvitedEmailMismatch: "InvitedEmailMismatch",
    Invite: "Invite",
    ViewingRoom: "ViewingRoom",
    RoomNotFound: "RoomNotFound",
    OtherError: "OtherError",
});

module.exports = React.createClass({
    displayName: 'RoomPreviewBar',

    propTypes: {
        onJoinClick: PropTypes.func,
        onRejectClick: PropTypes.func,
        onForgetClick: PropTypes.func,
        onSignInClick: PropTypes.func,
        onSignUpClick: PropTypes.func,

        // if inviterName is specified, the preview bar will shown an invite to the room.
        // You should also specify onRejectClick if specifiying inviterName
        inviterName: PropTypes.string,

        // If invited by 3rd party invite, the email address the invite was sent to
        invitedEmail: PropTypes.string,

        // A standard client/server API error object. If supplied, indicates that the
        // caller was unable to fetch details about the room for the given reason.
        error: PropTypes.object,

        canPreview: PropTypes.bool,
        room: PropTypes.object,

        // When a spinner is present, a spinnerState can be specified to indicate the
        // purpose of the spinner.
        spinner: PropTypes.bool,
        spinnerState: PropTypes.oneOf(["joining"]),

        // The alias that was used to access this room, if appropriate
        // If given, this will be how the room is referred to (eg.
        // in error messages).
        roomAlias: PropTypes.string,
    },

    getDefaultProps: function() {
        return {
            onJoinClick: function() {},
        };
    },

    getInitialState: function() {
        return {
            busy: false,
        };
    },

    componentWillMount: function() {
        // If this is an invite and we've been told what email
        // address was invited, fetch the user's list of Threepids
        // so we can check them against the one that was invited
        if (this.props.inviterName && this.props.invitedEmail) {
            this.setState({busy: true});
            MatrixClientPeg.get().lookupThreePid(
                'email', this.props.invitedEmail,
            ).finally(() => {
                this.setState({busy: false});
            }).done((result) => {
                this.setState({invitedEmailMxid: result.mxid});
            }, (err) => {
                this.setState({threePidFetchError: err});
            });
        }
    },

    _getMessageCase() {
        const isGuest = MatrixClientPeg.get().isGuest();

        if (isGuest) {
            return MessageCase.NotLoggedIn;
        }

        const myMember = this.props.room &&
            this.props.room.getMember(MatrixClientPeg.get().getUserId());

        if (myMember) {
            if (myMember.isKicked()) {
                return MessageCase.Kicked;
            } else if (myMember.membership === "ban") {
                return MessageCase.Banned;
            }
        }

        if (this.props.joining) {
            return MessageCase.Joining;
        } else if (this.props.rejecting) {
            return MessageCase.Rejecting;
        } else if (this.props.loading || this.state.busy) {
            return MessageCase.Loading;
        }

        if (this.props.inviterName) {
            if (this.props.invitedEmail) {
                if (this.state.threePidFetchError) {
                    return MessageCase.OtherThreePIDError;
                } else if (this.state.invitedEmailMxid != MatrixClientPeg.get().credentials.userId) {
                    return MessageCase.InvitedEmailMismatch;
                }
            }
            return MessageCase.Invite;
        } else if (this.props.error) {
            if (this.props.error.errcode == 'M_NOT_FOUND') {
                return MessageCase.RoomNotFound;
            } else {
                return MessageCase.OtherError;
            }
        } else {
            return MessageCase.ViewingRoom;
        }
    },

    _getKickOrBanInfo() {
        const myMember = this.props.room ?
            this.props.room.getMember(MatrixClientPeg.get().getUserId()) :
            null;
        if (!myMember) {
            return {};
        }
        const kickerMember = this.props.room.currentState.getMember(
            myMember.events.member.getSender(),
        );
        const memberName = kickerMember ?
            kickerMember.name : myMember.events.member.getSender();
        const reason = myMember.events.member.getContent().reason;
        return {memberName, reason};
    },

    _joinRule: function() {
        const room = this.props.room;
        if (room) {
            const joinRules = room.currentState.getStateEvents('m.room.join_rules', '');
            if (joinRules) {
                return joinRules.getContent().join_rule;
            }
        }
    },

    _roomName: function() {
        return this.props.room ? this.props.room.name : (this.props.room_alias || _t("This room"));
    },

    onLoginClick: function() {
        dis.dispatch({ action: 'start_login' });
    },

    onRegisterClick: function() {
        dis.dispatch({ action: 'start_registration' });
    },

    render: function() {
        let showSpinner = false;
        let darkStyle = false;
        let title;
        let subTitle;
        let primaryActionHandler;
        let primaryActionLabel;
        let secondaryActionHandler;
        let secondaryActionLabel;

        switch (this._getMessageCase()) {
            case MessageCase.Joining: {
                title = _t("Joining room …");
                showSpinner = true;
                break;
            }
            case MessageCase.Loading: {
                title = _t("Loading …");
                showSpinner = true;
                break;
            }
            case MessageCase.Rejecting: {
                title = _t("Rejecting invite …");
                showSpinner = true;
                break;
            }
            case MessageCase.NotLoggedIn: {
                darkStyle = true;
                title = _t("Join the conversation with an account");
                primaryActionLabel = _t("Sign Up");
                primaryActionHandler = this.onRegisterClick;
                secondaryActionLabel = _t("Sign In");
                secondaryActionHandler = this.onLoginClick;
                break;
            }
            case MessageCase.Kicked: {
                const {memberName, reason} = this._getKickOrBanInfo();
                title = _t("You were kicked from this room by %(memberName)s", {memberName});
                subTitle = _t("Reason: %(reason)s", {reason});
                primaryActionLabel = _t("Re-join");
                primaryActionHandler = this.props.onJoinClick;
                secondaryActionLabel = _t("Forget this room");
                secondaryActionHandler = this.props.onForgetClick;
                break;
            }
            case MessageCase.Banned: {
                const {memberName, reason} = this._getKickOrBanInfo();
                title = _t("You were banned from this room by %(memberName)s", {memberName});
                subTitle = _t("Reason: %(reason)s", {reason});
                primaryActionLabel = _t("Forget this room");
                primaryActionHandler = this.props.onForgetClick;
                break;
            }
            case MessageCase.OtherThreePIDError: {
                title = _t("Something went wrong with your invite to this room");
                const joinRule = this._joinRule();
                const errCodeMessage = _t("%(errcode)s was returned while trying to valide your invite. You could try to pass this information on to a room admin.",
                    {errcode: this.state.threePidFetchError.errcode},
                );
                switch (joinRule) {
                    case "invite":
                        subTitle = [
                            _t("You can only join it with a working invite."),
                            errCodeMessage,
                        ];
                        break;
                    case "public":
                        subTitle = _t("You can still join it because this is a public room.");
                        primaryActionLabel = _t("Join the discussion");
                        primaryActionHandler = this.props.onJoinClick;
                        break;
                    default:
                        subTitle = errCodeMessage;
                        primaryActionLabel = _t("Try to join anyway");
                        primaryActionHandler = this.props.onJoinClick;
                        break;
                }
                break;
            }
            case MessageCase.InvitedEmailMismatch: {
                title = _t("The room invite wasn't sent to your account");
                const joinRule = this._joinRule();
                if (joinRule === "public") {
                    subTitle = _t("You can still join it because this is a public room.");
                    primaryActionLabel = _t("Join the discussion");
                    primaryActionHandler = this.props.onJoinClick;
                } else {
                    subTitle = _t("Sign in with a different account, ask for another invite, or add the e-mail address %(email)s to this account.", {email: this.props.invitedEmail});
                    if (joinRule !== "invite") {
                        primaryActionLabel = _t("Try to join anyway");
                        primaryActionHandler = this.props.onJoinClick;
                    }
                }
                break;
            }
            case MessageCase.Invite: {
                if (this.props.canPreview) {
                    title = _t("%(memberName)s invited you to this room", {memberName: this.props.inviterName});
                } else {
                    title = _t("Do you want to join this room?");
                    subTitle = _t("%(memberName)s invited you", {memberName: this.props.inviterName});
                }
                primaryActionLabel = _t("Accept");
                primaryActionHandler = this.props.onJoinClick;
                secondaryActionLabel = _t("Reject");
                secondaryActionHandler = this.props.onRejectClick;
                break;
            }
            case MessageCase.ViewingRoom: {
                if (this.props.canPreview) {
                    title = _t("You're previewing this room. Want to join it?");
                } else {
                    title = _t("This room can't be previewed. Do you want to join it?");
                }
                primaryActionLabel = _t("Join the discussion");
                primaryActionHandler = this.props.onJoinClick;
                break;
            }
            case MessageCase.RoomNotFound: {
                title = _t("%(roomName)s does not exist.", {roomName: this._roomName()});
                subTitle = _t("This room doesn't exist. Are you sure you're at the right place?");
                break;
            }
            case MessageCase.OtherError: {
                title = _t("%(roomName)s is not accessible at this time.", {roomName: this._roomName()});
                subTitle = [
                    _t("Try again later, or ask a room admin to check if you have access."),
                    _t("%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please <issueLink>submit a bug report</issueLink>.",
                        { errcode: this.props.error.errcode },
                        { issueLink: label => { label } },
                    ),
                ];
                break;
            }
        }

        const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
        const Spinner = sdk.getComponent('elements.Spinner');

        let subTitleElements;
        if (subTitle) {
            if (!Array.isArray(subTitle)) {
                subTitle = [subTitle];
            }
            subTitleElements = subTitle.map((t, i) => <p key={`subTitle${i}`}>{t}
);
        }

        let titleElement;
        if (showSpinner) {
            titleElement =
<Spinner />{ title }
;
        } else {
            titleElement =
{ title }
;
        }

        let primaryButton;
        if (primaryActionHandler) {
            primaryButton = (
                <AccessibleButton kind="primary" onClick={primaryActionHandler}>
                    { primaryActionLabel }
                </AccessibleButton>
            );
        }

        let secondaryButton;
        if (secondaryActionHandler) {
            secondaryButton = (
                <AccessibleButton kind="secondary" onClick={secondaryActionHandler}>
                    { secondaryActionLabel }
                </AccessibleButton>
            );
        }

        const classes = classNames("mx_RoomPreviewBar", "dark-panel", {
            "mx_RoomPreviewBar_panel": this.props.canPreview,
            "mx_RoomPreviewBar_dialog": !this.props.canPreview,
            "mx_RoomPreviewBar_dark": darkStyle,
        });

        return (

                    { titleElement }
                    { subTitleElements }

                    { secondaryButton }
                    { primaryButton }

        );
    },
});
<|endoftext|>