Compare commits
No commits in common. "6fbfe81918bee023c95ba56e9162375c292adae6" and "d73bd1dd049547c56f5fe0641108580bf240c4ed" have entirely different histories.
6fbfe81918
...
d73bd1dd04
|
|
@ -1,19 +0,0 @@
|
||||||
import { ThemeProvider } from "@emotion/react";
|
|
||||||
import React from "react";
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import { selectDarkMode } from "./loginSlice";
|
|
||||||
import { createTheme } from "@mui/material";
|
|
||||||
|
|
||||||
const AppThemeProvider = ({ children }: { children: React.ReactNode }) => {
|
|
||||||
const darkThemeEnabled = useSelector(selectDarkMode);
|
|
||||||
|
|
||||||
const defaultTheme = createTheme({
|
|
||||||
palette: {
|
|
||||||
mode: darkThemeEnabled ? "dark" : "light",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return <ThemeProvider theme={defaultTheme}>{children}</ThemeProvider>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AppThemeProvider;
|
|
||||||
|
|
@ -13,32 +13,25 @@ interface loginState {
|
||||||
loggedIn: boolean;
|
loggedIn: boolean;
|
||||||
status: Status;
|
status: Status;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
darkMode: boolean;
|
|
||||||
userInfo: {
|
userInfo: {
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
jwt: string;
|
jwt: string;
|
||||||
id: number;
|
id: number;
|
||||||
profilePictureId: string;
|
profilePictureId: string;
|
||||||
notifications: Notification[];
|
|
||||||
isPrivate: boolean;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const storageDarkMode = localStorage.getItem("darkMode") === "true";
|
|
||||||
|
|
||||||
const initialState: loginState = {
|
const initialState: loginState = {
|
||||||
loggedIn: false,
|
loggedIn: false,
|
||||||
status: Status.idle,
|
status: Status.idle,
|
||||||
darkMode: storageDarkMode || false,
|
|
||||||
error: null,
|
error: null,
|
||||||
userInfo: {
|
userInfo: {
|
||||||
isPrivate: false,
|
|
||||||
firstName: "",
|
firstName: "",
|
||||||
lastName: "",
|
lastName: "",
|
||||||
jwt: "",
|
jwt: "",
|
||||||
id: -1,
|
id: -1,
|
||||||
profilePictureId: "",
|
profilePictureId: "",
|
||||||
notifications: [],
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -53,8 +46,6 @@ export const loginSlice = createSlice({
|
||||||
state.userInfo.firstName = action.payload.firstName;
|
state.userInfo.firstName = action.payload.firstName;
|
||||||
state.userInfo.lastName = action.payload.lastName;
|
state.userInfo.lastName = action.payload.lastName;
|
||||||
state.userInfo.profilePictureId = action.payload.profilePictureId;
|
state.userInfo.profilePictureId = action.payload.profilePictureId;
|
||||||
state.userInfo.notifications = action.payload.notifications;
|
|
||||||
state.userInfo.isPrivate = action.payload.isPrivate;
|
|
||||||
},
|
},
|
||||||
logoff: (state) => {
|
logoff: (state) => {
|
||||||
state.loggedIn = false;
|
state.loggedIn = false;
|
||||||
|
|
@ -66,12 +57,6 @@ export const loginSlice = createSlice({
|
||||||
setError: (state, action) => {
|
setError: (state, action) => {
|
||||||
state.error = action.payload;
|
state.error = action.payload;
|
||||||
},
|
},
|
||||||
setDarkMode: (state, action) => {
|
|
||||||
state.darkMode = action.payload;
|
|
||||||
},
|
|
||||||
setPrivate: (state, action) => {
|
|
||||||
state.userInfo.isPrivate = action.payload;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -108,7 +93,6 @@ export const postLogin =
|
||||||
lastName: userResponse.data.lastName,
|
lastName: userResponse.data.lastName,
|
||||||
id: userResponse.data.id,
|
id: userResponse.data.id,
|
||||||
profilePictureId: userResponse.data.profilePictureId,
|
profilePictureId: userResponse.data.profilePictureId,
|
||||||
notifications: userResponse.data.notifications,
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
dispatch(setStatus(Status.succeeded));
|
dispatch(setStatus(Status.succeeded));
|
||||||
|
|
@ -136,22 +120,6 @@ export const postSignup =
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateMe =
|
|
||||||
(isPrivate: boolean): AppThunk =>
|
|
||||||
async (dispatch) => {
|
|
||||||
const userApi = new UsersApi(
|
|
||||||
new Configuration({
|
|
||||||
basePath: process.env.REACT_APP_BACKEND_URL,
|
|
||||||
accessToken: localStorage.getItem("jwt") || "",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
console.log("From inside updateME dispatch, param is:", isPrivate);
|
|
||||||
|
|
||||||
await userApi.usersMePatch({ isPrivate });
|
|
||||||
const userResponse = await userApi.usersMeGet();
|
|
||||||
dispatch(setPrivate(userResponse.data.isPrivate));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postLogout = (): AppThunk => async (dispatch) => {
|
export const postLogout = (): AppThunk => async (dispatch) => {
|
||||||
localStorage.removeItem("jwt");
|
localStorage.removeItem("jwt");
|
||||||
sessionStorage.removeItem("jwt");
|
sessionStorage.removeItem("jwt");
|
||||||
|
|
@ -163,8 +131,7 @@ const addJWT = async (token: string) => {
|
||||||
localStorage.setItem("jwt", token);
|
localStorage.setItem("jwt", token);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const { login, logoff, setStatus, setError, setDarkMode, setPrivate } =
|
export const { login, logoff, setStatus, setError } = loginSlice.actions;
|
||||||
loginSlice.actions;
|
|
||||||
|
|
||||||
export default loginSlice.reducer;
|
export default loginSlice.reducer;
|
||||||
|
|
||||||
|
|
@ -176,6 +143,3 @@ export const selectUserInfo = (state: { login: loginState }) =>
|
||||||
|
|
||||||
export const selectErrorMessage = (state: { login: loginState }) =>
|
export const selectErrorMessage = (state: { login: loginState }) =>
|
||||||
state.login.error;
|
state.login.error;
|
||||||
|
|
||||||
export const selectDarkMode = (state: { login: loginState }) =>
|
|
||||||
state.login.darkMode;
|
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,12 @@ interface postSliceInterface {
|
||||||
status: Status;
|
status: Status;
|
||||||
followedPosts: Post[];
|
followedPosts: Post[];
|
||||||
globalPosts: Post[];
|
globalPosts: Post[];
|
||||||
aPost: Post | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: postSliceInterface = {
|
const initialState: postSliceInterface = {
|
||||||
status: Status.idle,
|
status: Status.idle,
|
||||||
followedPosts: [],
|
followedPosts: [],
|
||||||
globalPosts: [],
|
globalPosts: [],
|
||||||
aPost: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const postSlice = createSlice({
|
export const postSlice = createSlice({
|
||||||
|
|
@ -30,9 +28,6 @@ export const postSlice = createSlice({
|
||||||
setGlobalPosts: (state, action) => {
|
setGlobalPosts: (state, action) => {
|
||||||
state.globalPosts = action.payload;
|
state.globalPosts = action.payload;
|
||||||
},
|
},
|
||||||
setAPost: (state, action) => {
|
|
||||||
state.aPost = action.payload;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -55,99 +50,26 @@ export const fetchGlobalPosts = (): AppThunk => async (dispatch: any) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const likePost =
|
export const likePost =
|
||||||
(postId: number, postType?: "global" | "followed"): AppThunk =>
|
(postId: number): AppThunk =>
|
||||||
async (dispatch: any) => {
|
async (dispatch: any) => {
|
||||||
const postApi = createApi(store);
|
const postApi = createApi(store);
|
||||||
|
|
||||||
dispatch(setStatus(Status.loading));
|
dispatch(setStatus(Status.loading));
|
||||||
await postApi.likePost(postId);
|
await postApi.likePost(postId);
|
||||||
dispatch(setStatus(Status.idle));
|
dispatch(setStatus(Status.idle));
|
||||||
|
|
||||||
if (postType === "global") {
|
|
||||||
dispatch(fetchGlobalPosts());
|
|
||||||
} else if (postType === "followed") {
|
|
||||||
dispatch(fetchFollowedPosts());
|
|
||||||
} else {
|
|
||||||
dispatch(fetchFollowedPosts());
|
|
||||||
dispatch(fetchGlobalPosts());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const unLikePost =
|
export const unLikePost =
|
||||||
(postId: number, postType?: "global" | "followed"): AppThunk =>
|
(postId: number): AppThunk =>
|
||||||
async (dispatch: any) => {
|
async (dispatch: any) => {
|
||||||
const postApi = createApi(store);
|
const postApi = createApi(store);
|
||||||
|
|
||||||
dispatch(setStatus(Status.loading));
|
dispatch(setStatus(Status.loading));
|
||||||
await postApi.unlikePost(postId);
|
await postApi.unlikePost(postId);
|
||||||
dispatch(setStatus(Status.idle));
|
dispatch(setStatus(Status.idle));
|
||||||
|
|
||||||
if (postType === "global") {
|
|
||||||
dispatch(fetchGlobalPosts());
|
|
||||||
} else if (postType === "followed") {
|
|
||||||
dispatch(fetchFollowedPosts());
|
|
||||||
} else {
|
|
||||||
dispatch(fetchFollowedPosts());
|
|
||||||
dispatch(fetchGlobalPosts());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchAPost =
|
export const { setFollowedPosts, setGlobalPosts, setStatus } =
|
||||||
(postId: number): AppThunk =>
|
|
||||||
async (dispatch: any) => {
|
|
||||||
const postApi = createApi(store);
|
|
||||||
|
|
||||||
dispatch(setStatus(Status.loading));
|
|
||||||
const response = await postApi.getPost(postId);
|
|
||||||
dispatch(setAPost(response.data));
|
|
||||||
dispatch(setStatus(Status.idle));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const commentAPost =
|
|
||||||
(postId: number, comment: string): AppThunk =>
|
|
||||||
async (dispatch: any) => {
|
|
||||||
const postApi = createApi(store);
|
|
||||||
|
|
||||||
dispatch(setStatus(Status.loading));
|
|
||||||
await postApi.commentPost(postId, { content: comment });
|
|
||||||
dispatch(fetchAPost(postId));
|
|
||||||
dispatch(setStatus(Status.idle));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createPost =
|
|
||||||
(title: string, content: string): AppThunk =>
|
|
||||||
async (dispatch: any) => {
|
|
||||||
const postApi = createApi(store);
|
|
||||||
|
|
||||||
dispatch(setStatus(Status.loading));
|
|
||||||
await postApi.createPost({ title, content });
|
|
||||||
dispatch(fetchGlobalPosts());
|
|
||||||
dispatch(setStatus(Status.idle));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updatePost =
|
|
||||||
(postId: number, title: string, content: string): AppThunk =>
|
|
||||||
async (dispatch: any) => {
|
|
||||||
const postApi = createApi(store);
|
|
||||||
|
|
||||||
dispatch(setStatus(Status.loading));
|
|
||||||
await postApi.updatePost(postId, { title, content });
|
|
||||||
dispatch(fetchAPost(postId));
|
|
||||||
dispatch(fetchGlobalPosts());
|
|
||||||
dispatch(setStatus(Status.idle));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deletePost =
|
|
||||||
(postId: number): AppThunk =>
|
|
||||||
async (dispatch: any) => {
|
|
||||||
const postApi = createApi(store);
|
|
||||||
|
|
||||||
dispatch(setStatus(Status.loading));
|
|
||||||
await postApi.deletePost(postId);
|
|
||||||
dispatch(setStatus(Status.idle));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const { setFollowedPosts, setGlobalPosts, setStatus, setAPost } =
|
|
||||||
postSlice.actions;
|
postSlice.actions;
|
||||||
|
|
||||||
export default postSlice.reducer;
|
export default postSlice.reducer;
|
||||||
|
|
@ -155,7 +77,6 @@ export default postSlice.reducer;
|
||||||
export const selectFollowedPosts = (state: any) => state.post.followedPosts;
|
export const selectFollowedPosts = (state: any) => state.post.followedPosts;
|
||||||
export const selectAllPosts = (state: any) => state.post.globalPosts;
|
export const selectAllPosts = (state: any) => state.post.globalPosts;
|
||||||
export const selectStatus = (state: any) => state.post.status;
|
export const selectStatus = (state: any) => state.post.status;
|
||||||
export const selectAPost = (state: any) => state.post.aPost;
|
|
||||||
|
|
||||||
function createApi(store: Store) {
|
function createApi(store: Store) {
|
||||||
return new PostsApi(
|
return new PostsApi(
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { User, UserWithRelations } from "../api";
|
||||||
|
|
||||||
interface AppAvatarProps {
|
interface AppAvatarProps {
|
||||||
user: User | UserWithRelations;
|
user: User | UserWithRelations;
|
||||||
small?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AppAvatar(props: AppAvatarProps) {
|
export function AppAvatar(props: AppAvatarProps) {
|
||||||
|
|
@ -11,10 +10,6 @@ export function AppAvatar(props: AppAvatarProps) {
|
||||||
<Avatar
|
<Avatar
|
||||||
alt={`${props.user.firstName} ${props.user.lastName}`}
|
alt={`${props.user.firstName} ${props.user.lastName}`}
|
||||||
src={`/images/${props.user.profilePictureId}`}
|
src={`/images/${props.user.profilePictureId}`}
|
||||||
sx={{
|
|
||||||
width: props.small ? "24px" : "42px",
|
|
||||||
height: props.small ? "24px" : "42px",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,14 @@
|
||||||
import { BottomNavigation, BottomNavigationAction, Paper } from "@mui/material";
|
import { BottomNavigation, BottomNavigationAction, Paper } from "@mui/material";
|
||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import FeedIcon from "@mui/icons-material/Feed";
|
import FeedIcon from "@mui/icons-material/Feed";
|
||||||
import GlobalIcon from "@mui/icons-material/Public";
|
import GlobalIcon from "@mui/icons-material/Public";
|
||||||
import AddIcon from "@mui/icons-material/Add";
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
import SearchIcon from "@mui/icons-material/Search";
|
import SearchIcon from "@mui/icons-material/Search";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
export default function BottomAppBar() {
|
export default function BottomAppBar() {
|
||||||
const [value, setValue] = useState("");
|
const [value, setValue] = useState(0);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setValue(location.pathname.replace("/", ""));
|
|
||||||
}, [location]);
|
|
||||||
|
|
||||||
const handleClick = (to: string) => () => {
|
const handleClick = (to: string) => () => {
|
||||||
navigate(to);
|
navigate(to);
|
||||||
|
|
@ -32,25 +27,21 @@ export default function BottomAppBar() {
|
||||||
>
|
>
|
||||||
<BottomNavigationAction
|
<BottomNavigationAction
|
||||||
label="My Feed"
|
label="My Feed"
|
||||||
value="feed"
|
|
||||||
icon={<FeedIcon />}
|
icon={<FeedIcon />}
|
||||||
onClick={handleClick("feed")}
|
onClick={handleClick("feed")}
|
||||||
/>
|
/>
|
||||||
<BottomNavigationAction
|
<BottomNavigationAction
|
||||||
label="Global Feed"
|
label="Global Feed"
|
||||||
value="global"
|
|
||||||
icon={<GlobalIcon />}
|
icon={<GlobalIcon />}
|
||||||
onClick={handleClick("global")}
|
onClick={handleClick("global")}
|
||||||
/>
|
/>
|
||||||
<BottomNavigationAction
|
<BottomNavigationAction
|
||||||
label="New Post"
|
label="New Post"
|
||||||
value="newpost"
|
|
||||||
icon={<AddIcon />}
|
icon={<AddIcon />}
|
||||||
onClick={handleClick("newpost")}
|
onClick={handleClick("newpost")}
|
||||||
/>
|
/>
|
||||||
<BottomNavigationAction
|
<BottomNavigationAction
|
||||||
label="Search"
|
label="Search"
|
||||||
value="search"
|
|
||||||
icon={<SearchIcon />}
|
icon={<SearchIcon />}
|
||||||
onClick={handleClick("search")}
|
onClick={handleClick("search")}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,11 @@
|
||||||
import NotificationsIcon from "@mui/icons-material/Notifications";
|
import NotificationsIcon from "@mui/icons-material/Notifications";
|
||||||
import { Badge, Tooltip } from "@mui/material";
|
import { Badge, Tooltip } from "@mui/material";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
|
|
||||||
export default function NotificationBell() {
|
export default function NotificationBell() {
|
||||||
const navigate = useNavigate();
|
|
||||||
const handleClick = () => {
|
|
||||||
navigate("/notifications");
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<Badge badgeContent={0} color="secondary">
|
<Badge badgeContent={4} color="secondary">
|
||||||
<Tooltip title="Notifications">
|
<Tooltip title="Notifications" >
|
||||||
<NotificationsIcon onClick={handleClick} />
|
<NotificationsIcon />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,7 @@ import { selectUserInfo } from "../app/loginSlice";
|
||||||
|
|
||||||
interface PostListItemProps {
|
interface PostListItemProps {
|
||||||
post: Post;
|
post: Post;
|
||||||
postType: "feed" | "user";
|
postType: "all" | "user";
|
||||||
feedType?: "global" | "followed";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PostListItem(props: PostListItemProps) {
|
export default function PostListItem(props: PostListItemProps) {
|
||||||
|
|
@ -41,19 +40,19 @@ export default function PostListItem(props: PostListItemProps) {
|
||||||
props.post.likedBy?.some((user) => user.id === userInfo.id) || false
|
props.post.likedBy?.some((user) => user.id === userInfo.id) || false
|
||||||
);
|
);
|
||||||
setNumberOfLikes(props.post.likedBy?.length || 0);
|
setNumberOfLikes(props.post.likedBy?.length || 0);
|
||||||
}, [props.post, userInfo.id]);
|
}, [props.post.likedBy, userInfo.id]);
|
||||||
|
|
||||||
const handleLike = () => {
|
const handleLike = () => {
|
||||||
if (!liked) {
|
if (!liked) {
|
||||||
setNumberOfLikes(numberOfLikes + 1);
|
setNumberOfLikes(numberOfLikes + 1);
|
||||||
dispatch(likePost(props.post.id as number, props.feedType));
|
dispatch(likePost(props.post.id as number));
|
||||||
}
|
}
|
||||||
// If the user has already liked the post, unlike it
|
// If the user has already liked the post, unlike it
|
||||||
else {
|
else {
|
||||||
setNumberOfLikes(numberOfLikes - 1);
|
setNumberOfLikes(numberOfLikes - 1);
|
||||||
dispatch(unLikePost(props.post.id as number, props.feedType));
|
dispatch(unLikePost(props.post.id as number));
|
||||||
}
|
}
|
||||||
setLiked(!liked);
|
setLiked((prevState) => !prevState);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePostClick = () => {
|
const handlePostClick = () => {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import {useSelector} from "react-redux";
|
||||||
import { postLogout, selectUserInfo } from "../app/loginSlice";
|
import { postLogout, selectUserInfo } from "../app/loginSlice";
|
||||||
import { useAppDispatch } from "../app/store";
|
import { useAppDispatch } from "../app/store";
|
||||||
import { AppAvatar } from "./appAvatar";
|
import { AppAvatar } from "./appAvatar";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
|
|
||||||
interface TopAppBarProps {
|
interface TopAppBarProps {
|
||||||
height: number;
|
height: number;
|
||||||
|
|
@ -20,12 +19,12 @@ interface TopAppBarProps {
|
||||||
|
|
||||||
function TopAppBar(props: TopAppBarProps) {
|
function TopAppBar(props: TopAppBarProps) {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const navigate = useNavigate();
|
|
||||||
const userInfo = useSelector(selectUserInfo);
|
const userInfo = useSelector(selectUserInfo);
|
||||||
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(
|
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorElUser(event.currentTarget);
|
setAnchorElUser(event.currentTarget);
|
||||||
};
|
};
|
||||||
|
|
@ -39,19 +38,9 @@ function TopAppBar(props: TopAppBarProps) {
|
||||||
setAnchorElUser(null);
|
setAnchorElUser(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProfileClick = () => {
|
|
||||||
navigate("/me");
|
|
||||||
setAnchorElUser(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSettingsClick = () => {
|
|
||||||
navigate("/settings");
|
|
||||||
setAnchorElUser(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar
|
<AppBar
|
||||||
position="fixed"
|
position="absolute"
|
||||||
sx={{ zIndex: 1600, height: `${props.height}px` }}
|
sx={{ zIndex: 1600, height: `${props.height}px` }}
|
||||||
>
|
>
|
||||||
<Toolbar disableGutters>
|
<Toolbar disableGutters>
|
||||||
|
|
@ -105,10 +94,10 @@ function TopAppBar(props: TopAppBarProps) {
|
||||||
open={Boolean(anchorElUser)}
|
open={Boolean(anchorElUser)}
|
||||||
onClose={handleCloseUserMenu}
|
onClose={handleCloseUserMenu}
|
||||||
>
|
>
|
||||||
<MenuItem key={"profile"} onClick={handleProfileClick}>
|
<MenuItem key={"profile"} onClick={handleCloseUserMenu}>
|
||||||
<Typography textAlign="center">{"Profile"}</Typography>
|
<Typography textAlign="center">{"Profile"}</Typography>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem key={"settings"} onClick={handleSettingsClick}>
|
<MenuItem key={"settings"} onClick={handleCloseUserMenu}>
|
||||||
<Typography textAlign="center">{"Settings"}</Typography>
|
<Typography textAlign="center">{"Settings"}</Typography>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem key={"logout"} onClick={handleLogout}>
|
<MenuItem key={"logout"} onClick={handleLogout}>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import "@fontsource/roboto";
|
||||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||||
import Root from "./routes/root";
|
import Root from "./routes/root";
|
||||||
import ErrorPage from "./error-page";
|
import ErrorPage from "./error-page";
|
||||||
import { CssBaseline } from "@mui/material";
|
import { createTheme, CssBaseline, ThemeProvider } from "@mui/material";
|
||||||
import Login from "./routes/Auth/login";
|
import Login from "./routes/Auth/login";
|
||||||
import Register from "./routes/Auth/register";
|
import Register from "./routes/Auth/register";
|
||||||
import AuthRoot from "./routes/Auth/authRoot";
|
import AuthRoot from "./routes/Auth/authRoot";
|
||||||
|
|
@ -14,17 +14,20 @@ import { Provider } from "react-redux";
|
||||||
import { store } from "./app/store";
|
import { store } from "./app/store";
|
||||||
import PostList from "./routes/postList";
|
import PostList from "./routes/postList";
|
||||||
import Profile from "./routes/profile";
|
import Profile from "./routes/profile";
|
||||||
import PostView from "./routes/post";
|
import Post from "./routes/post";
|
||||||
import NewPost from "./routes/newPost";
|
import NewPost from "./routes/newPost";
|
||||||
import Search from "./routes/search";
|
import Search from "./routes/search";
|
||||||
import Settings from "./routes/settings";
|
|
||||||
import ANotification from "./routes/notification";
|
|
||||||
import AppThemeProvider from "./app/AppThemeProvider";
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById("root") as HTMLElement
|
document.getElementById("root") as HTMLElement
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const defaultTheme = createTheme({
|
||||||
|
palette: {
|
||||||
|
mode: "light",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
|
|
@ -33,11 +36,11 @@ const router = createBrowserRouter([
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "feed",
|
path: "feed",
|
||||||
element: <PostList type="feed" feedType="followed" />,
|
element: <PostList type="user" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "global",
|
path: "global",
|
||||||
element: <PostList type="feed" feedType="global" />,
|
element: <PostList type="all" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "me",
|
path: "me",
|
||||||
|
|
@ -49,11 +52,7 @@ const router = createBrowserRouter([
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "post/:id",
|
path: "post/:id",
|
||||||
element: <PostView />,
|
element: <Post />,
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "post/:id/edit",
|
|
||||||
element: <NewPost />,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "search",
|
path: "search",
|
||||||
|
|
@ -63,14 +62,6 @@ const router = createBrowserRouter([
|
||||||
path: "newpost",
|
path: "newpost",
|
||||||
element: <NewPost />,
|
element: <NewPost />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "settings",
|
|
||||||
element: <Settings />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "notifications",
|
|
||||||
element: <ANotification />,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -93,10 +84,10 @@ const router = createBrowserRouter([
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<AppThemeProvider>
|
<ThemeProvider theme={defaultTheme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<RouterProvider router={router} />
|
<RouterProvider router={router} />
|
||||||
</AppThemeProvider>
|
</ThemeProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,93 +1,3 @@
|
||||||
import { Box, Button, TextField, Typography } from "@mui/material";
|
|
||||||
import { useAppDispatch } from "../app/store";
|
|
||||||
import React, { useEffect } from "react";
|
|
||||||
import {
|
|
||||||
createPost,
|
|
||||||
selectAPost,
|
|
||||||
updatePost,
|
|
||||||
} from "../app/postSlice";
|
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
|
|
||||||
export default function NewPost() {
|
export default function NewPost() {
|
||||||
const { id } = useParams();
|
return <>New Post</>;
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const [newPostText, setNewPostText] = React.useState("");
|
|
||||||
const [newPostTitle, setNewPostTitle] = React.useState("");
|
|
||||||
const aPost = useSelector(selectAPost);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (id) {
|
|
||||||
setNewPostText(aPost.content);
|
|
||||||
setNewPostTitle(aPost.title);
|
|
||||||
}
|
|
||||||
}, [aPost, id]);
|
|
||||||
|
|
||||||
const handleCreatePost = () => {
|
|
||||||
if (newPostText === "" || newPostTitle === "") return;
|
|
||||||
|
|
||||||
dispatch(createPost(newPostTitle, newPostText));
|
|
||||||
setNewPostText("");
|
|
||||||
setNewPostTitle("");
|
|
||||||
navigate("/global");
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUpdatePost = () => {
|
|
||||||
if (newPostText === "" || newPostTitle === "") return;
|
|
||||||
if (!id) return;
|
|
||||||
|
|
||||||
dispatch(updatePost(parseInt(id), newPostTitle, newPostText));
|
|
||||||
setNewPostText("");
|
|
||||||
setNewPostTitle("");
|
|
||||||
navigate("/global");
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
margin: "auto",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="h4" sx={{ marginBottom: 2 }}>
|
|
||||||
Share your thoughts!
|
|
||||||
</Typography>
|
|
||||||
<TextField
|
|
||||||
label="Title"
|
|
||||||
value={newPostTitle}
|
|
||||||
multiline
|
|
||||||
onChange={(e) => setNewPostTitle(e.target.value)}
|
|
||||||
sx={{ marginBottom: 2 }}
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
label="Post"
|
|
||||||
value={newPostText}
|
|
||||||
onChange={(e) => setNewPostText(e.target.value)}
|
|
||||||
multiline
|
|
||||||
rows={4}
|
|
||||||
sx={{ marginBottom: 2 }}
|
|
||||||
/>
|
|
||||||
{id ? (
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="success"
|
|
||||||
fullWidth
|
|
||||||
onClick={handleUpdatePost}
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
fullWidth
|
|
||||||
onClick={handleCreatePost}
|
|
||||||
>
|
|
||||||
Create
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,3 @@
|
||||||
import { useSelector } from "react-redux";
|
export default function Notification() {
|
||||||
import { selectUserInfo } from "../app/loginSlice";
|
return <>Notification</>;
|
||||||
import { Box, List, ListItem, Paper } from "@mui/material";
|
|
||||||
|
|
||||||
export default function ANotification() {
|
|
||||||
const userInfo = useSelector(selectUserInfo);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
|
||||||
<List sx={{ width: "100%" }}>
|
|
||||||
{userInfo.notifications.length === 0 ? (
|
|
||||||
<ListItem sx={{ width: "100%" }}>No notifications</ListItem>
|
|
||||||
) : (
|
|
||||||
userInfo.notifications.map((notification: any) => (
|
|
||||||
<ListItem key={notification.id} sx={{ width: "100%" }}>
|
|
||||||
<Paper
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
margin: "1rem",
|
|
||||||
padding: "0.2rem 1rem 0.2rem 1rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{notification.message}
|
|
||||||
</Paper>
|
|
||||||
</ListItem>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</List>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,155 +1,3 @@
|
||||||
import { useSelector } from "react-redux";
|
export default function Post() {
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
return <>Post</>;
|
||||||
import {
|
|
||||||
commentAPost,
|
|
||||||
deletePost,
|
|
||||||
fetchAPost,
|
|
||||||
selectAPost,
|
|
||||||
} from "../app/postSlice";
|
|
||||||
import { useAppDispatch } from "../app/store";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
import React from "react";
|
|
||||||
import { Post } from "../api";
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Divider,
|
|
||||||
Paper,
|
|
||||||
TextField,
|
|
||||||
Typography,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { AppAvatar } from "../components/appAvatar";
|
|
||||||
import { selectUserInfo } from "../app/loginSlice";
|
|
||||||
|
|
||||||
export default function PostView() {
|
|
||||||
const postId = useParams<{ id: string }>().id;
|
|
||||||
const storePost = useSelector(selectAPost);
|
|
||||||
const selfUser = useSelector(selectUserInfo);
|
|
||||||
const [postToDisplay, setPostToDisplay] = React.useState<Post | null>(null);
|
|
||||||
const [newComentText, setNewCommentText] = React.useState("");
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(fetchAPost(parseInt(postId!, 10)));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setPostToDisplay(storePost);
|
|
||||||
}, [storePost]);
|
|
||||||
|
|
||||||
const handleCommentPost = () => {
|
|
||||||
if (newComentText.length > 0) {
|
|
||||||
dispatch(commentAPost(postToDisplay!.id!, newComentText));
|
|
||||||
setNewCommentText("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCancelComment = () => {
|
|
||||||
setNewCommentText("");
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeletePost = () => {
|
|
||||||
dispatch(deletePost(postToDisplay!.id!));
|
|
||||||
navigate("/global");
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
width: "60%",
|
|
||||||
margin: "auto",
|
|
||||||
minWidth: "330px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ width: "100%" }}>
|
|
||||||
<Paper>
|
|
||||||
<Box sx={{ margin: "1rem" }}>
|
|
||||||
<Box
|
|
||||||
sx={{ display: "flex", alignItems: "center", paddingTop: "1rem" }}
|
|
||||||
>
|
|
||||||
{postToDisplay?.createdBy && (
|
|
||||||
<AppAvatar user={postToDisplay?.createdBy!} />
|
|
||||||
)}
|
|
||||||
<Typography
|
|
||||||
variant="subtitle2"
|
|
||||||
sx={{ mb: "0.2rem", ml: "0.3rem" }}
|
|
||||||
>
|
|
||||||
{postToDisplay?.createdBy?.firstName}{" "}
|
|
||||||
{postToDisplay?.createdBy?.lastName}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Divider sx={{ mb: "0.3rem" }} />
|
|
||||||
<Typography variant="h4">{postToDisplay?.title}</Typography>
|
|
||||||
<Typography variant="body1">{postToDisplay?.content}</Typography>
|
|
||||||
</Box>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ width: "100%" }}>
|
|
||||||
{postToDisplay?.createdBy?.id === selfUser?.id && (
|
|
||||||
<Box>
|
|
||||||
<Button onClick={() => navigate("edit")}>Edit</Button>
|
|
||||||
<Button color="warning" onClick={handleDeletePost}>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<TextField
|
|
||||||
label="Add a comment..."
|
|
||||||
fullWidth
|
|
||||||
multiline
|
|
||||||
value={newComentText}
|
|
||||||
onChange={(e) => setNewCommentText(e.target.value)}
|
|
||||||
InputProps={{
|
|
||||||
endAdornment: (
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={handleCommentPost}
|
|
||||||
sx={{
|
|
||||||
display: `${newComentText.length > 0 ? "inherit" : "none"}`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Accept
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
onClick={handleCancelComment}
|
|
||||||
sx={{
|
|
||||||
display: `${newComentText.length > 0 ? "inherit" : "none"}`,
|
|
||||||
ml: "0.5rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
mb: "1rem",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Box>
|
|
||||||
{postToDisplay?.comments?.map((comment) => (
|
|
||||||
<Paper>
|
|
||||||
<Box sx={{ margin: "1rem" }}>
|
|
||||||
<Box sx={{ display: "flex", mb: "0.3rem" }}>
|
|
||||||
<AppAvatar user={comment.createdBy!} small />
|
|
||||||
<Typography variant="subtitle2" sx={{ ml: "0.5rem" }}>
|
|
||||||
{comment.createdBy?.firstName} {comment.createdBy?.lastName}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Divider sx={{ mb: "0.3rem" }} />
|
|
||||||
<Typography variant="body1">{comment.content}</Typography>
|
|
||||||
</Box>
|
|
||||||
</Paper>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@ import PostListItem from "../components/postListItem";
|
||||||
import { Status } from "../util/types";
|
import { Status } from "../util/types";
|
||||||
|
|
||||||
interface PostListProps {
|
interface PostListProps {
|
||||||
type: "feed" | "user";
|
type: "all" | "user";
|
||||||
feedType?: "global" | "followed";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PostList(props: PostListProps) {
|
export default function PostList(props: PostListProps) {
|
||||||
|
|
@ -26,33 +25,13 @@ export default function PostList(props: PostListProps) {
|
||||||
const [posts, setPosts] = useState<Post[]>([]);
|
const [posts, setPosts] = useState<Post[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.feedType === "global") {
|
dispatch(fetchGlobalPosts());
|
||||||
dispatch(fetchGlobalPosts());
|
dispatch(fetchFollowedPosts());
|
||||||
} else {
|
}, []);
|
||||||
dispatch(fetchFollowedPosts());
|
|
||||||
}
|
|
||||||
}, [props.feedType]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let postsToDisplay: Post[];
|
setPosts(props.type === "all" ? globalPosts : followedPosts);
|
||||||
if (props.feedType === "global") {
|
}, [followedPosts, globalPosts, props.type]);
|
||||||
postsToDisplay = globalPosts;
|
|
||||||
} else {
|
|
||||||
postsToDisplay = followedPosts;
|
|
||||||
}
|
|
||||||
let sortedPosts: Post[] = [];
|
|
||||||
|
|
||||||
if (postsToDisplay.length > 0) {
|
|
||||||
sortedPosts = [...postsToDisplay].sort((a: Post, b: Post) => {
|
|
||||||
return b.createdAt! > a.createdAt!
|
|
||||||
? 1
|
|
||||||
: b.createdAt! < a.createdAt!
|
|
||||||
? -1
|
|
||||||
: 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setPosts(sortedPosts);
|
|
||||||
}, [followedPosts, globalPosts, props.feedType]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
|
|
@ -67,27 +46,17 @@ export default function PostList(props: PostListProps) {
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{props.feedType === "global" && (
|
{props.type === "all" && (
|
||||||
<List>
|
<List>
|
||||||
{posts.map((post: Post) => (
|
{posts.map((post: Post) => (
|
||||||
<PostListItem
|
<PostListItem post={post} postType="all" key={post.id} />
|
||||||
post={post}
|
|
||||||
postType="feed"
|
|
||||||
feedType="global"
|
|
||||||
key={post.id}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
)}
|
)}
|
||||||
{props.feedType === "followed" && (
|
{props.type === "user" && (
|
||||||
<List>
|
<List>
|
||||||
{followedPosts.map((post: Post) => (
|
{followedPosts.map((post: Post) => (
|
||||||
<PostListItem
|
<PostListItem post={post} postType="user" key={post.id} />
|
||||||
post={post}
|
|
||||||
postType="feed"
|
|
||||||
feedType="followed"
|
|
||||||
key={post.id}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
{followedPosts.length === 0 && (
|
{followedPosts.length === 0 && (
|
||||||
<Box
|
<Box
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ export default function Search() {
|
||||||
<Paper>
|
<Paper>
|
||||||
<List>
|
<List>
|
||||||
{displayUserList.map((user) => (
|
{displayUserList.map((user) => (
|
||||||
<Box key={user.id}>
|
<>
|
||||||
<ListItem key={user.id}>
|
<ListItem key={user.id}>
|
||||||
<Button
|
<Button
|
||||||
fullWidth
|
fullWidth
|
||||||
|
|
@ -78,7 +78,7 @@ export default function Search() {
|
||||||
</Button>
|
</Button>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider sx={{ mb: "0.3rem" }} />
|
<Divider sx={{ mb: "0.3rem" }} />
|
||||||
</Box>
|
</>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
import { Box, Checkbox, Divider, Typography } from "@mui/material";
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import {
|
|
||||||
selectDarkMode,
|
|
||||||
selectUserInfo,
|
|
||||||
setDarkMode,
|
|
||||||
updateMe,
|
|
||||||
} from "../app/loginSlice";
|
|
||||||
import { useAppDispatch } from "../app/store";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
export default function Settings() {
|
|
||||||
const userInfo = useSelector(selectUserInfo);
|
|
||||||
const darkModeState = useSelector(selectDarkMode);
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const [isPrivate, setIsPrivate] = useState(false);
|
|
||||||
const [darkModeLocal, setDarkModeLocal] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsPrivate(userInfo.isPrivate);
|
|
||||||
}, [userInfo]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setDarkModeLocal(darkModeState);
|
|
||||||
}, [darkModeState]);
|
|
||||||
|
|
||||||
const handleDarkChange = () => {
|
|
||||||
dispatch(setDarkMode(!darkModeLocal));
|
|
||||||
localStorage.setItem("darkMode", darkModeLocal ? "false" : "true");
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePrivateChange = () => {
|
|
||||||
dispatch(updateMe(!isPrivate));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Box>
|
|
||||||
<Checkbox checked={isPrivate} onChange={handlePrivateChange} /> Private
|
|
||||||
profile
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
<Box>
|
|
||||||
<Checkbox checked={darkModeLocal} onChange={handleDarkChange} /> Dark
|
|
||||||
mode
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
<Box>
|
|
||||||
<Typography variant="h4">More Settings</Typography>
|
|
||||||
<Typography variant="body1">Coming soon...</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -213,9 +213,8 @@ export class UserController {
|
||||||
notifications: true,
|
notifications: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// Strict check, as we are dealing with a boolean value in this field
|
|
||||||
user.isPrivate =
|
user.isPrivate = req.body.isPrivate || user.isPrivate;
|
||||||
req.body.isPrivate === undefined ? user.isPrivate : req.body.isPrivate;
|
|
||||||
user.profilePictureId =
|
user.profilePictureId =
|
||||||
req.body.profilePictureId || user.profilePictureId;
|
req.body.profilePictureId || user.profilePictureId;
|
||||||
await this.userRepository.save(user);
|
await this.userRepository.save(user);
|
||||||
|
|
|
||||||
|
|
@ -51,18 +51,20 @@ export class PostController {
|
||||||
});
|
});
|
||||||
// Remove private, non followed users
|
// Remove private, non followed users
|
||||||
const filteredPosts = posts.filter((post) => {
|
const filteredPosts = posts.filter((post) => {
|
||||||
let followStatus = false;
|
let followStatus = false;
|
||||||
|
|
||||||
// Get if the req user is in the follower list
|
if(post.createdBy.followers){
|
||||||
if (post.createdBy.followers) {
|
|
||||||
followStatus = post.createdBy.followers.some(
|
followStatus = post.createdBy.followers.some(
|
||||||
|
(follower) => follower.id === req.user.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (post.createdBy.isPrivate && !followStatus) {
|
||||||
|
return post.createdBy.followers.some(
|
||||||
(follower) => follower.id === req.user.id
|
(follower) => follower.id === req.user.id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
return !(post.createdBy.isPrivate && !followStatus);
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).send(filteredPosts);
|
res.status(200).send(filteredPosts);
|
||||||
|
|
@ -139,32 +141,20 @@ export class PostController {
|
||||||
const user = await this.userRepository.findOne({
|
const user = await this.userRepository.findOne({
|
||||||
where: { id: req.user.id },
|
where: { id: req.user.id },
|
||||||
relations: {
|
relations: {
|
||||||
followed: { posts: { likedBy: true, comments: true } },
|
followed: true,
|
||||||
posts: {
|
posts: { likedBy: true, comments: true },
|
||||||
likedBy: true,
|
|
||||||
comments: true,
|
|
||||||
createdBy: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get all posts from the followed users
|
|
||||||
const followedPosts = user.followed
|
const followedPosts = user.followed
|
||||||
.map((followedUser) => {
|
.map((followedUser) => followedUser.posts)
|
||||||
followedUser.deleteSensitiveFields();
|
|
||||||
if (followedUser.posts) {
|
|
||||||
return followedUser.posts.map((post) => {
|
|
||||||
if (post.likedBy.length > 0) {
|
|
||||||
post.likedBy.forEach((user) => user.deleteSensitiveFields());
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...post,
|
|
||||||
createdBy: followedUser,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flat();
|
.flat();
|
||||||
|
|
||||||
|
// Remove sensitive fields
|
||||||
|
followedPosts.forEach((post) => {
|
||||||
|
post.deleteSensitiveFields();
|
||||||
|
});
|
||||||
|
|
||||||
res.send(followedPosts);
|
res.send(followedPosts);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,18 +28,12 @@ export class Post {
|
||||||
|
|
||||||
public deleteSensitiveFields(){
|
public deleteSensitiveFields(){
|
||||||
this.createdBy.deleteSensitiveFields()
|
this.createdBy.deleteSensitiveFields()
|
||||||
if (this.likedBy) {
|
if(this.likedBy.length > 0){
|
||||||
if (this.likedBy.length > 0) {
|
this.likedBy.forEach(user => user.deleteSensitiveFields())
|
||||||
this.likedBy.forEach((user) => user.deleteSensitiveFields());
|
}
|
||||||
}
|
if(this.comments.length > 0){
|
||||||
|
this.comments.forEach(comment => comment.createdBy.deleteSensitiveFields())
|
||||||
}
|
}
|
||||||
if (this.comments) {
|
|
||||||
if (this.comments.length > 0) {
|
|
||||||
this.comments.forEach((comment) =>
|
|
||||||
comment.createdBy.deleteSensitiveFields()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue