Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
140 commits
Select commit Hold shift + click to select a range
ba2fdeb
chore: Add Dropdown Package
b0nsu Dec 10, 2025
d919a0f
chore: Add Dropdown, Toast Package
b0nsu Dec 10, 2025
a4a6b43
style: Add Icons
b0nsu Dec 10, 2025
c8f2d0b
style: Add Icons
b0nsu Dec 10, 2025
e757c03
feat: Implement Scrap Main Header
b0nsu Dec 10, 2025
19d4f26
feat: Implement Scrap Page
b0nsu Dec 10, 2025
6ea1442
feat: Implement Scrap Item Grid
b0nsu Dec 10, 2025
bdb85dc
chore: Add Dropdown Package
b0nsu Dec 10, 2025
867c43e
feat: Initialize Scrap Search Page
b0nsu Dec 10, 2025
8b0a953
feat: Implement Reducer
b0nsu Dec 10, 2025
6ac7e3f
feat: add tooltip and toast packages
b0nsu Dec 12, 2025
1939103
feat: add scrapDataStore with search functionality
b0nsu Dec 12, 2025
dea0524
feat: add scrapDataStore with search functionality
b0nsu Dec 12, 2025
02ceb4b
chore: add temporary data generation functions
b0nsu Dec 12, 2025
76258d4
feat: add tooltip and toast packages
b0nsu Dec 12, 2025
8fe1835
feat: add sorting functionality
b0nsu Dec 12, 2025
c28fb01
feat: implement scrap search page
b0nsu Dec 12, 2025
f097373
feat: add sorting functionality, refactor: change ScrapGrid data prop
b0nsu Dec 12, 2025
5f742e6
feat: add scrap item types
b0nsu Dec 12, 2025
f7abc0d
refactor: restructure ScrapItem props
b0nsu Dec 12, 2025
6a32431
refactor: change directory structure
b0nsu Dec 12, 2025
8db60c3
refactor: change directory structure
b0nsu Dec 12, 2025
fd493b7
refactor: change directory structure
b0nsu Dec 12, 2025
38a0778
refactor(scrap): update scrap and folder type definitions
b0nsu Dec 16, 2025
0824af4
feat(scrap): implement scrap create, update, delete, and search logic
b0nsu Dec 16, 2025
4fc5cc0
feat(trash): add trash store with restore and permanent delete
b0nsu Dec 16, 2025
9610791
feat(scrap): add folder content list screen
b0nsu Dec 16, 2025
f443d5a
refactor(sort): improve sorting logic and usage
b0nsu Dec 16, 2025
f616f75
feat(ui): implement responsive auto grid layout
b0nsu Dec 16, 2025
2249678
refactor(search): improve scrap search page and results
b0nsu Dec 16, 2025
b605394
feat(trash): add trash page and header
b0nsu Dec 16, 2025
cdd5b28
feat(trash): add tooltip and close behavior
b0nsu Dec 16, 2025
2de653d
chore(ui): rename Item components to Card and update filenames
b0nsu Dec 16, 2025
0c2e6ec
refactor(reducer): make reducer exhaustive
b0nsu Dec 16, 2025
a91023d
feat(scrap): implement search logic
b0nsu Dec 16, 2025
2936c88
refactor(sort): improve sorting logic and usage
b0nsu Dec 16, 2025
a0e2b3d
refactor(search): improve scrap search page and results
b0nsu Dec 16, 2025
0b2199d
refactor(ui): improve modal UI
b0nsu Dec 16, 2025
6c6eeb6
refactor(scrap): update scrap header props
b0nsu Dec 16, 2025
1b2ef37
refactor(scrap): improve scrap page implementation
b0nsu Dec 16, 2025
9481c12
Temp will deprecated
b0nsu Dec 16, 2025
bc14101
fix: correct typos
b0nsu Dec 16, 2025
52a5157
feat: add scrap API integration
b0nsu Dec 18, 2025
899aeec
refactor: separate card component
b0nsu Dec 18, 2025
6d0947a
refactor: separate tooltip component
b0nsu Dec 18, 2025
dab5732
feat: update header back navigation UI
b0nsu Dec 18, 2025
f9b4fbb
refactor: separate scrap card component
b0nsu Dec 18, 2025
fe5e74b
refactor: remove scrap and trash stores and update store logic
b0nsu Dec 18, 2025
d5b3dc9
chore: update toast config
b0nsu Dec 18, 2025
11cbbb6
feat: integrate scrap API
b0nsu Dec 18, 2025
8471240
feat: revise screen UI and integrate API with refactored structure
b0nsu Dec 18, 2025
b3244e8
refactor: add props type to card component
b0nsu Dec 18, 2025
0b52ed9
feat: implement fullscreen modal
b0nsu Dec 18, 2025
7b9c563
refactor: deprecate unused feature
b0nsu Dec 18, 2025
a82c140
refactor: deprecate unused feature
b0nsu Dec 18, 2025
0536124
refactor: update code to match API schema
b0nsu Dec 18, 2025
c376f4f
refactor: update code to match API schema
b0nsu Dec 18, 2025
98169ef
refactor: update code to match API schema
b0nsu Dec 18, 2025
42b247d
refactor: update navigator
b0nsu Dec 18, 2025
a668700
chore: add expo-blur and expo-image-picker
b0nsu Dec 18, 2025
2396814
refactor: remove type workaround
b0nsu Dec 18, 2025
2e6a788
refactor(api): migrate to TanStack Query client
b0nsu Dec 19, 2025
ac65dc1
fix(api): invalidate all search queries to prevent stale cache
b0nsu Dec 19, 2025
63c40c5
feat(api): add file upload API
b0nsu Dec 19, 2025
849a5ce
fix(api): update trash API
b0nsu Dec 19, 2025
f71f1f3
refactor(store): rename store
b0nsu Dec 19, 2025
e40aae2
refactor(api): refactor API
b0nsu Dec 19, 2025
f78d31b
fix(reducer): improve select state handling with type checks
b0nsu Dec 19, 2025
2c903e9
refactor(api): change data fetching Query
b0nsu Dec 19, 2025
982a95f
refactor(api): change data fetching Query and add search debounce
b0nsu Dec 19, 2025
7635306
feat(scrap): add create scrap functionality
b0nsu Dec 19, 2025
adbab4a
refactor(api): modify fetch query parameters
b0nsu Dec 19, 2025
ffdd0a3
feat(scrap): implement scrap detail page
b0nsu Dec 19, 2025
ebf902b
feat(scrap): implement scrap detail page
b0nsu Dec 19, 2025
4490a10
chore: add react-native-skia dependency
b0nsu Dec 22, 2025
005d502
feat: implement handwriting editor
b0nsu Dec 22, 2025
be12853
feat: add smoothing to handwriting editor
b0nsu Dec 22, 2025
66e9adf
Refactor: relocate handwriting API to dedicated directory
b0nsu Dec 24, 2025
dc06dbd
Refactor Card component to support responsive sizing in grid layout
b0nsu Dec 24, 2025
a260fdd
Refactor Grid component to support responsive layout
b0nsu Dec 24, 2025
1a15f0a
Refactor grid layout function for responsive design
b0nsu Dec 24, 2025
e5185ab
Implement handwriting functionality
b0nsu Dec 24, 2025
0bf46c6
Refactor props type
b0nsu Dec 24, 2025
e4e24d8
Refactor props type
b0nsu Dec 24, 2025
f754754
Implement handwriting functionality
b0nsu Dec 24, 2025
1c73a8e
Implement useGetFolderDetail for folder data fetching
b0nsu Dec 24, 2025
b2a3f2a
Refactor: separate tooltip directory
b0nsu Dec 25, 2025
1fef3cf
Fix: remove max size constraint from grid layout
b0nsu Dec 25, 2025
fccb6d4
Feat: add scrap move feature and modal
b0nsu Dec 25, 2025
d618ccc
Feat: add folder creation and separate image picker modal
b0nsu Dec 25, 2025
eea566f
Feat: add scrap move feature
b0nsu Dec 25, 2025
5771865
Fix: correct responsive layout behavior
b0nsu Dec 25, 2025
62233e0
Fix: correct responsive layout behavior
b0nsu Dec 25, 2025
30e5a32
Fix: invalidate API queries
b0nsu Dec 25, 2025
4c19034
Refactor: move ScrapHeadCard directory
b0nsu Dec 25, 2025
412f741
fix(api): handle optimistic update for API
b0nsu Dec 29, 2025
33907d2
feat: apply skeleton image
b0nsu Dec 29, 2025
974df70
feat: implement recent scrap
b0nsu Dec 29, 2025
ecfd4a9
fix: update image util
b0nsu Dec 29, 2025
3fe9d40
feat: add recent scrap section
b0nsu Dec 29, 2025
06dd643
fix: update FlatList keyExtractor
b0nsu Dec 29, 2025
1dd92bb
feat: make Card component responsive and add skeleton image
b0nsu Dec 29, 2025
fd6ce3f
fix: update header
b0nsu Dec 29, 2025
d3e0e70
fix: update image upload functionality
b0nsu Dec 29, 2025
b5ac3ae
fix: update drag tab UX
b0nsu Dec 29, 2025
481ef94
fix: update MoveScrapModal props
b0nsu Dec 29, 2025
47ed665
fix: exclude self info in MoveScrapModal
b0nsu Dec 29, 2025
a237b3f
fix: update image functionality
b0nsu Dec 29, 2025
d274db5
feat: connect AsyncStorage
b0nsu Dec 29, 2025
d725059
feat: add new API endpoints for scrap and Q&A functionalities
b0nsu Dec 30, 2025
a1a9ea4
feat: enhance card types with trash functionality and update API resp…
b0nsu Dec 30, 2025
200ecc0
feat: add @react-native-async-storage/async-storage dependency and up…
b0nsu Dec 30, 2025
2f5775c
feat: add Q&A image retrieval and folder update functionalities
b0nsu Dec 30, 2025
284623f
feat: extend card exports to include trash-related types
b0nsu Dec 30, 2025
6388621
feat: refactor card components to utilize new props structure and enh…
b0nsu Dec 30, 2025
d76fd2e
feat: refactor image upload handling and enhance pre-signed URL funct…
b0nsu Dec 30, 2025
1857479
feat: enhance TooltipPopover component with dynamic styling based on …
b0nsu Dec 30, 2025
4c68a8c
feat: refactor SearchScrapScreen to separate folder and scrap results…
b0nsu Dec 30, 2025
7181b79
feat: refactor CreateFolderModal to enhance pre-signed URL handling a…
b0nsu Dec 30, 2025
f41f347
feat: update TrashItemTooltip to use new TrashListItemProps for impro…
b0nsu Dec 30, 2025
eefe975
refactor: simplify ScrapCard and TrashCard components by removing unn…
b0nsu Dec 30, 2025
0b2f2b3
feat: refactor scrap screens to integrate ScrapModalProvider and enha…
b0nsu Jan 1, 2026
08c10ee
feat: add useGetQnaAllImages hook for retrieving all Q&A images
b0nsu Jan 1, 2026
3f38576
refactor: update ScrapCard and ScrapAddItem components to utilize Scr…
b0nsu Jan 1, 2026
79fb9c2
feat: implement ScrapModalContext for managing modal states and refet…
b0nsu Jan 1, 2026
e9363e7
refactor: remove unused updateFolder mutation from ItemTooltip component
b0nsu Jan 1, 2026
6599edc
refactor: update modal components to utilize ScrapModalContext for im…
b0nsu Jan 1, 2026
cfdfaf1
fix: format updatedAt in RecentScrapCard to display time in minutes f…
b0nsu Jan 1, 2026
6b5e2d8
feat: add formatToMinute utility function for consistent date formatt…
b0nsu Jan 1, 2026
1642c25
fix: resolve TypeScript errors in scrap API controller
b0nsu Jan 1, 2026
0f9b086
refactor: reorganize scrap feature directory structure and rename fil…
b0nsu Jan 1, 2026
68c8351
feat: add loading skeleton to ImageWithSkeleton component for improve…
b0nsu Jan 1, 2026
051c882
fix: adjust ScrapHeader layout for conditional title rendering
b0nsu Jan 1, 2026
9a17f7c
refactor: update DeletedScrapHeader to use actions object for event h…
b0nsu Jan 1, 2026
b1aa192
refactor: update useCardImageSources hook to prioritize thumbnailUrl …
b0nsu Jan 1, 2026
a4821ea
refactor: increase recent scrap limit from 10 to 30 in useRecentScrap…
b0nsu Jan 1, 2026
f124915
refactor: streamline event handling in DeletedScrapScreen by using ac…
b0nsu Jan 1, 2026
79ae6de
refactor: enhance MoveScrapModal layout and structure for improved us…
b0nsu Jan 1, 2026
124e7d3
feat: implement FolderScrapScreen and ScrapDetailScreen for enhanced …
b0nsu Jan 1, 2026
b212557
feat: add history management and text handling improvements in Drawin…
b0nsu Jan 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"permissions": {
"allow": [
"Bash(grep:*)",
"Bash(tree:*)",
"Bash(find:*)",
"Bash(git mv:*)",
"Bash(wc:*)",
"Bash(npx tsc:*)",
"Bash(git add:*)",
"Bash(git commit:*)"
]
}
}
3 changes: 3 additions & 0 deletions apps/native/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import '@/app/providers/api';
import { LoadingScreen } from '@components/common';
import { useLoadAssets } from '@hooks';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import Toast from 'react-native-toast-message';
import { toastConfig } from '@/features/student/scrap/components/Notification/Toast';

