Skip to content

Commit 1e2527d

Browse files
committed
feat: add onboarding page
1 parent 4fe1d5a commit 1e2527d

File tree

15 files changed

+1006
-51
lines changed

15 files changed

+1006
-51
lines changed

app/src/main/kotlin/day/vitayuzu/neodb/MainActivity.kt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ import coil3.request.crossfade
2727
import com.mikepenz.aboutlibraries.ui.compose.android.produceLibraries
2828
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
2929
import dagger.hilt.android.AndroidEntryPoint
30+
import day.vitayuzu.neodb.data.AppSettingsManager
31+
import day.vitayuzu.neodb.data.AppSettingsManager.Companion.ONBOARDING_COMPLETED
3032
import day.vitayuzu.neodb.data.AuthRepository
31-
import day.vitayuzu.neodb.data.NeoDBRepository
32-
import day.vitayuzu.neodb.data.UpdateRepository
33+
import day.vitayuzu.neodb.data.OtherRepository
3334
import day.vitayuzu.neodb.ui.component.SharedBottomBar
3435
import day.vitayuzu.neodb.ui.page.detail.DetailPage
3536
import day.vitayuzu.neodb.ui.page.home.HomeScreen
@@ -58,11 +59,11 @@ import javax.inject.Inject
5859

5960
@AndroidEntryPoint
6061
class MainActivity : ComponentActivity() {
61-
@Inject lateinit var neoDBRepository: NeoDBRepository
62-
6362
@Inject lateinit var authRepository: AuthRepository
6463

65-
@Inject lateinit var updateRepository: UpdateRepository
64+
@Inject lateinit var updateRepository: OtherRepository
65+
66+
@Inject lateinit var appSettingsManager: AppSettingsManager
6667

6768
@Inject @AppScope lateinit var appScope: CoroutineScope
6869

@@ -80,6 +81,14 @@ class MainActivity : ComponentActivity() {
8081
appScope.launch { authRepository.updateAccountStatus() }
8182
updateRepository.checkUpdateFlow.launchIn(appScope)
8283

84+
// Check if onboarding is completed
85+
appScope.launch {
86+
val onboardingCompleted = appSettingsManager.getAuthData(ONBOARDING_COMPLETED) ?: false
87+
if (!onboardingCompleted) {
88+
startActivity(Intent(this@MainActivity, OauthActivity::class.java))
89+
}
90+
}
91+
8392
enableEdgeToEdge()
8493
setContent {
8594
NeoDBYouTheme {

app/src/main/kotlin/day/vitayuzu/neodb/OauthActivity.kt

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@ import androidx.activity.ComponentActivity
77
import androidx.activity.compose.setContent
88
import androidx.activity.enableEdgeToEdge
99
import androidx.activity.viewModels
10-
import androidx.compose.foundation.layout.padding
1110
import androidx.compose.material3.Scaffold
12-
import androidx.compose.ui.Modifier
1311
import dagger.hilt.android.AndroidEntryPoint
14-
import day.vitayuzu.neodb.ui.page.login.LoginPage
15-
import day.vitayuzu.neodb.ui.page.login.LoginViewModel
12+
import day.vitayuzu.neodb.ui.page.onboarding.OnboardingScreen
13+
import day.vitayuzu.neodb.ui.page.onboarding.OnboardingViewModel
1614
import day.vitayuzu.neodb.ui.theme.NeoDBYouTheme
1715
import day.vitayuzu.neodb.util.AUTH_CALLBACK
1816
import kotlinx.coroutines.CoroutineScope
@@ -42,15 +40,19 @@ import javax.inject.Inject
4240
class OauthActivity : ComponentActivity() {
4341
@Inject @AppScope lateinit var appScope: CoroutineScope
4442

45-
private val viewModel: LoginViewModel by viewModels()
43+
private val viewModel: OnboardingViewModel by viewModels()
4644

4745
override fun onCreate(savedInstanceState: Bundle?) {
4846
super.onCreate(savedInstanceState)
4947
enableEdgeToEdge()
5048
setContent {
5149
NeoDBYouTheme {
5250
Scaffold {
53-
LoginPage(modifier = Modifier.padding(it), viewModel = viewModel)
51+
OnboardingScreen(
52+
onSkip = { finish() },
53+
paddingValues = it,
54+
viewModel = viewModel,
55+
)
5456
}
5557
}
5658
}
@@ -65,8 +67,8 @@ class OauthActivity : ComponentActivity() {
6567
) {
6668
return
6769
}
68-
Log.d("OauthActivity", "Resume without oauth callback, show textfield")
69-
viewModel.reShowTextField()
70+
Log.d("OauthActivity", "Resume without oauth callback, reset oauth state")
71+
viewModel.resetOauthState()
7072
}
7173

7274
/**
@@ -88,8 +90,11 @@ class OauthActivity : ComponentActivity() {
8890
.handleAuthCode(authCode)
8991
.onCompletion {
9092
// Finish when successfully exchanged access token.
91-
// If failed, remain at this activity.
92-
if (it == null) finish()
93+
// If failed(it != null), remain at this activity.
94+
if (it == null) {
95+
viewModel.completeOnboarding()
96+
finish()
97+
}
9398
}.collect()
9499
}
95100
} else {

app/src/main/kotlin/day/vitayuzu/neodb/data/AppSettingsManager.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ class AppSettingsManager @Inject constructor(
129129

130130
// TODO: Control global log level
131131
val VERBOSE_LOG = booleanPreferencesKey("verbose_log")
132+
133+
// Onboarding
134+
val ONBOARDING_COMPLETED = booleanPreferencesKey("onboarding_completed")
132135
}
133136
}
134137

app/src/main/kotlin/day/vitayuzu/neodb/data/AuthRepository.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ class AuthRepository @Inject constructor(
4343

4444
init {
4545
ktorfit.httpClient.plugin(HttpSend).intercept { request ->
46-
// Bypass other requests or github will return 403
47-
if (request.url.host.contains("api.github.com")) {
46+
// Bypass other requests or github/neodb-api will return errors
47+
if (request.url.host.contains("api.github.com") ||
48+
request.url.host.contains("api.neodb.app")
49+
) {
4850
return@intercept execute(request)
4951
}
5052
// Intercept request host if user has logged.

app/src/main/kotlin/day/vitayuzu/neodb/data/NetworkHiltModule.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ object NetworkHiltModule {
6767
// Skip auth header
6868
sendWithoutRequest {
6969
!it.url.host.contains("api.github.com") &&
70+
!it.url.host.contains("api.neodb.app") &&
7071
!it.url.encodedPath.contains("api/v1") &&
7172
!it.url.encodedPath.contains("oauth/")
7273
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package day.vitayuzu.neodb.data
2+
3+
import android.util.Log
4+
import day.vitayuzu.neodb.BuildConfig
5+
import io.github.g00fy2.versioncompare.Version
6+
import kotlinx.coroutines.flow.flow
7+
import javax.inject.Inject
8+
9+
class OtherRepository @Inject constructor(private val remoteSource: RemoteSource) {
10+
val checkUpdateFlow = flow {
11+
runCatching {
12+
with(remoteSource.getLatestVersionFromGithub()) {
13+
if (Version(tagName.drop(1)) > Version(BuildConfig.VERSION_NAME)) {
14+
emit(this)
15+
Log.d("CheckUpdateFlow", "New version found: $tagName")
16+
} else {
17+
emit(null)
18+
Log.d("CheckUpdateFlow", "Has been the latest version")
19+
}
20+
}
21+
}.onFailure { e ->
22+
emit(null)
23+
Log.e("CheckUpdateFlow", "Failed to check update", e)
24+
}
25+
}
26+
27+
val fetchPublicInstancesFlow = flow {
28+
runCatching {
29+
remoteSource.fetchPublicInstances()
30+
}.onSuccess { instances ->
31+
// Sort by total users in descending order
32+
emit(instances.sortedByDescending { it.totalUsers })
33+
}.onFailure { error ->
34+
Log.e("FetchPublicInstancesFlow", "Failed to fetch public instances", error)
35+
}
36+
}
37+
}

app/src/main/kotlin/day/vitayuzu/neodb/data/RemoteSource.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import day.vitayuzu.neodb.data.schema.MarkInSchema
88
import day.vitayuzu.neodb.data.schema.MarkSchema
99
import day.vitayuzu.neodb.data.schema.PagedMarkSchema
1010
import day.vitayuzu.neodb.data.schema.PaginatedPostList
11+
import day.vitayuzu.neodb.data.schema.PublicInstanceSchema
1112
import day.vitayuzu.neodb.data.schema.ResultSchema
1213
import day.vitayuzu.neodb.data.schema.SearchResult
1314
import day.vitayuzu.neodb.data.schema.TokenSchema
@@ -137,10 +138,18 @@ class RemoteSource @Inject constructor(
137138
api.searchWithKeywords(keywords, category, page)
138139
}
139140

141+
// ===========================================< Others >=======================================
140142
suspend fun getLatestVersionFromGithub() = withContext(dispatcher) {
141143
api.getLatestVersionFromGithub()
142144
// GithubLatestReleaseSchema.TEST
143145
}
146+
147+
/**
148+
* Fetch list of public NeoDB instances from api.neodb.app.
149+
*/
150+
suspend fun fetchPublicInstances(): List<PublicInstanceSchema> = withContext(dispatcher) {
151+
api.fetchPublicInstances()
152+
}
144153
}
145154

146155
/**
@@ -244,4 +253,7 @@ interface NeoDbApi {
244253
@GET("https://api.github.com/repos/heddxh/NeoDB-You/releases/latest")
245254
@Headers("Accept: application/vnd.github+json", "X-GitHub-Api-Version: 2022-11-28")
246255
suspend fun getLatestVersionFromGithub(): GithubLatestReleaseSchema
256+
257+
@GET("https://api.neodb.app/servers")
258+
suspend fun fetchPublicInstances(): List<PublicInstanceSchema>
247259
}

app/src/main/kotlin/day/vitayuzu/neodb/data/UpdateRepository.kt

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package day.vitayuzu.neodb.data.schema
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
/**
7+
* Schema for public NeoDB instances fetched from api.neodb.app.
8+
*/
9+
@Serializable
10+
data class PublicInstanceSchema(
11+
val domain: String,
12+
val version: String,
13+
val title: String,
14+
val description: String,
15+
val languages: List<String> = emptyList(),
16+
val region: String = "",
17+
val categories: List<String> = emptyList(),
18+
@SerialName("proxied_thumbnail") val proxiedThumbnail: String = "",
19+
val blurhash: String = "",
20+
@SerialName("total_users") val totalUsers: Int = 0,
21+
@SerialName("last_week_users") val lastWeekUsers: Int = 0,
22+
@SerialName("approval_required") val approvalRequired: Boolean = false,
23+
val language: String = "",
24+
val category: String = "",
25+
)

0 commit comments

Comments
 (0)