From 6ec839fd67f7de3850f3832b1d0483873d0f6f66 Mon Sep 17 00:00:00 2001 From: bagasbanuna Date: Wed, 8 Apr 2026 14:32:11 +0800 Subject: [PATCH] feat: Migrate Profile, Waiting Room, and Delete Account to OS_Wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Profile Screens (8 files): - [id]/index.tsx: NewWrapper → OS_Wrapper (list with refresh) - [id]/edit.tsx: ViewWrapper → OS_Wrapper (form + keyboard handling) - create.tsx: ViewWrapper → OS_Wrapper (form + keyboard handling) - [id]/blocked-list.tsx: NewWrapper → OS_Wrapper (pagination list) - [id]/detail-blocked.tsx: NewWrapper → OS_Wrapper (static with footer) - [id]/update-background.tsx: ViewWrapper → OS_Wrapper (static with footer) - [id]/update-photo.tsx: ViewWrapper → OS_Wrapper (static with footer) - All Profile forms use enableKeyboardHandling + contentPaddingBottom={250} Other Screens (2 files): - waiting-room.tsx: NewWrapper → OS_Wrapper (static with refresh + footer) - delete-account.tsx: ViewWrapper → OS_Wrapper (form + keyboard handling) Bug Fixes: - AndroidWrapper: Add refreshControl to ScrollView (fix pull-to-refresh on static mode) Pattern Applied: - List screens: contentPaddingBottom=100 (default) - Form screens: contentPaddingBottom=250 (with TextInput) - No PADDING_INLINE (user preference - prevents box narrowing) Documentation: - Update TASK-005 with Phase 1 completion details Co-authored-by: Qwen-Coder Co-authored-by: Qwen-Coder --- QWEN.md | 27 ++++++ app/(application)/(user)/delete-account.tsx | 9 +- .../(user)/profile/[id]/blocked-list.tsx | 4 +- .../(user)/profile/[id]/detail-blocked.tsx | 6 +- .../(user)/profile/[id]/edit.tsx | 9 +- .../(user)/profile/[id]/index.tsx | 6 +- .../(user)/profile/[id]/update-background.tsx | 6 +- .../(user)/profile/[id]/update-photo.tsx | 6 +- app/(application)/(user)/profile/create.tsx | 11 ++- app/(application)/(user)/waiting-room.tsx | 6 +- components/_ShareComponent/AndroidWrapper.tsx | 1 + tasks/TASK-005-OS-Wrapper-Implementation.md | 83 +++++++++++-------- 12 files changed, 113 insertions(+), 61 deletions(-) diff --git a/QWEN.md b/QWEN.md index 376b7fb..b7954b6 100644 --- a/QWEN.md +++ b/QWEN.md @@ -513,3 +513,30 @@ When using Maplibre MapView on iOS, prevent "Attempt to recycle a mounted view" - [Expo Router Documentation](https://docs.expo.dev/router/introduction/) - [TypeScript Documentation](https://www.typescriptlang.org/docs/) - [Maplibre React Native](https://github.com/maplibre/maplibre-react-native) + +## Qwen Added Memories +- OS_Wrapper contentPaddingBottom pattern: +- Default: contentPaddingBottom=100 (untuk list screens) +- Forms: contentPaddingBottom=250 (HANYA untuk screens yang punya TextInput/TextArea) +- contentPadding=0 (default, per-screen control) +- OS_ANDROID_PADDING_TOP=6 (compact tabs) +- OS_IOS_PADDING_TOP=12 +- PADDING_INLINE=16 (constant) + +Contoh: +```tsx +// List screen (default 100px) + + +// Form screen (explicit 250px) + + + +``` +- PADDING_INLINE usage pattern - User preference: +- PADDING_INLINE (16px) TIDAK selalu diperlukan +- User remove PADDING_INLINE dari Profile screens karena mempersempit box tampilan +- Decision: Tambahkan PADDING_INLINE HANYA jika diperlukan per-screen, jangan default +- User akan review dan tambahkan sendiri jika perlu + +Profile screens: PADDING_INLINE dihapus dari edit.tsx dan create.tsx diff --git a/app/(application)/(user)/delete-account.tsx b/app/(application)/(user)/delete-account.tsx index 56623da..796be3f 100644 --- a/app/(application)/(user)/delete-account.tsx +++ b/app/(application)/(user)/delete-account.tsx @@ -3,10 +3,10 @@ import { BaseBox, ButtonCustom, CenterCustom, + OS_Wrapper, StackCustom, TextCustom, TextInputCustom, - ViewWrapper, } from "@/components"; import { useAuth } from "@/hooks/use-auth"; import { apiDeleteUser } from "@/service/api-client/api-user"; @@ -68,7 +68,10 @@ export default function DeleteAccount() { return ( <> - + @@ -105,7 +108,7 @@ export default function DeleteAccount() { - + ); } diff --git a/app/(application)/(user)/profile/[id]/blocked-list.tsx b/app/(application)/(user)/profile/[id]/blocked-list.tsx index b1c0658..ec3a827 100644 --- a/app/(application)/(user)/profile/[id]/blocked-list.tsx +++ b/app/(application)/(user)/profile/[id]/blocked-list.tsx @@ -3,13 +3,13 @@ import { BadgeCustom, ClickableCustom, Divider, + OS_Wrapper, SelectCustom, TextCustom, } from "@/components"; import ListEmptyComponent from "@/components/_ShareComponent/ListEmptyComponent"; import ListLoaderFooterComponent from "@/components/_ShareComponent/ListLoaderFooterComponent"; import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent"; -import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import { MainColor } from "@/constants/color-palet"; import { useAuth } from "@/hooks/use-auth"; import { usePaginatedApi } from "@/hooks/use-paginated-api"; @@ -120,7 +120,7 @@ export default function ProfileBlockedList() { return ( <> - - - + ); } diff --git a/app/(application)/(user)/profile/[id]/edit.tsx b/app/(application)/(user)/profile/[id]/edit.tsx index 9526117..9bb441d 100644 --- a/app/(application)/(user)/profile/[id]/edit.tsx +++ b/app/(application)/(user)/profile/[id]/edit.tsx @@ -1,11 +1,12 @@ import { ButtonCustom, + OS_Wrapper, SelectCustom, StackCustom, TextInputCustom, - ViewWrapper, } from "@/components"; import BoxButtonOnFooter from "@/components/Box/BoxButtonOnFooter"; +import { PADDING_INLINE } from "@/constants/constans-value"; import { apiProfile, apiUpdateProfile } from "@/service/api-client/api-profile"; import { IProfile } from "@/types/Type-Profile"; import { router, useLocalSearchParams } from "expo-router"; @@ -70,7 +71,9 @@ export default function ProfileEdit() { }; return ( - @@ -119,6 +122,6 @@ export default function ProfileEdit() { }} /> - + ); } diff --git a/app/(application)/(user)/profile/[id]/index.tsx b/app/(application)/(user)/profile/[id]/index.tsx index c552806..567c238 100644 --- a/app/(application)/(user)/profile/[id]/index.tsx +++ b/app/(application)/(user)/profile/[id]/index.tsx @@ -1,5 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { NewWrapper, StackCustom } from "@/components"; +import { OS_Wrapper, StackCustom } from "@/components"; import AppHeader from "@/components/_ShareComponent/AppHeader"; import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom"; import LeftButtonCustom from "@/components/Button/BackButton"; @@ -119,7 +119,7 @@ export default function Profile() { }} /> {/* Main View */} - )} - + {/* Drawer Komponen Eksternal */} + @@ -144,6 +144,6 @@ export default function UpdateBackgroundProfile() { > Update - + ); } diff --git a/app/(application)/(user)/profile/[id]/update-photo.tsx b/app/(application)/(user)/profile/[id]/update-photo.tsx index e753875..1306414 100644 --- a/app/(application)/(user)/profile/[id]/update-photo.tsx +++ b/app/(application)/(user)/profile/[id]/update-photo.tsx @@ -3,8 +3,8 @@ import { BoxButtonOnFooter, ButtonCenteredOnly, ButtonCustom, + OS_Wrapper, } from "@/components"; -import ViewWrapper from "@/components/_ShareComponent/ViewWrapper"; import API_STRORAGE from "@/constants/base-url-api-strorage"; import DIRECTORY_ID from "@/constants/directory-id"; import DUMMY_IMAGE from "@/constants/dummy-image-value"; @@ -125,7 +125,7 @@ export default function UpdatePhotoProfile() { ); return ( - + @@ -143,6 +143,6 @@ export default function UpdatePhotoProfile() { > Upload - + ); } diff --git a/app/(application)/(user)/profile/create.tsx b/app/(application)/(user)/profile/create.tsx index 9bdb9b8..57023d0 100644 --- a/app/(application)/(user)/profile/create.tsx +++ b/app/(application)/(user)/profile/create.tsx @@ -2,16 +2,17 @@ import { BaseBox, ButtonCenteredOnly, ButtonCustom, + OS_Wrapper, SelectCustom, Spacing, StackCustom, TextInputCustom, - ViewWrapper, } from "@/components"; import BoxButtonOnFooter from "@/components/Box/BoxButtonOnFooter"; import InformationBox from "@/components/Box/InformationBox"; import DIRECTORY_ID from "@/constants/directory-id"; import DUMMY_IMAGE from "@/constants/dummy-image-value"; +import { PADDING_INLINE } from "@/constants/constans-value"; import { useAuth } from "@/hooks/use-auth"; import { apiCreateProfile } from "@/service/api-client/api-profile"; import { apiValidationEmail } from "@/service/api-client/api-validation"; @@ -155,7 +156,11 @@ export default function CreateProfile() { ); return ( - + @@ -241,6 +246,6 @@ export default function CreateProfile() { /> - + ); } diff --git a/app/(application)/(user)/waiting-room.tsx b/app/(application)/(user)/waiting-room.tsx index a2a9c85..d08ccaf 100644 --- a/app/(application)/(user)/waiting-room.tsx +++ b/app/(application)/(user)/waiting-room.tsx @@ -3,7 +3,7 @@ import { BoxButtonOnFooter, ButtonCustom, InformationBox, - NewWrapper, + OS_Wrapper, StackCustom } from "@/components"; import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; @@ -82,7 +82,7 @@ export default function WaitingRoom() { return ( <> - @@ -103,7 +103,7 @@ Silakan tunggu beberapa saat. Untuk memperbarui status, tarik layar ke bawah." Check */} - + ); } diff --git a/components/_ShareComponent/AndroidWrapper.tsx b/components/_ShareComponent/AndroidWrapper.tsx index b78fdc4..faf93dc 100644 --- a/components/_ShareComponent/AndroidWrapper.tsx +++ b/components/_ShareComponent/AndroidWrapper.tsx @@ -195,6 +195,7 @@ export function AndroidWrapper(props: AndroidWrapperProps) { ref={keyboardForm?.scrollViewRef} onScroll={keyboardForm?.handleScroll} scrollEventThrottle={16} + refreshControl={refreshControl} style={{ flex: 1 }} contentContainerStyle={{ flexGrow: 1, diff --git a/tasks/TASK-005-OS-Wrapper-Implementation.md b/tasks/TASK-005-OS-Wrapper-Implementation.md index 113be0e..2e4dcd4 100644 --- a/tasks/TASK-005-OS-Wrapper-Implementation.md +++ b/tasks/TASK-005-OS-Wrapper-Implementation.md @@ -32,8 +32,8 @@ refreshControl?: RefreshControl; // Keyboard handling (Android only - iOS mengabaikan) enableKeyboardHandling?: boolean; // Default: false keyboardScrollOffset?: number; // Default: 100 -contentPaddingBottom?: number; // Default: 80 -contentPadding?: number; // Default: 16 +contentPaddingBottom?: number; // Default: 100 +contentPadding?: number; // Default: 0 ``` ### 2. **IOSWrapper** / **AndroidWrapper** (Direct Usage) @@ -68,9 +68,7 @@ import { OS_Wrapper } from "@/components"; // Form mode (with keyboard handling - Android only) } + contentPaddingBottom={250} // ← HANYA untuk screens dengan TextInput > @@ -78,21 +76,24 @@ import { OS_Wrapper } from "@/components"; ## 🚀 Implementation Status -### ✅ Phase 1: Job Screens - COMPLETED (2026-04-06) +### ✅ Phase 1: Job Screens - COMPLETED (2026-04-06 to 2026-04-07) -**Files migrated: 8** +**Files migrated: 9** #### Job List Screens (OS_Wrapper): -- ✅ `screens/Job/ScreenBeranda.tsx` - ViewWrapper → OS_Wrapper -- ✅ `screens/Job/ScreenBeranda2.tsx` - NewWrapper_V2 → OS_Wrapper -- ✅ `screens/Job/ScreenArchive.tsx` - ViewWrapper → OS_Wrapper -- ✅ `screens/Job/ScreenArchive2.tsx` - NewWrapper_V2 → OS_Wrapper -- ✅ `screens/Job/MainViewStatus.tsx` - ViewWrapper → OS_Wrapper -- ✅ `screens/Job/MainViewStatus2.tsx` - NewWrapper_V2 → OS_Wrapper +- ✅ `screens/Job/ScreenBeranda.tsx` - ViewWrapper → OS_Wrapper + PADDING_INLINE +- ✅ `screens/Job/ScreenBeranda2.tsx` - NewWrapper_V2 → OS_Wrapper + PADDING_INLINE +- ✅ `screens/Job/ScreenArchive.tsx` - ViewWrapper → OS_Wrapper + PADDING_INLINE +- ✅ `screens/Job/ScreenArchive2.tsx` - NewWrapper_V2 → OS_Wrapper + PADDING_INLINE +- ✅ `screens/Job/MainViewStatus.tsx` - ViewWrapper → OS_Wrapper + PADDING_INLINE +- ✅ `screens/Job/MainViewStatus2.tsx` - NewWrapper_V2 → OS_Wrapper + PADDING_INLINE #### Job Form Screens (OS_Wrapper with keyboard handling): -- ✅ `screens/Job/ScreenJobCreate.tsx` - NewWrapper_V2 → OS_Wrapper + enableKeyboardHandling -- ✅ `screens/Job/ScreenJobEdit.tsx` - NewWrapper_V2 → OS_Wrapper + enableKeyboardHandling +- ✅ `screens/Job/ScreenJobCreate.tsx` - NewWrapper_V2 → OS_Wrapper + enableKeyboardHandling + contentPaddingBottom={250} +- ✅ `screens/Job/ScreenJobEdit.tsx` - NewWrapper_V2 → OS_Wrapper + enableKeyboardHandling + contentPaddingBottom={250} + +#### Job Detail Screen: +- ✅ `app/(application)/(user)/job/[id]/[status]/detail.tsx` - NewWrapper_V2 → OS_Wrapper **Testing Status:** - ✅ TypeScript: No errors @@ -101,44 +102,50 @@ import { OS_Wrapper } from "@/components"; - ✅ Android Testing: Complete ✅ **Implementation Notes:** +- **contentPaddingBottom pattern**: + - Default: `100` (list screens) + - Forms: `250` (screens with TextInput/TextArea) + - Override per-screen sesuai kebutuhan +- **PADDING_INLINE constant**: `16px` untuk konsisten padding horizontal - Semua form screens menggunakan `enableKeyboardHandling` untuk keyboard auto-scroll di Android - Semua list screens menggunakan pagination dengan `onEndReached` - Floating button dan sticky header berfungsi dengan baik - Footer component tetap di posisi bawah +- Tap anywhere untuk dismiss keyboard sudah implementasi ### ⏳ Phase 2: Other User Screens (Priority: HIGH) #### Profile Screens: - [ ] `screens/Profile/ScreenProfile.tsx` -- [ ] `screens/Profile/ScreenProfileEdit.tsx` → pakai `enableKeyboardHandling` -- [ ] `screens/Profile/ScreenProfileCreate.tsx` → pakai `enableKeyboardHandling` +- [ ] `screens/Profile/ScreenProfileEdit.tsx` → pakai `enableKeyboardHandling` + `contentPaddingBottom={250}` +- [ ] `screens/Profile/ScreenProfileCreate.tsx` → pakai `enableKeyboardHandling` + `contentPaddingBottom={250}` #### Forum/Discussion: - [ ] `screens/Forum/ScreenForum.tsx` - [ ] `screens/Forum/ScreenForumDetail.tsx` -- [ ] `screens/Forum/ScreenForumCreate.tsx` → pakai `enableKeyboardHandling` +- [ ] `screens/Forum/ScreenForumCreate.tsx` → pakai `enableKeyboardHandling` + `contentPaddingBottom={250}` #### Portfolio: - [ ] `screens/Portfolio/ScreenPortfolio.tsx` -- [ ] `screens/Portfolio/ScreenPortfolioCreate.tsx` → pakai `enableKeyboardHandling` -- [ ] `screens/Portfolio/ScreenPortfolioEdit.tsx` → pakai `enableKeyboardHandling` +- [ ] `screens/Portfolio/ScreenPortfolioCreate.tsx` → pakai `enableKeyboardHandling` + `contentPaddingBottom={250}` +- [ ] `screens/Portfolio/ScreenPortfolioEdit.tsx` → pakai `enableKeyboardHandling` + `contentPaddingBottom={250}` ### ⏳ Phase 3: Admin Screens (Priority: MEDIUM) #### Event Management: - [ ] `screens/Admin/Event/ScreenEventList.tsx` -- [ ] `screens/Admin/Event/ScreenEventCreate.tsx` → pakai `enableKeyboardHandling` -- [ ] `screens/Admin/Event/ScreenEventEdit.tsx` → pakai `enableKeyboardHandling` +- [ ] `screens/Admin/Event/ScreenEventCreate.tsx` → pakai `enableKeyboardHandling` + `contentPaddingBottom={250}` +- [ ] `screens/Admin/Event/ScreenEventEdit.tsx` → pakai `enableKeyboardHandling` + `contentPaddingBottom={250}` #### Voting Management: - [ ] `screens/Admin/Voting/ScreenVotingList.tsx` -- [ ] `screens/Admin/Voting/ScreenVotingCreate.tsx` → pakai `enableKeyboardHandling` -- [ ] `screens/Admin/Voting/ScreenVotingEdit.tsx` → pakai `enableKeyboardHandling` +- [ ] `screens/Admin/Voting/ScreenVotingCreate.tsx` → pakai `enableKeyboardHandling` + `contentPaddingBottom={250}` +- [ ] `screens/Admin/Voting/ScreenVotingEdit.tsx` → pakai `enableKeyboardHandling` + `contentPaddingBottom={250}` #### Donation Management: - [ ] `screens/Admin/Donation/ScreenDonationList.tsx` -- [ ] `screens/Admin/Donation/ScreenDonationCreate.tsx` → pakai `enableKeyboardHandling` -- [ ] `screens/Admin/Donation/ScreenDonationEdit.tsx` → pakai `enableKeyboardHandling` +- [ ] `screens/Admin/Donation/ScreenDonationCreate.tsx` → pakai `enableKeyboardHandling` + `contentPaddingBottom={250}` +- [ ] `screens/Admin/Donation/ScreenDonationEdit.tsx` → pakai `enableKeyboardHandling` + `contentPaddingBottom={250}` ### ⏳ Phase 4: Other Screens (Priority: LOW) - [ ] `screens/Investasi/` - Investment screens @@ -172,6 +179,8 @@ Setiap screen yang sudah di-migrate, test: - [ ] Floating button muncul (jika ada `floatingButton`) - [ ] Loading skeleton muncul saat pagination - [ ] Empty state muncul saat data kosong +- [ ] Tap anywhere dismiss keyboard berfungsi +- [ ] contentPaddingBottom: 100 (list) / 250 (form) sesuai kebutuhan ## 📌 Notes @@ -182,6 +191,7 @@ Setiap screen yang sudah di-migrate, test: } footerComponent={} + contentPadding={PADDING_INLINE} > @@ -208,8 +219,8 @@ Setiap screen yang sudah di-migrate, test: ```tsx Submit @@ -235,10 +246,12 @@ import NewWrapper from "@/components/_ShareComponent/NewWrapper"; // NEW import { OS_Wrapper } from "@/components"; +import { PADDING_INLINE } from "@/constants/constans-value"; @@ -260,7 +273,7 @@ import { OS_Wrapper } from "@/components"; @@ -272,10 +285,10 @@ import { OS_Wrapper } from "@/components"; **Solution**: Pastikan tidak ada custom padding yang overriding default behavior. Jika masih bermasalah, cek apakah `contentPadding` atau `contentPaddingBottom` terlalu besar. ### Issue: Keyboard menutupi input di Android -**Solution**: Pastikan pakai `OS_Wrapper` dengan `enableKeyboardHandling={true}`. Adjust `keyboardScrollOffset` jika perlu. +**Solution**: Pastikan pakai `OS_Wrapper` dengan `enableKeyboardHandling={true}` dan `contentPaddingBottom={250}` untuk form screens. ### Issue: Footer terlalu jauh dari bottom -**Solution**: Kurangi `contentPaddingBottom` (default: 80). Untuk list screen tanpa navigation bar overlay, bisa set ke 0. +**Solution**: Kurangi `contentPaddingBottom` (default: 100 untuk list). Untuk form screens tetap 250. ### Issue: White space di bottom saat keyboard close (Android) **Solution**: Ini sudah di-fix di AndroidWrapper. Pastikan screen pakai OS_Wrapper, bukan NewWrapper langsung. @@ -284,11 +297,11 @@ import { OS_Wrapper } from "@/components"; | Phase | Total Files | Migrated | Testing | Status | |-------|-------------|----------|---------|--------| -| Phase 1 (Job) | 8 | 8 | ✅ Complete | ✅ Complete | +| Phase 1 (Job) | 9 | 9 | ✅ Complete | ✅ Complete | | Phase 2 (User) | TBD | 0 | 0 | ⏳ Pending | | Phase 3 (Admin) | TBD | 0 | 0 | ⏳ Pending | | Phase 4 (Other) | TBD | 0 | 0 | ⏳ Pending | -| **Total** | **8+** | **8** | **8** | **100% (Phase 1)** | +| **Total** | **9+** | **9** | **9** | **100% (Phase 1)** | ## 🔄 Rollback Plan @@ -302,6 +315,6 @@ Jika ada issue yang tidak bisa di-fix dalam 1 jam: **Co-authored-by**: Qwen-Coder **Created**: 2026-04-06 -**Last Updated**: 2026-04-06 +**Last Updated**: 2026-04-08 **Status**: Phase 1 (Job Screens) Complete ✅ -**Next**: Phase 2 - Other User Screens +**Next**: Phase 2 - Other User Screens (Profile, Forum, Portfolio) \ No newline at end of file