Skip to content

Comments

refactor(settings): Relocate Settings feature to Profile#261

Open
rainxchzed wants to merge 2 commits intomainfrom
settings-to-profile
Open

refactor(settings): Relocate Settings feature to Profile#261
rainxchzed wants to merge 2 commits intomainfrom
settings-to-profile

Conversation

@rainxchzed
Copy link
Owner

@rainxchzed rainxchzed commented Feb 24, 2026

This commit refactors the settings feature module into a new profile module. This change better reflects the content and purpose of the screen, which includes user profile information, settings, and other related actions.

All existing functionality from the settings module—including appearance settings, about section, and logout logic—has been migrated to the new profile module. Corresponding package names, class names, and Gradle module dependencies have been updated throughout the codebase to reflect this change.

  • refactor(module): Renamed the feature/settings module to feature/profile.
  • refactor(navigation): Updated AppNavigation and GithubStoreGraph to use ProfileScreen instead of SettingsScreen.
  • refactor(di): Renamed SettingsViewModel to ProfileViewModel and updated the dependency injection configuration.
  • chore(build): Updated settings.gradle.kts and composeApp/build.gradle.kts to reflect the module rename.
  • chore(db): Added a new Room schema for version 3.

Summary by CodeRabbit

  • New Features

    • Added user profile information display with image, account details, and user statistics.
    • Introduced new image component with improved loading states and visual transitions.
  • UI/UX Improvements

    • Settings screen renamed to Profile screen with enhanced layout and organization.
    • Updated multi-language support across all UI labels and strings.
  • Other

    • Enhanced database schema with profile-related data structures.

This commit refactors the `settings` feature module into a new `profile` module. This change better reflects the content and purpose of the screen, which includes user profile information, settings, and other related actions.

All existing functionality from the `settings` module—including appearance settings, about section, and logout logic—has been migrated to the new `profile` module. Corresponding package names, class names, and Gradle module dependencies have been updated throughout the codebase to reflect this change.

- **refactor(module)**: Renamed the `feature/settings` module to `feature/profile`.
- **refactor(navigation)**: Updated `AppNavigation` and `GithubStoreGraph` to use `ProfileScreen` instead of `SettingsScreen`.
- **refactor(di)**: Renamed `SettingsViewModel` to `ProfileViewModel` and updated the dependency injection configuration.
- **chore(build)**: Updated `settings.gradle.kts` and `composeApp/build.gradle.kts` to reflect the module rename.
- **chore(db)**: Added a new Room schema for version 3.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

Walkthrough

This pull request migrates the application from a Settings feature module to a Profile feature module, updating all dependencies, navigation references, and presentation layer components. It introduces new domain models (UserProfile), repositories (ProfileRepository), and UI components (GitHubStoreImage, AccountSection, SectionText) while renaming existing classes and updating string resources across multiple locales.

Changes

Cohort / File(s) Summary
Gradle & Module Configuration
composeApp/build.gradle.kts, feature/profile/.../*.gradle.kts, settings.gradle.kts
Replaced settings module dependencies with profile module dependencies across build configuration files.
Dependency Injection
composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/ViewModelsModule.kt, composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/initKoin.kt, feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/di/SharedModule.kt
Updated ViewModel and module bindings from SettingsViewModel to ProfileViewModel; replaced settingsModule import path to profile.data.di.
Navigation
composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/AppNavigation.kt, composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/BottomNavigationUtils.kt, composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/GithubStoreGraph.kt, composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/NavigationUtils.kt
Renamed SettingsScreen to ProfileScreen throughout navigation graph and route handlers; updated bottom navigation to reference ProfileScreen.
Domain Layer
feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/model/UserProfile.kt, feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/repository/ProfileRepository.kt
Added new UserProfile data class and ProfileRepository interface with methods for user profile retrieval, login state, version name, and logout.
Data Layer
feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/repository/ProfileRepositoryImpl.kt
Migrated SettingsRepositoryImpl to ProfileRepositoryImpl implementing new ProfileRepository interface.
Presentation - Action & Event Types
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt, feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt
Added new ProfileAction sealed interface with action variants and ProfileEvent sealed interface for logout state management.
Presentation - State & ViewModel
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt, feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt
Renamed SettingsState to ProfileState (adding userProfile property); renamed SettingsViewModel to ProfileViewModel with updated repository and action handling.
Presentation - Root Composable
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt
Migrated SettingsRoot to ProfileRoot composable with ProfileScreen, updated state/action/event references and logout flow wiring.
Presentation - UI Components
core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/GitHubStoreImage.kt, feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/SectionText.kt, feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/AccountSection.kt, feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/ProfileSection.kt, feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/SettingsSection.kt
Introduced GitHubStoreImage composable for image loading with fallback states; added SectionTitle/SectionHeader components; created AccountSection, ProfileSection, and SettingsSection for profile display.
Presentation - Section Components
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt, feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/About.kt, feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Account.kt, feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/LogoutDialog.kt
Refactored appearance, about, and account sections to use state-based pattern with ProfileAction; updated package paths and action references.
Presentation - Legacy Removal
feature/settings/presentation/src/commonMain/kotlin/zed/rainxch/settings/presentation/SettingsAction.kt, feature/settings/presentation/src/commonMain/kotlin/zed/rainxch/settings/presentation/SettingsEvent.kt
Removed SettingsAction and SettingsEvent sealed types from settings module as part of feature migration.
String Resources (Localization)
core/presentation/src/commonMain/composeResources/values*/strings-*.xml (13 files: default, bn, es, fr, hi, it, ja, kr, pl, ru, tr, zh-rCN)
Renamed string key from settings_title to profile_title with localized translations across all language variants.
Image Component Integration
core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/RepositoryCard.kt
Replaced CoilImage with new GitHubStoreImage for avatar rendering; removed inline loading placeholder UI.
Database Schema
core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/3.json
Added Room database version 3 schema defining tables: installed_apps, favorite_repos, update_history, starred_repos with field definitions and primary keys.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #179 — Modifies RepositoryCard.kt to integrate image components; overlaps with CoilImage → GitHubStoreImage replacement in this PR.
  • PR #240 — Updates bottom navigation wiring for profile/settings screen mapping and string key references; overlaps with navigation changes in this PR.
  • PR #177 — Introduces Room database schema version 3 with new tables; overlaps with database manifest changes in this PR.

Poem

🐰 From Settings to Profile, the refactor hops,
Renaming all the classes, the changes never stop!
New components sprout like carrots in spring,
UserProfile and repositories—such organized things! 🌱
The rabbit cheers as dependencies realign,
A profile feature module, now truly divine!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor(settings): Relocate Settings feature to Profile' clearly describes the main change—moving the settings feature to a profile module. It accurately reflects the primary objective evident across all file changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch settings-to-profile

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
feature/profile/data/src/commonMain/kotlin/zed/rainxch/settings/data/repository/SettingsRepositoryImpl.kt (1)

1-10: ⚠️ Potential issue | 🟡 Minor

Update the package to the profile namespace.

The file now lives under feature/profile, but the package is still zed.rainxch.settings.data.repository, which violates the required zed.rainxch.{module}.{layer} pattern and leaves the refactor incomplete. Rename the package to the profile namespace and adjust imports accordingly.

💡 Suggested change
-package zed.rainxch.settings.data.repository
+package zed.rainxch.feature.profile.data.repository

As per coding guidelines: **/*.kt: Organize packages following zed.rainxch.{module}.{layer} pattern (e.g., zed.rainxch.feature.home.presentation).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/data/src/commonMain/kotlin/zed/rainxch/settings/data/repository/SettingsRepositoryImpl.kt`
around lines 1 - 10, The package declaration in SettingsRepositoryImpl.kt is
still zed.rainxch.settings.data.repository but the file was moved under the
profile feature; change the package to follow the required pattern (e.g.,
zed.rainxch.feature.profile.data.repository) and update any affected
imports/usages (such as TokenStore, AuthenticationState, BuildKonfig, and
SettingsRepository references) to the new package so the class compiles and
follows zed.rainxch.{module}.{layer} conventions.
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Account.kt (1)

1-35: ⚠️ Potential issue | 🟠 Major

Fix package namespace to match the Profile module.
This file lives under feature/profile/... but still declares zed.rainxch.settings..., which breaks the package convention and keeps the Profile feature under the old namespace. Update the package declaration and adjust call sites (e.g., imports in ProfileRoot.kt) accordingly.

🔧 Suggested fix
- package zed.rainxch.settings.presentation.components.sections
+ package zed.rainxch.profile.presentation.components.sections

As per coding guidelines, organize packages following zed.rainxch.{module}.{layer} pattern (e.g., zed.rainxch.feature.home.presentation).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Account.kt`
around lines 1 - 35, The package declaration at the top of Account.kt is
incorrect (currently using
zed.rainxch.settings.presentation.components.sections) and must be changed to
the Profile module namespace to match its file path (e.g.,
zed.rainxch.profile.presentation.components.sections); update the package line,
then update all call sites and imports (for example in ProfileRoot.kt and any
other files importing logout or symbols from Account.kt such as the logout
LazyListScope extension) to the new package, rebuild and run tests to ensure no
unresolved imports remain.
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/About.kt (1)

1-39: ⚠️ Potential issue | 🟠 Major

Fix package namespace to match the Profile module.
This file is under feature/profile/... but still declares zed.rainxch.settings.... Please update the package to the Profile namespace and adjust imports where used.

🔧 Suggested fix
- package zed.rainxch.settings.presentation.components.sections
+ package zed.rainxch.profile.presentation.components.sections

As per coding guidelines, organize packages following zed.rainxch.{module}.{layer} pattern (e.g., zed.rainxch.feature.home.presentation).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/About.kt`
around lines 1 - 39, The package declaration is incorrect for the Profile
feature; change the package at the top of About.kt from
"zed.rainxch.settings.presentation.components.sections" to the Profile namespace
(e.g., "zed.rainxch.profile.presentation.components.sections") so it matches the
module location, and then update any imports/usages that reference the old
package across the codebase (references to the LazyListScope.about function /
About.kt) to the new package name so compilation and imports resolve correctly.
♻️ Duplicate comments (3)
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt (1)

1-14: Package name should include the feature segment (same as ProfileEvent.kt).

As per coding guidelines: "Organize packages following zed.rainxch.{module}.{layer} pattern".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt`
around lines 1 - 14, The package declaration at the top of ProfileState.kt is
missing the feature segment; update the package to match the project's package
pattern and the sibling file (same as ProfileEvent.kt) so ProfileState uses
zed.rainxch.feature.profile.presentation; locate the ProfileState data class and
change only the package line to include "feature" so the file follows
zed.rainxch.{module}.{layer} organization.
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt (1)

1-1: Package name should include the feature segment (same as ProfileEvent.kt).

As per coding guidelines: "Organize packages following zed.rainxch.{module}.{layer} pattern".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt`
at line 1, The package declaration in ProfileViewModel.kt is missing the
required feature segment; update the package line to match the project's package
pattern used by ProfileEvent.kt (zed.rainxch.feature.profile.presentation) so
the file sits under zed.rainxch.feature.profile.presentation and aligns with the
zed.rainxch.{module}.{layer} convention; update the package declaration at the
top of ProfileViewModel.kt (and any imports/usages if necessary) to the correct
package name.
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt (1)

1-16: Same package/ sealed-class guideline as ProfileEvent.kt.

As per coding guidelines: "Organize packages following zed.rainxch.{module}.{layer} pattern" and "Use sealed classes for type-safe navigation routes, actions, and events".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt`
around lines 1 - 16, Update this file to match the same package and sealed-class
style used by ProfileEvent.kt: change the package declaration to the identical
package used by ProfileEvent, replace "sealed interface ProfileAction" with
"sealed class ProfileAction", convert the singleton cases (currently declared as
"data object" like OnNavigateBackClick, OnLogoutClick, OnLogoutConfirmClick,
OnLogoutDismiss, OnHelpClick) to plain "object" singletons, and keep the
parameterized entries as data classes (OnThemeColorSelected,
OnAmoledThemeToggled, OnDarkThemeChange, OnFontThemeSelected); ensure the type
names (ProfileAction, OnNavigateBackClick, OnThemeColorSelected, etc.) remain
unchanged.
🧹 Nitpick comments (1)
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt (1)

1-6: Align package path and sealed type with guidelines.

Use zed.rainxch.feature.profile.presentation and switch to a sealed class for events.

♻️ Suggested update
-package zed.rainxch.profile.presentation
+package zed.rainxch.feature.profile.presentation

-sealed interface ProfileEvent {
-    data object OnLogoutSuccessful : ProfileEvent
-    data class OnLogoutError(val message: String) : ProfileEvent
-}
+sealed class ProfileEvent {
+    data object OnLogoutSuccessful : ProfileEvent()
+    data class OnLogoutError(val message: String) : ProfileEvent()
+}

As per coding guidelines: "Organize packages following zed.rainxch.{module}.{layer} pattern" and "Use sealed classes for type-safe navigation routes, actions, and events".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt`
around lines 1 - 6, Change the package declaration to
"zed.rainxch.feature.profile.presentation" and convert the sealed interface
ProfileEvent into a sealed class; update its members so the success variant is
an object (OnLogoutSuccessful) and the error variant remains a data class
(OnLogoutError) inheriting from ProfileEvent (use the class-style inheritance
syntax), ensuring constructors and inheritance use the sealed class pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@feature/profile/data/src/commonMain/kotlin/zed/rainxch/settings/data/repository/SettingsRepositoryImpl.kt`:
- Around line 1-10: The package declaration in SettingsRepositoryImpl.kt is
still zed.rainxch.settings.data.repository but the file was moved under the
profile feature; change the package to follow the required pattern (e.g.,
zed.rainxch.feature.profile.data.repository) and update any affected
imports/usages (such as TokenStore, AuthenticationState, BuildKonfig, and
SettingsRepository references) to the new package so the class compiles and
follows zed.rainxch.{module}.{layer} conventions.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/About.kt`:
- Around line 1-39: The package declaration is incorrect for the Profile
feature; change the package at the top of About.kt from
"zed.rainxch.settings.presentation.components.sections" to the Profile namespace
(e.g., "zed.rainxch.profile.presentation.components.sections") so it matches the
module location, and then update any imports/usages that reference the old
package across the codebase (references to the LazyListScope.about function /
About.kt) to the new package name so compilation and imports resolve correctly.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Account.kt`:
- Around line 1-35: The package declaration at the top of Account.kt is
incorrect (currently using
zed.rainxch.settings.presentation.components.sections) and must be changed to
the Profile module namespace to match its file path (e.g.,
zed.rainxch.profile.presentation.components.sections); update the package line,
then update all call sites and imports (for example in ProfileRoot.kt and any
other files importing logout or symbols from Account.kt such as the logout
LazyListScope extension) to the new package, rebuild and run tests to ensure no
unresolved imports remain.

---

Duplicate comments:
In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt`:
- Around line 1-16: Update this file to match the same package and sealed-class
style used by ProfileEvent.kt: change the package declaration to the identical
package used by ProfileEvent, replace "sealed interface ProfileAction" with
"sealed class ProfileAction", convert the singleton cases (currently declared as
"data object" like OnNavigateBackClick, OnLogoutClick, OnLogoutConfirmClick,
OnLogoutDismiss, OnHelpClick) to plain "object" singletons, and keep the
parameterized entries as data classes (OnThemeColorSelected,
OnAmoledThemeToggled, OnDarkThemeChange, OnFontThemeSelected); ensure the type
names (ProfileAction, OnNavigateBackClick, OnThemeColorSelected, etc.) remain
unchanged.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt`:
- Around line 1-14: The package declaration at the top of ProfileState.kt is
missing the feature segment; update the package to match the project's package
pattern and the sibling file (same as ProfileEvent.kt) so ProfileState uses
zed.rainxch.feature.profile.presentation; locate the ProfileState data class and
change only the package line to include "feature" so the file follows
zed.rainxch.{module}.{layer} organization.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt`:
- Line 1: The package declaration in ProfileViewModel.kt is missing the required
feature segment; update the package line to match the project's package pattern
used by ProfileEvent.kt (zed.rainxch.feature.profile.presentation) so the file
sits under zed.rainxch.feature.profile.presentation and aligns with the
zed.rainxch.{module}.{layer} convention; update the package declaration at the
top of ProfileViewModel.kt (and any imports/usages if necessary) to the correct
package name.

---

Nitpick comments:
In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt`:
- Around line 1-6: Change the package declaration to
"zed.rainxch.feature.profile.presentation" and convert the sealed interface
ProfileEvent into a sealed class; update its members so the success variant is
an object (OnLogoutSuccessful) and the error variant remains a data class
(OnLogoutError) inheriting from ProfileEvent (use the class-style inheritance
syntax), ensuring constructors and inheritance use the sealed class pattern.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 39e788f and e08a4c1.

📒 Files selected for processing (31)
  • composeApp/build.gradle.kts
  • composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/ViewModelsModule.kt
  • composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/AppNavigation.kt
  • composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/BottomNavigationUtils.kt
  • composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/GithubStoreGraph.kt
  • composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/NavigationUtils.kt
  • core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/3.json
  • feature/profile/data/.gitignore
  • feature/profile/data/build.gradle.kts
  • feature/profile/data/src/androidMain/AndroidManifest.xml
  • feature/profile/data/src/commonMain/kotlin/zed/rainxch/settings/data/di/SharedModule.kt
  • feature/profile/data/src/commonMain/kotlin/zed/rainxch/settings/data/repository/SettingsRepositoryImpl.kt
  • feature/profile/domain/.gitignore
  • feature/profile/domain/build.gradle.kts
  • feature/profile/domain/src/androidMain/AndroidManifest.xml
  • feature/profile/domain/src/commonMain/kotlin/zed/rainxch/settings/domain/repository/SettingsRepository.kt
  • feature/profile/presentation/.gitignore
  • feature/profile/presentation/build.gradle.kts
  • feature/profile/presentation/src/androidMain/AndroidManifest.xml
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/LogoutDialog.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/About.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Account.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt
  • feature/settings/presentation/src/commonMain/kotlin/zed/rainxch/settings/presentation/SettingsAction.kt
  • feature/settings/presentation/src/commonMain/kotlin/zed/rainxch/settings/presentation/SettingsEvent.kt
  • settings.gradle.kts
💤 Files with no reviewable changes (2)
  • feature/settings/presentation/src/commonMain/kotlin/zed/rainxch/settings/presentation/SettingsEvent.kt
  • feature/settings/presentation/src/commonMain/kotlin/zed/rainxch/settings/presentation/SettingsAction.kt

…ile UI

This commit refactors the "Settings" feature into "Profile", including renaming repositories, data models, and UI components. It also introduces a new user profile section in the UI and updates translations across multiple languages.

- **refactor**: Renamed `SettingsRepository` to `ProfileRepository` and updated its implementation and DI modules.
- **feat(profile)**: Added `UserProfile` domain model and `accountSection` to display user information (avatar, etc.) in `ProfileRoot`.
- **feat(profile)**: Reorganized `ProfileRoot` into modular sections: `profile`, `settings`, `about`, and `logout`.
- **feat(i18n)**: Renamed `settings_title` to `profile_title` and updated translations for `ru`, `tr`, `bn`, `zh-rCN`, `es`, `fr`, `it`, `hi`, `ja`, `kr`, and `pl`.
- **refactor(presentation)**: Created `GitHubStoreImage` component to unify image loading logic using Coil and shared it across `RepositoryCard` and profile sections.
- **ui**: Added `SectionHeader` and `SectionTitle` components for consistent layout in the profile screen.
Copy link
Contributor

@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: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/repository/ProfileRepositoryImpl.kt (1)

11-27: ⚠️ Potential issue | 🔴 Critical

getUser() declared in ProfileRepository but never implemented — compile error.

ProfileRepository declares fun getUser(): Flow<UserProfile?> (line 8 of ProfileRepository.kt) but ProfileRepositoryImpl provides no override fun getUser() body. Kotlin will reject this with "Class 'ProfileRepositoryImpl' is not abstract and does not implement abstract member 'getUser'."

A stub implementation is needed at minimum:

🐛 Proposed fix (stub — replace with real data-source call)
+    override fun getUser(): Flow<UserProfile?> {
+        // TODO: fetch from remote/local data source
+        return kotlinx.coroutines.flow.flowOf(null)
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/repository/ProfileRepositoryImpl.kt`
around lines 11 - 27, ProfileRepositoryImpl is missing the required override for
fun getUser(): Flow<UserProfile?> declared in ProfileRepository, causing a
compile error; add an override fun getUser(): Flow<UserProfile?> to
ProfileRepositoryImpl that returns a Flow (at minimum a stub Flow emitting null)
and ensure it is executed on the IO dispatcher (e.g., use flow { ...
}.flowOn(Dispatchers.IO)) or map an existing authenticationState/TokenStore flow
to UserProfile as appropriate; reference the class ProfileRepositoryImpl, the
method getUser(), the interface ProfileRepository and the UserProfile type when
making the change.
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/LogoutDialog.kt (1)

71-83: ⚠️ Potential issue | 🟡 Minor

Logout button text color overrides the button's contentColor, risking contrast issues.

The Button sets contentColor = MaterialTheme.colorScheme.onErrorContainer (appropriate for an errorContainer background), but the Text inside explicitly sets color = MaterialTheme.colorScheme.onSurface, discarding that. In themes where onSurface has insufficient contrast against errorContainer, the label becomes hard to read.

🎨 Proposed fix
 Button(
     onClick = onLogout,
     colors = ButtonDefaults.buttonColors(
         containerColor = MaterialTheme.colorScheme.errorContainer,
         contentColor = MaterialTheme.colorScheme.onErrorContainer
     )
 ) {
     Text(
         text = stringResource(Res.string.logout),
         style = MaterialTheme.typography.bodyMedium,
-        color = MaterialTheme.colorScheme.onSurface
     )
 }

Removing the explicit color lets the button's contentColor propagate correctly through LocalContentColor.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/LogoutDialog.kt`
around lines 71 - 83, The Text inside the logout Button is overriding the
Button's contentColor and breaking contrast; in LogoutDialog.kt remove the
explicit color usage on the Text so it inherits the Button's contentColor (set
via ButtonDefaults.buttonColors) instead—locate the Button(...) block where
onClick = onLogout and the nested Text(...) and delete the color =
MaterialTheme.colorScheme.onSurface property so LocalContentColor drives the
label color.
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt (1)

27-35: ⚠️ Potential issue | 🟠 Major

userProfile is never populated — profileRepository.getUser() is not called anywhere.

The onStart block only invokes loadCurrentTheme(), collectIsUserLoggedIn(), and loadVersionName(). There is no corresponding call to load the authenticated user's profile, so ProfileState.userProfile will always stay null. The AccountSection has an explicit null check for this field and is designed to display the user's avatar when populated — but that path is unreachable.

Add a loadUserProfile() function and call it from the initialization block:

🐛 Proposed fix
 if (!hasLoadedInitialData) {
     loadCurrentTheme()
     collectIsUserLoggedIn()
     loadVersionName()
+    loadUserProfile()
     hasLoadedInitialData = true
 }
+private fun loadUserProfile() {
+    viewModelScope.launch {
+        profileRepository.getUser()?.let { user ->
+            _state.update { it.copy(userProfile = user) }
+        }
+    }
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt`
around lines 27 - 35, The ProfileState.userProfile is never set because
profileRepository.getUser() is not invoked; add a loadUserProfile() suspending
function in ProfileViewModel that calls profileRepository.getUser() and updates
the state (ProfileState.userProfile) accordingly, then call loadUserProfile()
inside the existing onStart block alongside loadCurrentTheme(),
collectIsUserLoggedIn(), and loadVersionName() (guarded by hasLoadedInitialData)
so the AccountSection can see the populated userProfile.
♻️ Duplicate comments (1)
composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/initKoin.kt (1)

34-34: settingsModule identifier is stale — see the SharedModule.kt comment.

Once SharedModule.kt renames the variable to profileModule, this reference must be updated to match.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/initKoin.kt`
at line 34, Update the stale identifier reference: replace the use of
settingsModule in the Koin initialization list with the new name profileModule
to match the renaming in SharedModule.kt; locate the registration site (e.g.,
the initKoin or module list where settingsModule is referenced) and change that
symbol to profileModule so the DI module resolves correctly.
🧹 Nitpick comments (6)
core/presentation/src/commonMain/composeResources/values-kr/strings-kr.xml (1)

105-105: profile_title, profile (line 178), and bottom_nav_profile_title (line 335) all share the same Korean value "프로필".

While linguistically correct, the value collision across three distinct keys can silently mislead future translators who update one entry and expect the others to differ. Adding a brief distinguishing comment next to each key (e.g., <!-- Screen title -->, <!-- Author label -->, <!-- Bottom nav label -->) would make the intent clear.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/presentation/src/commonMain/composeResources/values-kr/strings-kr.xml`
at line 105, The three Korean string resources profile_title, profile, and
bottom_nav_profile_title currently share the same value "프로필"; add short XML
comments next to each <string> entry to clarify intent (for example: <!-- Screen
title --> next to profile_title, <!-- Author label --> next to profile, and <!--
Bottom nav label --> next to bottom_nav_profile_title) so future translators
know the distinct usage contexts; update the entries for profile_title, profile,
and bottom_nav_profile_title accordingly.
feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/repository/ProfileRepository.kt (1)

9-9: getVersionName() is a minor SRP concern — consider a dedicated abstraction.

Retrieving the app build version is unrelated to user profile state. A thin AppInfoRepository or a simple top-level function in core.domain would be a cleaner home for this. Low priority since this predates the PR, but worth tracking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/repository/ProfileRepository.kt`
at line 9, The ProfileRepository interface currently exposes getVersionName(),
which breaks SRP; remove getVersionName() from ProfileRepository and introduce a
dedicated abstraction (e.g., an AppInfoRepository interface with fun
getVersionName(): String or a top-level function in core.domain) to own
build/version retrieval; update all callers to depend on the new
AppInfoRepository (or call the top-level function) and implement
platform-specific providers where ProfileRepository was previously implemented,
ensuring you delete getVersionName() from ProfileRepository and wire the new
AppInfoRepository into DI or constructor parameters in places that need the
version.
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/AccountSection.kt (2)

55-65: AccountSectionPreview is public — preview composables should be private.

♻️ Proposed fix
 `@Preview`(showBackground = true)
 `@Composable`
-fun AccountSectionPreview() {
+private fun AccountSectionPreview() {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/AccountSection.kt`
around lines 55 - 65, The preview composable AccountSectionPreview is declared
public but should be private; change its visibility to private (private fun
AccountSectionPreview()) so the preview is not exposed as part of the public
API, leaving the body (GithubStoreTheme, LazyColumn, accountSection call with
ProfileState and onAction) unchanged.

25-27: onAction parameter is accepted but never used.

The current implementation has no interactive elements in the account section, so onAction is dead. Either remove it (and update callers) or add a //TODO: wire onAction when profile tap/edit is implemented comment to signal intent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/AccountSection.kt`
around lines 25 - 27, The accountSection function currently accepts an unused
onAction parameter (ProfileAction) which is dead code; either remove the
onAction parameter from accountSection(state: ProfileState, onAction:
(ProfileAction) -> Unit) and update all callers to stop passing a handler, or
keep the parameter but add a clear TODO comment inside accountSection (e.g., "//
TODO: wire onAction when profile tap/edit is implemented") so intent is
recorded; if you choose removal, also update any usages to the new signature and
adjust imports/types referencing ProfileAction accordingly.
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/SettingsSection.kt (1)

1-15: SettingsSection.kt / settings() naming is inconsistent with the module-rename goal.

This PR's stated objective is to move away from the "Settings" naming entirely, yet the file is still named SettingsSection.kt and the extension function is still settings(). This is jarring inside the profile module.

Consider either:

  • Renaming the file to PreferencesSection.kt (or AppearanceSection.kt) and the function to preferences() / appearanceSection() if this only ever wraps appearance, or
  • Keeping the settings() name but renaming the file to ProfileSettingsSection.kt to make it clear it is the settings sub-section of the profile screen.

ProfileRoot.kt calls settings(state, onAction) at line 134, which should be updated to match.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/SettingsSection.kt`
around lines 1 - 15, The file and function still use "Settings" although the
module is being renamed away from that term; rename the extension function
settings() and/or the file to match the new naming convention and update its
callers: either rename the file SettingsSection.kt -> PreferencesSection.kt (or
AppearanceSection.kt) and change the function name to preferences() (or
appearanceSection()) if it only wraps appearanceSection(...), or keep the
broader name and rename the file to ProfileSettingsSection.kt; then update the
call in ProfileRoot.kt (where settings(state, onAction) is invoked) to the new
function name so imports and references compile.
feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt (1)

165-192: Local TopAppBar function shadows the imported Material3 TopAppBar — consider renaming.

The private function TopAppBar(onAction: (ProfileAction) -> Unit) has the same name as the imported androidx.compose.material3.TopAppBar. It works because the signatures are distinct, but the shadowing creates readability confusion — especially when TopAppBar(navigationIcon = ..., title = ...) inside the function body resolves to M3's API only by signature matching. A name like ProfileTopAppBar or TopBar removes the ambiguity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt`
around lines 165 - 192, The private Composable function TopAppBar(onAction:
(ProfileAction) -> Unit) shadows the imported
androidx.compose.material3.TopAppBar and should be renamed (e.g.,
ProfileTopAppBar or TopBar) to remove ambiguity; change the function name
declaration (TopAppBar) to the new name, keep the same parameters and
annotations (`@OptIn`, `@Composable`), update all call sites that invoke
TopAppBar(...) to use the new name, and verify the internal call to Material3's
TopAppBar(navigationIcon = ..., title = ...) remains unchanged so it resolves to
the library component.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml`:
- Around line 106-107: The XML section comment is stale: replace the comment
text <!-- Settings --> with <!-- Profile --> so it accurately reflects the
section that contains the profile string (look for the string resource named
"profile_title") to avoid misleading future contributors; update the comment
directly above or near the <string name="profile_title"> entry.

In `@core/presentation/src/commonMain/composeResources/values-kr/strings-kr.xml`:
- Around line 104-105: Update the stale XML section comment so it accurately
describes the key: replace the comment string "<!-- Settings -->" with "<!--
Profile -->" near the localized string entry for profile_title to match the
renamed key (string name="profile_title") and avoid misleading section headers.

In
`@core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/GitHubStoreImage.kt`:
- Around line 44-49: The Icon currently uses contentDescription = null which
hides the failure state from assistive tech; update the Icon in
GitHubStoreImage.kt to provide a meaningful, localized content description
(e.g., use stringResource(R.string.image_load_failed) or an appropriate platform
string accessor) instead of null so screen readers announce the image-load
failure; ensure the string resource key (e.g., image_load_failed) is added to
your i18n resources and referenced via stringResource(...) in the composable.

In
`@feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/di/SharedModule.kt`:
- Around line 7-14: Rename the stale module identifier settingsModule to
profileModule in SharedModule.kt and update its references where consumed (e.g.,
the consumer in initKoin.kt) so the Koin module name matches the feature; modify
the declaration val settingsModule = module { ... } to val profileModule =
module { ... } and replace any usages of settingsModule with profileModule
(notably the consumer call in initKoin.kt) to complete the settings→profile
rename.

In
`@feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/model/UserProfile.kt`:
- Around line 3-12: The UserProfile data class currently declares name and bio
as non-nullable Strings which will fail when GitHub returns null; update the
UserProfile class (data class UserProfile) to make the name and bio properties
nullable (String?), adjust any callers or deserialization expectations to handle
null values safely (e.g., defaulting or null checks), and run tests to ensure
serialization/deserialization and downstream code handle the nullable types.
- Line 4: The UserProfile data model declares val id: Int which can overflow for
GitHub 64-bit IDs; change the property type to Long in the UserProfile class
(update any related constructors, equals/hashCode/serializers, and usages that
construct or parse UserProfile) so id is stored and propagated as a Long to
avoid truncation.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/AccountSection.kt`:
- Around line 35-47: The current GitHubStoreImage call passes
Icons.Outlined.AccountCircle (an ImageVector) as imageModel which Coil cannot
load; update AccountSection so you branch on state.userProfile before invoking
GitHubStoreImage: if state.userProfile == null render a plain Compose
Icon(Icons.Outlined.AccountCircle, ...) with the same
Modifier/clip/size/background, otherwise call GitHubStoreImage with imageModel =
state.userProfile.imageUrl; ensure you remove passing ImageVector into
GitHubStoreImage and keep UI modifiers consistent between the two branches.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt`:
- Around line 133-138: In ProfileViewModel's logout failure handler (the
onFailure after calling profileRepository.logout()), don't drop errors with null
messages: always emit a ProfileEvent.OnLogoutError via _events.send and close
the dialog via _state.update; if error.message is null/blank, send a fallback
message like "Logout failed" (or a localized equivalent) so the user gets
feedback; optionally also log the throwable for diagnostics.

---

Outside diff comments:
In
`@feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/repository/ProfileRepositoryImpl.kt`:
- Around line 11-27: ProfileRepositoryImpl is missing the required override for
fun getUser(): Flow<UserProfile?> declared in ProfileRepository, causing a
compile error; add an override fun getUser(): Flow<UserProfile?> to
ProfileRepositoryImpl that returns a Flow (at minimum a stub Flow emitting null)
and ensure it is executed on the IO dispatcher (e.g., use flow { ...
}.flowOn(Dispatchers.IO)) or map an existing authenticationState/TokenStore flow
to UserProfile as appropriate; reference the class ProfileRepositoryImpl, the
method getUser(), the interface ProfileRepository and the UserProfile type when
making the change.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/LogoutDialog.kt`:
- Around line 71-83: The Text inside the logout Button is overriding the
Button's contentColor and breaking contrast; in LogoutDialog.kt remove the
explicit color usage on the Text so it inherits the Button's contentColor (set
via ButtonDefaults.buttonColors) instead—locate the Button(...) block where
onClick = onLogout and the nested Text(...) and delete the color =
MaterialTheme.colorScheme.onSurface property so LocalContentColor drives the
label color.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt`:
- Around line 27-35: The ProfileState.userProfile is never set because
profileRepository.getUser() is not invoked; add a loadUserProfile() suspending
function in ProfileViewModel that calls profileRepository.getUser() and updates
the state (ProfileState.userProfile) accordingly, then call loadUserProfile()
inside the existing onStart block alongside loadCurrentTheme(),
collectIsUserLoggedIn(), and loadVersionName() (guarded by hasLoadedInitialData)
so the AccountSection can see the populated userProfile.

---

Duplicate comments:
In `@composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/initKoin.kt`:
- Line 34: Update the stale identifier reference: replace the use of
settingsModule in the Koin initialization list with the new name profileModule
to match the renaming in SharedModule.kt; locate the registration site (e.g.,
the initKoin or module list where settingsModule is referenced) and change that
symbol to profileModule so the DI module resolves correctly.

---

Nitpick comments:
In `@core/presentation/src/commonMain/composeResources/values-kr/strings-kr.xml`:
- Line 105: The three Korean string resources profile_title, profile, and
bottom_nav_profile_title currently share the same value "프로필"; add short XML
comments next to each <string> entry to clarify intent (for example: <!-- Screen
title --> next to profile_title, <!-- Author label --> next to profile, and <!--
Bottom nav label --> next to bottom_nav_profile_title) so future translators
know the distinct usage contexts; update the entries for profile_title, profile,
and bottom_nav_profile_title accordingly.

In
`@feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/repository/ProfileRepository.kt`:
- Line 9: The ProfileRepository interface currently exposes getVersionName(),
which breaks SRP; remove getVersionName() from ProfileRepository and introduce a
dedicated abstraction (e.g., an AppInfoRepository interface with fun
getVersionName(): String or a top-level function in core.domain) to own
build/version retrieval; update all callers to depend on the new
AppInfoRepository (or call the top-level function) and implement
platform-specific providers where ProfileRepository was previously implemented,
ensuring you delete getVersionName() from ProfileRepository and wire the new
AppInfoRepository into DI or constructor parameters in places that need the
version.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/AccountSection.kt`:
- Around line 55-65: The preview composable AccountSectionPreview is declared
public but should be private; change its visibility to private (private fun
AccountSectionPreview()) so the preview is not exposed as part of the public
API, leaving the body (GithubStoreTheme, LazyColumn, accountSection call with
ProfileState and onAction) unchanged.
- Around line 25-27: The accountSection function currently accepts an unused
onAction parameter (ProfileAction) which is dead code; either remove the
onAction parameter from accountSection(state: ProfileState, onAction:
(ProfileAction) -> Unit) and update all callers to stop passing a handler, or
keep the parameter but add a clear TODO comment inside accountSection (e.g., "//
TODO: wire onAction when profile tap/edit is implemented") so intent is
recorded; if you choose removal, also update any usages to the new signature and
adjust imports/types referencing ProfileAction accordingly.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/SettingsSection.kt`:
- Around line 1-15: The file and function still use "Settings" although the
module is being renamed away from that term; rename the extension function
settings() and/or the file to match the new naming convention and update its
callers: either rename the file SettingsSection.kt -> PreferencesSection.kt (or
AppearanceSection.kt) and change the function name to preferences() (or
appearanceSection()) if it only wraps appearanceSection(...), or keep the
broader name and rename the file to ProfileSettingsSection.kt; then update the
call in ProfileRoot.kt (where settings(state, onAction) is invoked) to the new
function name so imports and references compile.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt`:
- Around line 165-192: The private Composable function TopAppBar(onAction:
(ProfileAction) -> Unit) shadows the imported
androidx.compose.material3.TopAppBar and should be renamed (e.g.,
ProfileTopAppBar or TopBar) to remove ambiguity; change the function name
declaration (TopAppBar) to the new name, keep the same parameters and
annotations (`@OptIn`, `@Composable`), update all call sites that invoke
TopAppBar(...) to use the new name, and verify the internal call to Material3's
TopAppBar(navigationIcon = ..., title = ...) remains unchanged so it resolves to
the library component.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e08a4c1 and 709e914.

📒 Files selected for processing (30)
  • composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/di/initKoin.kt
  • core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml
  • core/presentation/src/commonMain/composeResources/values-es/strings-es.xml
  • core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml
  • core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml
  • core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
  • core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml
  • core/presentation/src/commonMain/composeResources/values-kr/strings-kr.xml
  • core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml
  • core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
  • core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml
  • core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml
  • core/presentation/src/commonMain/composeResources/values/strings.xml
  • core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/GitHubStoreImage.kt
  • core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/RepositoryCard.kt
  • feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/di/SharedModule.kt
  • feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/repository/ProfileRepositoryImpl.kt
  • feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/model/UserProfile.kt
  • feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/repository/ProfileRepository.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/LogoutDialog.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/SectionText.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/About.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Account.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/AccountSection.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/ProfileSection.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/SettingsSection.kt
✅ Files skipped from review due to trivial changes (1)
  • core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
🚧 Files skipped from review as they are similar to previous changes (2)
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Account.kt
  • feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/About.kt

Comment on lines 106 to +107
<!-- Settings -->
<string name="settings_title">সেটিংস</string>
<string name="profile_title">প্রোফাইল</string>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Stale section comment — update <!-- Settings --> to <!-- Profile -->.

The XML comment on Line 106 still reads Settings, but the only key in this section is now profile_title. This will mislead future contributors looking for where to add profile-related strings.

✏️ Proposed fix
-    <!-- Settings -->
+    <!-- Profile -->
     <string name="profile_title">প্রোফাইল</string>
📝 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
<!-- Settings -->
<string name="settings_title">সেটিংস</string>
<string name="profile_title">প্রোফাইল</string>
<!-- Profile -->
<string name="profile_title">প্রোফাইল</string>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml`
around lines 106 - 107, The XML section comment is stale: replace the comment
text <!-- Settings --> with <!-- Profile --> so it accurately reflects the
section that contains the profile string (look for the string resource named
"profile_title") to avoid misleading future contributors; update the comment
directly above or near the <string name="profile_title"> entry.

Comment on lines 104 to +105
<!-- Settings -->
<string name="settings_title">설정</string>
<string name="profile_title">프로필</string>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Stale section comment — update <!-- Settings --> to <!-- Profile -->.

The comment on line 104 still reads <!-- Settings --> but the key has been renamed to profile_title, making the comment misleading.

✏️ Proposed fix
-    <!-- Settings -->
+    <!-- Profile -->
     <string name="profile_title">프로필</string>
📝 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
<!-- Settings -->
<string name="settings_title">설정</string>
<string name="profile_title">프로필</string>
<!-- Profile -->
<string name="profile_title">프로필</string>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/presentation/src/commonMain/composeResources/values-kr/strings-kr.xml`
around lines 104 - 105, Update the stale XML section comment so it accurately
describes the key: replace the comment string "<!-- Settings -->" with "<!--
Profile -->" near the localized string entry for profile_title to match the
renamed key (string name="profile_title") and avoid misleading section headers.

Comment on lines +44 to +49
Icon(
imageVector = Icons.Default.Warning,
contentDescription = null,
tint = MaterialTheme.colorScheme.error,
modifier = Modifier.fillMaxSize(.5f)
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Failure icon needs a meaningful contentDescription for accessibility.

contentDescription = null marks the icon as decorative and makes it invisible to screen readers. Since this icon is the sole indicator of an image-load failure, users relying on accessibility services receive no feedback about the error state.

♿ Proposed fix
-                Icon(
-                    imageVector = Icons.Default.Warning,
-                    contentDescription = null,
-                    tint = MaterialTheme.colorScheme.error,
-                    modifier = Modifier.fillMaxSize(.5f)
-                )
+                Icon(
+                    imageVector = Icons.Default.Warning,
+                    contentDescription = "Image failed to load",
+                    tint = MaterialTheme.colorScheme.error,
+                    modifier = Modifier.fillMaxSize(.5f)
+                )

Ideally, source this string from a string resource for proper localization.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/GitHubStoreImage.kt`
around lines 44 - 49, The Icon currently uses contentDescription = null which
hides the failure state from assistive tech; update the Icon in
GitHubStoreImage.kt to provide a meaningful, localized content description
(e.g., use stringResource(R.string.image_load_failed) or an appropriate platform
string accessor) instead of null so screen readers announce the image-load
failure; ensure the string resource key (e.g., image_load_failed) is added to
your i18n resources and referenced via stringResource(...) in the composable.

Comment on lines +7 to +14
val settingsModule = module {
single<ProfileRepository> {
ProfileRepositoryImpl(
authenticationState = get(),
tokenStore = get()
)
}
} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

settingsModule is a leftover name — should be profileModule.

The module is in zed.rainxch.profile.data.di and provides ProfileRepository, yet it's still named settingsModule. This is a core part of the PR's stated goal (rename settings → profile) that was missed. The same stale identifier is consumed in initKoin.kt at line 34.

♻️ Proposed fix

SharedModule.kt:

-val settingsModule = module {
+val profileModule = module {
     single<ProfileRepository> {
         ProfileRepositoryImpl(
             authenticationState = get(),
             tokenStore = get()
         )
     }
 }

initKoin.kt:

-import zed.rainxch.profile.data.di.settingsModule
+import zed.rainxch.profile.data.di.profileModule
 ...
-        settingsModule
+        profileModule
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/data/src/commonMain/kotlin/zed/rainxch/profile/data/di/SharedModule.kt`
around lines 7 - 14, Rename the stale module identifier settingsModule to
profileModule in SharedModule.kt and update its references where consumed (e.g.,
the consumer in initKoin.kt) so the Koin module name matches the feature; modify
the declaration val settingsModule = module { ... } to val profileModule =
module { ... } and replace any usages of settingsModule with profileModule
(notably the consumer call in initKoin.kt) to complete the settings→profile
rename.

Comment on lines +3 to +12
data class UserProfile(
val id: Int,
val imageUrl: String,
val name: String,
val username: String,
val bio: String,
val repositoryCount: Int,
val followers: Int,
val following: Int,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

name and bio should be nullable to match the GitHub API contract.

The GitHub REST API returns null for both name (user's display name) and bio when not set. Non-nullable String fields here will cause a NullPointerException (or deserialization failure) for users who haven't filled in these fields.

🐛 Proposed fix
 data class UserProfile(
     val id: Int,
     val imageUrl: String,
-    val name: String,
+    val name: String?,
     val username: String,
-    val bio: String,
+    val bio: String?,
     val repositoryCount: Int,
     val followers: Int,
     val following: Int,
 )
📝 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
data class UserProfile(
val id: Int,
val imageUrl: String,
val name: String,
val username: String,
val bio: String,
val repositoryCount: Int,
val followers: Int,
val following: Int,
)
data class UserProfile(
val id: Int,
val imageUrl: String,
val name: String?,
val username: String,
val bio: String?,
val repositoryCount: Int,
val followers: Int,
val following: Int,
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/model/UserProfile.kt`
around lines 3 - 12, The UserProfile data class currently declares name and bio
as non-nullable Strings which will fail when GitHub returns null; update the
UserProfile class (data class UserProfile) to make the name and bio properties
nullable (String?), adjust any callers or deserialization expectations to handle
null values safely (e.g., defaulting or null checks), and run tests to ensure
serialization/deserialization and downstream code handle the nullable types.

package zed.rainxch.profile.domain.model

data class UserProfile(
val id: Int,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Prefer Long for id to avoid potential overflow.

GitHub user IDs are effectively unbounded integers. The GitHub REST API spec documents id as a 64-bit integer. Using Int (max ≈ 2.1 B) risks silent truncation for high-numbered IDs. GitHub's own Kotlin/JVM clients (e.g., the official Octokit) use Long for all entity IDs.

🛡️ Proposed fix
 data class UserProfile(
-    val id: Int,
+    val id: Long,
     ...
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/domain/src/commonMain/kotlin/zed/rainxch/profile/domain/model/UserProfile.kt`
at line 4, The UserProfile data model declares val id: Int which can overflow
for GitHub 64-bit IDs; change the property type to Long in the UserProfile class
(update any related constructors, equals/hashCode/serializers, and usages that
construct or parse UserProfile) so id is stored and propagated as a Long to
avoid truncation.

Comment on lines +35 to +47
GitHubStoreImage(
imageModel = {
if (state.userProfile == null) {
Icons.Outlined.AccountCircle
} else {
state.userProfile.imageUrl
}
},
modifier = Modifier
.size(100.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.surfaceContainerHigh)
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the AccountSection.kt file
fd -t f "AccountSection.kt"

Repository: rainxchzed/Github-Store

Length of output: 188


🏁 Script executed:

# Find GitHubStoreImage implementation
fd -t f ".*Image.*\.kt" | head -20

Repository: rainxchzed/Github-Store

Length of output: 285


🏁 Script executed:

# Search for GitHubStoreImage definition
rg -t kt "GitHubStoreImage" --max-count=20

Repository: rainxchzed/Github-Store

Length of output: 93


🏁 Script executed:

cat -n "feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/AccountSection.kt"

Repository: rainxchzed/Github-Store

Length of output: 2708


🏁 Script executed:

cat -n "core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/GitHubStoreImage.kt"

Repository: rainxchzed/Github-Store

Length of output: 2379


🏁 Script executed:

# Find build files to check Coil3 version
fd -t f "build\.gradle.*|gradle\.kts|gradle\.properties" | head -10

Repository: rainxchzed/Github-Store

Length of output: 376


🏁 Script executed:

# Search for other GitHubStoreImage usages to understand the pattern
rg "GitHubStoreImage" -A 5 -B 2

Repository: rainxchzed/Github-Store

Length of output: 4910


🏁 Script executed:

# Check if there's any special handling or importing of imageModel types
rg "imageModel" -A 3 -B 3

Repository: rainxchzed/Github-Store

Length of output: 10138


🏁 Script executed:

# Check composeApp build file for Coil3 version
cat "composeApp/build.gradle.kts"

Repository: rainxchzed/Github-Store

Length of output: 6096


🏁 Script executed:

# Look for any gradle properties or version catalogs
cat "build-logic/gradle.properties" 2>/dev/null || echo "File not found"

Repository: rainxchzed/Github-Store

Length of output: 149


🏁 Script executed:

# Find the version catalog
fd -t f "libs\.versions\.toml|version.*\.toml"

Repository: rainxchzed/Github-Store

Length of output: 92


🏁 Script executed:

# Also check in gradle directory
find . -name "*.toml" -o -name "libs.gradle.kts"

Repository: rainxchzed/Github-Store

Length of output: 94


🏁 Script executed:

cat "gradle/libs.versions.toml"

Repository: rainxchzed/Github-Store

Length of output: 12491


ImageVector is not a Coil-supported imageModel type — the fallback avatar will render the failure state instead of AccountCircle.

Coil3 is designed to load images from URLs, files, or resource identifiers, not Compose drawing primitives like ImageVector. When state.userProfile == null, passing Icons.Outlined.AccountCircle to CoilImage will fail, and GitHubStoreImage's failure composable (a Warning icon) will render instead of the intended account circle placeholder.

Fix by branching on userProfile before reaching GitHubStoreImage, using a plain Icon for the null case:

🐛 Proposed fix
-GitHubStoreImage(
-    imageModel = {
-        if (state.userProfile == null) {
-            Icons.Outlined.AccountCircle
-        } else {
-            state.userProfile.imageUrl
-        }
-    },
-    modifier = Modifier
-        .size(100.dp)
-        .clip(CircleShape)
-        .background(MaterialTheme.colorScheme.surfaceContainerHigh)
-)
+if (state.userProfile != null) {
+    GitHubStoreImage(
+        imageModel = { state.userProfile.imageUrl },
+        modifier = Modifier
+            .size(100.dp)
+            .clip(CircleShape)
+            .background(MaterialTheme.colorScheme.surfaceContainerHigh)
+    )
+} else {
+    Icon(
+        imageVector = Icons.Outlined.AccountCircle,
+        contentDescription = null,
+        modifier = Modifier
+            .size(100.dp)
+            .clip(CircleShape)
+            .background(MaterialTheme.colorScheme.surfaceContainerHigh),
+        tint = MaterialTheme.colorScheme.onSurfaceVariant
+    )
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/AccountSection.kt`
around lines 35 - 47, The current GitHubStoreImage call passes
Icons.Outlined.AccountCircle (an ImageVector) as imageModel which Coil cannot
load; update AccountSection so you branch on state.userProfile before invoking
GitHubStoreImage: if state.userProfile == null render a plain Compose
Icon(Icons.Outlined.AccountCircle, ...) with the same
Modifier/clip/size/background, otherwise call GitHubStoreImage with imageModel =
state.userProfile.imageUrl; ensure you remove passing ImageVector into
GitHubStoreImage and keep UI modifiers consistent between the two branches.

Comment on lines 133 to 138
}.onFailure { error ->
_state.update { it.copy(isLogoutDialogVisible = false) }
error.message?.let {
_events.send(SettingsEvent.OnLogoutError(it))
_events.send(ProfileEvent.OnLogoutError(it))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Logout error is silently swallowed when error.message is null.

If profileRepository.logout() throws an exception with a null message, the onFailure block closes the dialog but emits no event, leaving the user with no feedback.

🐛 Proposed fix
 .onFailure { error ->
     _state.update { it.copy(isLogoutDialogVisible = false) }
-    error.message?.let {
-        _events.send(ProfileEvent.OnLogoutError(it))
-    }
+    _events.send(ProfileEvent.OnLogoutError(error.message ?: "Unknown 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
}.onFailure { error ->
_state.update { it.copy(isLogoutDialogVisible = false) }
error.message?.let {
_events.send(SettingsEvent.OnLogoutError(it))
_events.send(ProfileEvent.OnLogoutError(it))
}
}
}.onFailure { error ->
_state.update { it.copy(isLogoutDialogVisible = false) }
_events.send(ProfileEvent.OnLogoutError(error.message ?: "Unknown error"))
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt`
around lines 133 - 138, In ProfileViewModel's logout failure handler (the
onFailure after calling profileRepository.logout()), don't drop errors with null
messages: always emit a ProfileEvent.OnLogoutError via _events.send and close
the dialog via _state.update; if error.message is null/blank, send a fallback
message like "Logout failed" (or a localized equivalent) so the user gets
feedback; optionally also log the throwable for diagnostics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant