Skip to content

Commit

Permalink
Merge pull request geteduroam#4 from geteduroam/feature/select-instit…
Browse files Browse the repository at this point in the history
…ution

Handle institution selection and prepare for profile selection
  • Loading branch information
IuliaSTANA authored Aug 2, 2022
2 parents 509e964 + d19a9a1 commit 85370a2
Show file tree
Hide file tree
Showing 17 changed files with 428 additions and 193 deletions.
2 changes: 2 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ android {
dependencies {
implementation project(":shared:core")
implementation Libs.Kotlin.stdlibJdk
implementation Libs.Ktor.clientSerializationJvm

implementation Libs.Kotlin.Coroutines.android
implementation Libs.AndroidX.Activity.activityCompose
implementation Libs.AndroidX.appcompat
Expand Down
10 changes: 0 additions & 10 deletions android/app/src/main/java/app/eduroam/geteduroam/EduTopAppBar.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package app.eduroam.geteduroam

import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
Expand Down Expand Up @@ -37,14 +35,6 @@ fun EduTopAppBar(
navigationIcon = navigationIcon,
scrollBehavior = scrollBehavior,
colors = foregroundColors,
actions = {
IconButton(onClick = {}) {
Icon(
imageVector = Icons.Outlined.Search,
contentDescription = "Search"
)
}
},
modifier = Modifier.windowInsetsPadding(
WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top)
)
Expand Down
21 changes: 15 additions & 6 deletions android/app/src/main/java/app/eduroam/geteduroam/NavGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import app.eduroam.geteduroam.welcome.SelectInstitutionScreen
import app.eduroam.geteduroam.institutions.SelectInstitutionScreen
import app.eduroam.shared.select.SelectInstitutionViewModel
import co.touchlab.kermit.Logger

Expand All @@ -13,10 +13,19 @@ fun NavGraph(viewModel: SelectInstitutionViewModel, log: Logger) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = SelectInstitution
startDestination = Screens.SelectInstitution.route
) {
composable(SelectInstitution) { SelectInstitutionScreen(viewModel, log) }
composable(Screens.SelectInstitution.route) {
SelectInstitutionScreen(
viewModel = viewModel,
gotToProfileSelection = { it -> navController.navigate("${Screens.SelectProfile.route}/$it") }
)
}
composable(
route = Screens.SelectProfile.routeWithArgs,
arguments = Screens.SelectProfile.arguments
) { backStackEntry ->
//TODO: open profile screen
}
}
}

private const val SelectInstitution = "selectInstitution"
}
17 changes: 17 additions & 0 deletions android/app/src/main/java/app/eduroam/geteduroam/Screens.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package app.eduroam.geteduroam

import androidx.navigation.NavType
import androidx.navigation.navArgument

