Source: app/selectors/userAccountSelectors.js

import { createSelector } from 'reselect';
import { addressFields } from 'common/src/app/data/enum/FieldNames/AccountFieldNames';
import returnOnlyPresentValues from 'common/src/app/util/form/returnOnlyPresentValues';
import AccountState from '../data/enum/AccountState';
import { hasFlag } from '../util/bitwiseUtils';
import { JWT_CLAUSE_MAP, JWT_CLAUSE_MAP_ID_TOKEN } from '../reducers/authenticationReducer';
import { ACCOUNT } from '../data/entityTypes';

const EMPTY_ARRAY = [];
const EMPTY_OBJECT = {};

/**
 * Selector that receives the root redux state and returns an object
 * with user information. If the user is not logged in, this selector returns
 * null. If the user is logged in but the account entity is not loaded from
 * the API, this object will contain a limited amount of properties which are
 * available on the access token.
 */
export const userAccountSelector = createSelector(
  state => (state.authentication && state.authentication.userInfo) || EMPTY_OBJECT,
  state => state.entities[ACCOUNT] || {},
  (userInfo = {}, accountEntities) => {
    const result = {};

    result.initialUserState = userInfo.initialUserState;

    Object.keys(JWT_CLAUSE_MAP).forEach(
      clause => (result[JWT_CLAUSE_MAP[clause]] = userInfo[clause]),
    );
    Object.keys(JWT_CLAUSE_MAP_ID_TOKEN).forEach(
      clause => (result[JWT_CLAUSE_MAP_ID_TOKEN[clause]] = userInfo[clause]),
    );

    // Always return groupId as an array
    if (result[JWT_CLAUSE_MAP.group_id]) {
      result[JWT_CLAUSE_MAP.group_id] = [].concat(result[JWT_CLAUSE_MAP.group_id]);
    }

    if (accountEntities[result.id]) {
      return {
        ...result,
        ...accountEntities[result.id],
      };
    }

    return result;
  },
);

/**
 * Selector that extracts the user id from the userAccountSelector.
 * Returns null if no account data is available
 */
export const userIdSelector = createSelector(
  [userAccountSelector, (state, props) => props && props.id],
  (userAccount, userId) => userId || (userAccount && userAccount.id),
);

/**
 * Selector that extracts the user groupIds from the userAccountSelector.
 * Returns empty array if no account data is available
 */
export const userGroupIdsSelector = createSelector(
  userAccountSelector,
  (state, props) => props && props.groups,
  (userAccount, groupId) => groupId || (userAccount && userAccount.groupId) || EMPTY_ARRAY,
);

/**
 * Retrieve a user account by id
 */
export const userAccountByIdSelector = userId =>
  createSelector(
    state => state.entities[ACCOUNT][userId],
    accountEntities => {
      if (accountEntities) {
        return {
          account: accountEntities,
        };
      }

      return {
        account: {},
      };
    },
  );

export const isSubscriptionValidSelector = createSelector(userAccountSelector, account => {
  if (account) {
    const onlineSubscriptionValid = hasFlag(
      account.accountState,
      AccountState.ONLINE_SUBSCRIPTION_VALID,
    );

    const groupSubscriptionvalid = hasFlag(
      account.accountState,
      AccountState.GROUP_MEMBERSHIP_VALID,
    );

    // return if we have a valid group or online subscription
    return onlineSubscriptionValid || groupSubscriptionvalid;
  }

  return false;
});

export const isUserLoggedInSelector = createSelector(
  state => state.identity,
  identity => identity && !!identity.user,
);

export const userIdentitySelector = createSelector(
  state => state.identity,
  identity => (identity && identity.user) || EMPTY_OBJECT,
);

export const isOnlineSubscriptionValidSelector = createSelector(userAccountSelector, account => {
  if (!account) return false;

  return hasFlag(account.accountState, AccountState.ONLINE_SUBSCRIPTION_VALID);
});

export const isGroupMembershipValidSelector = createSelector(userAccountSelector, account => {
  if (!account) return false;

  return hasFlag(account.accountState, AccountState.GROUP_MEMBERSHIP_VALID);
});

export const userAndEntityIdSelector = createSelector(
  (state, userId) => {
    const ownUserId = userIdSelector(state);
    const selectedId = !userId || userId.toString() === ownUserId ? 'me' : userId;
    const entityId = selectedId === 'me' ? ownUserId : selectedId;

    return {
      selectedId,
      entityId,
    };
  },
  user => ({
    ...user,
  }),
);

export const memberTypeSelector = createSelector(userAccountSelector, account =>
  !account ? false : account.memberType,
);

// Map values a user has to each possible field
export const userAddressFieldsSelector = createSelector(userAccountSelector, account =>
  returnOnlyPresentValues(addressFields, account),
);

export const userAccountAvatarSelector = createSelector(
  userAccountSelector,
  account => account?.avatar,
);

export const userAccountWeightUnitSelector = createSelector(
  userAccountSelector,
  account => account?.weightUnit,
);

export const authUserInfoSelector = createSelector(
  state => state.authentication.userInfo,
  userInfo => userInfo,
);

export const userAccountFirstNameSelector = createSelector(
  userAccountSelector,
  account => account?.firstName,
);