diff --git a/src/app.jsx b/src/app.jsx index 4c81a27f2..4eb2f1963 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -4,6 +4,7 @@ import cn from 'classnames' import { provider } from '@k-ramel/react' import withTheme from 'styles/themes/withTheme' +import { AuthProvider } from './features/auth' import NotFound from './screens/components/notFound' import Conference from './screens/conference' import Organizer from './screens/organizer' @@ -15,13 +16,15 @@ import store from './store' import './styles' const App = ({ className }) => ( -
- - - - - -
+ +
+ + + + + +
+
) App.propTypes = { diff --git a/src/features/auth/context.js b/src/features/auth/context.js new file mode 100644 index 000000000..b36f9230e --- /dev/null +++ b/src/features/auth/context.js @@ -0,0 +1,110 @@ +/* eslint-disable react/jsx-filename-extension */ +import React, { useState, useEffect, useContext, useCallback, useMemo } from 'react' +import { inject } from '@k-ramel/react' +import PropTypes from 'prop-types' +import firebase from 'firebase/app' +import pick from 'lodash/pick' + +import userCrud from 'firebase/user' +import { preloadFunctions } from 'firebase/functionCalls' + +const AuthContext = React.createContext() + +export const useAuth = () => useContext(AuthContext) + +export const AuthContextProvider = ({ children, resetStore, goToHome }) => { + const [user, setUser] = useState() + const [loading, setLoading] = useState(true) + + useEffect(() => { + firebase.auth().onAuthStateChanged(async (authUser) => { + if (authUser) { + // check if user exists in database + const userRef = await userCrud.read(authUser.uid) + if (userRef.exists) { + // get user info from db + setUser(userRef.data()) + } else { + // first connexion, add user in database + const userData = pick(authUser, ['uid', 'displayName', 'photoURL', 'email']) + await userCrud.create(userData) + setUser(userData) + } + // preload cloud functions + preloadFunctions() + } else { + setUser(null) + resetStore() + } + setLoading(false) + }) + }, [resetStore]) + + const signin = useCallback(async (providerName) => { + setLoading(true) + + let provider + switch (providerName) { + case 'google': + provider = new firebase.auth.GoogleAuthProvider() + break + case 'twitter': + provider = new firebase.auth.TwitterAuthProvider() + break + case 'github': + provider = new firebase.auth.GithubAuthProvider() + break + case 'facebook': + provider = new firebase.auth.FacebookAuthProvider() + break + default: + return + } + provider.setCustomParameters({ + prompt: 'select_account', + }) + + firebase.auth().signInWithRedirect(provider) + }, []) + + const signout = useCallback(async () => { + setLoading(true) + firebase.auth().signOut() + goToHome() + localStorage.removeItem('currentEventId') + }, [goToHome]) + + const updateUser = useCallback( + async (data) => { + const updatedUser = { ...user, ...data } + setUser(updatedUser) + return userCrud.update(updatedUser) + }, + [user], + ) + + const resetUserFromProvider = useCallback(async () => { + const data = pick(firebase.auth().currentUser, ['uid', 'email', 'displayName', 'photoURL']) + return updateUser(data) + }, [updateUser]) + + const value = useMemo( + () => ({ user, loading, signin, signout, updateUser, resetUserFromProvider }), + [user, loading, signin, signout, updateUser, resetUserFromProvider], + ) + + return {children} +} + +AuthContextProvider.propTypes = { + resetStore: PropTypes.func.isRequired, + goToHome: PropTypes.func.isRequired, + children: PropTypes.any.isRequired, +} + +export const AuthProvider = inject((store, props, { router }) => { + return { + resetStore: () => store.data.reset(), + goToHome: () => router.push('home'), + } +})(AuthContextProvider) diff --git a/src/features/auth/index.js b/src/features/auth/index.js new file mode 100644 index 000000000..6455a6d5f --- /dev/null +++ b/src/features/auth/index.js @@ -0,0 +1,2 @@ +export { AuthProvider, useAuth } from './context' +export { default as protect } from './protect' diff --git a/src/features/auth/protect.js b/src/features/auth/protect.js new file mode 100644 index 000000000..43a9c5070 --- /dev/null +++ b/src/features/auth/protect.js @@ -0,0 +1,41 @@ +/* eslint-disable react/jsx-filename-extension */ +import React, { useEffect } from 'react' +import PropTypes from 'prop-types' +import { inject } from '@k-ramel/react' + +import { useAuth } from 'features/auth' +import { LoadingIndicator } from 'components/loader' + +export default (Component) => { + const ProtectedComponent = ({ redirectLogin, ...rest }) => { + const { user, loading } = useAuth() + + useEffect(() => { + if (!user && !loading) { + redirectLogin() + } + }, [user, loading, redirectLogin]) + + if (loading) { + return + } + + if (!user) { + return null + } + + return + } + + ProtectedComponent.propTypes = { + redirectLogin: PropTypes.func.isRequired, + } + + return inject((store) => { + return { + redirectLogin: () => { + store.dispatch({ type: '@@router/REPLACE_WITH_NEXT_URL', payload: 'login' }) + }, + } + })(ProtectedComponent) +} diff --git a/src/features/beta/index.js b/src/features/beta/index.js new file mode 100644 index 000000000..579a98e4b --- /dev/null +++ b/src/features/beta/index.js @@ -0,0 +1,2 @@ +/* eslint-disable import/prefer-default-export */ +export { default as restrictBeta } from './restrictBeta' diff --git a/src/features/beta/restrictBeta.js b/src/features/beta/restrictBeta.js new file mode 100644 index 000000000..daf91ffcb --- /dev/null +++ b/src/features/beta/restrictBeta.js @@ -0,0 +1,33 @@ +/* eslint-disable react/jsx-filename-extension */ +import React, { useEffect } from 'react' +import PropTypes from 'prop-types' +import { inject } from '@k-ramel/react' +import { useAuth } from 'features/auth' + +const SKIP_BETA_ACCESS = process.env.NODE_ENV === 'development' + +export default (Component) => { + const BetaRestricted = ({ redirectBetaAccess, ...rest }) => { + const { user } = useAuth() + const { betaAccess } = user + + useEffect(() => { + if (SKIP_BETA_ACCESS) return + if (!betaAccess) redirectBetaAccess() + }, [betaAccess, redirectBetaAccess]) + + return SKIP_BETA_ACCESS || betaAccess ? : null + } + + BetaRestricted.propTypes = { + redirectBetaAccess: PropTypes.func.isRequired, + } + + return inject((store) => { + return { + redirectBetaAccess: () => { + store.dispatch({ type: '@@router/REPLACE_WITH_NEXT_URL', payload: 'beta-access' }) + }, + } + })(BetaRestricted) +} diff --git a/src/firebase/betaAccess.js b/src/firebase/betaAccess.js index 08b698fe6..27906fd0d 100644 --- a/src/firebase/betaAccess.js +++ b/src/firebase/betaAccess.js @@ -1,3 +1,9 @@ +/* eslint-disable import/prefer-default-export */ import crud from './crud' -export default crud('betaAccess', 'id') +const betaAccess = crud('betaAccess', 'id') + +export const isValidBetaAccessKey = async (accessKey) => { + const accessRef = await betaAccess.read(accessKey) + return accessRef.exists +} diff --git a/src/layout/avatarDropdown/avatarDropdown.container.js b/src/layout/avatarDropdown/avatarDropdown.container.js index b817b33be..357662059 100644 --- a/src/layout/avatarDropdown/avatarDropdown.container.js +++ b/src/layout/avatarDropdown/avatarDropdown.container.js @@ -3,9 +3,6 @@ import { inject } from '@k-ramel/react' import AvatarDropdown from './avatarDropdown' const mapStore = (store, props, { router }) => { - const { uid } = store.auth.get() - const { displayName, photoURL } = store.data.users.get(uid) || {} - let contributorsRoute = 'public-contributors' if (router.getParam('root') === 'speaker') { contributorsRoute = 'speaker-contributors' @@ -13,12 +10,7 @@ const mapStore = (store, props, { router }) => { contributorsRoute = 'organizer-contributors' } - return { - displayName, - photoURL, - contributorsRoute, - signout: () => store.dispatch('@@ui/SIGN_OUT'), - } + return { contributorsRoute } } export default inject(mapStore)(AvatarDropdown) diff --git a/src/layout/avatarDropdown/avatarDropdown.jsx b/src/layout/avatarDropdown/avatarDropdown.jsx index a03b84dd5..876c9989b 100644 --- a/src/layout/avatarDropdown/avatarDropdown.jsx +++ b/src/layout/avatarDropdown/avatarDropdown.jsx @@ -5,11 +5,18 @@ import { Link } from '@k-redux-router/react-k-ramel' import IconLabel from 'components/iconLabel' import Avatar from 'components/avatar' import Dropdown from 'components/dropdown' +import { useAuth } from 'features/auth' import './avatarDropdown.css' -const AvatarDropdown = ({ displayName, photoURL, contributorsRoute, signout }) => { +const AvatarDropdown = ({ contributorsRoute }) => { + const { user, signout } = useAuth() + + if (!user) return null + + const { displayName, photoURL } = user const avatar = + return (
{displayName}
@@ -27,15 +34,7 @@ const AvatarDropdown = ({ displayName, photoURL, contributorsRoute, signout }) = } AvatarDropdown.propTypes = { - displayName: PropTypes.string, - photoURL: PropTypes.string, contributorsRoute: PropTypes.string.isRequired, - signout: PropTypes.func.isRequired, -} - -AvatarDropdown.defaultProps = { - displayName: undefined, - photoURL: undefined, } export default AvatarDropdown diff --git a/src/screens/components/addUserModal/inviteLink/index.js b/src/screens/components/addUserModal/inviteLink/index.js index dda234f91..4bda2e190 100644 --- a/src/screens/components/addUserModal/inviteLink/index.js +++ b/src/screens/components/addUserModal/inviteLink/index.js @@ -1 +1 @@ -export { default } from './inviteLink.container' +export { default } from './inviteLink' diff --git a/src/screens/components/addUserModal/inviteLink/inviteLink.container.js b/src/screens/components/addUserModal/inviteLink/inviteLink.container.js deleted file mode 100644 index 92182fa52..000000000 --- a/src/screens/components/addUserModal/inviteLink/inviteLink.container.js +++ /dev/null @@ -1,12 +0,0 @@ -import { inject } from '@k-ramel/react' - -import InviteLink from './inviteLink' - -const mapStore = (store) => { - const { uid } = store.auth.get() - return { - uid, - } -} - -export default inject(mapStore)(InviteLink) diff --git a/src/screens/components/addUserModal/inviteLink/inviteLink.jsx b/src/screens/components/addUserModal/inviteLink/inviteLink.jsx index ba9472904..d866d3a3b 100644 --- a/src/screens/components/addUserModal/inviteLink/inviteLink.jsx +++ b/src/screens/components/addUserModal/inviteLink/inviteLink.jsx @@ -4,16 +4,19 @@ import PropTypes from 'prop-types' import CopyInput from 'components/copyInput' import Button from 'components/button' import IconLabel from 'components/iconLabel/iconLabel' +import { useAuth } from 'features/auth' import useInviteLink from './useInviteLink' import styles from './inviteLink.module.css' -const InviteLink = ({ entity, entityId, entityTitle, uid }) => { +const InviteLink = ({ entity, entityId, entityTitle }) => { + const { user } = useAuth() + const { generate, revoke, loading, inviteLink } = useInviteLink({ entity, entityId, entityTitle, - uid, + uid: user.uid, }) if (!inviteLink || loading) { @@ -38,7 +41,6 @@ InviteLink.propTypes = { entity: PropTypes.string.isRequired, entityId: PropTypes.string.isRequired, entityTitle: PropTypes.string.isRequired, - uid: PropTypes.string.isRequired, } export default InviteLink diff --git a/src/screens/components/hasRole/hasRole.container.js b/src/screens/components/hasRole/hasRole.container.js index 50a236d47..e4dd26553 100644 --- a/src/screens/components/hasRole/hasRole.container.js +++ b/src/screens/components/hasRole/hasRole.container.js @@ -2,14 +2,13 @@ import { inject } from '@k-ramel/react' import HasRole from './hasRole' -const mapStore = (store, { of, forEventId, forOrganizationId }) => { +const mapStore = (store, { forEventId, forOrganizationId }) => { const event = store.data.events.get(forEventId) const organization = store.data.organizations.get()[event?.organization ?? forOrganizationId] - const { uid } = store.auth.get() - const roles = Array.isArray(of) ? of : [of] return { - authorized: roles.includes(organization?.members?.[uid]) || event.owner === uid, + eventOwner: event?.owner, + orgaMembers: organization?.members, } } diff --git a/src/screens/components/hasRole/hasRole.jsx b/src/screens/components/hasRole/hasRole.jsx index a95108890..e1b5c83b2 100644 --- a/src/screens/components/hasRole/hasRole.jsx +++ b/src/screens/components/hasRole/hasRole.jsx @@ -1,20 +1,27 @@ -import { bool, node } from 'prop-types' +import { node } from 'prop-types' +import { useAuth } from 'features/auth' -const HasRole = ({ authorized, children, otherwise }) => { - if (!authorized) return otherwise +// TODO Refactor later with a hook +const HasRole = ({ of, orgaMembers, eventOwner, children, otherwise }) => { + const { user } = useAuth() + const roles = Array.isArray(of) ? of : [of] + + if (!roles.includes(orgaMembers?.[user.uid]) && eventOwner !== user.uid) { + return otherwise + } return children } HasRole.propTypes = { - authorized: bool, children: node.isRequired, otherwise: node, } HasRole.defaultProps = { - authorized: false, otherwise: null, + orgaMembers: null, + eventOwner: null, } export default HasRole diff --git a/src/screens/components/notFound/notFound.jsx b/src/screens/components/notFound/notFound.jsx index a7846e991..cb09b134f 100644 --- a/src/screens/components/notFound/notFound.jsx +++ b/src/screens/components/notFound/notFound.jsx @@ -1,4 +1,5 @@ -import React from 'react' +import React, { memo } from 'react' +import { compose } from 'redux' import { forRoute, Link } from '@k-redux-router/react-k-ramel' @@ -11,4 +12,4 @@ const PageNotFound = () => ( ) -export default forRoute.notFound()(PageNotFound) +export default compose(memo, forRoute.notFound())(PageNotFound) diff --git a/src/screens/components/profile/index.js b/src/screens/components/profile/index.js index 78279711a..dedcfa17b 100644 --- a/src/screens/components/profile/index.js +++ b/src/screens/components/profile/index.js @@ -1 +1 @@ -export { default } from './profile.container' +export { default } from './profile' diff --git a/src/screens/components/profile/profile.container.js b/src/screens/components/profile/profile.container.js deleted file mode 100644 index 8d1086254..000000000 --- a/src/screens/components/profile/profile.container.js +++ /dev/null @@ -1,29 +0,0 @@ -import firebase from 'firebase/app' -import { compose } from 'redux' -import { inject } from '@k-ramel/react' -import { forRoute } from '@k-redux-router/react-k-ramel' -import pick from 'lodash/pick' - -import Profile from './profile' - -const mapStore = (store) => { - const { uid } = store.auth.get() - const user = store.data.users.get(uid) - return { - ...user, - initialValues: user, - submitting: store.ui.loaders.get().isProfileSaving, - setDefault: () => { - const payload = pick(firebase.auth().currentUser, ['uid', 'email', 'displayName', 'photoURL']) - store.dispatch({ type: '@@ui/SAVE_PROFILE', payload }) - }, - onSubmit: (payload) => { - store.dispatch({ type: '@@ui/SAVE_PROFILE', payload }) - }, - } -} - -export default compose( - forRoute(['speaker-profile', 'organizer-profile']), // - inject(mapStore), // -)(Profile) diff --git a/src/screens/components/profile/profile.jsx b/src/screens/components/profile/profile.jsx index 14d296838..7ada8d617 100644 --- a/src/screens/components/profile/profile.jsx +++ b/src/screens/components/profile/profile.jsx @@ -1,5 +1,6 @@ import React from 'react' -import PropTypes from 'prop-types' +import { forRoute } from '@k-redux-router/react-k-ramel' + import { Form } from 'react-final-form' import Field from 'components/form/field' import Button from 'components/button' @@ -9,119 +10,103 @@ import * as validators from 'components/form/validators' import Avatar from 'components/avatar' import './profile.css' +import { useAuth } from 'features/auth' const validateEmail = validators.validate([validators.required, validators.email]) const validatePhoto = validators.validate([validators.required, validators.url]) -const Profile = ({ - displayName, - photoURL, - email, - onSubmit, - initialValues, - submitting, - setDefault, -}) => ( -
-
- -
-

{displayName}

- {email} -
-
- -
-
+const Profile = () => { + const { user, updateUser, resetUserFromProvider } = useAuth() + const { displayName, photoURL, email } = user -
- {({ handleSubmit, pristine, invalid }) => ( - - - - - - - - - - - - + return ( + + {({ handleSubmit, pristine, invalid, submitting }) => ( +
+
+ +
+

{displayName}

+ {email} +
+
+ +
+
+ + + + + + + + + + + + - - Save profile - - + + Save profile + + +
)} -
-) - -Profile.propTypes = { - displayName: PropTypes.string, - email: PropTypes.string, - photoURL: PropTypes.string, - onSubmit: PropTypes.func.isRequired, - setDefault: PropTypes.func.isRequired, - initialValues: PropTypes.object, - submitting: PropTypes.bool, -} - -Profile.defaultProps = { - displayName: undefined, - email: undefined, - photoURL: undefined, - initialValues: {}, - submitting: false, + ) } -export default Profile +export default forRoute(['speaker-profile', 'organizer-profile'])(Profile) diff --git a/src/screens/conference/betaAccess/betaAccess.container.js b/src/screens/conference/betaAccess/betaAccess.container.js index 06f577e9f..0ead0e4ac 100644 --- a/src/screens/conference/betaAccess/betaAccess.container.js +++ b/src/screens/conference/betaAccess/betaAccess.container.js @@ -1,17 +1,11 @@ import { compose } from 'redux' import { forRoute } from '@k-redux-router/react-k-ramel' -import { inject, listen } from '@k-ramel/react' +import { inject } from '@k-ramel/react' -import listeners from './betaAccess.listeners' import BetaAccess from './betaAccess' const mapStore = (store) => ({ - error: store.ui.beta.get().error, - validateAccessKey: (key) => store.dispatch({ type: '@@ui/CHECK_BETA_ACCESS_KEY', payload: key }), + redirectToNextUrl: () => store.dispatch('@@router/REDIRECT_TO_NEXT_URL'), }) -export default compose( - forRoute.absolute('beta-access'), - inject(mapStore), - listen(listeners), -)(BetaAccess) +export default compose(forRoute.absolute('beta-access'), inject(mapStore))(BetaAccess) diff --git a/src/screens/conference/betaAccess/betaAccess.jsx b/src/screens/conference/betaAccess/betaAccess.jsx index a0067e964..4a61e9879 100644 --- a/src/screens/conference/betaAccess/betaAccess.jsx +++ b/src/screens/conference/betaAccess/betaAccess.jsx @@ -1,38 +1,53 @@ -import React from 'react' -import PropTypes from 'prop-types' +import React, { useCallback, useState } from 'react' +import { func } from 'prop-types' +import { isValidBetaAccessKey } from 'firebase/betaAccess' +import { useAuth } from 'features/auth' import InputButton from 'components/form/inputButton' import styles from './betaAccess.module.css' -const BetaAccess = ({ validateAccessKey, error }) => ( -
-

Beta Access key needed

-

The organizer hall is in closed-beta access, you need a key to access it.

-
- -
- - {error &&
{error}
} -
-) +const BetaAccess = ({ redirectToNextUrl }) => { + const { updateUser } = useAuth() + const [error, setError] = useState() -BetaAccess.propTypes = { - validateAccessKey: PropTypes.func.isRequired, - error: PropTypes.string, + const onValidateBetaKey = useCallback( + async (betaAccess) => { + const valid = await isValidBetaAccessKey(betaAccess) + if (valid) { + await updateUser({ betaAccess }) + redirectToNextUrl(betaAccess) + } else { + setError('Sorry, invalid beta access key.') + } + }, + [redirectToNextUrl, updateUser], + ) + + return ( +
+

Beta Access key needed

+

The organizer hall is in closed-beta access, you need a key to access it.

+
+ +
+ + {error &&
{error}
} +
+ ) } -BetaAccess.defaultProps = { - error: undefined, +BetaAccess.propTypes = { + redirectToNextUrl: func.isRequired, } export default BetaAccess diff --git a/src/screens/conference/betaAccess/betaAccess.listeners.js b/src/screens/conference/betaAccess/betaAccess.listeners.js deleted file mode 100644 index 95f448195..000000000 --- a/src/screens/conference/betaAccess/betaAccess.listeners.js +++ /dev/null @@ -1,23 +0,0 @@ -import { when } from 'k-ramel' - -import betaAccess from 'firebase/betaAccess' -import userCrud from 'firebase/user' - -export default [ - when('@@ui/CHECK_BETA_ACCESS_KEY')(async (action, store) => { - const key = action.payload - const accessRef = await betaAccess.read(key) - - if (accessRef.exists) { - const { uid } = store.auth.get() - await userCrud.update({ uid, betaAccess: key }) - store.data.users.update({ uid, betaAccess: key }) - store.ui.beta.reset() - - // redirect to the next url if exists - store.dispatch('@@router/REDIRECT_TO_NEXT_URL') - } else { - store.ui.beta.set({ error: 'Sorry, invalid beta access key.' }) - } - }), -] diff --git a/src/screens/conference/betaAccess/hoc/index.js b/src/screens/conference/betaAccess/hoc/index.js deleted file mode 100644 index 3f938c5ea..000000000 --- a/src/screens/conference/betaAccess/hoc/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './restrict' diff --git a/src/screens/conference/betaAccess/hoc/restrict.js b/src/screens/conference/betaAccess/hoc/restrict.js deleted file mode 100644 index 51d049577..000000000 --- a/src/screens/conference/betaAccess/hoc/restrict.js +++ /dev/null @@ -1,51 +0,0 @@ -/* eslint-disable react/jsx-filename-extension */ -import React from 'react' -import PropTypes from 'prop-types' -import { inject } from '@k-ramel/react' - -export default (Component) => { - class BetaRestricted extends React.Component { - // eslint-disable-next-line react/static-property-placement - static propTypes = { - betaAccess: PropTypes.string, - skipBetaAccess: PropTypes.bool.isRequired, - redirectBetaAccess: PropTypes.func.isRequired, - } - - // eslint-disable-next-line react/static-property-placement - static defaultProps = { - betaAccess: undefined, - } - - componentDidMount() { - this.checkAccess() - } - - componentDidUpdate() { - this.checkAccess() - } - - checkAccess = () => { - const { betaAccess, skipBetaAccess, redirectBetaAccess } = this.props - if (skipBetaAccess) return - if (!betaAccess) redirectBetaAccess() - } - - render() { - const { betaAccess, skipBetaAccess, ...rest } = this.props - return skipBetaAccess || betaAccess ? : null - } - } - - return inject((store) => { - const { uid } = store.auth.get() || {} - const { betaAccess } = store.data.users.get(uid) || {} - - return { - betaAccess, - skipBetaAccess: process.env.NODE_ENV === 'development', - redirectBetaAccess: () => - store.dispatch({ type: '@@router/REPLACE_WITH_NEXT_URL', payload: 'beta-access' }), - } - })(BetaRestricted) -} diff --git a/src/screens/conference/betaAccess/index.js b/src/screens/conference/betaAccess/index.js index c56b0cc17..73e7cfc42 100644 --- a/src/screens/conference/betaAccess/index.js +++ b/src/screens/conference/betaAccess/index.js @@ -1,2 +1 @@ export { default } from './betaAccess.container' -export { default as restrictBeta } from './hoc' diff --git a/src/screens/conference/conference.jsx b/src/screens/conference/conference.jsx index 086602b61..4a5124981 100644 --- a/src/screens/conference/conference.jsx +++ b/src/screens/conference/conference.jsx @@ -1,4 +1,5 @@ -import React from 'react' +import React, { memo } from 'react' +import { compose } from 'redux' import { forRoute } from '@k-redux-router/react-k-ramel' import Home from './home' @@ -15,4 +16,4 @@ const Conference = () => ( ) -export default forRoute('home')(Conference) +export default compose(memo, forRoute('home'))(Conference) diff --git a/src/screens/conference/login/index.js b/src/screens/conference/login/index.js index e0d197679..e6ead40da 100644 --- a/src/screens/conference/login/index.js +++ b/src/screens/conference/login/index.js @@ -1 +1 @@ -export { default } from './login.container' +export { default } from './login' diff --git a/src/screens/conference/login/login.container.js b/src/screens/conference/login/login.container.js deleted file mode 100644 index 5f93d1554..000000000 --- a/src/screens/conference/login/login.container.js +++ /dev/null @@ -1,15 +0,0 @@ -import { compose } from 'redux' -import { inject } from '@k-ramel/react' -import { forRoute } from '@k-redux-router/react-k-ramel' - -import Login from './login' - -const mapStore = (store) => ({ - ...store.auth.get(), - signin: (provider) => store.dispatch({ type: '@@ui/SIGN_IN', payload: provider }), -}) - -export default compose( - forRoute.absolute('login'), // - inject(mapStore), // -)(Login) diff --git a/src/screens/conference/login/login.jsx b/src/screens/conference/login/login.jsx index e97c28481..8c9fe5401 100644 --- a/src/screens/conference/login/login.jsx +++ b/src/screens/conference/login/login.jsx @@ -1,56 +1,66 @@ -import firebase from 'firebase/app' import React, { useState, useEffect } from 'react' import PropTypes from 'prop-types' +import firebase from 'firebase/app' +import { forRoute } from '@k-redux-router/react-k-ramel' +import { inject } from '@k-ramel/react' +import split from 'lodash/split' +import { useAuth } from 'features/auth' import { LoadingIndicator } from 'components/loader' import IconLabel from 'components/iconLabel' import Button from 'components/button' import './login.css' -const Login = ({ authenticated, providers, signin }) => { - const [authenticating, setAuthenticating] = useState(false) - const [errorMessage, setErrorMessage] = useState() +const PROVIDERS = split(process.env.REACT_APP_AUTH_PROVIDERS, ',') || [] + +const Login = ({ redirectNext }) => { + const [redirecting, setRedirecting] = useState(false) + const [error, setError] = useState() + const { user, loading, signin } = useAuth() + + useEffect(() => { + if (user && !redirecting) redirectNext() + }, [user, redirecting, redirectNext]) useEffect(() => { - setAuthenticating(true) + setRedirecting(true) firebase .auth() .getRedirectResult() - .then(() => { - setAuthenticating(false) - }) - .catch((error) => { - setAuthenticating(false) - setErrorMessage(error.message) + .then(() => setRedirecting(false)) + .catch((e) => { + setRedirecting(false) + setError(e.message) }) }, []) - if (authenticating || authenticated) { + if (redirecting || loading) { return } return (

Connexion

- {providers.map((provider) => ( + {PROVIDERS.map((provider) => ( ))} - {errorMessage &&

{errorMessage}

} + {error &&

{error}

}
) } Login.propTypes = { - authenticated: PropTypes.bool, - providers: PropTypes.arrayOf(PropTypes.string), - signin: PropTypes.func.isRequired, + redirectNext: PropTypes.func.isRequired, } -Login.defaultProps = { - providers: [], - authenticated: false, -} +const LoginContainer = inject((store) => { + return { + redirectNext: () => { + store.dispatch('@@router/REDIRECT_TO_NEXT_URL') + }, + } +})(Login) -export default Login +export default forRoute.absolute('login')(LoginContainer) diff --git a/src/screens/invite/invite.jsx b/src/screens/invite/invite.jsx index a6693bd99..aac908b07 100644 --- a/src/screens/invite/invite.jsx +++ b/src/screens/invite/invite.jsx @@ -1,8 +1,8 @@ -import React from 'react' +import React, { memo } from 'react' import { compose } from 'redux' import { forRoute } from '@k-redux-router/react-k-ramel' -import { protect } from 'store/reducers/auth' +import { protect } from 'features/auth' import AppLayout from 'layout' import InvitePage from './invitePage' @@ -13,4 +13,4 @@ const Invite = () => ( ) -export default compose(forRoute.absolute('invite'), protect)(Invite) +export default compose(memo, forRoute.absolute('invite'), protect)(Invite) diff --git a/src/screens/organizer/components/ratingsProgress/ratingsProgress.container.js b/src/screens/organizer/components/ratingsProgress/ratingsProgress.container.js index e03d82491..584fb4aaa 100644 --- a/src/screens/organizer/components/ratingsProgress/ratingsProgress.container.js +++ b/src/screens/organizer/components/ratingsProgress/ratingsProgress.container.js @@ -4,9 +4,8 @@ import { getRatingsProgress } from 'store/reducers/data/ratings.selectors' import RatingsProgress from './ratingsProgress' -const mapStore = (store) => { - const { uid } = store.auth.get() - const progress = getRatingsProgress(uid, store) +const mapStore = (store, { userId }) => { + const progress = getRatingsProgress(userId, store) return progress } diff --git a/src/screens/organizer/event/create/eventCreate.container.js b/src/screens/organizer/event/create/eventCreate.container.js index 69f962558..c455877c3 100644 --- a/src/screens/organizer/event/create/eventCreate.container.js +++ b/src/screens/organizer/event/create/eventCreate.container.js @@ -4,7 +4,7 @@ import { forRoute } from '@k-redux-router/react-k-ramel' import EventForm from '../form' -const mapStore = (store) => ({ +const mapStore = (store, { userId }) => ({ submitting: store.ui.loaders.get().isEventSaving, isCreateForm: true, organizations: store.data.organizations.getAsArray(), @@ -17,8 +17,11 @@ const mapStore = (store) => ({ store.dispatch({ type: '@@ui/ON_CREATE_EVENT', payload: { - ...values, - visibility: values.visibility ? 'private' : 'public', + userId, + data: { + ...values, + visibility: values.visibility ? 'private' : 'public', + }, }, }) }, diff --git a/src/screens/organizer/events/events.container.js b/src/screens/organizer/events/events.container.js index 8bcf22361..6aaa9a50c 100644 --- a/src/screens/organizer/events/events.container.js +++ b/src/screens/organizer/events/events.container.js @@ -5,10 +5,10 @@ import { forRoute } from '@k-redux-router/react-k-ramel' import loader from 'components/loader' import Events from './events' -const mapStore = (store) => ({ +const mapStore = (store, { userId }) => ({ loaded: store.ui.organizer.myEvents.isInitialized(), events: store.ui.organizer.myEvents.getAsArray(), - load: () => store.dispatch('@@ui/ON_LOAD_ORGANIZER_EVENTS'), + load: () => store.dispatch({ type: '@@ui/ON_LOAD_ORGANIZER_EVENTS', payload: { userId } }), onSelect: (eventId) => store.dispatch({ type: '@@ui/ON_ORGANIZER_CHANGE_EVENT', payload: { eventId } }), }) diff --git a/src/screens/organizer/isEventAutorized.js b/src/screens/organizer/isEventAutorized.js index 72caad50e..fb50f0f70 100644 --- a/src/screens/organizer/isEventAutorized.js +++ b/src/screens/organizer/isEventAutorized.js @@ -2,7 +2,9 @@ import React from 'react' import { inject } from '@k-ramel/react' +import loader from 'components/loader' import LoadingIndicator from 'components/loader/loading' +import { useAuth } from 'features/auth' const hasAccessEvent = (uid, event, organization) => { if (!event) return false @@ -11,15 +13,16 @@ const hasAccessEvent = (uid, event, organization) => { } export default (Component) => { - const AuthorizedEventComponent = ({ canAccess, isEventPage, ...rest }) => { + const AuthorizedEventComponent = ({ event, organization, isEventPage, ...rest }) => { + const { user } = useAuth() + const canAccess = hasAccessEvent(user.uid, event, organization) if (isEventPage && !canAccess) return return } - return inject((store, props, { router }) => { + return inject((store, { userId }, { router }) => { const eventId = router.getParam('eventId') const isEventPage = router.getParam('isEventPage') - const { uid } = store.auth.get() const event = store.data.events.get(eventId) let organization = null if (event && event.organization) { @@ -27,8 +30,14 @@ export default (Component) => { } return { + loaded: store.data.organizations.isInitialized(), + load: () => { + store.dispatch('@@ui/ON_LOAD_EVENT') + store.dispatch({ type: '@@ui/ON_LOAD_USER_ORGANIZATIONS', payload: { userId } }) + }, isEventPage, - canAccess: hasAccessEvent(uid, event, organization), + event, + organization, } - })(AuthorizedEventComponent) + })(loader(AuthorizedEventComponent)) } diff --git a/src/screens/organizer/organization/form/organizationCreate.container.js b/src/screens/organizer/organization/form/organizationCreate.container.js index 53828b738..4a1e17288 100644 --- a/src/screens/organizer/organization/form/organizationCreate.container.js +++ b/src/screens/organizer/organization/form/organizationCreate.container.js @@ -4,10 +4,10 @@ import { forRoute } from '@k-redux-router/react-k-ramel' import OrganizationForm from './organizationForm' -const mapStore = (store) => ({ +const mapStore = (store, { userId }) => ({ submitting: store.ui.loaders.get().isOrganizationSaving, - onSubmit: (payload) => { - store.dispatch({ type: '@@ui/ON_CREATE_ORGANIZATION', payload }) + onSubmit: (data) => { + store.dispatch({ type: '@@ui/ON_CREATE_ORGANIZATION', payload: { userId, data } }) }, }) diff --git a/src/screens/organizer/organization/list/organizations.container.js b/src/screens/organizer/organization/list/organizations.container.js index d66aad158..4460e61de 100644 --- a/src/screens/organizer/organization/list/organizations.container.js +++ b/src/screens/organizer/organization/list/organizations.container.js @@ -5,10 +5,10 @@ import { forRoute } from '@k-redux-router/react-k-ramel' import loader from 'components/loader' import Organizations from './organizations' -const mapStore = (store, ownProps, { router }) => ({ +const mapStore = (store, { userId }, { router }) => ({ loaded: store.data.organizations.isInitialized(), organizations: store.data.organizations.getAsArray(), - load: () => store.dispatch('@@ui/ON_LOAD_USER_ORGANIZATIONS'), + load: () => store.dispatch({ type: '@@ui/ON_LOAD_USER_ORGANIZATIONS', payload: { userId } }), onSelect: (organizationId) => router.push('organizer-organization-page', { organizationId }), }) diff --git a/src/screens/organizer/organization/page/changeRole/changeRole.container.js b/src/screens/organizer/organization/page/changeRole/changeRole.container.js index ee35bfeba..41e442baf 100644 --- a/src/screens/organizer/organization/page/changeRole/changeRole.container.js +++ b/src/screens/organizer/organization/page/changeRole/changeRole.container.js @@ -4,10 +4,7 @@ import { inject } from '@k-ramel/react' import ChangeRole from './changeRole' const mapStore = (store, { user, organizationId }) => { - const { uid: userId } = store.auth.get() - return { - isAuthenticatedUser: userId === user.uid, changeMemberRole: (role) => store.dispatch({ type: '@@ui/CHANGE_ORGANIZATION_MEMBER_ROLE', diff --git a/src/screens/organizer/organization/page/changeRole/changeRole.jsx b/src/screens/organizer/organization/page/changeRole/changeRole.jsx index 6f03aa33d..90fee2b36 100644 --- a/src/screens/organizer/organization/page/changeRole/changeRole.jsx +++ b/src/screens/organizer/organization/page/changeRole/changeRole.jsx @@ -4,10 +4,14 @@ import capitalize from 'lodash/capitalize' import { ConfirmationPopin } from 'components/portals' import { ROLES } from 'firebase/constants' +import { useAuth } from 'features/auth' import RoleText from './roleText' -const ChangeRoleSelect = ({ user, role, isAuthenticatedUser, changeMemberRole }) => { +const ChangeRoleSelect = ({ user, role, changeMemberRole }) => { + const { user: authUser } = useAuth() + const isAuthenticatedUser = authUser.uid === user.uid + const [selectedRole, setSelectedRole] = useState(role) if (isAuthenticatedUser) return null @@ -41,7 +45,6 @@ const ChangeRoleSelect = ({ user, role, isAuthenticatedUser, changeMemberRole }) ChangeRoleSelect.propTypes = { user: PropTypes.object.isRequired, - isAuthenticatedUser: PropTypes.bool.isRequired, role: PropTypes.string, changeMemberRole: PropTypes.func.isRequired, } diff --git a/src/screens/organizer/organization/page/organizationPage.container.js b/src/screens/organizer/organization/page/organizationPage.container.js index f7acfac83..55f513bc9 100644 --- a/src/screens/organizer/organization/page/organizationPage.container.js +++ b/src/screens/organizer/organization/page/organizationPage.container.js @@ -9,12 +9,10 @@ import OrganizationPage from './organizationPage' const mapStore = (store, _, { router }) => { const organizationId = router.getParam('organizationId') const organization = store.data.organizations.get(organizationId) - const { uid: userId } = store.auth.get() return { loaded: store.data.organizations.hasKey(organizationId), ...organization, - authUserId: userId, load: () => store.dispatch('@@ui/ON_LOAD_ORGANIZATION'), addMember: (uid) => store.dispatch({ type: '@@ui/ADD_ORGANIZATION_MEMBER', payload: { uid, organizationId } }), diff --git a/src/screens/organizer/organization/page/organizationPage.jsx b/src/screens/organizer/organization/page/organizationPage.jsx index ee3402bfa..72c2caae2 100644 --- a/src/screens/organizer/organization/page/organizationPage.jsx +++ b/src/screens/organizer/organization/page/organizationPage.jsx @@ -10,12 +10,14 @@ import Button from 'components/button' import HasRole from 'screens/components/hasRole' import { fetchUsersList } from 'firebase/user' import { ROLES } from 'firebase/constants' +import { useAuth } from 'features/auth' import AddMember from './addMember' import MemberRow from './memberRow' import './organizationPage.css' -const OrganizationPage = ({ id: organizationId, name, members, addMember, authUserId }) => { +const OrganizationPage = ({ id: organizationId, name, members, addMember }) => { + const { user } = useAuth() const [users, setUsers] = useState([]) useEffect(() => { @@ -24,7 +26,7 @@ const OrganizationPage = ({ id: organizationId, name, members, addMember, authUs }) }, [members]) - const isOwner = members[authUserId] === ROLES.OWNER + const isOwner = members[user.uid] === ROLES.OWNER return (
@@ -51,12 +53,12 @@ const OrganizationPage = ({ id: organizationId, name, members, addMember, authUs className="organization-content" array={users} noResult="No users yet !" - renderRow={(user) => ( + renderRow={(member) => ( )} @@ -69,7 +71,6 @@ OrganizationPage.propTypes = { id: PropTypes.string.isRequired, name: PropTypes.string.isRequired, members: PropTypes.objectOf(PropTypes.string), - authUserId: PropTypes.string.isRequired, addMember: PropTypes.func.isRequired, } diff --git a/src/screens/organizer/organizer.jsx b/src/screens/organizer/organizer.jsx index a9bfc7e06..cb75d9977 100644 --- a/src/screens/organizer/organizer.jsx +++ b/src/screens/organizer/organizer.jsx @@ -1,15 +1,15 @@ -import React from 'react' +import React, { memo } from 'react' import { compose } from 'redux' import { forRoute } from '@k-redux-router/react-k-ramel' -import { protect } from 'store/reducers/auth' import AppLayout from 'layout' import Contributors from 'screens/components/contributors' import Profile from 'screens/components/profile' +import { useAuth, protect } from 'features/auth' +import { restrictBeta } from 'features/beta' + import Sidebar from './sidebar' import isEventAuthorized from './isEventAutorized' - -import { restrictBeta } from '../conference/betaAccess' import EventCreate from './event/create' import EventEdit from './event/edit' import Event from './event/page' @@ -21,21 +21,30 @@ import OrganizationsList from './organization/list' import Proposals from './proposals' import Proposal from './proposal' -const Organizer = () => ( - }> - - - - - - - - - - - - - -) +const Organizer = () => { + const { user } = useAuth() + return ( + }> + + + + + + + + + + + + + + ) +} -export default compose(forRoute('organizer'), protect, restrictBeta, isEventAuthorized)(Organizer) +export default compose( + memo, + forRoute('organizer'), + protect, + restrictBeta, + isEventAuthorized, +)(Organizer) diff --git a/src/screens/organizer/proposal/actions/reviewersThread/index.js b/src/screens/organizer/proposal/actions/reviewersThread/index.js index 58bae2f40..e9482c9ab 100644 --- a/src/screens/organizer/proposal/actions/reviewersThread/index.js +++ b/src/screens/organizer/proposal/actions/reviewersThread/index.js @@ -1 +1 @@ -export { default } from './reviewersThread.container' +export { default } from './reviewersThread' diff --git a/src/screens/organizer/proposal/actions/reviewersThread/reviewersThread.container.js b/src/screens/organizer/proposal/actions/reviewersThread/reviewersThread.container.js deleted file mode 100644 index 8f335bdf0..000000000 --- a/src/screens/organizer/proposal/actions/reviewersThread/reviewersThread.container.js +++ /dev/null @@ -1,10 +0,0 @@ -import { inject } from '@k-ramel/react' - -import ReviewersThread from './reviewersThread' - -const mapStore = (store) => { - const { uid } = store.auth.get() - return { user: store.data.users.get(uid) } -} - -export default inject(mapStore)(ReviewersThread) diff --git a/src/screens/organizer/proposal/actions/reviewersThread/reviewersThread.jsx b/src/screens/organizer/proposal/actions/reviewersThread/reviewersThread.jsx index d49f0625a..96e753ab3 100644 --- a/src/screens/organizer/proposal/actions/reviewersThread/reviewersThread.jsx +++ b/src/screens/organizer/proposal/actions/reviewersThread/reviewersThread.jsx @@ -2,6 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import isEmpty from 'lodash/isEmpty' +import { useAuth } from 'features/auth' import { Drawer } from 'components/portals' import Button from 'components/button' import IconLabel from 'components/iconLabel' @@ -10,7 +11,9 @@ import Thread from 'components/thread' import useReviewerThreads from './useReviewerThreads' import styles from './reviewersThread.module.css' -const ReviewersThread = ({ eventId, proposalId, user }) => { +const ReviewersThread = ({ eventId, proposalId }) => { + const { user } = useAuth() + const { messages, saveMessage, deleteMessage } = useReviewerThreads({ eventId, proposalId, @@ -42,11 +45,6 @@ const ReviewersThread = ({ eventId, proposalId, user }) => { ReviewersThread.propTypes = { eventId: PropTypes.string.isRequired, proposalId: PropTypes.string.isRequired, - user: PropTypes.object, -} - -ReviewersThread.defaultProps = { - user: {}, } export default ReviewersThread diff --git a/src/screens/organizer/proposal/proposal.jsx b/src/screens/organizer/proposal/proposal.jsx index a700acfec..8606a56f1 100644 --- a/src/screens/organizer/proposal/proposal.jsx +++ b/src/screens/organizer/proposal/proposal.jsx @@ -1,6 +1,8 @@ import React from 'react' import PropTypes from 'prop-types' +import { useAuth } from 'features/auth' + import Talk from './talk' import Speakers from './speakers' import Ratings from './ratings' @@ -8,21 +10,23 @@ import Actions from './actions' import './proposal.css' -const Proposal = ({ eventId, proposal, deliberationActive, blindRating }) => ( -
- - - {!blindRating && ( - - )} - -
-) +const Proposal = ({ eventId, proposal, deliberationActive, blindRating }) => { + const { user } = useAuth() + + return ( +
+ + + {!blindRating && } + +
+ ) +} Proposal.propTypes = { eventId: PropTypes.string.isRequired, diff --git a/src/screens/organizer/proposal/ratings/ratings.container.js b/src/screens/organizer/proposal/ratings/ratings.container.js index 0ca3e4af1..a3b96441a 100644 --- a/src/screens/organizer/proposal/ratings/ratings.container.js +++ b/src/screens/organizer/proposal/ratings/ratings.container.js @@ -3,18 +3,17 @@ import { inject } from '@k-ramel/react' import Ratings from './ratings' -const mapStore = (store) => { - const { uid } = store.auth.get() +const mapStore = (store, { userId }) => { const proposals = store.data.proposals.getKeys() const { proposalIndex } = store.ui.organizer.proposal.get() return { isLoaded: store.data.ratings.isInitialized(), - ...store.data.ratings.get(uid), + ...store.data.ratings.get(userId), hasNext: proposalIndex + 1 < proposals.length, hasPrevious: proposalIndex - 1 >= 0, onRating: (rating, feeling) => { - store.dispatch({ type: '@@ui/RATE_PROPOSAL', payload: { rating, feeling } }) + store.dispatch({ type: '@@ui/RATE_PROPOSAL', payload: { userId, rating, feeling } }) }, onNext: () => store.dispatch('@@ui/ON_NEXT_PROPOSAL'), onPrevious: () => store.dispatch('@@ui/ON_PREVIOUS_PROPOSAL'), diff --git a/src/screens/organizer/proposals/proposals.jsx b/src/screens/organizer/proposals/proposals.jsx index 799eac7e2..aa97286cc 100644 --- a/src/screens/organizer/proposals/proposals.jsx +++ b/src/screens/organizer/proposals/proposals.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types' import HasRole from 'screens/components/hasRole' import { ROLE_OWNER_OR_MEMBER } from 'firebase/constants' +import { useAuth } from 'features/auth' import ProposalsHeader from './proposalsHeader' import ProposalsFilters from './proposalsFilters' @@ -10,17 +11,21 @@ import ProposalsToolbar from './proposalsToolbar' import ProposalsList from './proposalsList' import ProposalsPaging from './proposalsPaging' -const Proposals = ({ eventId }) => ( -
- - - - - - - -
-) +const Proposals = ({ eventId }) => { + const { user } = useAuth() + + return ( +
+ + + + + + + +
+ ) +} Proposals.propTypes = { eventId: PropTypes.string.isRequired, diff --git a/src/screens/organizer/proposals/proposals.listeners.js b/src/screens/organizer/proposals/proposals.listeners.js index 6ce75821e..2f1ea00d7 100644 --- a/src/screens/organizer/proposals/proposals.listeners.js +++ b/src/screens/organizer/proposals/proposals.listeners.js @@ -11,5 +11,5 @@ export default [ when('@@ui/ACCEPT_PROPOSALS')(reactions.acceptProposals), when('@@ui/REJECT_PROPOSALS')(reactions.rejectProposals), when('@@ui/ON_ADD_PROPOSAL_TO_SELECTION')(reactions.addProposalToSelection), - when('@@krf/UPDATE>UI_ORGANIZER>PROPOSALS')(reactions.changeFilter), + when('@@ui/CHANGE_PROPOSAL_FILTER')(reactions.changeFilter), ] diff --git a/src/screens/organizer/proposals/proposals.reactions.js b/src/screens/organizer/proposals/proposals.reactions.js index 7a2f92b2d..db5985ffa 100644 --- a/src/screens/organizer/proposals/proposals.reactions.js +++ b/src/screens/organizer/proposals/proposals.reactions.js @@ -1,10 +1,8 @@ -import flow from 'lodash/fp/flow' import isEqual from 'lodash/fp/isEqual' -import omit from 'lodash/fp/omit' -import over from 'lodash/fp/over' import pick from 'lodash/fp/pick' import update from 'lodash/fp/update' import pickBy from 'lodash/fp/pickBy' +import omitBy from 'lodash/omitBy' import toLower from 'lodash/toLower' import deburr from 'lodash/deburr' @@ -42,7 +40,7 @@ export const setProposalFiltersFromRouter = (action, store, { router }) => { /* Send email to a selection of proposals */ export const sendEmails = async (action, store, { router }) => { - const { selection } = action.payload + const { selection, userId } = action.payload if (!selection) return const eventId = router.getParam('eventId') @@ -53,12 +51,12 @@ export const sendEmails = async (action, store, { router }) => { firebase.updateProposal(eventId, proposal) } } - store.dispatch('@@ui/ON_LOAD_EVENT_PROPOSALS') + store.dispatch({ type: '@@ui/ON_LOAD_EVENT_PROPOSALS', payload: { userId } }) } /* reject several proposals */ export const rejectProposals = async (action, store, { router }) => { - const { selection } = action.payload + const { selection, userId } = action.payload if (!selection) return const eventId = router.getParam('eventId') @@ -70,12 +68,12 @@ export const rejectProposals = async (action, store, { router }) => { firebase.updateProposal(eventId, proposal) } } - store.dispatch('@@ui/ON_LOAD_EVENT_PROPOSALS') + store.dispatch({ type: '@@ui/ON_LOAD_EVENT_PROPOSALS', payload: { userId } }) } /* accept several proposals */ export const acceptProposals = async (action, store, { router }) => { - const { selection } = action.payload + const { selection, userId } = action.payload if (!selection) return const eventId = router.getParam('eventId') @@ -87,7 +85,7 @@ export const acceptProposals = async (action, store, { router }) => { firebase.updateProposal(eventId, proposal) } } - store.dispatch('@@ui/ON_LOAD_EVENT_PROPOSALS') + store.dispatch({ type: '@@ui/ON_LOAD_EVENT_PROPOSALS', payload: { userId } }) } /* select a proposal to send email */ @@ -137,10 +135,10 @@ export const loadProposals = async (action, store, { router }) => { store.ui.organizer.proposalsPaging.reset() const eventId = router.getParam('eventId') - const { uid } = store.auth.get() + const { userId } = action.payload const filters = store.ui.organizer.proposals.get() - const proposals = await firebase.fetchEventProposals(eventId, uid, filters) + const proposals = await firebase.fetchEventProposals(eventId, userId, filters) let props = await Promise.all( proposals.map(async (proposal) => { const prop = { ...proposal } @@ -188,24 +186,20 @@ export const selectProposal = async (action, store, { router }) => { /* when filters changes synchronize filters with url and load proposals */ export const changeFilter = async (action, store, { router }) => { - const [removedFilters, addedOrModifiedFilters] = over([ - flow( - pickBy((filter) => !filter), - Object.keys, - ), - pickBy((filter) => filter), - ])(action.payload) + const { userId, key, value } = action.payload - const query = router.getQueryParams() - const updatedQuery = flow(omit(removedFilters), (filters) => ({ - ...filters, - ...addedOrModifiedFilters, - }))(query) + store.ui.organizer.proposals.update({ [key]: value }) + + const oldQuery = router.getQueryParams() + let newQuery = { ...oldQuery, [key]: value } + if (!value) { + newQuery = omitBy(newQuery, key) + } - if (!isEqual(query, updatedQuery)) { + if (!isEqual(oldQuery, newQuery)) { const route = router.getCurrentCode() const pathParams = router.getPathParams() - router.replace(route, pathParams, updatedQuery) - store.dispatch('@@ui/ON_LOAD_EVENT_PROPOSALS') + router.replace(route, pathParams, newQuery) + store.dispatch({ type: '@@ui/ON_LOAD_EVENT_PROPOSALS', payload: { userId } }) } } diff --git a/src/screens/organizer/proposals/proposalsFilters/proposalsFilters.container.js b/src/screens/organizer/proposals/proposalsFilters/proposalsFilters.container.js index 8980a54c2..2bc10a8af 100644 --- a/src/screens/organizer/proposals/proposalsFilters/proposalsFilters.container.js +++ b/src/screens/organizer/proposals/proposalsFilters/proposalsFilters.container.js @@ -7,7 +7,7 @@ const filterSortOrders = (sortOrders, hideRatings) => { return sortOrders.filter((order) => !(/Rating/gm.test(order) && hideRatings)) } -const mapStore = (store, props, { router }) => { +const mapStore = (store, { userId }, { router }) => { const eventId = router.getParam('eventId') const sortOrders = router.getParam('sortOrders') const ratings = router.getParam('ratings') @@ -26,8 +26,11 @@ const mapStore = (store, props, { router }) => { filters, sortOrders: filterSortOrders(sortOrders, hideRatings), deliberationActive: get(settings, 'deliberation.enabled'), - onChange: ({ target }) => { - store.ui.organizer.proposals.update({ [target.id]: target.value }) + onChange: (event) => { + store.dispatch({ + type: '@@ui/CHANGE_PROPOSAL_FILTER', + payload: { userId, key: event.target.id, value: event.target.value }, + }) }, } } diff --git a/src/screens/organizer/proposals/proposalsHeader/proposalsHeader.jsx b/src/screens/organizer/proposals/proposalsHeader/proposalsHeader.jsx index 4f134d6db..8b82d49e6 100644 --- a/src/screens/organizer/proposals/proposalsHeader/proposalsHeader.jsx +++ b/src/screens/organizer/proposals/proposalsHeader/proposalsHeader.jsx @@ -4,19 +4,23 @@ import classnames from 'classnames' import Titlebar from 'components/titlebar' import RatingsProgress from 'screens/organizer/components/ratingsProgress' +import { useAuth } from 'features/auth' import styles from './proposalsHeader.module.css' -const ProposalsHeader = ({ eventId }) => ( -
- - -
-) +const ProposalsHeader = ({ eventId }) => { + const { user } = useAuth() + return ( +
+ + +
+ ) +} ProposalsHeader.propTypes = { eventId: PropTypes.string.isRequired, diff --git a/src/screens/organizer/proposals/proposalsList/proposalsList.container.js b/src/screens/organizer/proposals/proposalsList/proposalsList.container.js index 43222b3b1..efb4ba093 100644 --- a/src/screens/organizer/proposals/proposalsList/proposalsList.container.js +++ b/src/screens/organizer/proposals/proposalsList/proposalsList.container.js @@ -6,7 +6,7 @@ import get from 'lodash/get' import loader from 'components/loader' import ProposalsList from './proposalsList' -const mapStore = (store, { eventId }) => { +const mapStore = (store, { eventId, userId }) => { const settings = store.data.eventsSettings.get(eventId) const { page, itemsPerPage } = store.ui.organizer.proposalsPaging.get() const { items } = store.ui.organizer.proposalsSelection.get() @@ -22,7 +22,7 @@ const mapStore = (store, { eventId }) => { proposalsSelection: items, deliberationActive: get(settings, 'deliberation.enabled'), blindRating: get(settings, 'deliberation.blindRating'), - load: () => store.dispatch('@@ui/ON_LOAD_EVENT_PROPOSALS'), + load: () => store.dispatch({ type: '@@ui/ON_LOAD_EVENT_PROPOSALS', payload: { userId } }), onSelect: (proposalId) => { store.dispatch({ type: '@@ui/ON_SELECT_PROPOSAL', payload: { eventId, proposalId } }) }, diff --git a/src/screens/organizer/proposals/proposalsToolbar/proposalsToolbar.container.js b/src/screens/organizer/proposals/proposalsToolbar/proposalsToolbar.container.js index 26d536152..f1a59ef67 100644 --- a/src/screens/organizer/proposals/proposalsToolbar/proposalsToolbar.container.js +++ b/src/screens/organizer/proposals/proposalsToolbar/proposalsToolbar.container.js @@ -8,7 +8,7 @@ const countEmailsToSend = (type, selection = [], proposals = []) => { return result.length } -const mapStore = (store, props, { router }) => { +const mapStore = (store, { userId }, { router }) => { const eventId = router.getParam('eventId') const settings = store.data.eventsSettings.get(eventId) const { exporting } = store.ui.organizer.proposalsExport.get() @@ -34,11 +34,11 @@ const mapStore = (store, props, { router }) => { onExportProposals: (output) => () => store.dispatch({ type: '@@ui/EXPORT_PROPOSALS', payload: { output } }), onSendEmails: () => - store.dispatch({ type: '@@ui/SEND_EMAIL_FOR_PROPOSALS', payload: { selection } }), + store.dispatch({ type: '@@ui/SEND_EMAIL_FOR_PROPOSALS', payload: { userId, selection } }), onAcceptProposals: () => - store.dispatch({ type: '@@ui/ACCEPT_PROPOSALS', payload: { selection } }), + store.dispatch({ type: '@@ui/ACCEPT_PROPOSALS', payload: { userId, selection } }), onRejectProposals: () => - store.dispatch({ type: '@@ui/REJECT_PROPOSALS', payload: { selection } }), + store.dispatch({ type: '@@ui/REJECT_PROPOSALS', payload: { userId, selection } }), } } diff --git a/src/screens/organizer/sidebar/index.js b/src/screens/organizer/sidebar/index.js index 8b1d143d1..f47730965 100644 --- a/src/screens/organizer/sidebar/index.js +++ b/src/screens/organizer/sidebar/index.js @@ -1 +1 @@ -export { default } from './sidebar.container' +export { default } from './sidebar' diff --git a/src/screens/organizer/sidebar/sidebar.container.js b/src/screens/organizer/sidebar/sidebar.container.js deleted file mode 100644 index 380f90263..000000000 --- a/src/screens/organizer/sidebar/sidebar.container.js +++ /dev/null @@ -1,13 +0,0 @@ -import { inject } from '@k-ramel/react' - -import Sidebar from './sidebar' - -const mapStore = (store) => { - const { uid } = store.auth.get() - const { displayName } = store.data.users.get(uid) || {} - return { - fullname: displayName, - } -} - -export default inject(mapStore)(Sidebar) diff --git a/src/screens/organizer/sidebar/sidebar.jsx b/src/screens/organizer/sidebar/sidebar.jsx index 7e64f376a..c9da3d9b5 100644 --- a/src/screens/organizer/sidebar/sidebar.jsx +++ b/src/screens/organizer/sidebar/sidebar.jsx @@ -3,32 +3,34 @@ import PropTypes from 'prop-types' import { SideBar, SideBarPanel, SideBarLink } from 'layout/sidebar' import IconLabel from 'components/iconLabel' +import { useAuth } from 'features/auth' import EventSideBar from './event' -const OrganizerSideBar = ({ fullname, className }) => ( - - - - - - - - - - - - - - -) +const OrganizerSideBar = ({ className }) => { + const { user } = useAuth() + return ( + + + + + + + + + + + + + + + ) +} OrganizerSideBar.propTypes = { - fullname: PropTypes.string, className: PropTypes.string, } OrganizerSideBar.defaultProps = { - fullname: undefined, className: undefined, } diff --git a/src/screens/speaker/event/submissions/submissions.container.js b/src/screens/speaker/event/submissions/submissions.container.js index 9668e604e..ddaa3ffeb 100644 --- a/src/screens/speaker/event/submissions/submissions.container.js +++ b/src/screens/speaker/event/submissions/submissions.container.js @@ -6,7 +6,7 @@ import loader from 'components/loader' import Submissions from './submissions' -const mapStore = (store, props, { router }) => { +const mapStore = (store, { userId }, { router }) => { const eventId = router.getParam('eventId') const talks = store.ui.speaker.myTalks .getAsArray() @@ -22,7 +22,7 @@ const mapStore = (store, props, { router }) => { eventName, talks, loaded, - load: () => store.dispatch('@@ui/ON_LOAD_SPEAKER_TALKS'), + load: () => store.dispatch({ type: '@@ui/ON_LOAD_SPEAKER_TALKS', payload: { userId } }), onSelect: (talkId) => router.push('speaker-event-submission-page', { eventId, talkId }), } } diff --git a/src/screens/speaker/event/submitWizard/submitWizard.jsx b/src/screens/speaker/event/submitWizard/submitWizard.jsx index 6c9c0cc83..e80aabd1c 100644 --- a/src/screens/speaker/event/submitWizard/submitWizard.jsx +++ b/src/screens/speaker/event/submitWizard/submitWizard.jsx @@ -17,13 +17,13 @@ const steps = [ { label: 'Done !', icon: 'fa fa-paper-plane' }, ] -const SubmitWizard = ({ eventId, cfpOpened, eventName, currentStep }) => { +const SubmitWizard = ({ eventId, userId, cfpOpened, eventName, currentStep }) => { if (!eventId || !cfpOpened) return null return (
- {currentStep === 0 && } + {currentStep === 0 && } {currentStep === 1 && } {currentStep === 2 && } {currentStep === 3 && } @@ -32,6 +32,7 @@ const SubmitWizard = ({ eventId, cfpOpened, eventName, currentStep }) => { } SubmitWizard.propTypes = { + userId: PropTypes.string, eventId: PropTypes.string, cfpOpened: PropTypes.bool, eventName: PropTypes.string, @@ -39,6 +40,7 @@ SubmitWizard.propTypes = { } SubmitWizard.defaultProps = { + userId: undefined, eventId: undefined, cfpOpened: false, eventName: undefined, diff --git a/src/screens/speaker/event/submitWizard/talksSelection/talksSelection.container.js b/src/screens/speaker/event/submitWizard/talksSelection/talksSelection.container.js index 5b4ad698a..99024e380 100644 --- a/src/screens/speaker/event/submitWizard/talksSelection/talksSelection.container.js +++ b/src/screens/speaker/event/submitWizard/talksSelection/talksSelection.container.js @@ -3,13 +3,13 @@ import { inject } from '@k-ramel/react' import loader from 'components/loader' import TalksSelection from './talksSelection' -const mapStore = (store, { eventId }) => { +const mapStore = (store, { userId, eventId }) => { const talks = store.ui.speaker.myTalks.getAsArray().filter((talk) => !talk.archived) return { loaded: store.ui.speaker.myTalks.isInitialized(), talks, - load: () => store.dispatch('@@ui/ON_LOAD_SPEAKER_TALKS'), + load: () => store.dispatch({ type: '@@ui/ON_LOAD_SPEAKER_TALKS', payload: { userId } }), onSelect: (talkId) => { store.dispatch({ type: '@@ui/GO_TO_EVENT_SUBMISSION', payload: { eventId, talkId } }) }, diff --git a/src/screens/speaker/event/survey/survey.container.js b/src/screens/speaker/event/survey/survey.container.js index 90b6a31fa..c197cc3a1 100644 --- a/src/screens/speaker/event/survey/survey.container.js +++ b/src/screens/speaker/event/survey/survey.container.js @@ -6,13 +6,11 @@ import loader from 'components/loader' import SpeakerSurvey from './survey' const mapState = (store, props, { router }) => { - const { uid } = store.auth.get() const eventId = router.getParam('eventId') const { name } = store.data.events.get(eventId) || {} return { - loaded: store.data.events.hasKey(eventId), - uid, name, + loaded: store.data.events.hasKey(eventId), load: () => store.dispatch({ type: '@@ui/ON_LOAD_EVENT', payload: eventId }), } } diff --git a/src/screens/speaker/event/survey/survey.jsx b/src/screens/speaker/event/survey/survey.jsx index 201c62135..e95301628 100644 --- a/src/screens/speaker/event/survey/survey.jsx +++ b/src/screens/speaker/event/survey/survey.jsx @@ -3,25 +3,28 @@ import PropTypes from 'prop-types' import EventTitle from 'screens/components/event/eventTitle' import SurveyForm from 'screens/components/event/survey/form' +import { useAuth } from 'features/auth' import './survey.css' -const SpeakerSurvey = ({ uid, name }) => ( -
- -
-

- Organizers need some information about you in order to make a better event experience for - speakers. Please fill the following survey to help them. -

- +const SpeakerSurvey = ({ name }) => { + const { user } = useAuth() + return ( +
+ +
+

+ Organizers need some information about you in order to make a better event experience for + speakers. Please fill the following survey to help them. +

+ +
-
-) + ) +} SpeakerSurvey.propTypes = { name: PropTypes.string.isRequired, - uid: PropTypes.string.isRequired, } export default SpeakerSurvey diff --git a/src/screens/speaker/sidebar/index.js b/src/screens/speaker/sidebar/index.js index 8b1d143d1..f47730965 100644 --- a/src/screens/speaker/sidebar/index.js +++ b/src/screens/speaker/sidebar/index.js @@ -1 +1 @@ -export { default } from './sidebar.container' +export { default } from './sidebar' diff --git a/src/screens/speaker/sidebar/sidebar.container.js b/src/screens/speaker/sidebar/sidebar.container.js deleted file mode 100644 index 380f90263..000000000 --- a/src/screens/speaker/sidebar/sidebar.container.js +++ /dev/null @@ -1,13 +0,0 @@ -import { inject } from '@k-ramel/react' - -import Sidebar from './sidebar' - -const mapStore = (store) => { - const { uid } = store.auth.get() - const { displayName } = store.data.users.get(uid) || {} - return { - fullname: displayName, - } -} - -export default inject(mapStore)(Sidebar) diff --git a/src/screens/speaker/sidebar/sidebar.jsx b/src/screens/speaker/sidebar/sidebar.jsx index dad3cc8f7..c75949b2e 100644 --- a/src/screens/speaker/sidebar/sidebar.jsx +++ b/src/screens/speaker/sidebar/sidebar.jsx @@ -1,31 +1,33 @@ import React from 'react' import PropTypes from 'prop-types' +import { useAuth } from 'features/auth' import { SideBar, SideBarPanel, SideBarLink } from 'layout/sidebar' import IconLabel from 'components/iconLabel' import EventSidebar from './event' -const SpeakerSideBar = ({ fullname, className }) => ( - - - - - - - - - - - -) +const SpeakerSideBar = ({ className }) => { + const { user } = useAuth() + return ( + + + + + + + + + + + + ) +} SpeakerSideBar.propTypes = { - fullname: PropTypes.string, className: PropTypes.string, } SpeakerSideBar.defaultProps = { - fullname: undefined, className: undefined, } diff --git a/src/screens/speaker/speaker.jsx b/src/screens/speaker/speaker.jsx index 130d64c0c..73e44ed62 100644 --- a/src/screens/speaker/speaker.jsx +++ b/src/screens/speaker/speaker.jsx @@ -1,11 +1,12 @@ -import React from 'react' +import React, { memo } from 'react' import { compose } from 'redux' import { forRoute } from '@k-redux-router/react-k-ramel' -import { protect } from 'store/reducers/auth' import AppLayout from 'layout' import Contributors from 'screens/components/contributors' import Profile from 'screens/components/profile' +import { useAuth, protect } from 'features/auth' + import Sidebar from './sidebar' import MyTalks from './talks' import { TalkEdit, TalkCreate } from './talk/form' @@ -17,21 +18,24 @@ import EventSubmissions from './event/submissions' import EventSubmissionPage from './event/submission' import EventSurvey from './event/survey' -const Speaker = () => ( - }> - - - - - - - - - - - - - -) +const Speaker = () => { + const { user } = useAuth() + return ( + }> + + + + + + + + + + + + + + ) +} -export default compose(forRoute('speaker'), protect)(Speaker) +export default compose(memo, forRoute('speaker'), protect)(Speaker) diff --git a/src/screens/speaker/talk/form/talkCreate.container.js b/src/screens/speaker/talk/form/talkCreate.container.js index 94ba82388..afc073037 100644 --- a/src/screens/speaker/talk/form/talkCreate.container.js +++ b/src/screens/speaker/talk/form/talkCreate.container.js @@ -4,10 +4,10 @@ import { forRoute } from '@k-redux-router/react-k-ramel' import TalkForm from './talkForm' -const mapStore = (store) => ({ +const mapStore = (store, { userId }) => ({ submitting: store.ui.loaders.get().isTalkSaving, - onSubmit: (payload) => { - store.dispatch({ type: '@@ui/ON_CREATE_TALK', payload }) + onSubmit: (data) => { + store.dispatch({ type: '@@ui/ON_CREATE_TALK', payload: { userId, data } }) }, }) diff --git a/src/screens/speaker/talks/myTalks.container.js b/src/screens/speaker/talks/myTalks.container.js index d22ba9ebb..2cd6ab10f 100644 --- a/src/screens/speaker/talks/myTalks.container.js +++ b/src/screens/speaker/talks/myTalks.container.js @@ -5,10 +5,10 @@ import { forRoute } from '@k-redux-router/react-k-ramel' import loader from 'components/loader' import MyTalks from './myTalks' -const mapStore = (store, props, { router }) => ({ +const mapStore = (store, { userId }, { router }) => ({ loaded: store.ui.speaker.myTalks.isInitialized(), talks: store.ui.speaker.myTalks.getAsArray(), - load: () => store.dispatch('@@ui/ON_LOAD_SPEAKER_TALKS'), + load: () => store.dispatch({ type: '@@ui/ON_LOAD_SPEAKER_TALKS', payload: { userId } }), onSelect: (talkId) => router.push('speaker-talk-page', { talkId }), }) diff --git a/src/store/listeners.js b/src/store/listeners.js index 3a52cbee3..e7328ac1f 100644 --- a/src/store/listeners.js +++ b/src/store/listeners.js @@ -2,8 +2,6 @@ import { when } from 'k-ramel' import * as app from './reactions/app' import * as router from './reactions/router' -import * as auth from './reactions/auth' -import * as firebase from './reactions/firebase' import * as user from './reactions/user' import * as talks from './reactions/talks' import * as events from './reactions/events' @@ -15,21 +13,13 @@ import * as survey from './reactions/survey' export default [ /* app loaded */ - when('@@krml/INIT')(firebase.init), when('@@krml/INIT')(app.init), /* router */ when('@@router/ROUTE_FOUND')(router.onRouteChanged), when('@@router/REPLACE_WITH_NEXT_URL')(router.replaceWithNextUrl), when('@@router/REDIRECT_TO_NEXT_URL')(router.redirectToNextUrl), - /* firebase actions */ - when('@@firebase/SIGNED_IN')(auth.signedIn), - when('@@firebase/SIGNED_OUT')(auth.signedOut), - /* authentication */ - when('@@ui/SIGN_IN')(auth.signin), - when('@@ui/SIGN_OUT')(auth.signout), /* user */ when('@@ui/FETCH_USER')(user.fetchUser), - when('@@ui/SAVE_PROFILE')(user.saveProfile), /* talks */ when('@@ui/ON_CREATE_TALK')(talks.createTalk), when('@@ui/ON_UPDATE_TALK')(talks.updateTalk), diff --git a/src/store/reactions/auth.js b/src/store/reactions/auth.js deleted file mode 100644 index 93f151b13..000000000 --- a/src/store/reactions/auth.js +++ /dev/null @@ -1,68 +0,0 @@ -import firebase from 'firebase/app' -import pick from 'lodash/pick' - -import userCrud from 'firebase/user' -import { fetchUserOrganizations } from 'firebase/organizations' - -export const signin = (action) => { - const providerId = action.payload - let provider - switch (providerId) { - case 'google': - provider = new firebase.auth.GoogleAuthProvider() - break - case 'twitter': - provider = new firebase.auth.TwitterAuthProvider() - break - case 'github': - provider = new firebase.auth.GithubAuthProvider() - break - case 'facebook': - provider = new firebase.auth.FacebookAuthProvider() - break - default: - return - } - provider.setCustomParameters({ - prompt: 'select_account', - }) - - firebase.auth().signInWithRedirect(provider) -} - -export const signout = (action, store, { router }) => { - firebase.auth().signOut() - router.push('home') - localStorage.removeItem('currentEventId') -} - -export const signedIn = async (action, store) => { - let user = pick(action.payload, ['uid', 'displayName', 'photoURL', 'email']) - - // set auth authenticated - store.auth.update({ authenticated: true, uid: user.uid }) - - // check if user exists in database - const userRef = await userCrud.read(user.uid) - if (userRef.exists) { - // get user info from db - user = userRef.data() - } else { - // first connexion, add user in database - await userCrud.create(user) - } - // add user in store - store.data.users.add(user) - - // get users organizations - const organizations = await fetchUserOrganizations(user.uid) - store.data.organizations.set(organizations) - - // go to the redirect url if exists - store.dispatch('@@router/REDIRECT_TO_NEXT_URL') -} - -export const signedOut = (action, store) => { - store.auth.update({ authenticated: false, uid: undefined }) - store.data.users.reset() -} diff --git a/src/store/reactions/events.js b/src/store/reactions/events.js index 6b57df181..50e1e4404 100644 --- a/src/store/reactions/events.js +++ b/src/store/reactions/events.js @@ -12,9 +12,8 @@ import eventCrud, { } from 'firebase/events' export const createEvent = async (action, store, { router }) => { - const { uid } = store.auth.get() - const eventData = action.payload - const event = { ...eventData, owner: uid } + const { userId, data } = action.payload + const event = { ...data, owner: userId } store.ui.loaders.update({ isEventSaving: true }) const ref = await eventCrud.create(event) @@ -81,10 +80,10 @@ export const fetchEvent = async (action, store, { router }) => { } export const fetchOrganizerEvents = async (action, store, { router }) => { - const { uid } = store.auth.get() + const { userId } = action.payload const organizations = store.data.organizations.getKeys() - const result = await fetchUserEvents(uid) + const result = await fetchUserEvents(userId) const events = result.docs.map((ref) => ({ id: ref.id, ...ref.data() })) const organizationsEvents = await Promise.all( map(organizations, async (organizationId) => { diff --git a/src/store/reactions/firebase.js b/src/store/reactions/firebase.js deleted file mode 100644 index 445438247..000000000 --- a/src/store/reactions/firebase.js +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable no-console, import/prefer-default-export */ -import firebase from 'firebase/app' -import 'firebase/auth' -import 'firebase/firestore' -import 'firebase/functions' -import 'firebase/storage' - -import { preloadFunctions } from 'firebase/functionCalls' - -export const init = (action, store) => { - firebase.auth().onAuthStateChanged((user) => { - if (user) { - store.dispatch({ type: '@@firebase/SIGNED_IN', payload: user }) - preloadFunctions() - } else { - store.dispatch('@@firebase/SIGNED_OUT') - } - }) -} diff --git a/src/store/reactions/organizations.js b/src/store/reactions/organizations.js index 339fae3af..49303e877 100644 --- a/src/store/reactions/organizations.js +++ b/src/store/reactions/organizations.js @@ -4,9 +4,11 @@ import organizationCrud, { fetchUserOrganizations } from 'firebase/organizations import { ROLES } from 'firebase/constants' export const create = async (action, store, { router }) => { - const data = action.payload - const { uid } = store.auth.get() - const newUserOrganization = flow(set(`members.${uid}`, ROLES.OWNER), set(ROLES.OWNER, uid))(data) + const { userId, data } = action.payload + const newUserOrganization = flow( + set(`members.${userId}`, ROLES.OWNER), + set(ROLES.OWNER, userId), + )(data) store.ui.loaders.update({ isOrganizationSaving: true }) const ref = await organizationCrud.create(newUserOrganization) @@ -38,8 +40,8 @@ export const get = async (action, store, { router }) => { } export const ofUser = async (action, store) => { - const { uid } = store.auth.get() - const organizations = await fetchUserOrganizations(uid) + const { userId } = action.payload + const organizations = await fetchUserOrganizations(userId) store.data.organizations.set(organizations) } diff --git a/src/store/reactions/ratings.js b/src/store/reactions/ratings.js index 94d307f1e..8d9ea3483 100644 --- a/src/store/reactions/ratings.js +++ b/src/store/reactions/ratings.js @@ -13,20 +13,19 @@ export const fetchRatings = async (action, store) => { } export const rateProposal = async (action, store, { router }) => { - const rating = action.payload + const { rating, feeling, userId } = action.payload // select needed inputs in the state - const { uid } = store.auth.get() const eventId = router.getParam('eventId') const proposalId = router.getParam('proposalId') // add or remove the rating in database and store - const rated = !!rating.rating || !!rating.feeling + const rated = !!rating || !!feeling if (rated) { - await addRating(eventId, proposalId, uid, rating) - store.data.ratings.addOrUpdate({ uid, ...rating }) + await addRating(eventId, proposalId, userId, { rating, feeling }) + store.data.ratings.addOrUpdate({ uid: userId, rating, feeling }) } else { - await deleteRating(eventId, proposalId, uid) - store.data.ratings.remove([uid]) + await deleteRating(eventId, proposalId, userId) + store.data.ratings.remove([userId]) } // retrieve all ratings before updating proposal computed @@ -43,6 +42,6 @@ export const rateProposal = async (action, store, { router }) => { noopinion, } // save the rating average in database and store - updateRating(eventId, proposalId, uid, ratingUpdated, rated) + updateRating(eventId, proposalId, userId, ratingUpdated, rated) store.data.proposals.update({ id: proposalId, ...ratingUpdated }) } diff --git a/src/store/reactions/talks.js b/src/store/reactions/talks.js index c08baab62..02523fb80 100644 --- a/src/store/reactions/talks.js +++ b/src/store/reactions/talks.js @@ -3,15 +3,14 @@ import { set, unset } from 'immutadot' import talkCrud, { fetchUserTalks } from 'firebase/talks' export const createTalk = async (action, store, { router }) => { - const talk = action.payload - const { uid } = store.auth.get() + const { userId, data } = action.payload store.ui.loaders.update({ isTalkSaving: true }) const ref = await talkCrud.create({ - ...talk, - owner: uid, + ...data, + owner: userId, archived: false, - speakers: { [uid]: true }, + speakers: { [userId]: true }, }) store.ui.loaders.update({ isTalkSaving: false }) @@ -51,8 +50,8 @@ export const fetchTalk = async (action, store, { router }) => { } export const fetchSpeakerTalks = async (action, store) => { - const { uid } = store.auth.get() - const talks = await fetchUserTalks(uid) + const { userId } = action.payload + const talks = await fetchUserTalks(userId) // set talks in the store store.data.talks.set(talks) // set talks id to the speaker talk store diff --git a/src/store/reactions/user.js b/src/store/reactions/user.js index 1966e3bad..d901eef38 100644 --- a/src/store/reactions/user.js +++ b/src/store/reactions/user.js @@ -1,3 +1,4 @@ +/* eslint-disable import/prefer-default-export */ import userCrud from 'firebase/user' export const fetchUser = async (action, store) => { @@ -12,13 +13,3 @@ export const fetchUser = async (action, store) => { store.data.users.add(userRef.data()) } } - -export const saveProfile = async (action, store) => { - const profile = action.payload - - store.ui.loaders.update({ isProfileSaving: true }) - await userCrud.update(profile) - store.ui.loaders.update({ isProfileSaving: false }) - - store.data.users.update(profile) -} diff --git a/src/store/reducers/auth/auth.js b/src/store/reducers/auth/auth.js deleted file mode 100644 index 831463d16..000000000 --- a/src/store/reducers/auth/auth.js +++ /dev/null @@ -1,11 +0,0 @@ -import { types } from 'k-ramel' -import split from 'lodash/split' - -const defaultData = { - providers: split(process.env.REACT_APP_AUTH_PROVIDERS, ','), - authenticated: false, - uid: undefined, - error: {}, -} - -export default types.object({ defaultData }) diff --git a/src/store/reducers/auth/index.js b/src/store/reducers/auth/index.js deleted file mode 100644 index ea38ab1b3..000000000 --- a/src/store/reducers/auth/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from './auth' -export { default as protect } from './protect' diff --git a/src/store/reducers/auth/protect.js b/src/store/reducers/auth/protect.js deleted file mode 100644 index 3261f7692..000000000 --- a/src/store/reducers/auth/protect.js +++ /dev/null @@ -1,52 +0,0 @@ -/* eslint-disable react/jsx-filename-extension */ -import React from 'react' -import PropTypes from 'prop-types' -import { inject } from '@k-ramel/react' - -import LoadingIndicator from 'components/loader/loading' - -export default (Component) => { - class ProtectedComponent extends React.Component { - // eslint-disable-next-line react/static-property-placement - static propTypes = { - authenticated: PropTypes.bool.isRequired, - userDataLoaded: PropTypes.bool.isRequired, - redirectLogin: PropTypes.func.isRequired, - } - - componentDidMount() { - this.checkAuth() - } - - componentDidUpdate() { - this.checkAuth() - } - - checkAuth = () => { - const { authenticated, redirectLogin } = this.props - if (!authenticated) { - redirectLogin() - } - } - - render() { - const { authenticated, userDataLoaded, ...rest } = this.props - if (!authenticated) return null - if (!userDataLoaded) return - return - } - } - - return inject((store) => { - const auth = store.auth.get() - const userLoaded = store.data.users.hasKey(auth.uid) - const orgaLoaded = store.data.organizations.isInitialized() - - return { - ...auth, - userDataLoaded: userLoaded && orgaLoaded, - redirectLogin: () => - store.dispatch({ type: '@@router/REPLACE_WITH_NEXT_URL', payload: 'login' }), - } - })(ProtectedComponent) -} diff --git a/src/store/reducers/reducers.js b/src/store/reducers/reducers.js index c9c83f306..f3f285b80 100644 --- a/src/store/reducers/reducers.js +++ b/src/store/reducers/reducers.js @@ -1,9 +1,7 @@ -import auth from './auth' import data from './data' import ui from './ui' export default { - auth, data, ui, } diff --git a/src/store/reducers/ui/beta.js b/src/store/reducers/ui/beta.js deleted file mode 100644 index 1de3c283b..000000000 --- a/src/store/reducers/ui/beta.js +++ /dev/null @@ -1,5 +0,0 @@ -import { types } from 'k-ramel' - -const defaultData = { error: undefined } - -export default types.object({ defaultData }) diff --git a/src/store/reducers/ui/index.js b/src/store/reducers/ui/index.js index a7664dd43..42e0f6192 100644 --- a/src/store/reducers/ui/index.js +++ b/src/store/reducers/ui/index.js @@ -1,13 +1,11 @@ import app from './app' import speaker from './speaker' import organizer from './organizer' -import beta from './beta' import loaders from './loaders' export default { app, speaker, organizer, - beta, loaders, }