From dce969b5e6fdbb6e0bf392a073ae3c78a57b1159 Mon Sep 17 00:00:00 2001 From: SB2318 Date: Sat, 28 Feb 2026 01:10:18 +0530 Subject: [PATCH 1/6] --- --- frontend/src/hooks/useUserLogin.ts | 30 ++++++++++++++++++++ frontend/src/hooks/useUserRegistration.ts | 34 +++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 frontend/src/hooks/useUserLogin.ts create mode 100644 frontend/src/hooks/useUserRegistration.ts diff --git a/frontend/src/hooks/useUserLogin.ts b/frontend/src/hooks/useUserLogin.ts new file mode 100644 index 0000000..2d9915b --- /dev/null +++ b/frontend/src/hooks/useUserLogin.ts @@ -0,0 +1,30 @@ +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import axios, {AxiosError} from 'axios'; +import {LOGIN_API} from '../helper/APIUtils'; +import {User} from '../type'; + +type LoginReq = { + email: string; + password: string; + fcmToken: string; +}; + +const loginFunc = async ({email, password, fcmToken}: LoginReq) => { + const res = await axios.post(LOGIN_API, { + email, + password, + fcmToken, + }); + return res.data.user as User; +}; + +export const useLoginMutation = (): UseMutationResult< + User, + AxiosError, + LoginReq +> => { + return useMutation({ + mutationKey: ['user_login'], + mutationFn: loginFunc, + }); +}; diff --git a/frontend/src/hooks/useUserRegistration.ts b/frontend/src/hooks/useUserRegistration.ts new file mode 100644 index 0000000..b99ef38 --- /dev/null +++ b/frontend/src/hooks/useUserRegistration.ts @@ -0,0 +1,34 @@ +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import axios, {AxiosError} from 'axios'; +import {REGISTRATION_API} from '../helper/APIUtils'; +import {Contactdetail} from '../type'; + +type RegdReq = { + user_name: string; + user_handle: string; + email: string; + password: string; + isDoctor: boolean; + Profile_image: string | null; + specialization?: string | null; + qualification?: string | null; + Years_of_experience?: string | null; + + contact_detail?: Contactdetail; +}; + +const regdFunc = async (request: RegdReq) => { + const res = await axios.post(REGISTRATION_API, request); + return res.data.token as string; +}; + +export const useRegdMutation = (): UseMutationResult< + string, + AxiosError, + RegdReq +> => { + return useMutation({ + mutationKey: ['user_registration'], + mutationFn: regdFunc, + }); +}; From 8f69b317635ad8b205d83b26c8f1e5a11ac6bd6a Mon Sep 17 00:00:00 2001 From: SB2318 Date: Sun, 8 Mar 2026 20:46:11 +0530 Subject: [PATCH 2/6] --work--start--on--hook--structure --- frontend/src/components/AppContent.tsx | 10 ++++-- frontend/src/hooks/useChangePassword.ts | 24 ++++++++++++++ frontend/src/hooks/useGetArticleDetail.ts | 17 ++++++++++ frontend/src/hooks/useGetArticleTags.ts | 28 ++++++++++++++++ frontend/src/hooks/useGetProfile.ts | 14 ++++++++ frontend/src/hooks/useGetProfileImageById.ts | 18 +++++++++++ frontend/{ => src}/hooks/useGetTokenStatus.ts | 0 .../src/hooks/useGetTotalLikeViewStatus.ts | 0 frontend/src/hooks/useGetUserDetails.ts | 20 ++++++++++++ frontend/src/hooks/useLikeArticle.ts | 29 +++++++++++++++++ frontend/src/hooks/useMailVerification.ts | 26 +++++++++++++++ frontend/src/hooks/usePostArticle.ts | 32 +++++++++++++++++++ frontend/src/hooks/useResendVerification.ts | 20 ++++++++++++ frontend/src/hooks/useSaveArticle.ts | 26 +++++++++++++++ frontend/src/hooks/useSendOtp.ts | 19 +++++++++++ frontend/src/hooks/useUpdateFollowStatus.ts | 26 +++++++++++++++ frontend/src/hooks/useUpdatePassword.ts | 28 ++++++++++++++++ frontend/src/hooks/useUpdateProfileImage.ts | 24 ++++++++++++++ frontend/src/hooks/useUpdateReadEvent.ts | 24 ++++++++++++++ .../src/hooks/useUpdateUserContactDetail.ts | 24 ++++++++++++++ .../src/hooks/useUpdateUserGeneralDetails.ts | 30 +++++++++++++++++ .../src/hooks/useUpdateUserProfDetails.ts | 31 ++++++++++++++++++ frontend/src/hooks/useUpdateViewCount.ts | 20 ++++++++++++ frontend/{ => src}/hooks/useUploadAudio.tsx | 0 frontend/{ => src}/hooks/useUploadImage.tsx | 0 frontend/src/hooks/useUserLogout.ts | 13 ++++++++ frontend/src/hooks/useVerifyOtp.ts | 24 ++++++++++++++ frontend/src/screens/PodcastPlayer.tsx | 4 +-- frontend/src/screens/ProfileEditScreen.tsx | 2 +- frontend/src/screens/SplashScreen.tsx | 2 +- .../src/screens/article/PreviewScreen.tsx | 3 +- .../src/screens/auth/SignUpScreenFirst.tsx | 2 +- .../src/screens/auth/SignUpScreenSecond.tsx | 2 +- 33 files changed, 532 insertions(+), 10 deletions(-) create mode 100644 frontend/src/hooks/useChangePassword.ts create mode 100644 frontend/src/hooks/useGetArticleDetail.ts create mode 100644 frontend/src/hooks/useGetArticleTags.ts create mode 100644 frontend/src/hooks/useGetProfile.ts create mode 100644 frontend/src/hooks/useGetProfileImageById.ts rename frontend/{ => src}/hooks/useGetTokenStatus.ts (100%) create mode 100644 frontend/src/hooks/useGetTotalLikeViewStatus.ts create mode 100644 frontend/src/hooks/useGetUserDetails.ts create mode 100644 frontend/src/hooks/useLikeArticle.ts create mode 100644 frontend/src/hooks/useMailVerification.ts create mode 100644 frontend/src/hooks/usePostArticle.ts create mode 100644 frontend/src/hooks/useResendVerification.ts create mode 100644 frontend/src/hooks/useSaveArticle.ts create mode 100644 frontend/src/hooks/useSendOtp.ts create mode 100644 frontend/src/hooks/useUpdateFollowStatus.ts create mode 100644 frontend/src/hooks/useUpdatePassword.ts create mode 100644 frontend/src/hooks/useUpdateProfileImage.ts create mode 100644 frontend/src/hooks/useUpdateReadEvent.ts create mode 100644 frontend/src/hooks/useUpdateUserContactDetail.ts create mode 100644 frontend/src/hooks/useUpdateUserGeneralDetails.ts create mode 100644 frontend/src/hooks/useUpdateUserProfDetails.ts create mode 100644 frontend/src/hooks/useUpdateViewCount.ts rename frontend/{ => src}/hooks/useUploadAudio.tsx (100%) rename frontend/{ => src}/hooks/useUploadImage.tsx (100%) create mode 100644 frontend/src/hooks/useUserLogout.ts create mode 100644 frontend/src/hooks/useVerifyOtp.ts diff --git a/frontend/src/components/AppContent.tsx b/frontend/src/components/AppContent.tsx index e8d085b..5eefdb1 100644 --- a/frontend/src/components/AppContent.tsx +++ b/frontend/src/components/AppContent.tsx @@ -1,5 +1,5 @@ import {FirebaseProvider} from '@/hooks/FirebaseContext'; -import {useCheckTokenStatus} from '@/hooks/useGetTokenStatus'; +import {useCheckTokenStatus} from '@/src/hooks/useGetTokenStatus'; import {useNotificationListeners} from '@/hooks/useNotificationListener'; import {useVersionCheck} from '@/hooks/useVersionCheck'; import {SocketProvider} from '@/SocketContext'; @@ -23,6 +23,7 @@ import {setConnected} from '../store/NetworkSlice'; import {firebaseInit} from '../helper/firebase'; import {cleanUpDownloads, KEYS, retrieveItem} from '../helper/Utils'; import { setUserToken } from '../store/UserSlice'; +import axios from 'axios'; export default function AppContent() { const navigationRef = useRef(null); @@ -32,6 +33,7 @@ export default function AppContent() { }; const {data: tokenRes = null, isLoading} = useCheckTokenStatus(); + const {visible, storeUrl} = useVersionCheck(); const dispatch = useDispatch(); @@ -43,8 +45,10 @@ export default function AppContent() { }, [tokenRes, isLoading]); const checkToken = async () =>{ - const token = await retrieveItem(KEYS.USER_TOKEN); - + const token = await retrieveItem(KEYS.USER_TOKEN); + //axios.defaults.headers. + axios.defaults.headers.common["Authorization"] = `Bearer ${token}`; + axios.defaults.headers.common["Content-Type"] = "application/json"; dispatch(setUserToken(token)); initDeepLinking(navigationRef.current, tokenRes?.isValid || false); diff --git a/frontend/src/hooks/useChangePassword.ts b/frontend/src/hooks/useChangePassword.ts new file mode 100644 index 0000000..1e76ed3 --- /dev/null +++ b/frontend/src/hooks/useChangePassword.ts @@ -0,0 +1,24 @@ +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import axios, {AxiosError} from 'axios'; +import {CHANGE_PASSWORD_API} from '../helper/APIUtils'; + +type ChangePassWordReq = { + email: string; + newPassword: string; +}; +export const useChangePasswordMutation = (): UseMutationResult< + any, + AxiosError, + ChangePassWordReq +> => { + return useMutation({ + mutationKey: ['generate-new-password'], + mutationFn: async (req: ChangePassWordReq) => { + const res = await axios.post(CHANGE_PASSWORD_API, { + email: req.email, + newPassword: req.newPassword, + }); + return res.data as any; + }, + }); +}; diff --git a/frontend/src/hooks/useGetArticleDetail.ts b/frontend/src/hooks/useGetArticleDetail.ts new file mode 100644 index 0000000..9e91efe --- /dev/null +++ b/frontend/src/hooks/useGetArticleDetail.ts @@ -0,0 +1,17 @@ +import {useQuery, UseQueryResult} from '@tanstack/react-query'; +import {ArticleData} from '../type'; +import axios, {AxiosError} from 'axios'; +import {GET_ARTICLE_BY_ID} from '../helper/APIUtils'; + +export const useGetArticleDetails = ( + articleId: number, +): UseQueryResult => { + return useQuery({ + queryKey: ['get-article-by-id', articleId], + queryFn: async () => { + const response = await axios.get(`${GET_ARTICLE_BY_ID}/${articleId}`); + + return response.data.article as ArticleData; + }, + }); +}; diff --git a/frontend/src/hooks/useGetArticleTags.ts b/frontend/src/hooks/useGetArticleTags.ts new file mode 100644 index 0000000..649bf13 --- /dev/null +++ b/frontend/src/hooks/useGetArticleTags.ts @@ -0,0 +1,28 @@ +import {useQuery, UseQueryResult} from '@tanstack/react-query'; +import axios, {AxiosError} from 'axios'; +import {ARTICLE_TAGS_API, PROD_URL} from '../helper/APIUtils'; +import {Category} from '../type'; + +const categoryFunc = async () => { + try{ + const {data: categoryData} = await axios.get( + `${PROD_URL + ARTICLE_TAGS_API}`, + ); + + return categoryData as Category; + }catch(err){ + console.log("GET CATEGORY ERR", err); + return null; + } +}; + +export const useGetCategories = (isConnected: boolean): UseQueryResult< + Category | null, + AxiosError +> => { + return useQuery({ + queryKey: ['get-categories'], + queryFn: categoryFunc, + enabled: isConnected + }); +}; diff --git a/frontend/src/hooks/useGetProfile.ts b/frontend/src/hooks/useGetProfile.ts new file mode 100644 index 0000000..fdd6760 --- /dev/null +++ b/frontend/src/hooks/useGetProfile.ts @@ -0,0 +1,14 @@ +import {useQuery, UseQueryResult} from '@tanstack/react-query'; +import {User} from '../type'; +import axios, {AxiosError} from 'axios'; +import {GET_PROFILE_API} from '../helper/APIUtils'; + +export const useGetProfile = (): UseQueryResult => { + return useQuery({ + queryKey: ['get-my-profile'], + queryFn: async () => { + const response = await axios.get(`${GET_PROFILE_API}`); + return response.data.profile as User; + }, + }); +}; diff --git a/frontend/src/hooks/useGetProfileImageById.ts b/frontend/src/hooks/useGetProfileImageById.ts new file mode 100644 index 0000000..abeedae --- /dev/null +++ b/frontend/src/hooks/useGetProfileImageById.ts @@ -0,0 +1,18 @@ +import axios, { AxiosError } from "axios"; +import { GET_PROFILE_IMAGE_BY_ID } from "../helper/APIUtils"; +import { useQuery, UseQueryResult } from "@tanstack/react-query"; + +export const useGetUserProfileImage = (authorId: string): UseQueryResult< + string, + AxiosError +>=>{ + + return useQuery({ + queryKey: ['author_profile_image', authorId], + queryFn: async () => { + const response = await axios.get( + `${GET_PROFILE_IMAGE_BY_ID}/${authorId}`); + return response.data.profile_image as string; + }, + }); +} \ No newline at end of file diff --git a/frontend/hooks/useGetTokenStatus.ts b/frontend/src/hooks/useGetTokenStatus.ts similarity index 100% rename from frontend/hooks/useGetTokenStatus.ts rename to frontend/src/hooks/useGetTokenStatus.ts diff --git a/frontend/src/hooks/useGetTotalLikeViewStatus.ts b/frontend/src/hooks/useGetTotalLikeViewStatus.ts new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/hooks/useGetUserDetails.ts b/frontend/src/hooks/useGetUserDetails.ts new file mode 100644 index 0000000..47e1ca5 --- /dev/null +++ b/frontend/src/hooks/useGetUserDetails.ts @@ -0,0 +1,20 @@ +import { useQuery, UseQueryResult } from "@tanstack/react-query"; +import axios, { AxiosError } from "axios"; +import { User } from "../type"; +import { GET_USER_DETAILS_API } from "../helper/APIUtils"; + +export const useGetUserDetails = (isConnected: boolean): UseQueryResult< +User, +AxiosError +>=>{ + + return useQuery({ + queryKey: ['get-user-details-by-id'], + queryFn: async () => { + const response = await axios.get(`${GET_USER_DETAILS_API}`); + + return response.data.profile as User; + }, + enabled: !!isConnected, + }); +} \ No newline at end of file diff --git a/frontend/src/hooks/useLikeArticle.ts b/frontend/src/hooks/useLikeArticle.ts new file mode 100644 index 0000000..9af32db --- /dev/null +++ b/frontend/src/hooks/useLikeArticle.ts @@ -0,0 +1,29 @@ +import axios, {AxiosError} from 'axios'; +import {ArticleData} from '../type'; +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import {LIKE_ARTICLE} from '../helper/APIUtils'; + +export const useLikeArticle = ( + articleId: number, +): UseMutationResult< + { + article: ArticleData; + likeStatus: boolean; + }, + AxiosError +> => { + return useMutation({ + mutationKey: ['update-like-status', articleId], + + mutationFn: async () => { + const res = await axios.post(LIKE_ARTICLE, { + article_id: articleId, + }); + + return res.data.data as { + article: ArticleData; + likeStatus: boolean; + }; + }, + }); +}; diff --git a/frontend/src/hooks/useMailVerification.ts b/frontend/src/hooks/useMailVerification.ts new file mode 100644 index 0000000..b517c85 --- /dev/null +++ b/frontend/src/hooks/useMailVerification.ts @@ -0,0 +1,26 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import axios, { AxiosError } from "axios"; +import { VERIFICATION_MAIL_API } from "../helper/APIUtils"; + +type VerificationReq = { + email: string; + token: string +} +export const useVerificationMailMutation = (): UseMutationResult< + string, + AxiosError, + VerificationReq + +>=>{ + return useMutation({ + mutationKey: ['send-verification-mail'], + mutationFn: async (req: VerificationReq) => { + const res = await axios.post(VERIFICATION_MAIL_API, { + email: req.email, + token: req.token, + }); + + return res.data.message as string; + }, + }); +} \ No newline at end of file diff --git a/frontend/src/hooks/usePostArticle.ts b/frontend/src/hooks/usePostArticle.ts new file mode 100644 index 0000000..90d7301 --- /dev/null +++ b/frontend/src/hooks/usePostArticle.ts @@ -0,0 +1,32 @@ +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import {ArticleData, Category} from '../type'; +import axios, {AxiosError} from 'axios'; +import {POST_ARTICLE} from '../helper/APIUtils'; + +type PostReq = { + title: string; + authorName: string; + authorId: string; + content: string; + tags: Category[]; + imageUtils: string[]; + description: string; + pb_recordId: string; + allow_podcast: boolean; + language: string; +}; + +export const usePostArticleData = (): UseMutationResult< + ArticleData, + AxiosError, + PostReq +> => { + return useMutation({ + mutationKey: ['create-post-key'], + mutationFn: async (data: PostReq) => { + const response = await axios.post(POST_ARTICLE, data); + // console.log(article); + return response.data.newArticle as ArticleData; + }, + }); +}; diff --git a/frontend/src/hooks/useResendVerification.ts b/frontend/src/hooks/useResendVerification.ts new file mode 100644 index 0000000..f7e27f2 --- /dev/null +++ b/frontend/src/hooks/useResendVerification.ts @@ -0,0 +1,20 @@ +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import axios, {AxiosError} from 'axios'; +import {RESEND_VERIFICATION} from '../helper/APIUtils'; + +export const useRequestVerification = (): UseMutationResult< + string, + AxiosError, + {email: string} +> => { + return useMutation({ + mutationKey: ['resend-verification-mail'], + mutationFn: async ({email}: {email: string}) => { + const res = await axios.post(RESEND_VERIFICATION, { + email: email, + }); + + return res.data.message as string; + }, + }); +}; diff --git a/frontend/src/hooks/useSaveArticle.ts b/frontend/src/hooks/useSaveArticle.ts new file mode 100644 index 0000000..50fa5da --- /dev/null +++ b/frontend/src/hooks/useSaveArticle.ts @@ -0,0 +1,26 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import { SAVE_ARTICLE } from "../helper/APIUtils"; +import axios, { AxiosError } from "axios"; + + +export const useSaveArticle = (articleId: number): UseMutationResult< + any, + AxiosError +>=>{ + + return useMutation({ + mutationKey: [`update-save-article`, articleId], + mutationFn: async () => { + + const res = await axios.post( + SAVE_ARTICLE, + { + article_id: articleId, + }, + ); + + return res.data as any; + }, + + }); +} \ No newline at end of file diff --git a/frontend/src/hooks/useSendOtp.ts b/frontend/src/hooks/useSendOtp.ts new file mode 100644 index 0000000..4f773ba --- /dev/null +++ b/frontend/src/hooks/useSendOtp.ts @@ -0,0 +1,19 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import axios, { AxiosError } from "axios"; +import { SEND_OTP } from "../helper/APIUtils"; + +export const useSendOtpMutation = ():UseMutationResult< + string, + AxiosError, + {email: string} +> =>{ + return useMutation({ + mutationKey: ['forgot-password-otp'], + mutationFn: async ({email}: {email: string}) => { + const res = await axios.post(SEND_OTP, { + email: email, + }); + return res.data.otp as string; + }, + }); +} \ No newline at end of file diff --git a/frontend/src/hooks/useUpdateFollowStatus.ts b/frontend/src/hooks/useUpdateFollowStatus.ts new file mode 100644 index 0000000..6b8a685 --- /dev/null +++ b/frontend/src/hooks/useUpdateFollowStatus.ts @@ -0,0 +1,26 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import axios, { AxiosError } from "axios"; +import { FOLLOW_USER } from "../helper/APIUtils"; + + +export const useUpdateFollowStatus = (): UseMutationResult< + boolean, + AxiosError, + string +>=>{ + return useMutation({ + mutationKey: ['update-follow-status'], + + mutationFn: async (userid: string) => { + + const res = await axios.post( + FOLLOW_USER, + { + followUserId: userid, + }, + ); + return res.data.followStatus as boolean; + }, + }); + +} \ No newline at end of file diff --git a/frontend/src/hooks/useUpdatePassword.ts b/frontend/src/hooks/useUpdatePassword.ts new file mode 100644 index 0000000..2224fdc --- /dev/null +++ b/frontend/src/hooks/useUpdatePassword.ts @@ -0,0 +1,28 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import { UPDATE_USER_PASSWORD } from "../helper/APIUtils"; +import axios, { AxiosError } from "axios"; + +type UpdateReq = { + old_password: string; + new_password: string; +} +export const useUpdatePassword = (): UseMutationResult< + any, + AxiosError, + UpdateReq +>=>{ + + return useMutation({ + mutationKey: ['user-password-updation'], + mutationFn: async (req: UpdateReq) => { + const response = await axios.put( + `${UPDATE_USER_PASSWORD}`, + { + old_password: req.old_password, + new_password: req.new_password, + }, + ); + return response.data as any; + }, + }); +} \ No newline at end of file diff --git a/frontend/src/hooks/useUpdateProfileImage.ts b/frontend/src/hooks/useUpdateProfileImage.ts new file mode 100644 index 0000000..2eccdcf --- /dev/null +++ b/frontend/src/hooks/useUpdateProfileImage.ts @@ -0,0 +1,24 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import axios, { AxiosError } from "axios"; +import { UPDATE_PROFILE_IMAGE } from "../helper/APIUtils"; + + +export const useUpdateProfileImage = (): UseMutationResult< + any, + AxiosError, + string +>=>{ + return useMutation({ + mutationKey: ['user-profile-image-updation'], + mutationFn: async (profileImageUrl: string) => { + const response = await axios.post( + `${UPDATE_PROFILE_IMAGE}`, + { + profileImageUrl, + } + ); + return response.data as any; + }, + + }); +} \ No newline at end of file diff --git a/frontend/src/hooks/useUpdateReadEvent.ts b/frontend/src/hooks/useUpdateReadEvent.ts new file mode 100644 index 0000000..b2d788d --- /dev/null +++ b/frontend/src/hooks/useUpdateReadEvent.ts @@ -0,0 +1,24 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import axios, { AxiosError } from "axios"; +import { UPDATE_READ_EVENT } from "../helper/APIUtils"; + +export const useUpdateReadEvent = (articleId: number): UseMutationResult< +any, +AxiosError +>=>{ + + return useMutation({ + mutationKey: ['update-read-event-status', articleId], + + mutationFn: async () => { + + const res = await axios.post( + UPDATE_READ_EVENT, + { + article_id: articleId, + } + ); + return res.data as any; + } + }); +} \ No newline at end of file diff --git a/frontend/src/hooks/useUpdateUserContactDetail.ts b/frontend/src/hooks/useUpdateUserContactDetail.ts new file mode 100644 index 0000000..7599dca --- /dev/null +++ b/frontend/src/hooks/useUpdateUserContactDetail.ts @@ -0,0 +1,24 @@ +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import axios, {AxiosError} from 'axios'; +import {UPDATE_USER_CONTACT_DETAILS} from '../helper/APIUtils'; + +type UpdateReq = { + phone: string; + email: string; +}; +export const useUpdateUserContactDetail = (): UseMutationResult< + any, + AxiosError, + UpdateReq +> => { + return useMutation({ + mutationKey: ['user-contact-details-updation'], + mutationFn: async (req: UpdateReq) => { + const response = await axios.put(`${UPDATE_USER_CONTACT_DETAILS}`, { + phone: req.phone, + email: req.email, + }); + return response.data as any; + }, + }); +}; diff --git a/frontend/src/hooks/useUpdateUserGeneralDetails.ts b/frontend/src/hooks/useUpdateUserGeneralDetails.ts new file mode 100644 index 0000000..f4b447e --- /dev/null +++ b/frontend/src/hooks/useUpdateUserGeneralDetails.ts @@ -0,0 +1,30 @@ +import axios, { AxiosError } from "axios"; +import { UPDATE_USER_GENERAL_DETAILS } from "../helper/APIUtils"; +import { useMutation, UseMutationResult } from "@tanstack/react-query"; + + +type UpdateReq ={ + username: string; + about: string; +} +export const useUpdateUserGeneralDetails = ():UseMutationResult< +any, +AxiosError, +UpdateReq +>=>{ + + return useMutation({ + mutationKey: ['user-general-details-updation'], + mutationFn: async (req: UpdateReq) => { + const response = await axios.put( + `${UPDATE_USER_GENERAL_DETAILS}`, + { + username: req.username, + about: req.about, + } + ); + return response.data as any; + }, + + }); +} \ No newline at end of file diff --git a/frontend/src/hooks/useUpdateUserProfDetails.ts b/frontend/src/hooks/useUpdateUserProfDetails.ts new file mode 100644 index 0000000..9c68017 --- /dev/null +++ b/frontend/src/hooks/useUpdateUserProfDetails.ts @@ -0,0 +1,31 @@ +import axios, { AxiosError } from "axios"; +import { UPDATE_USER_PROFESSIONAL_DETAILS } from "../helper/APIUtils"; +import { useMutation, UseMutationResult } from "@tanstack/react-query"; + + +type UpdateReq ={ + specialization: string; + qualification: string; + experience: string; +} +export const useUpdateUserProfDetails = ():UseMutationResult< +any, +AxiosError, +UpdateReq +>=>{ + + return useMutation({ + mutationKey: ['user-professional-details-updation'], + mutationFn: async (req: UpdateReq) => { + const response = await axios.put( + `${UPDATE_USER_PROFESSIONAL_DETAILS}`, + { + specialization: req.specialization, + qualification: req.qualification, + experience: req.experience, + } + ); + return response.data as any; + }, + }); +} \ No newline at end of file diff --git a/frontend/src/hooks/useUpdateViewCount.ts b/frontend/src/hooks/useUpdateViewCount.ts new file mode 100644 index 0000000..45e5f3d --- /dev/null +++ b/frontend/src/hooks/useUpdateViewCount.ts @@ -0,0 +1,20 @@ +import axios, {AxiosError} from 'axios'; +import {ArticleData} from '../type'; +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import {UPDATE_VIEW_COUNT} from '../helper/APIUtils'; + +export const useUpdateViewCount = (articleId: number): UseMutationResult< + ArticleData, + AxiosError +> => { + return useMutation({ + mutationKey: ['update-view-count', articleId], + mutationFn: async () => { + const res = await axios.post(UPDATE_VIEW_COUNT, { + article_id: articleId, + }); + + return res.data.article as ArticleData; + }, + }); +}; diff --git a/frontend/hooks/useUploadAudio.tsx b/frontend/src/hooks/useUploadAudio.tsx similarity index 100% rename from frontend/hooks/useUploadAudio.tsx rename to frontend/src/hooks/useUploadAudio.tsx diff --git a/frontend/hooks/useUploadImage.tsx b/frontend/src/hooks/useUploadImage.tsx similarity index 100% rename from frontend/hooks/useUploadImage.tsx rename to frontend/src/hooks/useUploadImage.tsx diff --git a/frontend/src/hooks/useUserLogout.ts b/frontend/src/hooks/useUserLogout.ts new file mode 100644 index 0000000..e25cacd --- /dev/null +++ b/frontend/src/hooks/useUserLogout.ts @@ -0,0 +1,13 @@ +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import axios, {AxiosError} from 'axios'; +import {USER_LOGOUT} from '../helper/APIUtils'; + +export const useUserLogout = (): UseMutationResult => { + return useMutation({ + mutationKey: ['user-logout'], + mutationFn: async () => { + const response = await axios.post(`${USER_LOGOUT}`, {}); + return response.data as any; + }, + }); +}; diff --git a/frontend/src/hooks/useVerifyOtp.ts b/frontend/src/hooks/useVerifyOtp.ts new file mode 100644 index 0000000..20206da --- /dev/null +++ b/frontend/src/hooks/useVerifyOtp.ts @@ -0,0 +1,24 @@ +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import axios, {AxiosError} from 'axios'; +import {CHECK_OTP} from '../helper/APIUtils'; + +type VerifyReq = { + email: string; + otp: string; +}; +export const useVerifyOtpMutation = (): UseMutationResult< + string, + AxiosError, + VerifyReq +> => { + return useMutation({ + mutationKey: ['verify-otp'], + mutationFn: async (req: VerifyReq) => { + const res = await axios.post(CHECK_OTP, { + email: req.email, + otp: req.otp, + }); + return res.data.message as string; + }, + }); +}; diff --git a/frontend/src/screens/PodcastPlayer.tsx b/frontend/src/screens/PodcastPlayer.tsx index 63b68a7..58bffa3 100644 --- a/frontend/src/screens/PodcastPlayer.tsx +++ b/frontend/src/screens/PodcastPlayer.tsx @@ -8,9 +8,9 @@ import axios from 'axios'; import Snackbar from 'react-native-snackbar'; import {UPLOAD_PODCAST} from '../helper/APIUtils'; -import useUploadImage from '../../hooks/useUploadImage'; +import useUploadImage from '../hooks/useUploadImage'; import ImageResizer from '@bam.tech/react-native-image-resizer'; -import useUploadAudio from '../../hooks/useUploadAudio'; +import useUploadAudio from '../hooks/useUploadAudio'; import Slider from '@react-native-community/slider'; import {useAudioPlayer} from 'expo-audio'; diff --git a/frontend/src/screens/ProfileEditScreen.tsx b/frontend/src/screens/ProfileEditScreen.tsx index b12947a..bcd225f 100644 --- a/frontend/src/screens/ProfileEditScreen.tsx +++ b/frontend/src/screens/ProfileEditScreen.tsx @@ -36,7 +36,7 @@ import { ImagePickerResponse, } from 'react-native-image-picker'; import ImageResizer from '@bam.tech/react-native-image-resizer'; -import useUploadImage from '../../hooks/useUploadImage'; +import useUploadImage from '../hooks/useUploadImage'; import {showAlert} from '../store/alertSlice'; import Snackbar from 'react-native-snackbar'; // eslint-disable-next-line @typescript-eslint/no-require-imports diff --git a/frontend/src/screens/SplashScreen.tsx b/frontend/src/screens/SplashScreen.tsx index f8c65f6..de230ce 100644 --- a/frontend/src/screens/SplashScreen.tsx +++ b/frontend/src/screens/SplashScreen.tsx @@ -11,7 +11,7 @@ import {SplashScreenProp} from '../type'; import {useDispatch} from 'react-redux'; import {retrieveItem, KEYS, clearStorage} from '../helper/Utils'; import {setUserId, setUserToken, setUserHandle} from '../store/UserSlice'; -import { useCheckTokenStatus } from '@/hooks/useGetTokenStatus'; +import { useCheckTokenStatus } from '@/src/hooks/useGetTokenStatus'; export default function SplashScreen({navigation}: SplashScreenProp) { const opacity = useSharedValue(0); diff --git a/frontend/src/screens/article/PreviewScreen.tsx b/frontend/src/screens/article/PreviewScreen.tsx index 6376396..fd7f55d 100644 --- a/frontend/src/screens/article/PreviewScreen.tsx +++ b/frontend/src/screens/article/PreviewScreen.tsx @@ -32,7 +32,7 @@ import { UPLOAD_IMPROVEMENT_TO_POCKETBASE, } from '../../helper/APIUtils'; import {useDispatch, useSelector} from 'react-redux'; -import useUploadImage from '../../../hooks/useUploadImage'; +import useUploadImage from '../../hooks/useUploadImage'; import {setSuggestion} from '../../store/dataSlice'; import Snackbar from 'react-native-snackbar'; import AutoHeightWebView from '@brown-bear/react-native-autoheight-webview'; @@ -229,6 +229,7 @@ export default function PreviewScreen({navigation, route}: PreviewScreenProp) { description: description, pb_recordId: recordId, allow_podcast: true, + language: "en-IN" }, { headers: { diff --git a/frontend/src/screens/auth/SignUpScreenFirst.tsx b/frontend/src/screens/auth/SignUpScreenFirst.tsx index 4bacc3b..61583ce 100644 --- a/frontend/src/screens/auth/SignUpScreenFirst.tsx +++ b/frontend/src/screens/auth/SignUpScreenFirst.tsx @@ -27,7 +27,7 @@ import { import EmailVerifiedModal from '../../components/VerifiedModal'; import ImageResizer from '@bam.tech/react-native-image-resizer'; import Loader from '../../components/Loader'; -import useUploadImage from '../../../hooks/useUploadImage'; +import useUploadImage from '../../hooks/useUploadImage'; import { ImageLibraryOptions, launchImageLibrary, diff --git a/frontend/src/screens/auth/SignUpScreenSecond.tsx b/frontend/src/screens/auth/SignUpScreenSecond.tsx index 450a20e..0a6da32 100644 --- a/frontend/src/screens/auth/SignUpScreenSecond.tsx +++ b/frontend/src/screens/auth/SignUpScreenSecond.tsx @@ -18,7 +18,7 @@ import {REGISTRATION_API, VERIFICATION_MAIL_API} from '../../helper/APIUtils'; import EmailVerifiedModal from '../../components/VerifiedModal'; import Loader from '../../components/Loader'; import Snackbar from 'react-native-snackbar'; -import useUploadImage from '../../../hooks/useUploadImage'; +import useUploadImage from '../../hooks/useUploadImage'; import {SafeAreaView} from 'react-native-safe-area-context'; let validator = require('email-validator'); From 79f5504fecef550a43b6c0c129ead9113d5a4486 Mon Sep 17 00:00:00 2001 From: SB2318 Date: Tue, 10 Mar 2026 21:56:58 +0530 Subject: [PATCH 3/6] Add new hooks for fetching user statistics: likes, views, reads, and writes --- frontend/src/hooks/useGetMonthlyReadReport.ts | 36 +++++++++++++++++++ .../src/hooks/useGetMonthlyWriteReport.ts | 35 ++++++++++++++++++ frontend/src/hooks/useGetMostViewedArticle.ts | 30 ++++++++++++++++ .../src/hooks/useGetTotalLikeViewStatus.ts | 30 ++++++++++++++++ frontend/src/hooks/useGetTotalReads.ts | 30 ++++++++++++++++ frontend/src/hooks/useGetTotalWrites.ts | 30 ++++++++++++++++ 6 files changed, 191 insertions(+) create mode 100644 frontend/src/hooks/useGetMonthlyReadReport.ts create mode 100644 frontend/src/hooks/useGetMonthlyWriteReport.ts create mode 100644 frontend/src/hooks/useGetMostViewedArticle.ts create mode 100644 frontend/src/hooks/useGetTotalReads.ts create mode 100644 frontend/src/hooks/useGetTotalWrites.ts diff --git a/frontend/src/hooks/useGetMonthlyReadReport.ts b/frontend/src/hooks/useGetMonthlyReadReport.ts new file mode 100644 index 0000000..9d6cc3e --- /dev/null +++ b/frontend/src/hooks/useGetMonthlyReadReport.ts @@ -0,0 +1,36 @@ + +import { useQuery, UseQueryResult } from "@tanstack/react-query"; +import axios from "axios"; +import { MonthStatus } from "../type"; +import { GET_MONTHLY_READ_REPORT } from "../helper/APIUtils"; + +export const useGetAuthorMonthlyReadReport = ( + user_id: string, + selectedMonth: number, + userId?: string, + others?: boolean, + isConnected?: boolean, + +): UseQueryResult => { + + return useQuery({ + queryKey: ["get-user-monthly-read-report", user_id, userId, others], + + queryFn: async () => { + + if(selectedMonth === -1){ + return []; + } + + let url = others + ? `${GET_MONTHLY_READ_REPORT}?userId=${userId}&month=${selectedMonth}` + : `${GET_MONTHLY_READ_REPORT}?userId=${user_id}&month=${selectedMonth}`; + + const response = await axios.get(url); + + return response.data.monthlyReads as MonthStatus[]; + }, + + enabled: !!isConnected && !!(!userId && others) && !!(!user_id && !others), + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useGetMonthlyWriteReport.ts b/frontend/src/hooks/useGetMonthlyWriteReport.ts new file mode 100644 index 0000000..a2c3702 --- /dev/null +++ b/frontend/src/hooks/useGetMonthlyWriteReport.ts @@ -0,0 +1,35 @@ + +import { useQuery, UseQueryResult } from "@tanstack/react-query"; +import axios from "axios"; +import { MonthStatus } from "../type"; +import { GET_MONTHLY_WRITES_REPORT } from "../helper/APIUtils"; + +export const useGetAuthorMonthlyReadReport = ( + user_id: string, + selectedMonth: number, + userId?: string, + others?: boolean, + isConnected?: boolean, +): UseQueryResult => { + + return useQuery({ + queryKey: ["get-user-monthly-write-report", user_id, userId, others], + + queryFn: async () => { + + if(selectedMonth === -1){ + return []; + } + + let url = others + ? `${GET_MONTHLY_WRITES_REPORT}?userId=${userId}&month=${selectedMonth}` + : `${GET_MONTHLY_WRITES_REPORT}?userId=${user_id}&month=${selectedMonth}`; + + const response = await axios.get(url); + + return response.data.monthlyWrites as MonthStatus[]; + }, + + enabled: !!isConnected && !!(!userId && others) && !!(!user_id && !others), + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useGetMostViewedArticle.ts b/frontend/src/hooks/useGetMostViewedArticle.ts new file mode 100644 index 0000000..4d69035 --- /dev/null +++ b/frontend/src/hooks/useGetMostViewedArticle.ts @@ -0,0 +1,30 @@ + +import { useQuery, UseQueryResult } from "@tanstack/react-query"; +import axios from "axios"; +import { ArticleData } from "../type"; +import { GET_MOSTLY_VIEWED } from "../helper/APIUtils"; + +export const useGetAuthorMostViewedArticles = ( + user_id: string, + userId?: string, + others?: boolean, + isConnected?: boolean +): UseQueryResult => { + + return useQuery({ + queryKey: ["get-mostly-viewed-article", user_id, userId, others], + + queryFn: async () => { + + const url = others + ? `${GET_MOSTLY_VIEWED}${userId}` + : `${GET_MOSTLY_VIEWED}${user_id}`; + + const response = await axios.get(url); + + return response.data as ArticleData[]; + }, + + enabled: !!isConnected && !!(!userId && others) && !!(!user_id && !others), + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useGetTotalLikeViewStatus.ts b/frontend/src/hooks/useGetTotalLikeViewStatus.ts index e69de29..811aa45 100644 --- a/frontend/src/hooks/useGetTotalLikeViewStatus.ts +++ b/frontend/src/hooks/useGetTotalLikeViewStatus.ts @@ -0,0 +1,30 @@ +import { useQuery, UseQueryResult } from "@tanstack/react-query"; +import axios from "axios"; +import { UserStatus } from "../type"; +import { GET_TOTAL_LIKES_VIEWS } from "../helper/APIUtils"; + +export const useGetTotalLikeViewStatus = ( + user_id: string, + userId?: string, + others?: boolean, + isConnected?: boolean +): UseQueryResult => { + + return useQuery({ + queryKey: ["get-like-view-status", user_id, userId, others], + + queryFn: async () => { + + + const url = others + ? `${GET_TOTAL_LIKES_VIEWS}${userId}` + : `${GET_TOTAL_LIKES_VIEWS}${user_id}`; + + const response = await axios.get(url); + + return response.data as UserStatus; + }, + + enabled: !!isConnected && !!(!userId && others) && !!(!user_id && !others), + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useGetTotalReads.ts b/frontend/src/hooks/useGetTotalReads.ts new file mode 100644 index 0000000..7a1d226 --- /dev/null +++ b/frontend/src/hooks/useGetTotalReads.ts @@ -0,0 +1,30 @@ + +import { useQuery, UseQueryResult } from "@tanstack/react-query"; +import axios from "axios"; +import { ReadStatus } from "../type"; +import { GET_TOTAL_READS } from "../helper/APIUtils"; + +export const useGetTotalReads = ( + user_id: string, + userId?: string, + others?: boolean, + isConnected?: boolean +): UseQueryResult => { + + return useQuery({ + queryKey: ["get-total-reads", user_id, userId, others], + + queryFn: async () => { + + const url = others + ? `${GET_TOTAL_READS}${userId}` + : `${GET_TOTAL_READS}${user_id}`; + + const response = await axios.get(url); + + return response.data as ReadStatus; + }, + + enabled: !!isConnected && !!(!userId && others) && !!(!user_id && !others), + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useGetTotalWrites.ts b/frontend/src/hooks/useGetTotalWrites.ts new file mode 100644 index 0000000..67d5eeb --- /dev/null +++ b/frontend/src/hooks/useGetTotalWrites.ts @@ -0,0 +1,30 @@ + +import { useQuery, UseQueryResult } from "@tanstack/react-query"; +import axios from "axios"; +import { WriteStatus } from "../type"; +import { GET_TOTAL_WRITES } from "../helper/APIUtils"; + +export const useGetTotalReads = ( + user_id: string, + userId?: string, + others?: boolean, + isConnected?: boolean +): UseQueryResult => { + + return useQuery({ + queryKey: ["get-total-writes", user_id, userId, others], + + queryFn: async () => { + + const url = others + ? `${GET_TOTAL_WRITES}${userId}` + : `${GET_TOTAL_WRITES}${user_id}`; + + const response = await axios.get(url); + + return response.data as WriteStatus; + }, + + enabled: !!isConnected && !!(!userId && others) && !!(!user_id && !others), + }); +}; \ No newline at end of file From cdcdd310de1c390de74a19c238f419f24c62be4e Mon Sep 17 00:00:00 2001 From: SB2318 Date: Wed, 11 Mar 2026 01:45:09 +0530 Subject: [PATCH 4/6] Add new hooks for fetching user reports and user handle availability --- frontend/src/hooks/useArticleRepost.ts | 18 +++++++ frontend/src/hooks/useCheckUserHandle.ts | 18 +++++++ frontend/src/hooks/useGetArticleTags.ts | 4 +- frontend/src/hooks/useGetReportReasons.ts | 28 ++++++++++ frontend/src/hooks/useGetYearlyReadReport.ts | 32 ++++++++++++ frontend/src/hooks/useGetYearlyWriteReport.ts | 32 ++++++++++++ .../src/hooks/useSubmitEditRequestMutation.ts | 44 ++++++++++++++++ frontend/src/hooks/useSubmitReport.ts | 47 +++++++++++++++++ .../src/hooks/useSubmitSuggestedChanges.ts | 51 +++++++++++++++++++ .../src/screens/auth/SignUpScreenFirst.tsx | 2 +- 10 files changed, 273 insertions(+), 3 deletions(-) create mode 100644 frontend/src/hooks/useArticleRepost.ts create mode 100644 frontend/src/hooks/useCheckUserHandle.ts create mode 100644 frontend/src/hooks/useGetReportReasons.ts create mode 100644 frontend/src/hooks/useGetYearlyReadReport.ts create mode 100644 frontend/src/hooks/useGetYearlyWriteReport.ts create mode 100644 frontend/src/hooks/useSubmitEditRequestMutation.ts create mode 100644 frontend/src/hooks/useSubmitReport.ts create mode 100644 frontend/src/hooks/useSubmitSuggestedChanges.ts diff --git a/frontend/src/hooks/useArticleRepost.ts b/frontend/src/hooks/useArticleRepost.ts new file mode 100644 index 0000000..0249652 --- /dev/null +++ b/frontend/src/hooks/useArticleRepost.ts @@ -0,0 +1,18 @@ +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import {REPOST_ARTICLE} from '../helper/APIUtils'; +import axios, {AxiosError} from 'axios'; + +export const useRepostArticle = ( + articleId: number, +): UseMutationResult => { + return useMutation({ + mutationKey: [`repost-article`, articleId], + mutationFn: async () => { + const res = await axios.post(REPOST_ARTICLE, { + articleId: articleId, + }); + + return res.data as any; + }, + }); +}; diff --git a/frontend/src/hooks/useCheckUserHandle.ts b/frontend/src/hooks/useCheckUserHandle.ts new file mode 100644 index 0000000..c7adcbb --- /dev/null +++ b/frontend/src/hooks/useCheckUserHandle.ts @@ -0,0 +1,18 @@ +import { useQuery } from "@tanstack/react-query"; +import axios from "axios"; +import { CHECK_USER_HANDLE } from "../helper/APIUtils"; + +export const useCheckUserHandleAvailability = (handle: string) => { + return useQuery({ + queryKey: ["check-user-handle", handle], + queryFn: async () => { + const response = await axios.post(CHECK_USER_HANDLE, { + userHandle: handle, + }); + + return response.data; + }, + enabled: !!handle, + retry: false, + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useGetArticleTags.ts b/frontend/src/hooks/useGetArticleTags.ts index 649bf13..94955d3 100644 --- a/frontend/src/hooks/useGetArticleTags.ts +++ b/frontend/src/hooks/useGetArticleTags.ts @@ -9,7 +9,7 @@ const categoryFunc = async () => { `${PROD_URL + ARTICLE_TAGS_API}`, ); - return categoryData as Category; + return categoryData as Category[]; }catch(err){ console.log("GET CATEGORY ERR", err); return null; @@ -17,7 +17,7 @@ const categoryFunc = async () => { }; export const useGetCategories = (isConnected: boolean): UseQueryResult< - Category | null, + Category[] | null, AxiosError > => { return useQuery({ diff --git a/frontend/src/hooks/useGetReportReasons.ts b/frontend/src/hooks/useGetReportReasons.ts new file mode 100644 index 0000000..8d6383a --- /dev/null +++ b/frontend/src/hooks/useGetReportReasons.ts @@ -0,0 +1,28 @@ +import {useQuery, UseQueryResult} from '@tanstack/react-query'; +import axios, {AxiosError} from 'axios'; +import {GET_REPORT_REASONS} from '../helper/APIUtils'; +import {ReportReason} from '../type'; + +const reasonsFunc = async () => { + try{ + const {data: categoryData} = await axios.get( + GET_REPORT_REASONS + ); + + return categoryData as ReportReason[]; + }catch(err){ + console.log("GET CATEGORY ERR", err); + return null; + } +}; + +export const useGetReasons = (isConnected: boolean): UseQueryResult< + ReportReason[] | null, + AxiosError +> => { + return useQuery({ + queryKey: ['get-report-reasons'], + queryFn: reasonsFunc, + enabled: isConnected + }); +}; diff --git a/frontend/src/hooks/useGetYearlyReadReport.ts b/frontend/src/hooks/useGetYearlyReadReport.ts new file mode 100644 index 0000000..d4ad7a9 --- /dev/null +++ b/frontend/src/hooks/useGetYearlyReadReport.ts @@ -0,0 +1,32 @@ +import {useQuery, UseQueryResult} from '@tanstack/react-query'; +import axios from 'axios'; +import {YearStatus} from '../type'; +import {GET_YEARLY_READ_REPORT} from '../helper/APIUtils'; + +export const useGetAuthorYearlyReadReport = ( + user_id: string, + selectedYear: number, + userId?: string, + others?: boolean, + isConnected?: boolean, +): UseQueryResult => { + return useQuery({ + queryKey: ['get-user-yearly-read-report', user_id, userId, others], + + queryFn: async () => { + if (selectedYear === -1) { + return []; + } + + let url = others + ? `${GET_YEARLY_READ_REPORT}?userId=${userId}&year=${selectedYear}` + : `${GET_YEARLY_READ_REPORT}?userId=${user_id}&year=${selectedYear}`; + + const response = await axios.get(url); + + return response.data.yearlyReads as YearStatus[]; + }, + + enabled: !!isConnected && !!(!userId && others) && !!(!user_id && !others), + }); +}; diff --git a/frontend/src/hooks/useGetYearlyWriteReport.ts b/frontend/src/hooks/useGetYearlyWriteReport.ts new file mode 100644 index 0000000..4580ac6 --- /dev/null +++ b/frontend/src/hooks/useGetYearlyWriteReport.ts @@ -0,0 +1,32 @@ +import {useQuery, UseQueryResult} from '@tanstack/react-query'; +import axios from 'axios'; +import {YearStatus} from '../type'; +import {GET_YEARLY_WRITES_REPORT} from '../helper/APIUtils'; + +export const useGetAuthorYearlyWriteReport = ( + user_id: string, + selectedYear: number, + userId?: string, + others?: boolean, + isConnected?: boolean, +): UseQueryResult => { + return useQuery({ + queryKey: ['get-user-yearly-write-report', user_id, userId, others], + + queryFn: async () => { + if (selectedYear === -1) { + return []; + } + + let url = others + ? `${GET_YEARLY_WRITES_REPORT}?userId=${userId}&year=${selectedYear}` + : `${GET_YEARLY_WRITES_REPORT}?userId=${user_id}&year=${selectedYear}`; + + const response = await axios.get(url); + + return response.data.yearlyWrites as YearStatus[]; + }, + + enabled: !!isConnected && !!(!userId && others) && !!(!user_id && !others), + }); +}; diff --git a/frontend/src/hooks/useSubmitEditRequestMutation.ts b/frontend/src/hooks/useSubmitEditRequestMutation.ts new file mode 100644 index 0000000..9d00126 --- /dev/null +++ b/frontend/src/hooks/useSubmitEditRequestMutation.ts @@ -0,0 +1,44 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import axios, { AxiosError } from "axios"; +import { REQUEST_EDIT } from "../helper/APIUtils"; + +type SubmitEditRequestParams = { + articleId: string; + reason: string; + articleRecordId: string; + userToken: string; +}; + +export const useSubmitEditRequest = ():UseMutationResult< + string, + AxiosError, + SubmitEditRequestParams +> => { + return useMutation({ + mutationKey: ["submit-edit-request"], + + mutationFn: async ({ + articleId, + reason, + articleRecordId, + userToken, + }: SubmitEditRequestParams) => { + const res = await axios.post( + REQUEST_EDIT, + { + article_id: articleId, + edit_reason: reason, + article_recordId: articleRecordId, + }, + { + headers: { + Authorization: `Bearer ${userToken}`, + }, + } + ); + + return res.data.message as string; + }, + + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useSubmitReport.ts b/frontend/src/hooks/useSubmitReport.ts new file mode 100644 index 0000000..029dd73 --- /dev/null +++ b/frontend/src/hooks/useSubmitReport.ts @@ -0,0 +1,47 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import axios, { AxiosError } from "axios"; +import { SUBMIT_REPORT } from "../helper/APIUtils"; + +type SubmitReportParams = { + articleId?: number | null; + podcastId?: number | null; + commentId?: number | null; + reportedBy: number; + reasonId: number; + authorId: number; +}; + +export const useSubmitReport = ():UseMutationResult< +any, +AxiosError, +SubmitReportParams +> => { + return useMutation({ + mutationKey: ["submit-report"], + mutationFn: async ({ + articleId, + podcastId, + commentId, + reportedBy, + reasonId, + authorId, + }: SubmitReportParams) => { + + const res = await axios.post( + SUBMIT_REPORT, + { + articleId: podcastId ? null : articleId, + podcastId: podcastId, + commentId: commentId, + reportedBy: reportedBy, + reasonId: reasonId, + authorId: authorId, + }, + ); + + return { data: res.data }; + }, + + + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useSubmitSuggestedChanges.ts b/frontend/src/hooks/useSubmitSuggestedChanges.ts new file mode 100644 index 0000000..9ee8728 --- /dev/null +++ b/frontend/src/hooks/useSubmitSuggestedChanges.ts @@ -0,0 +1,51 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import axios, { AxiosError } from "axios"; +import { SUBMIT_SUGGESTED_CHANGES } from "../helper/APIUtils"; + +type SubmitSuggestedChangesParams = { + article: string; + title: string; + userId: number; + authorName: string; + articleId: string; + tags: string[]; + imageUtils: any; + description: string; +}; + +export const useSubmitSuggestedChanges = (): UseMutationResult< +any, +AxiosError, +SubmitSuggestedChangesParams +> => { + return useMutation({ + mutationKey: ["submit-post-key"], + + mutationFn: async ({ + article, + title, + userId, + authorName, + articleId, + tags, + imageUtils, + description, + }: SubmitSuggestedChangesParams) => { + const response = await axios.post( + SUBMIT_SUGGESTED_CHANGES, + { + title, + userId, + authorName, + articleId, + content: article, + tags, + imageUtils, + description, + } + ); + + return { data: response.data.newArticle}; + }, + }); +}; \ No newline at end of file diff --git a/frontend/src/screens/auth/SignUpScreenFirst.tsx b/frontend/src/screens/auth/SignUpScreenFirst.tsx index 61583ce..b89851f 100644 --- a/frontend/src/screens/auth/SignUpScreenFirst.tsx +++ b/frontend/src/screens/auth/SignUpScreenFirst.tsx @@ -86,7 +86,7 @@ const SignupPageFirst = ({navigation}: SignUpScreenFirstProp) => { }); }; - const checkUserHandleAvailability = async handle => { + const checkUserHandleAvailability = async (handle:string) => { try { const response = await axios.post(CHECK_USER_HANDLE, { userHandle: username, From 8cb89699866e4acf0c8c54dd3efafc5c7a9ff18e Mon Sep 17 00:00:00 2001 From: SB2318 Date: Wed, 11 Mar 2026 13:56:18 +0530 Subject: [PATCH 5/6] Add new hooks for fetching article content, improvements, user articles, and social circle data --- frontend/src/hooks/useGetArticleContent.ts | 15 +++++ frontend/src/hooks/useGetImprovementById.ts | 17 +++++ .../src/hooks/useGetImprovementContent.ts | 31 +++++++++ frontend/src/hooks/useGetUserAllArticles.ts | 65 +++++++++++++++++++ .../src/hooks/useGetUserAllImprovements.ts | 65 +++++++++++++++++++ frontend/src/hooks/useGetUserSocialCircle.ts | 37 +++++++++++ frontend/src/hooks/useRequestArticleEdit.ts | 36 ++++++++++ frontend/src/hooks/useSubmitImprovement.ts | 35 ++++++++++ .../src/hooks/useUploadArticlePocketbase.ts | 25 +++++++ frontend/src/screens/SocialScreen.tsx | 2 +- 10 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 frontend/src/hooks/useGetArticleContent.ts create mode 100644 frontend/src/hooks/useGetImprovementById.ts create mode 100644 frontend/src/hooks/useGetImprovementContent.ts create mode 100644 frontend/src/hooks/useGetUserAllArticles.ts create mode 100644 frontend/src/hooks/useGetUserAllImprovements.ts create mode 100644 frontend/src/hooks/useGetUserSocialCircle.ts create mode 100644 frontend/src/hooks/useRequestArticleEdit.ts create mode 100644 frontend/src/hooks/useSubmitImprovement.ts create mode 100644 frontend/src/hooks/useUploadArticlePocketbase.ts diff --git a/frontend/src/hooks/useGetArticleContent.ts b/frontend/src/hooks/useGetArticleContent.ts new file mode 100644 index 0000000..0b089b2 --- /dev/null +++ b/frontend/src/hooks/useGetArticleContent.ts @@ -0,0 +1,15 @@ +import {useQuery, UseQueryResult} from '@tanstack/react-query'; +import axios, {AxiosError} from 'axios'; +import {GET_ARTICLE_CONTENT} from '../helper/APIUtils'; + +export const useGetArticleContent = ( + recordId: string, +): UseQueryResult => { + return useQuery({ + queryKey: ['get-article-content', recordId], + queryFn: async () => { + const response = await axios.get(`${GET_ARTICLE_CONTENT}/${recordId}`); + return response.data.htmlContent as string; + }, + }); +}; diff --git a/frontend/src/hooks/useGetImprovementById.ts b/frontend/src/hooks/useGetImprovementById.ts new file mode 100644 index 0000000..e8fb2aa --- /dev/null +++ b/frontend/src/hooks/useGetImprovementById.ts @@ -0,0 +1,17 @@ +import {useQuery, UseQueryResult} from '@tanstack/react-query'; +import {EditRequest} from '../type'; +import axios, {AxiosError} from 'axios'; +import {GET_IMPROVEMENT_BY_ID} from '../helper/APIUtils'; + +export const useGetImprovementById = ( + requestId: string, +): UseQueryResult => { + return useQuery({ + queryKey: ['get-improvement-by-id', requestId], + queryFn: async () => { + const response = await axios.get(`${GET_IMPROVEMENT_BY_ID}/${requestId}`); + + return response.data as EditRequest; + }, + }); +}; diff --git a/frontend/src/hooks/useGetImprovementContent.ts b/frontend/src/hooks/useGetImprovementContent.ts new file mode 100644 index 0000000..f52e915 --- /dev/null +++ b/frontend/src/hooks/useGetImprovementContent.ts @@ -0,0 +1,31 @@ +import { useQuery } from '@tanstack/react-query'; +import axios from 'axios'; +import { GET_IMPROVEMENT_CONTENT } from '../helper/APIUtils'; + +interface Props { + recordId?: string; + articleRecordId?: string; +} + +export const useGetImprovementContent = ({ + recordId, + articleRecordId, +}: Props) => { + return useQuery({ + queryKey: ['get-improvement-content', recordId, articleRecordId], + queryFn: async () => { + let url = ''; + + if (recordId) { + url = `${GET_IMPROVEMENT_CONTENT}?articleRecordId=${articleRecordId}`; + } else { + url = `${GET_IMPROVEMENT_CONTENT}?recordid=${recordId}&articleRecordId=${articleRecordId}`; + } + + const response = await axios.get(url); + + return response.data.htmlContent as string; + }, + enabled: !!articleRecordId, + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useGetUserAllArticles.ts b/frontend/src/hooks/useGetUserAllArticles.ts new file mode 100644 index 0000000..0e5adde --- /dev/null +++ b/frontend/src/hooks/useGetUserAllArticles.ts @@ -0,0 +1,65 @@ +import { useQuery } from '@tanstack/react-query'; +import axios from 'axios'; +import { ArticleData } from '../type'; +import { GET_ALL_ARTICLES_FOR_USER } from '../helper/APIUtils'; + +interface Props { + page: number; + selectedStatus: string; + visit: number; + setVisit: (v: number) => void; + setTotalPages: (v: number) => void; + setArticleData: React.Dispatch>; + setPublishedLabel: (v: string) => void; + setProgressLabel: (v: string) => void; + setDiscardLabel: (v: string) => void; +} + +export const useGetAllArticlesForUser = ({ + page, + selectedStatus, + visit, + setVisit, + setTotalPages, + setArticleData, + setPublishedLabel, + setProgressLabel, + setDiscardLabel, +}: Props) => { + return useQuery({ + queryKey: ['get-all-articles-for-user', page, selectedStatus], + queryFn: async () => { + const response = await axios.get( + `${GET_ALL_ARTICLES_FOR_USER}?page=${page}&status=${selectedStatus}` + ); + + const data = response.data; + + if (Number(page) === 1 && data.totalPages) { + setTotalPages(data.totalPages); + setArticleData(data.articles); + } else if (Array.isArray(data.articles)) { + setArticleData(prev => [...prev, ...data.articles]); + } + + if (Number(visit) === 1) { + if (data.publishedCount) { + setPublishedLabel(`Published(${data.publishedCount})`); + } + + if (data.progressCount) { + setProgressLabel(`Progress(${data.progressCount})`); + } + + if (data.discardCount) { + setDiscardLabel(`Discarded(${data.discardCount})`); + } + + setVisit(0); + } + + return data.articles as ArticleData[]; + }, + enabled: !!page, + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useGetUserAllImprovements.ts b/frontend/src/hooks/useGetUserAllImprovements.ts new file mode 100644 index 0000000..b479673 --- /dev/null +++ b/frontend/src/hooks/useGetUserAllImprovements.ts @@ -0,0 +1,65 @@ +import { useQuery } from '@tanstack/react-query'; +import axios from 'axios'; +import { EditRequest } from '../type'; +import { GET_ALL_IMPROVEMENTS_FOR_USER } from '../helper/APIUtils'; + +interface Props { + page: number; + selectedStatus: string; + visit: number; + setVisit: (v: number) => void; + setTotalPages: (v: number) => void; + setImprovementData: React.Dispatch>; + setPublishedLabel: (v: string) => void; + setProgressLabel: (v: string) => void; + setDiscardLabel: (v: string) => void; +} + +export const useGetAllImprovementsForReview = ({ + page, + selectedStatus, + visit, + setVisit, + setTotalPages, + setImprovementData, + setPublishedLabel, + setProgressLabel, + setDiscardLabel, +}: Props) => { + return useQuery({ + queryKey: ['get-all-improvements-for-review', page, selectedStatus], + queryFn: async () => { + const response = await axios.get( + `${GET_ALL_IMPROVEMENTS_FOR_USER}?page=${page}&status=${selectedStatus}` + ); + + const data = response.data; + + if (Number(page) === 1 && data.totalPages) { + setTotalPages(data.totalPages); + setImprovementData(data.articles); + } else if (data.articles) { + setImprovementData(prev => [...prev, ...data.articles]); + } + + if (Number(visit) === 1) { + if (data.publishedCount) { + setPublishedLabel(`Published(${data.publishedCount})`); + } + + if (data.progressCount) { + setProgressLabel(`Progress(${data.progressCount})`); + } + + if (data.discardCount) { + setDiscardLabel(`Discarded(${data.discardCount})`); + } + + setVisit(0); + } + + return data.articles as EditRequest[]; + }, + enabled: !!page, + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useGetUserSocialCircle.ts b/frontend/src/hooks/useGetUserSocialCircle.ts new file mode 100644 index 0000000..53a2a97 --- /dev/null +++ b/frontend/src/hooks/useGetUserSocialCircle.ts @@ -0,0 +1,37 @@ + +import { useQuery } from '@tanstack/react-query'; +import axios from 'axios'; +import { GET_SOCIALS } from '../helper/APIUtils'; +import { User } from '../type'; + +interface Props { + type: string; + articleId?: string; + social_user_id?: string; +} + +export const useGetUserSocials = ({ + type, + articleId, + social_user_id, +}: Props) => { + return useQuery({ + queryKey: ['get-user-socials', type, articleId, social_user_id], + queryFn: async () => { + let url = articleId + ? `${GET_SOCIALS}?type=${type}&articleId=${articleId}` + : `${GET_SOCIALS}?type=${type}`; + + if (social_user_id && social_user_id !== '') { + url = `${url}&social_user_id=${social_user_id}`; + } + + console.log('Request Url', url); + + const response = await axios.get(url); + + return response.data.followers as User[]; + }, + enabled: !!type, + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/useRequestArticleEdit.ts b/frontend/src/hooks/useRequestArticleEdit.ts new file mode 100644 index 0000000..41d8e05 --- /dev/null +++ b/frontend/src/hooks/useRequestArticleEdit.ts @@ -0,0 +1,36 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import axios, { AxiosError } from "axios"; +import { REQUEST_EDIT } from "../helper/APIUtils"; + +type ArticleEditReq = { + articleId: string; + reason: string; + articleRecordId: string; +}; + +export const useRequestArticleEdit = (): UseMutationResult< + string, + AxiosError, + ArticleEditReq +>=>{ + return useMutation({ + mutationKey: ['submit-edit-request'], + mutationFn: async ({ + articleId, + reason, + articleRecordId, + }:ArticleEditReq) => { + const res = await axios.post( + REQUEST_EDIT, + { + article_id: articleId, + edit_reason: reason, + article_recordId: articleRecordId, + } + ); + + return res.data.message as string; + }, + + }); +} diff --git a/frontend/src/hooks/useSubmitImprovement.ts b/frontend/src/hooks/useSubmitImprovement.ts new file mode 100644 index 0000000..fdfd0c1 --- /dev/null +++ b/frontend/src/hooks/useSubmitImprovement.ts @@ -0,0 +1,35 @@ +import {useMutation, UseMutationResult} from '@tanstack/react-query'; +import {ArticleData} from '../type'; +import axios, {AxiosError} from 'axios'; +import {SUBMIT_IMPROVEMENT} from '../helper/APIUtils'; + +type SubmitReq = { + edited_content: string; + recordId: string; + requestId: string; + imageUtils: string[]; +}; +export const useSubmitImprovement = (): UseMutationResult< + ArticleData, + AxiosError, + SubmitReq +> => { + return useMutation({ + mutationKey: ['submiit-improvemeny-key'], + mutationFn: async ({ + edited_content, + recordId, + requestId, + imageUtils, + }: SubmitReq) => { + const response = await axios.post(SUBMIT_IMPROVEMENT, { + requestId: requestId, + edited_content: edited_content, + pb_recordId: recordId, + imageUtils: imageUtils, + }); + // console.log(article); + return response.data.newArticle as ArticleData; + }, + }); +}; diff --git a/frontend/src/hooks/useUploadArticlePocketbase.ts b/frontend/src/hooks/useUploadArticlePocketbase.ts new file mode 100644 index 0000000..27bba7f --- /dev/null +++ b/frontend/src/hooks/useUploadArticlePocketbase.ts @@ -0,0 +1,25 @@ +import {useMutation} from '@tanstack/react-query'; +import axios from 'axios'; +import {ArticleData, PocketBaseResponse} from '../type'; +import {UPLOAD_ARTICLE_TO_POCKETBASE} from '../helper/APIUtils'; + +interface Props { + title: string; + articleData?: ArticleData; + htmlContent: string; +} + +export const useUploadArticleToPocketbase = () => { + return useMutation({ + mutationKey: ['upload-article-to-pocketbase-key'], + mutationFn: async ({title, articleData, htmlContent}: Props) => { + const response = await axios.post(UPLOAD_ARTICLE_TO_POCKETBASE, { + title: title, + htmlContent: htmlContent, + record_id: articleData ? articleData.pb_recordId : null, + }); + + return response.data as PocketBaseResponse; + }, + }); +}; diff --git a/frontend/src/screens/SocialScreen.tsx b/frontend/src/screens/SocialScreen.tsx index ecc1498..d1ce280 100644 --- a/frontend/src/screens/SocialScreen.tsx +++ b/frontend/src/screens/SocialScreen.tsx @@ -55,7 +55,7 @@ export default function Socialcreen({navigation, route}: SocialScreenProps) { Authorization: `Bearer ${user_token}`, }, }); - console.log('Response', response.data.followers); + // console.log('Response', response.data.followers); return response.data.followers as User[]; }, }); From 0d32e61d6338d17cb1810342ce8fd6c38da8ccab Mon Sep 17 00:00:00 2001 From: SB2318 Date: Thu, 12 Mar 2026 01:02:46 +0530 Subject: [PATCH 6/6] Refactor article repost functionality and user handle availability checks. Implement hooks for reposting articles and checking user handle availability, enhancing error handling and user feedback in the password reset and signup processes. --- frontend/src/hooks/useArticleRepost.ts | 8 +- frontend/src/hooks/useCheckUserHandle.ts | 2 +- frontend/src/screens/HomeScreen.tsx | 177 +++--- frontend/src/screens/ProfileScreen.tsx | 150 +++-- frontend/src/screens/UserProfileScreen.tsx | 170 +++--- .../src/screens/auth/NewPasswordScreen.tsx | 519 ++++++++++-------- .../src/screens/auth/SignUpScreenFirst.tsx | 62 ++- 7 files changed, 605 insertions(+), 483 deletions(-) diff --git a/frontend/src/hooks/useArticleRepost.ts b/frontend/src/hooks/useArticleRepost.ts index 0249652..45295b4 100644 --- a/frontend/src/hooks/useArticleRepost.ts +++ b/frontend/src/hooks/useArticleRepost.ts @@ -3,11 +3,11 @@ import {REPOST_ARTICLE} from '../helper/APIUtils'; import axios, {AxiosError} from 'axios'; export const useRepostArticle = ( - articleId: number, -): UseMutationResult => { + +): UseMutationResult => { return useMutation({ - mutationKey: [`repost-article`, articleId], - mutationFn: async () => { + mutationKey: [`repost-article`], + mutationFn: async ( articleId: number,) => { const res = await axios.post(REPOST_ARTICLE, { articleId: articleId, }); diff --git a/frontend/src/hooks/useCheckUserHandle.ts b/frontend/src/hooks/useCheckUserHandle.ts index c7adcbb..5c7fc98 100644 --- a/frontend/src/hooks/useCheckUserHandle.ts +++ b/frontend/src/hooks/useCheckUserHandle.ts @@ -12,7 +12,7 @@ export const useCheckUserHandleAvailability = (handle: string) => { return response.data; }, - enabled: !!handle, + enabled: handle.length > 2, retry: false, }); }; \ No newline at end of file diff --git a/frontend/src/screens/HomeScreen.tsx b/frontend/src/screens/HomeScreen.tsx index f793de0..ef1aba4 100644 --- a/frontend/src/screens/HomeScreen.tsx +++ b/frontend/src/screens/HomeScreen.tsx @@ -26,7 +26,6 @@ import { ARTICLE_TAGS_API, GET_PROFILE_API, PROD_URL, - REPOST_ARTICLE, REQUEST_EDIT, } from '../helper/APIUtils'; import FilterModal from '../components/FilterModal'; @@ -49,6 +48,7 @@ import {useFocusEffect} from '@react-navigation/native'; import InactiveUserModal from '../components/InactiveUserModal'; import {StatusBar} from 'expo-status-bar'; import {wp} from '../helper/Metric'; +import {useRepostArticle} from '../hooks/useArticleRepost'; // Here The purpose of using Redux is to maintain filter state throughout the app session. globally const HomeScreen = ({navigation}: HomeScreenProps) => { @@ -61,6 +61,8 @@ const HomeScreen = ({navigation}: HomeScreenProps) => { const [repostItem, setRepostItem] = useState(null); const [selectCategoryList, setSelectCategoryList] = useState([]); const [filterLoading, setFilterLoading] = useState(false); + + const {mutate: repost, isPending: repostPending} = useRepostArticle(); const { filteredArticles, searchedArticles, @@ -193,79 +195,109 @@ const HomeScreen = ({navigation}: HomeScreenProps) => { }; const handleRepostAction = (item: ArticleData) => { - if (isConnected) { - setRepostItem(item); - repostMutation.mutate({ - articleId: Number(item._id), - }); - } else { + if (!isConnected) { Snackbar.show({ text: 'Please check your network connection', duration: Snackbar.LENGTH_SHORT, }); + return; } - }; - const repostMutation = useMutation({ - mutationKey: ['repost-user-article'], - mutationFn: async ({ - articleId, - }: // authorId, - { - articleId: number; - // authorId: string; - }) => { - if (user_token === '') { - Alert.alert('No token found'); - return; - } - const res = await axios.post( - REPOST_ARTICLE, - { - articleId: articleId, - }, - { - headers: { - Authorization: `Bearer ${user_token}`, - }, - }, - ); + repost(Number(item._id), { + onSuccess: () => { + refetch(); - return res.data as any; - }, - onSuccess: () => { - refetch(); - Snackbar.show({ - text: 'Article reposted in your feed', - duration: Snackbar.LENGTH_SHORT, - }); + Snackbar.show({ + text: 'Article reposted in your feed', + duration: Snackbar.LENGTH_SHORT, + }); - if (repostItem) { const body = { type: 'repost', userId: user_id, - authorId: repostItem.authorId, - postId: repostItem._id, - articleRecordId: repostItem.pb_recordId, + authorId: item.authorId, + postId: item._id, + articleRecordId: item.pb_recordId, message: { title: `${user_handle} reposted`, - message: `${repostItem.title}`, + message: `${item.title}`, }, authorMessage: { title: `${user_handle} reposted your article`, - message: `${repostItem.title}`, + message: `${item.title}`, }, }; socket.emit('notification', body); - } - }, + }, - onError: error => { - console.log('Repost Error', error); - Alert.alert('Internal server error, try again!'); - }, - }); + onError: error => { + console.log('Repost Error', error); + Alert.alert('Internal server error, try again!'); + }, + }); + }; + + // const repostMutation = useMutation({ + // mutationKey: ['repost-user-article'], + // mutationFn: async ({ + // articleId, + // }: // authorId, + // { + // articleId: number; + // // authorId: string; + // }) => { + // if (user_token === '') { + // Alert.alert('No token found'); + // return; + // } + // const res = await axios.post( + // REPOST_ARTICLE, + // { + // articleId: articleId, + // }, + // { + // headers: { + // Authorization: `Bearer ${user_token}`, + // }, + // }, + // ); + + // return res.data as any; + // }, + // onSuccess: () => { + // refetch(); + // Snackbar.show({ + // text: 'Article reposted in your feed', + // duration: Snackbar.LENGTH_SHORT, + // }); + + // if (repostItem) { + // const body = { + // type: 'repost', + // userId: user_id, + // authorId: repostItem.authorId, + // postId: repostItem._id, + // articleRecordId: repostItem.pb_recordId, + // message: { + // title: `${user_handle} reposted`, + // message: `${repostItem.title}`, + // }, + // authorMessage: { + // title: `${user_handle} reposted your article`, + // message: `${repostItem.title}`, + // }, + // }; + + // socket.emit('notification', body); + // } + // }, + + // onError: error => { + // console.log('Repost Error', error); + // Alert.alert('Internal server error, try again!'); + // }, + // }); const submitEditRequestMutation = useMutation({ mutationKey: ['submit-edit-request'], @@ -351,22 +383,21 @@ const HomeScreen = ({navigation}: HomeScreenProps) => { // Update Redux State Variables console.log('enter'); if (selectCategoryList.length > 0) { - // console.log("enter") + // console.log("enter") dispatch(setSelectedTags({selectedTags: selectCategoryList})); } else { - //console.log("enter ele", articleCategories); + //console.log("enter ele", articleCategories); dispatch( setSelectedTags({ selectedTags: articleCategories, }), ); - } - if(sortingType && sortingType !== ''){ - console.log("Sort type", sortType); - dispatch(setSortType({sortType: sortingType})); + if (sortingType && sortingType !== '') { + console.log('Sort type', sortType); + dispatch(setSortType({sortType: sortingType})); } if (sortingType && sortingType !== '') { @@ -480,19 +511,17 @@ const HomeScreen = ({navigation}: HomeScreenProps) => { } }; - const listData = useMemo(() => { - if (searchMode) return searchedArticles; + const listData = useMemo(() => { + if (searchMode) return searchedArticles; - const filtered = filteredArticles.filter( - (article:ArticleData) => - article.tags && - article.tags.some( - tag => tag.name === selectedCategory?.name, - ), - ); + const filtered = filteredArticles.filter( + (article: ArticleData) => + article.tags && + article.tags.some(tag => tag.name === selectedCategory?.name), + ); - return filtered.sort(() => Math.random() - 0.5); -}, [searchMode, searchedArticles, filteredArticles, selectedCategory]); + return filtered.sort(() => Math.random() - 0.5); + }, [searchMode, searchedArticles, filteredArticles, selectedCategory]); if (!articleData || articleData.length === 0) { return ( @@ -604,7 +633,7 @@ const HomeScreen = ({navigation}: HomeScreenProps) => { ))} - + { @@ -622,8 +651,6 @@ const HomeScreen = ({navigation}: HomeScreenProps) => { ); } - - return ( { {((filteredArticles && filteredArticles.length > 0) || searchedArticles.length > 0) && ( item._id.toString()} contentContainerStyle={styles.flatListContentContainer} diff --git a/frontend/src/screens/ProfileScreen.tsx b/frontend/src/screens/ProfileScreen.tsx index 0449728..d0bf764 100644 --- a/frontend/src/screens/ProfileScreen.tsx +++ b/frontend/src/screens/ProfileScreen.tsx @@ -10,7 +10,6 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'; import ProfileHeader from '../components/ProfileHeader'; import { GET_PROFILE_API, - REPOST_ARTICLE, UPDATE_VIEW_COUNT, } from '../helper/APIUtils'; import {ArticleData, ProfileScreenProps, User} from '../type'; @@ -21,6 +20,7 @@ import {useFocusEffect} from '@react-navigation/native'; import Snackbar from 'react-native-snackbar'; import {useSocket} from '../../SocketContext'; import {setUserHandle} from '../store/UserSlice'; +import { useRepostArticle } from '../hooks/useArticleRepost'; const ProfileScreen = ({navigation}: ProfileScreenProps) => { const {user_handle, user_id, user_token} = useSelector( @@ -36,6 +36,8 @@ const ProfileScreen = ({navigation}: ProfileScreenProps) => { const socket = useSocket(); const dispatch = useDispatch(); + const {mutate: repost, isPending: repostPending} = useRepostArticle(); + const { data: user, refetch, @@ -138,83 +140,111 @@ const ProfileScreen = ({navigation}: ProfileScreenProps) => { ); // eslint-disable-next-line react-hooks/exhaustive-deps - const handleRepostAction = (item: ArticleData) => { - if (isConnected) { - // updateLikeMutation.mutate(); - setRepostItem(item); - - repostMutation.mutate({ - articleId: Number(item._id), - }); - } else { + const handleRepostAction = (item: ArticleData) => { + if (!isConnected) { Snackbar.show({ text: 'Please check your network connection', duration: Snackbar.LENGTH_SHORT, }); + return; } - }; - const repostMutation = useMutation({ - mutationKey: ['repost-user-article'], - mutationFn: async ({ - articleId, - }: // authorId, - { - articleId: number; - // authorId: string; - }) => { - if (user_token === '') { - Alert.alert('No token found'); - return; - } - const res = await axios.post( - REPOST_ARTICLE, - { - articleId: articleId, - }, - { - headers: { - Authorization: `Bearer ${user_token}`, - }, - }, - ); + repost(Number(item._id), { + onSuccess: () => { + refetch(); - return res.data as any; - }, - onSuccess: () => { - refetch(); - Snackbar.show({ - text: 'Article reposted in your feed', - duration: Snackbar.LENGTH_SHORT, - }); + Snackbar.show({ + text: 'Article reposted in your feed', + duration: Snackbar.LENGTH_SHORT, + }); - if (repostItem) { - //emitNotification(repostItem); - socket.emit('notification', { + const body = { type: 'repost', userId: user_id, - authorId: repostItem.authorId, - postId: repostItem._id, - articleRecordId: repostItem.pb_recordId, + authorId: item.authorId, + postId: item._id, + articleRecordId: item.pb_recordId, message: { title: `${user_handle} reposted`, - message: `${repostItem.title}`, + message: `${item.title}`, }, authorMessage: { title: `${user_handle} reposted your article`, - message: `${repostItem.title}`, + message: `${item.title}`, }, - }); - } + }; - // Emit notification - }, + socket.emit('notification', body); + }, - onError: error => { - console.log('Repost Error', error); - Alert.alert('Internal server error, try again!'); - }, - }); + onError: error => { + console.log('Repost Error', error); + Alert.alert('Internal server error, try again!'); + }, + }); + }; + + // const repostMutation = useMutation({ + // mutationKey: ['repost-user-article'], + // mutationFn: async ({ + // articleId, + // }: // authorId, + // { + // articleId: number; + // // authorId: string; + // }) => { + // if (user_token === '') { + // Alert.alert('No token found'); + // return; + // } + // const res = await axios.post( + // REPOST_ARTICLE, + // { + // articleId: articleId, + // }, + // { + // headers: { + // Authorization: `Bearer ${user_token}`, + // }, + // }, + // ); + + // return res.data as any; + // }, + // onSuccess: () => { + // refetch(); + // Snackbar.show({ + // text: 'Article reposted in your feed', + // duration: Snackbar.LENGTH_SHORT, + // }); + + // if (repostItem) { + // //emitNotification(repostItem); + // socket.emit('notification', { + // type: 'repost', + // userId: user_id, + // authorId: repostItem.authorId, + // postId: repostItem._id, + // articleRecordId: repostItem.pb_recordId, + // message: { + // title: `${user_handle} reposted`, + // message: `${repostItem.title}`, + // }, + // authorMessage: { + // title: `${user_handle} reposted your article`, + // message: `${repostItem.title}`, + // }, + // }); + // } + + // // Emit notification + // }, + + // onError: error => { + // console.log('Repost Error', error); + // Alert.alert('Internal server error, try again!'); + // }, + // }); // eslint-disable-next-line react-hooks/exhaustive-deps const handleReportAction = (item: ArticleData) => { navigation.navigate('ReportScreen', { diff --git a/frontend/src/screens/UserProfileScreen.tsx b/frontend/src/screens/UserProfileScreen.tsx index 6b32911..296cfdd 100644 --- a/frontend/src/screens/UserProfileScreen.tsx +++ b/frontend/src/screens/UserProfileScreen.tsx @@ -10,7 +10,6 @@ import ProfileHeader from '../components/ProfileHeader'; import { FOLLOW_USER, PROD_URL, - REPOST_ARTICLE, REQUEST_EDIT, UPDATE_VIEW_COUNT, } from '../helper/APIUtils'; @@ -22,6 +21,7 @@ import Loader from '../components/Loader'; import {useFocusEffect} from '@react-navigation/native'; import Snackbar from 'react-native-snackbar'; import {useSocket} from '../../SocketContext'; +import {useRepostArticle} from '../hooks/useArticleRepost'; const UserProfileScreen = ({navigation, route}: UserProfileScreenProp) => { const {authorId, author_handle} = route.params; @@ -38,6 +38,8 @@ const UserProfileScreen = ({navigation, route}: UserProfileScreenProp) => { //const [authorId, setAuthorId] = useState(''); const socket = useSocket(); + const {mutate: repost, isPending: repostPending} = useRepostArticle(); + const { data: user, refetch, @@ -69,19 +71,47 @@ const UserProfileScreen = ({navigation, route}: UserProfileScreenProp) => { // eslint-disable-next-line react-hooks/exhaustive-deps const handleRepostAction = (item: ArticleData) => { - if (isConnected) { - // updateLikeMutation.mutate(); - setRepostItem(item); - - repostMutation.mutate({ - articleId: Number(item._id), - }); - } else { + if (!isConnected) { Snackbar.show({ text: 'Please check your network connection', duration: Snackbar.LENGTH_SHORT, }); + return; } + + repost(Number(item._id), { + onSuccess: () => { + refetch(); + + Snackbar.show({ + text: 'Article reposted in your feed', + duration: Snackbar.LENGTH_SHORT, + }); + + const body = { + type: 'repost', + userId: user_id, + authorId: item.authorId, + postId: item._id, + articleRecordId: item.pb_recordId, + message: { + title: `${user_handle} reposted`, + message: `${item.title}`, + }, + authorMessage: { + title: `${user_handle} reposted your article`, + message: `${item.title}`, + }, + }; + + socket.emit('notification', body); + }, + + onError: error => { + console.log('Repost Error', error); + Alert.alert('Internal server error, try again!'); + }, + }); }; const onArticleViewed = ({ @@ -107,66 +137,66 @@ const UserProfileScreen = ({navigation, route}: UserProfileScreenProp) => { } }; - const repostMutation = useMutation({ - mutationKey: ['repost-user-article'], - mutationFn: async ({ - articleId, - }: // authorId, - { - articleId: number; - // authorId: string; - }) => { - if (user_token === '') { - Alert.alert('No token found'); - return; - } - const res = await axios.post( - REPOST_ARTICLE, - { - articleId: articleId, - }, - { - headers: { - Authorization: `Bearer ${user_token}`, - }, - }, - ); - - return res.data as any; - }, - onSuccess: () => { - //refetch(); - Snackbar.show({ - text: 'Article reposted in your feed', - duration: Snackbar.LENGTH_SHORT, - }); - - // Emit notification - if (repostItem) { - //emitNotification(repostItem); - socket.emit('notification', { - type: 'repost', - userId: user_id, - authorId: repostItem.authorId, - postId: repostItem._id, - articleRecordId: repostItem.pb_recordId, - message: { - title: `${user_handle} reposted`, - message: `${repostItem.title}`, - }, - authorMessage: { - title: `${user_handle} reposted your article`, - message: `${repostItem.title}`, - }, - }); - } - }, - - onError: error => { - console.log('Repost Error', error); - Alert.alert('Internal server error, try again!'); - }, - }); + // const repostMutation = useMutation({ + // mutationKey: ['repost-user-article'], + // mutationFn: async ({ + // articleId, + // }: // authorId, + // { + // articleId: number; + // // authorId: string; + // }) => { + // if (user_token === '') { + // Alert.alert('No token found'); + // return; + // } + // const res = await axios.post( + // REPOST_ARTICLE, + // { + // articleId: articleId, + // }, + // { + // headers: { + // Authorization: `Bearer ${user_token}`, + // }, + // }, + // ); + + // return res.data as any; + // }, + // onSuccess: () => { + // //refetch(); + // Snackbar.show({ + // text: 'Article reposted in your feed', + // duration: Snackbar.LENGTH_SHORT, + // }); + + // // Emit notification + // if (repostItem) { + // //emitNotification(repostItem); + // socket.emit('notification', { + // type: 'repost', + // userId: user_id, + // authorId: repostItem.authorId, + // postId: repostItem._id, + // articleRecordId: repostItem.pb_recordId, + // message: { + // title: `${user_handle} reposted`, + // message: `${repostItem.title}`, + // }, + // authorMessage: { + // title: `${user_handle} reposted your article`, + // message: `${repostItem.title}`, + // }, + // }); + // } + // }, + + // onError: error => { + // console.log('Repost Error', error); + // Alert.alert('Internal server error, try again!'); + // }, + // }); const updateViewCountMutation = useMutation({ mutationKey: ['update-view-count-user-profile'], @@ -553,7 +583,7 @@ const styles = StyleSheet.create({ overflow: 'hidden', }, scrollViewContentContainer: { - // paddingHorizontal: 10, + // paddingHorizontal: 10, marginTop: 16, }, flatListContentContainer: { diff --git a/frontend/src/screens/auth/NewPasswordScreen.tsx b/frontend/src/screens/auth/NewPasswordScreen.tsx index 0715fbf..eb5a1b9 100644 --- a/frontend/src/screens/auth/NewPasswordScreen.tsx +++ b/frontend/src/screens/auth/NewPasswordScreen.tsx @@ -22,6 +22,7 @@ import AntDesign from '@expo/vector-icons/AntDesign'; import {ON_PRIMARY_COLOR, PRIMARY_COLOR} from '@/src/helper/Theme'; import Entypo from '@expo/vector-icons/Entypo'; import Icon from '@expo/vector-icons/Ionicons'; +import {useChangePasswordMutation} from '@/src/hooks/useChangePassword'; export default function NewPasswordScreen({ navigation, @@ -36,6 +37,8 @@ export default function NewPasswordScreen({ const [secureTextEntry, setSecureTextEntry] = useState(true); const [secureNewTextEntry, setSecureNewTextEntry] = useState(true); + const {mutate: changePassword, isPending} = useChangePasswordMutation(); + const handleSecureEntryClickEvent = () => { setSecureTextEntry(!secureTextEntry); }; @@ -44,35 +47,71 @@ export default function NewPasswordScreen({ setSecureNewTextEntry(!secureNewTextEntry); }; - const handlePasswordSubmit = () => { - if (!password || password.length === 0) { - //setError({...error, new: true}); - //setErrorText({...errorText, new: 'Please give a password'}); - Alert.alert('Please give a password'); - setErrorMessage('Please give a password'); - return; - } else if (!passwordVerify) { - //setError({...error, new: true}); - Alert.alert('Please enter a valid password'); - setErrorMessage('Please enter a valid password'); - return; - } else if (!confirmPassword || confirmPassword.length === 0) { - //setError({...error, confirm: true}); - Alert.alert('Please confirm your password'); - setErrorMessage('Please confirm your password'); - return; - } else if (password !== confirmPassword) { - // setError({...error, confirm: true}); - Alert.alert('confirmation password does not match the new password'); - setErrorMessage('confirmation password does not match the new password'); - return; - } else { - //navigation.navigate('LoginScreen'); - setErrorMessage(null); - changePasswordMutation.mutate(); - } + const handlePasswordSubmit = () => { + const showError = (msg: string) => { + Alert.alert(msg); + setErrorMessage(msg); }; + if (!password?.trim()) { + return showError('Please give a password'); + } + + if (!passwordVerify) { + return showError('Please enter a valid password'); + } + + if (!confirmPassword?.trim()) { + return showError('Please confirm your password'); + } + + if (password !== confirmPassword) { + return showError( + 'Confirmation password does not match the new password', + ); + } + + setErrorMessage(null); + + changePassword( + { + email, + newPassword: password, + }, + { + onSuccess: () => { + Alert.alert('Password reset successfully'); + navigation.navigate('LoginScreen'); + }, + + onError: (error: AxiosError) => { + if (error.response) { + switch (error.response.status) { + case 400: + Alert.alert('Error', 'User not found'); + break; + + case 402: + Alert.alert( + 'Error', + 'New password should not be same as old password', + ); + break; + + default: + Alert.alert( + 'Error', + 'Something went wrong. Please try again.', + ); + } + } else { + Alert.alert('Error', 'Something went wrong. Please try again.'); + } + }, + }, + ); +}; + const handlePassword = e => { let pass = e; setErrorMessage(null); @@ -97,234 +136,230 @@ export default function NewPasswordScreen({ } }; - const changePasswordMutation = useMutation({ - mutationKey: ['generate-new-password'], - mutationFn: async () => { - const res = await axios.post(CHANGE_PASSWORD_API, { - email: email, - newPassword: password, - }); - return res.data as any; - }, - onSuccess: () => { - Alert.alert('Password reset successfully'); - navigation.navigate('LoginScreen'); - }, + // const changePasswordMutation = useMutation({ + // mutationKey: ['generate-new-password'], + // mutationFn: async () => { + // const res = await axios.post(CHANGE_PASSWORD_API, { + // email: email, + // newPassword: password, + // }); + // return res.data as any; + // }, + // onSuccess: () => { + // Alert.alert('Password reset successfully'); + // navigation.navigate('LoginScreen'); + // }, - onError: (error: AxiosError) => { - if (error.response) { - const statusCode = error.response.status; - switch (statusCode) { - case 400: - Alert.alert('Error', 'User not found'); - break; - case 402: - Alert.alert( - 'Error', - 'New password should not be same as old password', - ); - break; - default: - Alert.alert('Error', 'Something went wrong. Please try again.'); - } - } else { - Alert.alert('Error', 'Something went wrong. Please try again.'); - } - }, - }); + // onError: (error: AxiosError) => { + // if (error.response) { + // const statusCode = error.response.status; + // switch (statusCode) { + // case 400: + // Alert.alert('Error', 'User not found'); + // break; + // case 402: + // Alert.alert( + // 'Error', + // 'New password should not be same as old password', + // ); + // break; + // default: + // Alert.alert('Error', 'Something went wrong. Please try again.'); + // } + // } else { + // Alert.alert('Error', 'Something went wrong. Please try again.'); + // } + // }, + // }); - if (changePasswordMutation.isPending) { + if (isPending) { return ; } return ( - - - - - {/* Icon Circle */} - - - - - {/* Title & Subtitle */} - - Set your new password - + + + + + {/* Icon Circle */} + + + - {/* Inputs */} - - - - Password + {/* Title & Subtitle */} + + Set your new password - - - - - - + {/* Inputs */} + + + + Password + - - - Confirm your new password - + + + + + + - - - - - - - + + + Confirm your new password + - {/* Error Message */} - {errorMessage && ( - - {errorMessage} - - )} + + + + + + + - {/* Confirm Button */} - - - + {/* Error Message */} + {errorMessage && ( + + {errorMessage} + + )} - {/* Return Link */} - - - navigation.navigate('LoginScreen')} - > - Return to the login screen - - - - - - + {/* Confirm Button */} + + + + {/* Return Link */} + + + navigation.navigate('LoginScreen')}> + Return to the login screen + + + + + + ); } diff --git a/frontend/src/screens/auth/SignUpScreenFirst.tsx b/frontend/src/screens/auth/SignUpScreenFirst.tsx index b89851f..e15c5bd 100644 --- a/frontend/src/screens/auth/SignUpScreenFirst.tsx +++ b/frontend/src/screens/auth/SignUpScreenFirst.tsx @@ -20,7 +20,6 @@ import {useMutation} from '@tanstack/react-query'; import axios, {AxiosError} from 'axios'; import Snackbar from 'react-native-snackbar'; import { - CHECK_USER_HANDLE, REGISTRATION_API, VERIFICATION_MAIL_API, } from '../../helper/APIUtils'; @@ -33,6 +32,7 @@ import { launchImageLibrary, ImagePickerResponse, } from 'react-native-image-picker'; +import { useCheckUserHandleAvailability } from '@/src/hooks/useCheckUserHandle'; // eslint-disable-next-line @typescript-eslint/no-require-imports let validator = require('email-validator'); @@ -46,11 +46,13 @@ const SignupPageFirst = ({navigation}: SignUpScreenFirstProp) => { const [role, setRole] = useState(''); const [verifyBtntext, setVerifyBtntxt] = useState('Request Verification'); const [verifiedModalVisible, setVerifiedModalVisible] = useState(false); - const [isHandleAvailable, setIsHandleAvailable] = useState(false); + // const [isHandleAvailable, setIsHandleAvailable] = useState(false); const [token, setToken] = useState(''); const [isFocus, setIsFocus] = useState(false); const [isSecureEntry, setIsSecureEntry] = useState(true); - const [error, setError] = useState(''); + //const [error, setError] = useState(''); + + const { data: checkhandle, isLoading } = useCheckUserHandleAvailability(username); const selectImage = async () => { const options: ImageLibraryOptions = { @@ -86,34 +88,34 @@ const SignupPageFirst = ({navigation}: SignUpScreenFirstProp) => { }); }; - const checkUserHandleAvailability = async (handle:string) => { - try { - const response = await axios.post(CHECK_USER_HANDLE, { - userHandle: username, - }); - if (response.data.status === true) { - setIsHandleAvailable(true); - console.log('User Handle ', isHandleAvailable); - } else { - setIsHandleAvailable(false); - console.log('User Handle ', isHandleAvailable); - setError(response.data.error); // Show the error message - } - } catch (err) { - console.error('Error checking user handle:', err); - setError('An error occurred while checking the user handle.'); - } - }; + // const checkUserHandleAvailability = async (handle:string) => { + // try { + // const response = await axios.post(CHECK_USER_HANDLE, { + // userHandle: username, + // }); + // if (response.data.status === true) { + // setIsHandleAvailable(true); + // console.log('User Handle ', isHandleAvailable); + // } else { + // setIsHandleAvailable(false); + // console.log('User Handle ', isHandleAvailable); + // setError(response.data.error); // Show the error message + // } + // } catch (err) { + // console.error('Error checking user handle:', err); + // setError('An error occurred while checking the user handle.'); + // } + // }; const handleUserHandleChange = text => { setUsername(text); - if (text.length > 2) { - // Check if the user handle is available every time the user types - checkUserHandleAvailability(text); - } else { - setIsHandleAvailable(true); - setError(''); - } + // if (text.length > 2) { + // // Check if the user handle is available every time the user types + // checkUserHandleAvailability(text); + // } else { + // setIsHandleAvailable(true); + // setError(''); + // } }; const userRegisterMutation = useMutation({ @@ -409,12 +411,12 @@ const SignupPageFirst = ({navigation}: SignUpScreenFirstProp) => { {/* Handle Error */} - {isHandleAvailable && ( + {checkhandle?.status === false && ( User handle is already in use. )} - + {isLoading && Checking...} {/* User Handle */}