Browse Source

fix(http): fix http request

zhaoyadi 1 year ago
parent
commit
4352e8c601
49 changed files with 797 additions and 148 deletions
  1. 3 11
      app/build.gradle.kts
  2. 1 4
      app/src/main/AndroidManifest.xml
  3. 45 0
      app/src/main/java/com/zaojiao/app/EntryActivity.kt
  4. 32 0
      app/src/main/java/com/zaojiao/app/navigation/HomeDestination.kt
  5. 40 0
      app/src/main/java/com/zaojiao/app/navigation/NavHost.kt
  6. 0 6
      app/src/main/java/com/zaojiao/app/ui/AdvertiseActivity.kt
  7. 138 0
      app/src/main/java/com/zaojiao/app/ui/App.kt
  8. 82 0
      app/src/main/java/com/zaojiao/app/ui/AppState.kt
  9. 32 32
      app/src/main/java/com/zaojiao/app/ui/home/HomeActivity.kt
  10. 2 1
      app/src/main/java/com/zaojiao/app/ui/home/HomePersonFragment.kt
  11. 0 17
      app/src/main/java/com/zaojiao/app/ui/login/LoginActivity.kt
  12. 0 7
      app/src/main/java/com/zaojiao/app/ui/login/LoginViewModel.kt
  13. 10 0
      built/convention/build.gradle.kts
  14. 10 0
      built/convention/src/main/kotlin/ApplicationConventionPlugin.kt
  15. 28 0
      built/convention/src/main/kotlin/LifecycleConventionPlugin.kt
  16. 22 0
      built/convention/src/main/kotlin/NavigationConventionPlugin.kt
  17. 2 0
      built/convention/src/main/kotlin/com/zaojiao/app/built/convention/AndroidKotlin.kt
  18. 3 1
      core/http/build.gradle.kts
  19. 0 10
      core/http/src/main/java/com/zaojiao/http/api/UserApi.kt
  20. 10 0
      core/http/src/main/java/com/zaojiao/http/common/NetResult.kt
  21. 22 0
      core/http/src/main/java/com/zaojiao/http/converter/ColorAdapter.kt
  22. 37 0
      core/http/src/main/java/com/zaojiao/http/converter/ResultConverterFactory.kt
  23. 25 5
      core/http/src/main/java/com/zaojiao/http/di/HttpModule.kt
  24. 15 0
      core/http/src/main/java/com/zaojiao/http/interceptor/TokenInterceptor.kt
  25. 31 0
      core/http/src/main/java/com/zaojiao/http/interceptor/VersionInterceptor.kt
  26. 4 0
      data/local/build.gradle.kts
  27. 0 27
      data/model/src/main/java/com/zaojiao/app/data/model/Result.kt
  28. 1 1
      data/model/src/main/java/com/zaojiao/app/data/model/UserModel.kt
  29. 2 0
      data/remote/build.gradle.kts
  30. 6 2
      data/remote/src/main/kotlin/com/zaojiao/app/data/remote/RemoteUserData.kt
  31. 12 0
      data/remote/src/main/kotlin/com/zaojiao/app/data/remote/api/UserApi.kt
  32. 2 2
      data/remote/src/main/kotlin/com/zaojiao/app/data/remote/di/ApiModule.kt
  33. 2 0
      data/repo/src/main/kotlin/com/zaojiao/app/data/repo/RepoModule.kt
  34. 4 1
      data/repo/src/main/kotlin/com/zaojiao/app/data/repo/UserRepository.kt
  35. 10 2
      data/repo/src/main/kotlin/com/zaojiao/app/data/repo/UserRepositoryImpl.kt
  36. 4 7
      feat/home/build.gradle.kts
  37. 8 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/course/HomeCoursePage.kt
  38. 3 3
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/course/HomeCourseViewModel.kt
  39. 10 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexPage.kt
  40. 9 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexViewModel.kt
  41. 21 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/navigation/HomeCourseNavigation.kt
  42. 26 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/navigation/HomeIndexNavigation.kt
  43. 25 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/navigation/HomePersonalNavigation.kt
  44. 0 4
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalNavigation.kt
  45. 17 2
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalPage.kt
  46. 6 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalUiState.kt
  47. 4 2
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalUserBar.kt
  48. 28 1
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalViewModel.kt
  49. 3 0
      gradle/libs.versions.toml

+ 3 - 11
app/build.gradle.kts

@@ -1,10 +1,8 @@
-import org.jetbrains.kotlin.gradle.tasks.KaptGenerateStubs
-
 plugins {
     id("d.convention.application")
     id("d.convention.compose")
     id("d.convention.hilt")
-    id("androidx.navigation.safeargs.kotlin")
+    id("d.convention.navigation")
 }
 
 android {
@@ -25,21 +23,15 @@ android {
             )
         }
     }
-
-    buildFeatures {
-        aidl = true
-        viewBinding = true
-    }
 }
 
 dependencies {
     implementation(project(":core:http"))
-
     implementation(project(":feat:home"))
 
+    implementation("androidx.activity:activity-compose:1.6.1")
+
     implementation("androidx.appcompat:appcompat:1.6.1")
-    implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
-    implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
 
     implementation("androidx.constraintlayout:constraintlayout:2.1.4")
     implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0")

+ 1 - 4
app/src/main/AndroidManifest.xml

@@ -12,7 +12,7 @@
         android:theme="@style/Theme.Luojigou">
 
         <activity
-            android:name=".ui.home.HomeActivity"
+            android:name="com.zaojiao.app.EntryActivity"
             android:exported="true"
             android:theme="@style/Theme.Luojigou.LightBar">
             <intent-filter>
@@ -38,9 +38,6 @@
                     android:scheme="route" />
             </intent-filter>
         </activity>
-
-        <activity android:name=".ui.AdvertiseActivity" />
-        <activity android:name="com.zaojiao.app.ui.login.LoginActivity" />
     </application>
 
 </manifest>

+ 45 - 0
app/src/main/java/com/zaojiao/app/EntryActivity.kt

@@ -0,0 +1,45 @@
+package com.zaojiao.app
+
+import android.graphics.Color
+import android.os.Build
+import android.os.Bundle
+import android.view.WindowManager
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.WindowInsetsControllerCompat
+import com.google.accompanist.systemuicontroller.rememberSystemUiController
+import com.zaojiao.app.ui.App
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class EntryActivity : ComponentActivity() {
+    private lateinit var windowInsetsController: WindowInsetsControllerCompat
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        configureSystemBar()
+        setContent {
+            val systemUiController = rememberSystemUiController()
+            MaterialTheme { App() }
+        }
+    }
+
+    private fun configureSystemBar() {
+        WindowCompat.setDecorFitsSystemWindows(window, false)
+        windowInsetsController = WindowInsetsControllerCompat(window, window.decorView)
+
+        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
+        window.statusBarColor = Color.TRANSPARENT
+        window.navigationBarColor = Color.TRANSPARENT
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            window.addFlags(WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
+        }
+        windowInsetsController.apply {
+            show(WindowInsetsCompat.Type.systemBars())
+        }
+    }
+}

+ 32 - 0
app/src/main/java/com/zaojiao/app/navigation/HomeDestination.kt

@@ -0,0 +1,32 @@
+package com.zaojiao.app.navigation
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Add
+import androidx.compose.material.icons.outlined.Home
+import androidx.compose.material.icons.outlined.Person
+import androidx.compose.material.icons.rounded.Add
+import androidx.compose.material.icons.rounded.Home
+import androidx.compose.material.icons.rounded.Person
+import androidx.compose.ui.graphics.vector.ImageVector
+
+enum class HomeDestination(
+    val selectedIcon: ImageVector,
+    val unselectedIcon: ImageVector,
+    val iconTitle: String,
+) {
+    INDEX(
+        selectedIcon = Icons.Rounded.Home,
+        unselectedIcon = Icons.Outlined.Home,
+        iconTitle = "首页",
+    ),
+    COURSE(
+        selectedIcon = Icons.Rounded.Add,
+        unselectedIcon = Icons.Outlined.Add,
+        iconTitle = "成长乐园",
+    ),
+    PERSONAL(
+        selectedIcon = Icons.Rounded.Person,
+        unselectedIcon = Icons.Outlined.Person,
+        iconTitle = "我的勋章",
+    ),
+}

+ 40 - 0
app/src/main/java/com/zaojiao/app/navigation/NavHost.kt

@@ -0,0 +1,40 @@
+package com.zaojiao.app.navigation
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.navigation.compose.NavHost
+import com.zaojiao.app.feat.home.navigation.homeCoursePage
+import com.zaojiao.app.feat.home.navigation.homeIndex
+import com.zaojiao.app.feat.home.navigation.homeIndexPage
+import com.zaojiao.app.feat.home.navigation.homePersonalPage
+import com.zaojiao.app.ui.AppState
+
+/**
+ * Top-level navigation graph. Navigation is organized as explained at
+ * https://d.android.com/jetpack/compose/nav-adaptive
+ *
+ * The navigation graph defined in this file defines the different top level routes. Navigation
+ * within each route is handled using state and Back Handlers.
+ */
+@Composable
+fun NavHost(
+    appState: AppState,
+    modifier: Modifier = Modifier,
+    startDestination: String = homeIndex,
+) {
+    val navController = appState.navController
+    NavHost(
+        navController = navController,
+        startDestination = startDestination,
+        modifier = modifier,
+    ) {
+        homeIndexPage()
+
+        homeCoursePage(
+            onBannerClick = {},
+            onCourseClick = {},
+        )
+
+        homePersonalPage()
+    }
+}

+ 0 - 6
app/src/main/java/com/zaojiao/app/ui/AdvertiseActivity.kt

@@ -1,6 +0,0 @@
-package com.zaojiao.app.ui
-
-import androidx.appcompat.app.AppCompatActivity
-
-class AdvertiseActivity : AppCompatActivity() {
-}

+ 138 - 0
app/src/main/java/com/zaojiao/app/ui/App.kt

