Skip to content

Commit b2971c8

Browse files
committed
feat: Implement fetching project data from the GitHub API
1 parent 6bca471 commit b2971c8

File tree

13 files changed

+219
-11
lines changed

13 files changed

+219
-11
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.nsh07.nsh07.network
2+
3+
import io.ktor.client.*
4+
import io.ktor.client.call.*
5+
import io.ktor.client.request.*
6+
import io.ktor.http.*
7+
import io.ktor.util.network.*
8+
import kotlinx.serialization.SerializationException
9+
import org.nsh07.nsh07.util.NetworkError
10+
import org.nsh07.nsh07.util.Result
11+
12+
class GitHubApiClient(
13+
private val client: HttpClient
14+
) {
15+
16+
private val url = "https://api.github.com/search/repositories"
17+
18+
suspend fun getTopRepos(username: String): Result<TopRepos, NetworkError> {
19+
val response = try {
20+
client.get(url) {
21+
header(HttpHeaders.Accept, ContentType.Application.Json.toString())
22+
23+
parameter("q", "user:$username")
24+
parameter("sort", "stars")
25+
parameter("order", "desc")
26+
parameter("per_page", "5")
27+
}
28+
} catch (_: UnresolvedAddressException) {
29+
return Result.Error(NetworkError.NO_INTERNET)
30+
} catch (_: SerializationException) {
31+
return Result.Error(NetworkError.SERIALIZATION)
32+
}
33+
34+
return when (response.status.value) {
35+
in 200..299 -> {
36+
val topRepos = response.body<TopRepos>()
37+
Result.Success(topRepos)
38+
}
39+
40+
401 -> Result.Error(NetworkError.UNAUTHORIZED)
41+
409 -> Result.Error(NetworkError.CONFLICT)
42+
408 -> Result.Error(NetworkError.REQUEST_TIMEOUT)
43+
413 -> Result.Error(NetworkError.PAYLOAD_TOO_LARGE)
44+
429 -> Result.Error(NetworkError.TOO_MANY_REQUESTS)
45+
in 500..599 -> Result.Error(NetworkError.SERVER_ERROR)
46+
47+
else -> Result.Error(NetworkError.UNKNOWN)
48+
}
49+
}
50+
51+
}

composeApp/src/wasmJsMain/kotlin/org/nsh07/nsh07/ui/network/License.kt renamed to composeApp/src/wasmJsMain/kotlin/org/nsh07/nsh07/network/License.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.nsh07.nsh07.ui.network
1+
package org.nsh07.nsh07.network
22

33
import kotlinx.serialization.SerialName
44
import kotlinx.serialization.Serializable

composeApp/src/wasmJsMain/kotlin/org/nsh07/nsh07/ui/network/Owner.kt renamed to composeApp/src/wasmJsMain/kotlin/org/nsh07/nsh07/network/Owner.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.nsh07.nsh07.ui.network
1+
package org.nsh07.nsh07.network
22

33
import kotlinx.serialization.SerialName
44
import kotlinx.serialization.Serializable

composeApp/src/wasmJsMain/kotlin/org/nsh07/nsh07/ui/network/Repo.kt renamed to composeApp/src/wasmJsMain/kotlin/org/nsh07/nsh07/network/Repo.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.nsh07.nsh07.ui.network
1+
package org.nsh07.nsh07.network
22