const queryClient = new QueryClient();

Expand Down Expand Up @@ -46,6 +48,7 @@ export default function App() {
<NavigationContainer theme={navigationTheme} linking={linking}>
<StatusBar style='dark' />
<RootNavigator />
<Toast config={toastConfig} style={{ zIndex: 9999 }} />
</NavigationContainer>
</SafeAreaProvider>
</GestureHandlerRootView>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions apps/native/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ module.exports = function (api) {
api.cache(true);
return {
presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }], 'nativewind/babel'],
plugins: ['react-native-reanimated/plugin'],
};
};
9 changes: 9 additions & 0 deletions apps/native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,25 @@
"dependencies": {
"@expo/vector-icons": "^15.0.3",
"@gorhom/bottom-sheet": "^5.2.7",
"@react-native-async-storage/async-storage": "^2.2.0",
"@react-native-community/datetimepicker": "^8.5.1",
"@react-navigation/bottom-tabs": "^7.4.0",
"@react-navigation/elements": "^2.6.3",
"@react-navigation/native": "^7.1.8",
"@react-navigation/native-stack": "^7.8.0",
"@react-navigation/stack": "^7.1.1",
"@shopify/react-native-skia": "2.2.12",
"@tanstack/react-query": "^5.66.0",
"dotenv": "^17.2.3",
"expo": "~54.0.25",
"expo-asset": "^12.0.10",
"expo-blur": "^15.0.8",
"expo-constants": "~18.0.10",
"expo-file-system": "^19.0.19",
"expo-font": "~14.0.9",
"expo-haptics": "~15.0.7",
"expo-image": "~3.0.10",
"expo-image-picker": "^17.0.10",
"expo-linking": "~8.0.9",
"expo-modules-core": "^3.0.26",
"expo-router": "~6.0.15",
Expand All @@ -46,11 +50,16 @@
"react-dom": "19.1.0",
"react-native": "0.81.5",
"react-native-css-interop": "^0.2.1",
"react-native-element-dropdown": "^2.12.4",
"react-native-gesture-handler": "~2.28.0",
"react-native-image-picker": "^8.2.1",
"react-native-popover-view": "^6.1.0",
"react-native-reanimated": "~4.1.5",
"react-native-safe-area-context": "~5.4.0",
"react-native-screens": "~4.16.0",
"react-native-svg": "^15.15.0",
"react-native-toast-message": "^2.3.3",
"react-native-tooltips": "^1.0.3",
"react-native-web": "~0.21.0",
"react-native-webview": "^13.16.0",
"react-native-worklets": "0.5.1",
Expand Down
2 changes: 2 additions & 0 deletions apps/native/src/apis/controller/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './postGetPreSignedUrl';

23 changes: 23 additions & 0 deletions apps/native/src/apis/controller/common/postGetPreSignedUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useMutation } from '@tanstack/react-query';
import { client } from '@/apis/client';
import { paths } from '@/types/api/schema';

type GetPreSignedUrlRequest =
paths['/api/common/upload-file']['post']['requestBody']['content']['application/json'];
type GetPreSignedUrlResponse =
paths['/api/common/upload-file']['post']['responses']['200']['content']['*/*'];

/**
* 파일 업로드를 위한 Pre-signed URL 요청
* @description AWS S3 업로드를 위한 pre-signed URL을 받아옵니다.
*/
export const useGetPreSignedUrl = () => {
return useMutation({
mutationFn: async (request: GetPreSignedUrlRequest): Promise<GetPreSignedUrlResponse> => {
const { data } = await client.POST('/api/common/upload-file', {
body: request,
});
return data as GetPreSignedUrlResponse;
},
});
};
2 changes: 2 additions & 0 deletions apps/native/src/apis/controller/qna/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './useGetQnaImages';
export * from './useGetQnaAllImages';
8 changes: 8 additions & 0 deletions apps/native/src/apis/controller/qna/useGetQnaAllImages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { TanstackQueryClient } from '@apis';

/**
* Q&A 내가 참여한 모든 이미지 조회 (최신순)
*/
export const useGetQnaAllImages = () => {
return TanstackQueryClient.useQuery('get', '/api/student/qna/images');
};
21 changes: 21 additions & 0 deletions apps/native/src/apis/controller/qna/useGetQnaImages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { TanstackQueryClient } from '@apis';

