🏗️ Views and routes boilerplate
Signed-off-by: Pau Costa <mico@micodev.es>pull/2/head
parent
31ba7bead1
commit
591dae9567
|
|
@ -5,9 +5,9 @@ import {
|
||||||
AuthSignupPostRequest,
|
AuthSignupPostRequest,
|
||||||
AuthenticationApi,
|
AuthenticationApi,
|
||||||
Configuration,
|
Configuration,
|
||||||
|
UsersApi,
|
||||||
} from "../api";
|
} from "../api";
|
||||||
import { AppThunk } from "./store";
|
import { AppThunk } from "./store";
|
||||||
import { Axios, AxiosError } from "axios";
|
|
||||||
|
|
||||||
interface loginState {
|
interface loginState {
|
||||||
loggedIn: boolean;
|
loggedIn: boolean;
|
||||||
|
|
@ -15,6 +15,7 @@ interface loginState {
|
||||||
error: string | null;
|
error: string | null;
|
||||||
userInfo: {
|
userInfo: {
|
||||||
firstName: string;
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
jwt: string;
|
jwt: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -25,6 +26,7 @@ const initialState: loginState = {
|
||||||
error: null,
|
error: null,
|
||||||
userInfo: {
|
userInfo: {
|
||||||
firstName: "",
|
firstName: "",
|
||||||
|
lastName: "",
|
||||||
jwt: "",
|
jwt: "",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -35,7 +37,9 @@ export const loginSlice = createSlice({
|
||||||
reducers: {
|
reducers: {
|
||||||
login: (state, action) => {
|
login: (state, action) => {
|
||||||
state.loggedIn = true;
|
state.loggedIn = true;
|
||||||
state.userInfo.jwt = action.payload;
|
state.userInfo.jwt = action.payload.jwt;
|
||||||
|
state.userInfo.firstName = action.payload.firstName;
|
||||||
|
state.userInfo.lastName = action.payload.lastName;
|
||||||
},
|
},
|
||||||
logoff: (state) => {
|
logoff: (state) => {
|
||||||
state.loggedIn = false;
|
state.loggedIn = false;
|
||||||
|
|
@ -50,7 +54,7 @@ export const loginSlice = createSlice({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const api = new AuthenticationApi(
|
const authApi = new AuthenticationApi(
|
||||||
new Configuration({
|
new Configuration({
|
||||||
basePath: process.env.REACT_APP_BACKEND_URL,
|
basePath: process.env.REACT_APP_BACKEND_URL,
|
||||||
})
|
})
|
||||||
|
|
@ -59,15 +63,30 @@ const api = new AuthenticationApi(
|
||||||
export const postLogin =
|
export const postLogin =
|
||||||
(params: AuthLoginPostRequest): AppThunk =>
|
(params: AuthLoginPostRequest): AppThunk =>
|
||||||
async (dispatch) => {
|
async (dispatch) => {
|
||||||
let response;
|
let response, userResponse;
|
||||||
try {
|
try {
|
||||||
dispatch(setStatus(Status.loading));
|
dispatch(setStatus(Status.loading));
|
||||||
response = await api.authLoginPost(params);
|
response = await authApi.authLoginPost(params);
|
||||||
|
|
||||||
dispatch(login(response.data.token));
|
|
||||||
await addJWT(response.data.token || "");
|
await addJWT(response.data.token || "");
|
||||||
|
|
||||||
dispatch(setError(""));
|
// Get user info
|
||||||
|
const userApi = new UsersApi(
|
||||||
|
new Configuration({
|
||||||
|
basePath: process.env.REACT_APP_BACKEND_URL,
|
||||||
|
accessToken: response.data.token,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
userResponse = await userApi.usersMeGet();
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
login({
|
||||||
|
jwt: response.data.token,
|
||||||
|
firstName: userResponse.data.firstName,
|
||||||
|
lastName: userResponse.data.lastName,
|
||||||
|
})
|
||||||
|
);
|
||||||
dispatch(setStatus(Status.succeeded));
|
dispatch(setStatus(Status.succeeded));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dispatch(setStatus(Status.failed));
|
dispatch(setStatus(Status.failed));
|
||||||
|
|
@ -83,7 +102,7 @@ export const postSignup =
|
||||||
console.log(params);
|
console.log(params);
|
||||||
try {
|
try {
|
||||||
dispatch(setStatus(Status.loading));
|
dispatch(setStatus(Status.loading));
|
||||||
response = await api.authSignupPost(params);
|
response = await authApi.authSignupPost(params);
|
||||||
|
|
||||||
dispatch(postLogin({ email: params.email, password: params.password }));
|
dispatch(postLogin({ email: params.email, password: params.password }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -97,7 +116,7 @@ export const postLogout = (): AppThunk => async (dispatch) => {
|
||||||
localStorage.removeItem("jwt");
|
localStorage.removeItem("jwt");
|
||||||
sessionStorage.removeItem("jwt");
|
sessionStorage.removeItem("jwt");
|
||||||
dispatch(logoff());
|
dispatch(logoff());
|
||||||
await api.authLogoutGet();
|
await authApi.authLogoutGet();
|
||||||
};
|
};
|
||||||
|
|
||||||
const addJWT = async (token: string) => {
|
const addJWT = async (token: string) => {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { Store, createSlice } from "@reduxjs/toolkit";
|
||||||
|
import { Configuration, Post, PostsApi } from "../api";
|
||||||
|
import { Status } from "../util/types";
|
||||||
|
import { store } from "./store";
|
||||||
|
|
||||||
|
interface postSlice {
|
||||||
|
status: Status;
|
||||||
|
followedPosts: Post[];
|
||||||
|
globalPosts: Post[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: postSlice = {
|
||||||
|
status: Status.idle,
|
||||||
|
followedPosts: [],
|
||||||
|
globalPosts: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const postSlice = createSlice({
|
||||||
|
name: "post",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setStatus: (state, action) => {
|
||||||
|
state.status = action.payload;
|
||||||
|
},
|
||||||
|
setFollowedPosts: (state, action) => {
|
||||||
|
state.followedPosts = action.payload;
|
||||||
|
},
|
||||||
|
setGlobalPosts: (state, action) => {
|
||||||
|
state.globalPosts = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fetchFollowedPosts = () => async (dispatch: any) => {
|
||||||
|
const postApi = createApi(store);
|
||||||
|
|
||||||
|
dispatch(setStatus(Status.loading));
|
||||||
|
const response = await postApi.getFollowedPosts();
|
||||||
|
dispatch(setFollowedPosts(response.data));
|
||||||
|
dispatch(setStatus(Status.idle));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchGlobalPosts = () => async (dispatch: any) => {
|
||||||
|
const postApi = createApi(store);
|
||||||
|
|
||||||
|
dispatch(setStatus(Status.loading));
|
||||||
|
const response = await postApi.getAllPosts();
|
||||||
|
dispatch(setGlobalPosts(response.data));
|
||||||
|
dispatch(setStatus(Status.idle));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const { setFollowedPosts, setGlobalPosts, setStatus } =
|
||||||
|
postSlice.actions;
|
||||||
|
|
||||||
|
export default postSlice.reducer;
|
||||||
|
|
||||||
|
export const selectFollowedPosts = (state: any) => state.post.followedPosts;
|
||||||
|
export const selectAllPosts = (state: any) => state.post.globalPosts;
|
||||||
|
export const selectStatus = (state: any) => state.post.status;
|
||||||
|
|
||||||
|
function createApi(store: Store) {
|
||||||
|
return new PostsApi(
|
||||||
|
new Configuration({
|
||||||
|
basePath: process.env.REACT_APP_BACKEND_URL,
|
||||||
|
accessToken: store.getState().login.userInfo.jwt,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import { Action, ThunkAction, configureStore } from "@reduxjs/toolkit";
|
import { Action, ThunkAction, configureStore } from "@reduxjs/toolkit";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import loginReducer from "./loginSlice";
|
import loginReducer from "./loginSlice";
|
||||||
|
import postReducer from "./postSlice";
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
login: loginReducer,
|
login: loginReducer,
|
||||||
|
post: postReducer,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,15 @@ 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 { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
export default function BottomAppBar() {
|
export default function BottomAppBar() {
|
||||||
const [value, setValue] = useState(0);
|
const [value, setValue] = useState(0);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleClick = (to: string) => () => {
|
||||||
|
navigate(to);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
sx={{ position: "fixed", bottom: 0, left: 0, right: 0 }}
|
sx={{ position: "fixed", bottom: 0, left: 0, right: 0 }}
|
||||||
|
|
@ -20,10 +25,26 @@ export default function BottomAppBar() {
|
||||||
setValue(newValue);
|
setValue(newValue);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BottomNavigationAction label="My Feed" icon={<FeedIcon />} />
|
<BottomNavigationAction
|
||||||
<BottomNavigationAction label="Global Feed" icon={<GlobalIcon />} />
|
label="My Feed"
|
||||||
<BottomNavigationAction label="New Post" icon={<AddIcon />} />
|
icon={<FeedIcon />}
|
||||||
<BottomNavigationAction label="Search" icon={<SearchIcon />} />
|
onClick={handleClick("feed")}
|
||||||
|
/>
|
||||||
|
<BottomNavigationAction
|
||||||
|
label="Global Feed"
|
||||||
|
icon={<GlobalIcon />}
|
||||||
|
onClick={handleClick("global")}
|
||||||
|
/>
|
||||||
|
<BottomNavigationAction
|
||||||
|
label="New Post"
|
||||||
|
icon={<AddIcon />}
|
||||||
|
onClick={handleClick("newpost")}
|
||||||
|
/>
|
||||||
|
<BottomNavigationAction
|
||||||
|
label="Search"
|
||||||
|
icon={<SearchIcon />}
|
||||||
|
onClick={handleClick("search")}
|
||||||
|
/>
|
||||||
</BottomNavigation>
|
</BottomNavigation>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { ListItem, ListItemText } from "@mui/material";
|
||||||
|
import { Post } from "../api";
|
||||||
|
|
||||||
|
interface PostListItemProps {
|
||||||
|
post: Post;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PostListItem(props: PostListItemProps) {
|
||||||
|
console.log(props.post);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem>
|
||||||
|
<ListItemText
|
||||||
|
primary={props.post.title}
|
||||||
|
secondary={`${props.post.createdBy?.firstName} ${props.post.createdBy?.lastName}`}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ 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 { Link, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
export interface SideAppBarProps {
|
export interface SideAppBarProps {
|
||||||
drawerWidth: number;
|
drawerWidth: number;
|
||||||
|
|
@ -20,6 +21,12 @@ export interface SideAppBarProps {
|
||||||
|
|
||||||
export default function SideAppBar(props: SideAppBarProps) {
|
export default function SideAppBar(props: SideAppBarProps) {
|
||||||
const { drawerWidth } = props;
|
const { drawerWidth } = props;
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleClick = (to: string) => () => {
|
||||||
|
navigate(to);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
sx={{
|
sx={{
|
||||||
|
|
@ -37,7 +44,7 @@ export default function SideAppBar(props: SideAppBarProps) {
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<List>
|
<List>
|
||||||
<ListItem key={"myfeed"} disablePadding>
|
<ListItem key={"myfeed"} disablePadding onClick={handleClick("feed")}>
|
||||||
<ListItemButton>
|
<ListItemButton>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<FeedIcon />
|
<FeedIcon />
|
||||||
|
|
@ -46,7 +53,11 @@ export default function SideAppBar(props: SideAppBarProps) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem key={"globalfeed"} disablePadding>
|
<ListItem
|
||||||
|
key={"globalfeed"}
|
||||||
|
disablePadding
|
||||||
|
onClick={handleClick("global")}
|
||||||
|
>
|
||||||
<ListItemButton>
|
<ListItemButton>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<GlobalIcon />
|
<GlobalIcon />
|
||||||
|
|
@ -55,7 +66,11 @@ export default function SideAppBar(props: SideAppBarProps) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem key={"newpost"} disablePadding>
|
<ListItem
|
||||||
|
key={"newpost"}
|
||||||
|
disablePadding
|
||||||
|
onClick={handleClick("newpost")}
|
||||||
|
>
|
||||||
<ListItemButton>
|
<ListItemButton>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
|
|
@ -64,7 +79,7 @@ export default function SideAppBar(props: SideAppBarProps) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem key={"search"} disablePadding>
|
<ListItem key={"search"} disablePadding onClick={handleClick("search")}>
|
||||||
<ListItemButton>
|
<ListItemButton>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<SearchIcon />
|
<SearchIcon />
|
||||||
|
|
@ -72,7 +87,6 @@ export default function SideAppBar(props: SideAppBarProps) {
|
||||||
<ListItemText primary={"Search"} />
|
<ListItemText primary={"Search"} />
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
</List>
|
</List>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,21 @@ import Tooltip from "@mui/material/Tooltip";
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
import NotificationBell from "./notificationBell";
|
import NotificationBell from "./notificationBell";
|
||||||
import {useSelector} from "react-redux";
|
import {useSelector} from "react-redux";
|
||||||
import {selectUserInfo} from "../app/loginSlice";
|
import { postLogout, selectUserInfo } from "../app/loginSlice";
|
||||||
|
import { useAppDispatch } from "../app/store";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
const settings = ["Profile", "Account", "Dashboard", "Logout"];
|
const settings = ["Profile", "Account", "Dashboard", "Logout"];
|
||||||
|
|
||||||
function TopAppBar() {
|
function TopAppBar() {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
const userInfo = useSelector(selectUserInfo);
|
const navigate = useNavigate();
|
||||||
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(
|
const userInfo = useSelector(selectUserInfo);
|
||||||
|
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(userInfo)
|
console.log(userInfo);
|
||||||
|
|
||||||
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorElUser(event.currentTarget);
|
setAnchorElUser(event.currentTarget);
|
||||||
|
|
@ -32,6 +35,11 @@ function TopAppBar() {
|
||||||
setAnchorElUser(null);
|
setAnchorElUser(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
dispatch(postLogout());
|
||||||
|
setAnchorElUser(null);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar position="static">
|
<AppBar position="static">
|
||||||
<Container maxWidth="xl" sx={{ zIndex: 2500 }}>
|
<Container maxWidth="xl" sx={{ zIndex: 2500 }}>
|
||||||
|
|
@ -67,7 +75,10 @@ function TopAppBar() {
|
||||||
<Box sx={{ flexGrow: 0 }}>
|
<Box sx={{ flexGrow: 0 }}>
|
||||||
<Tooltip title="Open settings">
|
<Tooltip title="Open settings">
|
||||||
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
|
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
|
||||||
<Avatar alt={userInfo.firstName} src="/static/images/avatar/2.jpg" />
|
<Avatar
|
||||||
|
alt={`${userInfo.firstName} ${userInfo.lastName}`}
|
||||||
|
src="/static/images/avatar/2.jpg"
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Menu
|
<Menu
|
||||||
|
|
@ -86,11 +97,15 @@ function TopAppBar() {
|
||||||
open={Boolean(anchorElUser)}
|
open={Boolean(anchorElUser)}
|
||||||
onClose={handleCloseUserMenu}
|
onClose={handleCloseUserMenu}
|
||||||
>
|
>
|
||||||
{settings.map((setting) => (
|
<MenuItem key={"profile"} onClick={handleCloseUserMenu}>
|
||||||
<MenuItem key={setting} onClick={handleCloseUserMenu}>
|
<Typography textAlign="center">{"Profile"}</Typography>
|
||||||
<Typography textAlign="center">{setting}</Typography>
|
</MenuItem>
|
||||||
</MenuItem>
|
<MenuItem key={"settings"} onClick={handleCloseUserMenu}>
|
||||||
))}
|
<Typography textAlign="center">{"Settings"}</Typography>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem key={"logout"} onClick={handleLogout}>
|
||||||
|
<Typography textAlign="center">{"Logout"}</Typography>
|
||||||
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</Box>
|
</Box>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,11 @@ import Register from "./routes/Auth/register";
|
||||||
import AuthRoot from "./routes/Auth/authRoot";
|
import AuthRoot from "./routes/Auth/authRoot";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import { store } from "./app/store";
|
import { store } from "./app/store";
|
||||||
|
import PostList from "./routes/postList";
|
||||||
|
import Profile from "./routes/profile";
|
||||||
|
import Post from "./routes/post";
|
||||||
|
import NewPost from "./routes/newPost";
|
||||||
|
import Search from "./routes/search";
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById("root") as HTMLElement
|
document.getElementById("root") as HTMLElement
|
||||||
|
|
@ -28,6 +33,36 @@ const router = createBrowserRouter([
|
||||||
path: "/",
|
path: "/",
|
||||||
element: <Root />,
|
element: <Root />,
|
||||||
errorElement: <ErrorPage />,
|
errorElement: <ErrorPage />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "feed",
|
||||||
|
element: <PostList type="user" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "global",
|
||||||
|
element: <PostList type="all" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "me",
|
||||||
|
element: <Profile selfProfile />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "user/:id",
|
||||||
|
element: <Profile />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "post/:id",
|
||||||
|
element: <Post />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "search",
|
||||||
|
element: <Search />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "newpost",
|
||||||
|
element: <NewPost />,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/auth",
|
path: "/auth",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function NewPost() {
|
||||||
|
return <>New Post</>;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Notification() {
|
||||||
|
return <>Notification</>;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Post() {
|
||||||
|
return <>Post</>;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import {
|
||||||
|
fetchFollowedPosts,
|
||||||
|
fetchGlobalPosts,
|
||||||
|
selectAllPosts,
|
||||||
|
selectFollowedPosts,
|
||||||
|
} from "../app/postSlice";
|
||||||
|
import { store, useAppDispatch } from "../app/store";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { List, ListItem, ListItemText } from "@mui/material";
|
||||||
|
import { Post } from "../api";
|
||||||
|
import PostListItem from "../components/postListItem";
|
||||||
|
|
||||||
|
interface PostListProps {
|
||||||
|
type: "all" | "user";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PostList(props: PostListProps) {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const followedPosts = useSelector(selectFollowedPosts);
|
||||||
|
const globalPosts = useSelector(selectAllPosts);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.type === "all") dispatch(fetchGlobalPosts());
|
||||||
|
if (props.type === "user") dispatch(fetchFollowedPosts());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
console.log(globalPosts);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{props.type === "all" && (
|
||||||
|
<List>
|
||||||
|
{globalPosts.map((post: Post) => (
|
||||||
|
<PostListItem post={post} key={post.id} />
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
)}
|
||||||
|
{props.type === "user" && <List></List>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
|
export interface ProfileProps {
|
||||||
|
selfProfile?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Profile(props: ProfileProps) {
|
||||||
|
const { selfProfile } = props;
|
||||||
|
const userId = useParams<{ userId: string }>().userId;
|
||||||
|
|
||||||
|
return <>Profile</>;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Search() {
|
||||||
|
return <>Search</>;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue