From ff2bc75fd045fda77d01c518d8a355a83964e8e8 Mon Sep 17 00:00:00 2001 From: Ujjwal Kar Date: Mon, 23 Feb 2026 00:33:25 +0530 Subject: [PATCH] feat: add hooks for profile editing, networking, news feed, and viewing profiles - Implemented `useEditProfileScreen` for managing user profile editing. - Created `useNetworkScreen` for handling connections and profile suggestions. - Added `useNewsFeedScreen` to encapsulate news feed logic. - Developed `useViewProfileScreen` for viewing user profiles and handling follow actions. - Introduced utility services for date formatting and banner message handling. - Established error resolvers for WebSocket connection errors. - Removed deprecated dependency injection and related hooks for cleaner architecture. --- App.tsx | 2 +- docker-compose.yml | 20 ++++---- .../command}/useAddEducationMutation.ts | 9 +--- .../command}/useAddWorkExperienceMutation.ts | 9 +--- src/application/command/useCreateComment.ts | 15 ++++++ .../command}/useCreatePostMutation.ts | 10 ++-- .../command}/useFollowUserMutation.ts | 10 +--- .../hooks => application/command}/useLogin.ts | 2 +- .../command/useMarkAllNotificationsAsRead.ts | 13 +++++ .../command/useMarkNotificationAsRead.ts | 13 +++++ .../command}/useRegister.ts | 2 +- .../command}/useToggleConnection.ts | 10 ++-- src/application/command/useToggleLike.ts | 23 +++++++++ .../command}/useUpdateProfileMutation.ts | 9 ++-- .../command}/useVerifyOtp.ts | 2 +- .../command}/useVerifyToken.ts | 2 +- .../handler/GetNotificationsHandler.ts | 12 ----- .../handler/MarkAllAsReadHandler.ts | 10 ---- .../post/handler/CreatePostHandler.ts | 11 ----- src/application/query/useActivityLog.ts | 10 ++++ .../hooks => application/query}/useChat.ts | 35 ++++--------- src/application/query/useComments.ts | 10 ++++ src/application/query/useConnections.ts | 9 ++++ .../query}/useCurrentUserProfile.ts | 4 +- .../query}/useNewsFeed.ts | 4 +- src/application/query/useNotifications.ts | 9 ++++ .../hooks => application/query}/useProfile.ts | 4 +- .../query/useProfileSuggestions.ts | 10 ++++ src/application/query/useViewProfile.ts | 15 ++++++ .../hooks => application}/useChatWebSocket.ts | 4 +- .../hooks => application}/useConversation.ts | 11 ++--- .../useMessageOperations.ts | 6 +-- .../useNewsFeedHeader.ts | 7 +-- .../usePasswordVisibility.ts | 0 .../hooks => application}/useSubtitleText.ts | 0 .../useTypingIndicator.ts | 0 .../hooks => application}/useUserAvatar.ts | 49 ++++++------------- src/di.ts | 26 ++++++++++ .../activity/usecases/GetActivityLog.ts | 15 ------ .../chat/usecases/ConversationUseCases.ts | 26 ---------- src/domain/chat/usecases/MessageUseCases.ts | 26 ---------- .../notification/usecases/GetNotifications.ts | 15 ------ .../usecases/MarkAllNotificationsAsRead.ts | 14 ------ .../usecases/MarkNotificationAsRead.ts | 14 ------ .../post/usecases/commands/CreateComment.ts | 15 ------ .../post/usecases/commands/CreatePost.ts | 13 ----- .../post/usecases/commands/ToggleLikePost.ts | 17 ------- .../post/usecases/queries/GetComments.ts | 15 ------ .../post/usecases/queries/GetNewsFeed.ts | 10 ---- .../post/usecases/queries/GetPostById.ts | 10 ---- .../repositories/IConnectionRepository.ts | 2 +- src/domain/user/usecases/GetConnections.ts | 18 ------- .../user/usecases/GetCurrentUserProfile.ts | 10 ---- src/domain/user/usecases/GetProfileById.ts | 10 ---- .../user/usecases/ToggleConnectionStatus.ts | 17 ------- src/domain/user/usecases/UpdateProfile.ts | 10 ---- .../types}/AddEducationForm.types.ts | 0 .../types}/AddWorkExperienceForm.types.ts | 0 .../types}/AdvancedSettingsForm.types.ts | 0 .../types}/CreatePostForm.types.ts | 0 .../types}/EditProfileForm.types.ts | 0 .../useAddEducationForm.ts | 2 +- .../useAddWorkExperienceForm.ts | 2 +- .../useAdvancedSettingsForm.ts | 2 +- .../useEditProfileForm.ts | 2 +- src/{ui/form-hooks => forms}/useLoginForm.ts | 0 .../form-hooks => forms}/useRegisterForm.ts | 2 +- .../repositories/ActivityLogRepository.ts | 0 .../repositories/AuthRepository.ts | 0 .../repositories/ChatRepository.ts | 0 .../repositories/CommentRepository.ts | 0 .../repositories/ConnectionRepository.ts | 2 +- .../repositories/NotificationRepository.ts | 0 .../repositories/PostRepository.ts | 0 .../repositories/ProfileRepository.ts | 0 .../services/ActivityLogFormatter.ts | 0 .../services/CommentFormatter.ts | 0 .../services/MediaApi.ts | 0 .../services/NotificationFormatter.ts | 0 .../components/molecules/ActivityItem.tsx | 2 +- .../components/molecules/CommentItem.tsx | 2 +- .../components/molecules/MessageItem.tsx | 2 +- .../components/molecules/NewsFeedHeader.tsx | 4 +- .../components/molecules/NotificationItem.tsx | 2 +- .../components/molecules/PostInput.tsx | 2 +- .../components/organisms/ChatHeader.tsx | 2 +- .../organisms/CommentBottomSheet.tsx | 11 +---- .../components/organisms/ConnectionBanner.tsx | 4 +- .../components/organisms/ConnectionCard.tsx | 2 +- .../components/organisms/ConversationCard.tsx | 2 +- .../components/organisms/ErrorBanner.tsx | 2 +- .../components/organisms/PollCreator.tsx | 2 +- .../components/organisms/PostList.tsx | 10 +--- .../templates/AddEducationScreenTemplate.tsx | 2 +- .../AddWorkExperienceScreenTemplate.tsx | 2 +- .../AdvancedSettingsScreenTemplate.tsx | 4 +- .../templates/ChatScreenTemplate.tsx | 2 +- .../templates/CreatePostScreenTemplate.tsx | 2 +- .../templates/EditProfileScreenTemplate.tsx | 2 +- .../screens/ActivityLogScreen.tsx | 10 +--- .../screens/AddEducationScreen.tsx | 11 +---- .../screens/AddWorkExperienceScreen.tsx | 11 +---- .../screens/AdvancedSettingsScreen.tsx | 9 +--- src/presentation/screens/ChatScreen.tsx | 14 +----- src/presentation/screens/CreatePostScreen.tsx | 11 +---- .../screens/EditProfileScreen.tsx | 11 +---- src/presentation/screens/HomeScreen.tsx | 12 +---- src/presentation/screens/LoginScreen.tsx | 7 ++- src/presentation/screens/MessagesScreen.tsx | 5 +- src/presentation/screens/NetworkScreen.tsx | 12 +---- src/presentation/screens/NewsFeedScreen.tsx | 11 +---- .../screens/NotificationsScreen.tsx | 20 +++----- .../screens/OtpValidationScreen.tsx | 2 +- src/presentation/screens/RegisterScreen.tsx | 10 ++-- src/presentation/screens/SplashScreen.tsx | 5 +- src/presentation/screens/ViewPostScreen.tsx | 6 +-- .../screens/ViewProfileScreen.tsx | 15 +----- .../screens}/hooks/useAddEducationScreen.ts | 8 +-- .../hooks/useAddWorkExperienceScreen.ts | 8 +-- .../hooks/useAdvancedSettingsScreen.ts | 6 +-- .../screens}/hooks/useChatScreen.ts | 16 +++--- .../screens}/hooks/useCreatePostScreen.ts | 10 ++-- .../screens}/hooks/useEditProfileScreen.ts | 8 +-- .../screens}/hooks/useNetworkScreen.ts | 21 +++----- .../screens}/hooks/useNewsFeedScreen.ts | 2 +- .../screens}/hooks/useViewProfileScreen.ts | 12 +---- src/presentation/utils/uploadToAWS.ts | 3 -- src/{ui => }/queryClient.ts | 0 src/{ui => }/resolvers/createPostResolver.ts | 7 ++- .../services/BannerStateResolverService.ts | 9 +--- .../services/ConnectionSubtitleService.ts | 2 +- .../services/ConversationTimeFormatter.ts | 0 src/{ui => }/services/DateFormatterService.ts | 0 .../services/constants/BannerMessages.ts | 0 .../interfaces/IErrorStateResolver.ts | 2 +- .../services/resolvers/AuthErrorResolver.ts | 8 +-- .../resolvers/ConnectionErrorResolver.ts | 2 +- .../resolvers/DefaultErrorResolver.ts | 2 +- src/ui/di.ts | 44 ----------------- src/ui/hooks/useActivityLog.ts | 14 ------ src/ui/hooks/useComments.ts | 32 ------------ src/ui/hooks/useConnections.ts | 20 -------- src/ui/hooks/useNotifications.ts | 44 ----------------- src/ui/hooks/useToggleLike.ts | 31 ------------ src/ui/hooks/useViewProfile.ts | 21 -------- 145 files changed, 339 insertions(+), 904 deletions(-) rename src/{ui/hooks => application/command}/useAddEducationMutation.ts (70%) rename src/{ui/hooks => application/command}/useAddWorkExperienceMutation.ts (63%) create mode 100644 src/application/command/useCreateComment.ts rename src/{ui/hooks => application/command}/useCreatePostMutation.ts (62%) rename src/{ui/hooks => application/command}/useFollowUserMutation.ts (54%) rename src/{ui/hooks => application/command}/useLogin.ts (86%) create mode 100644 src/application/command/useMarkAllNotificationsAsRead.ts create mode 100644 src/application/command/useMarkNotificationAsRead.ts rename src/{ui/hooks => application/command}/useRegister.ts (87%) rename src/{ui/hooks => application/command}/useToggleConnection.ts (57%) create mode 100644 src/application/command/useToggleLike.ts rename src/{ui/hooks => application/command}/useUpdateProfileMutation.ts (77%) rename src/{ui/hooks => application/command}/useVerifyOtp.ts (83%) rename src/{ui/hooks => application/command}/useVerifyToken.ts (82%) delete mode 100644 src/application/notification/handler/GetNotificationsHandler.ts delete mode 100644 src/application/notification/handler/MarkAllAsReadHandler.ts delete mode 100644 src/application/post/handler/CreatePostHandler.ts create mode 100644 src/application/query/useActivityLog.ts rename src/{ui/hooks => application/query}/useChat.ts (61%) create mode 100644 src/application/query/useComments.ts create mode 100644 src/application/query/useConnections.ts rename src/{ui/hooks => application/query}/useCurrentUserProfile.ts (59%) rename src/{ui/hooks => application/query}/useNewsFeed.ts (60%) create mode 100644 src/application/query/useNotifications.ts rename src/{ui/hooks => application/query}/useProfile.ts (63%) create mode 100644 src/application/query/useProfileSuggestions.ts create mode 100644 src/application/query/useViewProfile.ts rename src/{ui/hooks => application}/useChatWebSocket.ts (99%) rename src/{ui/hooks => application}/useConversation.ts (84%) rename src/{ui/hooks => application}/useMessageOperations.ts (89%) rename src/{ui/hooks => application}/useNewsFeedHeader.ts (61%) rename src/{ui/hooks => application}/usePasswordVisibility.ts (100%) rename src/{ui/hooks => application}/useSubtitleText.ts (100%) rename src/{ui/hooks => application}/useTypingIndicator.ts (100%) rename src/{ui/hooks => application}/useUserAvatar.ts (71%) create mode 100644 src/di.ts delete mode 100644 src/domain/activity/usecases/GetActivityLog.ts delete mode 100644 src/domain/chat/usecases/ConversationUseCases.ts delete mode 100644 src/domain/chat/usecases/MessageUseCases.ts delete mode 100644 src/domain/notification/usecases/GetNotifications.ts delete mode 100644 src/domain/notification/usecases/MarkAllNotificationsAsRead.ts delete mode 100644 src/domain/notification/usecases/MarkNotificationAsRead.ts delete mode 100644 src/domain/post/usecases/commands/CreateComment.ts delete mode 100644 src/domain/post/usecases/commands/CreatePost.ts delete mode 100644 src/domain/post/usecases/commands/ToggleLikePost.ts delete mode 100644 src/domain/post/usecases/queries/GetComments.ts delete mode 100644 src/domain/post/usecases/queries/GetNewsFeed.ts delete mode 100644 src/domain/post/usecases/queries/GetPostById.ts delete mode 100644 src/domain/user/usecases/GetConnections.ts delete mode 100644 src/domain/user/usecases/GetCurrentUserProfile.ts delete mode 100644 src/domain/user/usecases/GetProfileById.ts delete mode 100644 src/domain/user/usecases/ToggleConnectionStatus.ts delete mode 100644 src/domain/user/usecases/UpdateProfile.ts rename src/{ui/form-types => forms/types}/AddEducationForm.types.ts (100%) rename src/{ui/form-types => forms/types}/AddWorkExperienceForm.types.ts (100%) rename src/{ui/form-types => forms/types}/AdvancedSettingsForm.types.ts (100%) rename src/{ui/form-types => forms/types}/CreatePostForm.types.ts (100%) rename src/{ui/form-types => forms/types}/EditProfileForm.types.ts (100%) rename src/{ui/form-hooks => forms}/useAddEducationForm.ts (95%) rename src/{ui/form-hooks => forms}/useAddWorkExperienceForm.ts (97%) rename src/{ui/form-hooks => forms}/useAdvancedSettingsForm.ts (96%) rename src/{ui/form-hooks => forms}/useEditProfileForm.ts (96%) rename src/{ui/form-hooks => forms}/useLoginForm.ts (100%) rename src/{ui/form-hooks => forms}/useRegisterForm.ts (94%) rename src/{data => infrastructure}/repositories/ActivityLogRepository.ts (100%) rename src/{data => infrastructure}/repositories/AuthRepository.ts (100%) rename src/{data => infrastructure}/repositories/ChatRepository.ts (100%) rename src/{data => infrastructure}/repositories/CommentRepository.ts (100%) rename src/{data => infrastructure}/repositories/ConnectionRepository.ts (92%) rename src/{data => infrastructure}/repositories/NotificationRepository.ts (100%) rename src/{data => infrastructure}/repositories/PostRepository.ts (100%) rename src/{data => infrastructure}/repositories/ProfileRepository.ts (100%) rename src/{data => infrastructure}/services/ActivityLogFormatter.ts (100%) rename src/{data => infrastructure}/services/CommentFormatter.ts (100%) rename src/{data => infrastructure}/services/MediaApi.ts (100%) rename src/{data => infrastructure}/services/NotificationFormatter.ts (100%) rename src/{ui => presentation/screens}/hooks/useAddEducationScreen.ts (92%) rename src/{ui => presentation/screens}/hooks/useAddWorkExperienceScreen.ts (93%) rename src/{ui => presentation/screens}/hooks/useAdvancedSettingsScreen.ts (93%) rename src/{ui => presentation/screens}/hooks/useChatScreen.ts (88%) rename src/{ui => presentation/screens}/hooks/useCreatePostScreen.ts (90%) rename src/{ui => presentation/screens}/hooks/useEditProfileScreen.ts (91%) rename src/{ui => presentation/screens}/hooks/useNetworkScreen.ts (84%) rename src/{ui => presentation/screens}/hooks/useNewsFeedScreen.ts (91%) rename src/{ui => presentation/screens}/hooks/useViewProfileScreen.ts (88%) rename src/{ui => }/queryClient.ts (100%) rename src/{ui => }/resolvers/createPostResolver.ts (87%) rename src/{ui => }/services/BannerStateResolverService.ts (86%) rename src/{ui => }/services/ConnectionSubtitleService.ts (91%) rename src/{ui => }/services/ConversationTimeFormatter.ts (100%) rename src/{ui => }/services/DateFormatterService.ts (100%) rename src/{ui => }/services/constants/BannerMessages.ts (100%) rename src/{ui => }/services/interfaces/IErrorStateResolver.ts (86%) rename src/{ui => }/services/resolvers/AuthErrorResolver.ts (79%) rename src/{ui => }/services/resolvers/ConnectionErrorResolver.ts (92%) rename src/{ui => }/services/resolvers/DefaultErrorResolver.ts (90%) delete mode 100644 src/ui/di.ts delete mode 100644 src/ui/hooks/useActivityLog.ts delete mode 100644 src/ui/hooks/useComments.ts delete mode 100644 src/ui/hooks/useConnections.ts delete mode 100644 src/ui/hooks/useNotifications.ts delete mode 100644 src/ui/hooks/useToggleLike.ts delete mode 100644 src/ui/hooks/useViewProfile.ts diff --git a/App.tsx b/App.tsx index 54f53f5..829b2e8 100644 --- a/App.tsx +++ b/App.tsx @@ -1,7 +1,7 @@ import React, { StrictMode } from 'react'; import { QueryClientProvider } from '@tanstack/react-query'; import RootNavigator from './src/presentation/navigation/RootNavigator'; -import { queryClient } from './src/ui/queryClient'; +import { queryClient } from './src/queryClient'; import { StatusBar } from 'react-native'; import { SafeAreaProvider } from 'react-native-safe-area-context'; diff --git a/docker-compose.yml b/docker-compose.yml index 582cb01..139fe26 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,7 +46,7 @@ services: condition: service_completed_successfully networks: bizbuch_network: - ipv4_address: 172.28.0.10 + ipv4_address: 172.27.0.10 db: image: postgres:15 @@ -67,7 +67,7 @@ services: retries: 20 networks: bizbuch_network: - ipv4_address: 172.28.0.11 + ipv4_address: 172.27.0.11 redis: image: redis:7-alpine @@ -77,7 +77,7 @@ services: # - "6379:6379" networks: bizbuch_network: - ipv4_address: 172.28.0.12 + ipv4_address: 172.27.0.12 redisinsight: image: redis/redisinsight:latest @@ -89,7 +89,7 @@ services: - redis networks: bizbuch_network: - ipv4_address: 172.28.0.17 + ipv4_address: 172.27.0.17 minio: image: minio/minio:latest @@ -108,7 +108,7 @@ services: - "9001:9001" networks: bizbuch_network: - ipv4_address: 172.28.0.13 + ipv4_address: 172.27.0.13 minio_init: image: minio/mc:latest @@ -124,7 +124,7 @@ services: " networks: bizbuch_network: - ipv4_address: 172.28.0.14 + ipv4_address: 172.27.0.14 pgadmin: image: dpage/pgadmin4:latest @@ -143,7 +143,7 @@ services: - db networks: bizbuch_network: - ipv4_address: 172.28.0.16 + ipv4_address: 172.27.0.16 # Uncomment the following section to enable Cloudflare Tunnel tunnel: @@ -154,7 +154,7 @@ services: - web networks: bizbuch_network: - ipv4_address: 172.28.0.15 + ipv4_address: 172.27.0.15 volumes: postgres_data: @@ -165,5 +165,5 @@ networks: driver: bridge ipam: config: - - subnet: 172.28.0.0/16 - gateway: 172.28.0.1 + - subnet: 172.27.0.0/16 + gateway: 172.27.0.1 diff --git a/src/ui/hooks/useAddEducationMutation.ts b/src/application/command/useAddEducationMutation.ts similarity index 70% rename from src/ui/hooks/useAddEducationMutation.ts rename to src/application/command/useAddEducationMutation.ts index f6ffdaf..1faecad 100644 --- a/src/ui/hooks/useAddEducationMutation.ts +++ b/src/application/command/useAddEducationMutation.ts @@ -1,13 +1,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { ProfileRepository } from '../../data/repositories/ProfileRepository'; +import { ProfileRepository } from '../../infrastructure/repositories/ProfileRepository'; import { AddEducationData } from '../../domain/user/repositories/IProfileRepository'; -/** - * useAddEducationMutation Hook - * SOLID Principles: - * - Single Responsibility: Handle education creation mutation - * - Dependency Inversion: Depends on ProfileRepository abstraction - */ + export const useAddEducationMutation = () => { const queryClient = useQueryClient(); const profileRepo = new ProfileRepository(); diff --git a/src/ui/hooks/useAddWorkExperienceMutation.ts b/src/application/command/useAddWorkExperienceMutation.ts similarity index 63% rename from src/ui/hooks/useAddWorkExperienceMutation.ts rename to src/application/command/useAddWorkExperienceMutation.ts index e6dcc3f..7643061 100644 --- a/src/ui/hooks/useAddWorkExperienceMutation.ts +++ b/src/application/command/useAddWorkExperienceMutation.ts @@ -1,13 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { ProfileRepository } from '../../data/repositories/ProfileRepository'; +import { ProfileRepository } from '../../infrastructure/repositories/ProfileRepository'; import { AddWorkExperienceData } from '../../domain/user/repositories/IProfileRepository'; -/** - * useAddWorkExperienceMutation Hook - * SOLID Principles: - * - Single Responsibility: Handle work experience creation mutation - * - Dependency Inversion: Depends on ProfileRepository abstraction - */ export const useAddWorkExperienceMutation = () => { const queryClient = useQueryClient(); const profileRepo = new ProfileRepository(); @@ -17,7 +11,6 @@ export const useAddWorkExperienceMutation = () => { await profileRepo.addWorkExperience(data); }, onSuccess: () => { - // Invalidate profile queries to refetch with new work experience queryClient.invalidateQueries({ queryKey: ['profile'] }); queryClient.invalidateQueries({ queryKey: ['currentUserProfile'] }); }, diff --git a/src/application/command/useCreateComment.ts b/src/application/command/useCreateComment.ts new file mode 100644 index 0000000..6669f00 --- /dev/null +++ b/src/application/command/useCreateComment.ts @@ -0,0 +1,15 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { commentRepository } from '../../di'; +import { CreateCommentRequest } from '../../domain/post/entities/Comment'; + +export const useCreateComment = (postId: string) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (data: CreateCommentRequest) => commentRepository.createComment(postId, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['comments', postId] }); + queryClient.invalidateQueries({ queryKey: ['newsFeed'] }); + }, + }); +}; diff --git a/src/ui/hooks/useCreatePostMutation.ts b/src/application/command/useCreatePostMutation.ts similarity index 62% rename from src/ui/hooks/useCreatePostMutation.ts rename to src/application/command/useCreatePostMutation.ts index 9883f0c..c34a032 100644 --- a/src/ui/hooks/useCreatePostMutation.ts +++ b/src/application/command/useCreatePostMutation.ts @@ -1,13 +1,11 @@ import { useMutation } from '@tanstack/react-query'; -import { PostRepository } from '../../data/repositories/PostRepository'; -import { CreatePostHandler } from '../../application/post/handler/CreatePostHandler'; -import { MediaApi } from '../../data/services/MediaApi'; +import { MediaApi } from '../../infrastructure/services/MediaApi'; import { uploadToAWS } from '../../presentation/utils/uploadToAWS'; import { PostRequestBody } from '../../domain/post/entities/Post'; +import { postRepository } from '../../di'; + export const useCreatePostMutation = () => { - const postRepo = new PostRepository(); - const handler = new CreatePostHandler(postRepo); const mediaApi = new MediaApi(); return useMutation({ @@ -19,7 +17,7 @@ export const useCreatePostMutation = () => { input.imageUrl = publicUrl; } - await handler.handle(input); + await postRepository.create(input); }, }); }; diff --git a/src/ui/hooks/useFollowUserMutation.ts b/src/application/command/useFollowUserMutation.ts similarity index 54% rename from src/ui/hooks/useFollowUserMutation.ts rename to src/application/command/useFollowUserMutation.ts index d9e5efd..85a6c47 100644 --- a/src/ui/hooks/useFollowUserMutation.ts +++ b/src/application/command/useFollowUserMutation.ts @@ -1,22 +1,14 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { ProfileRepository } from '../../data/repositories/ProfileRepository'; +import { profileRepository } from '../../di'; -const profileRepository = new ProfileRepository(); -/** - * useFollowUserMutation Hook - * SOLID: Single Responsibility - Handle follow user mutation - * SOLID: Dependency Inversion - Depends on ProfileRepository abstraction - */ export const useFollowUserMutation = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (userId: number) => profileRepository.followUser(userId), onSuccess: (_data, userId) => { - // Invalidate the viewed profile to refresh follow status queryClient.invalidateQueries({ queryKey: ['profile', userId] }); - // Also invalidate current user profile as followers count may change queryClient.invalidateQueries({ queryKey: ['currentUserProfile'] }); }, onError: (error) => { diff --git a/src/ui/hooks/useLogin.ts b/src/application/command/useLogin.ts similarity index 86% rename from src/ui/hooks/useLogin.ts rename to src/application/command/useLogin.ts index e974518..fac4121 100644 --- a/src/ui/hooks/useLogin.ts +++ b/src/application/command/useLogin.ts @@ -1,6 +1,6 @@ import { useMutation } from '@tanstack/react-query'; import { LoginPayload, AuthResponse } from '../../domain/auth/entities/Auth'; -import { authRepository } from '../di'; +import { authRepository } from '../../di'; export function useLogin() { return useMutation({ diff --git a/src/application/command/useMarkAllNotificationsAsRead.ts b/src/application/command/useMarkAllNotificationsAsRead.ts new file mode 100644 index 0000000..9dfd0af --- /dev/null +++ b/src/application/command/useMarkAllNotificationsAsRead.ts @@ -0,0 +1,13 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { notificationRepository } from '../../di'; + +export const useMarkAllNotificationsAsRead = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: () => notificationRepository.markAllAsRead(), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['notifications'] }); + }, + }); +}; diff --git a/src/application/command/useMarkNotificationAsRead.ts b/src/application/command/useMarkNotificationAsRead.ts new file mode 100644 index 0000000..dbef80b --- /dev/null +++ b/src/application/command/useMarkNotificationAsRead.ts @@ -0,0 +1,13 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { notificationRepository } from '../../di'; + +export const useMarkNotificationAsRead = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: number) => notificationRepository.markAsRead(id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['notifications'] }); + }, + }); +}; diff --git a/src/ui/hooks/useRegister.ts b/src/application/command/useRegister.ts similarity index 87% rename from src/ui/hooks/useRegister.ts rename to src/application/command/useRegister.ts index 9840592..36fca69 100644 --- a/src/ui/hooks/useRegister.ts +++ b/src/application/command/useRegister.ts @@ -3,7 +3,7 @@ import { RegisterPayload, MessageResponse, } from '../../domain/auth/entities/Auth'; -import { authRepository } from '../di'; +import { authRepository } from '../../di'; export const useRegister = () => useMutation({ diff --git a/src/ui/hooks/useToggleConnection.ts b/src/application/command/useToggleConnection.ts similarity index 57% rename from src/ui/hooks/useToggleConnection.ts rename to src/application/command/useToggleConnection.ts index a9952ef..ec5ec54 100644 --- a/src/ui/hooks/useToggleConnection.ts +++ b/src/application/command/useToggleConnection.ts @@ -1,15 +1,11 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { connectionRepository } from "../di"; -import { SendConnectionRequest, RemoveConnection } from "../../domain/user/usecases/ToggleConnectionStatus"; - -const sendConnectionRequestUseCase = new SendConnectionRequest(connectionRepository); -const removeConnectionUseCase = new RemoveConnection(connectionRepository); +import { connectionRepository } from "../../di"; export const useSendConnectionRequest = () => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (userId: number) => sendConnectionRequestUseCase.execute(userId), + mutationFn: (userId: number) => connectionRepository.sendConnectionRequest(userId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["profiles"] }); queryClient.invalidateQueries({ queryKey: ["myConnections"] }); @@ -21,7 +17,7 @@ export const useRemoveConnection = () => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (userId: number) => removeConnectionUseCase.execute(userId), + mutationFn: (userId: number) => connectionRepository.removeConnection(userId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["profiles"] }); queryClient.invalidateQueries({ queryKey: ["myConnections"] }); diff --git a/src/application/command/useToggleLike.ts b/src/application/command/useToggleLike.ts new file mode 100644 index 0000000..6ce7e03 --- /dev/null +++ b/src/application/command/useToggleLike.ts @@ -0,0 +1,23 @@ +import { useMutation } from '@tanstack/react-query'; +import { postRepository } from '../../di'; + + +export const useToggleLike = () => { + + const mutation = useMutation({ + mutationFn: ({ postId, isLiked }: { postId: string; isLiked: boolean }) => + isLiked ? postRepository.likePost(postId) : postRepository.unlikePost(postId), + onError: (error) => { + console.error('Error toggling like:', error); + }, + }); + + const toggleLike = (postId: string, isLiked: boolean) => { + mutation.mutate({ postId, isLiked }); + }; + + return { + toggleLike, + isLoading: mutation.isPending, + }; +}; diff --git a/src/ui/hooks/useUpdateProfileMutation.ts b/src/application/command/useUpdateProfileMutation.ts similarity index 77% rename from src/ui/hooks/useUpdateProfileMutation.ts rename to src/application/command/useUpdateProfileMutation.ts index ade9ce4..4860368 100644 --- a/src/ui/hooks/useUpdateProfileMutation.ts +++ b/src/application/command/useUpdateProfileMutation.ts @@ -1,13 +1,11 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { profileRepository } from "../di"; -import { UpdateProfile } from "../../domain/user/usecases/UpdateProfile"; +import { profileRepository } from '../../di'; import { UpdateProfileData } from "../../domain/user/repositories/IProfileRepository"; -import { MediaApi } from "../../data/services/MediaApi"; +import { MediaApi } from "../../infrastructure/services/MediaApi"; import { uploadToAWS } from "../../presentation/utils/uploadToAWS"; export const useUpdateProfileMutation = () => { const queryClient = useQueryClient(); - const updateProfileUseCase = new UpdateProfile(profileRepository); const mediaApi = new MediaApi(); return useMutation({ @@ -32,10 +30,9 @@ export const useUpdateProfileMutation = () => { delete (updateData as any).avatarUri; delete (updateData as any).coverImageUri; - return updateProfileUseCase.execute(updateData); + return profileRepository.updateProfile(updateData); }, onSuccess: () => { - // Invalidate the current user profile query to refresh the data queryClient.invalidateQueries({ queryKey: ["currentUserProfile"] }); }, }); diff --git a/src/ui/hooks/useVerifyOtp.ts b/src/application/command/useVerifyOtp.ts similarity index 83% rename from src/ui/hooks/useVerifyOtp.ts rename to src/application/command/useVerifyOtp.ts index cb64ac0..85c4a8a 100644 --- a/src/ui/hooks/useVerifyOtp.ts +++ b/src/application/command/useVerifyOtp.ts @@ -1,5 +1,5 @@ import { useMutation } from '@tanstack/react-query'; -import { authRepository } from '../di'; +import { authRepository } from '../../di'; export const useVerifyOtp = () => useMutation({ diff --git a/src/ui/hooks/useVerifyToken.ts b/src/application/command/useVerifyToken.ts similarity index 82% rename from src/ui/hooks/useVerifyToken.ts rename to src/application/command/useVerifyToken.ts index 2fc874f..ab58dfb 100644 --- a/src/ui/hooks/useVerifyToken.ts +++ b/src/application/command/useVerifyToken.ts @@ -1,5 +1,5 @@ import { useMutation } from '@tanstack/react-query'; -import { authRepository } from '../di'; +import { authRepository } from '../../di'; export const useVerifyToken = () => diff --git a/src/application/notification/handler/GetNotificationsHandler.ts b/src/application/notification/handler/GetNotificationsHandler.ts deleted file mode 100644 index d340db7..0000000 --- a/src/application/notification/handler/GetNotificationsHandler.ts +++ /dev/null @@ -1,12 +0,0 @@ -// application/notification/handlers/GetNotificationsHandler.ts - -import { Notification } from "../../../domain/notification/entities/Notification"; -import { INotificationRepository } from "../../../domain/notification/repositories/INotificationRepository"; - -export class GetNotificationsHandler { - constructor(private repo: INotificationRepository) {} - - async handle(): Promise { - return this.repo.getAll(); - } -} diff --git a/src/application/notification/handler/MarkAllAsReadHandler.ts b/src/application/notification/handler/MarkAllAsReadHandler.ts deleted file mode 100644 index f564b6c..0000000 --- a/src/application/notification/handler/MarkAllAsReadHandler.ts +++ /dev/null @@ -1,10 +0,0 @@ -// application/notification/handlers/MarkAllAsReadHandler.ts -import { INotificationRepository } from "../../../domain/notification/repositories/INotificationRepository"; - -export class MarkAllAsReadHandler { - constructor(private repo: INotificationRepository) {} - - async handle(): Promise { - await this.repo.markAllAsRead(); - } -} diff --git a/src/application/post/handler/CreatePostHandler.ts b/src/application/post/handler/CreatePostHandler.ts deleted file mode 100644 index 7e75265..0000000 --- a/src/application/post/handler/CreatePostHandler.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Poll, PostRequestBody } from '../../../domain/post/entities/Post'; -import { IPostRepository } from '../../../domain/post/repositories/IPostRepository'; - -export class CreatePostHandler { - constructor(private readonly postRepository: IPostRepository) {} - - async handle(post: PostRequestBody): Promise { - const response = await this.postRepository.create(post); - return true; - } -} diff --git a/src/application/query/useActivityLog.ts b/src/application/query/useActivityLog.ts new file mode 100644 index 0000000..175dfb9 --- /dev/null +++ b/src/application/query/useActivityLog.ts @@ -0,0 +1,10 @@ +import { useQuery } from '@tanstack/react-query'; +import { activityLogRepository } from '../../di'; + + +export const useActivityLog = () => { + return useQuery({ + queryKey: ['activityLog'], + queryFn: () => activityLogRepository.getActivityLog(), + }); +}; diff --git a/src/ui/hooks/useChat.ts b/src/application/query/useChat.ts similarity index 61% rename from src/ui/hooks/useChat.ts rename to src/application/query/useChat.ts index 2862d8f..bfcceb9 100644 --- a/src/ui/hooks/useChat.ts +++ b/src/application/query/useChat.ts @@ -1,37 +1,20 @@ import { useQuery, useMutation, useQueryClient, useInfiniteQuery } from "@tanstack/react-query"; -import { chatRepository } from "../di"; -import { - GetConversations, - GetConversationById, - StartConversation -} from "../../domain/chat/usecases/ConversationUseCases"; -import { - GetMessages, - SendMessage, - MarkMessagesAsRead -} from "../../domain/chat/usecases/MessageUseCases"; +import { chatRepository } from '../../di'; import { SendMessagePayload } from "../../domain/chat/entities/Message"; -// Use case instances -const getConversationsUseCase = new GetConversations(chatRepository); -const getConversationByIdUseCase = new GetConversationById(chatRepository); -const startConversationUseCase = new StartConversation(chatRepository); -const getMessagesUseCase = new GetMessages(chatRepository); -const sendMessageUseCase = new SendMessage(chatRepository); -const markMessagesAsReadUseCase = new MarkMessagesAsRead(chatRepository); - -// Query hooks +// To be corrected: With Chat features there are several issues. +// These will fixed later. export const useConversations = () => { return useQuery({ queryKey: ["conversations"], - queryFn: () => getConversationsUseCase.execute(), + queryFn: () => chatRepository.getConversations(), }); }; export const useConversation = (conversationId: number) => { return useQuery({ queryKey: ["conversation", conversationId], - queryFn: () => getConversationByIdUseCase.execute(conversationId), + queryFn: () => chatRepository.getConversationById(conversationId), enabled: !!conversationId, }); }; @@ -39,7 +22,7 @@ export const useConversation = (conversationId: number) => { export const useMessages = (conversationId: number) => { return useInfiniteQuery({ queryKey: ["messages", conversationId], - queryFn: ({ pageParam = 1 }) => getMessagesUseCase.execute(conversationId, pageParam), + queryFn: ({ pageParam = 1 }) => chatRepository.getMessages(conversationId, pageParam), initialPageParam: 1, getNextPageParam: (lastPage, allPages) => { if (lastPage.has_more) { @@ -56,7 +39,7 @@ export const useStartConversation = () => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (userId: number) => startConversationUseCase.execute(userId), + mutationFn: (userId: number) => chatRepository.startConversation(userId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["conversations"] }); }, @@ -67,7 +50,7 @@ export const useSendMessage = () => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (payload: SendMessagePayload) => sendMessageUseCase.execute(payload), + mutationFn: (payload: SendMessagePayload) => chatRepository.sendMessage(payload), onSuccess: (data, variables) => { // Invalidate messages for the conversation queryClient.invalidateQueries({ queryKey: ["messages"] }); @@ -82,7 +65,7 @@ export const useMarkAsRead = () => { return useMutation({ mutationFn: ({ conversationId, messageIds }: { conversationId: number; messageIds: number[] }) => - markMessagesAsReadUseCase.execute(conversationId, messageIds), + chatRepository.markAsRead(conversationId, messageIds), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["messages"] }); queryClient.invalidateQueries({ queryKey: ["conversations"] }); diff --git a/src/application/query/useComments.ts b/src/application/query/useComments.ts new file mode 100644 index 0000000..0407488 --- /dev/null +++ b/src/application/query/useComments.ts @@ -0,0 +1,10 @@ +import { useQuery } from '@tanstack/react-query'; +import { commentRepository } from '../../di'; + +export const useComments = (postId: string) => { + return useQuery({ + queryKey: ['comments', postId], + queryFn: () => commentRepository.getComments(postId), + enabled: !!postId, + }); +}; \ No newline at end of file diff --git a/src/application/query/useConnections.ts b/src/application/query/useConnections.ts new file mode 100644 index 0000000..839fba0 --- /dev/null +++ b/src/application/query/useConnections.ts @@ -0,0 +1,9 @@ +import { useQuery } from "@tanstack/react-query"; +import { connectionRepository } from '../../di'; + +export const useMyConnections = () => { + return useQuery({ + queryKey: ["myConnections"], + queryFn: () => connectionRepository.getMyConnections(), + }); +}; diff --git a/src/ui/hooks/useCurrentUserProfile.ts b/src/application/query/useCurrentUserProfile.ts similarity index 59% rename from src/ui/hooks/useCurrentUserProfile.ts rename to src/application/query/useCurrentUserProfile.ts index 16138aa..88036b5 100644 --- a/src/ui/hooks/useCurrentUserProfile.ts +++ b/src/application/query/useCurrentUserProfile.ts @@ -1,9 +1,9 @@ import { useQuery } from "@tanstack/react-query"; -import { getCurrentUserProfileUseCase } from "../di"; +import { profileRepository } from '../../di'; export const useCurrentUserProfile = () => { return useQuery({ queryKey: ["currentUserProfile"], - queryFn: () => getCurrentUserProfileUseCase.execute(), + queryFn: () => profileRepository.getCurrentUserProfile(), }); }; diff --git a/src/ui/hooks/useNewsFeed.ts b/src/application/query/useNewsFeed.ts similarity index 60% rename from src/ui/hooks/useNewsFeed.ts rename to src/application/query/useNewsFeed.ts index 76f8348..7fe94a7 100644 --- a/src/ui/hooks/useNewsFeed.ts +++ b/src/application/query/useNewsFeed.ts @@ -1,9 +1,9 @@ import { useQuery } from "@tanstack/react-query"; -import { getNewsFeedUseCase } from "../di"; +import { postRepository } from '../../di'; export const useNewsFeed = () => { return useQuery({ queryKey: ["newsFeed"], - queryFn: () => getNewsFeedUseCase.execute(), + queryFn: () => postRepository.getAllPosts() }); }; diff --git a/src/application/query/useNotifications.ts b/src/application/query/useNotifications.ts new file mode 100644 index 0000000..2d5534f --- /dev/null +++ b/src/application/query/useNotifications.ts @@ -0,0 +1,9 @@ +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { notificationRepository } from '../../di'; + +export const useNotifications = () => { + return useQuery({ + queryKey: ['notifications'], + queryFn: () => notificationRepository.getNotifications(), + }); +}; \ No newline at end of file diff --git a/src/ui/hooks/useProfile.ts b/src/application/query/useProfile.ts similarity index 63% rename from src/ui/hooks/useProfile.ts rename to src/application/query/useProfile.ts index a24665e..dea042f 100644 --- a/src/ui/hooks/useProfile.ts +++ b/src/application/query/useProfile.ts @@ -1,10 +1,10 @@ import { useQuery } from '@tanstack/react-query'; -import { getProfileByIdUseCase } from '../di'; +import { profileRepository } from '../../di'; export const useProfile = (userId: number) => { return useQuery({ queryKey: ['profile', userId], - queryFn: () => getProfileByIdUseCase.execute(userId), + queryFn: () => profileRepository.getProfileById(userId), enabled: !!userId, }); }; diff --git a/src/application/query/useProfileSuggestions.ts b/src/application/query/useProfileSuggestions.ts new file mode 100644 index 0000000..589db4b --- /dev/null +++ b/src/application/query/useProfileSuggestions.ts @@ -0,0 +1,10 @@ +import { useQuery } from "@tanstack/react-query"; +import { connectionRepository } from '../../di'; + +export const useProfileSuggestions = () => { + return useQuery({ + queryKey: ["profileSuggestions"], + queryFn: () => connectionRepository.getProfileSuggestions(), + }); +}; + \ No newline at end of file diff --git a/src/application/query/useViewProfile.ts b/src/application/query/useViewProfile.ts new file mode 100644 index 0000000..7366cad --- /dev/null +++ b/src/application/query/useViewProfile.ts @@ -0,0 +1,15 @@ +import { useQuery } from '@tanstack/react-query'; +import { profileRepository } from '../../di'; + + +export const useViewProfile = (userId?: number) => { + const isCurrentUser = userId === undefined || userId === null; + + return useQuery({ + queryKey: isCurrentUser ? ['currentUserProfile'] : ['profile', userId], + queryFn: () => + isCurrentUser + ? profileRepository.getCurrentUserProfile() + : profileRepository.getProfileById(userId!), + }); +}; diff --git a/src/ui/hooks/useChatWebSocket.ts b/src/application/useChatWebSocket.ts similarity index 99% rename from src/ui/hooks/useChatWebSocket.ts rename to src/application/useChatWebSocket.ts index 9899bc5..062c3c7 100644 --- a/src/ui/hooks/useChatWebSocket.ts +++ b/src/application/useChatWebSocket.ts @@ -1,9 +1,7 @@ -// ui/hooks/useChatWebSocket.ts import { useEffect, useRef, useCallback, useState } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import AsyncStorage from '@react-native-async-storage/async-storage'; -import Config from '../../core/config'; -import { Message } from '../../domain/chat/entities/Message'; +import Config from '../core/config'; // WebSocket close codes from backend const WS_CLOSE_CODES = { diff --git a/src/ui/hooks/useConversation.ts b/src/application/useConversation.ts similarity index 84% rename from src/ui/hooks/useConversation.ts rename to src/application/useConversation.ts index 02e6097..6e0b9aa 100644 --- a/src/ui/hooks/useConversation.ts +++ b/src/application/useConversation.ts @@ -1,5 +1,5 @@ -import { useState, useEffect, useCallback } from 'react'; -import { useStartConversation } from './useChat'; +import { useState, useEffect } from 'react'; +import { useStartConversation } from './query/useChat'; interface ConversationState { conversationId: number | null; @@ -9,10 +9,7 @@ interface ConversationState { error: Error | null; } -/** - * Custom hook for managing conversation lifecycle - * Single Responsibility: Handles conversation initialization and state - */ + export const useConversation = (userId: number | null) => { const [state, setState] = useState({ conversationId: null, @@ -30,7 +27,7 @@ export const useConversation = (userId: number | null) => { setState(prev => ({ ...prev, isLoading: true })); startConversationMutation.mutate(userId, { - onSuccess: conversation => { + onSuccess: (conversation:any) => { const otherParticipant = conversation.other_participant; setState({ conversationId: conversation.id, diff --git a/src/ui/hooks/useMessageOperations.ts b/src/application/useMessageOperations.ts similarity index 89% rename from src/ui/hooks/useMessageOperations.ts rename to src/application/useMessageOperations.ts index 7da28d7..ae3597a 100644 --- a/src/ui/hooks/useMessageOperations.ts +++ b/src/application/useMessageOperations.ts @@ -1,6 +1,6 @@ -import { useCallback, useEffect } from 'react'; -import { useMarkAsRead } from './useChat'; -import { Message } from '../../domain/chat/entities/Message'; +import { useEffect } from 'react'; +import { Message } from '../domain/chat/entities/Message'; +import { useMarkAsRead } from './query/useChat'; interface MessageOperationsProps { conversationId: number | null; diff --git a/src/ui/hooks/useNewsFeedHeader.ts b/src/application/useNewsFeedHeader.ts similarity index 61% rename from src/ui/hooks/useNewsFeedHeader.ts rename to src/application/useNewsFeedHeader.ts index 1024201..4825eb6 100644 --- a/src/ui/hooks/useNewsFeedHeader.ts +++ b/src/application/useNewsFeedHeader.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react'; -import { FeedNavigationProp } from '../../presentation/navigation/news-feed-navigation/NewsFeedScreenStackParamList'; +import { FeedNavigationProp } from '../presentation/navigation/news-feed-navigation/NewsFeedScreenStackParamList'; interface UseNewsFeedHeaderProps { navigation: FeedNavigationProp; @@ -9,11 +9,6 @@ interface UseNewsFeedHeaderReturn { handleProfilePress: () => void; } -/** - * useNewsFeedHeader Hook - * SOLID: Single Responsibility - Handles header navigation logic - * SOLID: Dependency Inversion - Depends on navigation abstraction - */ export const useNewsFeedHeader = ({ navigation, }: UseNewsFeedHeaderProps): UseNewsFeedHeaderReturn => { diff --git a/src/ui/hooks/usePasswordVisibility.ts b/src/application/usePasswordVisibility.ts similarity index 100% rename from src/ui/hooks/usePasswordVisibility.ts rename to src/application/usePasswordVisibility.ts diff --git a/src/ui/hooks/useSubtitleText.ts b/src/application/useSubtitleText.ts similarity index 100% rename from src/ui/hooks/useSubtitleText.ts rename to src/application/useSubtitleText.ts diff --git a/src/ui/hooks/useTypingIndicator.ts b/src/application/useTypingIndicator.ts similarity index 100% rename from src/ui/hooks/useTypingIndicator.ts rename to src/application/useTypingIndicator.ts diff --git a/src/ui/hooks/useUserAvatar.ts b/src/application/useUserAvatar.ts similarity index 71% rename from src/ui/hooks/useUserAvatar.ts rename to src/application/useUserAvatar.ts index 2bb127d..3c1b26c 100644 --- a/src/ui/hooks/useUserAvatar.ts +++ b/src/application/useUserAvatar.ts @@ -5,11 +5,6 @@ import ReactNativeBlobUtil from 'react-native-blob-util'; const AVATAR_FILENAME = 'user_avatar.jpg'; const AVATAR_DOWNLOADED_URL_KEY = 'userAvatarDownloadedUrl'; -/** - * useUserAvatar Hook - * SOLID: Single Responsibility - Provides locally cached user avatar - * Downloads avatar image and stores it locally for offline access - */ export const useUserAvatar = () => { const [avatarUri, setAvatarUri] = useState(''); const [isLoading, setIsLoading] = useState(true); @@ -23,21 +18,25 @@ export const useUserAvatar = () => { try { const localPath = getLocalAvatarPath(); const exists = await ReactNativeBlobUtil.fs.exists(localPath); - const currentProfileUrl = await AsyncStorage.getItem('userProfilePhoto'); - const downloadedUrl = await AsyncStorage.getItem(AVATAR_DOWNLOADED_URL_KEY); - - // Check if cached file exists and was downloaded from the current URL - if (exists && currentProfileUrl && currentProfileUrl === downloadedUrl) { - // Use locally stored avatar (URL hasn't changed) + const currentProfileUrl = await AsyncStorage.getItem( + 'userProfilePhoto', + ); + const downloadedUrl = await AsyncStorage.getItem( + AVATAR_DOWNLOADED_URL_KEY, + ); + + if ( + exists && + currentProfileUrl && + currentProfileUrl === downloadedUrl + ) { setAvatarUri(`file://${localPath}`); } else if (currentProfileUrl) { - // URL changed or no cached file - delete old and download new if (exists) { await ReactNativeBlobUtil.fs.unlink(localPath); } await downloadAndCacheAvatar(currentProfileUrl); } else if (exists) { - // No current URL but file exists - clear stale cache await ReactNativeBlobUtil.fs.unlink(localPath); await AsyncStorage.removeItem(AVATAR_DOWNLOADED_URL_KEY); setAvatarUri(''); @@ -52,23 +51,17 @@ export const useUserAvatar = () => { loadAvatar(); }, []); - /** - * Downloads avatar from URL and saves it locally - */ const downloadAndCacheAvatar = async (url: string): Promise => { try { const localPath = getLocalAvatarPath(); - - // Download the image + const res = await ReactNativeBlobUtil.config({ fileCache: true, path: localPath, }).fetch('GET', url); if (res.info().status === 200) { - // Save the original URL for reference await AsyncStorage.setItem('userProfilePhoto', url); - // Track which URL was actually downloaded to the local file await AsyncStorage.setItem(AVATAR_DOWNLOADED_URL_KEY, url); setAvatarUri(`file://${localPath}`); return true; @@ -80,24 +73,16 @@ export const useUserAvatar = () => { } }; - /** - * Updates avatar with a new URL - downloads and caches locally - * Use this when user uploads a new profile photo - */ const updateAvatar = async (newUrl: string) => { setIsLoading(true); - setAvatarUri(''); // Clear current avatar to trigger UI refresh + setAvatarUri(''); try { - // Delete old cached image const localPath = getLocalAvatarPath(); const exists = await ReactNativeBlobUtil.fs.exists(localPath); if (exists) { await ReactNativeBlobUtil.fs.unlink(localPath); } - // Clear old downloaded URL tracking await AsyncStorage.removeItem(AVATAR_DOWNLOADED_URL_KEY); - - // Download and cache new avatar await downloadAndCacheAvatar(newUrl); } catch (error) { console.error('Error updating avatar:', error); @@ -106,9 +91,6 @@ export const useUserAvatar = () => { } }; - /** - * Refreshes avatar by re-downloading from stored URL - */ const refreshAvatar = async () => { try { const cachedUrl = await AsyncStorage.getItem('userProfilePhoto'); @@ -120,9 +102,6 @@ export const useUserAvatar = () => { } }; - /** - * Clears locally cached avatar - */ const clearAvatar = async () => { try { const localPath = getLocalAvatarPath(); diff --git a/src/di.ts b/src/di.ts new file mode 100644 index 0000000..8edec1a --- /dev/null +++ b/src/di.ts @@ -0,0 +1,26 @@ +import { AuthRepository } from "./infrastructure/repositories/AuthRepository"; +import { PostRepository } from "./infrastructure/repositories/PostRepository"; +import { ConnectionRepository } from "./infrastructure/repositories/ConnectionRepository"; +import { ProfileRepository } from "./infrastructure/repositories/ProfileRepository"; +import { ChatRepository } from "./infrastructure/repositories/ChatRepository"; +import { ActivityLogRepository } from "./infrastructure/repositories/ActivityLogRepository"; +import { NotificationRepository } from "./infrastructure/repositories/NotificationRepository"; +import { CommentRepository } from "./infrastructure/repositories/CommentRepository"; + +import { IAuthRepository } from "./domain/auth/repositories/IAuthRepository"; +import { IPostRepository } from "./domain/post/repositories/IPostRepository"; +import { IConnectionRepository } from "./domain/user/repositories/IConnectionRepository"; +import { IProfileRepository } from "./domain/user/repositories/IProfileRepository"; +import { IChatRepository } from "./domain/chat/repositories/IChatRepository"; +import { IActivityLogRepository } from "./domain/activity/repositories/IActivityLogRepository"; +import { INotificationRepository } from "./domain/notification/repositories/INotificationRepository"; +import { ICommentRepository } from "./domain/post/repositories/ICommentRepository"; + +export const authRepository: IAuthRepository = new AuthRepository(); +export const postRepository: IPostRepository = new PostRepository(); +export const connectionRepository: IConnectionRepository = new ConnectionRepository(); +export const profileRepository: IProfileRepository = new ProfileRepository(); +export const chatRepository: IChatRepository = new ChatRepository(); +export const activityLogRepository: IActivityLogRepository = new ActivityLogRepository(); +export const notificationRepository: INotificationRepository = new NotificationRepository(); +export const commentRepository: ICommentRepository = new CommentRepository(); \ No newline at end of file diff --git a/src/domain/activity/usecases/GetActivityLog.ts b/src/domain/activity/usecases/GetActivityLog.ts deleted file mode 100644 index c72995e..0000000 --- a/src/domain/activity/usecases/GetActivityLog.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ActivityLog } from '../entities/ActivityLog'; -import { IActivityLogRepository } from '../repositories/IActivityLogRepository'; - -/** - * GetActivityLog UseCase - * SOLID: Single Responsibility - Encapsulates activity log fetching logic - * SOLID: Dependency Inversion - Depends on repository abstraction - */ -export class GetActivityLog { - constructor(private readonly repository: IActivityLogRepository) {} - - async execute(): Promise { - return this.repository.getActivityLog(); - } -} diff --git a/src/domain/chat/usecases/ConversationUseCases.ts b/src/domain/chat/usecases/ConversationUseCases.ts deleted file mode 100644 index 5a0e3a5..0000000 --- a/src/domain/chat/usecases/ConversationUseCases.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { IChatRepository } from "../repositories/IChatRepository"; -import { Conversation } from "../entities/Conversation"; - -export class GetConversations { - constructor(private chatRepository: IChatRepository) {} - - async execute(): Promise { - return this.chatRepository.getConversations(); - } -} - -export class GetConversationById { - constructor(private chatRepository: IChatRepository) {} - - async execute(conversationId: number): Promise { - return this.chatRepository.getConversationById(conversationId); - } -} - -export class StartConversation { - constructor(private chatRepository: IChatRepository) {} - - async execute(userId: number): Promise { - return this.chatRepository.startConversation(userId); - } -} diff --git a/src/domain/chat/usecases/MessageUseCases.ts b/src/domain/chat/usecases/MessageUseCases.ts deleted file mode 100644 index 59e4194..0000000 --- a/src/domain/chat/usecases/MessageUseCases.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { IChatRepository } from "../repositories/IChatRepository"; -import { Message, MessagesResponse, SendMessagePayload } from "../entities/Message"; - -export class GetMessages { - constructor(private chatRepository: IChatRepository) {} - - async execute(conversationId: number, page?: number): Promise { - return this.chatRepository.getMessages(conversationId, page); - } -} - -export class SendMessage { - constructor(private chatRepository: IChatRepository) {} - - async execute(payload: SendMessagePayload): Promise { - return this.chatRepository.sendMessage(payload); - } -} - -export class MarkMessagesAsRead { - constructor(private chatRepository: IChatRepository) {} - - async execute(conversationId: number, messageIds: number[]): Promise { - return this.chatRepository.markAsRead(conversationId, messageIds); - } -} diff --git a/src/domain/notification/usecases/GetNotifications.ts b/src/domain/notification/usecases/GetNotifications.ts deleted file mode 100644 index f04b4b5..0000000 --- a/src/domain/notification/usecases/GetNotifications.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Activity } from '../entities/Activity'; -import { INotificationRepository } from '../repositories/INotificationRepository'; - -/** - * GetNotifications UseCase - * SOLID: Single Responsibility - Encapsulates notification fetching logic - * SOLID: Dependency Inversion - Depends on repository abstraction - */ -export class GetNotifications { - constructor(private readonly repository: INotificationRepository) {} - - async execute(): Promise { - return this.repository.getNotifications(); - } -} diff --git a/src/domain/notification/usecases/MarkAllNotificationsAsRead.ts b/src/domain/notification/usecases/MarkAllNotificationsAsRead.ts deleted file mode 100644 index 1244fd8..0000000 --- a/src/domain/notification/usecases/MarkAllNotificationsAsRead.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { INotificationRepository } from '../repositories/INotificationRepository'; - -/** - * MarkAllNotificationsAsRead UseCase - * SOLID: Single Responsibility - Mark all notifications as read - * SOLID: Dependency Inversion - Depends on repository abstraction - */ -export class MarkAllNotificationsAsRead { - constructor(private readonly repository: INotificationRepository) {} - - async execute(): Promise { - return this.repository.markAllAsRead(); - } -} diff --git a/src/domain/notification/usecases/MarkNotificationAsRead.ts b/src/domain/notification/usecases/MarkNotificationAsRead.ts deleted file mode 100644 index 80fde10..0000000 --- a/src/domain/notification/usecases/MarkNotificationAsRead.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { INotificationRepository } from '../repositories/INotificationRepository'; - -/** - * MarkNotificationAsRead UseCase - * SOLID: Single Responsibility - Mark single notification as read - * SOLID: Dependency Inversion - Depends on repository abstraction - */ -export class MarkNotificationAsRead { - constructor(private readonly repository: INotificationRepository) {} - - async execute(id: number): Promise { - return this.repository.markAsRead(id); - } -} diff --git a/src/domain/post/usecases/commands/CreateComment.ts b/src/domain/post/usecases/commands/CreateComment.ts deleted file mode 100644 index 7224dfc..0000000 --- a/src/domain/post/usecases/commands/CreateComment.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Comment, CreateCommentRequest } from "../../entities/Comment"; -import { ICommentRepository } from "../../repositories/ICommentRepository"; - -/** - * CreateComment UseCase - * SOLID: Single Responsibility - Encapsulates comment creation logic - * SOLID: Dependency Inversion - Depends on repository abstraction - */ -export class CreateComment { - constructor(private readonly repository: ICommentRepository) {} - - async execute(postId: string, data: CreateCommentRequest): Promise { - return this.repository.createComment(postId, data); - } -} diff --git a/src/domain/post/usecases/commands/CreatePost.ts b/src/domain/post/usecases/commands/CreatePost.ts deleted file mode 100644 index 0f749b5..0000000 --- a/src/domain/post/usecases/commands/CreatePost.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Post } from "../../entities/Post"; -import { IPostRepository } from "../../repositories/IPostRepository"; - -export class CreatePost { - constructor(private repo: IPostRepository) {} - - async execute(postData: Omit): Promise { - if (!postData.content.trim() && !postData.image) { - throw new Error("Post must have content or image"); - } - return this.repo.createPost(postData); - } -} diff --git a/src/domain/post/usecases/commands/ToggleLikePost.ts b/src/domain/post/usecases/commands/ToggleLikePost.ts deleted file mode 100644 index 266b57d..0000000 --- a/src/domain/post/usecases/commands/ToggleLikePost.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IPostRepository } from '../../repositories/IPostRepository'; - -/** - * ToggleLikePost Use Case - * SOLID: Single Responsibility - Handle like/unlike logic - */ -export class ToggleLikePost { - constructor(private readonly postRepository: IPostRepository) {} - - async execute(postId: string, isCurrentlyLiked: boolean): Promise { - if (isCurrentlyLiked) { - await this.postRepository.unlikePost(postId); - } else { - await this.postRepository.likePost(postId); - } - } -} diff --git a/src/domain/post/usecases/queries/GetComments.ts b/src/domain/post/usecases/queries/GetComments.ts deleted file mode 100644 index ceffaa7..0000000 --- a/src/domain/post/usecases/queries/GetComments.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Comment } from "../../entities/Comment"; -import { ICommentRepository } from "../../repositories/ICommentRepository"; - -/** - * GetComments UseCase - * SOLID: Single Responsibility - Encapsulates comment fetching logic - * SOLID: Dependency Inversion - Depends on repository abstraction - */ -export class GetComments { - constructor(private readonly repository: ICommentRepository) {} - - async execute(postId: string): Promise { - return this.repository.getComments(postId); - } -} diff --git a/src/domain/post/usecases/queries/GetNewsFeed.ts b/src/domain/post/usecases/queries/GetNewsFeed.ts deleted file mode 100644 index c2ef65e..0000000 --- a/src/domain/post/usecases/queries/GetNewsFeed.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { PostResponseBody } from "../../entities/Post"; -import { IPostRepository } from "../../repositories/IPostRepository"; - -export class GetNewsFeed { - constructor(private readonly postRepository: IPostRepository) {} - - execute(): Promise { - return this.postRepository.getAllPosts(); - } -} diff --git a/src/domain/post/usecases/queries/GetPostById.ts b/src/domain/post/usecases/queries/GetPostById.ts deleted file mode 100644 index 49b84bc..0000000 --- a/src/domain/post/usecases/queries/GetPostById.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IPostRepository } from "../../repositories/IPostRepository"; -import { Post } from "../../entities/Post"; - -export class GetPostById { - constructor(private repo: IPostRepository) {} - - async execute(id: string): Promise { - return this.repo.getPostById(id); - } -} diff --git a/src/domain/user/repositories/IConnectionRepository.ts b/src/domain/user/repositories/IConnectionRepository.ts index f379cc9..5c0c85a 100644 --- a/src/domain/user/repositories/IConnectionRepository.ts +++ b/src/domain/user/repositories/IConnectionRepository.ts @@ -1,7 +1,7 @@ import { Connection } from "../entities/Connection"; export interface IConnectionRepository { - getProfiles(): Promise; + getProfileSuggestions(): Promise; getMyConnections(): Promise; sendConnectionRequest(userId: number): Promise; removeConnection(userId: number): Promise; diff --git a/src/domain/user/usecases/GetConnections.ts b/src/domain/user/usecases/GetConnections.ts deleted file mode 100644 index b3af2d6..0000000 --- a/src/domain/user/usecases/GetConnections.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IConnectionRepository } from "../repositories/IConnectionRepository"; -import { Connection } from "../entities/Connection"; - -export class GetProfiles { - constructor(private repo: IConnectionRepository) {} - - async execute(): Promise { - return this.repo.getProfiles(); - } -} - -export class GetMyConnections { - constructor(private repo: IConnectionRepository) {} - - async execute(): Promise { - return this.repo.getMyConnections(); - } -} \ No newline at end of file diff --git a/src/domain/user/usecases/GetCurrentUserProfile.ts b/src/domain/user/usecases/GetCurrentUserProfile.ts deleted file mode 100644 index c749bea..0000000 --- a/src/domain/user/usecases/GetCurrentUserProfile.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IProfileRepository } from "../repositories/IProfileRepository"; -import { Profile } from "../entities/Profile"; - -export class GetCurrentUserProfile { - constructor(private repo: IProfileRepository) {} - - async execute(): Promise { - return this.repo.getCurrentUserProfile(); - } -} diff --git a/src/domain/user/usecases/GetProfileById.ts b/src/domain/user/usecases/GetProfileById.ts deleted file mode 100644 index 9ea053f..0000000 --- a/src/domain/user/usecases/GetProfileById.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IProfileRepository } from "../repositories/IProfileRepository"; -import { Profile } from "../entities/Profile"; - -export class GetProfileById { - constructor(private repo: IProfileRepository) {} - - async execute(userId: number): Promise { - return this.repo.getProfileById(userId); - } -} diff --git a/src/domain/user/usecases/ToggleConnectionStatus.ts b/src/domain/user/usecases/ToggleConnectionStatus.ts deleted file mode 100644 index 725547c..0000000 --- a/src/domain/user/usecases/ToggleConnectionStatus.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IConnectionRepository } from "../repositories/IConnectionRepository"; - -export class SendConnectionRequest { - constructor(private repo: IConnectionRepository) {} - - async execute(userId: number): Promise { - return this.repo.sendConnectionRequest(userId); - } -} - -export class RemoveConnection { - constructor(private repo: IConnectionRepository) {} - - async execute(userId: number): Promise { - return this.repo.removeConnection(userId); - } -} diff --git a/src/domain/user/usecases/UpdateProfile.ts b/src/domain/user/usecases/UpdateProfile.ts deleted file mode 100644 index 7980153..0000000 --- a/src/domain/user/usecases/UpdateProfile.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IProfileRepository, UpdateProfileData } from "../repositories/IProfileRepository"; -import { Profile } from "../entities/Profile"; - -export class UpdateProfile { - constructor(private repo: IProfileRepository) {} - - async execute(data: UpdateProfileData): Promise { - return this.repo.updateProfile(data); - } -} diff --git a/src/ui/form-types/AddEducationForm.types.ts b/src/forms/types/AddEducationForm.types.ts similarity index 100% rename from src/ui/form-types/AddEducationForm.types.ts rename to src/forms/types/AddEducationForm.types.ts diff --git a/src/ui/form-types/AddWorkExperienceForm.types.ts b/src/forms/types/AddWorkExperienceForm.types.ts similarity index 100% rename from src/ui/form-types/AddWorkExperienceForm.types.ts rename to src/forms/types/AddWorkExperienceForm.types.ts diff --git a/src/ui/form-types/AdvancedSettingsForm.types.ts b/src/forms/types/AdvancedSettingsForm.types.ts similarity index 100% rename from src/ui/form-types/AdvancedSettingsForm.types.ts rename to src/forms/types/AdvancedSettingsForm.types.ts diff --git a/src/ui/form-types/CreatePostForm.types.ts b/src/forms/types/CreatePostForm.types.ts similarity index 100% rename from src/ui/form-types/CreatePostForm.types.ts rename to src/forms/types/CreatePostForm.types.ts diff --git a/src/ui/form-types/EditProfileForm.types.ts b/src/forms/types/EditProfileForm.types.ts similarity index 100% rename from src/ui/form-types/EditProfileForm.types.ts rename to src/forms/types/EditProfileForm.types.ts diff --git a/src/ui/form-hooks/useAddEducationForm.ts b/src/forms/useAddEducationForm.ts similarity index 95% rename from src/ui/form-hooks/useAddEducationForm.ts rename to src/forms/useAddEducationForm.ts index d7a56f7..eb00bf1 100644 --- a/src/ui/form-hooks/useAddEducationForm.ts +++ b/src/forms/useAddEducationForm.ts @@ -1,5 +1,5 @@ import { useForm } from 'react-hook-form'; -import { AddEducationFormValues } from '../form-types/AddEducationForm.types'; +import { AddEducationFormValues } from './types/AddEducationForm.types'; /** * useAddEducationForm Hook diff --git a/src/ui/form-hooks/useAddWorkExperienceForm.ts b/src/forms/useAddWorkExperienceForm.ts similarity index 97% rename from src/ui/form-hooks/useAddWorkExperienceForm.ts rename to src/forms/useAddWorkExperienceForm.ts index 1fb6660..c4fd7b1 100644 --- a/src/ui/form-hooks/useAddWorkExperienceForm.ts +++ b/src/forms/useAddWorkExperienceForm.ts @@ -2,7 +2,7 @@ import { useForm } from 'react-hook-form'; import { AddWorkExperienceFormValues, EmploymentType, -} from '../form-types/AddWorkExperienceForm.types'; +} from './types/AddWorkExperienceForm.types'; /** * useAddWorkExperienceForm Hook diff --git a/src/ui/form-hooks/useAdvancedSettingsForm.ts b/src/forms/useAdvancedSettingsForm.ts similarity index 96% rename from src/ui/form-hooks/useAdvancedSettingsForm.ts rename to src/forms/useAdvancedSettingsForm.ts index 749486f..33c0311 100644 --- a/src/ui/form-hooks/useAdvancedSettingsForm.ts +++ b/src/forms/useAdvancedSettingsForm.ts @@ -1,5 +1,5 @@ import { useForm, UseFormReturn } from 'react-hook-form'; -import { AdvancedSettingsFormValues } from '../form-types/AdvancedSettingsForm.types'; +import { AdvancedSettingsFormValues } from './types/AdvancedSettingsForm.types'; /** * Validation rules for advanced settings form diff --git a/src/ui/form-hooks/useEditProfileForm.ts b/src/forms/useEditProfileForm.ts similarity index 96% rename from src/ui/form-hooks/useEditProfileForm.ts rename to src/forms/useEditProfileForm.ts index 3a69ba4..160cb60 100644 --- a/src/ui/form-hooks/useEditProfileForm.ts +++ b/src/forms/useEditProfileForm.ts @@ -1,5 +1,5 @@ import { useForm } from 'react-hook-form'; -import { EditProfileFormValues } from '../form-types/EditProfileForm.types'; +import { EditProfileFormValues } from './types/EditProfileForm.types'; export const useEditProfileForm = (defaultValues?: Partial) => { const form = useForm({ diff --git a/src/ui/form-hooks/useLoginForm.ts b/src/forms/useLoginForm.ts similarity index 100% rename from src/ui/form-hooks/useLoginForm.ts rename to src/forms/useLoginForm.ts diff --git a/src/ui/form-hooks/useRegisterForm.ts b/src/forms/useRegisterForm.ts similarity index 94% rename from src/ui/form-hooks/useRegisterForm.ts rename to src/forms/useRegisterForm.ts index 8744cde..2a67724 100644 --- a/src/ui/form-hooks/useRegisterForm.ts +++ b/src/forms/useRegisterForm.ts @@ -1,5 +1,5 @@ import { useForm } from 'react-hook-form'; -import { RegisterPayload } from '../../domain/auth/entities/Auth'; +import { RegisterPayload } from '../domain/auth/entities/Auth'; export const useRegisterForm = () => { diff --git a/src/data/repositories/ActivityLogRepository.ts b/src/infrastructure/repositories/ActivityLogRepository.ts similarity index 100% rename from src/data/repositories/ActivityLogRepository.ts rename to src/infrastructure/repositories/ActivityLogRepository.ts diff --git a/src/data/repositories/AuthRepository.ts b/src/infrastructure/repositories/AuthRepository.ts similarity index 100% rename from src/data/repositories/AuthRepository.ts rename to src/infrastructure/repositories/AuthRepository.ts diff --git a/src/data/repositories/ChatRepository.ts b/src/infrastructure/repositories/ChatRepository.ts similarity index 100% rename from src/data/repositories/ChatRepository.ts rename to src/infrastructure/repositories/ChatRepository.ts diff --git a/src/data/repositories/CommentRepository.ts b/src/infrastructure/repositories/CommentRepository.ts similarity index 100% rename from src/data/repositories/CommentRepository.ts rename to src/infrastructure/repositories/CommentRepository.ts diff --git a/src/data/repositories/ConnectionRepository.ts b/src/infrastructure/repositories/ConnectionRepository.ts similarity index 92% rename from src/data/repositories/ConnectionRepository.ts rename to src/infrastructure/repositories/ConnectionRepository.ts index cf7a136..80fa0c3 100644 --- a/src/data/repositories/ConnectionRepository.ts +++ b/src/infrastructure/repositories/ConnectionRepository.ts @@ -3,7 +3,7 @@ import { IConnectionRepository } from "../../domain/user/repositories/IConnectio import { getAuth, postAuth } from "../../core/http"; export class ConnectionRepository implements IConnectionRepository { - async getProfiles(): Promise { + async getProfileSuggestions(): Promise { return getAuth("profiles/"); } diff --git a/src/data/repositories/NotificationRepository.ts b/src/infrastructure/repositories/NotificationRepository.ts similarity index 100% rename from src/data/repositories/NotificationRepository.ts rename to src/infrastructure/repositories/NotificationRepository.ts diff --git a/src/data/repositories/PostRepository.ts b/src/infrastructure/repositories/PostRepository.ts similarity index 100% rename from src/data/repositories/PostRepository.ts rename to src/infrastructure/repositories/PostRepository.ts diff --git a/src/data/repositories/ProfileRepository.ts b/src/infrastructure/repositories/ProfileRepository.ts similarity index 100% rename from src/data/repositories/ProfileRepository.ts rename to src/infrastructure/repositories/ProfileRepository.ts diff --git a/src/data/services/ActivityLogFormatter.ts b/src/infrastructure/services/ActivityLogFormatter.ts similarity index 100% rename from src/data/services/ActivityLogFormatter.ts rename to src/infrastructure/services/ActivityLogFormatter.ts diff --git a/src/data/services/CommentFormatter.ts b/src/infrastructure/services/CommentFormatter.ts similarity index 100% rename from src/data/services/CommentFormatter.ts rename to src/infrastructure/services/CommentFormatter.ts diff --git a/src/data/services/MediaApi.ts b/src/infrastructure/services/MediaApi.ts similarity index 100% rename from src/data/services/MediaApi.ts rename to src/infrastructure/services/MediaApi.ts diff --git a/src/data/services/NotificationFormatter.ts b/src/infrastructure/services/NotificationFormatter.ts similarity index 100% rename from src/data/services/NotificationFormatter.ts rename to src/infrastructure/services/NotificationFormatter.ts diff --git a/src/presentation/components/molecules/ActivityItem.tsx b/src/presentation/components/molecules/ActivityItem.tsx index 07155d6..c224fc2 100644 --- a/src/presentation/components/molecules/ActivityItem.tsx +++ b/src/presentation/components/molecules/ActivityItem.tsx @@ -4,7 +4,7 @@ import { ActivityIcon } from '../atoms/ActivityIcon'; import { ActivityDescription } from '../atoms/ActivityDescription'; import { ActivityTimestamp } from '../atoms/ActivityTimestamp'; import { ActivityLog } from '../../../domain/activity/entities/ActivityLog'; -import { ActivityLogFormatter } from '../../../data/services/ActivityLogFormatter'; +import { ActivityLogFormatter } from '../../../infrastructure/services/ActivityLogFormatter'; import { theme } from '../../theme'; const { activityItem } = theme.components; diff --git a/src/presentation/components/molecules/CommentItem.tsx b/src/presentation/components/molecules/CommentItem.tsx index 069b4be..0442939 100644 --- a/src/presentation/components/molecules/CommentItem.tsx +++ b/src/presentation/components/molecules/CommentItem.tsx @@ -1,7 +1,7 @@ import React, { useMemo } from 'react'; import { View, Text, ViewStyle, TextStyle } from 'react-native'; import { Comment } from '../../../domain/post/entities/Comment'; -import { CommentFormatter } from '../../../data/services/CommentFormatter'; +import { CommentFormatter } from '../../../infrastructure/services/CommentFormatter'; import { theme } from '../../theme'; const { commentItem } = theme.components; diff --git a/src/presentation/components/molecules/MessageItem.tsx b/src/presentation/components/molecules/MessageItem.tsx index f59d05a..02dbedc 100644 --- a/src/presentation/components/molecules/MessageItem.tsx +++ b/src/presentation/components/molecules/MessageItem.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Message } from '../../../domain/chat/entities/Message'; import { MessageBubble } from './MessageBubble'; -import { DateFormatterService } from '../../../ui/services/DateFormatterService'; +import { DateFormatterService } from '../../../services/DateFormatterService'; interface MessageItemProps { message: Message; diff --git a/src/presentation/components/molecules/NewsFeedHeader.tsx b/src/presentation/components/molecules/NewsFeedHeader.tsx index 2f9659c..2ecdaf7 100644 --- a/src/presentation/components/molecules/NewsFeedHeader.tsx +++ b/src/presentation/components/molecules/NewsFeedHeader.tsx @@ -3,8 +3,8 @@ import { View, ViewStyle } from 'react-native'; import { FeedNavigationProp } from '../../navigation/news-feed-navigation/NewsFeedScreenStackParamList'; import { LogoSection } from './LogoSection'; import { ProfileAvatarButton } from './ProfileAvatarButton'; -import { useNewsFeedHeader } from '../../../ui/hooks/useNewsFeedHeader'; -import { useUserAvatar } from '../../../ui/hooks/useUserAvatar'; +import { useNewsFeedHeader } from '../../../application/useNewsFeedHeader'; +import { useUserAvatar } from '../../../application/useUserAvatar'; import { theme } from '../../theme'; const { newsFeedHeader } = theme.components; diff --git a/src/presentation/components/molecules/NotificationItem.tsx b/src/presentation/components/molecules/NotificationItem.tsx index 2c81b3c..9a1083a 100644 --- a/src/presentation/components/molecules/NotificationItem.tsx +++ b/src/presentation/components/molecules/NotificationItem.tsx @@ -6,7 +6,7 @@ import { ActivityTimestamp } from '../atoms/ActivityTimestamp'; import { UnreadDot } from '../atoms/UnreadDot'; import { AvatarImage } from '../atoms/AvatarImage'; import { Activity } from '../../../domain/notification/entities/Activity'; -import { NotificationFormatter } from '../../../data/services/NotificationFormatter'; +import { NotificationFormatter } from '../../../infrastructure/services/NotificationFormatter'; import { theme } from '../../theme'; const { notificationItem } = theme.components; diff --git a/src/presentation/components/molecules/PostInput.tsx b/src/presentation/components/molecules/PostInput.tsx index c8dfba3..15c9388 100644 --- a/src/presentation/components/molecules/PostInput.tsx +++ b/src/presentation/components/molecules/PostInput.tsx @@ -2,7 +2,7 @@ import React, { useMemo } from 'react'; import { TextInput, TextStyle } from 'react-native'; import { Controller, Control } from 'react-hook-form'; import { theme } from '../../theme'; -import { CreatePostFormValues } from '../../../ui/form-types/CreatePostForm.types'; +import { CreatePostFormValues } from '../../../forms/types/CreatePostForm.types'; import { PostRequestBody } from '../../../domain/post/entities/Post'; const { postInput } = theme.components; diff --git a/src/presentation/components/organisms/ChatHeader.tsx b/src/presentation/components/organisms/ChatHeader.tsx index f170721..153c8b9 100644 --- a/src/presentation/components/organisms/ChatHeader.tsx +++ b/src/presentation/components/organisms/ChatHeader.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { View, StyleSheet } from 'react-native'; import { theme } from '../../theme'; -import { useSubtitleText } from '../../../ui/hooks/useSubtitleText'; +import { useSubtitleText } from '../../../application/useSubtitleText'; import { BackButton } from '../atoms/BackButton'; import { Avatar } from '../atoms/Avatar'; import { HeaderInfo } from '../molecules/HeaderInfo'; diff --git a/src/presentation/components/organisms/CommentBottomSheet.tsx b/src/presentation/components/organisms/CommentBottomSheet.tsx index 0bd05fe..f5ad3fe 100644 --- a/src/presentation/components/organisms/CommentBottomSheet.tsx +++ b/src/presentation/components/organisms/CommentBottomSheet.tsx @@ -12,8 +12,9 @@ import { import { CommentSheetHeader } from '../molecules/CommentSheetHeader'; import { CommentList } from './CommentList'; import { CommentInput } from '../molecules/CommentInput'; -import { useComments, useCreateComment } from '../../../ui/hooks/useComments'; import { theme } from '../../theme'; +import { useComments } from '../../../application/query/useComments'; +import { useCreateComment } from '../../../application/command/useCreateComment'; interface CommentBottomSheetProps { visible: boolean; @@ -21,14 +22,6 @@ interface CommentBottomSheetProps { onClose: () => void; } -/** - * CommentBottomSheet Organism - * Atomic Design: Organism - Complete comment section with header, list, and input - * SOLID Principles: - * - Single Responsibility: Comment sheet UI orchestration - * - Open/Closed: Extended through molecule composition - * - Dependency Inversion: Uses hooks for data access - */ export const CommentBottomSheet: React.FC = ({ visible, postId, diff --git a/src/presentation/components/organisms/ConnectionBanner.tsx b/src/presentation/components/organisms/ConnectionBanner.tsx index 217f61d..efde76c 100644 --- a/src/presentation/components/organisms/ConnectionBanner.tsx +++ b/src/presentation/components/organisms/ConnectionBanner.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { WebSocketConnectionError } from '../../../ui/hooks/useChatWebSocket'; -import { BannerStateResolverService } from '../../../ui/services/BannerStateResolverService'; +import { WebSocketConnectionError } from '../../../application/useChatWebSocket'; +import { BannerStateResolverService } from '../../../services/BannerStateResolverService'; import { ConnectingBanner } from './ConnectingBanner'; import { ErrorBanner } from './ErrorBanner'; import { WarningBanner } from './WarningBanner'; diff --git a/src/presentation/components/organisms/ConnectionCard.tsx b/src/presentation/components/organisms/ConnectionCard.tsx index 552e375..e84a780 100644 --- a/src/presentation/components/organisms/ConnectionCard.tsx +++ b/src/presentation/components/organisms/ConnectionCard.tsx @@ -3,7 +3,7 @@ import { View, ViewStyle } from 'react-native'; import { Connection } from '../../../domain/user/entities/Connection'; import { ConnectionInfo } from '../molecules/ConnectionInfo'; import { ConnectionActions } from '../molecules/ConnectionActions'; -import { ConnectionSubtitleService } from '../../../ui/services/ConnectionSubtitleService'; +import { ConnectionSubtitleService } from '../../../services/ConnectionSubtitleService'; import { theme } from '../../theme'; const { connectionCard } = theme.components; diff --git a/src/presentation/components/organisms/ConversationCard.tsx b/src/presentation/components/organisms/ConversationCard.tsx index 67237ff..1145d22 100644 --- a/src/presentation/components/organisms/ConversationCard.tsx +++ b/src/presentation/components/organisms/ConversationCard.tsx @@ -4,7 +4,7 @@ import { Conversation } from '../../../domain/chat/entities/Conversation'; import { AvatarPhoto } from '../atoms/AvatarPhoto'; import { AvatarRing } from '../atoms/AvatarRing'; import { ConversationInfo } from '../molecules/ConversationInfo'; -import { ConversationTimeFormatter } from '../../../ui/services/ConversationTimeFormatter'; +import { ConversationTimeFormatter } from '../../../services/ConversationTimeFormatter'; import { theme } from '../../theme'; const { conversationCard } = theme.components; diff --git a/src/presentation/components/organisms/ErrorBanner.tsx b/src/presentation/components/organisms/ErrorBanner.tsx index 4392d2c..9c39e49 100644 --- a/src/presentation/components/organisms/ErrorBanner.tsx +++ b/src/presentation/components/organisms/ErrorBanner.tsx @@ -3,7 +3,7 @@ import { View, StyleSheet } from 'react-native'; import { BannerContainer } from '../molecules/BannerContainer'; import { BannerText } from '../atoms/BannerText'; import { BannerActions } from '../molecules/BannerActions'; -import { BannerAction } from '../../../ui/services/BannerStateResolverService'; +import { BannerAction } from '../../../services/BannerStateResolverService'; import { theme } from '../../theme'; interface ErrorBannerProps { diff --git a/src/presentation/components/organisms/PollCreator.tsx b/src/presentation/components/organisms/PollCreator.tsx index d0e5698..1cfd9d1 100644 --- a/src/presentation/components/organisms/PollCreator.tsx +++ b/src/presentation/components/organisms/PollCreator.tsx @@ -3,7 +3,7 @@ import { View, Text, TextInput, TouchableOpacity, ViewStyle, TextStyle } from 'r import { Controller, Control, useFieldArray } from 'react-hook-form'; import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; import { faPlus, faXmark } from '@fortawesome/free-solid-svg-icons'; -import { CreatePostFormValues } from '../../../ui/form-types/CreatePostForm.types'; +import { CreatePostFormValues } from '../../../forms/types/CreatePostForm.types'; import { PostRequestBody } from '../../../domain/post/entities/Post'; import { theme } from '../../theme'; diff --git a/src/presentation/components/organisms/PostList.tsx b/src/presentation/components/organisms/PostList.tsx index c935827..c2583e2 100644 --- a/src/presentation/components/organisms/PostList.tsx +++ b/src/presentation/components/organisms/PostList.tsx @@ -3,7 +3,7 @@ import { FlatList, StyleSheet } from 'react-native'; import { PostResponseBody } from '../../../domain/post/entities/Post'; import { theme } from '../../theme'; import { PostCard } from './PostCard'; -import { useToggleLike } from '../../../ui/hooks/useToggleLike'; +import { useToggleLike } from '../../../application/command/useToggleLike'; interface PostListProps { posts: PostResponseBody[]; @@ -13,14 +13,6 @@ interface PostListProps { ListHeaderComponent?: React.ReactElement; } -/** - * PostList Organism - * Atomic Design: Organism - Complex list of posts - * SOLID Principles: - * - Single Responsibility: Render scrollable post list - * - Open/Closed: Extended via ListHeaderComponent - * - Interface Segregation: Focused props interface - */ export const PostList: React.FC = ({ posts, isRefreshing, diff --git a/src/presentation/components/templates/AddEducationScreenTemplate.tsx b/src/presentation/components/templates/AddEducationScreenTemplate.tsx index 392f747..e533258 100644 --- a/src/presentation/components/templates/AddEducationScreenTemplate.tsx +++ b/src/presentation/components/templates/AddEducationScreenTemplate.tsx @@ -16,7 +16,7 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import { Control, FieldErrors, Controller } from 'react-hook-form'; import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; import { faArrowLeft, faChevronDown, faCheck, faTimes } from '@fortawesome/free-solid-svg-icons'; -import { AddEducationFormValues } from '../../../ui/form-types/AddEducationForm.types'; +import { AddEducationFormValues } from '../../../forms/types/AddEducationForm.types'; import { theme } from '../../theme'; // Atoms diff --git a/src/presentation/components/templates/AddWorkExperienceScreenTemplate.tsx b/src/presentation/components/templates/AddWorkExperienceScreenTemplate.tsx index dd91f8e..452014e 100644 --- a/src/presentation/components/templates/AddWorkExperienceScreenTemplate.tsx +++ b/src/presentation/components/templates/AddWorkExperienceScreenTemplate.tsx @@ -16,7 +16,7 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import { Control, FieldErrors, Controller } from 'react-hook-form'; import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; import { faArrowLeft, faChevronDown, faCheck, faTimes } from '@fortawesome/free-solid-svg-icons'; -import { AddWorkExperienceFormValues, EmploymentType } from '../../../ui/form-types/AddWorkExperienceForm.types'; +import { AddWorkExperienceFormValues, EmploymentType } from '../../../forms/types/AddWorkExperienceForm.types'; import { theme } from '../../theme'; // Atoms diff --git a/src/presentation/components/templates/AdvancedSettingsScreenTemplate.tsx b/src/presentation/components/templates/AdvancedSettingsScreenTemplate.tsx index c73095d..8e465d6 100644 --- a/src/presentation/components/templates/AdvancedSettingsScreenTemplate.tsx +++ b/src/presentation/components/templates/AdvancedSettingsScreenTemplate.tsx @@ -15,8 +15,8 @@ import { faRotateLeft, } from '@fortawesome/free-solid-svg-icons'; import { theme } from '../../theme'; -import { AdvancedSettingsFormValues } from '../../../ui/form-types/AdvancedSettingsForm.types'; -import { AdvancedSettingsValidationRules } from '../../../ui/form-hooks/useAdvancedSettingsForm'; +import { AdvancedSettingsFormValues } from '../../../forms/types/AdvancedSettingsForm.types'; +import { AdvancedSettingsValidationRules } from '../../../forms/useAdvancedSettingsForm'; import { SafeAreaView } from 'react-native-safe-area-context'; import { PageHeader } from '../molecules/PageHeader'; diff --git a/src/presentation/components/templates/ChatScreenTemplate.tsx b/src/presentation/components/templates/ChatScreenTemplate.tsx index 7ed2eb9..477da23 100644 --- a/src/presentation/components/templates/ChatScreenTemplate.tsx +++ b/src/presentation/components/templates/ChatScreenTemplate.tsx @@ -5,7 +5,7 @@ import { ChatHeader, HeaderAction } from '../organisms/ChatHeader'; import { ConnectionBanner } from '../organisms/ConnectionBanner'; import { ChatContent } from '../organisms/ChatContent'; import { Message } from '../../../domain/chat/entities/Message'; -import { WebSocketConnectionError } from '../../../ui/hooks/useChatWebSocket'; +import { WebSocketConnectionError } from '../../../application/useChatWebSocket'; import { theme } from '../../theme'; interface ChatScreenTemplateProps { diff --git a/src/presentation/components/templates/CreatePostScreenTemplate.tsx b/src/presentation/components/templates/CreatePostScreenTemplate.tsx index 293a8f6..13b5fb4 100644 --- a/src/presentation/components/templates/CreatePostScreenTemplate.tsx +++ b/src/presentation/components/templates/CreatePostScreenTemplate.tsx @@ -2,7 +2,7 @@ import React, { useMemo } from 'react'; import { ScrollView, StyleSheet, ViewStyle } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { Control } from 'react-hook-form'; -import { CreatePostFormValues } from '../../../ui/form-types/CreatePostForm.types'; +import { CreatePostFormValues } from '../../../forms/types/CreatePostForm.types'; import { PostRequestBody } from '../../../domain/post/entities/Post'; import { theme } from '../../theme'; diff --git a/src/presentation/components/templates/EditProfileScreenTemplate.tsx b/src/presentation/components/templates/EditProfileScreenTemplate.tsx index f18fef2..1b11cdc 100644 --- a/src/presentation/components/templates/EditProfileScreenTemplate.tsx +++ b/src/presentation/components/templates/EditProfileScreenTemplate.tsx @@ -2,7 +2,7 @@ import React, { useMemo } from 'react'; import { ScrollView, View, ViewStyle } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { Control, FieldErrors, Controller } from 'react-hook-form'; -import { EditProfileFormValues } from '../../../ui/form-types/EditProfileForm.types'; +import { EditProfileFormValues } from '../../../forms/types/EditProfileForm.types'; import { theme } from '../../theme'; // Atoms diff --git a/src/presentation/screens/ActivityLogScreen.tsx b/src/presentation/screens/ActivityLogScreen.tsx index 4f8feed..08e7fe4 100644 --- a/src/presentation/screens/ActivityLogScreen.tsx +++ b/src/presentation/screens/ActivityLogScreen.tsx @@ -2,18 +2,10 @@ import React, { useCallback } from 'react'; import { NativeStackScreenProps } from '@react-navigation/native-stack'; import { RootStackParamList } from '../navigation/RootStackParamList'; import { ActivityLogScreenTemplate } from '../components/templates/ActivityLogScreenTemplate'; -import { useActivityLog } from '../../ui/hooks/useActivityLog'; +import { useActivityLog } from '../../application/query/useActivityLog'; type ActivityLogScreenProps = NativeStackScreenProps; -/** - * ActivityLogScreen Page - * Atomic Design: Page - Final screen with real data - * SOLID Principles: - * - Single Responsibility: Connect data to template - * - Open/Closed: Extended through hooks and template - * - Dependency Inversion: Depends on abstractions (hook, template) - */ export const ActivityLogScreen: React.FC = ({ navigation }) => { const { data, isLoading, isError, refetch, isRefetching } = useActivityLog(); diff --git a/src/presentation/screens/AddEducationScreen.tsx b/src/presentation/screens/AddEducationScreen.tsx index 71679b1..c45e127 100644 --- a/src/presentation/screens/AddEducationScreen.tsx +++ b/src/presentation/screens/AddEducationScreen.tsx @@ -1,15 +1,8 @@ import React from 'react'; import { AddEducationScreenTemplate } from '../components/templates/AddEducationScreenTemplate'; -import { useAddEducationScreen } from '../../ui/hooks/useAddEducationScreen'; +import { useAddEducationScreen } from './hooks/useAddEducationScreen'; + -/** - * AddEducationScreen Page - * Atomic Design: Page - Final screen with real data - * SOLID Principles: - * - Single Responsibility: Connect data to template - * - Open/Closed: Extended through hook and template - * - Dependency Inversion: Depends on abstractions - */ export const AddEducationScreen: React.FC = () => { const { // Form diff --git a/src/presentation/screens/AddWorkExperienceScreen.tsx b/src/presentation/screens/AddWorkExperienceScreen.tsx index 7eaf948..502c474 100644 --- a/src/presentation/screens/AddWorkExperienceScreen.tsx +++ b/src/presentation/screens/AddWorkExperienceScreen.tsx @@ -1,15 +1,8 @@ import React from 'react'; import { AddWorkExperienceScreenTemplate } from '../components/templates/AddWorkExperienceScreenTemplate'; -import { useAddWorkExperienceScreen } from '../../ui/hooks/useAddWorkExperienceScreen'; +import { useAddWorkExperienceScreen } from './hooks/useAddWorkExperienceScreen'; + -/** - * AddWorkExperienceScreen Page - * Atomic Design: Page - Final screen with real data - * SOLID Principles: - * - Single Responsibility: Connect data to template - * - Open/Closed: Extended through hook and template - * - Dependency Inversion: Depends on abstractions - */ export const AddWorkExperienceScreen: React.FC = () => { const { // Form diff --git a/src/presentation/screens/AdvancedSettingsScreen.tsx b/src/presentation/screens/AdvancedSettingsScreen.tsx index 96d1480..573c4aa 100644 --- a/src/presentation/screens/AdvancedSettingsScreen.tsx +++ b/src/presentation/screens/AdvancedSettingsScreen.tsx @@ -1,20 +1,15 @@ import React from 'react'; import { NativeStackScreenProps } from '@react-navigation/native-stack'; import { RootStackParamList } from '../navigation/RootStackParamList'; -import { useAdvancedSettingsScreen } from '../../ui/hooks/useAdvancedSettingsScreen'; import { AdvancedSettingsScreenTemplate } from '../components/templates/AdvancedSettingsScreenTemplate'; +import { useAdvancedSettingsScreen } from './hooks/useAdvancedSettingsScreen'; type AdvancedSettingsScreenProps = NativeStackScreenProps< RootStackParamList, 'AdvancedSettings' >; -/** - * AdvancedSettingsScreen - * SOLID: Single Responsibility - Compose hook and template - * SOLID: Dependency Inversion - Depends on abstractions (hook, template) - * Atomic Design: Screen - Connects business logic to UI template - */ + export const AdvancedSettingsScreen: React.FC = () => { const { control, diff --git a/src/presentation/screens/ChatScreen.tsx b/src/presentation/screens/ChatScreen.tsx index 7eecbf0..7ddd216 100644 --- a/src/presentation/screens/ChatScreen.tsx +++ b/src/presentation/screens/ChatScreen.tsx @@ -1,19 +1,7 @@ -// presentation/screens/shared/ChatScreen.tsx -/** - * ChatScreen Component (Page) - * - * Atomic Design: Page - Connects data layer to template - * SOLID Principles Applied: - * - Single Responsibility: Route handling and hook connection only - * - Open/Closed: Extended through useChatScreen hook and ChatScreenTemplate - * - Liskov Substitution: Interchangeable message sending (WebSocket vs REST) - * - Interface Segregation: Focused props passed to template - * - Dependency Inversion: Depends on abstracted hooks and templates - */ import React from 'react'; import { ChatScreenProps } from '../navigation/messages-screen-navigation/MessageScreenStackParamList'; -import { useChatScreen } from '../../ui/hooks/useChatScreen'; import { ChatScreenTemplate } from '../components/templates/ChatScreenTemplate'; +import { useChatScreen } from './hooks/useChatScreen'; export const ChatScreen: React.FC = ({ route, diff --git a/src/presentation/screens/CreatePostScreen.tsx b/src/presentation/screens/CreatePostScreen.tsx index 6f345f4..094c413 100644 --- a/src/presentation/screens/CreatePostScreen.tsx +++ b/src/presentation/screens/CreatePostScreen.tsx @@ -1,15 +1,8 @@ import React from 'react'; import { CreatePostScreenTemplate } from '../components/templates/CreatePostScreenTemplate'; -import { useCreatePostScreen } from '../../ui/hooks/useCreatePostScreen'; +import { useCreatePostScreen } from './hooks/useCreatePostScreen'; + -/** - * CreatePostScreen Page - * Atomic Design: Page - Final screen with real data - * SOLID Principles: - * - Single Responsibility: Connect data to template - * - Open/Closed: Extended through hook and template - * - Dependency Inversion: Depends on abstractions (useCreatePostScreen, CreatePostScreenTemplate) - */ export const CreatePostScreen: React.FC = () => { const { // Form diff --git a/src/presentation/screens/EditProfileScreen.tsx b/src/presentation/screens/EditProfileScreen.tsx index 1ca59a0..cae8227 100644 --- a/src/presentation/screens/EditProfileScreen.tsx +++ b/src/presentation/screens/EditProfileScreen.tsx @@ -2,21 +2,14 @@ import React from 'react'; import { NativeStackScreenProps } from '@react-navigation/native-stack'; import { RootStackParamList } from '../navigation/RootStackParamList'; import { EditProfileScreenTemplate } from '../components/templates/EditProfileScreenTemplate'; -import { useEditProfileScreen } from '../../ui/hooks/useEditProfileScreen'; +import { useEditProfileScreen } from './hooks/useEditProfileScreen'; type EditProfileScreenProps = NativeStackScreenProps< RootStackParamList, 'EditProfile' >; -/** - * EditProfileScreen Page - * Atomic Design: Page - Final screen with real data - * SOLID Principles: - * - Single Responsibility: Connect data to template - * - Open/Closed: Extended through hook and template - * - Dependency Inversion: Depends on abstractions (useEditProfileScreen, EditProfileScreenTemplate) - */ + export const EditProfileScreen: React.FC = ({ navigation, }) => { diff --git a/src/presentation/screens/HomeScreen.tsx b/src/presentation/screens/HomeScreen.tsx index 2570682..7e91227 100644 --- a/src/presentation/screens/HomeScreen.tsx +++ b/src/presentation/screens/HomeScreen.tsx @@ -2,17 +2,7 @@ import React from 'react'; import { BottomNavigationTemplate } from '../components/templates/BottomNavigationTemplate'; -/** - * HomeScreen Page - * Atomic Design: Page - Wrapper for BottomNavigationTemplate - * SOLID Principles: - * - Single Responsibility: Renders the main navigation template - * - Open/Closed: Extended through BottomNavigationTemplate - * - Dependency Inversion: Depends on BottomNavigationTemplate abstraction - * - * Note: This screen delegates to BottomNavigationTemplate which handles - * all tab navigation logic, styling (from theme), and TabIcon atom usage. - */ + const HomeScreen: React.FC = () => { return ; }; diff --git a/src/presentation/screens/LoginScreen.tsx b/src/presentation/screens/LoginScreen.tsx index 75dab42..baf0b66 100644 --- a/src/presentation/screens/LoginScreen.tsx +++ b/src/presentation/screens/LoginScreen.tsx @@ -10,13 +10,12 @@ import { useNavigation } from '@react-navigation/native'; // import Icon from 'react-native-vector-icons/Ionicons'; import { LoginNavigationProp } from '../navigation/auth-screen-navigation/AuthScreenStackParamList'; -import { useLoginForm } from '../../ui/form-hooks/useLoginForm'; -import { useLogin } from '../../ui/hooks/useLogin'; +import { useLoginForm } from '../../forms/useLoginForm'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { theme } from '../theme'; -import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; -import { usePasswordVisibility } from '../../ui/hooks/usePasswordVisibility'; +import { usePasswordVisibility } from '../../application/usePasswordVisibility'; +import { useLogin } from '../../application/command/useLogin'; const LoginScreen: React.FC = () => { const navigation = useNavigation(); diff --git a/src/presentation/screens/MessagesScreen.tsx b/src/presentation/screens/MessagesScreen.tsx index 8cad301..e6a4eea 100644 --- a/src/presentation/screens/MessagesScreen.tsx +++ b/src/presentation/screens/MessagesScreen.tsx @@ -1,22 +1,21 @@ -// presentation/screens/MessagesScreen.tsx import React, { useState, useMemo } from "react"; import { FlatList, StyleSheet, RefreshControl } from "react-native"; import { useNavigation } from "@react-navigation/native"; import { SafeAreaView } from "react-native-safe-area-context"; import { Conversation } from "../../domain/chat/entities/Conversation"; -import { useConversations } from "../../ui/hooks/useChat"; import { ConversationCard } from "../components/organisms/ConversationCard"; import { EmptyState } from "../components/molecules/EmptyState"; import { PageHeader } from '../components/molecules/PageHeader'; import { SearchInput } from '../components/molecules/SearchInput'; import { MessagesNavigationProp } from "../navigation/messages-screen-navigation/MessageScreenStackParamList"; import { theme } from "../theme"; +import { useConversations } from "../../application/query/useChat"; const MessagesScreen: React.FC = () => { const [search, setSearch] = useState(""); const navigation = useNavigation(); - const { data: conversations = [], isLoading, isError, refetch, isRefetching } = useConversations(); + const { data: conversations = [], refetch, isRefetching } = useConversations(); // Filter conversations by search const filteredConversations = useMemo(() => { diff --git a/src/presentation/screens/NetworkScreen.tsx b/src/presentation/screens/NetworkScreen.tsx index 7ba48b7..e9780ff 100644 --- a/src/presentation/screens/NetworkScreen.tsx +++ b/src/presentation/screens/NetworkScreen.tsx @@ -1,17 +1,7 @@ -// src/presentation/screens/Network/NetworkScreen.tsx -/** - * NetworkScreen Component (Page) - * - * Atomic Design: Page - Connects data layer to template - * SOLID Principles Applied: - * - Single Responsibility: Route handling and hook connection only - * - Open/Closed: Extended through useNetworkScreen hook and NetworkScreenTemplate - * - Dependency Inversion: Depends on abstracted hooks and templates - */ import React from 'react'; import { NetworkScreenProps } from '../navigation/network-screen-navigation/NetworkScreenStackParamList'; -import { useNetworkScreen } from '../../ui/hooks/useNetworkScreen'; import { NetworkScreenTemplate } from '../components/templates/NetworkScreenTemplate'; +import { useNetworkScreen } from './hooks/useNetworkScreen'; export const NetworkScreen: React.FC = ({ navigation }) => { const { diff --git a/src/presentation/screens/NewsFeedScreen.tsx b/src/presentation/screens/NewsFeedScreen.tsx index b731210..9c819ed 100644 --- a/src/presentation/screens/NewsFeedScreen.tsx +++ b/src/presentation/screens/NewsFeedScreen.tsx @@ -1,17 +1,9 @@ import React, { useCallback, useState } from 'react'; import { NewsFeedScreenProps } from '../navigation/news-feed-navigation/NewsFeedScreenStackParamList'; import { NewsFeedScreenTemplate } from '../components/templates/NewsFeedScreenTemplate'; -import { useNewsFeedScreen } from '../../ui/hooks/useNewsFeedScreen'; import { CommentBottomSheet } from '../components/organisms/CommentBottomSheet'; +import { useNewsFeedScreen } from './hooks/useNewsFeedScreen'; -/** - * NewsFeedScreen Page - * Atomic Design: Page - Final screen with real data - * SOLID Principles: - * - Single Responsibility: Connect data to template - * - Open/Closed: Extended through hooks and template - * - Dependency Inversion: Depends on abstractions (hook, template) - */ const NewsFeedScreen: React.FC = ({ navigation }) => { const { posts, @@ -31,7 +23,6 @@ const NewsFeedScreen: React.FC = ({ navigation }) => { const handleCloseComments = useCallback(() => { setIsCommentSheetVisible(false); - // Delay clearing postId to allow modal animation to complete setTimeout(() => setSelectedPostId(null), 300); }, []); diff --git a/src/presentation/screens/NotificationsScreen.tsx b/src/presentation/screens/NotificationsScreen.tsx index 2b1d77a..634e1da 100644 --- a/src/presentation/screens/NotificationsScreen.tsx +++ b/src/presentation/screens/NotificationsScreen.tsx @@ -1,17 +1,11 @@ import React, { useState, useCallback, useMemo } from 'react'; import { NotificationsScreenTemplate } from '../components/templates/NotificationsScreenTemplate'; -import { useNotifications, useMarkNotificationAsRead, useMarkAllNotificationsAsRead } from '../../ui/hooks/useNotifications'; import { Activity, NotificationTabType } from '../../domain/notification/entities/Activity'; -import { NotificationFormatter } from '../../data/services/NotificationFormatter'; +import { NotificationFormatter } from '../../infrastructure/services/NotificationFormatter'; +import { useNotifications } from '../../application/query/useNotifications'; +import { useMarkAllNotificationsAsRead } from '../../application/command/useMarkAllNotificationsAsRead'; +import { useMarkNotificationAsRead } from '../../application/command/useMarkNotificationAsRead'; -/** - * NotificationsScreen Page - * Atomic Design: Page - Final screen with real data - * SOLID Principles: - * - Single Responsibility: Connect data to template - * - Open/Closed: Extended through hooks and template - * - Dependency Inversion: Depends on abstractions (hook, template) - */ export const NotificationsScreen: React.FC = () => { const [activeTab, setActiveTab] = useState('all'); @@ -25,16 +19,16 @@ export const NotificationsScreen: React.FC = () => { const filteredNotifications = useMemo(() => { switch (activeTab) { case 'unread': - return notifications.filter((n) => !n.is_read); + return notifications.filter((n: any) => !n.is_read); case 'mentions': - return notifications.filter((n) => NotificationFormatter.isMention(n)); + return notifications.filter((n: any) => NotificationFormatter.isMention(n)); default: return notifications; } }, [notifications, activeTab]); const unreadCount = useMemo( - () => notifications.filter((n) => !n.is_read).length, + () => notifications.filter((n: any) => !n.is_read).length, [notifications], ); diff --git a/src/presentation/screens/OtpValidationScreen.tsx b/src/presentation/screens/OtpValidationScreen.tsx index 661a916..d8ea637 100644 --- a/src/presentation/screens/OtpValidationScreen.tsx +++ b/src/presentation/screens/OtpValidationScreen.tsx @@ -6,9 +6,9 @@ import { TouchableOpacity, StyleSheet, } from 'react-native'; -import { useVerifyOtp } from '../../ui/hooks/useVerifyOtp'; import { useNavigation } from '@react-navigation/native'; import { OtpValidationNavigationProp } from '../navigation/auth-screen-navigation/AuthScreenStackParamList'; +import { useVerifyOtp } from '../../application/command/useVerifyOtp'; const OTP_LENGTH = 6; diff --git a/src/presentation/screens/RegisterScreen.tsx b/src/presentation/screens/RegisterScreen.tsx index 6e278a4..185bfed 100644 --- a/src/presentation/screens/RegisterScreen.tsx +++ b/src/presentation/screens/RegisterScreen.tsx @@ -3,14 +3,12 @@ import { View, StyleSheet, KeyboardAvoidingView, Platform } from 'react-native'; import { TextInput, Button, Text, HelperText } from 'react-native-paper'; import { Controller } from 'react-hook-form'; import { useNavigation } from '@react-navigation/native'; -import { RegisterPayload } from '../../domain/auth/entities/Auth'; -import { useRegister } from '../../ui/hooks/useRegister'; -import { RegisterNavigationProp } from '../navigation/auth-screen-navigation/AuthScreenStackParamList'; -import { useRegisterForm } from '../../ui/form-hooks/useRegisterForm'; +import { RegisterPayload } from '../../domain/auth/entities/Auth';import { RegisterNavigationProp } from '../navigation/auth-screen-navigation/AuthScreenStackParamList'; +import { useRegisterForm } from '../../forms/useRegisterForm'; import { theme } from '../theme'; -import { usePasswordVisibility } from '../../ui/hooks/usePasswordVisibility'; -import { faEyeSlash, faEye } from '@fortawesome/free-solid-svg-icons'; +import { usePasswordVisibility } from '../../application/usePasswordVisibility'; import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; +import { useRegister } from '../../application/command/useRegister'; const RegisterScreen: React.FC = () => { const navigation = useNavigation(); diff --git a/src/presentation/screens/SplashScreen.tsx b/src/presentation/screens/SplashScreen.tsx index 4e782f8..7090eb7 100644 --- a/src/presentation/screens/SplashScreen.tsx +++ b/src/presentation/screens/SplashScreen.tsx @@ -9,10 +9,10 @@ import { } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { useNavigation } from '@react-navigation/native'; -import { useVerifyToken } from '../../ui/hooks/useVerifyToken'; import { WelcomeNavigationProp } from '../navigation/auth-screen-navigation/AuthScreenStackParamList'; import Logo from '../../assets/images/splash.svg'; import { configManager } from '../../core/config'; +import { useVerifyToken } from '../../application/command/useVerifyToken'; const { width } = Dimensions.get('window'); const AnimatedLogo = Animated.createAnimatedComponent(Logo); @@ -39,8 +39,7 @@ const SplashScreen: React.FC = () => { await mutateAsync( { token }, { - onSuccess: async (response) => { - // Cache user profile photo for header avatar + onSuccess: async (response: any) => { if (response.user?.profile_photo) { await AsyncStorage.setItem('userProfilePhoto', response.user.profile_photo); } diff --git a/src/presentation/screens/ViewPostScreen.tsx b/src/presentation/screens/ViewPostScreen.tsx index 9ec0c24..90b68ba 100644 --- a/src/presentation/screens/ViewPostScreen.tsx +++ b/src/presentation/screens/ViewPostScreen.tsx @@ -8,10 +8,9 @@ import { faShare, faPaperPlane, } from "@fortawesome/free-solid-svg-icons"; -import { PostRepository } from "../../data/repositories/PostRepository"; import { PostResponseBody } from "../../domain/post/entities/Post"; -import { GetPostById } from "../../domain/post/usecases/queries/GetPostById"; import { ViewPostScreenProps } from "../navigation/news-feed-navigation/NewsFeedScreenStackParamList"; +import { postRepository } from "../../di"; export const ViewPostScreen: React.FC = ({ route, navigation }) => { @@ -19,10 +18,9 @@ export const ViewPostScreen: React.FC = ({ route, navigatio const [post, setPost] = useState(null); - const useCase = new GetPostById(new PostRepository()); useEffect(() => { - useCase.execute(postId).then(setPost); + postRepository.getPostById(postId).then(setPost); // todo: missing usequery }, []); if (!post) return Loading...; diff --git a/src/presentation/screens/ViewProfileScreen.tsx b/src/presentation/screens/ViewProfileScreen.tsx index 322608a..0c37de3 100644 --- a/src/presentation/screens/ViewProfileScreen.tsx +++ b/src/presentation/screens/ViewProfileScreen.tsx @@ -1,20 +1,9 @@ import React from 'react'; import { ViewProfileScreenProps } from '../navigation/network-screen-navigation/NetworkScreenStackParamList'; import { ViewProfileScreenTemplate } from '../components/templates/ViewProfileScreenTemplate'; -import { useViewProfileScreen } from '../../ui/hooks/useViewProfileScreen'; +import { useViewProfileScreen } from './hooks/useViewProfileScreen'; + -/** - * ViewProfileScreen Page - * Atomic Design: Page - Final screen with real data - * SOLID Principles: - * - Single Responsibility: Connect data to template - * - Open/Closed: Extended through hook and template - * - Dependency Inversion: Depends on abstractions (useViewProfileScreen, ViewProfileScreenTemplate) - * - * Navigation: - * - From Network tab: userId provided → calls profiles/{id} - * - From NewsFeed tab profile icon: no userId → calls profiles/me - */ export const ViewProfileScreen: React.FC = ({ route, }) => { diff --git a/src/ui/hooks/useAddEducationScreen.ts b/src/presentation/screens/hooks/useAddEducationScreen.ts similarity index 92% rename from src/ui/hooks/useAddEducationScreen.ts rename to src/presentation/screens/hooks/useAddEducationScreen.ts index 95951c5..f9c9b62 100644 --- a/src/ui/hooks/useAddEducationScreen.ts +++ b/src/presentation/screens/hooks/useAddEducationScreen.ts @@ -2,13 +2,13 @@ import { useCallback, useMemo } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { Alert } from 'react-native'; -import { useAddEducationForm } from '../form-hooks/useAddEducationForm'; -import { useAddEducationMutation } from './useAddEducationMutation'; +import { useAddEducationForm } from '../../../forms/useAddEducationForm'; +import { useAddEducationMutation } from '../../../application/command/useAddEducationMutation'; import { AddEducationFormValues, AddEducationRequestBody, -} from '../form-types/AddEducationForm.types'; -import { RootStackParamList } from '../../presentation/navigation/RootStackParamList'; +} from '../../../forms/types/AddEducationForm.types'; +import { RootStackParamList } from '../../navigation/RootStackParamList'; type NavigationProp = NativeStackNavigationProp; diff --git a/src/ui/hooks/useAddWorkExperienceScreen.ts b/src/presentation/screens/hooks/useAddWorkExperienceScreen.ts similarity index 93% rename from src/ui/hooks/useAddWorkExperienceScreen.ts rename to src/presentation/screens/hooks/useAddWorkExperienceScreen.ts index b1491b1..57320ed 100644 --- a/src/ui/hooks/useAddWorkExperienceScreen.ts +++ b/src/presentation/screens/hooks/useAddWorkExperienceScreen.ts @@ -2,14 +2,14 @@ import { useCallback, useMemo } from 'react'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { Alert } from 'react-native'; -import { useAddWorkExperienceForm } from '../form-hooks/useAddWorkExperienceForm'; -import { useAddWorkExperienceMutation } from './useAddWorkExperienceMutation'; +import { useAddWorkExperienceForm } from '../../../forms/useAddWorkExperienceForm'; +import { useAddWorkExperienceMutation } from '../../../application/command/useAddWorkExperienceMutation'; import { AddWorkExperienceFormValues, AddWorkExperienceRequestBody, EmploymentType, -} from '../form-types/AddWorkExperienceForm.types'; -import { RootStackParamList } from '../../presentation/navigation/RootStackParamList'; +} from '../../../forms/types/AddWorkExperienceForm.types'; +import { RootStackParamList } from '../../navigation/RootStackParamList'; type NavigationProp = NativeStackNavigationProp; diff --git a/src/ui/hooks/useAdvancedSettingsScreen.ts b/src/presentation/screens/hooks/useAdvancedSettingsScreen.ts similarity index 93% rename from src/ui/hooks/useAdvancedSettingsScreen.ts rename to src/presentation/screens/hooks/useAdvancedSettingsScreen.ts index d778e45..3363da1 100644 --- a/src/ui/hooks/useAdvancedSettingsScreen.ts +++ b/src/presentation/screens/hooks/useAdvancedSettingsScreen.ts @@ -1,9 +1,9 @@ import { useState, useEffect, useCallback } from 'react'; import { Alert } from 'react-native'; import { useNavigation } from '@react-navigation/native'; -import { useAdvancedSettingsForm } from '../form-hooks/useAdvancedSettingsForm'; -import { AdvancedSettingsFormValues } from '../form-types/AdvancedSettingsForm.types'; -import { configManager } from '../../core/config'; +import { useAdvancedSettingsForm } from '../../../forms/useAdvancedSettingsForm'; +import { AdvancedSettingsFormValues } from '../../../forms/types/AdvancedSettingsForm.types'; +import { configManager } from '../../../core/config'; /** * useAdvancedSettingsScreen Hook diff --git a/src/ui/hooks/useChatScreen.ts b/src/presentation/screens/hooks/useChatScreen.ts similarity index 88% rename from src/ui/hooks/useChatScreen.ts rename to src/presentation/screens/hooks/useChatScreen.ts index b77e54e..71a9163 100644 --- a/src/ui/hooks/useChatScreen.ts +++ b/src/presentation/screens/hooks/useChatScreen.ts @@ -1,14 +1,14 @@ import { useRef, useCallback, useMemo } from 'react'; import { FlatList } from 'react-native'; -import { useMessages, useSendMessage } from './useChat'; -import { useChatWebSocket, WebSocketConnectionError } from './useChatWebSocket'; -import { useConversation } from './useConversation'; -import { useTypingIndicator } from './useTypingIndicator'; -import { useMessageOperations } from './useMessageOperations'; -import { HeaderAction } from '../../presentation/components/organisms/ChatHeader'; +import { HeaderAction } from '../../components/organisms/ChatHeader'; import { faEllipsisVertical } from '@fortawesome/free-solid-svg-icons'; -import { theme } from '../../presentation/theme'; -import { Message } from '../../domain/chat/entities/Message'; +import { theme } from '../../theme'; +import { Message } from '../../../domain/chat/entities/Message'; +import { useMessages, useSendMessage } from '../../../application/query/useChat'; +import { WebSocketConnectionError, useChatWebSocket } from '../../../application/useChatWebSocket'; +import { useConversation } from '../../../application/useConversation'; +import { useMessageOperations } from '../../../application/useMessageOperations'; +import { useTypingIndicator } from '../../../application/useTypingIndicator'; interface UseChatScreenProps { userId: number; diff --git a/src/ui/hooks/useCreatePostScreen.ts b/src/presentation/screens/hooks/useCreatePostScreen.ts similarity index 90% rename from src/ui/hooks/useCreatePostScreen.ts rename to src/presentation/screens/hooks/useCreatePostScreen.ts index a409cc1..d0df42e 100644 --- a/src/ui/hooks/useCreatePostScreen.ts +++ b/src/presentation/screens/hooks/useCreatePostScreen.ts @@ -1,10 +1,10 @@ import { useState, useCallback, useMemo } from 'react'; import { useForm } from 'react-hook-form'; -import { PostRequestBody } from '../../domain/post/entities/Post'; -import { CreatePostFormValues } from '../form-types/CreatePostForm.types'; -import { useCreatePostMutation } from './useCreatePostMutation'; -import { createPostResolver } from '../resolvers/createPostResolver'; -import { useUserAvatar } from './useUserAvatar'; +import { PostRequestBody } from '../../../domain/post/entities/Post'; +import { CreatePostFormValues } from '../../../forms/types/CreatePostForm.types'; +import { useCreatePostMutation } from '../../../application/command/useCreatePostMutation'; +import { createPostResolver } from '../../../resolvers/createPostResolver'; +import { useUserAvatar } from '../../../application/useUserAvatar'; /** * Feeling type definition diff --git a/src/ui/hooks/useEditProfileScreen.ts b/src/presentation/screens/hooks/useEditProfileScreen.ts similarity index 91% rename from src/ui/hooks/useEditProfileScreen.ts rename to src/presentation/screens/hooks/useEditProfileScreen.ts index ed69622..4025961 100644 --- a/src/ui/hooks/useEditProfileScreen.ts +++ b/src/presentation/screens/hooks/useEditProfileScreen.ts @@ -1,9 +1,9 @@ import { useState, useCallback, useEffect } from 'react'; import { Alert } from 'react-native'; -import { useCurrentUserProfile } from './useCurrentUserProfile'; -import { useEditProfileForm } from '../form-hooks/useEditProfileForm'; -import { useUpdateProfileMutation } from './useUpdateProfileMutation'; -import { EditProfileFormValues } from '../form-types/EditProfileForm.types'; +import { useEditProfileForm } from '../../../forms/useEditProfileForm'; +import { EditProfileFormValues } from '../../../forms/types/EditProfileForm.types'; +import { useCurrentUserProfile } from '../../../application/query/useCurrentUserProfile'; +import { useUpdateProfileMutation } from '../../../application/command/useUpdateProfileMutation'; /** * useEditProfileScreen Hook diff --git a/src/ui/hooks/useNetworkScreen.ts b/src/presentation/screens/hooks/useNetworkScreen.ts similarity index 84% rename from src/ui/hooks/useNetworkScreen.ts rename to src/presentation/screens/hooks/useNetworkScreen.ts index 7091bc2..a74f17f 100644 --- a/src/ui/hooks/useNetworkScreen.ts +++ b/src/presentation/screens/hooks/useNetworkScreen.ts @@ -1,10 +1,8 @@ import { useState, useCallback, useMemo } from 'react'; -import { Connection } from '../../domain/user/entities/Connection'; -import { - useProfiles, - useMyConnections, -} from './useConnections'; -import { useSendConnectionRequest } from './useToggleConnection'; +import { Connection } from '../../../domain/user/entities/Connection'; +import { useSendConnectionRequest } from '../../../application/command/useToggleConnection'; +import { useMyConnections } from '../../../application/query/useConnections'; +import { useProfileSuggestions } from '../../../application/query/useProfileSuggestions'; interface UseNetworkScreenReturn { // State @@ -28,11 +26,6 @@ interface UseNetworkScreenReturn { handleConnect: (userId: number) => void; } -/** - * useNetworkScreen Hook (Facade Pattern) - * SOLID: Single Responsibility - Consolidates all network screen logic - * SOLID: Dependency Inversion - Depends on abstracted hooks - */ export const useNetworkScreen = (): UseNetworkScreenReturn => { const [searchQuery, setSearchQuery] = useState(''); const [tab, setTab] = useState<'suggestions' | 'connections'>('suggestions'); @@ -45,7 +38,7 @@ export const useNetworkScreen = (): UseNetworkScreenReturn => { isError: isErrorProfiles, refetch: refetchProfiles, isRefetching: isRefetchingProfiles, - } = useProfiles(); + } = useProfileSuggestions(); const { data: myConnections = [], @@ -62,9 +55,9 @@ export const useNetworkScreen = (): UseNetworkScreenReturn => { const isError = isErrorProfiles || isErrorConnections; const isRefetching = isRefetchingProfiles || isRefetchingConnections; - // Filtered data + // Filtered data --> To Be implemented in the backend, this is just a placeholder for now const suggestions = useMemo( - () => profiles.filter((p: Connection) => p.is_connected !== 'true'), + () => profiles.filter((p: Connection) => p), [profiles], ); diff --git a/src/ui/hooks/useNewsFeedScreen.ts b/src/presentation/screens/hooks/useNewsFeedScreen.ts similarity index 91% rename from src/ui/hooks/useNewsFeedScreen.ts rename to src/presentation/screens/hooks/useNewsFeedScreen.ts index afa25e1..70bac36 100644 --- a/src/ui/hooks/useNewsFeedScreen.ts +++ b/src/presentation/screens/hooks/useNewsFeedScreen.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react'; -import { useNewsFeed } from './useNewsFeed'; +import { useNewsFeed } from '../../../application/query/useNewsFeed'; /** * useNewsFeedScreen Hook diff --git a/src/ui/hooks/useViewProfileScreen.ts b/src/presentation/screens/hooks/useViewProfileScreen.ts similarity index 88% rename from src/ui/hooks/useViewProfileScreen.ts rename to src/presentation/screens/hooks/useViewProfileScreen.ts index 1f22484..50e330c 100644 --- a/src/ui/hooks/useViewProfileScreen.ts +++ b/src/presentation/screens/hooks/useViewProfileScreen.ts @@ -3,23 +3,15 @@ import { Alert } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { CommonActions, useNavigation } from '@react-navigation/native'; import { useQueryClient } from '@tanstack/react-query'; -import { useViewProfile } from './useViewProfile'; -import { useFollowUserMutation } from './useFollowUserMutation'; +import { useFollowUserMutation } from '../../../application/command/useFollowUserMutation'; +import { useViewProfile } from '../../../application/query/useViewProfile'; -/** - * useViewProfileScreen Hook - * SOLID: Single Responsibility - Handle all ViewProfile screen business logic - * SOLID: Dependency Inversion - Depends on abstractions (useViewProfile, navigation) - * - * @param userId - Optional user ID. If not provided, shows current user's profile - */ export const useViewProfileScreen = (userId?: number) => { const navigation = useNavigation(); const queryClient = useQueryClient(); const isCurrentUser = userId === undefined || userId === null; - // Fetch profile data const { data: user, isLoading, diff --git a/src/presentation/utils/uploadToAWS.ts b/src/presentation/utils/uploadToAWS.ts index cb74099..05b1e7c 100644 --- a/src/presentation/utils/uploadToAWS.ts +++ b/src/presentation/utils/uploadToAWS.ts @@ -1,8 +1,5 @@ -import AsyncStorage from '@react-native-async-storage/async-storage'; import ReactNativeBlobUtil from 'react-native-blob-util'; -import Config from '../../core/config'; -// ✅ CORRECT - Sends raw binary data export const uploadToAWS = async (presignedUrl: string, imageUri: string) => { try { const filePath = imageUri.replace('file://', ''); diff --git a/src/ui/queryClient.ts b/src/queryClient.ts similarity index 100% rename from src/ui/queryClient.ts rename to src/queryClient.ts diff --git a/src/ui/resolvers/createPostResolver.ts b/src/resolvers/createPostResolver.ts similarity index 87% rename from src/ui/resolvers/createPostResolver.ts rename to src/resolvers/createPostResolver.ts index b216ee1..9325680 100644 --- a/src/ui/resolvers/createPostResolver.ts +++ b/src/resolvers/createPostResolver.ts @@ -1,8 +1,11 @@ -import { PostRequestBody } from '../../domain/post/entities/Post'; -import { CreatePostFormValues } from '../form-types/CreatePostForm.types'; +import { PostRequestBody } from '../domain/post/entities/Post'; +import { CreatePostFormValues } from '../forms/types/CreatePostForm.types'; import { Resolver, FieldErrors } from 'react-hook-form'; +// This file will be remove and there will be a post form where +// the validation will be done there + export const createPostResolver: Resolver< CreatePostFormValues, any, diff --git a/src/ui/services/BannerStateResolverService.ts b/src/services/BannerStateResolverService.ts similarity index 86% rename from src/ui/services/BannerStateResolverService.ts rename to src/services/BannerStateResolverService.ts index 781a32d..de3566c 100644 --- a/src/ui/services/BannerStateResolverService.ts +++ b/src/services/BannerStateResolverService.ts @@ -1,4 +1,4 @@ -import { WebSocketConnectionError } from '../hooks/useChatWebSocket'; +import { WebSocketConnectionError } from '../application/useChatWebSocket'; import { BannerMessages } from './constants/BannerMessages'; import { IErrorStateResolver } from './interfaces/IErrorStateResolver'; import { AuthErrorResolver } from './resolvers/AuthErrorResolver'; @@ -28,13 +28,6 @@ interface BannerStateResolverInput { onGoBack: () => void; } -/** - * BannerStateResolverService - * SOLID: Single Responsibility - Orchestrates state resolution - * SOLID: Open/Closed - Add new resolvers without modifying this class - * SOLID: Dependency Inversion - Depends on IErrorStateResolver interface - * Pattern: Strategy - Delegates error resolution to specialized resolvers - */ export class BannerStateResolverService { private static readonly errorResolvers: IErrorStateResolver[] = [ new AuthErrorResolver(), diff --git a/src/ui/services/ConnectionSubtitleService.ts b/src/services/ConnectionSubtitleService.ts similarity index 91% rename from src/ui/services/ConnectionSubtitleService.ts rename to src/services/ConnectionSubtitleService.ts index 050ee87..d666a53 100644 --- a/src/ui/services/ConnectionSubtitleService.ts +++ b/src/services/ConnectionSubtitleService.ts @@ -1,4 +1,4 @@ -import { Connection } from '../../domain/user/entities/Connection'; +import { Connection } from '../domain/user/entities/Connection'; /** * ConnectionSubtitleService diff --git a/src/ui/services/ConversationTimeFormatter.ts b/src/services/ConversationTimeFormatter.ts similarity index 100% rename from src/ui/services/ConversationTimeFormatter.ts rename to src/services/ConversationTimeFormatter.ts diff --git a/src/ui/services/DateFormatterService.ts b/src/services/DateFormatterService.ts similarity index 100% rename from src/ui/services/DateFormatterService.ts rename to src/services/DateFormatterService.ts diff --git a/src/ui/services/constants/BannerMessages.ts b/src/services/constants/BannerMessages.ts similarity index 100% rename from src/ui/services/constants/BannerMessages.ts rename to src/services/constants/BannerMessages.ts diff --git a/src/ui/services/interfaces/IErrorStateResolver.ts b/src/services/interfaces/IErrorStateResolver.ts similarity index 86% rename from src/ui/services/interfaces/IErrorStateResolver.ts rename to src/services/interfaces/IErrorStateResolver.ts index 09e94a3..034e97b 100644 --- a/src/ui/services/interfaces/IErrorStateResolver.ts +++ b/src/services/interfaces/IErrorStateResolver.ts @@ -1,4 +1,4 @@ -import { WebSocketConnectionError } from '../../hooks/useChatWebSocket'; +import { WebSocketConnectionError } from '../../application/useChatWebSocket'; import { BannerAction, ResolvedBannerState } from '../BannerStateResolverService'; /** diff --git a/src/ui/services/resolvers/AuthErrorResolver.ts b/src/services/resolvers/AuthErrorResolver.ts similarity index 79% rename from src/ui/services/resolvers/AuthErrorResolver.ts rename to src/services/resolvers/AuthErrorResolver.ts index 935f81c..f98bfbf 100644 --- a/src/ui/services/resolvers/AuthErrorResolver.ts +++ b/src/services/resolvers/AuthErrorResolver.ts @@ -1,13 +1,9 @@ -import { WebSocketConnectionError } from '../../hooks/useChatWebSocket'; +import { WebSocketConnectionError } from '../../application/useChatWebSocket'; import { ResolvedBannerState } from '../BannerStateResolverService'; import { IErrorStateResolver } from '../interfaces/IErrorStateResolver'; import { BannerMessages } from '../constants/BannerMessages'; -/** - * AuthErrorResolver - * SOLID: Single Responsibility - Handles authentication errors only - * SOLID: Open/Closed - Implements IErrorStateResolver interface - */ + export class AuthErrorResolver implements IErrorStateResolver { private readonly authCodes = ['UNAUTHORIZED', 'FORBIDDEN'] as const; diff --git a/src/ui/services/resolvers/ConnectionErrorResolver.ts b/src/services/resolvers/ConnectionErrorResolver.ts similarity index 92% rename from src/ui/services/resolvers/ConnectionErrorResolver.ts rename to src/services/resolvers/ConnectionErrorResolver.ts index 93bb810..ac6c938 100644 --- a/src/ui/services/resolvers/ConnectionErrorResolver.ts +++ b/src/services/resolvers/ConnectionErrorResolver.ts @@ -1,4 +1,4 @@ -import { WebSocketConnectionError } from '../../hooks/useChatWebSocket'; +import { WebSocketConnectionError } from '../../application/useChatWebSocket'; import { ResolvedBannerState } from '../BannerStateResolverService'; import { IErrorStateResolver } from '../interfaces/IErrorStateResolver'; import { BannerMessages } from '../constants/BannerMessages'; diff --git a/src/ui/services/resolvers/DefaultErrorResolver.ts b/src/services/resolvers/DefaultErrorResolver.ts similarity index 90% rename from src/ui/services/resolvers/DefaultErrorResolver.ts rename to src/services/resolvers/DefaultErrorResolver.ts index 15cbb18..275226c 100644 --- a/src/ui/services/resolvers/DefaultErrorResolver.ts +++ b/src/services/resolvers/DefaultErrorResolver.ts @@ -1,4 +1,4 @@ -import { WebSocketConnectionError } from '../../hooks/useChatWebSocket'; +import { WebSocketConnectionError } from '../../application/useChatWebSocket'; import { ResolvedBannerState } from '../BannerStateResolverService'; import { IErrorStateResolver } from '../interfaces/IErrorStateResolver'; diff --git a/src/ui/di.ts b/src/ui/di.ts deleted file mode 100644 index 7a3ef36..0000000 --- a/src/ui/di.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { AuthRepository } from "../data/repositories/AuthRepository"; -import { PostRepository } from "../data/repositories/PostRepository"; -import { ConnectionRepository } from "../data/repositories/ConnectionRepository"; -import { ProfileRepository } from "../data/repositories/ProfileRepository"; -import { ChatRepository } from "../data/repositories/ChatRepository"; -import { ActivityLogRepository } from "../data/repositories/ActivityLogRepository"; -import { NotificationRepository } from "../data/repositories/NotificationRepository"; -import { CommentRepository } from "../data/repositories/CommentRepository"; -import { IAuthRepository } from "../domain/auth/repositories/IAuthRepository"; -import { IPostRepository } from "../domain/post/repositories/IPostRepository"; -import { IConnectionRepository } from "../domain/user/repositories/IConnectionRepository"; -import { IProfileRepository } from "../domain/user/repositories/IProfileRepository"; -import { IChatRepository } from "../domain/chat/repositories/IChatRepository"; -import { IActivityLogRepository } from "../domain/activity/repositories/IActivityLogRepository"; -import { INotificationRepository } from "../domain/notification/repositories/INotificationRepository"; -import { ICommentRepository } from "../domain/post/repositories/ICommentRepository"; -import { GetNewsFeed } from "../domain/post/usecases/queries/GetNewsFeed"; -import { GetProfileById } from "../domain/user/usecases/GetProfileById"; -import { GetCurrentUserProfile } from "../domain/user/usecases/GetCurrentUserProfile"; -import { GetActivityLog } from "../domain/activity/usecases/GetActivityLog"; -import { GetNotifications } from "../domain/notification/usecases/GetNotifications"; -import { MarkNotificationAsRead } from "../domain/notification/usecases/MarkNotificationAsRead"; -import { MarkAllNotificationsAsRead } from "../domain/notification/usecases/MarkAllNotificationsAsRead"; -import { GetComments } from "../domain/post/usecases/queries/GetComments"; -import { CreateComment } from "../domain/post/usecases/commands/CreateComment"; - -export const authRepository: IAuthRepository = new AuthRepository(); -export const postRepository: IPostRepository = new PostRepository(); -export const connectionRepository: IConnectionRepository = new ConnectionRepository(); -export const profileRepository: IProfileRepository = new ProfileRepository(); -export const chatRepository: IChatRepository = new ChatRepository(); -export const activityLogRepository: IActivityLogRepository = new ActivityLogRepository(); -export const notificationRepository: INotificationRepository = new NotificationRepository(); -export const commentRepository: ICommentRepository = new CommentRepository(); - -export const getNewsFeedUseCase = new GetNewsFeed(postRepository); -export const getProfileByIdUseCase = new GetProfileById(profileRepository); -export const getCurrentUserProfileUseCase = new GetCurrentUserProfile(profileRepository); -export const getActivityLogUseCase = new GetActivityLog(activityLogRepository); -export const getNotificationsUseCase = new GetNotifications(notificationRepository); -export const markNotificationAsReadUseCase = new MarkNotificationAsRead(notificationRepository); -export const markAllNotificationsAsReadUseCase = new MarkAllNotificationsAsRead(notificationRepository); -export const getCommentsUseCase = new GetComments(commentRepository); -export const createCommentUseCase = new CreateComment(commentRepository); \ No newline at end of file diff --git a/src/ui/hooks/useActivityLog.ts b/src/ui/hooks/useActivityLog.ts deleted file mode 100644 index e15afe9..0000000 --- a/src/ui/hooks/useActivityLog.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { getActivityLogUseCase } from '../di'; - -/** - * useActivityLog Hook - * UI Layer: React Query hook for activity log data - * SOLID: Single Responsibility - Manages activity log query state - */ -export const useActivityLog = () => { - return useQuery({ - queryKey: ['activityLog'], - queryFn: () => getActivityLogUseCase.execute(), - }); -}; diff --git a/src/ui/hooks/useComments.ts b/src/ui/hooks/useComments.ts deleted file mode 100644 index 43ee25f..0000000 --- a/src/ui/hooks/useComments.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { getCommentsUseCase, createCommentUseCase } from '../di'; -import { CreateCommentRequest } from '../../domain/post/entities/Comment'; - -/** - * useComments Hook - * UI Layer: React Query hook for fetching comments - * SOLID: Single Responsibility - Manages comments query state - */ -export const useComments = (postId: string) => { - return useQuery({ - queryKey: ['comments', postId], - queryFn: () => getCommentsUseCase.execute(postId), - enabled: !!postId, - }); -}; - -/** - * useCreateComment Hook - * UI Layer: Mutation hook for creating a comment - */ -export const useCreateComment = (postId: string) => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: (data: CreateCommentRequest) => createCommentUseCase.execute(postId, data), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['comments', postId] }); - queryClient.invalidateQueries({ queryKey: ['newsFeed'] }); - }, - }); -}; diff --git a/src/ui/hooks/useConnections.ts b/src/ui/hooks/useConnections.ts deleted file mode 100644 index 2151705..0000000 --- a/src/ui/hooks/useConnections.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { connectionRepository } from "../di"; -import { GetProfiles, GetMyConnections } from "../../domain/user/usecases/GetConnections"; - -const getProfilesUseCase = new GetProfiles(connectionRepository); -const getMyConnectionsUseCase = new GetMyConnections(connectionRepository); - -export const useProfiles = () => { - return useQuery({ - queryKey: ["profiles"], - queryFn: () => getProfilesUseCase.execute(), - }); -}; - -export const useMyConnections = () => { - return useQuery({ - queryKey: ["myConnections"], - queryFn: () => getMyConnectionsUseCase.execute(), - }); -}; diff --git a/src/ui/hooks/useNotifications.ts b/src/ui/hooks/useNotifications.ts deleted file mode 100644 index 5dac25f..0000000 --- a/src/ui/hooks/useNotifications.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { getNotificationsUseCase, markNotificationAsReadUseCase, markAllNotificationsAsReadUseCase } from '../di'; - -/** - * useNotifications Hook - * UI Layer: React Query hook for notifications data - * SOLID: Single Responsibility - Manages notifications query state - */ -export const useNotifications = () => { - return useQuery({ - queryKey: ['notifications'], - queryFn: () => getNotificationsUseCase.execute(), - }); -}; - -/** - * useMarkNotificationAsRead Hook - * UI Layer: Mutation hook for marking single notification as read - */ -export const useMarkNotificationAsRead = () => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: (id: number) => markNotificationAsReadUseCase.execute(id), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['notifications'] }); - }, - }); -}; - -/** - * useMarkAllNotificationsAsRead Hook - * UI Layer: Mutation hook for marking all notifications as read - */ -export const useMarkAllNotificationsAsRead = () => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: () => markAllNotificationsAsReadUseCase.execute(), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['notifications'] }); - }, - }); -}; diff --git a/src/ui/hooks/useToggleLike.ts b/src/ui/hooks/useToggleLike.ts deleted file mode 100644 index 4fca375..0000000 --- a/src/ui/hooks/useToggleLike.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { PostRepository } from '../../data/repositories/PostRepository'; -import { ToggleLikePost } from '../../domain/post/usecases/commands/ToggleLikePost'; - -const postRepository = new PostRepository(); -const toggleLikeUseCase = new ToggleLikePost(postRepository); - -/** - * useToggleLike Hook - * SOLID: Single Responsibility - Handle like/unlike API calls with optimistic updates - */ -export const useToggleLike = () => { - const queryClient = useQueryClient(); - - const mutation = useMutation({ - mutationFn: ({ postId, isLiked }: { postId: string; isLiked: boolean }) => - toggleLikeUseCase.execute(postId, isLiked), - onError: (error) => { - console.error('Error toggling like:', error); - }, - }); - - const toggleLike = (postId: string, isLiked: boolean) => { - mutation.mutate({ postId, isLiked }); - }; - - return { - toggleLike, - isLoading: mutation.isPending, - }; -}; diff --git a/src/ui/hooks/useViewProfile.ts b/src/ui/hooks/useViewProfile.ts deleted file mode 100644 index 23c19c0..0000000 --- a/src/ui/hooks/useViewProfile.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { getProfileByIdUseCase, getCurrentUserProfileUseCase } from '../di'; - -/** - * useViewProfile Hook - * SOLID: Single Responsibility - Unified profile fetching for ViewProfile screen - * SOLID: Open/Closed - Handles both current user (profiles/me) and other users (profiles/{id}) - * - * @param userId - Optional user ID. If not provided, fetches current user's profile - */ -export const useViewProfile = (userId?: number) => { - const isCurrentUser = userId === undefined || userId === null; - - return useQuery({ - queryKey: isCurrentUser ? ['currentUserProfile'] : ['profile', userId], - queryFn: () => - isCurrentUser - ? getCurrentUserProfileUseCase.execute() - : getProfileByIdUseCase.execute(userId!), - }); -};