Skip to content

Conversation

@JinUng41
Copy link
Collaborator

@JinUng41 JinUng41 commented May 23, 2025

👻 PULL REQUEST

📄 작업 내용

  • 프로필 화면 프로그레스 바에 적용되는 값을 수정하였습니다.
  • 프로필 화면 글/댓글에 대하여 좋아요, 투명도 내리기, 삭제하기, 신고하기, 밴하기 등을 적용하였습니다.
  • 댓글 셀이 보이지 않던 이슈를 해결하였습니다.
  • 프로필 인포를 터치 시, 상세 페이지로 이동하던 문제를 해결하였습니다.
  • 뷰잇 밴 후 동작을 수정하였습니다.
  • 네비게이션 팝 동작을 위한 코드를 수정하였습니다.
  • 뷰잇에서 프로필 터치 시, 프로필 화면으로 이동하는 로직을 추가하였습니다.

📚 참고자료

  • 저답지 않게 뇌를 많이 빼고 작성하였습니다. 코드를 보시다 보면 중복 코드가 많아요.

🔗 연결된 이슈

Summary by CodeRabbit

  • New Features

    • Added async/await support for content, comment, like, report, ban, and ghost actions, enabling smoother and more responsive user interactions.
    • Introduced profile badge icons and improved progress bar animation in profile UI.
    • Added contextual action sheets for reporting, banning, and ghosting content/comments, with admin-specific options.
    • Enabled profile navigation from Viewit list, allowing users to view other profiles directly.
  • Improvements

    • Enhanced pagination and event handling in profile and list views with debounced triggers for better performance.
    • Streamlined UI event handling using Combine and async/await, reducing complexity and improving reliability.
    • Improved error handling and user feedback for reporting and moderation actions.
    • Updated segmented controls and button handlers for more intuitive interaction.
  • Bug Fixes

    • Corrected date formatting in comment displays for accurate timestamps.
  • Refactor

    • Simplified and modularized view models and repositories, replacing use cases with direct repository calls.
    • Centralized and unified error handling across profile and moderation actions.
    • Improved code readability and maintainability in UI components and view controllers.
  • Style

    • Updated UI layouts in profile cells for better alignment and clarity.
  • Chores

    • Reorganized project structure for better file grouping and clarity.

@JinUng41 JinUng41 requested a review from youz2me May 23, 2025 18:15
@JinUng41 JinUng41 self-assigned this May 23, 2025
@JinUng41 JinUng41 added the 🛠️ fix 기능적 버그나 오류 해결 시 사용 label May 23, 2025
@JinUng41 JinUng41 added the 🍻 진웅 술 한잔 가온나~ label May 23, 2025
@coderabbitai
Copy link

coderabbitai bot commented May 23, 2025

Caution

Review failed

The pull request is closed.

## Walkthrough

This update introduces extensive improvements and refactoring to the profile feature across the codebase. It adds async/await variants to repository interfaces and implementations, enhances the profile UI, introduces new moderation actions (report, ban, ghost), refactors view models for modularity and clarity, and updates navigation and gesture handling. Project structure and dependency injection are also reorganized.

## Changes

| Files/Groupings                                                                                      | Change Summary |
|------------------------------------------------------------------------------------------------------|---------------|
| Wable-iOS.xcodeproj/project.pbxproj                                                                  | Renamed `ProfileSegmentKind.swift` to `ProfileSegment.swift` and moved `ProfileEditViewController.swift` into a new `Edit` group under `Profile`. |
| Wable-iOS/App/AppDelegate+InjectDependency.swift                                                     | Registered `GhostRepository` in dependency injection container. |
| Wable-iOS/Data/Mapper/CommentMapper.swift                                                            | Fixed date format strings for seconds in comment mapping methods. |
| Wable-iOS/Data/RepositoryImpl/CommentRepositoryImpl.swift<br>Wable-iOS/Domain/RepositoryInterface/CommentRepository.swift | Added async/await `deleteComment` method to repository and protocol; updated mock implementation. |
| Wable-iOS/Data/RepositoryImpl/ContentLikedRepositoryImpl.swift<br>Wable-iOS/Domain/RepositoryInterface/ContentLikedRepository.swift | Added async/await `createContentLiked` and `deleteContentLiked` methods to repository and protocol. |
| Wable-iOS/Data/RepositoryImpl/ContentRepositoryImpl.swift<br>Wable-iOS/Domain/RepositoryInterface/ContentRepository.swift | Added async/await `deleteContent` method to repository and protocol. |
| Wable-iOS/Data/RepositoryImpl/GhostRepositoryImpl.swift<br>Wable-iOS/Domain/RepositoryInterface/GhostRepository.swift | Added async/await `postGhostReduction` method to repository and protocol. |
| Wable-iOS/Data/RepositoryImpl/ReportRepositoryImpl.swift<br>Wable-iOS/Domain/RepositoryInterface/ReportRepository.swift | Added async/await `createReport` and `createBan` methods to repository, protocol, and mocks. |
| Wable-iOS/Presentation/Home/View/HomeViewController.swift                                            | Changed injected use cases for profile view model; updated cell registration closure formatting. |
| Wable-iOS/Presentation/Overview/Detail/View/AnnouncementDetailViewController.swift<br>Wable-iOS/Presentation/Profile/AlarmSetting/View/AlarmSettingViewController.swift | Removed gesture recognizer delegate conformance and related methods; simplified gesture recognizer setup. |
| Wable-iOS/Presentation/Profile/Component/ProfileInfoCell.swift                                       | Added level badge image view; adjusted layout constraints; fixed progress bar scaling and animation; streamlined edit button action handler. |
| Wable-iOS/Presentation/Profile/Component/ProfileSegmentedHeaderView.swift                            | Replaced target-action with Combine-based reactive handling for segmented control changes; renamed closure property. |
| Wable-iOS/Presentation/Profile/My/View/MyProfileViewController.swift                                 | Refactored to use Combine for willDisplay events; added delete confirmation action sheets; updated cell handlers and pagination logic. |
| Wable-iOS/Presentation/Profile/My/ViewModel/MyProfileViewModel.swift                                 | Refactored to use repositories directly; removed Combine subjects; added async/await deletion and like toggle methods; centralized error handling. |
| Wable-iOS/Presentation/Profile/Other/View/OtherProfileViewController.swift                           | Added Combine-based pagination; added contextual action sheets for report, ban, and ghost; improved cell handlers and UI feedback for reports. |
| Wable-iOS/Presentation/Profile/Other/ViewModel/OtherProfileViewModel.swift                           | Refactored to use repositories; removed Combine; added moderation and like toggle methods; centralized error handling; added completion flags. |
| Wable-iOS/Presentation/TabBar/TabBarController.swift                                                 | Updated dependency injection for profile and viewit controllers; disabled animation on tab setup. |
| Wable-iOS/Presentation/Viewit/List/View/ViewitListViewController.swift                               | Added profile tap relay; implemented explicit navigation logic for profile taps; added navigation helper methods. |
| Wable-iOS/Presentation/Viewit/List/ViewModel/ViewitListViewModel.swift                               | Added user session awareness; added profile tap logic; simplified list update after banning actions. |
| Wable-iOS/Presentation/WableComponent/Cell/CommentCollectionViewCell.swift                           | Moved guard statement for `createdDate` to after more UI setup in `configureCell`. |
| Wable-iOS/Presentation/Profile/Model/ProfileEmptyCellItem.swift                                      | Changed `segment` property type from `ProfileSegmentKind` to `ProfileSegment`. |
| Wable-iOS/Presentation/Profile/Model/ProfileSegment.swift                                            | Renamed enum from `ProfileSegmentKind` to `ProfileSegment`. |
| Wable-iOS/Presentation/Profile/Model/ProfileViewItem.swift                                          | Changed `currentSegment` property type from `ProfileSegmentKind` to `ProfileSegment`. |
| Wable-iOS/Presentation/Profile/My/View/Cell/MyProfileEmptyCell.swift                                | Updated `configure` method parameter type from `ProfileSegmentKind` to `ProfileSegment`. |
| Wable-iOS/Presentation/Profile/Other/View/Cell/OtherProfileEmptyCell.swift                          | Updated `configure` method parameter type from `ProfileSegmentKind` to `ProfileSegment`. |
| Wable-iOS/Resource/Assets.xcassets/Icon/ic_level_badge.imageset/Contents.json                        | Added new asset catalog JSON for level badge icon with multiple scale variants. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
actor User
participant MyProfileViewController
participant MyProfileViewModel
participant ContentRepository
participant CommentRepository

User->>MyProfileViewController: Tap like/delete on content/comment
MyProfileViewController->>MyProfileViewModel: Call toggleLike/delete method
MyProfileViewModel->>ContentRepository: (async) Toggle like/delete content
MyProfileViewModel->>CommentRepository: (async) Toggle like/delete comment
ContentRepository-->>MyProfileViewModel: Result/Error
CommentRepository-->>MyProfileViewModel: Result/Error
MyProfileViewModel-->>MyProfileViewController: Update UI state or error message
sequenceDiagram
actor User
participant OtherProfileViewController
participant OtherProfileViewModel
participant ReportRepository
participant GhostRepository

User->>OtherProfileViewController: Tap report/ban/ghost on content/comment
OtherProfileViewController->>OtherProfileViewModel: Call report/ban/ghost method
OtherProfileViewModel->>ReportRepository: (async) Report/Ban content/comment
OtherProfileViewModel->>GhostRepository: (async) Ghost content/comment
ReportRepository-->>OtherProfileViewModel: Result/Error
GhostRepository-->>OtherProfileViewModel: Result/Error
OtherProfileViewModel-->>OtherProfileViewController: Update UI state or show toast
Loading

Assessment against linked issues

Objective (Issue #196) Addressed Explanation
프로필 화면 프로그레스 바 값 수정
프로필 글, 댓글 좋아요 기능 추가
댓글 셀 안보이는 이슈 해결
미트볼 버튼 관련 동작 추가
프로필 인포 터치 시 글 상세페이지로 이동하는 문제 해결
뷰잇 밴 후 다시 새로 받도록 수정
뒤로가기 제스처 코드 수정
뷰잇에서 프로필 터치 시 프로필 화면 이동 로직 추가
다른 사람 프로필인 경우, 투명도 내리기 기능 추가

Possibly related PRs

  • #190: Also reorganizes the Xcode project file and grouping of profile-related files, including ProfileEditViewController.swift.
  • #193: Modifies project file groupings under Profile, similar to the structural changes here.
  • #164: Related restructuring of profile and home features source files and project organization.

Poem

🐇✨
In the garden of code, the profile grew,
With badges and likes—something new!
Async flows, a gentle breeze,
Moderation tools with utmost ease.
Rabbits cheer as bugs are gone,
The profile's brighter—hop along!
🥕


<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

<!-- announcements_end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between a949f3ee1f7fe5be383f207e85ac28dbc950a569 and 817022e5d7b4eabbf3bd8cce0de081c2056f2c57.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `Wable-iOS/Presentation/Profile/Component/ProfileInfoCell.swift` (5 hunks)

</details>

</details>
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Team-Wable/WABLE-iOS&utm_content=197):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (10)
Wable-iOS/App/AppDelegate+InjectDependency.swift (1)

67-69: Consider following the established dependency injection pattern.

The GhostRepository registration doesn't follow the same pattern as other repositories in this file. Most repositories use a switch statement on the config parameter to provide mock implementations for debug builds.

Consider updating to match the established pattern:

 diContainer.register(for: GhostRepository.self) { config in
-    return GhostRepositoryImpl()
+    switch config {
+    case .debug:
+        return MockGhostRepository()  // if available
+    case .release:
+        return GhostRepositoryImpl()
+    }
 }

Or document why GhostRepository doesn't need a mock implementation if that's intentional.

Wable-iOS/Data/RepositoryImpl/GhostRepositoryImpl.swift (1)

39-60: Implementation is correct but consider reducing code duplication.

The async implementation follows the correct pattern and properly handles errors. However, there's significant duplication with the Combine version.

Consider extracting the request construction into a private helper method:

+    private func createGhostReductionRequest(
+        alarmTriggerType: String,
+        alarmTriggerID: Int,
+        targetMemberID: Int,
+        reason: String
+    ) -> DTO.Request.UpdateGhost {
+        return DTO.Request.UpdateGhost(
+            alarmTriggerType: alarmTriggerType,
+            targetMemberID: targetMemberID,
+            alarmTriggerID: alarmTriggerID,
+            ghostReason: reason
+        )
+    }

Then use it in both methods to reduce duplication.

Wable-iOS/Data/RepositoryImpl/ContentRepositoryImpl.swift (1)

46-55: Implementation is correct and follows established patterns.

The async deleteContent method properly implements the async/await pattern with appropriate error handling using ErrorMapper.map.

Similar to other repository implementations, consider extracting common logic to reduce duplication between the Combine and async versions:

+    private func performDeleteContent(contentID: Int) -> TargetType {
+        return .deleteContent(contentID: contentID)
+    }

This would help maintain consistency and reduce the risk of divergence between the two implementations.

Wable-iOS/Data/RepositoryImpl/ContentLikedRepositoryImpl.swift (1)

55-64: Consistent async implementation for deleteContentLiked.

The implementation follows the same pattern as the create method with proper error handling and mirroring of the Combine-based approach.

Consider adding documentation comments to indicate the relationship between the Combine and async variants, and establish a plan for eventual consolidation once the migration to async/await is complete to reduce maintenance overhead.

Wable-iOS/Data/RepositoryImpl/ReportRepositoryImpl.swift (1)

94-96: Improve mock repository async method implementations

The mock async methods are currently empty stubs. Consider adding proper mock behavior for better testing:

-    func createReport(nickname: String, text: String) async throws {
-        
-    }
+    func createReport(nickname: String, text: String) async throws {
+        try await Task.sleep(nanoseconds: UInt64(delaySeconds * 1_000_000_000))
+    }

-    func createBan(memberID: Int, triggerType: TriggerType.Ban, triggerID: Int) async throws {
-        
-    }
+    func createBan(memberID: Int, triggerType: TriggerType.Ban, triggerID: Int) async throws {
+        try await Task.sleep(nanoseconds: UInt64(delaySeconds * 1_000_000_000))
+    }

Also applies to: 104-106

Wable-iOS/Data/RepositoryImpl/CommentRepositoryImpl.swift (1)

118-120: Consider improving mock error handling

The mock implementation always throws WableError.unknownError, which might not be ideal for all testing scenarios. Consider making the mock behavior configurable:

-    func deleteComment(commentID: Int) async throws {
-        throw WableError.unknownError
-    }
+    func deleteComment(commentID: Int) async throws {
+        // Add configurable success/failure behavior for testing
+        try await Task.sleep(nanoseconds: 100_000_000) // 0.1 second delay
+    }
Wable-iOS/Presentation/Viewit/List/View/ViewitListViewController.swift (1)

299-316: Consider dependency injection for better testability.

Creating all dependencies inline makes the code harder to test and maintain. Consider injecting these dependencies or using a factory pattern.

-    func navigateToOtherProfile(for userID: Int) {
-        let otherProfileViewController = OtherProfileViewController(
-            viewModel: .init(
-                userID: userID,
-                fetchUserProfileUseCase: FetchUserProfileUseCaseImpl(),
-                checkUserRoleUseCase: CheckUserRoleUseCaseImpl(
-                    repository: UserSessionRepositoryImpl(
-                        userDefaults: UserDefaultsStorage(
-                            jsonEncoder: JSONEncoder(),
-                            jsonDecoder: JSONDecoder()
-                        )
-                    )
-                )
-            )
-        )
-        
-        navigationController?.pushViewController(otherProfileViewController, animated: true)
-    }
+    func navigateToOtherProfile(for userID: Int) {
+        let viewModel = viewModelFactory.makeOtherProfileViewModel(userID: userID)
+        let otherProfileViewController = OtherProfileViewController(viewModel: viewModel)
+        navigationController?.pushViewController(otherProfileViewController, animated: true)
+    }
Wable-iOS/Presentation/Profile/My/View/MyProfileViewController.swift (1)

485-503: Consider refactoring to reduce code duplication.

The delete methods for content and comments are nearly identical. Consider extracting a common method.

+    func presentDeleteActionSheet(
+        title: String,
+        message: String,
+        deleteAction: @escaping () -> Void
+    ) {
+        let actionSheet = WableSheetViewController(title: title, message: message)
+        let cancelAction = WableSheetAction(title: "취소", style: .gray)
+        let confirmAction = WableSheetAction(title: "삭제하기", style: .primary) { deleteAction() }
+        actionSheet.addActions(cancelAction, confirmAction)
+        present(actionSheet, animated: true)
+    }
+    
     func presentDeleteContentActionSheet(for contentID: Int) {
-        let actionSheet = WableSheetViewController(title: "게시글을 삭제하시겠어요?", message: "게시글이 영구히 삭제됩니다.")
-        let cancelAction = WableSheetAction(title: "취소", style: .gray)
-        let confirmAction = WableSheetAction(title: "삭제하기", style: .primary) { [weak self] in
-            self?.viewModel.deleteContent(for: contentID)
-        }
-        actionSheet.addActions(cancelAction, confirmAction)
-        present(actionSheet, animated: true)
+        presentDeleteActionSheet(
+            title: "게시글을 삭제하시겠어요?",
+            message: "게시글이 영구히 삭제됩니다.",
+            deleteAction: { [weak self] in self?.viewModel.deleteContent(for: contentID) }
+        )
     }
     
     func presentDeleteCommentActionSheet(for commentID: Int) {
-        let actionSheet = WableSheetViewController(title: "댓글을 삭제하시겠어요?", message: "댓글이 영구히 삭제됩니다.")
-        let cancelAction = WableSheetAction(title: "취소", style: .gray)
-        let confirmAction = WableSheetAction(title: "삭제하기", style: .primary) { [weak self] in
-            self?.viewModel.deleteComment(for: commentID)
-        }
-        actionSheet.addActions(cancelAction, confirmAction)
-        present(actionSheet, animated: true)
+        presentDeleteActionSheet(
+            title: "댓글을 삭제하시겠어요?",
+            message: "댓글이 영구히 삭제됩니다.",
+            deleteAction: { [weak self] in self?.viewModel.deleteComment(for: commentID) }
+        )
     }
Wable-iOS/Presentation/Viewit/List/ViewModel/ViewitListViewModel.swift (1)

161-172: Consider the performance impact of full list refresh.

Changing from partial updates to full list refresh after ban actions ensures data consistency but may impact performance with large lists. Monitor the user experience and consider implementing incremental updates if performance becomes an issue.

Wable-iOS/Presentation/Profile/Other/ViewModel/OtherProfileViewModel.swift (1)

191-223: Consider adding completion feedback for ghost actions

The ghost methods correctly implement the functionality but don't set any completion state. For consistency with report actions, consider setting isGhostCompleted = true after successful ghosting to provide user feedback.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 907bedb and 98b555c.

📒 Files selected for processing (26)
  • Wable-iOS.xcodeproj/project.pbxproj (2 hunks)
  • Wable-iOS/App/AppDelegate+InjectDependency.swift (1 hunks)
  • Wable-iOS/Data/Mapper/CommentMapper.swift (2 hunks)
  • Wable-iOS/Data/RepositoryImpl/CommentRepositoryImpl.swift (2 hunks)
  • Wable-iOS/Data/RepositoryImpl/ContentLikedRepositoryImpl.swift (2 hunks)
  • Wable-iOS/Data/RepositoryImpl/ContentRepositoryImpl.swift (1 hunks)
  • Wable-iOS/Data/RepositoryImpl/GhostRepositoryImpl.swift (1 hunks)
  • Wable-iOS/Data/RepositoryImpl/ReportRepositoryImpl.swift (3 hunks)
  • Wable-iOS/Domain/RepositoryInterface/CommentRepository.swift (1 hunks)
  • Wable-iOS/Domain/RepositoryInterface/ContentLikedRepository.swift (1 hunks)
  • Wable-iOS/Domain/RepositoryInterface/ContentRepository.swift (1 hunks)
  • Wable-iOS/Domain/RepositoryInterface/GhostRepository.swift (1 hunks)
  • Wable-iOS/Domain/RepositoryInterface/ReportRepository.swift (1 hunks)
  • Wable-iOS/Presentation/Home/View/HomeViewController.swift (2 hunks)
  • Wable-iOS/Presentation/Overview/Detail/View/AnnouncementDetailViewController.swift (2 hunks)
  • Wable-iOS/Presentation/Profile/AlarmSetting/View/AlarmSettingViewController.swift (1 hunks)
  • Wable-iOS/Presentation/Profile/Component/ProfileInfoCell.swift (5 hunks)
  • Wable-iOS/Presentation/Profile/Component/ProfileSegmentedHeaderView.swift (2 hunks)
  • Wable-iOS/Presentation/Profile/My/View/MyProfileViewController.swift (10 hunks)
  • Wable-iOS/Presentation/Profile/My/ViewModel/MyProfileViewModel.swift (5 hunks)
  • Wable-iOS/Presentation/Profile/Other/View/OtherProfileViewController.swift (11 hunks)
  • Wable-iOS/Presentation/Profile/Other/ViewModel/OtherProfileViewModel.swift (5 hunks)
  • Wable-iOS/Presentation/TabBar/TabBarController.swift (2 hunks)
  • Wable-iOS/Presentation/Viewit/List/View/ViewitListViewController.swift (5 hunks)
  • Wable-iOS/Presentation/Viewit/List/ViewModel/ViewitListViewModel.swift (5 hunks)
  • Wable-iOS/Presentation/WableComponent/Cell/CommentCollectionViewCell.swift (1 hunks)
👮 Files not reviewed due to content moderation or server errors (2)
  • Wable-iOS/Presentation/Profile/AlarmSetting/View/AlarmSettingViewController.swift
  • Wable-iOS/Presentation/Overview/Detail/View/AnnouncementDetailViewController.swift
🔇 Additional comments (71)
Wable-iOS.xcodeproj/project.pbxproj (2)

1668-1675: Correctly added new Edit PBXGroup for Profile editing
The new group Edit (DE4EFCCF2DE0060700E4146C) encapsulates ProfileEditViewController.swift as expected. Verify that the fileRef (DD51A44B2DD458A8004295B6) matches the actual file reference in the PBXFileReference section.


1734-1734: Included Edit subgroup under the Profile group
The Edit group is correctly inserted into the Profile PBXGroup’s children list between My and AlarmSetting, reflecting the intended project structure.

Wable-iOS/Presentation/Profile/AlarmSetting/View/AlarmSettingViewController.swift (3)

107-110: Verify unconditional gesture enabling doesn't break existing functionality.

The change simplifies gesture recognizer setup by removing the delegate-based conditional logic, but unconditionally enabling the interactive pop gesture might introduce unintended behavior. The previous implementation likely had logic to prevent the gesture in certain states.

Please verify that:

  1. This change maintains the same functional behavior as the previous delegate-based implementation
  2. The gesture won't interfere with user interactions (e.g., during form input or critical operations)
  3. There are no edge cases where the gesture should be disabled
#!/bin/bash
# Description: Search for any remaining gesture delegate usage or conditional gesture logic
# Test: Look for patterns that might indicate where conditional gesture logic was previously used

# Search for gesture recognizer delegate patterns
rg -A 3 -B 3 "UIGestureRecognizerDelegate|gestureRecognizerShouldBegin|interactivePopGestureRecognizer.*delegate"

# Search for conditional gesture enabling patterns
rg -A 3 -B 3 "interactivePopGestureRecognizer.*isEnabled.*="

# Look for navigation controller count checks (common pattern for conditional gesture enabling)
rg -A 3 -B 3 "navigationController.*viewControllers.*count"

109-109: Good simplification of gesture recognizer setup.

The direct enabling of the interactive pop gesture recognizer simplifies the code by removing the need for delegate conformance and conditional logic. This improves maintainability.

However, please verify that unconditionally enabling the gesture doesn't cause issues in edge cases where the navigation stack might have only one view controller.

#!/bin/bash
# Description: Search for any remaining gesture recognizer delegate implementations to ensure consistency
# Expected: No remaining UIGestureRecognizerDelegate conformance in view controllers that were simplified

echo "Checking for remaining UIGestureRecognizerDelegate conformance in the codebase..."
rg -A 3 "UIGestureRecognizerDelegate" --type swift

echo -e "\nChecking for gestureRecognizerShouldBegin implementations..."
rg -A 5 "gestureRecognizerShouldBegin" --type swift

109-109: Verify navigation stack behavior with unconditional gesture enabling.

The direct enabling of the interactive pop gesture recognizer removes the previous conditional logic that likely checked the navigation stack count. This could cause navigation issues if this view controller is ever presented as the root view controller, as users would be able to swipe back from the root, potentially causing unexpected behavior.

Please verify that this view controller is never presented as the root view controller in the navigation stack:

#!/bin/bash
# Description: Search for instantiation patterns of AlarmSettingViewController to verify it's never used as root

# Search for AlarmSettingViewController instantiation and navigation patterns
rg -A 5 -B 5 "AlarmSettingViewController" --type swift

# Look for direct presentation or setting as root view controller
ast-grep --pattern 'navigationController?.setViewControllers([$$$], animated: $_)'
ast-grep --pattern 'navigationController?.viewControllers = [$$$]'
Wable-iOS/Presentation/Overview/Detail/View/AnnouncementDetailViewController.swift (6)

58-58: Consistent navigation setup pattern - verify edge case handling.

Good addition of the setupNavigationBar() call to ensure proper navigation configuration during view setup.


98-101: Verify unconditional gesture enabling aligns with user experience requirements.

The implementation follows the same simplified pattern as AlarmSettingViewController.swift, which is good for consistency. However, the same concern applies here regarding unconditional gesture enabling.

Since this is an announcement detail view, users might be reading content and could accidentally trigger the gesture. Please ensure this doesn't negatively impact the reading experience.


58-58: Proper integration of navigation setup.

Good placement of the setupNavigationBar() call in viewDidLoad() following the standard view controller lifecycle pattern.


98-101: Consistent and clean navigation bar setup implementation.

The setupNavigationBar() method implementation is consistent with the pattern used in AlarmSettingViewController.swift. The method properly:

  • Hides the navigation bar for custom navigation UI
  • Enables the interactive pop gesture for intuitive navigation

This approach simplifies the gesture recognizer setup compared to the previous delegate-based conditional logic.


58-58: Good practice: Consistent navigation setup.

The addition of setupNavigationBar() call in viewDidLoad() maintains consistency with the navigation setup pattern used across the codebase.


98-101: Verify navigation stack behavior with unconditional gesture enabling.

Similar to the other view controller in this PR, the new setupNavigationBar() method unconditionally enables the interactive pop gesture recognizer. This removes previous safeguards that likely prevented swipe-back gestures when this view controller is the root view controller.

While the hidesBottomBarWhenPushed = true in the initializer suggests this is typically pushed onto a navigation stack, it's important to verify this view controller is never presented as the root to avoid navigation issues.

Please verify the usage patterns of this view controller:

#!/bin/bash
# Description: Search for AnnouncementDetailViewController instantiation and navigation patterns

# Search for instantiation and navigation patterns
rg -A 5 -B 5 "AnnouncementDetailViewController" --type swift

# Look for any direct presentation patterns that might make it a root view controller
ast-grep --pattern 'let $_ = AnnouncementDetailViewController()'
ast-grep --pattern 'AnnouncementDetailViewController().present($_)'
Wable-iOS/Domain/RepositoryInterface/ContentRepository.swift (1)

15-15: LGTM! Good API modernization approach.

Adding an async/await variant alongside the existing Combine method provides flexibility for consumers to choose their preferred concurrency model. The method signature correctly follows Swift async conventions.

Wable-iOS/Domain/RepositoryInterface/CommentRepository.swift (1)

17-17: LGTM! Consistent with async/await modernization pattern.

The async variant follows the same pattern established in other repositories, providing a modern alternative to the Combine-based API while maintaining backward compatibility.

Wable-iOS/Data/Mapper/CommentMapper.swift (2)

15-15: Critical bug fix for date parsing.

Good catch! The original format "yyyy-MM-dd HH:mm:dd" incorrectly used dd (day of month) for the seconds component. The corrected format "yyyy-MM-dd HH:mm:ss" properly uses ss for seconds.


57-57: Standardized seconds format for consistency.

The change from "yyyy-MM-dd HH:mm:SS" to "yyyy-MM-dd HH:mm:ss" standardizes the seconds format. SS represents fractional seconds while ss represents whole seconds, which is likely what the API returns.

Wable-iOS/Domain/RepositoryInterface/GhostRepository.swift (1)

18-25: LGTM! Good async/await support addition.

The new async throwing method provides a clean alternative to the Combine-based approach while maintaining the same interface contract. This pattern supports gradual migration to Swift concurrency.

Wable-iOS/Presentation/TabBar/TabBarController.swift (2)

74-81: LGTM! Dependency injection follows consistent pattern.

The new userSessionUseCase dependency injection is properly structured and aligns with the other use case dependencies in the view model.


155-155: Verify the impact of disabling tab bar setup animation.

The change from animated: true to animated: false will make the tab bar appear immediately without animation during setup. While this might improve perceived performance, ensure this aligns with the intended user experience.

Consider documenting the reason for this change if it's intentional for better perceived performance or if it resolves a specific UI issue.

Wable-iOS/Domain/RepositoryInterface/ContentLikedRepository.swift (1)

14-14: Good addition of async/await API variants.

Adding async/await versions alongside the existing Combine methods provides flexibility during the migration to Swift concurrency while maintaining backward compatibility. The method signatures are consistent and follow Swift concurrency best practices.

Also applies to: 16-16

Wable-iOS/Domain/RepositoryInterface/ReportRepository.swift (1)

14-14: Consistent async/await API addition.

The addition of async/await variants maintains consistency with other repository interfaces in this PR, providing the same dual API pattern for gradual migration from Combine to Swift concurrency.

Also applies to: 16-16

Wable-iOS/Presentation/WableComponent/Cell/CommentCollectionViewCell.swift (1)

231-231: Minor optimization in guard positioning.

Moving the guard statement after UI setup allows more cell configuration to occur before early exit. This ensures handlers and basic UI state are set even if createdDate is nil, which could be beneficial for cell reuse scenarios.

Wable-iOS/Data/RepositoryImpl/ContentLikedRepositoryImpl.swift (1)

32-44: Well-implemented async variant of createContentLiked.

The async implementation correctly mirrors the Combine version with proper error handling via ErrorMapper.map. The do-catch pattern follows Swift concurrency best practices.

Wable-iOS/Presentation/Home/View/HomeViewController.swift (4)

178-181: LGTM: Improved closure readability

The multi-line closure syntax with explicit parameters enhances code readability without affecting functionality.


276-283: Verify consistency of dependency injection refactoring

The initialization of OtherProfileViewController has been refactored to use CheckUserRoleUseCaseImpl instead of the previous use cases. This appears to be part of a broader architectural change.

Please verify that this change is consistent across the codebase and that OtherProfileViewModel has been updated to work with this new dependency structure:

#!/bin/bash
# Description: Verify OtherProfileViewModel constructor and CheckUserRoleUseCaseImpl usage

# Search for OtherProfileViewModel initialization patterns
ast-grep --pattern 'OtherProfileViewModel(
  $$$
)'

# Search for CheckUserRoleUseCaseImpl usage across the codebase
rg -A 3 -B 3 "CheckUserRoleUseCaseImpl"

178-181: LGTM: Improved code readability

The closure syntax change from single-line trailing closure to multi-line format with explicit parameters improves readability without altering functionality.


276-283: Verify that OtherProfileViewController works with the new use case configuration

The initialization of OtherProfileViewController's view model was significantly changed - the previous use cases FetchUserContentUseCaseImpl and FetchUserCommentListUseCaseImpl were removed and replaced with a single CheckUserRoleUseCaseImpl. This could be a breaking change if the profile view controller depends on those specific use cases.

Please run the following script to verify that OtherProfileViewController and OtherProfileViewModel have been updated to work with this new configuration:

#!/bin/bash
# Description: Check OtherProfileViewController and OtherProfileViewModel for use case dependencies

# Search for references to the removed use cases in profile-related files
echo "=== Checking for removed use case references ==="
rg "FetchUserContentUseCaseImpl|FetchUserCommentListUseCaseImpl" --type swift

# Search for OtherProfileViewModel constructor to see what dependencies it expects
echo "=== Checking OtherProfileViewModel constructor ==="
ast-grep --pattern 'init($$$) {
  $$$
}' --lang swift | rg -A 10 -B 5 "OtherProfileViewModel"

# Check if CheckUserRoleUseCaseImpl is actually used in OtherProfileViewModel
echo "=== Checking CheckUserRoleUseCaseImpl usage ==="
rg "CheckUserRoleUseCaseImpl" --type swift -A 3 -B 3
Wable-iOS/Data/RepositoryImpl/ReportRepositoryImpl.swift (5)

34-48: LGTM: Well-implemented async/await method

The async version of createReport properly handles errors using do-catch with ErrorMapper.map() and maintains consistency with the existing Combine implementation.


65-80: LGTM: Consistent async/await implementation

The async version of createBan follows the same error handling pattern as the createReport method, maintaining consistency across the repository.


34-48: LGTM: Well-implemented async variant

The async createReport method correctly follows the established pattern:

  • Wraps the provider request in a do-catch block
  • Uses the same request structure as the Combine variant
  • Properly maps errors via ErrorMapper

This addition supports modern Swift concurrency alongside existing Combine patterns.


65-80: LGTM: Consistent async implementation

The async createBan method maintains consistency with the async createReport implementation and follows the same error handling pattern.


94-96: LGTM: Appropriate mock stubs

The empty async method stubs in MockReportRepository are appropriate placeholders that maintain interface conformity without implementing actual functionality, which is expected behavior for mock repositories.

Also applies to: 104-106

Wable-iOS/Presentation/Profile/Component/ProfileSegmentedHeaderView.swift (6)

19-19: LGTM: Improved property naming

The rename from segmentDidChangeClosure to onSegmentIndexChanged provides better clarity about the property's purpose.


21-21: LGTM: Proper Combine lifecycle management

Adding cancelBag and calling setupAction() during initialization ensures proper subscription lifecycle management.

Also applies to: 28-28


52-58: LGTM: Excellent Combine pattern implementation

The refactoring from target-action to Combine publisher provides several benefits:

  • Debouncing prevents excessive calls during rapid segment changes
  • removeDuplicates() avoids unnecessary processing
  • Weak self capture prevents retain cycles
  • More declarative and testable code

This is a great example of modernizing UI event handling with Combine.


19-19: LGTM: Improved naming convention

The property rename from segmentDidChangeClosure to onSegmentIndexChanged follows better Swift naming conventions and is more descriptive.


21-21: LGTM: Proper Combine lifecycle management

Added cancelBag property and setupAction() call ensures proper subscription lifecycle management for the new reactive implementation.

Also applies to: 28-28


52-58: LGTM: Excellent modernization to reactive pattern

The new setupAction() method represents a significant improvement:

  • Replaces imperative target-action pattern with declarative Combine publisher
  • Adds 300ms debounce to prevent rapid-fire events, improving performance
  • Uses removeDuplicates() to avoid unnecessary callbacks
  • Properly manages subscription lifecycle with cancelBag

This change aligns with modern reactive programming patterns and provides better user experience.

Wable-iOS/Data/RepositoryImpl/CommentRepositoryImpl.swift (3)

65-74: LGTM: Consistent async/await implementation

The async version of deleteComment follows the established pattern with proper error handling using do-catch and ErrorMapper.map(). The implementation is consistent with other async methods in the repository.


65-74: LGTM: Consistent async implementation

The async deleteComment method follows the established pattern used throughout the codebase:

  • Proper do-catch error handling
  • Uses the same provider request as the Combine variant
  • Correctly maps errors via ErrorMapper

This addition supports the broader effort to modernize the data layer with Swift concurrency.


118-120: LGTM: Appropriate mock behavior

The mock implementation correctly throws WableError.unknownError, which is consistent with the expected behavior of a mock repository and provides predictable error handling for testing scenarios.

Wable-iOS/Presentation/Profile/Component/ProfileInfoCell.swift (5)

30-34: LGTM! Good addition for the level badge UI.

The implementation correctly adds the level badge image view with appropriate content mode and clipping settings.


78-78: Good improvement using .zero property.

Using .zero is more idiomatic and clear than setProgress(0.0, animated: false).


126-126: Good fix for progress bar normalization.

The new calculation correctly normalizes the ghost value to the 0.0-1.0 range required by UIProgressView. This fixes a potential issue where negative ghost values would have resulted in invalid progress values.


188-197: Well-structured UI layout with the badge.

The constraints properly position the level badge next to the profile image and align the level label and nickname label correctly with the new badge element.

Also applies to: 200-202


258-262: Clean action handler implementation.

The simplified closure syntax with weak self capture is more concise and follows Swift best practices.

Wable-iOS/Presentation/Viewit/List/View/ViewitListViewController.swift (2)

36-36: Good addition for profile tap handling.

The relay properly follows the reactive pattern used throughout the view controller.


119-123: Profile tap handling looks good.

The implementation correctly sends the user ID through the relay for profile navigation.

Wable-iOS/Presentation/Profile/My/View/MyProfileViewController.swift (4)

8-9: Good adoption of Combine for reactive programming.

The use of PassthroughSubject for pagination events with debouncing is a good pattern to prevent excessive API calls.

Also applies to: 58-59


98-99: Good defensive programming with guard statement.

The guard ensures only post sections trigger navigation, preventing potential crashes from invalid section access.


143-143: Good optimization for pagination trigger.

Changing from 5 to 2 items before the end provides a smoother user experience by loading content earlier.


319-322: Excellent use of debouncing for pagination.

The 1-second debounce prevents excessive API calls when scrolling quickly. This is a performance optimization best practice.

Wable-iOS/Presentation/Viewit/List/ViewModel/ViewitListViewModel.swift (2)

16-16: Good addition of user session dependency.

The dependency is properly injected and stored, following the existing pattern.

Also applies to: 23-23, 29-29


212-223: Well-implemented profile navigation logic.

The logic correctly determines whether to navigate to the user's own profile (nil) or another user's profile based on ID comparison.

Wable-iOS/Presentation/Profile/Other/View/OtherProfileViewController.swift (9)

8-8: Good architectural improvements!

Adding Combine import and Int raw value conformance to the Section enum are positive changes that enable safer section handling and reactive programming patterns.

Also applies to: 18-18


54-54: Excellent implementation of debounced pagination!

The use of PassthroughSubject with a 1-second debounce effectively prevents excessive API calls during rapid scrolling. The weak self capture in the sink closure properly avoids retain cycles.

Also applies to: 142-142, 178-182


96-96: Good safety improvement!

Using Section(rawValue:) with guard statement prevents potential crashes from invalid section indices.


207-233: Well-implemented moderation features with proper access control!

The cell configuration correctly:

  • Sets authorType to .others for other users' profiles
  • Uses weak self references to prevent retain cycles
  • Implements role-based access control where admins see additional "ban" option
  • Presents appropriate action sheets based on user permissions

243-267: Consistent implementation for comment cells!

The comment cell configuration mirrors the content cell pattern with proper authorType, weak self references, and role-based moderation options.


275-275: Better naming convention!

The property name onSegmentIndexChanged is more descriptive than segmentDidChangeClosure and clearly indicates that it receives an index parameter.


352-356: Good user feedback implementation!

The binding properly observes report completion and provides clear feedback to users with an informative toast message.


403-477: Well-structured moderation action methods!

The implementation provides:

  • Clear separation between content and comment actions
  • Consistent action sheet presentation pattern
  • Proper memory management with weak self references
  • Appropriate titles and messages for each action type

540-571: Excellent constants organization!

Centralizing UI strings in a nested Constants enum improves maintainability and makes future localization easier. The organization by feature (Report, Ban, Ghost, Cancel) provides clear structure.

Wable-iOS/Presentation/Profile/My/ViewModel/MyProfileViewModel.swift (6)

26-29: Excellent adoption of repository pattern with dependency injection!

Using @Injected property wrapper for repositories improves testability and follows SOLID principles. This refactoring from use cases to repositories with DI is a positive architectural change.


50-52: Good simplification of segment handling!

Directly updating the segment instead of using Combine subjects reduces complexity while maintaining functionality. This follows the KISS principle effectively.


83-105: Well-implemented delete functionality!

The delete methods properly:

  • Use async/await for repository calls
  • Update UI state on MainActor for thread safety
  • Handle errors through centralized error handling
  • Remove items from local state after successful deletion

107-154: Comprehensive like/unlike implementation!

The toggle methods effectively:

  • Handle both like and unlike states
  • Update local state on MainActor after successful API calls
  • Include proper trigger types for notifications
  • Maintain data consistency with immutable updates

164-247: Excellent async/await implementation!

The fetch methods demonstrate:

  • Effective use of structured concurrency with async let
  • Consistent thread-safe UI updates with MainActor.run
  • Proper task cancellation handling
  • Clear separation between async operations and UI state updates

249-252: Clean error handling pattern!

The @MainActor annotated error handler ensures thread-safe error message updates and provides a single point for error processing.

Wable-iOS/Presentation/Profile/Other/ViewModel/OtherProfileViewModel.swift (3)

16-32: Good additions for moderation features!

The new published properties enable proper user feedback, and the repository dependencies follow the established dependency injection pattern consistently.


124-126: Clean role checking interface!

The method provides a simple way for the view controller to check user permissions for moderation features.


128-155: Well-implemented reporting functionality!

The report methods properly extract relevant data (nickname and text) and provide user feedback through the completion state.

Comment on lines +294 to +297
func showMyProfile() {
let myProfileTabIndex = 4
navigationController?.tabBarController?.selectedIndex = myProfileTabIndex
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using an enum for tab indices instead of hard-coding.

The hard-coded tab index 4 is fragile and could break if the tab order changes. Consider defining an enum or constants for tab indices.

-    func showMyProfile() {
-        let myProfileTabIndex = 4
-        navigationController?.tabBarController?.selectedIndex = myProfileTabIndex
-    }
+    func showMyProfile() {
+        navigationController?.tabBarController?.selectedIndex = TabIndex.profile.rawValue
+    }

Add this enum to your tab bar controller or a shared constants file:

enum TabIndex: Int {
    case home = 0
    case search = 1
    case viewit = 2
    case notification = 3
    case profile = 4
}
🤖 Prompt for AI Agents
In Wable-iOS/Presentation/Viewit/List/View/ViewitListViewController.swift around
lines 294 to 297, the tab index 4 is hard-coded when selecting the profile tab,
which is fragile. Define an enum representing the tab indices, such as TabIndex
with cases for each tab and their raw values, and replace the hard-coded 4 with
TabIndex.profile.rawValue to improve code clarity and maintainability.

Comment on lines +173 to +189
func banComment(for commentID: Int) {
Task {
do {
try await reportRepository.createBan(
memberID: userID,
triggerType: .content,
triggerID: commentID
)

fetchViewItems(userID: userID, segment: item.currentSegment)

await MainActor.run { isGhostCompleted = true }
} catch {
await handleError(error: error)
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix critical issues in banComment method

The banComment method has two bugs that need to be fixed:

  1. Line 178: Incorrect trigger type - should be .comment instead of .content
  2. Line 184: Wrong completion flag - sets isGhostCompleted instead of a ban-specific flag

Apply this diff to fix the issues:

-                try await reportRepository.createBan(
-                    memberID: userID,
-                    triggerType: .content,
-                    triggerID: commentID
-                )
+                try await reportRepository.createBan(
+                    memberID: userID,
+                    triggerType: .comment,
+                    triggerID: commentID
+                )
                
                fetchViewItems(userID: userID, segment: item.currentSegment)
                
-                await MainActor.run { isGhostCompleted = true }
+                // Remove this line or add a proper ban completion flag
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func banComment(for commentID: Int) {
Task {
do {
try await reportRepository.createBan(
memberID: userID,
triggerType: .content,
triggerID: commentID
)
fetchViewItems(userID: userID, segment: item.currentSegment)
await MainActor.run { isGhostCompleted = true }
} catch {
await handleError(error: error)
}
}
}
func banComment(for commentID: Int) {
Task {
do {
try await reportRepository.createBan(
memberID: userID,
triggerType: .comment,
triggerID: commentID
)
fetchViewItems(userID: userID, segment: item.currentSegment)
// Remove this line or add a proper ban completion flag
} catch {
await handleError(error: error)
}
}
}
🤖 Prompt for AI Agents
In Wable-iOS/Presentation/Profile/Other/ViewModel/OtherProfileViewModel.swift
between lines 173 and 189, fix two bugs in the banComment method: change the
triggerType argument in the createBan call from .content to .comment, and
replace setting isGhostCompleted with the correct ban-specific completion flag
to reflect the ban action accurately.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
Wable-iOS/Presentation/Profile/Other/ViewModel/OtherProfileViewModel.swift (1)

173-189: ⚠️ Potential issue

Critical bugs still present in banComment method - Same issues from previous review.

The banComment method still contains the two critical bugs identified in the previous review:

  1. Line 178: Incorrect trigger type - using .content instead of .comment
  2. Line 184: Wrong completion flag - setting isGhostCompleted instead of a ban-specific flag

These bugs will cause incorrect API calls and misleading UI state.

Apply this diff to fix the critical issues:

                try await reportRepository.createBan(
                    memberID: userID,
-                    triggerType: .content,
+                    triggerType: .comment,
                    triggerID: commentID
                )
                
                fetchViewItems(userID: userID, segment: item.currentSegment)
                
-                await MainActor.run { isGhostCompleted = true }
+                // Remove this line or add a proper ban completion flag if needed
🧹 Nitpick comments (2)
Wable-iOS/Presentation/Profile/Other/ViewModel/OtherProfileViewModel.swift (2)

89-89: Fix ternary operator usage for void functions.

The static analysis correctly identified that using ternary operators to call void functions should be avoided for better readability.

Apply this diff to improve readability:

-                isLiked ? contentInfo.like.unlike() : contentInfo.like.like()
+                if isLiked {
+                    contentInfo.like.unlike()
+                } else {
+                    contentInfo.like.like()
+                }

And similarly for line 115:

-                isLiked ? commentInfo.like.unlike() : commentInfo.like.like()
+                if isLiked {
+                    commentInfo.like.unlike()
+                } else {
+                    commentInfo.like.like()
+                }

Also applies to: 115-115

🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 89-89: Using ternary to call Void functions should be avoided

(void_function_in_ternary)


191-223: Consider adding completion state tracking for ghost actions.

The ghosting methods are well-implemented, but unlike reporting, they don't update the isGhostCompleted flag consistently. Consider whether ghost actions should provide UI feedback similar to reporting.

If ghost completion feedback is needed, add this after successful operations:

                fetchViewItems(userID: userID, segment: item.currentSegment)
+                await MainActor.run { isGhostCompleted = true }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98b555c and 6b1a4f0.

📒 Files selected for processing (8)
  • Wable-iOS.xcodeproj/project.pbxproj (6 hunks)
  • Wable-iOS/Presentation/Profile/Model/ProfileEmptyCellItem.swift (1 hunks)
  • Wable-iOS/Presentation/Profile/Model/ProfileSegment.swift (1 hunks)
  • Wable-iOS/Presentation/Profile/Model/ProfileViewItem.swift (1 hunks)
  • Wable-iOS/Presentation/Profile/My/View/Cell/MyProfileEmptyCell.swift (1 hunks)
  • Wable-iOS/Presentation/Profile/My/ViewModel/MyProfileViewModel.swift (5 hunks)
  • Wable-iOS/Presentation/Profile/Other/View/Cell/OtherProfileEmptyCell.swift (1 hunks)
  • Wable-iOS/Presentation/Profile/Other/ViewModel/OtherProfileViewModel.swift (5 hunks)
✅ Files skipped from review due to trivial changes (2)
  • Wable-iOS/Presentation/Profile/Model/ProfileViewItem.swift
  • Wable-iOS/Presentation/Profile/Model/ProfileSegment.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • Wable-iOS.xcodeproj/project.pbxproj
🧰 Additional context used
🧬 Code Graph Analysis (2)
Wable-iOS/Presentation/Profile/My/View/Cell/MyProfileEmptyCell.swift (1)
Wable-iOS/Presentation/Profile/Other/View/Cell/OtherProfileEmptyCell.swift (1)
  • configure (32-35)
Wable-iOS/Presentation/Profile/Other/View/Cell/OtherProfileEmptyCell.swift (1)
Wable-iOS/Presentation/Profile/My/View/Cell/MyProfileEmptyCell.swift (1)
  • configure (43-58)
🪛 SwiftLint (0.57.0)
Wable-iOS/Presentation/Profile/Other/ViewModel/OtherProfileViewModel.swift

[Warning] 89-89: Using ternary to call Void functions should be avoided

(void_function_in_ternary)


[Warning] 115-115: Using ternary to call Void functions should be avoided

(void_function_in_ternary)

🔇 Additional comments (21)
Wable-iOS/Presentation/Profile/Model/ProfileEmptyCellItem.swift (2)

11-11: LGTM! Clean type rename as part of refactoring.

The property type update from ProfileSegmentKind to ProfileSegment is consistent with the broader enum renaming refactoring effort mentioned in the summary. This maintains the same functionality while improving code clarity.


11-11:

Details

✅ Verification successful

LGTM! Clean enum type refactoring.

The type change from ProfileSegmentKind to ProfileSegment improves naming consistency. However, ensure this change has been applied consistently across the entire codebase to avoid compilation errors.


🏁 Script executed:

#!/bin/bash
# Description: Verify that ProfileSegmentKind has been completely replaced with ProfileSegment across the codebase
# Expected: No occurrences of ProfileSegmentKind should remain

echo "Searching for any remaining ProfileSegmentKind references..."
rg "ProfileSegmentKind" --type swift

echo "Verifying ProfileSegment usage..."
rg "ProfileSegment" --type swift -A 2

Length of output: 4975


Enum refactoring complete and verified

Search for ProfileSegmentKind returned no hits, confirming it’s been fully replaced with ProfileSegment throughout the codebase. All usages of ProfileSegment are correct and consistent—no further action required.

Wable-iOS/Presentation/Profile/My/View/Cell/MyProfileEmptyCell.swift (2)

43-43: Excellent refactoring! Parameter type updated consistently.

The method signature update from ProfileSegmentKind to ProfileSegment maintains the same interface behavior - the switch statement logic with .content and .comment cases works identically. This demonstrates good refactoring practice where the type name is improved without breaking existing functionality.


43-58: Implementation looks correct and consistent.

The parameter type change aligns with the enum refactoring from ProfileSegmentKind to ProfileSegment. The method correctly handles both .content and .comment cases with appropriate UI visibility logic and nickname handling.

Wable-iOS/Presentation/Profile/Other/View/Cell/OtherProfileEmptyCell.swift (2)

32-32: Consistent type update across profile components.

The parameter type change from ProfileSegmentKind to ProfileSegment is consistent with the same update in MyProfileEmptyCell.swift (line 43-58). Both empty cell implementations now use the renamed enum type while preserving identical logic for handling .content and .comment cases. This demonstrates good consistency in the refactoring effort.


32-35: Implementation is correct and follows consistent patterns.

The parameter type change from ProfileSegmentKind to ProfileSegment is properly implemented. The method correctly uses the enum to select appropriate empty state messages and handles the optional nickname with a sensible fallback ("알 수 없음").

Wable-iOS/Presentation/Profile/Other/ViewModel/OtherProfileViewModel.swift (5)

16-17: LGTM: Good addition of completion state tracking.

The new published properties isReportCompleted and isGhostCompleted provide clear UI feedback for async moderation actions.


26-32: Excellent refactoring to dependency injection pattern.

The migration from direct use case dependencies to injected repositories improves testability and follows SOLID principles. The @injected pattern provides clean separation of concerns.


128-155: Well-implemented reporting functionality with proper error handling.

The reporting methods correctly extract user information and handle async operations with proper error handling and UI state updates.


227-265: Excellent async/await implementation with proper concurrency.

The refactored fetch method uses async let for concurrent operations and proper MainActor usage for UI updates. The error handling is centralized and clean.


317-321: Good centralized error handling approach.

The @MainActor annotation ensures thread-safe UI updates for error messages. This is a clean pattern for error handling across the view model.

Wable-iOS/Presentation/Profile/My/ViewModel/MyProfileViewModel.swift (10)

26-29: LGTM: Clean dependency injection pattern.

The migration from use cases to repositories using the @Injected property wrapper is well-structured and follows a consistent dependency injection pattern.


51-53: LGTM: Simple and correct segment change handling.

The method correctly maps the selected index to a ProfileSegment enum and updates the current segment state.


70-81: LGTM: Improved pagination logic with direct method calls.

The refactoring from Combine subjects to direct method calls makes the pagination logic more explicit and easier to follow. The switch statement properly handles different segment types.


107-128: LGTM: Well-implemented optimistic UI update with proper state capture.

The method correctly captures the current like status before the async operation and handles both like/unlike scenarios properly. The MainActor usage for UI updates is appropriate.


130-155: LGTM: Consistent implementation with proper state management.

The comment like toggle follows the same good pattern as content likes, with proper state capture and MainActor usage for UI updates. The inclusion of notification text is a nice touch.


158-196: LGTM: Clean async/await refactoring with proper concurrency handling.

The method properly uses async let for concurrent execution of independent operations and correctly handles UI updates on the MainActor. The error handling and loading state management are well-implemented.


198-222: LGTM: Robust pagination implementation with task cancellation.

The method includes proper task cancellation handling, loading state management, and MainActor usage for UI updates. The pagination logic is sound.


224-247: LGTM: Consistent pagination pattern for comments.

This method follows the same robust pattern as the content pagination with proper error handling and concurrency management.


249-253: LGTM: Centralized error handling with MainActor annotation.

The error handling method is properly annotated with @MainActor since it updates UI state, providing a clean centralized approach to error management.


83-93: 🛠️ Refactor suggestion

Potential race condition in content deletion.

The current implementation looks up the index before performing the async deletion, which could lead to a race condition if the content list is modified between the index lookup and the deletion.

Consider capturing the index and validating it after the successful deletion:

func deleteContent(for contentID: Int) {
    Task {
        do {
            try await contentRepository.deleteContent(contentID: contentID)
-           guard let index = item.contentList.firstIndex(where: { $0.id == contentID }) else { return }
-           _ = await MainActor.run { item.contentList.remove(at: index) }
+           await MainActor.run {
+               if let index = item.contentList.firstIndex(where: { $0.id == contentID }) {
+                   item.contentList.remove(at: index)
+               }
+           }
        } catch {
            await handleError(error: error)
        }
    }
}

Likely an incorrect or invalid review comment.

Comment on lines +95 to +105
func deleteComment(for commentID: Int) {
Task {
do {
try await commentRepository.deleteComment(commentID: commentID)
guard let index = item.commentList.firstIndex(where: { $0.comment.id == commentID }) else { return }
_ = await MainActor.run { item.commentList.remove(at: index) }
} catch {
await handleError(error: error)
}
.sink { [weak self] userID in
guard let self else { return }
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Same race condition issue in comment deletion.

This method has the same potential race condition as the content deletion method.

Apply the same fix pattern as suggested for content deletion:

func deleteComment(for commentID: Int) {
    Task {
        do {
            try await commentRepository.deleteComment(commentID: commentID)
-           guard let index = item.commentList.firstIndex(where: { $0.comment.id == commentID }) else { return }
-           _ = await MainActor.run { item.commentList.remove(at: index) }
+           await MainActor.run {
+               if let index = item.commentList.firstIndex(where: { $0.comment.id == commentID }) {
+                   item.commentList.remove(at: index)
+               }
+           }
        } catch {
            await handleError(error: error)
        }
    }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func deleteComment(for commentID: Int) {
Task {
do {
try await commentRepository.deleteComment(commentID: commentID)
guard let index = item.commentList.firstIndex(where: { $0.comment.id == commentID }) else { return }
_ = await MainActor.run { item.commentList.remove(at: index) }
} catch {
await handleError(error: error)
}
.sink { [weak self] userID in
guard let self else { return }
}
}
func deleteComment(for commentID: Int) {
Task {
do {
try await commentRepository.deleteComment(commentID: commentID)
await MainActor.run {
if let index = item.commentList.firstIndex(where: { $0.comment.id == commentID }) {
item.commentList.remove(at: index)
}
}
} catch {
await handleError(error: error)
}
}
}
🤖 Prompt for AI Agents
In Wable-iOS/Presentation/Profile/My/ViewModel/MyProfileViewModel.swift around
lines 95 to 105, the deleteComment method has a race condition when removing a
comment from item.commentList after deletion. To fix this, ensure that the check
for the comment's index and its removal from the list happen atomically on the
MainActor to prevent concurrent modifications. Wrap both the index lookup and
removal inside a single await MainActor.run block to synchronize access to
item.commentList.

Copy link
Member

@youz2me youz2me left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굿!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 고생하셨습니닷. 조금만 더 화이팅 🙇‍♀️

static func toDomain(_ response: [DTO.Response.FetchContentComments]) -> [ContentComment] {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:SS"
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SS로 하지 않아도 상관 없나요? 뭔가 항상 대문자와 소문자를 교차로 사용했던 것 같은데 아니어도 괜찮은거였나 보군요 ,,,

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 알기로는 SS는 잘못된 표현 방식으로 알고 있습니다.
대문자로 표기되는 것들은 그 만의 형식?(24시간/12시간)이 있어서 사용하는 것으로 알고 있어요.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그랬군요 🫨 모르고 살뻔 했네요 ,,, 감사합니닷.

image

Comment on lines 178 to 181
let homeCellRegistration = CellRegistration<ContentCollectionViewCell, Content> { [weak self] cell, indexPath, item in
let homeCellRegistration = CellRegistration<ContentCollectionViewCell, Content> {
[weak self] cell,
indexPath,
item in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분 들여쓰기 하는 거 혹시 맞을까요?? 제가 항상 들여쓰기를 안했어서 ㅎ ㅎ ..... 여쭤봅니닷.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

왜 이렇게 됐을까요?
여기는 제가 다시 수정하겠습니다.

Comment on lines -106 to +100
func setupDelegate() {
navigationController?.interactivePopGestureRecognizer?.delegate = self
func setupNavigationBar() {
navigationController?.navigationBar.isHidden = true
navigationController?.interactivePopGestureRecognizer?.isEnabled = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines 30 to 33
private let levelBadgeImageView = UIImageView(image: .icLevelBadge).then {
$0.contentMode = .scaleAspectFill
$0.clipsToBounds = true
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contentMode.scaleAspectFill이면 잘릴 수 있을 것 같기도 해서 .scaleAspectFit을 추천드려봅니닷.

Copy link
Collaborator Author

@JinUng41 JinUng41 May 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오, 감사합니다. 👍🏾

Comment on lines +212 to +216
guard let userRole = self?.viewModel.checkUserRole(),
userRole != .owner
else {
return
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 이렇게 하셨군요. 저도 홈 조만간 개선하도록 하겠습니다...

@github-project-automation github-project-automation bot moved this to In Review in Wable-iOS May 24, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
Wable-iOS/Presentation/Home/View/HomeViewController.swift (1)

178-179: Fix unused parameter in closure to address SwiftLint warning.

The closure syntax change improves readability, but the indexPath parameter is unused in the closure body and should be replaced with _ to follow Swift conventions.

-        let homeCellRegistration = CellRegistration<ContentCollectionViewCell, Content> {
-            [weak self] cell, indexPath, item in
+        let homeCellRegistration = CellRegistration<ContentCollectionViewCell, Content> {
+            [weak self] cell, _, item in
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 179-179: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b1a4f0 and c0b012b.

📒 Files selected for processing (1)
  • Wable-iOS/Presentation/Home/View/HomeViewController.swift (2 hunks)
🧰 Additional context used
🪛 SwiftLint (0.57.0)
Wable-iOS/Presentation/Home/View/HomeViewController.swift

[Warning] 179-179: Unused parameter in a closure should be replaced with _

(unused_closure_parameter)

🔇 Additional comments (1)
Wable-iOS/Presentation/Home/View/HomeViewController.swift (1)

274-281: LGTM! Dependency injection refactoring aligns with architecture improvements.

The replacement of FetchUserContentUseCaseImpl and FetchUserCommentListUseCaseImpl with CheckUserRoleUseCaseImpl is consistent with the broader refactoring mentioned in the PR objectives. The dependency injection pattern is properly maintained with the new repository-based architecture.

@JinUng41 JinUng41 merged commit b540209 into develop May 24, 2025
1 check was pending
@github-project-automation github-project-automation bot moved this from In Review to Done in Wable-iOS May 24, 2025
@JinUng41 JinUng41 deleted the fix/#196-profile-improvements branch May 24, 2025 15:56
youz2me pushed a commit that referenced this pull request Oct 26, 2025
[Fix] 프로필 기능 개선 및 기타 오류 수정
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍻 진웅 술 한잔 가온나~ 🛠️ fix 기능적 버그나 오류 해결 시 사용

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Fix] 프로필 기능 보완 및 기타 수정

3 participants