parent
e6f73932fa
commit
d73bd1dd04
|
|
@ -181,9 +181,7 @@
|
|||
"paths": {
|
||||
"/auth/signup": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Authentication"
|
||||
],
|
||||
"tags": ["Authentication"],
|
||||
"summary": "Sign up a new user",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
|
|
@ -236,9 +234,7 @@
|
|||
},
|
||||
"/auth/login": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Authentication"
|
||||
],
|
||||
"tags": ["Authentication"],
|
||||
"summary": "Log in a user",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
|
|
@ -301,9 +297,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Authentication"
|
||||
],
|
||||
"tags": ["Authentication"],
|
||||
"summary": "Log out the user",
|
||||
"responses": {
|
||||
"200": {
|
||||
|
|
@ -332,9 +326,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Posts"
|
||||
],
|
||||
"tags": ["Posts"],
|
||||
"operationId": "getAllPosts",
|
||||
"summary": "Get all posts",
|
||||
"responses": {
|
||||
|
|
@ -359,9 +351,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Posts"
|
||||
],
|
||||
"tags": ["Posts"],
|
||||
"operationId": "createPost",
|
||||
"summary": "Create a post",
|
||||
"requestBody": {
|
||||
|
|
@ -408,9 +398,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Posts"
|
||||
],
|
||||
"tags": ["Posts"],
|
||||
"operationId": "getPost",
|
||||
"summary": "Get a post by ID",
|
||||
"parameters": [
|
||||
|
|
@ -449,9 +437,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Posts"
|
||||
],
|
||||
"tags": ["Posts"],
|
||||
"operationId": "updatePost",
|
||||
"summary": "Update a post by ID",
|
||||
"parameters": [
|
||||
|
|
@ -510,9 +496,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Posts"
|
||||
],
|
||||
"tags": ["Posts"],
|
||||
"operationId": "deletePost",
|
||||
"summary": "Delete a post by ID",
|
||||
"parameters": [
|
||||
|
|
@ -546,9 +530,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Posts"
|
||||
],
|
||||
"tags": ["Posts"],
|
||||
"operationId": "getFollowedPosts",
|
||||
"summary": "Get posts of followed users",
|
||||
"responses": {
|
||||
|
|
@ -575,9 +557,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Posts"
|
||||
],
|
||||
"tags": ["Posts"],
|
||||
"operationId": "likePost",
|
||||
"summary": "Like a post by ID",
|
||||
"parameters": [
|
||||
|
|
@ -616,9 +596,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Posts"
|
||||
],
|
||||
"tags": ["Posts"],
|
||||
"operationId": "unlikePost",
|
||||
"summary": "Unlike a post by ID",
|
||||
"parameters": [
|
||||
|
|
@ -659,9 +637,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Posts"
|
||||
],
|
||||
"tags": ["Posts"],
|
||||
"operationId": "commentPost",
|
||||
"summary": "Comment on a post by ID",
|
||||
"parameters": [
|
||||
|
|
@ -718,9 +694,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"summary": "Get all users",
|
||||
"responses": {
|
||||
"200": {
|
||||
|
|
@ -746,9 +720,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"summary": "Get a single user",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -842,16 +814,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/users/{id}/follow": {
|
||||
"/users/follow/{id}": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"summary": "Follow a user",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -889,9 +859,7 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"tags": ["Users"],
|
||||
"summary": "Unfollow a user",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1406,40 +1406,6 @@ export class PostsApi extends BaseAPI {
|
|||
*/
|
||||
export const UsersApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @summary Get all users
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
usersGet: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/users`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication bearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Unfollow a user
|
||||
|
|
@ -1447,10 +1413,10 @@ export const UsersApiAxiosParamCreator = function (configuration?: Configuration
|
|||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
usersIdFollowDelete: async (id: number, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
usersFollowIdDelete: async (id: number, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('usersIdFollowDelete', 'id', id)
|
||||
const localVarPath = `/users/{id}/follow`
|
||||
assertParamExists('usersFollowIdDelete', 'id', id)
|
||||
const localVarPath = `/users/follow/{id}`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
|
|
@ -1485,10 +1451,10 @@ export const UsersApiAxiosParamCreator = function (configuration?: Configuration
|
|||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
usersIdFollowPost: async (id: number, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
usersFollowIdPost: async (id: number, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('usersIdFollowPost', 'id', id)
|
||||
const localVarPath = `/users/{id}/follow`
|
||||
assertParamExists('usersFollowIdPost', 'id', id)
|
||||
const localVarPath = `/users/follow/{id}`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
|
|
@ -1507,6 +1473,40 @@ export const UsersApiAxiosParamCreator = function (configuration?: Configuration
|
|||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Get all users
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
usersGet: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/users`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication bearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
|
@ -1638,18 +1638,6 @@ export const UsersApiAxiosParamCreator = function (configuration?: Configuration
|
|||
export const UsersApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = UsersApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @summary Get all users
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async usersGet(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<User>>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.usersGet(options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['UsersApi.usersGet']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Unfollow a user
|
||||
|
|
@ -1657,10 +1645,10 @@ export const UsersApiFp = function(configuration?: Configuration) {
|
|||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async usersIdFollowDelete(id: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<UserWithRelations>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.usersIdFollowDelete(id, options);
|
||||
async usersFollowIdDelete(id: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<UserWithRelations>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.usersFollowIdDelete(id, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['UsersApi.usersIdFollowDelete']?.[localVarOperationServerIndex]?.url;
|
||||
const localVarOperationServerBasePath = operationServerMap['UsersApi.usersFollowIdDelete']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
|
|
@ -1670,10 +1658,22 @@ export const UsersApiFp = function(configuration?: Configuration) {
|
|||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async usersIdFollowPost(id: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<UserWithRelations>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.usersIdFollowPost(id, options);
|
||||
async usersFollowIdPost(id: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<UserWithRelations>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.usersFollowIdPost(id, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['UsersApi.usersIdFollowPost']?.[localVarOperationServerIndex]?.url;
|
||||
const localVarOperationServerBasePath = operationServerMap['UsersApi.usersFollowIdPost']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Get all users
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async usersGet(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<User>>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.usersGet(options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['UsersApi.usersGet']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
|
|
@ -1724,15 +1724,6 @@ export const UsersApiFp = function(configuration?: Configuration) {
|
|||
export const UsersApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = UsersApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @summary Get all users
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
usersGet(options?: any): AxiosPromise<Array<User>> {
|
||||
return localVarFp.usersGet(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Unfollow a user
|
||||
|
|
@ -1740,8 +1731,8 @@ export const UsersApiFactory = function (configuration?: Configuration, basePath
|
|||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
usersIdFollowDelete(id: number, options?: any): AxiosPromise<UserWithRelations> {
|
||||
return localVarFp.usersIdFollowDelete(id, options).then((request) => request(axios, basePath));
|
||||
usersFollowIdDelete(id: number, options?: any): AxiosPromise<UserWithRelations> {
|
||||
return localVarFp.usersFollowIdDelete(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
|
|
@ -1750,8 +1741,17 @@ export const UsersApiFactory = function (configuration?: Configuration, basePath
|
|||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
usersIdFollowPost(id: number, options?: any): AxiosPromise<UserWithRelations> {
|
||||
return localVarFp.usersIdFollowPost(id, options).then((request) => request(axios, basePath));
|
||||
usersFollowIdPost(id: number, options?: any): AxiosPromise<UserWithRelations> {
|
||||
return localVarFp.usersFollowIdPost(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Get all users
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
usersGet(options?: any): AxiosPromise<Array<User>> {
|
||||
return localVarFp.usersGet(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
|
|
@ -1792,17 +1792,6 @@ export const UsersApiFactory = function (configuration?: Configuration, basePath
|
|||
* @extends {BaseAPI}
|
||||
*/
|
||||
export class UsersApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @summary Get all users
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof UsersApi
|
||||
*/
|
||||
public usersGet(options?: RawAxiosRequestConfig) {
|
||||
return UsersApiFp(this.configuration).usersGet(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @summary Unfollow a user
|
||||
|
|
@ -1811,8 +1800,8 @@ export class UsersApi extends BaseAPI {
|
|||
* @throws {RequiredError}
|
||||
* @memberof UsersApi
|
||||
*/
|
||||
public usersIdFollowDelete(id: number, options?: RawAxiosRequestConfig) {
|
||||
return UsersApiFp(this.configuration).usersIdFollowDelete(id, options).then((request) => request(this.axios, this.basePath));
|
||||
public usersFollowIdDelete(id: number, options?: RawAxiosRequestConfig) {
|
||||
return UsersApiFp(this.configuration).usersFollowIdDelete(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1823,8 +1812,19 @@ export class UsersApi extends BaseAPI {
|
|||
* @throws {RequiredError}
|
||||
* @memberof UsersApi
|
||||
*/
|
||||
public usersIdFollowPost(id: number, options?: RawAxiosRequestConfig) {
|
||||
return UsersApiFp(this.configuration).usersIdFollowPost(id, options).then((request) => request(this.axios, this.basePath));
|
||||
public usersFollowIdPost(id: number, options?: RawAxiosRequestConfig) {
|
||||
return UsersApiFp(this.configuration).usersFollowIdPost(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @summary Get all users
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof UsersApi
|
||||
*/
|
||||
public usersGet(options?: RawAxiosRequestConfig) {
|
||||
return UsersApiFp(this.configuration).usersGet(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ interface loginState {
|
|||
firstName: string;
|
||||
lastName: string;
|
||||
jwt: string;
|
||||
id: number;
|
||||
profilePictureId: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -28,6 +30,8 @@ const initialState: loginState = {
|
|||
firstName: "",
|
||||
lastName: "",
|
||||
jwt: "",
|
||||
id: -1,
|
||||
profilePictureId: "",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -37,9 +41,11 @@ export const loginSlice = createSlice({
|
|||
reducers: {
|
||||
login: (state, action) => {
|
||||
state.loggedIn = true;
|
||||
state.userInfo.id = action.payload.id;
|
||||
state.userInfo.jwt = action.payload.jwt;
|
||||
state.userInfo.firstName = action.payload.firstName;
|
||||
state.userInfo.lastName = action.payload.lastName;
|
||||
state.userInfo.profilePictureId = action.payload.profilePictureId;
|
||||
},
|
||||
logoff: (state) => {
|
||||
state.loggedIn = false;
|
||||
|
|
@ -85,6 +91,8 @@ export const postLogin =
|
|||
jwt: response.data.token,
|
||||
firstName: userResponse.data.firstName,
|
||||
lastName: userResponse.data.lastName,
|
||||
id: userResponse.data.id,
|
||||
profilePictureId: userResponse.data.profilePictureId,
|
||||
})
|
||||
);
|
||||
dispatch(setStatus(Status.succeeded));
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export const store = configureStore({
|
|||
post: postReducer,
|
||||
users: usersReducer,
|
||||
},
|
||||
devTools: true,
|
||||
});
|
||||
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export const getUserWithRelations =
|
|||
dispatch(setStatus(Status.loading));
|
||||
try {
|
||||
const response = await api.usersIdGet(userId);
|
||||
dispatch(setUserWithRelations(response));
|
||||
dispatch(setUserWithRelations(response.data));
|
||||
dispatch(setStatus(Status.idle));
|
||||
} catch (error) {
|
||||
dispatch(setError((error as Error).message));
|
||||
|
|
@ -73,10 +73,12 @@ export const followUser =
|
|||
|
||||
dispatch(setStatus(Status.loading));
|
||||
try {
|
||||
await api.usersIdFollowPost(userId);
|
||||
dispatch(getUsers());
|
||||
console.log("Trying to follow: ", userId);
|
||||
await api.usersFollowIdPost(userId);
|
||||
} catch (error) {
|
||||
dispatch(setError((error as Error).message));
|
||||
} finally {
|
||||
dispatch(getUserWithRelations(userId));
|
||||
dispatch(setStatus(Status.idle));
|
||||
}
|
||||
};
|
||||
|
|
@ -88,10 +90,12 @@ export const unFollowUser =
|
|||
|
||||
dispatch(setStatus(Status.loading));
|
||||
try {
|
||||
await api.usersIdFollowDelete(userId);
|
||||
dispatch(getUsers());
|
||||
console.log("Trying to unfollow: ", userId);
|
||||
await api.usersFollowIdDelete(userId);
|
||||
} catch (error) {
|
||||
dispatch(setError((error as Error).message));
|
||||
} finally {
|
||||
dispatch(getUserWithRelations(userId));
|
||||
dispatch(setStatus(Status.idle));
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,31 +28,31 @@ export default function PostListItem(props: PostListItemProps) {
|
|||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
const userInfo = useSelector(selectUserInfo);
|
||||
const [liked, setLiked] = useState(false);
|
||||
const [numberOfLikes, setNumberOfLikes] = useState(0);
|
||||
const [liked, setLiked] = useState(
|
||||
props.post.likedBy?.some((user) => user.id === userInfo.id) || false
|
||||
);
|
||||
const [numberOfLikes, setNumberOfLikes] = useState(
|
||||
props.post.likedBy?.length || 0
|
||||
);
|
||||
|
||||
// On component mount, set the number of likes
|
||||
// and whether the user has liked the post
|
||||
useEffect(() => {
|
||||
setLiked(
|
||||
props.post.likedBy?.some((user) => user.id === userInfo.id) || false
|
||||
);
|
||||
setNumberOfLikes(props.post.likedBy?.length || 0);
|
||||
setLiked(props.post.likedBy?.includes(userInfo) || false);
|
||||
}, []);
|
||||
}, [props.post.likedBy, userInfo.id]);
|
||||
|
||||
const handleLike = () => {
|
||||
if (!liked) {
|
||||
// Instant feedback to the user
|
||||
setLiked(true);
|
||||
setNumberOfLikes(numberOfLikes + 1);
|
||||
// Dispatch the call to the API
|
||||
dispatch(likePost(props.post.id as number));
|
||||
}
|
||||
// If the user has already liked the post, unlike it
|
||||
else {
|
||||
setLiked(false);
|
||||
setNumberOfLikes(numberOfLikes - 1);
|
||||
|
||||
dispatch(unLikePost(props.post.id as number));
|
||||
}
|
||||
setLiked((prevState) => !prevState);
|
||||
};
|
||||
|
||||
const handlePostClick = () => {
|
||||
|
|
@ -72,7 +72,13 @@ export default function PostListItem(props: PostListItemProps) {
|
|||
alignItems: "start",
|
||||
}}
|
||||
>
|
||||
<Button onClick={handleUserClick} sx={{ textTransform: "none" }}>
|
||||
<Button
|
||||
onClick={handleUserClick}
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
display: `${props.postType === "user" ? "none" : "block"}`,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
gutterBottom
|
||||
sx={{ textAlign: "left", width: "100%" }}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const root = ReactDOM.createRoot(
|
|||
|
||||
const defaultTheme = createTheme({
|
||||
palette: {
|
||||
mode: "dark",
|
||||
mode: "light",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@ import {
|
|||
fetchGlobalPosts,
|
||||
selectAllPosts,
|
||||
selectFollowedPosts,
|
||||
selectStatus,
|
||||
} from "../app/postSlice";
|
||||
import { useAppDispatch } from "../app/store";
|
||||
import { useEffect } from "react";
|
||||
import { Box, List, Typography } from "@mui/material";
|
||||
import { store, useAppDispatch } from "../app/store";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Box, CircularProgress, List, Typography } from "@mui/material";
|
||||
import { Post } from "../api";
|
||||
import PostListItem from "../components/postListItem";
|
||||
import { Status } from "../util/types";
|
||||
|
||||
interface PostListProps {
|
||||
type: "all" | "user";
|
||||
|
|
@ -19,12 +21,18 @@ export default function PostList(props: PostListProps) {
|
|||
const dispatch = useAppDispatch();
|
||||
const followedPosts = useSelector(selectFollowedPosts);
|
||||
const globalPosts = useSelector(selectAllPosts);
|
||||
const status = useSelector(selectStatus);
|
||||
const [posts, setPosts] = useState<Post[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.type === "all") dispatch(fetchGlobalPosts());
|
||||
if (props.type === "user") dispatch(fetchFollowedPosts());
|
||||
dispatch(fetchGlobalPosts());
|
||||
dispatch(fetchFollowedPosts());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setPosts(props.type === "all" ? globalPosts : followedPosts);
|
||||
}, [followedPosts, globalPosts, props.type]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
|
|
@ -34,9 +42,13 @@ export default function PostList(props: PostListProps) {
|
|||
display: "flex",
|
||||
}}
|
||||
>
|
||||
{status === Status.loading ? (
|
||||
<CircularProgress />
|
||||
) : (
|
||||
<>
|
||||
{props.type === "all" && (
|
||||
<List>
|
||||
{globalPosts.map((post: Post) => (
|
||||
{posts.map((post: Post) => (
|
||||
<PostListItem post={post} postType="all" key={post.id} />
|
||||
))}
|
||||
</List>
|
||||
|
|
@ -60,14 +72,19 @@ export default function PostList(props: PostListProps) {
|
|||
>
|
||||
Whops!
|
||||
</Typography>
|
||||
<Typography textAlign="center" sx={{ color: "text.secondary" }}>
|
||||
There is no content here! Try following some users, or check the
|
||||
global feed
|
||||
<Typography
|
||||
textAlign="center"
|
||||
sx={{ color: "text.secondary" }}
|
||||
>
|
||||
There is no content here! Try following some users, or check
|
||||
the global feed
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</List>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,155 @@
|
|||
import {
|
||||
Box,
|
||||
Button,
|
||||
CircularProgress,
|
||||
List,
|
||||
Paper,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { AppAvatar } from "../components/appAvatar";
|
||||
import { useAppDispatch } from "../app/store";
|
||||
import { useSelector } from "react-redux";
|
||||
import {
|
||||
followUser,
|
||||
getUserWithRelations,
|
||||
selectAUser,
|
||||
selectStatus,
|
||||
unFollowUser,
|
||||
} from "../app/usersSlice";
|
||||
import { useEffect } from "react";
|
||||
import { selectUserInfo } from "../app/loginSlice";
|
||||
import PostListItem from "../components/postListItem";
|
||||
import { Post, UserWithRelations } from "../api";
|
||||
import React from "react";
|
||||
import { Status } from "../util/types";
|
||||
|
||||
export interface ProfileProps {
|
||||
selfProfile?: boolean;
|
||||
}
|
||||
|
||||
export default function Profile(props: ProfileProps) {
|
||||
const { selfProfile } = props;
|
||||
const userId = useParams<{ userId: string }>().userId;
|
||||
const userId = useParams<{ id: string }>().id;
|
||||
const [userToDisplay, setUserToDisplay] =
|
||||
React.useState<UserWithRelations | null>();
|
||||
const aUser = useSelector(selectAUser);
|
||||
const [isFollowed, setIsFollowed] = React.useState(false);
|
||||
const status = useSelector(selectStatus);
|
||||
const selfUser = useSelector(selectUserInfo);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
return <>Profile</>;
|
||||
useEffect(() => {
|
||||
if (!props.selfProfile && userId) {
|
||||
dispatch(getUserWithRelations(parseInt(userId, 10)));
|
||||
} else {
|
||||
dispatch(getUserWithRelations(selfUser.id));
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setUserToDisplay(aUser);
|
||||
if (!props.selfProfile) {
|
||||
setIsFollowed(
|
||||
aUser?.followers?.some((user) => user.id === selfUser.id) || false
|
||||
);
|
||||
}
|
||||
}, [aUser, props.selfProfile, selfUser.id]);
|
||||
|
||||
const handleFollow = () => {
|
||||
if (isFollowed) {
|
||||
dispatch(unFollowUser(userToDisplay!.id!));
|
||||
} else {
|
||||
dispatch(followUser(userToDisplay!.id!));
|
||||
}
|
||||
setIsFollowed((prevState) => !prevState);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
{status === Status.loading && (
|
||||
<>
|
||||
<Typography variant="h3">Loading...</Typography>
|
||||
<CircularProgress />
|
||||
</>
|
||||
)}
|
||||
{userToDisplay && status === Status.idle && (
|
||||
<>
|
||||
<Paper
|
||||
sx={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
padding: "1rem 1rem",
|
||||
}}
|
||||
>
|
||||
<AppAvatar user={userToDisplay!} />
|
||||
<Typography variant="h4" sx={{ ml: "0.8rem" }}>
|
||||
{userToDisplay?.firstName} {userToDisplay?.lastName}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: "flex", padding: "1rem 1rem" }}>
|
||||
<Box sx={{ display: "flex" }}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ ml: "0.8rem", mr: "0.5rem" }}
|
||||
>
|
||||
Following:
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
{userToDisplay?.followed?.length || 0}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: "flex" }}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ ml: "0.8rem", mr: "0.5rem" }}
|
||||
>
|
||||
Followers:{" "}
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
{userToDisplay?.followers?.length || 0}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: `${props.selfProfile ? "none" : "initial"}`,
|
||||
ml: "0.8rem",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant={isFollowed ? "outlined" : "contained"}
|
||||
onClick={handleFollow}
|
||||
>
|
||||
{isFollowed ? "Unfollow" : "Follow"}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
<List sx={{ width: "75%" }}>
|
||||
{userToDisplay!.posts?.map((post: Post) => (
|
||||
<PostListItem post={post} postType="user" key={post.id} />
|
||||
)) || (
|
||||
<Typography variant="h3">
|
||||
This user has no posts yet!
|
||||
</Typography>
|
||||
)}
|
||||
</List>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,17 @@ export default function Search() {
|
|||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Dispatch the getUsers actions, and set it as the displayUserList
|
||||
// once on the first render
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getUsers());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setDisplayUserList(userList);
|
||||
}, [userList]);
|
||||
|
||||
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const searchValue = event.currentTarget.value;
|
||||
if (searchValue === "") {
|
||||
|
|
@ -39,6 +46,7 @@ export default function Search() {
|
|||
|
||||
const handleClick: MouseEventHandler = (event) => {
|
||||
const userId = event.currentTarget.id;
|
||||
console.log("User Id from the search component: ", userId);
|
||||
navigate(`/user/${userId}`);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ export class UserController {
|
|||
relations: {
|
||||
followed: true,
|
||||
followers: true,
|
||||
posts: true,
|
||||
posts: { likedBy: true },
|
||||
comments: true,
|
||||
},
|
||||
});
|
||||
|
|
@ -89,17 +89,36 @@ export class UserController {
|
|||
if (!user) return next(new AppError("No user found with that ID", 404));
|
||||
|
||||
// remove sensitive fields
|
||||
user.deleteSensitiveFields();
|
||||
user.deletePrivateFields();
|
||||
if (user.posts) {
|
||||
user.posts.forEach((post) => {
|
||||
if (post.likedBy) {
|
||||
post.likedBy.forEach((user) => user.deleteSensitiveFields());
|
||||
}
|
||||
if (post.comments) {
|
||||
post.comments.forEach((comment) =>
|
||||
comment.createdBy.deleteSensitiveFields()
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (user.followed) {
|
||||
user.followed.forEach((followedUser) =>
|
||||
followedUser.deleteSensitiveFields()
|
||||
);
|
||||
}
|
||||
let followStatus = false;
|
||||
if (user.followers) {
|
||||
user.followers.forEach((follower) => follower.deleteSensitiveFields());
|
||||
// Set the followed status of the user
|
||||
followStatus = user.followers.some(
|
||||
(follower) => follower.id === req.user.id
|
||||
);
|
||||
}
|
||||
|
||||
// If the user is private, only return the user
|
||||
// if the requesting user is following
|
||||
const followStatus = user.followers.some(
|
||||
(follower) => follower.id === req.user.id
|
||||
);
|
||||
|
||||
if (user.isPrivate && !followStatus) {
|
||||
user.followed = [];
|
||||
user.followers = [];
|
||||
|
|
@ -206,7 +225,7 @@ export class UserController {
|
|||
|
||||
/**
|
||||
* @swagger
|
||||
* /users/{id}/follow:
|
||||
* /users/follow/{id}:
|
||||
* post:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
|
|
@ -281,7 +300,7 @@ export class UserController {
|
|||
|
||||
/**
|
||||
* @swagger
|
||||
* /users/{id}/follow:
|
||||
* /users/follow/{id}:
|
||||
* delete:
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import {AppError} from "../util/AppError";
|
|||
import {AppRequest} from "../util/AppRequest";
|
||||
|
||||
export class PostController {
|
||||
private postRepository = AppDataSource.getRepository(Post)
|
||||
private postRepository = AppDataSource.getRepository(Post);
|
||||
|
||||
private commentRepository = AppDataSource.getRepository(Comment)
|
||||
private commentRepository = AppDataSource.getRepository(Comment);
|
||||
|
||||
private notificationRepository = AppDataSource.getRepository(Notification)
|
||||
private notificationRepository = AppDataSource.getRepository(Notification);
|
||||
|
||||
private userRepository = AppDataSource.getRepository(User)
|
||||
private userRepository = AppDataSource.getRepository(User);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
|
|
@ -36,17 +36,38 @@ export class PostController {
|
|||
* items:
|
||||
* $ref: '#/components/schemas/Post'
|
||||
*/
|
||||
public getAllPosts = catchAsync(async (_req, res, _next) => {
|
||||
public getAllPosts = catchAsync(async (req: AppRequest, res, _next) => {
|
||||
const posts = await this.postRepository.find({
|
||||
relations: { createdBy: true, likedBy: true, comments: {createdBy: true} },
|
||||
relations: {
|
||||
createdBy: true,
|
||||
likedBy: true,
|
||||
comments: { createdBy: true },
|
||||
},
|
||||
});
|
||||
|
||||
// Remove sensitive fields
|
||||
posts.forEach(post => {
|
||||
post.deleteSensitiveFields()
|
||||
})
|
||||
posts.forEach((post) => {
|
||||
post.deleteSensitiveFields();
|
||||
});
|
||||
// Remove private, non followed users
|
||||
const filteredPosts = posts.filter((post) => {
|
||||
let followStatus = false;
|
||||
|
||||
res.status(200).send(posts);
|
||||
if(post.createdBy.followers){
|
||||
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
|
||||
);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
res.status(200).send(filteredPosts);
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -83,13 +104,14 @@ export class PostController {
|
|||
req
|
||||
);
|
||||
|
||||
if(errorMessage == 'Invalid ID') return next(new AppError('Invalid ID', 400))
|
||||
if(errorMessage == 'No post found with that ID'){
|
||||
return next(new AppError('No post found with that ID', 404))
|
||||
if (errorMessage == "Invalid ID")
|
||||
return next(new AppError("Invalid ID", 400));
|
||||
if (errorMessage == "No post found with that ID") {
|
||||
return next(new AppError("No post found with that ID", 404));
|
||||
}
|
||||
|
||||
// Remove sensitive fields
|
||||
post.deleteSensitiveFields()
|
||||
post.deleteSensitiveFields();
|
||||
|
||||
res.send(post);
|
||||
});
|
||||
|
|
@ -115,7 +137,7 @@ export class PostController {
|
|||
* $ref: '#/components/schemas/Post'
|
||||
*/
|
||||
|
||||
public getFollowedPosts = catchAsync(async (req : AppRequest, res, _next) => {
|
||||
public getFollowedPosts = catchAsync(async (req: AppRequest, res, _next) => {
|
||||
const user = await this.userRepository.findOne({
|
||||
where: { id: req.user.id },
|
||||
relations: {
|
||||
|
|
@ -124,12 +146,14 @@ export class PostController {
|
|||
},
|
||||
});
|
||||
|
||||
const followedPosts = user.followed.map(followedUser => followedUser.posts).flat()
|
||||
const followedPosts = user.followed
|
||||
.map((followedUser) => followedUser.posts)
|
||||
.flat();
|
||||
|
||||
// Remove sensitive fields
|
||||
followedPosts.forEach(post => {
|
||||
post.deleteSensitiveFields()
|
||||
})
|
||||
followedPosts.forEach((post) => {
|
||||
post.deleteSensitiveFields();
|
||||
});
|
||||
|
||||
res.send(followedPosts);
|
||||
});
|
||||
|
|
@ -168,23 +192,26 @@ export class PostController {
|
|||
* description: Title and content are required
|
||||
*/
|
||||
|
||||
public createPost = catchAsync(async (req : AppRequest, res, next) => {
|
||||
const user = await this.userRepository.findOne({where: {id: req.user.id}})
|
||||
const {title, content} = req.body
|
||||
|
||||
if(!title || !content) return next(new AppError('Title and content are required', 400))
|
||||
public createPost = catchAsync(async (req: AppRequest, res, next) => {
|
||||
const user = await this.userRepository.findOne({
|
||||
where: { id: req.user.id },
|
||||
});
|
||||
const { title, content } = req.body;
|
||||
|
||||
if (!title || !content)
|
||||
return next(new AppError("Title and content are required", 400));
|
||||
|
||||
const newPost = Object.assign(new Post(), {
|
||||
title,
|
||||
content,
|
||||
createdBy: user,
|
||||
createdAt: new Date()})
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
const post = await this.postRepository.save(newPost)
|
||||
const post = await this.postRepository.save(newPost);
|
||||
|
||||
// Remove sensitive fields
|
||||
post.deleteSensitiveFields()
|
||||
post.deleteSensitiveFields();
|
||||
|
||||
res.status(201).send(post);
|
||||
});
|
||||
|
|
@ -232,27 +259,32 @@ export class PostController {
|
|||
* description: No post found with that ID
|
||||
*/
|
||||
|
||||
public updatePost = catchAsync(async (req : AppRequest, res, next) => {
|
||||
const {user, post, errorMessage} = await this.validateRequestAndGetEntities(req)
|
||||
const {title, content} = req.body
|
||||
public updatePost = catchAsync(async (req: AppRequest, res, next) => {
|
||||
const { user, post, errorMessage } =
|
||||
await this.validateRequestAndGetEntities(req);
|
||||
const { title, content } = req.body;
|
||||
|
||||
if(!title || !content) return next(new AppError('Title and content are required', 400))
|
||||
if (!title || !content)
|
||||
return next(new AppError("Title and content are required", 400));
|
||||
|
||||
if(errorMessage == 'Invalid ID') return next(new AppError('Invalid ID', 400))
|
||||
if(errorMessage == 'No post found with that ID'){
|
||||
return next(new AppError('No post found with that ID', 404))
|
||||
if (errorMessage == "Invalid ID")
|
||||
return next(new AppError("Invalid ID", 400));
|
||||
if (errorMessage == "No post found with that ID") {
|
||||
return next(new AppError("No post found with that ID", 404));
|
||||
}
|
||||
|
||||
if(post.createdBy.id !== user.id){
|
||||
return next(new AppError('You are not authorized to update this post', 403))
|
||||
if (post.createdBy.id !== user.id) {
|
||||
return next(
|
||||
new AppError("You are not authorized to update this post", 403)
|
||||
);
|
||||
}
|
||||
|
||||
post.title = title
|
||||
post.content = content
|
||||
await this.postRepository.save(post)
|
||||
post.title = title;
|
||||
post.content = content;
|
||||
await this.postRepository.save(post);
|
||||
|
||||
// Remove sensitive fields
|
||||
post.deleteSensitiveFields()
|
||||
post.deleteSensitiveFields();
|
||||
|
||||
res.status(200).send(post);
|
||||
});
|
||||
|
|
@ -283,14 +315,19 @@ export class PostController {
|
|||
* description: No post found with that ID
|
||||
*/
|
||||
|
||||
public deletePost = catchAsync(async (req : AppRequest, res, next) => {
|
||||
const {user, post, errorMessage} = await this.validateRequestAndGetEntities(req)
|
||||
public deletePost = catchAsync(async (req: AppRequest, res, next) => {
|
||||
const { user, post, errorMessage } =
|
||||
await this.validateRequestAndGetEntities(req);
|
||||
|
||||
if(errorMessage == 'Invalid ID') return next(new AppError('Invalid ID', 400))
|
||||
if(errorMessage == 'No post found with that ID') return next(new AppError('No post found with that ID', 404))
|
||||
if (errorMessage == "Invalid ID")
|
||||
return next(new AppError("Invalid ID", 400));
|
||||
if (errorMessage == "No post found with that ID")
|
||||
return next(new AppError("No post found with that ID", 404));
|
||||
|
||||
if(post.createdBy.id !== user.id){
|
||||
return next(new AppError('You are not authorized to delete this post', 403))
|
||||
if (post.createdBy.id !== user.id) {
|
||||
return next(
|
||||
new AppError("You are not authorized to delete this post", 403)
|
||||
);
|
||||
}
|
||||
|
||||
await this.postRepository.remove(post);
|
||||
|
|
@ -327,34 +364,38 @@ export class PostController {
|
|||
* description: No post found with that ID
|
||||
*/
|
||||
|
||||
public likePost = catchAsync(async (req : AppRequest, res, next) => {
|
||||
const {user, post, errorMessage} = await this.validateRequestAndGetEntities(req)
|
||||
public likePost = catchAsync(async (req: AppRequest, res, next) => {
|
||||
const { user, post, errorMessage } =
|
||||
await this.validateRequestAndGetEntities(req);
|
||||
|
||||
if(errorMessage == 'Invalid ID') return next(new AppError('Invalid ID', 400))
|
||||
if(errorMessage == 'No post found with that ID'){
|
||||
return next(new AppError('No post found with that ID', 404))
|
||||
if (errorMessage == "Invalid ID")
|
||||
return next(new AppError("Invalid ID", 400));
|
||||
if (errorMessage == "No post found with that ID") {
|
||||
return next(new AppError("No post found with that ID", 404));
|
||||
}
|
||||
|
||||
// Check if user has already liked the post
|
||||
if(post.likedBy.some(likedUser => likedUser.id === user.id)){
|
||||
return next(new AppError('You have already liked this post', 400))
|
||||
if (post.likedBy.some((likedUser) => likedUser.id === user.id)) {
|
||||
return next(new AppError("You have already liked this post", 400));
|
||||
}
|
||||
|
||||
post.likedBy.push(user)
|
||||
await this.postRepository.save(post)
|
||||
post.likedBy.push(user);
|
||||
await this.postRepository.save(post);
|
||||
|
||||
// Send notification to post creator
|
||||
const newNotification = Object.assign(new Notification(),{
|
||||
const newNotification = Object.assign(new Notification(), {
|
||||
message: `${user.firstName} ${user.lastName} liked your post`,
|
||||
belongsTo: post.createdBy,
|
||||
timeStamp: new Date()
|
||||
})
|
||||
const notification = await this.notificationRepository.save(newNotification)
|
||||
post.createdBy.notifications.push(notification)
|
||||
await this.userRepository.save(post.createdBy)
|
||||
timeStamp: new Date(),
|
||||
});
|
||||
const notification = await this.notificationRepository.save(
|
||||
newNotification
|
||||
);
|
||||
post.createdBy.notifications.push(notification);
|
||||
await this.userRepository.save(post.createdBy);
|
||||
|
||||
// Remove sensitive fields
|
||||
post.deleteSensitiveFields()
|
||||
post.deleteSensitiveFields();
|
||||
|
||||
res.status(200).send(post);
|
||||
});
|
||||
|
|
@ -389,22 +430,25 @@ export class PostController {
|
|||
* description: No post found with that ID
|
||||
*/
|
||||
|
||||
public unlikePost = catchAsync(async (req : AppRequest, res, next) => {
|
||||
const {user, post, errorMessage} = await this.validateRequestAndGetEntities(req)
|
||||
public unlikePost = catchAsync(async (req: AppRequest, res, next) => {
|
||||
const { user, post, errorMessage } =
|
||||
await this.validateRequestAndGetEntities(req);
|
||||
|
||||
if(errorMessage == 'Invalid ID') return next(new AppError('Invalid ID', 400))
|
||||
if(errorMessage == 'No post found with that ID') return next(new AppError('No post found with that ID', 404))
|
||||
if (errorMessage == "Invalid ID")
|
||||
return next(new AppError("Invalid ID", 400));
|
||||
if (errorMessage == "No post found with that ID")
|
||||
return next(new AppError("No post found with that ID", 404));
|
||||
|
||||
// Check if user likes the post
|
||||
if(!post.likedBy.some(likedUser => likedUser.id === user.id)){
|
||||
return next(new AppError('You have not liked this post', 400))
|
||||
if (!post.likedBy.some((likedUser) => likedUser.id === user.id)) {
|
||||
return next(new AppError("You have not liked this post", 400));
|
||||
}
|
||||
|
||||
post.likedBy = post.likedBy.filter(likedUser => likedUser.id !== user.id)
|
||||
await this.postRepository.save(post)
|
||||
post.likedBy = post.likedBy.filter((likedUser) => likedUser.id !== user.id);
|
||||
await this.postRepository.save(post);
|
||||
|
||||
// Remove sensitive fields
|
||||
post.deleteSensitiveFields()
|
||||
post.deleteSensitiveFields();
|
||||
|
||||
res.status(200).send(post);
|
||||
});
|
||||
|
|
@ -449,64 +493,66 @@ export class PostController {
|
|||
* description: No post found with that ID
|
||||
*/
|
||||
|
||||
public commentPost = catchAsync(async (req : AppRequest, res, next) => {
|
||||
const {user, post, errorMessage} = await this.validateRequestAndGetEntities(req)
|
||||
public commentPost = catchAsync(async (req: AppRequest, res, next) => {
|
||||
const { user, post, errorMessage } =
|
||||
await this.validateRequestAndGetEntities(req);
|
||||
|
||||
if(errorMessage == 'Invalid ID') return next(new AppError('Invalid ID', 400))
|
||||
if(errorMessage == 'No post found with that ID') {
|
||||
return next(new AppError('No post found with that ID', 404))
|
||||
if (errorMessage == "Invalid ID")
|
||||
return next(new AppError("Invalid ID", 400));
|
||||
if (errorMessage == "No post found with that ID") {
|
||||
return next(new AppError("No post found with that ID", 404));
|
||||
}
|
||||
|
||||
const {content} = req.body
|
||||
const { content } = req.body;
|
||||
const newComment = Object.assign(new Comment(), {
|
||||
content,
|
||||
createdBy: user,
|
||||
post,
|
||||
createdAt: new Date()
|
||||
})
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
const comment = await this.commentRepository.save(newComment)
|
||||
const comment = await this.commentRepository.save(newComment);
|
||||
|
||||
// Send notification to post creator
|
||||
const newNotification = Object.assign(new Notification(),{
|
||||
const newNotification = Object.assign(new Notification(), {
|
||||
message: `${user.firstName} ${user.lastName} commented on your post`,
|
||||
belongsTo: post.createdBy,
|
||||
timeStamp: new Date()
|
||||
})
|
||||
await this.notificationRepository.save(newNotification)
|
||||
timeStamp: new Date(),
|
||||
});
|
||||
await this.notificationRepository.save(newNotification);
|
||||
|
||||
// Remove sensitive fields
|
||||
comment.createdBy.deleteSensitiveFields()
|
||||
post.deleteSensitiveFields()
|
||||
comment.createdBy.deleteSensitiveFields();
|
||||
post.deleteSensitiveFields();
|
||||
|
||||
res.status(201).send(comment)
|
||||
})
|
||||
res.status(201).send(comment);
|
||||
});
|
||||
|
||||
|
||||
private validateRequestAndGetEntities = async (req : AppRequest,) : Promise<validatedEntities> => {
|
||||
const postId = req.params.id
|
||||
const parsedId = parseInt(postId)
|
||||
let errorMessage: 'Invalid ID' | 'No post found with that ID'
|
||||
private validateRequestAndGetEntities = async (
|
||||
req: AppRequest
|
||||
): Promise<validatedEntities> => {
|
||||
const postId = req.params.id;
|
||||
const parsedId = parseInt(postId);
|
||||
let errorMessage: "Invalid ID" | "No post found with that ID";
|
||||
|
||||
// Check if ID is a number
|
||||
if(isNaN(parsedId)) errorMessage = 'Invalid ID'
|
||||
if (isNaN(parsedId)) errorMessage = "Invalid ID";
|
||||
|
||||
const user = req.user
|
||||
const user = req.user;
|
||||
const post = await this.postRepository.findOne({
|
||||
where: {id: parsedId},
|
||||
where: { id: parsedId },
|
||||
relations: {
|
||||
likedBy: true,
|
||||
comments: { createdBy: true },
|
||||
createdBy: {notifications: true}
|
||||
}
|
||||
})
|
||||
createdBy: { notifications: true },
|
||||
},
|
||||
});
|
||||
|
||||
// Check if post exists
|
||||
if(!post) errorMessage = 'No post found with that ID'
|
||||
if (!post) errorMessage = "No post found with that ID";
|
||||
|
||||
return {user, post, errorMessage}
|
||||
|
||||
}
|
||||
return { user, post, errorMessage };
|
||||
};
|
||||
}
|
||||
interface validatedEntities {
|
||||
user: User
|
||||
|
|
|
|||
|
|
@ -53,10 +53,16 @@ export class User {
|
|||
}
|
||||
|
||||
public deleteSensitiveFields() {
|
||||
delete this.password;
|
||||
delete this.email;
|
||||
delete this.notifications;
|
||||
this.deleteExtraFields();
|
||||
this.deletePrivateFields();
|
||||
}
|
||||
public deleteExtraFields() {
|
||||
delete this.followed;
|
||||
delete this.followers;
|
||||
}
|
||||
public deletePrivateFields(){
|
||||
delete this.email;
|
||||
delete this.password;
|
||||
delete this.notifications;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue