Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement merchandise distribution #74

Merged
merged 8 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies {
implementation(project(mapOf("path" to ":attendance")))
implementation(project(mapOf("path" to ":auth")))
implementation(project(mapOf("path" to ":base")))
implementation(project(mapOf("path" to ":merchandise")))
implementation(project(mapOf("path" to ":navigation")))

// Dependencies
Expand Down Expand Up @@ -47,6 +48,7 @@ dependencies {

implementation(MaterialDependencies.material_android)

implementation(NetworkDependencies.moshi_adapters)
implementation(NetworkDependencies.moshi_converter_factory)
implementation(NetworkDependencies.okhttp)
implementation(platform(NetworkDependencies.okhttp_bom))
Expand Down
150 changes: 96 additions & 54 deletions app/src/main/java/org/robojackets/apiary/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.outlined.Contactless
import androidx.compose.material.icons.outlined.Storefront
import androidx.compose.material.navigation.ModalBottomSheetLayout
import androidx.compose.material.navigation.bottomSheet
import androidx.compose.material.navigation.rememberBottomSheetNavigator
Expand All @@ -21,6 +22,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
Expand Down Expand Up @@ -58,7 +60,10 @@ import org.robojackets.apiary.auth.ui.AuthenticationScreen
import org.robojackets.apiary.base.GlobalSettings
import org.robojackets.apiary.base.model.AttendableType
import org.robojackets.apiary.base.ui.nfc.NfcRequired
import org.robojackets.apiary.base.ui.snackbar.SnackbarControllerProvider
import org.robojackets.apiary.base.ui.theme.Apiary_MobileTheme
import org.robojackets.apiary.merchandise.ui.MerchandiseDistributionScreen
import org.robojackets.apiary.merchandise.ui.MerchandiseIndexScreen
import org.robojackets.apiary.navigation.NavigationActions
import org.robojackets.apiary.navigation.NavigationDestinations
import org.robojackets.apiary.navigation.NavigationManager
Expand Down Expand Up @@ -87,6 +92,14 @@ sealed class Screen(
"contactless"
)

object Merchandise :
Screen(
NavigationDestinations.merchandiseSubgraph,
R.string.merchandise,
Icons.Outlined.Storefront,
"storefront",
)

object Settings :
Screen(
NavigationDestinations.settingsSubgraph,
Expand Down Expand Up @@ -139,6 +152,7 @@ class MainActivity : ComponentActivity() {

val navItems = listOf(
Screen.Attendance,
Screen.Merchandise,
Screen.Settings,
)

Expand Down Expand Up @@ -170,64 +184,67 @@ class MainActivity : ComponentActivity() {
navReady = true
}

// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colorScheme.background) {
ModalBottomSheetLayout(bottomSheetNavigator) {
UpdateGate(
navReady = navReady,
onShowRequiredUpdatePrompt = {
navigationManager.navigate(
NavigationActions.UpdatePrompts.anyScreenToRequiredUpdatePrompt()
)
},
onShowOptionalUpdatePrompt = {
navigationManager.navigate(
NavigationActions.UpdatePrompts.anyScreenToOptionalUpdatePrompt()
)
},
onShowUpdateInProgressScreen = {
navigationManager.navigate(
NavigationActions.UpdatePrompts.anyScreenToUpdateInProgress()
)
}
) {
Scaffold(
topBar = { AppTopBar(settings.appEnv.production) },
bottomBar = {
val current = currentRoute(navController)
if (shouldShowBottomNav(nfcEnabled, current)) {
NavigationBar {
navItems.forEach { screen ->
NavigationBarItem(
icon = {
Icon(
screen.icon,
contentDescription = screen.imgContentDescriptor
)
},
label = { Text(stringResource(screen.resourceId)) },
selected = currentDestination
?.hierarchy
?.any {
it.route == screen.navigationDestination
} == true,
onClick = {
navigationManager.navigate(
NavigationActions.BottomNavTabs.withinBottomNavTabs(
screen.navigationDestination,
navController.graph.findStartDestination().id
SnackbarControllerProvider { snackbarHost ->
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colorScheme.background) {
ModalBottomSheetLayout(bottomSheetNavigator) {
UpdateGate(
navReady = navReady,
onShowRequiredUpdatePrompt = {
navigationManager.navigate(
NavigationActions.UpdatePrompts.anyScreenToRequiredUpdatePrompt()
)
},
onShowOptionalUpdatePrompt = {
navigationManager.navigate(
NavigationActions.UpdatePrompts.anyScreenToOptionalUpdatePrompt()
)
},
onShowUpdateInProgressScreen = {
navigationManager.navigate(
NavigationActions.UpdatePrompts.anyScreenToUpdateInProgress()
)
}
) {
Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHost) },
topBar = { AppTopBar(settings.appEnv.production) },
bottomBar = {
val current = currentRoute(navController)
if (shouldShowBottomNav(nfcEnabled, current)) {
NavigationBar {
navItems.forEach { screen ->
NavigationBarItem(
icon = {
Icon(
screen.icon,
contentDescription = screen.imgContentDescriptor
)
},
label = { Text(stringResource(screen.resourceId)) },
selected = currentDestination
?.hierarchy
?.any {
it.route == screen.navigationDestination
} == true,
onClick = {
navigationManager.navigate(
NavigationActions.BottomNavTabs.withinBottomNavTabs(
screen.navigationDestination,
navController.graph.findStartDestination().id
)
)
)
}
)
}
)
}
}
}
}
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
NfcRequired(nfcEnabled = nfcEnabled) {
AppNavigation(navController)
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
NfcRequired(nfcEnabled = nfcEnabled) {
AppNavigation(navController)
}
}
}
}
Expand Down Expand Up @@ -311,6 +328,31 @@ class MainActivity : ComponentActivity() {
)
}
}

navigation(
startDestination = NavigationDestinations.merchandiseIndex,
route = NavigationDestinations.merchandiseSubgraph
) {
composable(NavigationDestinations.merchandiseIndex) {
MerchandiseIndexScreen(hiltViewModel())
}

composable(
route = "${NavigationDestinations.merchandiseDistribution}/{merchandiseItemId}",
arguments = listOf(
navArgument("merchandiseItemId") { type = NavType.IntType },
)
) {
val merchandiseItemId = it.arguments?.getInt("merchandiseItemId")

MerchandiseDistributionScreen(
hiltViewModel(),
nfcLib,
merchandiseItemId as Int
)
}
}

navigation(
startDestination = NavigationDestinations.settings,
route = NavigationDestinations.settingsSubgraph,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.nxp.nfclib.NxpNfcLib
import com.skydoves.sandwich.retrofit.adapters.ApiResponseCallAdapterFactory
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -25,6 +26,7 @@ import org.robojackets.apiary.base.service.ServerInfoApiService
import org.robojackets.apiary.network.UserAgentInterceptor
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import java.util.Date

@Module
@InstallIn(ActivityRetainedComponent::class)
Expand Down Expand Up @@ -68,6 +70,7 @@ object MainActivityModule {
Types.newParameterizedType(List::class.java, Permission::class.java),
SkipNotFoundEnumInEnumListAdapter(Permission::class.java)
)
.add(Date::class.java, Rfc3339DateJsonAdapter())
.build()
}

Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<resources>
<string name="app_name">MyRJ</string>
<string name="attendance">Attendance</string>
<string name="merchandise">Merchandise</string>
<string name="settings">Settings</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ package org.robojackets.apiary.attendance.model
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.skydoves.sandwich.StatusCode
import com.skydoves.sandwich.message
import com.skydoves.sandwich.onError
import com.skydoves.sandwich.onException
import com.skydoves.sandwich.onFailure
import com.skydoves.sandwich.onSuccess
import com.skydoves.sandwich.retrofit.statusCode
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
Expand Down Expand Up @@ -77,34 +74,16 @@ class AttendableTypeSelectionViewModel @Inject constructor(

viewModelScope.launch {
loadingUserPermissions.value = true
userRepository.getLoggedInUserInfo().onSuccess {
val missingPermissions = getMissingPermissions(this.data.user.allPermissions, requiredPermissions)
userMissingPermissions.value = missingPermissions
user.value = this.data.user
}
.onError {
when {
statusCode.code >= StatusCode.InternalServerError.code ->
Timber.e(this.message())
else -> Timber.w(this.message())
}

permissionsCheckError.value = when {
this.statusCode.code >= StatusCode.InternalServerError.code ->
"A server error occurred while checking if you have permission to " +
"use this feature. Check your internet connection and try " +
"again, or ask in #it-helpdesk for assistance."
else ->
"An error occurred while checking if you have permission to use " +
"this feature. Check your internet connection and try again, or " +
"ask in #it-helpdesk for assistance."
}
userRepository.getLoggedInUserInfo()
.onSuccess {
val missingPermissions =
getMissingPermissions(this.data.user.allPermissions, requiredPermissions)
userMissingPermissions.value = missingPermissions
user.value = this.data.user
}
.onException {
Timber.e(this.throwable)
permissionsCheckError.value = "An error occurred while checking if you have " +
"permission to use this feature. Check your internet connection and " +
"try again, or ask in #it-helpdesk for assistance."
.onFailure {
Timber.e(this.message())
permissionsCheckError.value = "Error while checking permissions"
}
.also {
loadingUserPermissions.value = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class AttendanceViewModel @Inject constructor(
fun loadAttendables(attendableType: AttendableType, forceRefresh: Boolean = false) {
error.value = null
loadingAttendables.value = true

viewModelScope.launch {
if (attendableType == AttendableType.Team &&
(attendableTeams.value.isEmpty() || forceRefresh)
Expand All @@ -146,9 +147,10 @@ class AttendanceViewModel @Inject constructor(
}.onException {
Timber.e(this.message, "Could not fetch attendable teams due to an exception")
error.value = "Unable to fetch teams"
}.also {
loadingAttendables.value = false
}
}
if (attendableType == AttendableType.Event &&
} else if (attendableType == AttendableType.Event &&
(attendableEvents.value.isEmpty() || forceRefresh)
) {
meetingsRepository.getEvents().onSuccess {
Expand All @@ -159,9 +161,12 @@ class AttendanceViewModel @Inject constructor(
}.onException {
Timber.e(this.message, "Could not fetch attendable events due to an exception")
error.value = "Unable to fetch events"
}.also {
loadingAttendables.value = false
}
} else {
loadingAttendables.value = false
}
loadingAttendables.value = false
}
}

Expand Down
Loading
Loading