33
import kotlinx.serialization.SerialName
44
import kotlinx.serialization.Serializable
@@ -105,8 +105,6 @@ data class Repo(
105105
val mergesUrl: String,
106106
@SerialName("milestones_url")
107107
val milestonesUrl: String,
108-
@SerialName("mirror_url")
109-
val mirrorUrl: Any?,
110108
@SerialName("name")
111109
val name: String,
112110
@SerialName("node_id")

composeApp/src/wasmJsMain/kotlin/org/nsh07/nsh07/ui/network/TopRepos.kt renamed to composeApp/src/wasmJsMain/kotlin/org/nsh07/nsh07/network/TopRepos.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.nsh07.nsh07.ui.network
1+
package org.nsh07.nsh07.network
22

33
import kotlinx.serialization.SerialName
44
import kotlinx.serialization.Serializable
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.nsh07.nsh07.network
2+
3+
import io.ktor.client.*
4+
import io.ktor.client.plugins.*
5+
import io.ktor.client.plugins.contentnegotiation.*
6+
import io.ktor.client.request.*
7+
import io.ktor.http.*
8+
import io.ktor.serialization.kotlinx.json.*
9+
import kotlinx.serialization.json.Json
10+
11+
fun createHttpClient(): HttpClient {
12+
return HttpClient {
13+
install(ContentNegotiation) {
14+
json(
15+
Json {
16+
ignoreUnknownKeys = true
17+
}
18+
)
19+
}
20+
install(DefaultRequest) {
21+
header("X-GitHub-Api-Version", "2022-11-28")
22+
header(HttpHeaders.UserAgent, "nsh07.github.io/1.0.0 ([email protected]) ktor/3.3.0")
23+
}
24+
}
25+
}

composeApp/src/wasmJsMain/kotlin/org/nsh07/nsh07/ui/AppScreen.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@ package org.nsh07.nsh07.ui
22

33
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
44
import androidx.compose.runtime.Composable
5+
import androidx.compose.runtime.collectAsState
6+
import androidx.compose.runtime.getValue
7+
import androidx.compose.runtime.remember
58
import androidx.compose.ui.Modifier
9+
import org.nsh07.nsh07.network.GitHubApiClient
10+
import org.nsh07.nsh07.network.createHttpClient
611
import org.nsh07.nsh07.ui.homeScreen.AppHomeScreen
712

813
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
914
@Composable
1015
fun AppScreen(
16+
viewModel: UiViewModel = remember { UiViewModel(GitHubApiClient(createHttpClient())) },
1117
modifier: Modifier = Modifier
1218
) {
13-
AppHomeScreen(modifier)
19+
val projectsState by viewModel.projectsState.collectAsState()
20+
21+
AppHomeScreen(projectsState, modifier)
1422
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.nsh07.nsh07.ui
2+
3+
import androidx.lifecycle.ViewModel
4+
import androidx.lifecycle.viewModelScope
5+
import kotlinx.coroutines.flow.MutableStateFlow
6+
import kotlinx.coroutines.flow.StateFlow
7+
import kotlinx.coroutines.flow.asStateFlow
8+
import kotlinx.coroutines.flow.update
9+
import kotlinx.coroutines.launch
10+
import org.nsh07.nsh07.network.GitHubApiClient
11+
import org.nsh07.nsh07.ui.homeScreen.ProjectsState
12+
import org.nsh07.nsh07.util.onError
13+
import org.nsh07.nsh07.util.onSuccess
14+
15+
class UiViewModel(
16+
private val githubClient: GitHubApiClient
17+
) : ViewModel() {
18+
private val _projectsState = MutableStateFlow(ProjectsState())
19+
val projectsState: StateFlow<ProjectsState> = _projectsState.asStateFlow()
20+
21+
init {
22+
loadProjects()
23+
}
24+
25+
fun loadProjects() {
26+
viewModelScope.launch {
27+
githubClient.getTopRepos("nsh07")
28+
.onSuccess {
29+
_projectsState.update { currentState ->
30+
currentState.copy(
31+
isLoading = false,
32+
error = null,
33+
projects = it.repos
34+
)
35+
}
36+
}
37+
.onError {
38+
_projectsState.update { currentState ->
39+
currentState.copy(
40+
isLoading = false,
41+
error = it.name,
42+
projects = emptyList()
43+
)
44+
}
45+
}
46+
}
47+
}
48+
}

composeApp/src/wasmJsMain/kotlin/org/nsh07/nsh07/ui/homeScreen/AppHomeScreen.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ import nsh07.composeapp.generated.resources.*
2525
import org.jetbrains.compose.resources.painterResource
2626

2727
@Composable
28-
fun AppHomeScreen(modifier: Modifier = Modifier) {
28+
fun AppHomeScreen(
29+
projectState: ProjectsState,
30+
modifier: Modifier = Modifier
31+
) {
2932
val uriHandler = LocalUriHandler.current
3033
val scope = rememberCoroutineScope()
3134

@@ -187,6 +190,14 @@ fun AppHomeScreen(modifier: Modifier = Modifier) {
187190
}
188191
Spacer(Modifier.height(112.dp))
189192
}
193+
items(projectState.projects, key = { it.id }) {
194+
ProjectCard(
195+
project = it,
196+
cardPadding = cardPadding,
197+
projectImageUri = "",
198+
modifier = Modifier.padding(bottom = 32.dp)
199+
)
200+
}
190201
item("work in progress") {
191202
Column(
192203
horizontalAlignment = Alignment.CenterHorizontally,

composeApp/src/wasmJsMain/kotlin/org/nsh07/nsh07/ui/homeScreen/ProjectCard.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ fun ProjectCard(
2828
project: Repo,
2929
cardPadding: Dp,
3030
projectImageUri: String,
31-
projectDescription: String,
32-
modifier: Modifier = Modifier
31+
modifier: Modifier = Modifier,
32+
projectDescription: String? = null
3333
) {
3434
val colorScheme = colorScheme
3535
val uriHandler = LocalUriHandler.current
@@ -56,7 +56,7 @@ fun ProjectCard(
5656
)
5757
}
5858
Text(
59-
projectDescription,
59+
projectDescription ?: project.description,
6060
style = typography.bodyMedium,
6161
color = colorScheme.onSurfaceVariant,
6262
modifier = Modifier.padding(top = 8.dp)

0 commit comments

Comments
 (0)