sealed class Screens(val route: String) {
object SelectProfile : Screens(route = "select_profile") {
const val urlArg = "url"
val routeWithArgs = "${route}/{${urlArg}}"
val arguments = listOf(navArgument(urlArg) {
type = NavType.StringType
defaultValue = ""
})
}

object SelectInstitution : Screens(route = "select_institution")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package app.eduroam.geteduroam.institutions

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import app.eduroam.shared.response.Institution


@Composable
fun InstitutionRow(
institution: Institution,
onSelectInstitution: (Institution) -> Unit,
modifier: Modifier = Modifier,
) = Column(
modifier
.fillMaxWidth()
.clickable { onSelectInstitution(institution) }) {
Spacer(Modifier.height(8.dp))
Text(
text = institution.name,
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colorScheme.primary
)
Spacer(Modifier.height(4.dp))
Text(
text = institution.country,
style = MaterialTheme.typography.bodySmall,
)
Spacer(Modifier.height(8.dp))
Divider(
Modifier
.height(0.5.dp)
.fillMaxWidth()
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package app.eduroam.geteduroam.institutions

import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import app.eduroam.geteduroam.R
import app.eduroam.geteduroam.ui.theme.AppTheme

@Composable
fun InstitutionSearchHeader(
searchText: String, onSearchTextChange: (String) -> Unit, modifier: Modifier = Modifier,
) = Column(modifier.fillMaxWidth()) {
val focusManager = LocalFocusManager.current
Text(
text = stringResource(id = R.string.institution_select_title),
style = MaterialTheme.typography.headlineSmall,
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = searchText,
onValueChange = onSearchTextChange,
singleLine = true,
placeholder = {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Icon(
imageVector = Icons.Default.Search,
contentDescription = "",
modifier = Modifier.size(ButtonDefaults.IconSize)
)
Spacer(Modifier.width(8.dp))
Text(
text = stringResource(id = R.string.institution_search_text),
color = MaterialTheme.colorScheme.secondary,
modifier = Modifier.weight(1f),
fontWeight = FontWeight.Light
)
}
},

keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done, keyboardType = KeyboardType.Text
),
keyboardActions = KeyboardActions {
focusManager.clearFocus()
},
modifier = Modifier.fillMaxWidth(),
)
}


@ExperimentalMaterial3Api
@Preview
@Composable
private fun MarketPlaceHeader_Preview() {
AppTheme {
InstitutionSearchHeader("filterOn", {})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package app.eduroam.geteduroam.institutions

import android.content.Context
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.flowWithLifecycle
import app.eduroam.shared.response.Institution
import app.eduroam.shared.select.SelectInstitutionViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json


@Composable
fun rememberSelectInstitutionState(
viewModel: SelectInstitutionViewModel,
goToProfileSelection: (String) -> Unit,
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
context: Context = LocalContext.current,
coroutineScope: CoroutineScope = rememberCoroutineScope(),
): SelectInstitutionState {
val currentGoToProfileSelection by rememberUpdatedState(goToProfileSelection)
return remember(viewModel, lifecycleOwner, context, coroutineScope) {
SelectInstitutionState(
viewModel = viewModel,
coroutineScope = coroutineScope,
lifecycleOwner = lifecycleOwner,
goToProfileSelection = currentGoToProfileSelection,
)
}
}

@Stable
class SelectInstitutionState(
private val viewModel: SelectInstitutionViewModel,
coroutineScope: CoroutineScope,
goToProfileSelection: (String) -> Unit,
lifecycleOwner: LifecycleOwner,
) {
init {
coroutineScope.launch {
viewModel.currentInstitution.flowWithLifecycle(lifecycleOwner.lifecycle).collectLatest {
if (it != null) {
val institutionArgument = Json.encodeToString(it)
goToProfileSelection(institutionArgument)
}
}
}
}

fun onSelectInstitution(selectedInstitution: Institution) {
viewModel.onInstitutionSelect(selectedInstitution)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package app.eduroam.geteduroam.institutions

import android.annotation.SuppressLint
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Scaffold
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.flowWithLifecycle
import app.eduroam.geteduroam.EduTopAppBar
import app.eduroam.geteduroam.R
import app.eduroam.shared.models.DataState
import app.eduroam.shared.models.ItemDataSummary
import app.eduroam.shared.select.SelectInstitutionViewModel

@Composable
fun SelectInstitutionScreen(
viewModel: SelectInstitutionViewModel,
gotToProfileSelection: (String) -> Unit,
selectInstitutionState: SelectInstitutionState = rememberSelectInstitutionState(viewModel, gotToProfileSelection),
) {
val lifecycleOwner = LocalLifecycleOwner.current
val lifecycleAwareUiDataStateFlow = remember(viewModel.uiDataState, lifecycleOwner) {
viewModel.uiDataState.flowWithLifecycle(lifecycleOwner.lifecycle)
}

@SuppressLint("StateFlowValueCalledInComposition") // False positive lint check when used inside collectAsState()
val uiDataState: DataState<ItemDataSummary> by lifecycleAwareUiDataStateFlow.collectAsState(viewModel.uiDataState.value)

SelectInstitutionContent(
institutionsState = uiDataState,
selectInstitutionState = selectInstitutionState,
searchText = uiDataState.data?.filterOn.orEmpty(),
onSearchTextChange = { viewModel.onSearchTextChange(it) },
)
}

@Composable
fun SelectInstitutionContent(
institutionsState: DataState<ItemDataSummary>,
selectInstitutionState: SelectInstitutionState,
searchText: String,
onSearchTextChange: (String) -> Unit = {},
) = Scaffold(
topBar = {
EduTopAppBar(stringResource(R.string.name))
}
) { paddingValues ->
Column(
Modifier
.padding(paddingValues)
.fillMaxSize()
.systemBarsPadding()
.padding(horizontal = 16.dp)
) {
LazyColumn {
item {
InstitutionSearchHeader(
searchText = searchText,
onSearchTextChange = onSearchTextChange,
modifier = Modifier.fillMaxWidth()
)

Spacer(Modifier.height(8.dp))
}

if (institutionsState.loading) {
item {
LinearProgressIndicator(
modifier = Modifier.fillMaxWidth()
)
}
} else if (institutionsState.exception != null) {
item {
Text(
text = institutionsState.exception.orEmpty(),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodyLarge,
)
}
} else {
if (institutionsState.empty) {
item {
Text(stringResource(id = R.string.institutions_no_results))
}
} else {

item {
Text(
text = stringResource(id = R.string.institutions_choose_one),
style = MaterialTheme.typography.bodyMedium,
)
Spacer(Modifier.height(8.dp))
}

institutionsState.data?.institutions?.forEach { institution ->
item {
InstitutionRow(institution, { selectInstitutionState.onSelectInstitution(it) })
}
}
}
}
}
}
}

Loading

0 comments on commit 85370a2

Please sign in to comment.