diff --git a/src/client/chat.jsx b/src/client/chat.jsx index 2686875..651e183 100644 --- a/src/client/chat.jsx +++ b/src/client/chat.jsx @@ -1,6 +1,9 @@ import './chat.scss'; -import React, {useRef} from 'react'; +import React, {useEffect, useRef} from 'react'; +import {useHistory} from 'react-router-dom'; + +import hydration from '~/common/hydration'; import useBreakpoints from './hooks/useBreakpoints'; import ChatCenter from './chat--center'; @@ -8,8 +11,17 @@ import ChatLeft from './chat--left'; import ChatRight from './chat--right'; export default function Chat() { + const history = useHistory(); const ref = useRef(null); const {tablet} = useBreakpoints(); + useEffect(() => { + if (!hydration('user')) { + history.goBack(); + } + }); + if (!hydration('user')) { + return null; + } let origin; const abstract = { down: ({pageX}) => { diff --git a/src/client/store.js b/src/client/store.js index c98a227..8f5b8cb 100644 --- a/src/client/store.js +++ b/src/client/store.js @@ -1,6 +1,12 @@ import merge from 'deepmerge'; +import {combineReducers} from 'redux'; import createCommonStore from '~/common/store'; +import user from '~/common/state/user'; + +const reducer = combineReducers({ + user, +}); export default function createStore(options = {}) { return createCommonStore( @@ -8,7 +14,7 @@ export default function createStore(options = {}) { options, { middleware: [], - reducer: () => {}, + reducer, }, ), ); diff --git a/src/common/hydration.js b/src/common/hydration.js new file mode 100644 index 0000000..857cba8 --- /dev/null +++ b/src/common/hydration.js @@ -0,0 +1 @@ +export default (key) => window.__HYDRATION__[key]; diff --git a/src/common/state/user.js b/src/common/state/user.js new file mode 100644 index 0000000..6e6e56e --- /dev/null +++ b/src/common/state/user.js @@ -0,0 +1,51 @@ +/* eslint-disable no-param-reassign */ +import { + createSelector, + createSlice, +} from '@reduxjs/toolkit'; + +import hydration from '../hydration'; + +export const userSelector = (state) => state.user; + +export const friendsSelector = createSelector( + userSelector, + (user) => user.friends, +); + +export const activeFriendsSelector = createSelector( + friendsSelector, + (friends) => Object.values(friends).filter(({state}) => 'active' === state), +); + +export const pendingFriendsSelector = createSelector( + friendsSelector, + (friends) => Object.values(friends).filter(({state}) => 'pending' === state), +); + +export const redditUsernameSelector = createSelector( + userSelector, + (user) => user.redditUsername, +); + +const slice = createSlice({ + name: 'user', + initialState: hydration('user') || { + friends: {}, + redditUsername: 'anonymous', + }, + reducers: { + addFriend: (state, action) => {}, + blockUser: (state, action) => {}, + removeFriend: (state, action) => {}, + updateFriendshipStatus: (state, action) => {}, + }, +}); + +export const { + addFriend, + blockUser, + removeFriend, +} = slice.actions; + +export default slice.reducer; diff --git a/src/server/models/user.model.js b/src/server/models/user.model.js index 41df6d2..8200283 100644 --- a/src/server/models/user.model.js +++ b/src/server/models/user.model.js @@ -44,7 +44,7 @@ class User extends BaseModel { ], }, }); - return Promise.all( + const friendList = await Promise.all( friendships.map(async ({adderId, addeeId, state}) => { const otherId = adderId === this.id ? addeeId : adderId; const user = await User.findByPk(otherId); @@ -55,6 +55,7 @@ class User extends BaseModel { }; }), ); + return friendList.reduce((r, friend) => ({...r, [friend.id]: friend}), {}); } validatePassword(plaintext) { diff --git a/src/server/session.js b/src/server/session.js index 2dab20e..4707516 100644 --- a/src/server/session.js +++ b/src/server/session.js @@ -27,8 +27,10 @@ registerHooks({ const hydration = {}; const {user} = req; if (user) { - hydration.redditUsername = user.redditUsername; - hydration.friends = await user.friends(); + hydration.user = { + friends: await user.friends(), + redditUsername: user.redditUsername, + }; } else { hydration.user = null;