diff --git a/client/src/app/store.ts b/client/src/app/store.ts
index 089d197..fb13f32 100644
--- a/client/src/app/store.ts
+++ b/client/src/app/store.ts
@@ -2,11 +2,13 @@ import { Action, ThunkAction, configureStore } from "@reduxjs/toolkit";
import { useDispatch } from "react-redux";
import loginReducer from "./loginSlice";
import postReducer from "./postSlice";
+import usersReducer from "./usersSlice";
export const store = configureStore({
reducer: {
login: loginReducer,
post: postReducer,
+ users: usersReducer,
},
});
diff --git a/client/src/app/usersSlice.ts b/client/src/app/usersSlice.ts
new file mode 100644
index 0000000..71a1494
--- /dev/null
+++ b/client/src/app/usersSlice.ts
@@ -0,0 +1,116 @@
+import { Store, createSlice } from "@reduxjs/toolkit";
+import { Configuration, User, UserWithRelations, UsersApi } from "../api";
+import { Status } from "../util/types";
+import { AppThunk, store } from "./store";
+
+interface userState {
+ status: Status;
+ error: string | null;
+ users: User[];
+ userWithRelations: UserWithRelations | null;
+}
+
+const initialState: userState = {
+ status: Status.idle,
+ error: null,
+ users: [],
+ userWithRelations: null,
+};
+
+export const usersSlice = createSlice({
+ name: "users",
+ initialState,
+ reducers: {
+ setUsers: (state, action) => {
+ state.users = action.payload;
+ },
+ setStatus: (state, action) => {
+ state.status = action.payload;
+ },
+ setError: (state, action) => {
+ state.error = action.payload;
+ },
+ setUserWithRelations: (state, action) => {
+ state.userWithRelations = action.payload;
+ },
+ },
+});
+
+export const getUsers = (): AppThunk => async (dispatch) => {
+ const api = createApi(store);
+
+ dispatch(setStatus(Status.loading));
+ try {
+ const response = await api.usersGet();
+ dispatch(setUsers(response.data));
+ dispatch(setStatus(Status.idle));
+ } catch (error) {
+ dispatch(setError((error as Error).message));
+ dispatch(setStatus(Status.idle));
+ }
+};
+
+export const getUserWithRelations =
+ (userId: number): AppThunk =>
+ async (dispatch) => {
+ const api = createApi(store);
+
+ dispatch(setStatus(Status.loading));
+ try {
+ const response = await api.usersIdGet(userId);
+ dispatch(setUserWithRelations(response));
+ dispatch(setStatus(Status.idle));
+ } catch (error) {
+ dispatch(setError((error as Error).message));
+ dispatch(setStatus(Status.idle));
+ }
+ };
+
+export const followUser =
+ (userId: number): AppThunk =>
+ async (dispatch) => {
+ const api = createApi(store);
+
+ dispatch(setStatus(Status.loading));
+ try {
+ await api.usersIdFollowPost(userId);
+ dispatch(getUsers());
+ } catch (error) {
+ dispatch(setError((error as Error).message));
+ dispatch(setStatus(Status.idle));
+ }
+ };
+
+export const unFollowUser =
+ (userId: number): AppThunk =>
+ async (dispatch) => {
+ const api = createApi(store);
+
+ dispatch(setStatus(Status.loading));
+ try {
+ await api.usersIdFollowDelete(userId);
+ dispatch(getUsers());
+ } catch (error) {
+ dispatch(setError((error as Error).message));
+ dispatch(setStatus(Status.idle));
+ }
+ };
+
+export const { setUsers, setStatus, setError, setUserWithRelations } =
+ usersSlice.actions;
+
+export default usersSlice.reducer;
+
+export const selectUsers = (state: { users: userState }) => state.users.users;
+export const selectAUser = (state: { users: userState }) =>
+ state.users.userWithRelations;
+export const selectStatus = (state: { users: userState }) => state.users.status;
+export const selectError = (state: { users: userState }) => state.users.error;
+
+function createApi(store: Store) {
+ const configuration = new Configuration({
+ basePath: process.env.REACT_APP_BACKEND_URL,
+ accessToken: store.getState().login.userInfo.jwt,
+ });
+ return new UsersApi(configuration);
+}
diff --git a/client/src/components/appAvatar.tsx b/client/src/components/appAvatar.tsx
new file mode 100644
index 0000000..43b2fd6
--- /dev/null
+++ b/client/src/components/appAvatar.tsx
@@ -0,0 +1,15 @@
+import { Avatar } from "@mui/material";
+import { User, UserWithRelations } from "../api";
+
+interface AppAvatarProps {
+ user: User | UserWithRelations;
+}
+
+export function AppAvatar(props: AppAvatarProps) {
+ return (
+
+ );
+}
diff --git a/client/src/components/topAppBar.tsx b/client/src/components/topAppBar.tsx
index 4d8ced4..2f382ed 100644
--- a/client/src/components/topAppBar.tsx
+++ b/client/src/components/topAppBar.tsx
@@ -5,13 +5,13 @@ import Toolbar from "@mui/material/Toolbar";
import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography";
import Menu from "@mui/material/Menu";
-import Avatar from "@mui/material/Avatar";
import Tooltip from "@mui/material/Tooltip";
import MenuItem from "@mui/material/MenuItem";
import NotificationBell from "./notificationBell";
import {useSelector} from "react-redux";
import { postLogout, selectUserInfo } from "../app/loginSlice";
import { useAppDispatch } from "../app/store";
+import { AppAvatar } from "./appAvatar";
interface TopAppBarProps {
height: number;
@@ -39,73 +39,73 @@ function TopAppBar(props: TopAppBarProps) {
};
return (
-
-
-
- DevSpace
-
-
-
-
+
+
+
+ DevSpace
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
);
}
diff --git a/client/src/routes/search.tsx b/client/src/routes/search.tsx
index 935588d..226ec84 100644
--- a/client/src/routes/search.tsx
+++ b/client/src/routes/search.tsx
@@ -1,3 +1,79 @@
+import { useSelector } from "react-redux";
+import { getUsers, selectUsers } from "../app/usersSlice";
+import React, { MouseEventHandler, useEffect } from "react";
+import { useAppDispatch } from "../app/store";
+import {
+ Box,
+ Button,
+ Divider,
+ List,
+ ListItem,
+ ListItemText,
+ Paper,
+ TextField,
+} from "@mui/material";
+import { AppAvatar } from "../components/appAvatar";
+import { useNavigate } from "react-router-dom";
+import { matchSorter } from "match-sorter";
+
export default function Search() {
- return <>Search>;
+ const userList = useSelector(selectUsers);
+ const [displayUserList, setDisplayUserList] = React.useState(userList);
+ const dispatch = useAppDispatch();
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ dispatch(getUsers());
+ }, []);
+
+ const handleSearchChange = (event: React.ChangeEvent) => {
+ const searchValue = event.currentTarget.value;
+ if (searchValue === "") {
+ setDisplayUserList(userList);
+ return;
+ }
+ setDisplayUserList(
+ matchSorter(userList, searchValue, { keys: ["firstName", "lastName"] })
+ );
+ };
+
+ const handleClick: MouseEventHandler = (event) => {
+ const userId = event.currentTarget.id;
+ navigate(`/user/${userId}`);
+ };
+
+ return (
+
+
+
+
+
+
+ {displayUserList.map((user) => (
+ <>
+
+
+
+
+ >
+ ))}
+
+
+
+ );
}
diff --git a/server/src/entity/User.ts b/server/src/entity/User.ts
index 91e1866..2c06bf2 100644
--- a/server/src/entity/User.ts
+++ b/server/src/entity/User.ts
@@ -21,7 +21,7 @@ export class User {
@Column()
password: string;
- @Column({ default: true })
+ @Column({ default: false })
isPrivate: boolean;
@Column({ default: null })