From 86b6df8d5860a8b80812ec3c4525330de5cf86cb Mon Sep 17 00:00:00 2001
From: TheRealAshik <177647015+TheRealAshik@users.noreply.github.com>
Date: Sat, 16 May 2026 08:23:21 +0000
Subject: [PATCH] Implement Github Explore using real API data.
---
.../composeResources/values/strings.xml | 4 +
.../github/data/GitHubApiClient.kt | 54 +++++
.../github/explore/ExploreScreen.kt | 229 ++++++++++++++----
.../github/explore/ExploreUiState.kt | 43 ++--
.../github/explore/ExploreViewModel.kt | 124 +++++++---
.../therealashik/github/theme/Dimensions.kt | 3 +
6 files changed, 364 insertions(+), 93 deletions(-)
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index d9b77f4..eb0844e 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -289,4 +289,8 @@
%1$d followers ยท %2$d following
GitHub Logo
GitHub
+
+
+ contributed to %1$s
+ published a release
diff --git a/composeApp/src/commonMain/kotlin/dev/therealashik/github/data/GitHubApiClient.kt b/composeApp/src/commonMain/kotlin/dev/therealashik/github/data/GitHubApiClient.kt
index c95a375..0c1ef75 100644
--- a/composeApp/src/commonMain/kotlin/dev/therealashik/github/data/GitHubApiClient.kt
+++ b/composeApp/src/commonMain/kotlin/dev/therealashik/github/data/GitHubApiClient.kt
@@ -74,6 +74,43 @@ data class NotificationRepo(
@SerialName("full_name") val fullName: String
)
+@Serializable
+data class SearchResult(val items: List)
+
+@Serializable
+data class GitHubEvent(
+ val id: String,
+ val type: String,
+ val actor: GitHubActor,
+ val repo: EventRepo,
+ val payload: EventPayload? = null,
+ @SerialName("created_at") val createdAt: String
+)
+
+@Serializable
+data class GitHubActor(val login: String, @SerialName("avatar_url") val avatarUrl: String)
+
+@Serializable
+data class EventRepo(val name: String)
+
+@Serializable
+data class EventPayload(
+ val action: String? = null,
+ @SerialName("pull_request") val pullRequest: EventPullRequest? = null,
+ val release: EventRelease? = null
+)
+
+@Serializable
+data class EventPullRequest(
+ val title: String, val body: String? = null, val state: String, val head: EventHead
+)
+
+@Serializable
+data class EventHead(val ref: String)
+
+@Serializable
+data class EventRelease(val name: String? = null, @SerialName("tag_name") val tagName: String)
+
class GitHubApiClient(private val tokenStorage: TokenStorage) {
private val client = HttpClient {
@@ -134,5 +171,22 @@ class GitHubApiClient(private val tokenStorage: TokenStorage) {
)
}
+ suspend fun getTrendingRepos(): Result> = runCatching {
+ client.get("https://api.github.com/search/repositories") {
+ withAuth()
+ parameter("q", "stars:>1000")
+ parameter("sort", "stars")
+ parameter("order", "desc")
+ parameter("per_page", 10)
+ }.body()
+ }
+
+ suspend fun getReceivedEvents(username: String, perPage: Int = 20): Result> = runCatching {
+ client.get("https://api.github.com/users/${username}/received_events") {
+ withAuth()
+ parameter("per_page", perPage)
+ }.body()
+ }
+
fun close() = client.close()
}
diff --git a/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreScreen.kt b/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreScreen.kt
index 2a3aec8..a2a32e1 100644
--- a/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreScreen.kt
+++ b/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreScreen.kt
@@ -1,7 +1,9 @@
package dev.therealashik.github.explore
+import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -14,21 +16,25 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.List
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.SmartToy
+import androidx.compose.material.icons.filled.Star
+import androidx.compose.material.icons.outlined.ChatBubbleOutline
import androidx.compose.material.icons.outlined.FilterList
import androidx.compose.material.icons.outlined.LocalFireDepartment
import androidx.compose.material.icons.outlined.ThumbUp
-import androidx.compose.material.icons.outlined.ChatBubbleOutline
-import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Button
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -48,6 +54,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
import androidx.lifecycle.viewmodel.compose.viewModel
import dev.therealashik.github.theme.Dimensions
import github.composeapp.generated.resources.Res
@@ -58,6 +65,7 @@ import github.composeapp.generated.resources.explore_title
import github.composeapp.generated.resources.filter_activity
import github.composeapp.generated.resources.read_more
import github.composeapp.generated.resources.trending_repos
+import github.composeapp.generated.resources.retry
import org.jetbrains.compose.resources.stringResource
@Composable
@@ -67,31 +75,70 @@ fun ExploreScreen(viewModel: ExploreViewModel = viewModel { ExploreViewModel() }
Scaffold(
topBar = { ExploreTopBar() }
) { innerPadding ->
- LazyColumn(
+ Box(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.background(MaterialTheme.colorScheme.background)
) {
- item {
- DiscoverSection()
- }
- item {
- HorizontalDivider(
- modifier = Modifier.padding(vertical = Dimensions.PaddingSmall),
- color = MaterialTheme.colorScheme.surfaceVariant
- )
- }
- item {
- ActivitySectionHeader()
- }
- items(
- items = uiState.activityFeed,
- key = { it.id }
- ) { item ->
- when (item) {
- is ContributionItem -> ContributionCard(item)
- is ReleaseItem -> ReleaseCard(item)
+ when (val state = uiState) {
+ is ExploreUiState.Loading -> {
+ CircularProgressIndicator(
+ modifier = Modifier.align(Alignment.Center),
+ color = MaterialTheme.colorScheme.primary
+ )
+ }
+ is ExploreUiState.Error -> {
+ Column(
+ modifier = Modifier.align(Alignment.Center),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(Dimensions.SpacingMedium)
+ ) {
+ Text(
+ text = state.message,
+ color = MaterialTheme.colorScheme.error,
+ style = MaterialTheme.typography.bodyLarge
+ )
+ Button(onClick = { viewModel.loadData() }) {
+ Text(stringResource(Res.string.retry))
+ }
+ }
+ }
+ is ExploreUiState.Success -> {
+ LazyColumn(
+ modifier = Modifier.fillMaxSize()
+ ) {
+ item {
+ DiscoverSection()
+ }
+ item {
+ HorizontalDivider(
+ modifier = Modifier.padding(vertical = Dimensions.PaddingSmall),
+ color = MaterialTheme.colorScheme.surfaceVariant
+ )
+ }
+ item {
+ TrendingReposSection(state.trendingRepos)
+ }
+ item {
+ HorizontalDivider(
+ modifier = Modifier.padding(vertical = Dimensions.PaddingSmall),
+ color = MaterialTheme.colorScheme.surfaceVariant
+ )
+ }
+ item {
+ ActivitySectionHeader()
+ }
+ items(
+ items = state.activityFeed,
+ key = { it.id }
+ ) { item ->
+ when (item) {
+ is ContributionItem -> ContributionCard(item)
+ is ReleaseItem -> ReleaseCard(item)
+ }
+ }
+ }
}
}
}
@@ -105,12 +152,13 @@ private fun ExploreTopBar() {
title = {
Text(
text = stringResource(Res.string.explore_title),
- style = MaterialTheme.typography.headlineLarge,
+ style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
},
colors = TopAppBarDefaults.topAppBarColors(
- containerColor = MaterialTheme.colorScheme.background
+ containerColor = MaterialTheme.colorScheme.background,
+ titleContentColor = MaterialTheme.colorScheme.onBackground
)
)
}
@@ -182,6 +230,101 @@ private fun DiscoverSection() {
}
}
+@Composable
+private fun TrendingReposSection(repos: List) {
+ Column(modifier = Modifier.padding(vertical = Dimensions.PaddingMedium)) {
+ Text(
+ text = stringResource(Res.string.trending_repos),
+ style = MaterialTheme.typography.titleMedium,
+ fontWeight = FontWeight.SemiBold,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ modifier = Modifier.padding(start = Dimensions.PaddingMedium, end = Dimensions.PaddingMedium, bottom = Dimensions.PaddingMedium)
+ )
+
+ LazyRow(
+ horizontalArrangement = Arrangement.spacedBy(Dimensions.SpacingMedium),
+ modifier = Modifier.fillMaxWidth().padding(horizontal = Dimensions.PaddingMedium)
+ ) {
+ items(repos, key = { it.id }) { repo ->
+ TrendingRepoCard(repo)
+ }
+ }
+ }
+}
+
+@Composable
+private fun TrendingRepoCard(repo: TrendingRepoItem) {
+ Card(
+ modifier = Modifier
+ .width(Dimensions.TrendingRepoCardWidth)
+ .height(Dimensions.TrendingRepoCardHeight),
+ colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.surface
+ ),
+ shape = RoundedCornerShape(Dimensions.PaddingMedium)
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(Dimensions.PaddingMedium)
+ ) {
+ Text(
+ text = repo.fullName,
+ style = MaterialTheme.typography.titleMedium,
+ fontWeight = FontWeight.Bold,
+ color = MaterialTheme.colorScheme.onSurface,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ Spacer(modifier = Modifier.height(Dimensions.SpacingSmall))
+ Text(
+ text = repo.description ?: "",
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ maxLines = 2,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.weight(1f)
+ )
+ Spacer(modifier = Modifier.height(Dimensions.SpacingSmall))
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(Dimensions.SpacingMedium)
+ ) {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Icon(
+ imageVector = Icons.Default.Star,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.primary,
+ modifier = Modifier.size(Dimensions.IconSizeSmall)
+ )
+ Spacer(modifier = Modifier.width(Dimensions.SpacingExtraSmall))
+ Text(
+ text = repo.stars.toString(),
+ style = MaterialTheme.typography.labelMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ }
+ if (repo.language != null) {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Box(
+ modifier = Modifier
+ .size(Dimensions.IndicatorDotSize)
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.secondary)
+ )
+ Spacer(modifier = Modifier.width(Dimensions.SpacingExtraSmall))
+ Text(
+ text = repo.language,
+ style = MaterialTheme.typography.labelMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
@Composable
private fun ActivitySectionHeader() {
Row(
@@ -201,7 +344,7 @@ private fun ActivitySectionHeader() {
Icon(
imageVector = Icons.Outlined.FilterList,
contentDescription = stringResource(Res.string.filter_activity),
- tint = MaterialTheme.colorScheme.onSurfaceVariant
+ tint = MaterialTheme.colorScheme.primary
)
}
}
@@ -211,10 +354,14 @@ private fun ActivitySectionHeader() {
private fun ContributionCard(item: ContributionItem) {
var expanded by remember { mutableStateOf(false) }
- Column(modifier = Modifier.padding(Dimensions.PaddingMedium)) {
+ Column(
+ modifier = Modifier
+ .clickable { expanded = !expanded }
+ .padding(Dimensions.PaddingMedium)
+ ) {
// Header
Row(
- modifier = Modifier.fillMaxWidth().clickable { expanded = !expanded },
+ modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Box(
@@ -233,20 +380,20 @@ private fun ContributionCard(item: ContributionItem) {
}
Spacer(modifier = Modifier.width(Dimensions.SpacingSmall))
Text(
- text = stringResource(item.username),
+ text = item.username,
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.width(Dimensions.SpacingExtraSmall))
Text(
- text = stringResource(item.actionText),
+ text = item.actionText,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.weight(1f))
Text(
- text = stringResource(item.timestamp),
+ text = item.timestamp,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
@@ -266,13 +413,13 @@ private fun ContributionCard(item: ContributionItem) {
) {
Column {
Text(
- text = stringResource(item.repoPath),
+ text = item.repoPath,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(Dimensions.SpacingExtraSmall))
Text(
- text = stringResource(item.prTitle),
+ text = item.prTitle,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface
@@ -289,7 +436,7 @@ private fun ContributionCard(item: ContributionItem) {
.padding(horizontal = Dimensions.PaddingSmall, vertical = Dimensions.PaddingExtraSmall)
) {
Text(
- text = stringResource(item.statusText),
+ text = item.statusText,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onTertiaryContainer
)
@@ -301,7 +448,7 @@ private fun ContributionCard(item: ContributionItem) {
.padding(horizontal = Dimensions.PaddingSmall, vertical = Dimensions.PaddingExtraSmall)
) {
Text(
- text = stringResource(item.branchName),
+ text = item.branchName,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
@@ -309,7 +456,7 @@ private fun ContributionCard(item: ContributionItem) {
}
Spacer(modifier = Modifier.height(Dimensions.SpacingSmall))
Text(
- text = stringResource(item.bodyPreview),
+ text = item.bodyPreview,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
@@ -389,20 +536,20 @@ private fun ReleaseCard(item: ReleaseItem) {
}
Spacer(modifier = Modifier.width(Dimensions.SpacingSmall))
Text(
- text = stringResource(item.botName),
+ text = item.botName,
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.width(Dimensions.SpacingExtraSmall))
Text(
- text = stringResource(item.actionText),
+ text = item.actionText,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.weight(1f))
Text(
- text = stringResource(item.timestamp),
+ text = item.timestamp,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
@@ -427,7 +574,7 @@ private fun ReleaseCard(item: ReleaseItem) {
contentAlignment = Alignment.Center
) {
Text(
- text = stringResource(item.releaseTitle),
+ text = item.releaseTitle,
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onPrimaryContainer
diff --git a/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreUiState.kt b/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreUiState.kt
index 488bb8b..2d18750 100644
--- a/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreUiState.kt
+++ b/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreUiState.kt
@@ -1,31 +1,42 @@
package dev.therealashik.github.explore
-import org.jetbrains.compose.resources.StringResource
-
sealed class ActivityItem {
abstract val id: String
}
data class ContributionItem(
override val id: String,
- val username: StringResource,
- val actionText: StringResource,
- val timestamp: StringResource,
- val repoPath: StringResource,
- val prTitle: StringResource,
- val statusText: StringResource,
- val branchName: StringResource,
- val bodyPreview: StringResource
+ val username: String,
+ val actionText: String,
+ val timestamp: String,
+ val repoPath: String,
+ val prTitle: String,
+ val statusText: String,
+ val branchName: String,
+ val bodyPreview: String
) : ActivityItem()
data class ReleaseItem(
override val id: String,
- val botName: StringResource,
- val actionText: StringResource,
- val timestamp: StringResource,
- val releaseTitle: StringResource
+ val botName: String,
+ val actionText: String,
+ val timestamp: String,
+ val releaseTitle: String
) : ActivityItem()
-data class ExploreUiState(
- val activityFeed: List = emptyList()
+data class TrendingRepoItem(
+ val id: String,
+ val fullName: String,
+ val description: String?,
+ val stars: Int,
+ val language: String?
)
+
+sealed class ExploreUiState {
+ data object Loading : ExploreUiState()
+ data class Error(val message: String) : ExploreUiState()
+ data class Success(
+ val trendingRepos: List = emptyList(),
+ val activityFeed: List = emptyList()
+ ) : ExploreUiState()
+}
diff --git a/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreViewModel.kt b/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreViewModel.kt
index c8dedf9..a8202fa 100644
--- a/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreViewModel.kt
+++ b/composeApp/src/commonMain/kotlin/dev/therealashik/github/explore/ExploreViewModel.kt
@@ -1,53 +1,105 @@
package dev.therealashik.github.explore
import androidx.lifecycle.ViewModel
-import github.composeapp.generated.resources.Res
-import github.composeapp.generated.resources.explore_mock_user_1
-import github.composeapp.generated.resources.explore_mock_action_1
-import github.composeapp.generated.resources.explore_mock_time_1
-import github.composeapp.generated.resources.explore_mock_repo_1
-import github.composeapp.generated.resources.explore_mock_pr_title_1
-import github.composeapp.generated.resources.explore_mock_status_merged
-import github.composeapp.generated.resources.explore_mock_branch_1
-import github.composeapp.generated.resources.explore_mock_pr_body_1
-import github.composeapp.generated.resources.explore_mock_bot
-import github.composeapp.generated.resources.explore_mock_action_release
-import github.composeapp.generated.resources.explore_mock_time_release
-import github.composeapp.generated.resources.explore_mock_release_title
+import androidx.lifecycle.viewModelScope
+import dev.therealashik.github.data.GitHubApiClient
+import dev.therealashik.github.data.createTokenStorage
+import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import github.composeapp.generated.resources.Res
+import github.composeapp.generated.resources.explore_action_contributed
+import github.composeapp.generated.resources.explore_action_published
+import org.jetbrains.compose.resources.getString
class ExploreViewModel : ViewModel() {
- private val _uiState = MutableStateFlow(ExploreUiState())
+ private val apiClient = GitHubApiClient(createTokenStorage())
+
+ private val _uiState = MutableStateFlow(ExploreUiState.Loading)
val uiState: StateFlow = _uiState.asStateFlow()
init {
- loadMocks()
+ loadData()
}
- private fun loadMocks() {
- val mocks = listOf(
- ContributionItem(
- id = "1",
- username = Res.string.explore_mock_user_1,
- actionText = Res.string.explore_mock_action_1,
- timestamp = Res.string.explore_mock_time_1,
- repoPath = Res.string.explore_mock_repo_1,
- prTitle = Res.string.explore_mock_pr_title_1,
- statusText = Res.string.explore_mock_status_merged,
- branchName = Res.string.explore_mock_branch_1,
- bodyPreview = Res.string.explore_mock_pr_body_1
- ),
- ReleaseItem(
- id = "2",
- botName = Res.string.explore_mock_bot,
- actionText = Res.string.explore_mock_action_release,
- timestamp = Res.string.explore_mock_time_release,
- releaseTitle = Res.string.explore_mock_release_title
+ fun loadData() {
+ viewModelScope.launch {
+ _uiState.value = ExploreUiState.Loading
+
+ val userResult = apiClient.getAuthenticatedUser()
+ if (userResult.isFailure) {
+ _uiState.value = ExploreUiState.Error(userResult.exceptionOrNull()?.message ?: "Failed to get user")
+ return@launch
+ }
+ val user = userResult.getOrThrow()
+
+ val trendingDeferred = async { apiClient.getTrendingRepos() }
+ val eventsDeferred = async { apiClient.getReceivedEvents(user.login) }
+
+ val trendingResult = trendingDeferred.await()
+ val eventsResult = eventsDeferred.await()
+
+ if (trendingResult.isFailure && eventsResult.isFailure) {
+ _uiState.value = ExploreUiState.Error(trendingResult.exceptionOrNull()?.message ?: "Failed to load explore data")
+ return@launch
+ }
+
+ val trendingRepos = trendingResult.getOrDefault(dev.therealashik.github.data.SearchResult(emptyList())).items.map { repo ->
+ TrendingRepoItem(
+ id = repo.id.toString(),
+ fullName = repo.fullName,
+ description = repo.description,
+ stars = repo.stars,
+ language = repo.language
+ )
+ }
+
+ val activityFeed = eventsResult.getOrDefault(emptyList()).mapNotNull { event ->
+ when (event.type) {
+ "PullRequestEvent" -> {
+ val pr = event.payload?.pullRequest
+ if (pr != null) {
+ ContributionItem(
+ id = event.id,
+ username = event.actor.login,
+ actionText = getString(Res.string.explore_action_contributed, event.repo.name),
+ timestamp = event.createdAt,
+ repoPath = event.repo.name,
+ prTitle = pr.title,
+ statusText = pr.state,
+ branchName = pr.head.ref,
+ bodyPreview = pr.body?.take(120) ?: ""
+ )
+ } else null
+ }
+ "ReleaseEvent" -> {
+ val release = event.payload?.release
+ if (release != null) {
+ ReleaseItem(
+ id = event.id,
+ botName = event.actor.login,
+ actionText = getString(Res.string.explore_action_published),
+ timestamp = event.createdAt,
+ releaseTitle = release.name ?: release.tagName
+ )
+ } else null
+ }
+ else -> null
+ }
+ }
+
+ _uiState.value = ExploreUiState.Success(
+ trendingRepos = trendingRepos,
+ activityFeed = activityFeed
)
- )
- _uiState.value = ExploreUiState(activityFeed = mocks)
+ }
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ apiClient.close()
}
}
diff --git a/composeApp/src/commonMain/kotlin/dev/therealashik/github/theme/Dimensions.kt b/composeApp/src/commonMain/kotlin/dev/therealashik/github/theme/Dimensions.kt
index 2923d94..7f8c884 100644
--- a/composeApp/src/commonMain/kotlin/dev/therealashik/github/theme/Dimensions.kt
+++ b/composeApp/src/commonMain/kotlin/dev/therealashik/github/theme/Dimensions.kt
@@ -28,4 +28,7 @@ object Dimensions {
val Zero = 0.dp
val ReleaseBannerHeight = 100.dp
+
+ val TrendingRepoCardWidth = 280.dp
+ val TrendingRepoCardHeight = 140.dp
}