UNPKG

@selfcommunity/react-core

Version:

React Core Components useful for integrating UI Community components (react-ui).

263 lines (260 loc) • 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useSCUser = exports.SCUserContext = void 0; const tslib_1 = require("tslib"); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const api_services_1 = require("@selfcommunity/api-services"); const SCContextProvider_1 = require("../SCContextProvider"); const useSCAuth_1 = tslib_1.__importStar(require("../../../hooks/useSCAuth")); const utils_1 = require("@selfcommunity/utils"); const pubsub_js_1 = tslib_1.__importDefault(require("pubsub-js")); const Errors_1 = require("../../../constants/Errors"); const use_deep_compare_effect_1 = require("use-deep-compare-effect"); const useSCFollowedCategoriesManager_1 = tslib_1.__importDefault(require("../../../hooks/useSCFollowedCategoriesManager")); const useSCFollowedManager_1 = tslib_1.__importDefault(require("../../../hooks/useSCFollowedManager")); const useSCSettingsManager_1 = tslib_1.__importDefault(require("../../../hooks/useSCSettingsManager")); const useSCFollowersManager_1 = tslib_1.__importDefault(require("../../../hooks/useSCFollowersManager")); const useSCConnectionsManager_1 = tslib_1.__importDefault(require("../../../hooks/useSCConnectionsManager")); const types_1 = require("@selfcommunity/types"); const useSCSubscribedIncubatorsManager_1 = tslib_1.__importDefault(require("../../../hooks/useSCSubscribedIncubatorsManager")); const useSCBlockedUsersManager_1 = tslib_1.__importDefault(require("../../../hooks/useSCBlockedUsersManager")); const Session = tslib_1.__importStar(require("../../../constants/Session")); const useSCSubscribedGroupsManager_1 = tslib_1.__importDefault(require("../../../hooks/useSCSubscribedGroupsManager")); const useSCSubscribedEventsManager_1 = tslib_1.__importDefault(require("../../../hooks/useSCSubscribedEventsManager")); /** * SCUserContext (Authentication Context) * :::tip Context can be consumed in one of the following ways: ```jsx 1. <SCUserContext.Consumer>{(user, session, error, loading, logout) => (...)}</SCUserContext.Consumer> ``` ```jsx 2. const scUserContext: SCUserContextType = useContext(SCUserContext); ``` ```jsx 3. const scUserContext: SCUserContextType = useSCUser(); ```` ::: */ exports.SCUserContext = (0, react_1.createContext)({}); /** * #### Description: * This component keeps current user logged and session; it is exported as we need to wrap the entire app with it * @param children * @return * ```jsx * <SCUserContext.Provider value={contextValue}>{!state.loading && children}</SCUserContext.Provider> * ``` */ function SCUserProvider({ children }) { const scContext = (0, react_1.useContext)(SCContextProvider_1.SCContext); /** * Manage user session * Refresh token if necessary */ const initialSession = scContext.settings.session; const { state, dispatch, helpers } = (0, useSCAuth_1.default)(initialSession); /** * Helper handle change user */ function updateUser(info) { dispatch({ type: useSCAuth_1.userActionTypes.UPDATE_USER, payload: info }); } /** * Managers followed, connections, blocked users, categories, incubators, settings */ const settingsManager = (0, useSCSettingsManager_1.default)(state.user); const followedManager = (0, useSCFollowedManager_1.default)(state.user); const followersManager = (0, useSCFollowersManager_1.default)(state.user); const subscribedIncubatorsManager = (0, useSCSubscribedIncubatorsManager_1.default)(state.user); const connectionsManager = (0, useSCConnectionsManager_1.default)(state.user); const categoriesManager = (0, useSCFollowedCategoriesManager_1.default)(state.user, updateUser); const blockedUsersManager = (0, useSCBlockedUsersManager_1.default)(state.user); const subscribedGroupsManager = (0, useSCSubscribedGroupsManager_1.default)(state.user); const subscribedEventsManager = (0, useSCSubscribedEventsManager_1.default)(state.user); /** * Ref notifications subscribers: BLOCKED_USER, UNBLOCKED_USER */ const notificationUserBlockedSubscription = (0, react_1.useRef)(null); const notificationUserUnBlockedSubscription = (0, react_1.useRef)(null); /** * Check if there is a currently active session * when the provider is mounted for the first time. * If there is an error, it means there is no session. */ (0, use_deep_compare_effect_1.useDeepCompareEffectNoCheck)(() => { if ((state.session.authToken && state.session.authToken.accessToken) || state.session.type === Session.COOKIE_SESSION) { dispatch({ type: useSCAuth_1.userActionTypes.LOGIN_LOADING }); api_services_1.UserService.getCurrentUser() .then((user) => { dispatch({ type: useSCAuth_1.userActionTypes.LOGIN_SUCCESS, payload: { user } }); }) .catch((error) => { utils_1.Logger.error(Errors_1.SCOPE_SC_CORE, 'Unable to retrieve the authenticated user.'); dispatch({ type: useSCAuth_1.userActionTypes.LOGIN_FAILURE, payload: { error } }); }); } else { dispatch({ type: useSCAuth_1.userActionTypes.LOGIN_FAILURE }); } }, [state.session]); /** * Controls caching of follow categories, users, etc... * To avoid multi-tab problems (only for client side), on visibility change * and document is in foreground refresh the cache */ (0, react_1.useEffect)(() => { window.document.addEventListener('visibilitychange', handleVisibilityChange); return () => { window.document.removeEventListener('visibilitychange', handleVisibilityChange); }; }, []); /** * handler handleVisibilityChange for this provider * Refresh followed categories, users, etc.. */ function handleVisibilityChange() { if (!window.document.hidden && state.user) { settingsManager.refresh && settingsManager.refresh(); refreshCounters(); categoriesManager.refresh && categoriesManager.refresh(); followedManager.refresh && followedManager.refresh(); connectionsManager.refresh && connectionsManager.refresh(); subscribedIncubatorsManager.refresh && subscribedIncubatorsManager.refresh(); blockedUsersManager.refresh && blockedUsersManager.refresh(); subscribedGroupsManager.refresh && subscribedGroupsManager.refresh(); subscribedEventsManager.refresh && subscribedEventsManager.refresh(); } } /** * Subscribes to handle notifications of type BLOCKED_USER, UNBLOCKED_USER. * When receive this type of notifications, the user.status must be update. */ (0, react_1.useEffect)(() => { notificationUserBlockedSubscription.current = pubsub_js_1.default.subscribe(`${types_1.SCNotificationTopicType.INTERACTION}.${types_1.SCNotificationTypologyType.BLOCKED_USER}`, () => { dispatch({ type: useSCAuth_1.userActionTypes.UPDATE_USER, payload: { status: types_1.SCUserStatus.BLOCKED } }); }); notificationUserUnBlockedSubscription.current = pubsub_js_1.default.subscribe(`${types_1.SCNotificationTopicType.INTERACTION}.${types_1.SCNotificationTypologyType.UNBLOCKED_USER}`, () => { dispatch({ type: useSCAuth_1.userActionTypes.UPDATE_USER, payload: { status: types_1.SCUserStatus.APPROVED } }); }); return () => { pubsub_js_1.default.unsubscribe(notificationUserBlockedSubscription.current); pubsub_js_1.default.unsubscribe(notificationUserUnBlockedSubscription.current); }; }, []); /** * Handle change unseen interactions counter */ function setUnseenInteractionsCounter(counter) { dispatch({ type: useSCAuth_1.userActionTypes.UPDATE_USER, payload: { unseen_interactions_counter: Math.max(counter, 0) } }); } /** * Handle change unseen notification banners counter */ function setUnseenNotificationBannersCounter(counter) { dispatch({ type: useSCAuth_1.userActionTypes.UPDATE_USER, payload: { unseen_notification_banners_counter: Math.max(counter, 0) } }); } /** * Helper to refresh the session */ function refreshSession() { return helpers.refreshSession(); } /** * Helper to refresh counters * Use getCurrentUser service since the counters * have been inserted into the serialized user object */ const refreshCounters = (0, react_1.useMemo)(() => () => { if (state.user) { return api_services_1.UserService.getCurrentUser() .then((user) => { dispatch({ type: useSCAuth_1.userActionTypes.UPDATE_USER, payload: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ unseen_interactions_counter: user.unseen_interactions_counter, unseen_notification_banners_counter: user.unseen_notification_banners_counter }, (user.followers_counter ? { followers_counter: user.followers_counter } : {})), (user.followings_counter ? { followings_counter: user.followings_counter } : {})), (user.categories_counter ? { categories_counter: user.categories_counter } : {})), (user.discussions_counter ? { discussions_counter: user.discussions_counter } : {})), (user.posts_counter ? { posts_counter: user.posts_counter } : {})), (user.statuses_counter ? { status_counter: user.statuses_counter } : {})), (user.polls_counter ? { polls_counter: user.polls_counter } : {})), (user.connections_counter ? { connections_counter: user.connections_counter } : {})), (user.connection_requests_sent_counter ? { connection_requests_sent_counter: user.connection_requests_sent_counter } : {})), (user.connection_requests_received_counter ? { connection_requests_received_counter: user.connection_requests_received_counter } : {})), }); }) .catch((error) => { utils_1.Logger.error(Errors_1.SCOPE_SC_CORE, `Unable to refresh counters. Error: ${error.response.toString()}`); }); } return Promise.reject(); }, [state.user]); /** * Call the logout endpoint and then remove the user * from the state. */ function logout() { helpers.logoutSession(); } /** * Make the provider update only when it should. * We only want to force re-renders if the user, session, * loading or error states change. * * Whenever the `value` passed into a provider changes, * the whole tree under the provider re-renders, and * that can be very costly! Even in this case, where * you only get re-renders when logging in and out * we want to keep things very performant. */ const contextValue = (0, react_1.useMemo)(() => ({ user: state.user, session: state.session, loading: state.loading, error: state.error, updateUser, setUnseenInteractionsCounter, setUnseenNotificationBannersCounter, refreshCounters, logout, refreshSession, managers: { settings: settingsManager, categories: categoriesManager, followed: followedManager, followers: followersManager, connections: connectionsManager, incubators: subscribedIncubatorsManager, blockedUsers: blockedUsersManager, groups: subscribedGroupsManager, events: subscribedEventsManager, }, }), [ state, settingsManager.all, settingsManager.isLoading, categoriesManager.loading, categoriesManager.categories, followedManager.loading, followedManager.followed, followersManager.loading, followersManager.followers, connectionsManager.loading, connectionsManager.connections, blockedUsersManager.blocked, subscribedIncubatorsManager.loading, subscribedIncubatorsManager.incubators, subscribedGroupsManager.loading, subscribedGroupsManager.groups, subscribedEventsManager.loading, subscribedEventsManager.events, ]); /** * We only want to render the underlying app after we * assert for the presence of a current user. */ return (0, jsx_runtime_1.jsx)(exports.SCUserContext.Provider, Object.assign({ value: contextValue }, { children: children })); } exports.default = SCUserProvider; /** * Let's only export the `useSCUser` hook instead of the context. * We only want to use the hook directly and never the context component. */ function useSCUser() { return (0, react_1.useContext)(exports.SCUserContext); } exports.useSCUser = useSCUser;