/**
* Q&A 전체 이미지 조회 (질문 + 채팅)
* @param qnaId - Q&A ID
* @param enabled - 쿼리 활성화 여부
*/
export const useGetQnaImages = (qnaId: number, enabled = true) => {
return TanstackQueryClient.useQuery(
'get',
'/api/student/qna/{qnaId}/images',
{
params: {
path: { qnaId },
},
},
{
enabled,
}
);
};
15 changes: 15 additions & 0 deletions apps/native/src/apis/controller/scrap/deleteEmptyTrash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { client } from '@/apis/client';

export const useEmptyTrash = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async (): Promise<void> => {
await client.DELETE('/api/student/scrap/trash/all');
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['scrap', 'trash'] });
},
});
};
83 changes: 83 additions & 0 deletions apps/native/src/apis/controller/scrap/deleteFolders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { client } from '@/apis/client';
import { paths } from '@/types/api/schema';
import type { ScrapSearchResponse } from '@/features/student/scrap/utils/types';
import {
createSearchQueryFilters,
rollbackOptimisticUpdate,
invalidateScrapSearchQueries,
SCRAP_QUERY_KEYS,
} from './utils';

type DeleteFoldersRequest =
paths['/api/student/scrap/folder']['delete']['requestBody']['content']['application/json'];

export const useDeleteFolders = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async (request: DeleteFoldersRequest): Promise<void> => {
await client.DELETE('/api/student/scrap/folder', {
body: request,
});
},
// 낙관적 업데이트: 삭제 전 데이터 백업 및 즉시 UI 업데이트
onMutate: async (request) => {
const deletedFolderIds = new Set(request);

// 폴더 목록 쿼리 취소 및 백업
const folderQueryKey = SCRAP_QUERY_KEYS.folderList();
await queryClient.cancelQueries({ queryKey: folderQueryKey });
const previousFolders = queryClient.getQueryData(folderQueryKey);

// 검색 쿼리 취소 및 백업
const searchQueryFilters = createSearchQueryFilters();
await queryClient.cancelQueries(searchQueryFilters);
const previousQueries = queryClient.getQueriesData(searchQueryFilters);

// 낙관적 업데이트: 폴더 목록에서 삭제된 폴더 제거
queryClient.setQueryData(folderQueryKey, (old: any) => {
if (!old?.data) return old;
return {
...old,
data: old.data.filter((folder: any) => !deletedFolderIds.has(folder.id)),
};
});

// 낙관적 업데이트: 검색 결과에서 삭제된 폴더 제거
queryClient.setQueriesData<ScrapSearchResponse>(searchQueryFilters, (old) => {
if (!old) return old;
return {
folders: old.folders?.filter((folder) => !deletedFolderIds.has(folder.id)),
scraps: old.scraps,
};
});

// 롤백을 위한 이전 데이터 반환
return { previousFolders, previousQueries };
},
// 에러 발생 시 롤백
onError: (error, request, context) => {
if (context?.previousFolders) {
const folderQueryKey = SCRAP_QUERY_KEYS.folderList();
queryClient.setQueryData(folderQueryKey, context.previousFolders);
}
if (context?.previousQueries) {
rollbackOptimisticUpdate(queryClient, context.previousQueries);
}
},
// 성공/실패 관계없이 쿼리 무효화 (백그라운드에서 최신 데이터 가져오기)
onSettled: () => {
// 폴더 목록 갱신
queryClient.invalidateQueries({
queryKey: SCRAP_QUERY_KEYS.folderList(),
});
// 검색 결과 갱신
invalidateScrapSearchQueries(queryClient);
// 휴지통 목록 갱신
queryClient.invalidateQueries({
queryKey: SCRAP_QUERY_KEYS.trashList(),
});
},
});
};
25 changes: 25 additions & 0 deletions apps/native/src/apis/controller/scrap/deletePermanentTrash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { client } from '@/apis/client';
import { paths } from '@/types/api/schema';
import { SCRAP_QUERY_KEYS } from './utils';

type PermanentDeleteRequest =
paths['/api/student/scrap/trash']['delete']['requestBody']['content']['application/json'];

export const usePermanentDeleteTrash = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async (request: PermanentDeleteRequest): Promise<void> => {
await client.DELETE('/api/student/scrap/trash', {
body: request,
});
},
onSuccess: () => {
// 휴지통 목록 갱신
queryClient.invalidateQueries({
queryKey: SCRAP_QUERY_KEYS.trashList(),
});
},
});
};
54 changes: 54 additions & 0 deletions apps/native/src/apis/controller/scrap/deleteScrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { client } from '@/apis/client';
import { paths } from '@/types/api/schema';
import {
optimisticDeleteScrap,
rollbackOptimisticUpdate,
invalidateScrapSearchQueries,
invalidateFolderScrapsQueries,
SCRAP_QUERY_KEYS,
} from './utils';

type DeleteScrapRequest =
paths['/api/student/scrap']['delete']['requestBody']['content']['application/json'];

export const useDeleteScrap = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async (
request: DeleteScrapRequest
): Promise<{ success: boolean; request: DeleteScrapRequest }> => {
await client.DELETE('/api/student/scrap', {
body: request,
});

return { success: true, request };
},
// 낙관적 업데이트: 삭제 전 데이터 백업 및 즉시 UI 업데이트
onMutate: async (request) => {
return await optimisticDeleteScrap(queryClient, request.items);
},
// 에러 발생 시 롤백
onError: (error, request, context) => {
if (context?.previousQueries) {
rollbackOptimisticUpdate(queryClient, context.previousQueries);
}
},
// 성공/실패 관계없이 쿼리 무효화 (백그라운드에서 최신 데이터 가져오기)
onSettled: () => {
// 폴더 목록 갱신
queryClient.invalidateQueries({
queryKey: SCRAP_QUERY_KEYS.folderList(),
});
// 폴더 내 스크랩 목록 갱신
invalidateFolderScrapsQueries(queryClient);
// 검색 결과 갱신
invalidateScrapSearchQueries(queryClient);
// 휴지통 목록 갱신
queryClient.invalidateQueries({
queryKey: SCRAP_QUERY_KEYS.trashList(),
});
},
});
};
27 changes: 27 additions & 0 deletions apps/native/src/apis/controller/scrap/deleteUnscrapFromPointing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { client } from '@/apis/client';
import { paths } from '@/types/api/schema';
import { invalidateTrashMutationQueries } from './utils';

type UnscrapFromPointingRequest =
paths['/api/student/scrap/from-pointing']['delete']['requestBody']['content']['application/json'];

/**
* 포인팅에서 스크랩 취소 (다른 포인팅이 없으면 휴지통 처리)
* @description 포인팅 기반 스크랩을 취소하고, 다른 포인팅이 없으면 휴지통으로 이동합니다.
*/
export const useUnscrapFromPointing = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async (request: UnscrapFromPointingRequest): Promise<void> => {
await client.DELETE('/api/student/scrap/from-pointing', {
body: request,
});
},
onSuccess: () => {
// 휴지통 및 검색 쿼리 갱신
invalidateTrashMutationQueries(queryClient);
},
});
};
27 changes: 27 additions & 0 deletions apps/native/src/apis/controller/scrap/deleteUnscrapFromProblem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { client } from '@/apis/client';
import { paths } from '@/types/api/schema';
import { invalidateTrashMutationQueries } from './utils';

type UnscrapFromProblemRequest =
paths['/api/student/scrap/from-problem']['delete']['requestBody']['content']['application/json'];

/**
* 문제에서 스크랩 취소 (휴지통 처리)
* @description 문제 기반 스크랩을 취소하고 휴지통으로 이동합니다.
*/
export const useUnscrapFromProblem = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async (request: UnscrapFromProblemRequest): Promise<void> => {
await client.DELETE('/api/student/scrap/from-problem', {
body: request,
});
},
onSuccess: () => {
// 휴지통 및 검색 쿼리 갱신
invalidateTrashMutationQueries(queryClient);
},
});
};
Loading