Conversation
* feat(Social): 소셜 탭 UI 추가 * feat(Subscribe): 나의 픽, 나의 팬덤 목록 API 연동 * feat(Social): 목록 개수 추가 및 데이터 refetch 추가 * feat(Social): 친구 검색 및 상세 조회 페이지 추가 및 API연동 * feat: 프로필이미지 안뜨는 이슈 해결 * refactor: 컴포넌트 위치 변경 * refactor: 컴포넌트 위치 변경 * feat(SocialMyCollectionDiary): 소셜 내 컬렉션 페이지 다이어리 보기 기능 추가 * feat(isMyPick): 내 픽인지 아닌지 분기처리 추가 * feat(SocialView): isMyPick DTO 업데이트 반영 * feat(SocialMyCollectionVeiw): 구독 추가/취소 API 연동 * fix(SocialView): 데이터 refetch 이슈 수정 * refactor(SocialView): 소셜페이지 리팩토링 * feat(FeedSection): 피드 섹션 추가 및 폴더구조 변경 * Refactor(SocialMyCollection): 폴더 위치 변경 * fix(SocialFeedSection): UI 재구성 및 업데이트 * feat(SocialFeed): 영상 반복재생 추가 * feat(SocialFeed): 실시간 위치 UI 업데이트 * fix(SocialFeed): 다음 음악 재생 이슈 해결 * fix(FeedSection): 재생구간 하단에 고정 배치 * fix(DiaryService): 다이어리 api 잘못 동기화 된 부분 해결 * feat(FeedView): like, store api 세팅 및 연동 * feat(FeedView): 좋아요 및 킬링파트 일기 여백 조정 등 UI 업데이트 * fix(SocialFeed): 음악 피드 재진입 시 조건 추가 * fix(FeedSectoion): 다이어리 재생 부분 이슈 개선 * feat(1.1.0): 버전 업데이트
There was a problem hiding this comment.
Pull request overview
소셜 탭(친구/피드) 화면과 관련 데이터 로딩(구독 목록, 유저 검색, 피드 조회/상호작용)을 추가하고, URL/플레이어 동작을 보강하여 TestFlight 배포용 업데이트를 준비하는 PR입니다.
Changes:
- 메인 탭에 소셜 탭을 추가하고, 소셜(친구/피드) UI 구성 요소 및 상세 화면(컬렉션/다이어리)을 신규 구현
- 소셜 데이터 로딩을 위한 ViewModel/Service(구독, 유저 검색, 유저 피드, 좋아요/보관 토글) 추가 및 모델 확장
- 프로필 이미지 URL 정규화 유틸/테스트 추가, YouTube 플레이어에 재생 종료 콜백 및 루프 제어 추가, 빌드 버전/마케팅 버전 갱신
Reviewed changes
Copilot reviewed 38 out of 38 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| KillingPartTests/ProfileImageURLResolverTests.swift | 프로필 이미지 URL 정규화 헬퍼 테스트 추가 |
| KillingPart/Views/Screens/Main/Social/SocialView.swift | 소셜 루트 화면(상단 탭 전환 + 초기 로딩 트리거) 추가 |
| KillingPart/Views/Screens/Main/Social/Shared/Components/SocialTopToggleTabsView.swift | 소셜 상단 세그먼트 탭 UI 추가 |
| KillingPart/Views/Screens/Main/Social/Shared/Components/SocialTabs.swift | 소셜 탭/섹션 enum 정의 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/SocialMyCollection/[userId]/SocialMyCollectionView.swift | 친구 컬렉션(프로필 카드 + 피드 그리드) 화면 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/SocialMyCollection/[userId]/components/SocialMyCollectionProfileCard.swift | 친구 컬렉션 프로필 카드 UI 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/SocialMyCollection/[userId]/components/SocialMyCollectionPickToggleButton.swift | “나의 픽” 토글 버튼 UI 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/SocialMyCollection/[userId]/components/SocialMyCollectionFeedCard.swift | 친구 컬렉션 피드 카드 UI 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/SocialMyCollection/[userId]/[diaryId]/SocialMyCollectionDiary.swift | 친구 피드 상세(다이어리) 화면 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/FriendsSectionView.swift | 친구 섹션(검색/토글/리스트) 컨테이너 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/Components/SocialSearchedResultHeaderView.swift | 검색 결과 헤더 UI 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/Components/SocialSearchBarView.swift | 친구 검색바 UI 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/Components/SocialFriendToggleTabsView.swift | “나의 픽/팬덤” 토글 탭 UI 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/Components/SocialFriendRowView.swift | 친구 리스트 row UI 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/Components/SocialFriendProfileImageView.swift | 친구 프로필 이미지(AsyncImage) 뷰 추가 |
| KillingPart/Views/Screens/Main/Social/FriendsSection/Components/SocialFriendListView.swift | 친구 리스트(로딩/에러/페이지네이션/내비게이션) 추가 |
| KillingPart/Views/Screens/Main/Social/FeedSection/FeedSectionView.swift | 피드 섹션(TabView 페이징 + 자동 넘김) 화면 추가 |
| KillingPart/Views/Screens/Main/Social/FeedSection/Components/SocialFeedProfileView.swift | 피드 카드 내 유저 프로필 영역 추가 |
| KillingPart/Views/Screens/Main/Social/FeedSection/Components/SocialFeedPlaybackRangeBar.swift | 재생 구간 바(진행/라벨) UI 추가 |
| KillingPart/Views/Screens/Main/Social/FeedSection/Components/SocialFeedPageCardView.swift | 피드 페이지 카드(좋아요/보관/플레이어/본문) 추가 |
| KillingPart/Views/Screens/Main/Social/FeedSection/Components/SocialFeedAlbumImageView.swift | 앨범 이미지 AsyncImage 래퍼 추가 |
| KillingPart/Views/Screens/Main/My/MyCollection/Components/ProfileCard/MyCollectionProfileCard.swift | 내 컬렉션 프로필 카드 통계 표시 복구 |
| KillingPart/Views/Screens/Main/My/MyCollection/Components/FeedCard/MyCollectionFeedCard.swift | 내 피드 카드 좋아요 배지 표시 복구 |
| KillingPart/Views/Screens/Main/MainTabView.swift | 메인 탭에 소셜 탭 추가 |
| KillingPart/Views/Screens/Main/Add/AddSearchDetail/components/YoutubePlayerView.swift | 플레이어 루프 옵션/종료 콜백 및 JS 브릿지 추가 |
| KillingPart/Views/Screens/Auth/LoginView.swift | 테스터 로그인 버튼 주석 처리 변경 |
| KillingPart/ViewModels/Social/SocialViewModel.swift | 친구/팬덤/검색 목록 로딩 + 구독 토글/페이지네이션 ViewModel 추가 |
| KillingPart/ViewModels/Social/SocialMyCollectionViewModel.swift | 친구 컬렉션(유저 통계/유저 피드/픽 토글) ViewModel 추가 |
| KillingPart/ViewModels/Social/FeedViewModel.swift | 내 피드 로딩 + 좋아요/보관 토글 ViewModel 추가 |
| KillingPart/ViewModels/My/MyCollection/MyCollectionViewModel.swift | 내 피드 조회 API 변경(fetchMyDiaries) 반영 |
| KillingPart/Services/UserService.swift | 유저 검색 API 추가 + 유저 응답 DTO 매핑으로 전환 |
| KillingPart/Services/SubscribeService.swift | 구독(팬/구독) 목록 조회 및 구독/해제 Service 추가 |
| KillingPart/Services/DiaryService.swift | 내 피드/내 다이어리 분리, 유저 피드/좋아요/보관 토글 API 추가 |
| KillingPart/Models/UserModel.swift | UserModel Identifiable 전환 + DTO/검색 응답 모델 추가 |
| KillingPart/Models/SubscribeModel.swift | 구독 사용자/페이지/응답 모델 추가 |
| KillingPart/Models/ProfileImageURLResolver.swift | 프로필 이미지 URL 정규화 유틸 추가 |
| KillingPart/Models/DiaryModel.swift | 좋아요/보관 토글 응답 + 유저 피드 DTO/매핑 + interaction replace 헬퍼 추가 |
| KillingPart.xcodeproj/project.pbxproj | 빌드/마케팅 버전 증가 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| .onChange(of: searchText) { query in | ||
| Task { | ||
| await viewModel.searchUsers(with: query) | ||
| } | ||
| } |
There was a problem hiding this comment.
onChange(of: searchText) triggers a new Task on every keystroke, which can easily flood the network and worsen the out-of-order response issue. Consider debouncing (e.g., with a short delay + cancellation) or only firing when the user commits the search.
| MACOSX_DEPLOYMENT_TARGET = 14.0; | ||
| MARKETING_VERSION = 1.0.24; | ||
| MARKETING_VERSION = 1.0.25; | ||
| PRODUCT_BUNDLE_IDENTIFIER = com.killingpoint.killingpart; |
There was a problem hiding this comment.
PR title suggests a 1.1.0 release, but MARKETING_VERSION is set to 1.0.25 here. If this build is intended to be 1.1.0, align MARKETING_VERSION (and/or PR title/description) to avoid release/versioning confusion.
| @State private var currentPageIndex = 0 | ||
| @State private var elapsedInCurrentRange: TimeInterval = 0 | ||
| @State private var isViewActive = false | ||
| @State private var previousFeedCount = 0 | ||
|
|
There was a problem hiding this comment.
previousFeedCount is declared/updated but never used, which adds unnecessary state and updates. Remove this state (and the related assignments) or use it for the intended behavior.
| .onChange(of: viewModel.feeds.count) { newCount in | ||
| previousFeedCount = newCount | ||
| } |
There was a problem hiding this comment.
This .onChange(of: viewModel.feeds.count) only assigns to previousFeedCount, but that value is never read. If previousFeedCount is removed, this .onChange should be removed too.
| enum SocialTopTab: CaseIterable { | ||
| case feed | ||
| case friend | ||
|
|
There was a problem hiding this comment.
SocialTopTab is used as a Picker selection and with ForEach(..., id: \.self), which requires Hashable (and .task(id:) requires Equatable). Add Hashable/Equatable conformance (e.g., enum SocialTopTab: CaseIterable, Hashable).
| enum SocialFriendSection: CaseIterable { | ||
| case myPick | ||
| case myFandom | ||
|
|
There was a problem hiding this comment.
SocialFriendSection is iterated via ForEach(SocialFriendSection.allCases, id: \.self), which requires the element to be Hashable. Add Hashable conformance (e.g., enum SocialFriendSection: CaseIterable, Hashable).
| func searchUsers(with query: String) async { | ||
| currentSearchQuery = query.trimmingCharacters(in: .whitespacesAndNewlines) | ||
| guard !currentSearchQuery.isEmpty else { | ||
| clearSearchState() | ||
| if !hasLoadedInitialData { |
There was a problem hiding this comment.
searchUsers(with:) can be invoked repeatedly while earlier requests are still in flight. Because it awaits network work, an older search can complete later and overwrite state with stale results. Capture the trimmed query locally and only apply the response if it still matches currentSearchQuery (or cancel/ignore previous searches).
| .onAppear { | ||
| imageReloadKey = UUID() | ||
| } |
There was a problem hiding this comment.
Resetting imageReloadKey in onAppear forces AsyncImage to refetch whenever a row scrolls into view (even if the URL hasn't changed), causing unnecessary network usage/flicker. Consider removing the onAppear reset and only reloading when profileImageURL actually changes.
| .onAppear { | |
| imageReloadKey = UUID() | |
| } |
| // Button(action: viewModel.loginWithTester) { | ||
| // ZStack { | ||
| // Text("체험하기(Demo)") | ||
| // .font(AppFont.paperlogy5Medium(size: 14)) | ||
| // .opacity( |
There was a problem hiding this comment.
The demo/tester login button is left as a large commented-out block. Prefer removing it entirely or gating it behind a build configuration/feature flag (e.g., #if DEBUG) to keep LoginView maintainable and avoid accidentally shipping dead code.
| .padding(.bottom, bottomInset) | ||
| } | ||
| .frame(maxWidth: .infinity, maxHeight: .infinity) | ||
| .toolbar(.hidden, for: .navigationBar) | ||
| .padding(.bottom, bottomInset) |
There was a problem hiding this comment.
bottomInset padding is applied twice: once on the VStack and again on the outer container. This stacks the spacing. Consider keeping the bottom inset in a single place to avoid double-padding issues on devices with large safe-area insets.
업데이트 내용