Skip to content

Commit

Permalink
Allow staring and unstarring repos within the repo details page (#33)
Browse files Browse the repository at this point in the history
Co-authored-by: Sculas <contact@sculas.xyz>
Co-authored-by: Wing <44992537+wingio@users.noreply.github.com>
  • Loading branch information
3 people authored Nov 14, 2023
1 parent ba2cddb commit b03f001
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
fragment RepoDetails on Repository {
id
description
readme {
contentHTML
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ inline fun <T> GraphQLResponse<T>.ifSuccessful(block: (T) -> Unit) {
}
}

inline fun <T> GraphQLResponse<T>.ifUnsuccessful(block: (String) -> Unit) {
fold(
onSuccess = {},
onError = block
)
}

fun <T> GraphQLResponse<T>.getOrNull(): T? = when (this) {
is GraphQLResponse.Success -> data
is GraphQLResponse.Error,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,64 +3,72 @@ package com.materiiapps.gloom.ui.components
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun RowScope.LargeSegmentedButton(
icon: Any,
text: String,
onClick: () -> Unit,
iconDescription: String? = null,
text: String
enabled: Boolean = true
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterVertically),
modifier = Modifier
.clickable(enabled = enabled, role = Role.Button) { onClick() }
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp))
.weight(1f)
.padding(16.dp)
) {
when (icon) {
is ImageVector -> {
Icon(
imageVector = icon,
contentDescription = iconDescription,
tint = MaterialTheme.colorScheme.primary
)
}
CompositionLocalProvider(
LocalContentColor provides MaterialTheme.colorScheme.primary.copy(if (enabled) 1f else 0.5f)
) {
when (icon) {
is ImageVector -> {
Icon(
imageVector = icon,
contentDescription = iconDescription
)
}

is Painter -> {
Icon(
painter = icon,
contentDescription = iconDescription,
tint = MaterialTheme.colorScheme.primary
)
is Painter -> {
Icon(
painter = icon,
contentDescription = iconDescription
)
}
}
}

Text(
text = text,
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
maxLines = 1,
modifier = Modifier.basicMarquee(Int.MAX_VALUE)
)
Text(
text = text,
style = MaterialTheme.typography.labelLarge,
maxLines = 1,
modifier = Modifier.basicMarquee(Int.MAX_VALUE)
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,15 @@ class DetailsTab(
res = Res.plurals.stars,
count = repoDetails.stargazerCount,
repoDetails.stargazerCount
)
),
onClick = viewModel::toggleStar,
enabled = !viewModel.isStarLoading
)
repoDetails.licenseInfo?.let {
LargeSegmentedButton(
icon = Icons.Custom.Balance,
text = it.nickname ?: it.key.uppercase()
text = it.nickname ?: it.key.uppercase(),
onClick = { /* TODO */ }
)
}
LargeSegmentedButton(
Expand All @@ -176,7 +179,8 @@ class DetailsTab(
res = Res.plurals.forks,
count = repoDetails.forkCount,
repoDetails.forkCount
)
),
onClick = { /* TODO */ }
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import com.materiiapps.gloom.api.repository.GraphQLRepository
import com.materiiapps.gloom.api.utils.fold
import com.materiiapps.gloom.api.utils.ifUnsuccessful
import com.materiiapps.gloom.gql.fragment.RepoDetails
import kotlinx.coroutines.launch

Expand All @@ -22,6 +23,8 @@ class RepoDetailsViewModel(
var detailsLoading by mutableStateOf(false)
var hasError by mutableStateOf(false)

var isStarLoading by mutableStateOf(false)

init {
loadDetails()
}
Expand All @@ -42,4 +45,33 @@ class RepoDetailsViewModel(
}
}

private fun updateStarDetails(starred: Boolean) {
details = details?.copy(
viewerHasStarred = starred,
stargazerCount = details!!.stargazerCount + if (starred) 1 else -1
)
}

fun toggleStar() {
val details = details!!
val hasStarred = details.viewerHasStarred

// Optimistic update
updateStarDetails(starred = !hasStarred)

coroutineScope.launch {
isStarLoading = true

if (hasStarred) {
gql.unstarRepo(details.id)
} else {
gql.starRepo(details.id)
}.ifUnsuccessful {
// Revert optimistic update
updateStarDetails(starred = hasStarred)
}
isStarLoading = false
}
}

}

0 comments on commit b03f001

Please sign in to comment.