@@ -0,0 +1,138 @@
+package com.zaojiao.app.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.consumeWindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.NavigationBar
+import androidx.compose.material3.NavigationBarItem
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SnackbarHost
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.testTagsAsResourceId
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavDestination
+import androidx.navigation.NavDestination.Companion.hierarchy
+import com.zaojiao.app.navigation.HomeDestination
+import com.zaojiao.app.navigation.NavHost
+
+@OptIn(
+    ExperimentalMaterial3Api::class,
+    ExperimentalLayoutApi::class,
+    ExperimentalComposeUiApi::class,
+)
+@Composable
+fun App(
+    appState: AppState = rememberAppState()
+) {
+    val snackbarHostState = remember { SnackbarHostState() }
+
+    Scaffold(
+        modifier = Modifier.semantics {
+            testTagsAsResourceId = true
+        },
+        containerColor = Color.Transparent,
+        contentColor = MaterialTheme.colorScheme.onBackground,
+        contentWindowInsets = WindowInsets(0, 0, 0, 0),
+        snackbarHost = { SnackbarHost(snackbarHostState) },
+        bottomBar = {
+            AppBottomBar(
+                destinations = appState.homeDestinations,
+                onNavigateToDestination = appState::navigateToHomeDestination,
+                currentDestination = appState.currentDestination,
+                modifier = Modifier.testTag("NiaBottomBar"),
+            )
+        },
+    ) { padding ->
+        Box(modifier = Modifier
+            .fillMaxSize()
+            .padding(padding)
+            .consumeWindowInsets(padding)) {
+            NavHost(appState = appState)
+        }
+    }
+}
+
+@Composable
+private fun AppBottomBar(
+    destinations: List<HomeDestination>,
+    onNavigateToDestination: (HomeDestination) -> Unit,
+    currentDestination: NavDestination?,
+    modifier: Modifier = Modifier,
+) {
+    MaterialTheme.colorScheme.onSurfaceVariant
+
+    NavigationBar(
+        modifier = modifier,
+        contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+        tonalElevation = 0.dp,
+    ) {
+        destinations.forEach { destination ->
+            val selected = currentDestination.isTopLevelDestinationInHierarchy(destination)
+
+            NavigationBarItem(
+                selected = selected,
+                onClick = { onNavigateToDestination(destination) },
+                enabled = true,
+                icon = {
+                    if (selected) {
+                        Icon(
+                            imageVector = destination.selectedIcon,
+                            contentDescription = null,
+                        )
+                    } else {
+                        Icon(
+                            imageVector = destination.unselectedIcon,
+                            contentDescription = null,
+                        )
+                    }
+                },
+                alwaysShowLabel = true,
+                label = { Text(text = destination.iconTitle) },
+                modifier = Modifier.notificationDot(),
+            )
+        }
+    }
+}
+
+private fun Modifier.notificationDot(): Modifier =
+    composed {
+        val tertiaryColor = MaterialTheme.colorScheme.tertiary
+        drawWithContent {
+            drawContent()
+            drawCircle(
+                tertiaryColor,
+                radius = 5.dp.toPx(),
+                // This is based on the dimensions of the NavigationBar's "indicator pill";
+                // however, its parameters are private, so we must depend on them implicitly
+                // (NavigationBarTokens.ActiveIndicatorWidth = 64.dp)
+                center = center + Offset(
+                    64.dp.toPx() * .45f,
+                    32.dp.toPx() * -.45f - 6.dp.toPx(),
+                ),
+            )
+        }
+    }
+
+private fun NavDestination?.isTopLevelDestinationInHierarchy(destination: HomeDestination) =
+    this?.hierarchy?.any {
+        it.route?.contains(destination.name, true) ?: false
+    } ?: false

+ 82 - 0
app/src/main/java/com/zaojiao/app/ui/AppState.kt

@@ -0,0 +1,82 @@
+package com.zaojiao.app.ui
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.core.os.trace
+import androidx.navigation.NavDestination
+import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navOptions
+import com.zaojiao.app.navigation.HomeDestination
+import com.zaojiao.app.feat.home.navigation.homeCourse
+import com.zaojiao.app.feat.home.navigation.homeIndex
+import com.zaojiao.app.feat.home.navigation.homePersonal
+import com.zaojiao.app.feat.home.navigation.navigateToHomeCourse
+import com.zaojiao.app.feat.home.navigation.navigateToHomeIndex
+import com.zaojiao.app.feat.home.navigation.navigateToHomePersonal
+import kotlinx.coroutines.CoroutineScope
+
+@Composable
+fun rememberAppState(
+    coroutineScope: CoroutineScope = rememberCoroutineScope(),
+    navController: NavHostController = rememberNavController(),
+): AppState {
+    return remember(
+        navController,
+        coroutineScope,
+    ) {
+        AppState(
+            navController,
+            coroutineScope,
+        )
+    }
+}
+
+@Stable
+class AppState(
+    val navController: NavHostController,
+    val coroutineScope: CoroutineScope,
+) {
+    val currentDestination: NavDestination?
+        @Composable get() = navController
+            .currentBackStackEntryAsState().value?.destination
+
+    val currentHomeDestination: HomeDestination?
+        @Composable get() = when (currentDestination?.route) {
+            homeIndex -> HomeDestination.INDEX
+            homeCourse -> HomeDestination.COURSE
+            homePersonal -> HomeDestination.PERSONAL
+            else -> null
+        }
+
+    val homeDestinations: List<HomeDestination> = HomeDestination.values().asList()
+
+
+    fun navigateToHomeDestination(destination: HomeDestination) {
+        trace("Navigation: ${destination.name}") {
+            val topLevelNavOptions = navOptions {
+                // Pop up to the start destination of the graph to
+                // avoid building up a large stack of destinations
+                // on the back stack as users select items
+                popUpTo(navController.graph.findStartDestination().id) {
+                    saveState = true
+                }
+                // Avoid multiple copies of the same destination when
+                // reselecting the same item
+                launchSingleTop = true
+                // Restore state when reselecting a previously selected item
+                restoreState = true
+            }
+
+            when (destination) {
+                HomeDestination.INDEX -> navController.navigateToHomeIndex(topLevelNavOptions)
+                HomeDestination.COURSE -> navController.navigateToHomeCourse(topLevelNavOptions)
+                HomeDestination.PERSONAL -> navController.navigateToHomePersonal(topLevelNavOptions)
+            }
+        }
+    }
+}

+ 32 - 32
app/src/main/java/com/zaojiao/app/ui/home/HomeActivity.kt

@@ -1,32 +1,32 @@
-package com.zaojiao.app.ui.home
-
-import android.os.Bundle
-import androidx.navigation.findNavController
-import androidx.navigation.fragment.NavHostFragment
-import androidx.navigation.ui.setupWithNavController
-import com.zaojiao.app.R
-import com.zaojiao.app.base.BaseActivity
-import com.zaojiao.app.databinding.ActivityHomeBinding
-import dagger.hilt.android.AndroidEntryPoint
-
-@AndroidEntryPoint
-class HomeActivity : BaseActivity() {
-    private var _binding: ActivityHomeBinding? = null
-    private val binding get() = _binding!!
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-
-        _binding = ActivityHomeBinding.inflate(layoutInflater)
-        setContentView(binding.root)
-
-        val navHostFragment =
-            supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
-        val navController = navHostFragment.navController
-        binding.homeNav.setupWithNavController(navController)
-    }
-
-    override fun onSupportNavigateUp(): Boolean {
-        return findNavController(R.id.nav_host_fragment).navigateUp()
-    }
-}
+//package com.zaojiao.app.ui.home
+//
+//import android.os.Bundle
+//import androidx.navigation.findNavController
+//import androidx.navigation.fragment.NavHostFragment
+//import androidx.navigation.ui.setupWithNavController
+//import com.zaojiao.app.R
+//import com.zaojiao.app.base.BaseActivity
+//import com.zaojiao.app.databinding.ActivityHomeBinding
+//import dagger.hilt.android.AndroidEntryPoint
+//
+//@AndroidEntryPoint
+//class HomeActivity : BaseActivity() {
+//    private var _binding: ActivityHomeBinding? = null
+//    private val binding get() = _binding!!
+//
+//    override fun onCreate(savedInstanceState: Bundle?) {
+//        super.onCreate(savedInstanceState)
+//
+//        _binding = ActivityHomeBinding.inflate(layoutInflater)
+//        setContentView(binding.root)
+//
+//        val navHostFragment =
+//            supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
+//        val navController = navHostFragment.navController
+//        binding.homeNav.setupWithNavController(navController)
+//    }
+//
+//    override fun onSupportNavigateUp(): Boolean {
+//        return findNavController(R.id.nav_host_fragment).navigateUp()
+//    }
+//}

+ 2 - 1
app/src/main/java/com/zaojiao/app/ui/home/HomePersonFragment.kt

@@ -21,7 +21,8 @@ class HomePersonFragment : Fragment() {
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         if (view is ComposeView) {
-            view.setContent { HomePersonalPage() }
+            view.setContent {
+            }
         }
     }
 }

+ 0 - 17
app/src/main/java/com/zaojiao/app/ui/login/LoginActivity.kt

@@ -1,17 +0,0 @@
-package com.zaojiao.app.ui.login
-
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import com.zaojiao.app.LJGApplication
-import okhttp3.OkHttpClient
-import javax.inject.Inject
-
-class LoginActivity : AppCompatActivity() {
-    @Inject
-    lateinit var loginViewModel: LoginViewModel
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-    }
-}

+ 0 - 7
app/src/main/java/com/zaojiao/app/ui/login/LoginViewModel.kt

@@ -1,7 +0,0 @@
-package com.zaojiao.app.ui.login
-
-import javax.inject.Inject
-
-class LoginViewModel @Inject constructor() {
-
-}

+ 10 - 0
built/convention/build.gradle.kts

@@ -44,5 +44,15 @@ gradlePlugin {
             id = "d.convention.hilt"
             implementationClass = "HiltConventionPlugin"
         }
+
+        register("lifecycle") {
+            id = "d.convention.lifecycle"
+            implementationClass = "LifecycleConventionPlugin"
+        }
+
+        register("navigation") {
+            id = "d.convention.navigation"
+            implementationClass = "NavigationConventionPlugin"
+        }
     }
 }

+ 10 - 0
built/convention/src/main/kotlin/ApplicationConventionPlugin.kt

@@ -41,7 +41,17 @@ class ApplicationConventionPlugin : Plugin<Project> {
                     }
 
                     debug {
+                        applicationIdSuffix = ".debug"
+                        isDebuggable = true
+                    }
+                }
+
+                flavorDimensions += "manufacturer"
 
+                productFlavors {
+                    create("xiaomi") {
+                        dimension = "manufacturer"
+                        manifestPlaceholders["privacy"] = "www.xiaomi.com"
                     }
                 }
             }

+ 28 - 0
built/convention/src/main/kotlin/LifecycleConventionPlugin.kt

@@ -0,0 +1,28 @@
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+
+class LifecycleConventionPlugin: Plugin<Project> {
+private    val lifecycle_version = "2.5.1"
+
+    override fun apply(target: Project) {
+        with(target) {
+            with(pluginManager) {
+                apply("org.jetbrains.kotlin.kapt")
+            }
+
+            dependencies {
+                add("implementation","androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
+                add("implementation","androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version")
+                add( "implementation","androidx.navigation:navigation-compose:2.6.0")
+
+//                add("implementation","androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version")
+                add("kapt","androidx.lifecycle:lifecycle-compiler:$lifecycle_version")
+//                add("implementation","androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version")
+
+                add("implementation","androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version")
+                add("implementation","androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version")
+            }
+        }
+    }
+}

+ 22 - 0
built/convention/src/main/kotlin/NavigationConventionPlugin.kt

@@ -0,0 +1,22 @@
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+
+class NavigationConventionPlugin : Plugin<Project> {
+    override fun apply(target: Project) {
+        with(target) {
+            with(pluginManager) {
+                apply("org.jetbrains.kotlin.kapt")
+                apply("androidx.navigation.safeargs.kotlin")
+            }
+
+            dependencies {
+                add("implementation", "androidx.navigation:navigation-ui-ktx:2.6.0")
+                add("implementation", "androidx.navigation:navigation-compose:2.6.0")
+                add("implementation", "androidx.hilt:hilt-navigation-compose:1.0.0")
+                add("implementation", "androidx.hilt:hilt-navigation-fragment:1.0.0")
+                add("kapt", "androidx.hilt:hilt-compiler:1.0.0")
+            }
+        }
+    }
+}

+ 2 - 0
built/convention/src/main/kotlin/com/zaojiao/app/built/convention/AndroidKotlin.kt

@@ -26,6 +26,8 @@ internal fun Project.configureKotlinAndroid(
 
     dependencies {
         add("implementation", "androidx.core:core-ktx:1.10.1")
+
+        add("implementation", libs.findLibrary("kotlinx-coroutines-android").get())
     }
 
     configureKotlin()

+ 3 - 1
core/http/build.gradle.kts

@@ -5,6 +5,8 @@ plugins {
 
 android {
     namespace = "com.zaojiao.http"
+
+    buildFeatures.buildConfig = true
 }
 
 dependencies {
@@ -15,5 +17,5 @@ dependencies {
     api("com.squareup.okhttp3:logging-interceptor:4.10.0")
 
     api("com.squareup.retrofit2:retrofit:2.9.0")
-    api("com.squareup.retrofit2:converter-gson:2.9.0")
+    api("com.squareup.retrofit2:converter-moshi:2.9.0")
 }

+ 0 - 10
core/http/src/main/java/com/zaojiao/http/api/UserApi.kt

@@ -1,10 +0,0 @@
-package com.zaojiao.http.api
-
-import retrofit2.http.GET
-
-
-interface UserApi {
-
-    @GET
-    suspend fun getUser(): String
-}

+ 10 - 0
core/http/src/main/java/com/zaojiao/http/common/NetResult.kt

@@ -0,0 +1,10 @@
+package com.zaojiao.http.common
+
+import com.squareup.moshi.Json
+
+data class NetResult<out T>(
+    val status: Int,
+    val data: T?,
+    @Json(name = "msg")
+    val message: String?,
+)

+ 22 - 0
core/http/src/main/java/com/zaojiao/http/converter/ColorAdapter.kt

@@ -0,0 +1,22 @@
+package com.zaojiao.http.converter
+
+import com.squareup.moshi.FromJson
+import com.squareup.moshi.JsonQualifier
+import com.squareup.moshi.ToJson
+
+@Retention(value = AnnotationRetention.RUNTIME)
+@JsonQualifier
+annotation class HexColor
+
+internal class  HexColorAdapter {
+    @ToJson
+    fun toJson(@HexColor rgb: Int): String {
+        return "#%06x".format(rgb)
+    }
+
+    @FromJson
+    @HexColor
+    fun fromJson(rgb: String): Int {
+        return rgb.substring(1).toInt(16)
+    }
+}

+ 37 - 0
core/http/src/main/java/com/zaojiao/http/converter/ResultConverterFactory.kt

@@ -0,0 +1,37 @@
+package com.zaojiao.http.converter
+
+import com.squareup.moshi.JsonAdapter
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.Types
+import com.zaojiao.http.common.NetResult
+import okhttp3.ResponseBody
+import retrofit2.Converter
+import retrofit2.Retrofit
+import java.lang.reflect.Type
+
+class ResultConverterFactory private constructor(
+    private val moshi: Moshi,
+) : Converter.Factory() {
+    companion object {
+        fun create(moshi: Moshi): ResultConverterFactory {
+            return ResultConverterFactory(moshi)
+        }
+    }
+
+    override fun responseBodyConverter(
+        type: Type, annotations: Array<Annotation>, retrofit: Retrofit
+    ): Converter<ResponseBody, *> {
+        val responseType = Types.newParameterizedType(NetResult::class.java, type)
+        val adapter: JsonAdapter<NetResult<*>> = moshi.adapter(responseType)
+        return NetResultConverter(adapter)
+    }
+
+    private inner class NetResultConverter<T> constructor(
+        private val adapter: JsonAdapter<NetResult<T>>
+    ) : Converter<ResponseBody, NetResult<T>> {
+        override fun convert(value: ResponseBody): NetResult<T>? {
+            val json = value.string()
+            return adapter.fromJson(json)
+        }
+    }
+}

+ 25 - 5
core/http/src/main/java/com/zaojiao/http/di/HttpModule.kt

@@ -3,6 +3,11 @@ package com.zaojiao.http.di
 import android.content.Context
 import coil.ImageLoader
 import coil.decode.SvgDecoder
+import com.squareup.moshi.Moshi
+import com.zaojiao.http.converter.HexColorAdapter
+import com.zaojiao.http.converter.ResultConverterFactory
+import com.zaojiao.http.interceptor.TokenInterceptor
+import com.zaojiao.http.interceptor.VersionInterceptor
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn
@@ -11,7 +16,7 @@ import dagger.hilt.components.SingletonComponent
 import okhttp3.OkHttpClient
 import okhttp3.logging.HttpLoggingInterceptor
 import retrofit2.Retrofit
-import retrofit2.converter.gson.GsonConverterFactory
+import retrofit2.converter.moshi.MoshiConverterFactory
 import java.util.concurrent.TimeUnit
 import javax.inject.Named
 import javax.inject.Singleton
@@ -30,23 +35,33 @@ object HttpModule {
     @Singleton
     @Named("endpoint")
     fun provideEndpoint(): String {
-        return "https://open.api.luojigou.vip"
+        return "https://open.test.luojigou.vip"
     }
 
     @Provides
     @Singleton
-    fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
+    fun provideLoggingInterceptor(): HttpLoggingInterceptor {
         return HttpLoggingInterceptor()
             .apply {
                 setLevel(HttpLoggingInterceptor.Level.BODY)
             }
     }
 
+    @Provides
+    @Singleton
+    fun provideMoshi(): Moshi {
+        return Moshi.Builder()
+            .add(HexColorAdapter())
+            .build()
+    }
+
     @Provides
     @Singleton
     fun provideOkHttpClient(
         @Named("timeout") timeout: Long,
         loggingInterceptor: HttpLoggingInterceptor,
+        tokenInterceptor: TokenInterceptor,
+        versionInterceptor: VersionInterceptor,
     ): OkHttpClient {
         return OkHttpClient.Builder()
             .connectTimeout(timeout, TimeUnit.MILLISECONDS)
@@ -55,19 +70,24 @@ object HttpModule {
             .retryOnConnectionFailure(true)
             .followRedirects(false)
             .followSslRedirects(false)
+            .addInterceptor(versionInterceptor)
+            .addInterceptor(loggingInterceptor)
+            .addInterceptor(tokenInterceptor)
             .build()
     }
 
     @Provides
     @Singleton
     fun provideRetrofitClient(
-        okHttpClient: OkHttpClient,
         @Named("endpoint") endpoint: String,
+        okHttpClient: OkHttpClient,
+        moshi: Moshi,
     ): Retrofit {
         return Retrofit.Builder()
             .client(okHttpClient)
             .baseUrl(endpoint)
-            .addConverterFactory(GsonConverterFactory.create())
+            .addConverterFactory(MoshiConverterFactory.create(moshi))
+            .addConverterFactory(ResultConverterFactory.create(moshi))
             .build()
     }
 

+ 15 - 0
core/http/src/main/java/com/zaojiao/http/interceptor/TokenInterceptor.kt

@@ -0,0 +1,15 @@
+package com.zaojiao.http.interceptor
+
+import okhttp3.Interceptor
+import okhttp3.Response
+import javax.inject.Inject
+
+class TokenInterceptor @Inject constructor() : Interceptor {
+    override fun intercept(chain: Interceptor.Chain): Response {
+        val newRequest = chain.request().newBuilder()
+
+        newRequest.addHeader("token", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxNTgwODM5MzQ4NzU5Mjg5ODU4IiwiZXhwIjoxNjg5MDU1MDE4fQ.Qn32mhfgKmcp5JqQR3HQspDOydFsAHJxNAe6BpyHOpo")
+
+        return chain.proceed(newRequest.build())
+    }
+}

+ 31 - 0
core/http/src/main/java/com/zaojiao/http/interceptor/VersionInterceptor.kt

@@ -0,0 +1,31 @@
+package com.zaojiao.http.interceptor
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import dagger.hilt.android.qualifiers.ApplicationContext
+import okhttp3.Interceptor
+import okhttp3.Response
+import javax.inject.Inject
+
+class VersionInterceptor @Inject constructor(
+    @ApplicationContext private val application: Context,
+) : Interceptor {
+    override fun intercept(chain: Interceptor.Chain): Response {
+        val newRequest = chain.request().newBuilder()
+
+        val packageName = application.packageName
+        val packageInfo = application.packageManager.getPackageInfo(packageName, 0)
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            newRequest.addHeader("version", packageInfo.longVersionCode.toString())
+        } else {
+            newRequest.addHeader("version", packageInfo.versionCode.toString())
+        }
+        newRequest.addHeader("platform", "android")
+        newRequest.addHeader("packageName", packageName)
+        newRequest.addHeader("package", packageName)
+
+        return chain.proceed(newRequest.build())
+    }
+}

+ 4 - 0
data/local/build.gradle.kts

@@ -4,4 +4,8 @@ plugins {
 
 android {
     namespace = "com.zaojiao.app.data.local"
+}
+
+dependencies {
+    implementation(project(":data:model"))
 }

+ 0 - 27
data/model/src/main/java/com/zaojiao/app/data/model/Result.kt

@@ -1,27 +0,0 @@
-package com.zaojiao.app.data.model
-
-sealed class Result {
-    data class Success<T>(
-        val data: T,
-    ) : Result()
-
-    data class Failure(
-        val throwable: Throwable,
-    ) : Result()
-}
-
-inline fun <T> Result.onSuccess(callback: (T) -> Unit): Result {
-    if (this is Result.Success<*>) {
-        callback(data as T)
-    }
-
-    return this
-}
-
-inline fun Result.onFailure(callback: (Throwable) -> Unit): Result {
-    if (this is Result.Failure) {
-        callback(throwable)
-    }
-
-    return this
-}

+ 1 - 1
data/model/src/main/java/com/zaojiao/app/data/model/UserModel.kt

@@ -2,7 +2,7 @@ package com.zaojiao.app.data.model
 
 data class UserModel(
     val id: String,
-    val name: String? = null,
+    val nickName: String? = null,
 ) {
     companion object {
         val none = UserModel(

+ 2 - 0
data/remote/build.gradle.kts

@@ -9,4 +9,6 @@ android {
 
 dependencies {
     implementation(project(":core:http"))
+
+    implementation(project(":data:model"))
 }

+ 6 - 2
data/remote/src/main/kotlin/com/zaojiao/app/data/remote/RemoteUserData.kt

@@ -1,6 +1,7 @@
 package com.zaojiao.app.data.remote
 
-import com.zaojiao.http.api.UserApi
+import com.zaojiao.app.data.model.UserModel
+import com.zaojiao.app.data.remote.api.UserApi
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -8,5 +9,8 @@ import javax.inject.Singleton
 class RemoteUserData @Inject constructor(
     private val userApi: UserApi,
 ) {
-    suspend fun getUser(): String = userApi.getUser()
+    suspend fun getUser(): UserModel? {
+        val user = userApi.getUser()
+        return user.data
+    }
 }

+ 12 - 0
data/remote/src/main/kotlin/com/zaojiao/app/data/remote/api/UserApi.kt

@@ -0,0 +1,12 @@
+package com.zaojiao.app.data.remote.api
+
+import com.zaojiao.app.data.model.UserModel
+import com.zaojiao.http.common.NetResult
+import retrofit2.http.GET
+
+
+interface UserApi {
+
+    @GET("/app/user/getV2")
+    suspend fun getUser(): NetResult<UserModel>
+}

+ 2 - 2
core/http/src/main/java/com/zaojiao/http/di/ApiModule.kt → data/remote/src/main/kotlin/com/zaojiao/app/data/remote/di/ApiModule.kt

@@ -1,6 +1,6 @@
-package com.zaojiao.http.di
+package com.zaojiao.app.data.remote.di
 
-import com.zaojiao.http.api.UserApi
+import com.zaojiao.app.data.remote.api.UserApi
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn

+ 2 - 0
data/repo/src/main/kotlin/com/zaojiao/app/data/repo/RepoModule.kt

@@ -4,11 +4,13 @@ import dagger.Binds
 import dagger.Module
 import dagger.hilt.InstallIn
 import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
 
 @Module
 @InstallIn(SingletonComponent::class)
 interface RepoModule {
     @Binds
+    @Singleton
     fun bindsUserRepository(
         userRepositoryImpl: UserRepositoryImpl,
     ): UserRepository

+ 4 - 1
data/repo/src/main/kotlin/com/zaojiao/app/data/repo/UserRepository.kt

@@ -1,7 +1,10 @@
 package com.zaojiao.app.data.repo
 
+import com.zaojiao.app.data.model.UserModel
+import kotlinx.coroutines.flow.Flow
+
 interface UserRepository {
-    suspend fun getUser(): String
+    fun getUser(): Flow<UserModel?>
 
     suspend fun getBaby()
 

+ 10 - 2
data/repo/src/main/kotlin/com/zaojiao/app/data/repo/UserRepositoryImpl.kt

@@ -1,16 +1,24 @@
 package com.zaojiao.app.data.repo
 
+import com.zaojiao.app.data.model.UserModel
 import com.zaojiao.app.data.remote.RemoteUserData
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
 import javax.inject.Inject
 
 
 class UserRepositoryImpl @Inject constructor(
     private val remoteUserData: RemoteUserData,
 ) : UserRepository {
-    override suspend fun getUser(): String {
-        return remoteUserData.getUser()
+    override fun getUser(): Flow<UserModel?> = flow {
+        emit(null)
+        coroutineScope {
+            emit(remoteUserData.getUser())
+        }
     }
 
+
     override suspend fun getBaby() {
         TODO("Not yet implemented")
     }

+ 4 - 7
feat/home/build.gradle.kts

@@ -2,6 +2,8 @@ plugins {
     id("d.convention.library")
     id("d.convention.compose")
     id("d.convention.hilt")
+    id("d.convention.lifecycle")
+    id("d.convention.navigation")
 }
 
 android {
@@ -10,11 +12,6 @@ android {
 
 dependencies {
     implementation(project(":feat:common"))
-
-    implementation("androidx.core:core-ktx:1.10.1")
-
-    implementation("com.google.dagger:dagger:2.44.2")
-    kapt("com.google.dagger:dagger-compiler:2.44.2")
-
-    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
+    implementation(project(":data:repo"))
+    implementation(project(":data:model"))
 }

+ 8 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/course/HomeCoursePage.kt

@@ -9,11 +9,19 @@ import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
 import com.zaojiao.component.common.PageState
 import com.zaojiao.component.common.Screen
 import com.zaojiao.component.common.StatePage
 import com.zaojiao.component.common.grid
 
+@Composable
+internal fun HomeCourseRoute(
+    viewModel: HomeCourseViewModel = hiltViewModel(),
+) {
+    HomeCoursePage()
+}
+
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun HomeCoursePage() {

+ 3 - 3
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/course/HomeCourseViewModel.kt

@@ -1,10 +1,10 @@
 package com.zaojiao.app.feat.home.course
 
 import androidx.lifecycle.ViewModel
+import dagger.hilt.android.lifecycle.HiltViewModel
 import javax.inject.Inject
 
-class HomeCourseViewModel @Inject constructor(
-
-):ViewModel(){
+@HiltViewModel
+class HomeCourseViewModel @Inject constructor() : ViewModel() {
 
 }

+ 10 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexPage.kt

@@ -11,9 +11,19 @@ import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import com.zaojiao.app.feat.home.course.HomeCoursePage
+import com.zaojiao.app.feat.home.course.HomeCourseViewModel
 import com.zaojiao.component.common.grid
 import com.zaojiao.component.common.list
 
+
+@Composable
+internal fun HomeIndexRoute(
+    viewModel: HomeIndexViewModel = hiltViewModel(),
+) {
+    HomeIndexPage()
+}
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun HomeIndexPage() {

+ 9 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexViewModel.kt

@@ -0,0 +1,9 @@
+package com.zaojiao.app.feat.home.index
+
+import androidx.lifecycle.ViewModel
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+
+@HiltViewModel
+class HomeIndexViewModel @Inject constructor() : ViewModel() {
+}

+ 21 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/navigation/HomeCourseNavigation.kt

@@ -0,0 +1,21 @@
+package com.zaojiao.app.feat.home.navigation
+
+
+import androidx.navigation.NavController
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavOptions
+import androidx.navigation.compose.composable
+import com.zaojiao.app.feat.home.course.HomeCourseRoute
+
+const val homeCourse = "home/course"
+
+fun NavController.navigateToHomeCourse(navOptions: NavOptions? = null) {
+    this.navigate(homeCourse, navOptions)
+}
+
+fun NavGraphBuilder.homeCoursePage(
+    onBannerClick: (String) -> Unit,
+    onCourseClick: (String) -> Unit,
+) {
+    composable(route = homeCourse) { HomeCourseRoute() }
+}

+ 26 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/navigation/HomeIndexNavigation.kt

@@ -0,0 +1,26 @@
+package com.zaojiao.app.feat.home.navigation
+
+import androidx.navigation.NavController
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavOptions
+import androidx.navigation.compose.composable
+import com.zaojiao.app.feat.home.index.HomeIndexPage
+import com.zaojiao.app.feat.home.index.HomeIndexRoute
+
+const val homeIndex = "home/index"
+
+fun NavController.navigateToHomeIndex(navOptions: NavOptions? = null) {
+    this.navigate(homeIndex, navOptions)
+}
+
+fun NavGraphBuilder.homeIndexPage(
+//    onBannerClick: () -> Unit,
+//    onShopClick: () -> Unit,
+//    onGameClick: () -> Unit,
+//    onGoodClick: (String) -> Unit,
+//    onCourseClick: (String) -> Unit,
+) {
+    composable(route = homeIndex) {
+        HomeIndexRoute()
+    }
+}

+ 25 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/navigation/HomePersonalNavigation.kt

@@ -0,0 +1,25 @@
+package com.zaojiao.app.feat.home.navigation
+
+import androidx.navigation.NavController
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavOptions
+import androidx.navigation.compose.composable
+import com.zaojiao.app.feat.home.personal.HomePersonalRoute
+
+const val homePersonal = "home/personal"
+
+fun NavController.navigateToHomePersonal(navOptions: NavOptions? = null) {
+    this.navigate(homePersonal, navOptions)
+}
+
+fun NavGraphBuilder.homePersonalPage(
+//    onBannerClick: () -> Unit,
+//    onShopClick: () -> Unit,
+//    onGameClick: () -> Unit,
+//    onGoodClick: (String) -> Unit,
+//    onCourseClick: (String) -> Unit,
+) {
+    composable(route = homePersonal) {
+        HomePersonalRoute()
+    }
+}

+ 0 - 4
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalNavigation.kt

@@ -1,4 +0,0 @@
-package com.zaojiao.app.feat.home.personal
-
-class HomePersonalNavigation {
-}

+ 17 - 2
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalPage.kt

@@ -15,12 +15,27 @@ import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+
+@Composable
+internal fun HomePersonalRoute(
+    viewModel: HomePersonalViewModel = hiltViewModel()
+) {
+    val userUiState by viewModel.userUiState.collectAsStateWithLifecycle()
+
+    HomePersonalPage(
+        userUiState = userUiState,
+    )
+}
 
 /**
  *  首页 - 我的勋章
  */
 @Composable
-fun HomePersonalPage() {
+fun HomePersonalPage(
+    userUiState: HomePersonalUserUiState,
+) {
     Box(
         modifier = Modifier
             .background(
@@ -40,7 +55,7 @@ fun HomePersonalPage() {
             }
 
             item {
-                HomePersonalUserBar()
+                HomePersonalUserBar(userUiState)
             }
 
             item {

+ 6 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalUiState.kt

@@ -0,0 +1,6 @@
+package com.zaojiao.app.feat.home.personal
+
+data class HomePersonalUserUiState(
+    val name: String,
+    val avatar: String?,
+)

+ 4 - 2
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalUserBar.kt

@@ -20,7 +20,9 @@ import com.zaojiao.component.common.Icons
 import com.zaojiao.component.common.Spacer
 
 @Composable
-fun HomePersonalUserBar() {
+fun HomePersonalUserBar(
+    userUiState: HomePersonalUserUiState,
+) {
     Row(
         modifier = Modifier
             .padding(bottom = 9.dp)
@@ -35,7 +37,7 @@ fun HomePersonalUserBar() {
         )
         Spacer(width = 12.dp)
         Text(
-            text = "逻辑狗",
+            text = userUiState.name,
             modifier = Modifier.weight(1f),
             style = TextStyle(
                 fontSize = 18.sp,

+ 28 - 1
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalViewModel.kt

@@ -1,4 +1,31 @@
 package com.zaojiao.app.feat.home.personal
 
-class HomePersonalViewModel {
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.zaojiao.app.data.repo.UserRepository
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import javax.inject.Inject
+
+@HiltViewModel
+class HomePersonalViewModel @Inject constructor(
+    private val userRepository: UserRepository
+) : ViewModel() {
+    val userUiState: StateFlow<HomePersonalUserUiState> = userRepository.getUser()
+        .map {
+            HomePersonalUserUiState(
+                name = it?.nickName ?: "逻辑狗",
+                avatar = null,
+            )
+        }.stateIn(
+            scope = viewModelScope,
+            started = SharingStarted.WhileSubscribed(5_000),
+            initialValue = HomePersonalUserUiState(
+                name = "逻辑狗",
+                avatar = null,
+            )
+        )
 }

+ 3 - 0
gradle/libs.versions.toml

@@ -3,6 +3,7 @@ coil = "2.2.2"
 agp = "8.0.1"
 kotlin = "1.8.20"
 kotlinx = "1.8.0"
+coroutines = "1.3.9"
 ksp = "1.8.20-1.0.11"
 
 [libraries]
@@ -10,6 +11,8 @@ coil-kt = { group = "io.coil-kt", name = "coil", version.ref = "coil" }
 coil-kt-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
 coil-kt-svg = { group = "io.coil-kt", name = "coil-svg", version.ref = "coil" }
 
+kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
+
 kotlin-gradle = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
 android-gradle = { group = "com.android.tools.build", name = "gradle", version.ref = "agp" }
 ksp-gradle = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }