9 Commits

Author SHA1 Message Date
KaasKop-
4ac5fe7392 Update 2024-01-29 20:57:00 +01:00
KaasKop-
a1d17919e5 Update 2023-12-20 19:58:30 +01:00
KaasKop-
e401b3e34b Basic item editing implemented. 2023-04-23 16:07:23 +02:00
KaasKop-
159d96f7f3 Updated AGP to 8.0
The itemlist amount updating works

Started work on the item edit.
2023-04-21 15:54:48 +02:00
KaasKop-
28f5c22bf2 Added savedStateHandle to the viewmodel
Updated the thuisserver reflecting what does what.
2023-04-15 15:35:17 +02:00
KaasKop-
5a00cd8f32 make DateTimeAdded optional
Trying to make amount dynamic

Fix in route
2023-04-10 21:23:02 +02:00
Mitchel
da2c98ce83 Updated item list view 2023-04-10 15:28:29 +02:00
KaasKop-
79ed64760c Freezer item listt working, updated path, updated navigation updated home 2023-04-07 13:04:23 +02:00
KaasKop-
0630cd048e Architecture changes, updated MainActivity.kt to be simpler.
Updated colors

Changed main language to english.

Updated kotlin version.
2023-04-06 14:39:32 +02:00
55 changed files with 1252 additions and 631 deletions

123
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,123 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

2
.idea/compiler.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" /> <bytecodeTargetLevel target="17" />
</component> </component>
</project> </project>

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<targetSelectedWithDropDown>
<Target>
<type value="QUICK_BOOT_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="$USER_HOME$/.android/avd/Pixel_4_XL_API_31.avd" />
</Key>
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2022-11-06T10:26:15.426480Z" />
</component>
</project>

2
.idea/gradle.xml generated
View File

@@ -7,7 +7,7 @@
<option name="testRunner" value="GRADLE" /> <option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" /> <option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="Embedded JDK" /> <option name="gradleJvm" value="jbr-17" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />

View File

@@ -3,27 +3,35 @@
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
</profile> </profile>
</component> </component>

6
.idea/kotlinc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.10" />
</component>
</project>

3
.idea/misc.xml generated
View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@@ -5,14 +5,14 @@ plugins {
android { android {
namespace 'com.mitchelbv.thuis_c' namespace 'com.mitchelbv.thuis_c'
compileSdk 33 compileSdk 34
defaultConfig { defaultConfig {
applicationId "com.mitchelbv.thuis_c" applicationId "com.mitchelbv.thuis_c"
minSdk 30 minSdk 30
targetSdk 33 targetSdk 34
versionCode 1 versionCode 2
versionName "1.0" versionName "1.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {
@@ -37,7 +37,7 @@ android {
compose true compose true
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion '1.3.2' kotlinCompilerExtensionVersion '1.4.4'
} }
packagingOptions { packagingOptions {
resources { resources {
@@ -49,28 +49,31 @@ android {
dependencies { dependencies {
//Network //Network
implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// Display images from the internet // Display images from the internet
implementation "io.coil-kt:coil-compose:2.2.2" implementation 'io.coil-kt:coil-compose:2.5.0'
// Database // Database
implementation "androidx.room:room-runtime:2.5.0" implementation 'androidx.room:room-runtime:2.6.0'
annotationProcessor("androidx.room:room-compiler:2.5.0") annotationProcessor('androidx.room:room-compiler:2.6.0')
// Navigation // Navigation
implementation "androidx.navigation:navigation-compose:2.5.3" implementation 'androidx.navigation:navigation-compose:2.7.5'
// ViewModel // ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1" implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2'
implementation('androidx.lifecycle:lifecycle-runtime-compose:2.6.2')
implementation('androidx.compose.runtime:runtime-livedata:1.5.4')
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.activity:activity-compose:1.6.1' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
implementation 'androidx.activity:activity-compose:1.8.0'
implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.compose.material3:material3:1.0.1' implementation 'androidx.compose.material3:material3:1.1.2'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

View File

@@ -14,6 +14,7 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Thuis" android:theme="@style/Theme.Thuis"
android:usesCleartextTraffic="true"
tools:targetApi="31"> tools:targetApi="31">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"

View File

@@ -1,90 +1,151 @@
package com.mitchelbv.thuis_c package com.mitchelbv.thuis_c
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavType
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.mitchelbv.thuis_c.ui.card.CardsScreen import androidx.navigation.navArgument
import com.mitchelbv.thuis_c.ui.feyenoord.FeyenoordScreen import com.mitchelbv.thuis_c.network.thuis.responses.FreezerItem
import com.mitchelbv.thuis_c.ui.freezer.FreezerScreen
import com.mitchelbv.thuis_c.ui.freezer.detailed.FreezerDetailedScreen
import com.mitchelbv.thuis_c.ui.home.HomeScreen
import com.mitchelbv.thuis_c.ui.recipe.RecipeScreen
import com.mitchelbv.thuis_c.ui.theme.ThuisTheme import com.mitchelbv.thuis_c.ui.theme.ThuisTheme
import com.mitchelbv.thuis_c.views.feyenoord.MatchesScreen
import com.mitchelbv.thuis_c.views.freezer.FreezerScreen
import com.mitchelbv.thuis_c.views.freezer.item_list.FreezerItemListScreen
import com.mitchelbv.thuis_c.views.freezer.item_list.edit_item.FreezerItemEditScreen
import com.mitchelbv.thuis_c.views.home.HomeScreen
import com.mitchelbv.thuis_c.views.recipe.RecipeScreen
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
val navigationBarItems = listOf(HomeDestination, RecipesDestination, MatchesDestination)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
ThuisTheme { ThuisTheme {
// A surface container using the 'background' color from the theme LayoutWithNavSetup();
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
ThuisApp()
}
} }
} }
} }
}
val screenItems = listOf( @OptIn(ExperimentalMaterial3Api::class)
Screen.Home, @Composable
Screen.Recipes, fun LayoutWithNavSetup() {
Screen.Feyenoord, val navController = rememberNavController()
) var selectedItem by remember { mutableStateOf(0) }
val currentRoute = navController
.currentBackStackEntryFlow
.collectAsState(initial = navController.currentBackStackEntry)
@OptIn(ExperimentalMaterial3Api::class) Scaffold(
@Composable topBar = {
fun ThuisApp() { TopAppBar(title = {
val navController = rememberNavController() Text(stringResource(id = R.string.app_name))
val navBackStackEntry by navController.currentBackStackEntryAsState() })
Scaffold( },
topBar = { bottomBar = {
TopAppBar(title = { Text(stringResource(id = R.string.app_name))}) NavigationBar {
}, navigationBarItems.forEachIndexed { index, screen ->
bottomBar = { NavigationBarItem(
NavigationBar { icon = {
val currentDestination = navBackStackEntry?.destination Icon(
screenItems.forEach { screen -> screen.icon,
NavigationBarItem( contentDescription = stringResource(id = screen.destinationName)
icon = { Icon(screen.icon, contentDescription = stringResource(id = screen.resourceId))}, )
label = { Text(stringResource(id = screen.resourceId))}, },
selected = currentDestination?.hierarchy?.any {it.route == screen.route} == true, label = { Text(stringResource(id = screen.destinationName)) },
onClick = { selected = selectedItem == index,
navController.navigate(screen.route) { onClick = {
popUpTo(navController.graph.findStartDestination().id) { navController.navigate(screen.route)
saveState = true selectedItem = index
} })
launchSingleTop = true }
restoreState = true }
},
floatingActionButton = {
when (currentRoute.value?.destination?.route?.substringBefore("/")) {
FreezerListDestination.route -> {
FloatingActionButton(onClick = {
}) {
Icon(Icons.Default.Add, contentDescription = "idk")
} }
}
FreezerItemListDestination.route -> {
FloatingActionButton(onClick = {
}) {
Icon(Icons.Default.Add, contentDescription = "idk")
}
}
else -> {}
}
},
floatingActionButtonPosition = FabPosition.End
) { innerPadding ->
NavHost(
navController = navController,
startDestination = HomeDestination.route,
modifier = Modifier.padding(innerPadding)
) {
// Navigation items
composable(route = HomeDestination.route) {
HomeScreen(onClickFreezers = {
navController.navigate(
FreezerListDestination.route
)
}, onClickCards = { navController.navigate(CardsDestination.route) })
}
composable(route = RecipesDestination.route) { RecipeScreen() }
composable(route = MatchesDestination.route) { MatchesScreen() }
// Freezer screens
composable(route = FreezerListDestination.route) {
FreezerScreen(
onClickToFreezer = { freezer_id ->
navController.navigate("${FreezerItemListDestination.route}/${freezer_id}")
}
)
}
// Items list
composable(
route = "${FreezerItemListDestination.route}/{freezer_id}", arguments = listOf(
navArgument("freezer_id") {
this.type = NavType.IntType
this.nullable = false
})
) {
// this is called everytime you enter AND leave the view, so update the freezerlist like this?
val test = it.savedStateHandle.getLiveData<FreezerItem>("updatedFreezer")
Log.d("IDK", "${test.value?.item}")
FreezerItemListScreen(
onEditItem = { freezerItem ->
// I want onEditItem to open a modal dialog, so not sure if this is needed here...
navController.navigate("${FreezerItemDetailDestination.route}/${freezerItem}")
}) })
} }
// Edit items
composable(route = "${FreezerItemDetailDestination.route}/{freezer_id}",
arguments = listOf(
navArgument("freezer_id") {
this.type = NavType.IntType
this.nullable = false
}
)) {
FreezerItemEditScreen { navController.popBackStack() }
}
} }
} }
) { innerPadding ->
NavHost(navController = navController, startDestination = "home", Modifier.padding(innerPadding)) {
composable("home") { HomeScreen(navController) }
composable("feyenoord") { FeyenoordScreen(navHostController = navController) }
composable("recipes") { RecipeScreen(navHostController = navController)}
composable("freezer") {FreezerScreen(navHostController = navController)}
composable("cards") { CardsScreen(navHostController = navController) }
composable("freezer-details/{freezerId}") { backStackEntry -> FreezerDetailedScreen(navHostController = navController, freezerId = backStackEntry.arguments?.getString("freezerId")) }
composable("freezer-edit") { CardsScreen(navHostController = navController) }
}
} }
} }

View File

@@ -1,17 +0,0 @@
package com.mitchelbv.thuis_c
import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.navigation.NavArgs
sealed class Screen(val route: String, @StringRes val resourceId: Int, val icon: ImageVector) {
object Feyenoord : Screen("feyenoord", R.string.nav_feyenoord, Icons.Default.DateRange)
object Recipes : Screen("recipes", R.string.nav_recipes, Icons.Default.List)
object Home : Screen("home", R.string.nav_home, Icons.Default.Home)
object Cards : Screen("cards", R.string.home_card_cards, Icons.Default.Call)
object Freezer : Screen("freezer", R.string.home_card_freezer, Icons.Default.DateRange)
object FreezerDetails : Screen("freezer-details", R.string.home_card_freezer, Icons.Default.ShoppingCart)
object FreezerEdit : Screen("freezer-edit", R.string.edit_freezer, Icons.Default.Call)
}

View File

@@ -0,0 +1,55 @@
package com.mitchelbv.thuis_c
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.ui.graphics.vector.ImageVector
interface ThuisDestination {
val icon: ImageVector
val destinationName: Int
val route: String
}
object HomeDestination : ThuisDestination {
override val icon = Icons.Default.Home
override val destinationName = R.string.nav_home
override val route = "home"
}
object RecipesDestination : ThuisDestination {
override val icon = Icons.Default.List
override val destinationName = R.string.nav_recipes
override val route = "recipes"
}
object MatchesDestination : ThuisDestination {
override val icon = Icons.Default.DateRange
override val destinationName = R.string.nav_matches
override val route = "matches"
}
// Freezers
object FreezerListDestination : ThuisDestination {
override val icon = Icons.Default.DateRange
override val destinationName = R.string.home_card_freezer
override val route = "freezer_list"
}
object FreezerItemListDestination : ThuisDestination {
override val icon = Icons.Default.DateRange
override val destinationName = R.string.home_card_freezer
override val route = "freezer_item_list"
}
object FreezerItemDetailDestination : ThuisDestination {
override val icon = Icons.Default.DateRange
override val destinationName = R.string.home_card_freezer
override val route = "freezer_item_detail"
}
// Cards
object CardsDestination : ThuisDestination {
override val icon = Icons.Default.DateRange
override val destinationName = R.string.home_card_cards
override val route = "cards"
}

View File

@@ -4,7 +4,7 @@ import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
object FeyenoordRetrofitHelper { object FeyenoordRetrofitHelper {
val baseUrl = "https://tickets-api.feyenoord.nl/api/" val baseUrl = "https://feyenoord.api.tymes4-infra.com/v2/"
private val retrofit = Retrofit.Builder() private val retrofit = Retrofit.Builder()
.baseUrl(baseUrl) .baseUrl(baseUrl)

View File

@@ -1,10 +1,15 @@
package com.mitchelbv.thuis_c.network.feyenoord package com.mitchelbv.thuis_c.network.feyenoord
import com.mitchelbv.thuis_c.network.feyenoord.responses.FeyenoordEventResponse import com.mitchelbv.thuis_c.network.feyenoord.responses.FeyenoordEventResponse
import com.mitchelbv.thuis_c.network.feyenoord.responses.FeyenoordTokenResponse
import retrofit2.Call import retrofit2.Call
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header
interface FeyenoordService { interface FeyenoordService {
@GET("event") @GET("Event")
suspend fun getEvents(): FeyenoordEventResponse suspend fun getEvents(@Header("Authorization") token: String): FeyenoordEventResponse
@GET("Account/default-token")
suspend fun getDefaultToken(): FeyenoordTokenResponse
} }

View File

@@ -1,23 +0,0 @@
package com.mitchelbv.thuis_c.network.feyenoord.responses
import com.google.gson.annotations.SerializedName
data class ButtonData(
@SerializedName("EventId") var EventId: Int? = null,
@SerializedName("SaleCategoryId") var SaleCategoryId: Int? = null,
@SerializedName("TranslationCode") var TranslationCode: String? = null,
@SerializedName("ButtonStyle") var ButtonStyle: Int? = null,
@SerializedName("ActionType") var ActionType: Int? = null,
@SerializedName("Action") var Action: String? = null,
@SerializedName("ActiveFrom") var ActiveFrom: String? = null,
@SerializedName("ActiveTill") var ActiveTill: String? = null,
@SerializedName("OpenInNewTab") var OpenInNewTab: Boolean? = null,
@SerializedName("InitiativeId") var InitiativeId: String? = null,
@SerializedName("Id") var Id: Int? = null,
@SerializedName("Created") var Created: String? = null,
@SerializedName("Modified") var Modified: String? = null,
@SerializedName("Deleted") var Deleted: String? = null
)

View File

@@ -4,7 +4,7 @@ import com.google.gson.annotations.SerializedName
data class FeyenoordEventResponse( data class FeyenoordEventResponse(
@SerializedName("HeaderItem") var HeaderItem: HeaderItem? = HeaderItem(), @Transient @SerializedName("HeaderItem") var HeaderItem: Nothing? = null,
@SerializedName("TabItems") var TabItems: ArrayList<TabItems> = arrayListOf(), @SerializedName("TabItems") var TabItems: ArrayList<TabItems> = arrayListOf(),
@SerializedName("CrossSellInfo") var CrossSellInfo: String? = null @Transient @SerializedName("CrossSellInfo") var CrossSellInfo: Nothing? = null
) )

View File

@@ -0,0 +1,13 @@
package com.mitchelbv.thuis_c.network.feyenoord.responses
import com.google.gson.annotations.SerializedName
data class FeyenoordTokenResponse(
@SerializedName("AuthResult") var AuthResult: Int? = null,
@SerializedName("Token") var Token: String? = null,
@Transient @SerializedName("CurrentChannel") var CurrentChannel: Nothing? = null,
@Transient @SerializedName("SalesChannels") var SalesChannels: Nothing? = null,
)

View File

@@ -1,63 +0,0 @@
package com.mitchelbv.thuis_c.network.feyenoord.responses
import com.google.gson.annotations.SerializedName
data class HeaderItem(
@SerializedName("DisplayCountdown") var DisplayCountdown: Boolean? = null,
@SerializedName("ShowResellInHeader") var ShowResellInHeader: Boolean? = null,
@SerializedName("ResellTicketsAllowed") var ResellTicketsAllowed: Boolean? = null,
@SerializedName("ResellTicketsFrom") var ResellTicketsFrom: String? = null,
@SerializedName("ResellTicketsTill") var ResellTicketsTill: String? = null,
@SerializedName("EventId") var EventId: Int? = null,
@SerializedName("SaleCategoryId") var SaleCategoryId: Int? = null,
@SerializedName("HasGeneralSale") var HasGeneralSale: Boolean? = null,
@SerializedName("VisibleInShop") var VisibleInShop: Boolean? = null,
@SerializedName("HighlightInShop") var HighlightInShop: Boolean? = null,
@SerializedName("HasSoldOut") var HasSoldOut: Boolean? = null,
@SerializedName("OnGeneralSaleFrom") var OnGeneralSaleFrom: String? = null,
@SerializedName("OnGeneralSaleTill") var OnGeneralSaleTill: String? = null,
@SerializedName("VisibleInShopFrom") var VisibleInShopFrom: String? = null,
@SerializedName("VisibleInShopTill") var VisibleInShopTill: String? = null,
@SerializedName("Name") var Name: String? = null,
@SerializedName("NameHomeTeam") var NameHomeTeam: String? = null,
@SerializedName("NameAwayTeam") var NameAwayTeam: String? = null,
@SerializedName("EventStartDateTime") var EventStartDateTime: String? = null,
@SerializedName("EventStartDateTimeOffset") var EventStartDateTimeOffset: String? = null,
@SerializedName("EventEndDateTime") var EventEndDateTime: String? = null,
@SerializedName("CategoryTranslationCode") var CategoryTranslationCode: String? = null,
@SerializedName("SaleCategoryOrder") var SaleCategoryOrder: Int? = null,
@SerializedName("ImageId") var ImageId: String? = null,
@SerializedName("ImageFileName") var ImageFileName: String? = null,
@SerializedName("ImageUniqueName") var ImageUniqueName: String? = null,
@SerializedName("HomeImageId") var HomeImageId: Int? = null,
@SerializedName("HomeImageFileName") var HomeImageFileName: String? = null,
@SerializedName("HomeImageUniqueName") var HomeImageUniqueName: String? = null,
@SerializedName("AwayImageId") var AwayImageId: Int? = null,
@SerializedName("AwayImageFileName") var AwayImageFileName: String? = null,
@SerializedName("AwayImageUniqueName") var AwayImageUniqueName: String? = null,
@SerializedName("HasTicketsAvailable") var HasTicketsAvailable: Boolean? = null,
@SerializedName("HasMarketplaceTicketsAvailable") var HasMarketplaceTicketsAvailable: Boolean? = null,
@SerializedName("HasPossibleTicketsInFuture") var HasPossibleTicketsInFuture: Boolean? = null,
@SerializedName("CurrentlyOnSaleForUser") var CurrentlyOnSaleForUser: Boolean? = null,
@SerializedName("PurchaseRightAvailableAfterLogin") var PurchaseRightAvailableAfterLogin: Boolean? = null,
@SerializedName("ImageUrl") var ImageUrl: String? = null,
@SerializedName("HomeImageUrl") var HomeImageUrl: String? = null,
@SerializedName("AwayImageUrl") var AwayImageUrl: String? = null,
@SerializedName("EventTypeName") var EventTypeName: String? = null,
@SerializedName("EventTypeLogoId") var EventTypeLogoId: Int? = null,
@SerializedName("EventTypeLogoUrl") var EventTypeLogoUrl: String? = null,
@SerializedName("EventTypeLogoFileName") var EventTypeLogoFileName: String? = null,
@SerializedName("EventTypeLogoUniqueName") var EventTypeLogoUniqueName: String? = null,
@SerializedName("InfoUrl") var InfoUrl: String? = null,
@SerializedName("EventStartDateTimeFormatted") var EventStartDateTimeFormatted: EventStartDateTimeFormatted? = EventStartDateTimeFormatted(),
@SerializedName("EventEndDateTimeFormatted") var EventEndDateTimeFormatted: EventEndDateTimeFormatted? = EventEndDateTimeFormatted(),
@SerializedName("ButtonData") var ButtonData: ButtonData? = ButtonData(),
@SerializedName("ShowMarketplaceForEvent") var ShowMarketplaceForEvent: Boolean? = null,
@SerializedName("RedirectToMarketplace") var RedirectToMarketplace: Boolean? = null,
@SerializedName("MarketplaceButtonTextColor") var MarketplaceButtonTextColor: String? = null,
@SerializedName("MarketplaceButtonBackgroundColor") var MarketplaceButtonBackgroundColor: String? = null,
@SerializedName("DeepLinkRoute") var DeepLinkRoute: String? = null
)

View File

@@ -51,9 +51,7 @@ data class Items(
@SerializedName("EventTypeLogoFileName") var EventTypeLogoFileName: String? = null, @SerializedName("EventTypeLogoFileName") var EventTypeLogoFileName: String? = null,
@SerializedName("EventTypeLogoUniqueName") var EventTypeLogoUniqueName: String? = null, @SerializedName("EventTypeLogoUniqueName") var EventTypeLogoUniqueName: String? = null,
@SerializedName("InfoUrl") var InfoUrl: String? = null, @SerializedName("InfoUrl") var InfoUrl: String? = null,
@SerializedName("EventStartDateTimeFormatted") var EventStartDateTimeFormatted: EventStartDateTimeFormatted? = EventStartDateTimeFormatted(), @Transient @SerializedName("ButtonData") var ButtonData: Nothing? = null,
@SerializedName("EventEndDateTimeFormatted") var EventEndDateTimeFormatted: EventEndDateTimeFormatted? = EventEndDateTimeFormatted(),
@SerializedName("ButtonData") var ButtonData: ButtonData? = ButtonData(),
@SerializedName("ShowMarketplaceForEvent") var ShowMarketplaceForEvent: Boolean? = null, @SerializedName("ShowMarketplaceForEvent") var ShowMarketplaceForEvent: Boolean? = null,
@SerializedName("RedirectToMarketplace") var RedirectToMarketplace: Boolean? = null, @SerializedName("RedirectToMarketplace") var RedirectToMarketplace: Boolean? = null,
@SerializedName("MarketplaceButtonTextColor") var MarketplaceButtonTextColor: String? = null, @SerializedName("MarketplaceButtonTextColor") var MarketplaceButtonTextColor: String? = null,

View File

@@ -1,6 +1,7 @@
package com.mitchelbv.thuis_c.network.thuis package com.mitchelbv.thuis_c.network.thuis
import com.mitchelbv.thuis_c.network.thuis.responses.* import com.mitchelbv.thuis_c.network.thuis.responses.*
import retrofit2.Response
import retrofit2.http.* import retrofit2.http.*
interface ThuisService { interface ThuisService {
@@ -43,18 +44,18 @@ interface ThuisService {
suspend fun getFreezerItems(): FreezerItems suspend fun getFreezerItems(): FreezerItems
@GET("api/FreezerItem/{id}") @GET("api/FreezerItem/{id}")
suspend fun getFreezerItem(id: Int): FreezerItem suspend fun getFreezerItem(@Path("id") id: Int): FreezerItem
@GET("api/FreezerItem/InFreezer/{id}") @GET("api/FreezerItem/InFreezer/{id}")
suspend fun getFreezerItemsInFreezer(id: Int): FreezerItemsInFreezer suspend fun getFreezerItemsInFreezer(@Path("id") id: Int): List<FreezerItem>
// Post // Post (used to create a new item)
@POST("api/FreezerItem") @POST("api/FreezerItem")
suspend fun postFreezerItem(@Body freezerItem: FreezerItem): FreezerItem suspend fun postFreezerItem(@Body freezerItem: FreezerItem): FreezerItem
// Put // Put (used to update an item)
@PUT("api/FreezerItem/{id}") @PUT("api/FreezerItem/{id}")
suspend fun putFreezerItem(@Body freezerItem: FreezerItem): FreezerItem suspend fun putFreezerItem(@Path("id") id: Int, @Body freezerItem: FreezerItem): List<FreezerItem>
// Delete // Delete
@DELETE("api/FreezerItem/{id}") @DELETE("api/FreezerItem/{id}")

View File

@@ -4,6 +4,6 @@ import com.google.gson.annotations.SerializedName
data class Freezer( data class Freezer(
@SerializedName("freezerId") var freezerId: Int? = null, @SerializedName("freezerId") var freezerId: Int? = null,
@SerializedName("location") var location: String? = null, @SerializedName("name") var name: String? = null,
@SerializedName("amountInFreezer") var amountInFreezer: Int? = null @SerializedName("amountInFreezer") var amountInFreezer: Int? = null
) )

View File

@@ -8,7 +8,7 @@ data class FreezerItem(
@SerializedName("item") var item: String? = null, @SerializedName("item") var item: String? = null,
@SerializedName("amount") var amount: Int? = null, @SerializedName("amount") var amount: Int? = null,
@SerializedName("drawer") var drawer: Int? = null, @SerializedName("drawer") var drawer: Int? = null,
@SerializedName("dateTimeAdded") var dateTimeAdded: String? = null, @SerializedName("dateTimeAdded") var dateTimeAdded: String? = "",
@SerializedName("freezerId") var freezerId: Int? = null @SerializedName("freezerId") var freezerId: Int? = null
) )

View File

@@ -1,13 +0,0 @@
package com.mitchelbv.thuis_c.network.thuis.responses
import com.google.gson.annotations.SerializedName
data class FreezerItemsInFreezer(
@SerializedName("freezerItemId") var freezerItemId: Int? = null,
@SerializedName("item") var item: String? = null,
@SerializedName("amount") var amount: Int? = null,
@SerializedName("drawer") var drawer: Int? = null,
@SerializedName("dateTimeAdded") var dateTimeAdded: String? = null,
@SerializedName("freezerId") var freezerId: Int? = null
)

View File

@@ -1,54 +0,0 @@
package com.mitchelbv.thuis_c.ui.feyenoord
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mitchelbv.thuis_c.network.feyenoord.FeyenoordRetrofitHelper
import com.mitchelbv.thuis_c.network.feyenoord.FeyenoordService
import com.mitchelbv.thuis_c.network.feyenoord.responses.FeyenoordEventResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
data class FeyenoordUiState(
val matches: List<Match> = listOf()
)
class FeyenoordViewModel : ViewModel() {
private val _uiState = MutableStateFlow(FeyenoordUiState())
val uiState: StateFlow<FeyenoordUiState> = _uiState.asStateFlow()
init {
fillMatches()
}
private fun fillMatches() {
val tempMatches = mutableListOf<Match>()
viewModelScope.launch(Dispatchers.IO) {
val apiResponse = FeyenoordRetrofitHelper.feyenoord.getEvents()
for (tabItem in apiResponse.TabItems) {
val test = arrayOf(tabItem).filter { it.CategoryId == 2 }
test.forEach {
for (item in it.Items) {
tempMatches.add(Match(
home_team = item.NameHomeTeam!!,
away_team = item.NameAwayTeam!!,
date = item.EventStartDateTimeFormatted?.Full!!,
begin_time = item.EventStartDateTimeFormatted?.Time!!,
end_time = item.EventEndDateTimeFormatted?.Time!!,
away_image = item.AwayImageUrl!!,
home_image = item.HomeImageUrl!!
))
}
}
}
_uiState.value = FeyenoordUiState(matches = tempMatches)
}
}
}

View File

@@ -1,108 +0,0 @@
package com.mitchelbv.thuis_c.ui.freezer
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.mitchelbv.thuis_c.R
import com.mitchelbv.thuis_c.Screen
import com.mitchelbv.thuis_c.network.thuis.responses.Freezer
@Composable
fun FreezerScreen(
navHostController: NavHostController,
freezerViewModel: FreezerViewModel = viewModel()
) {
val freezerUiState by freezerViewModel.uiState.collectAsState()
FreezerList(freezerUiState.freezers, navHostController, Modifier)
FloatingActionButton(onClick = { navHostController.navigate("edit_freezer") }) {
Icon(Icons.Default.Edit, contentDescription = "Edit")
}
}
@Composable
fun FreezerList(freezers: List<Freezer>, navHostController: NavHostController, modifier: Modifier) {
if (freezers.isNotEmpty()) {
LazyColumn {
items(freezers) { freezer ->
FreezerListItem(
freezer = freezer,
navigateTo = { navHostController.navigate("${Screen.FreezerDetails.route}/${freezer.freezerId}") },
modifier = modifier
)
}
}
} else {
Text(stringResource(id = R.string.freezer_list_empty))
}
}
@Composable
fun FreezerListItem(freezer: Freezer, navigateTo: () -> Unit, modifier: Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.height(150.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(freezer.location!!)
Column(
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.Center,
modifier = modifier.fillMaxHeight()
) {
Icon(
painter = painterResource(id = R.drawable.arrow),
contentDescription = "arrow",
modifier = modifier.size(20.dp)
)
Text("${freezer.amountInFreezer} artikelen")
}
}
Spacer(
modifier = Modifier
.fillMaxWidth()
.background(Color(0f, 0f, 0f, .5f))
.height(1.dp)
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FreezerCard(freezer: Freezer, navigateTo: () -> Unit) {
Card(
onClick = { navigateTo() }, modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.padding(10.dp)
) {
Text(freezer.location!!)
}
}
@Preview(widthDp = 250, showBackground = true, heightDp = 300)
@Composable
fun FreezerListItemPreview() {
val freezer = Freezer(freezerId = 1, location = "Keuken", amountInFreezer = 5)
FreezerListItem(freezer = freezer, modifier = Modifier, navigateTo = {})
}

View File

@@ -1,10 +0,0 @@
package com.mitchelbv.thuis_c.ui.freezer.detailed
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
@Composable
fun FreezerDetailedScreen(navHostController: NavHostController, freezerId: String?) {
Text("Hi!, requesting for freezer $freezerId")
}

View File

@@ -1,30 +0,0 @@
package com.mitchelbv.thuis_c.ui.freezer.detailed
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mitchelbv.thuis_c.network.thuis.ThuisRetrofitHelper
import com.mitchelbv.thuis_c.network.thuis.responses.Freezer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.net.ConnectException
data class FreezerEditUiState(
var freezerName: String = "",
)
class FreezerEditViewModel : ViewModel() {
private val _uiState = MutableStateFlow(FreezerEditUiState())
val uiState: StateFlow<FreezerEditUiState> = _uiState.asStateFlow()
init {
fillFreezerTextField()
}
private fun fillFreezerTextField() {
// Todo
}
}

View File

@@ -1,30 +0,0 @@
package com.mitchelbv.thuis_c.ui.freezer.edit
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.mitchelbv.thuis_c.ui.freezer.detailed.FreezerEditViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FreezerEditScreen(
navHostController: NavHostController,
freezerEditViewModel: FreezerEditViewModel = viewModel(),
freezerId: String?) {
val freezerUiState by freezerEditViewModel.uiState.collectAsState()
val freezerName by remember { mutableStateOf(freezerUiState.freezerName)}
Text("Hi!, editing (or creating) freezer. $freezerId")
Column {
TextField(value = freezerName, onValueChange = { freezerUiState.freezerName = it })
Button(onClick = { /* TODO */ }) {
}
}
}

View File

@@ -1,56 +0,0 @@
package com.mitchelbv.thuis_c.ui.home
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.mitchelbv.thuis_c.ui.theme.Typography
import com.mitchelbv.thuis_c.R
import com.mitchelbv.thuis_c.Screen
@Composable
fun HomeScreen(navController: NavHostController) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("De thuis app!", style = Typography.bodySmall)
// Row(modifier = Modifier.height(100.dp).width(180.dp)) {
// HomeCard(cardId = R.drawable.storefront, cardText = R.string.home_card_cards, routeTo = Screen.Cards.route, navController = navController)
// HomeCard(cardId = R.drawable.feyenoord, cardText = R.string.home_card_freezer, routeTo = Screen.Freezer.route, navController = navController)
HomeButton(buttonText = R.string.home_card_cards, routeTo = Screen.Cards.route, navController = navController)
HomeButton(buttonText = R.string.home_card_freezer, routeTo = Screen.Freezer.route, navController = navController)
// }
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeCard(cardId: Int, cardText: Int, routeTo: String, navController: NavHostController) {
Card(onClick = { navController.navigate(routeTo) }) {
Row(modifier = Modifier.fillMaxSize()) {
Icon(
painterResource(cardId), contentDescription = null, modifier = Modifier
.padding(end = 10.dp)
)
Text(
text = stringResource(id = cardText),
Modifier
.padding(10.dp)
)
}
}
}
@Composable
fun HomeButton(buttonText: Int, routeTo: String, navController: NavHostController) {
Button(onClick = { navController.navigate(routeTo) }) {
Text(stringResource(id = buttonText))
}
}

View File

@@ -1,49 +0,0 @@
package com.mitchelbv.thuis_c.ui.home
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mitchelbv.thuis_c.Screen
import com.mitchelbv.thuis_c.network.feyenoord.FeyenoordRetrofitHelper
import com.mitchelbv.thuis_c.network.feyenoord.FeyenoordService
import com.mitchelbv.thuis_c.ui.feyenoord.Match
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
data class HomeUiState(
val upcomingMatch: Match? = null
)
class HomeViewModel : ViewModel() {
private val _uiState = MutableStateFlow(HomeUiState())
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
init {
getUpcomingMatch()
}
fun notNullHandler(test: String?): String {
if (!test.isNullOrEmpty()) {
return test;
}
return ""
}
private fun getUpcomingMatch() {
viewModelScope.launch {
val apiResponse = FeyenoordRetrofitHelper.feyenoord.getEvents().HeaderItem!!
val match = Match(
home_team = notNullHandler(apiResponse.NameHomeTeam),
away_team = notNullHandler(apiResponse.NameAwayTeam),
date = notNullHandler(apiResponse.EventStartDateTimeFormatted?.Full),
begin_time = notNullHandler(apiResponse.EventStartDateTimeFormatted?.Time),
end_time = notNullHandler(apiResponse.EventEndDateTimeFormatted?.Time),
away_image = notNullHandler(apiResponse.AwayImageUrl),
home_image = notNullHandler(apiResponse.HomeImageUrl)
)
_uiState.value = HomeUiState(upcomingMatch = match)
}
}
}

View File

@@ -2,10 +2,67 @@ package com.mitchelbv.thuis_c.ui.theme
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF) val md_theme_light_primary = Color(0xFF9F3087)
val PurpleGrey80 = Color(0xFFCCC2DC) val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val Pink80 = Color(0xFFEFB8C8) val md_theme_light_primaryContainer = Color(0xFFFFD8EE)
val md_theme_light_onPrimaryContainer = Color(0xFF3A0030)
val md_theme_light_secondary = Color(0xFF3154C8)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFDCE1FF)
val md_theme_light_onSecondaryContainer = Color(0xFF001551)
val md_theme_light_tertiary = Color(0xFF425AA9)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFDCE1FF)
val md_theme_light_onTertiaryContainer = Color(0xFF00164F)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFF)
val md_theme_light_onBackground = Color(0xFF231A00)
val md_theme_light_surface = Color(0xFFFFFBFF)
val md_theme_light_onSurface = Color(0xFF231A00)
val md_theme_light_surfaceVariant = Color(0xFFEFDEE6)
val md_theme_light_onSurfaceVariant = Color(0xFF4F444A)
val md_theme_light_outline = Color(0xFF81737A)
val md_theme_light_inverseOnSurface = Color(0xFFFFF0CB)
val md_theme_light_inverseSurface = Color(0xFF3C2F00)
val md_theme_light_inversePrimary = Color(0xFFFFADE4)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF9F3087)
val md_theme_light_outlineVariant = Color(0xFFD2C2CA)
val md_theme_light_scrim = Color(0xFF000000)
val Purple40 = Color(0xFF6650a4) val md_theme_dark_primary = Color(0xFFFFADE4)
val PurpleGrey40 = Color(0xFF625b71) val md_theme_dark_onPrimary = Color(0xFF5F004F)
val Pink40 = Color(0xFF7D5260) val md_theme_dark_primaryContainer = Color(0xFF81126D)
val md_theme_dark_onPrimaryContainer = Color(0xFFFFD8EE)
val md_theme_dark_secondary = Color(0xFFB7C4FF)
val md_theme_dark_onSecondary = Color(0xFF002681)
val md_theme_dark_secondaryContainer = Color(0xFF0A3AB0)
val md_theme_dark_onSecondaryContainer = Color(0xFFDCE1FF)
val md_theme_dark_tertiary = Color(0xFFB6C4FF)
val md_theme_dark_onTertiary = Color(0xFF082978)
val md_theme_dark_tertiaryContainer = Color(0xFF284190)
val md_theme_dark_onTertiaryContainer = Color(0xFFDCE1FF)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF231A00)
val md_theme_dark_onBackground = Color(0xFFFFE086)
val md_theme_dark_surface = Color(0xFF231A00)
val md_theme_dark_onSurface = Color(0xFFFFE086)
val md_theme_dark_surfaceVariant = Color(0xFF4F444A)
val md_theme_dark_onSurfaceVariant = Color(0xFFD2C2CA)
val md_theme_dark_outline = Color(0xFF9B8D94)
val md_theme_dark_inverseOnSurface = Color(0xFF231A00)
val md_theme_dark_inverseSurface = Color(0xFFFFE086)
val md_theme_dark_inversePrimary = Color(0xFF9F3087)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFFFFADE4)
val md_theme_dark_outlineVariant = Color(0xFF4F444A)
val md_theme_dark_scrim = Color(0xFF000000)
val seed = Color(0xFF9F3087)

View File

@@ -6,39 +6,85 @@ import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
private val DarkColorScheme = darkColorScheme( private val LightColors = lightColorScheme(
primary = Purple80, primary = md_theme_light_primary,
secondary = PurpleGrey80, onPrimary = md_theme_light_onPrimary,
tertiary = Pink80 primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
) )
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override private val DarkColors = darkColorScheme(
background = Color(0xFFFFFBFE), primary = md_theme_dark_primary,
surface = Color(0xFFFFFBFE), onPrimary = md_theme_dark_onPrimary,
onPrimary = Color.White, primaryContainer = md_theme_dark_primaryContainer,
onSecondary = Color.White, onPrimaryContainer = md_theme_dark_onPrimaryContainer,
onTertiary = Color.White, secondary = md_theme_dark_secondary,
onBackground = Color(0xFF1C1B1F), onSecondary = md_theme_dark_onSecondary,
onSurface = Color(0xFF1C1B1F), secondaryContainer = md_theme_dark_secondaryContainer,
*/ onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
) )
@Composable @Composable
fun ThuisTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { fun ThuisTheme(
val colors = if (darkTheme) { useDarkTheme: Boolean = isSystemInDarkTheme(),
DarkColorScheme content: @Composable() () -> Unit
) {
val colors = if (!useDarkTheme) {
LightColors
} else { } else {
LightColorScheme DarkColors
} }
MaterialTheme( MaterialTheme(
colorScheme = colors, colorScheme = colors,
typography = Typography, content = content,
content = content typography = ThuisTypography
) )
} }

View File

@@ -7,15 +7,14 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with // Set of Material typography styles to start with
val Typography = Typography( val ThuisTypography = Typography(
bodyLarge = TextStyle( bodyLarge = TextStyle(
fontFamily = FontFamily.Default, fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
fontSize = 16.sp, fontSize = 16.sp,
lineHeight = 24.sp, lineHeight = 24.sp,
letterSpacing = 0.5.sp letterSpacing = 0.5.sp
) ),
/* Other default text styles to override
titleLarge = TextStyle( titleLarge = TextStyle(
fontFamily = FontFamily.Default, fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
@@ -30,5 +29,4 @@ val Typography = Typography(
lineHeight = 16.sp, lineHeight = 16.sp,
letterSpacing = 0.5.sp letterSpacing = 0.5.sp
) )
*/
) )

View File

@@ -1,4 +1,4 @@
package com.mitchelbv.thuis_c.ui.card package com.mitchelbv.thuis_c.views.card
data class Card( data class Card(
val id: Int, val id: Int,

View File

@@ -1,4 +1,4 @@
package com.mitchelbv.thuis_c.ui.card package com.mitchelbv.thuis_c.views.card
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope

View File

@@ -1,4 +1,4 @@
package com.mitchelbv.thuis_c.ui.card package com.mitchelbv.thuis_c.views.card
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.material3.* import androidx.compose.material3.*

View File

@@ -1,4 +1,4 @@
package com.mitchelbv.thuis_c.ui.feyenoord package com.mitchelbv.thuis_c.views.feyenoord
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@@ -15,12 +15,11 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.mitchelbv.thuis_c.R import com.mitchelbv.thuis_c.R
@Composable @Composable
fun FeyenoordScreen(feyenoordViewModel: FeyenoordViewModel = viewModel(), navHostController: NavHostController) { fun MatchesScreen(feyenoordViewModel: FeyenoordViewModel = viewModel()) {
val feyenoordUiState by feyenoordViewModel.uiState.collectAsState() val feyenoordUiState by feyenoordViewModel.uiState.collectAsState()
Column( Column(
modifier = Modifier modifier = Modifier
@@ -41,16 +40,16 @@ fun MatchList(matches: List<Match>, modifier: Modifier) {
} }
@Composable @Composable
fun TeamNameWithLogo(team_name: String, team_logo_url: String, modifier: Modifier) { fun TeamNameWithLogo(teamName: String, teamLogoUrl: String, modifier: Modifier) {
Column(modifier = modifier.size(128.dp).fillMaxWidth(), Arrangement.Center, Alignment.CenterHorizontally) { Column(modifier = modifier.size(128.dp).fillMaxWidth(), Arrangement.Center, Alignment.CenterHorizontally) {
AsyncImage( AsyncImage(
model = team_logo_url, model = teamLogoUrl,
contentDescription = team_name, contentDescription = teamName,
placeholder = painterResource(id = R.drawable.placeholder), placeholder = painterResource(id = R.drawable.placeholder),
contentScale = ContentScale.Fit, contentScale = ContentScale.Fit,
modifier = modifier.size(64.dp) modifier = modifier.size(64.dp)
) )
Text(text = team_name, modifier = modifier.fillMaxWidth().wrapContentWidth(Alignment.CenterHorizontally)) Text(text = teamName, modifier = modifier.fillMaxWidth().wrapContentWidth(Alignment.CenterHorizontally))
} }
} }
@@ -66,8 +65,8 @@ fun MatchCard(match: Match, modifier: Modifier) {
.height(128.dp) .height(128.dp)
) { ) {
TeamNameWithLogo( TeamNameWithLogo(
team_name = match.home_team, teamName = match.home_team,
team_logo_url = match.home_image, teamLogoUrl = match.home_image,
modifier = modifier modifier = modifier
) )
Column( Column(
@@ -82,8 +81,8 @@ fun MatchCard(match: Match, modifier: Modifier) {
} }
} }
TeamNameWithLogo( TeamNameWithLogo(
team_name = match.away_team, teamName = match.away_team,
team_logo_url = match.away_image, teamLogoUrl = match.away_image ?: "",
modifier = modifier modifier = modifier
) )
@@ -98,7 +97,7 @@ fun PreviewMatchCard() {
away_image = "https://tymes4-cdn.azureedge.net/feyenoord/291-S.S._Lazio_badge.svg-min-original.png", away_image = "https://tymes4-cdn.azureedge.net/feyenoord/291-S.S._Lazio_badge.svg-min-original.png",
home_team = "Feyenoord Vrouwen 1", home_team = "Feyenoord Vrouwen 1",
away_team = "FC Twente Vrouwen 1", away_team = "FC Twente Vrouwen 1",
date = "do 03 November 2023", date = "03 Nov 2024",
begin_time = "18:03", begin_time = "18:03",
end_time = "20:03" end_time = "20:03"
) )

View File

@@ -0,0 +1,64 @@
package com.mitchelbv.thuis_c.views.feyenoord
import androidx.compose.ui.text.toLowerCase
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mitchelbv.thuis_c.network.feyenoord.FeyenoordRetrofitHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.util.Locale
data class FeyenoordUiState(
val matches: List<Match> = listOf()
)
class FeyenoordViewModel : ViewModel() {
private val _uiState = MutableStateFlow(FeyenoordUiState())
val uiState: StateFlow<FeyenoordUiState> = _uiState.asStateFlow()
init {
fillMatches()
}
private fun fillMatches() {
val tempMatches = mutableListOf<Match>()
viewModelScope.launch(Dispatchers.IO) {
val getToken = FeyenoordRetrofitHelper.feyenoord.getDefaultToken().Token
val apiResponse = FeyenoordRetrofitHelper.feyenoord.getEvents("Bearer $getToken")
val dayOfWeek = arrayOf("Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo")
for (tabItem in apiResponse.TabItems) {
val test = arrayOf(tabItem).filter { it.CategoryId == 2 }
test.forEach {
for (item in it.Items) {
val utcDateBegin = LocalDateTime.parse(item.EventStartDateTime)
val beginDate = utcDateBegin.atZone(ZoneId.of("UTC"))
.withZoneSameInstant(ZoneId.of("Europe/Amsterdam")).toLocalDateTime()
val utcDateEnd = LocalDateTime.parse(item.EventEndDateTime)
val endDate = utcDateEnd.atZone(ZoneId.of("UTC"))
.withZoneSameInstant(ZoneId.of("Europe/Amsterdam")).toLocalDateTime()
tempMatches.add(
Match(
home_team = item.NameHomeTeam!!,
away_team = item.NameAwayTeam!!,
date = "${dayOfWeek[beginDate.dayOfWeek.value - 1]} ${beginDate.dayOfMonth} ${beginDate.month.toString()
.lowercase(Locale.getDefault())}",
begin_time = "${beginDate.toLocalTime()}",
end_time = "${endDate.toLocalTime()}",
away_image = item.AwayImageUrl,
home_image = item.HomeImageUrl!!
)
)
}
}
}
_uiState.value = FeyenoordUiState(matches = tempMatches)
}
}
}

View File

@@ -1,8 +1,8 @@
package com.mitchelbv.thuis_c.ui.feyenoord package com.mitchelbv.thuis_c.views.feyenoord
data class Match( data class Match(
val home_image: String, val home_image: String,
val away_image: String, val away_image: String?,
val home_team: String, val home_team: String,
val away_team: String, val away_team: String,
val date: String, val date: String,

View File

@@ -0,0 +1,105 @@
package com.mitchelbv.thuis_c.views.freezer
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.mitchelbv.thuis_c.R
import com.mitchelbv.thuis_c.network.thuis.responses.Freezer
import com.mitchelbv.thuis_c.ui.theme.ThuisTypography
import kotlinx.coroutines.flow.StateFlow
lateinit var freezerUiState: State<FreezerUiState>
@Composable
fun FreezerScreen(
freezerViewModel: FreezerViewModel = viewModel(),
onClickToFreezer: (freezer_id: Int) -> Unit
) {
freezerUiState = freezerViewModel.uiState.collectAsState()
FreezerList(onClickToFreezer, Modifier)
}
@Composable
fun FreezerList(
onClickToFreezer: (freezer_id: Int) -> Unit,
modifier: Modifier
) {
if (freezerUiState.value.loading) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
CircularProgressIndicator()
}
}
if (freezerUiState.value.freezers.isNotEmpty() && !freezerUiState.value.loading) {
LazyColumn {
items(freezerUiState.value.freezers) { freezer ->
FreezerListItem(
freezer = freezer,
navigateTo = { freezer.freezerId?.let { onClickToFreezer(it) } },
modifier = modifier
)
}
}
} else {
Text(stringResource(id = R.string.freezer_list_empty))
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FreezerListItem(freezer: Freezer, navigateTo: () -> Unit, modifier: Modifier) {
Column {
ListItem(
headlineContent = { Text(freezer.name!!) },
supportingContent = { Text(" ${freezer.amountInFreezer} " + stringResource(id = R.string.freezer_item_count_partial)) },
trailingContent = { Icon(Icons.Default.ArrowForward, "")},
modifier = modifier.clickable { navigateTo() }
)
Divider()
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FreezerCard(freezer: Freezer, navigateTo: () -> Unit) {
Card(
onClick = { navigateTo() }, modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.padding(10.dp)
) {
Text(freezer.name!!)
}
}
@Preview(widthDp = 250, showBackground = true, heightDp = 300)
@Composable
fun FreezerListItemPreview() {
val freezer = Freezer(freezerId = 1, name = "Keuken", amountInFreezer = 5)
Column {
FreezerListItem(freezer = freezer, modifier = Modifier, navigateTo = {})
FreezerListItem(freezer = freezer, modifier = Modifier, navigateTo = {})
FreezerListItem(freezer = freezer, modifier = Modifier, navigateTo = {})
}
}

View File

@@ -1,4 +1,4 @@
package com.mitchelbv.thuis_c.ui.freezer package com.mitchelbv.thuis_c.views.freezer
import android.util.Log import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
@@ -13,7 +13,8 @@ import kotlinx.coroutines.launch
import java.net.ConnectException import java.net.ConnectException
data class FreezerUiState( data class FreezerUiState(
val freezers: List<Freezer> = listOf() val freezers: List<Freezer> = listOf(),
val loading: Boolean = true
) )
class FreezerViewModel : ViewModel() { class FreezerViewModel : ViewModel() {
@@ -28,9 +29,9 @@ class FreezerViewModel : ViewModel() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
try { try {
val apiResponse = ThuisRetrofitHelper.thuis.getFreezers() val apiResponse = ThuisRetrofitHelper.thuis.getFreezers()
_uiState.value = FreezerUiState(apiResponse) _uiState.value = FreezerUiState(freezers = apiResponse, loading = false)
} catch (e: ConnectException) { } catch (e: ConnectException) {
_uiState.value = FreezerUiState(listOf()) _uiState.value = FreezerUiState(listOf(), loading = false)
} }
} }

View File

@@ -0,0 +1,82 @@
package com.mitchelbv.thuis_c.views.freezer.item_list
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.window.Dialog
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.mitchelbv.thuis_c.R
import com.mitchelbv.thuis_c.network.thuis.responses.FreezerItem
import com.mitchelbv.thuis_c.ui.theme.ThuisTypography
// Is the destination of FreezerItemListDestination
@Composable
fun FreezerItemListScreen(
freezerItemListViewModel: FreezerItemListViewModel = viewModel(),
onEditItem: (Int) -> Unit
) {
val freezerItemListUiState by freezerItemListViewModel.uiState.collectAsStateWithLifecycle()
val itemObserver = freezerItemListUiState.items.observeAsState()
Surface {
itemObserver.value?.forEach { freezerItem ->
FreezerItemElement(
freezerItem = freezerItem,
arrowPressed = { item_id: Int, newAmount: Int ->
freezerItemListViewModel.UpdateItemAmount(
item_id,
newAmount
)
},
onEditItem = onEditItem
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FreezerItemElement(
freezerItem: FreezerItem,
arrowPressed: (Int, Int) -> Unit,
onEditItem: (Int) -> Unit
) {
ListItem(
headlineContent = { Text("${freezerItem.item}") },
supportingContent = { Text("${stringResource(R.string.freezer_item_drawer_partial)} ${freezerItem.drawer}") },
trailingContent = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly
) {
TextButton(onClick = {
arrowPressed(
freezerItem.freezerItemId!!,
freezerItem.amount!!.minus(1)
)
}) {
Icon(Icons.Default.KeyboardArrowDown, contentDescription = "Minus one")
}
Text("${freezerItem.amount}", style = ThuisTypography.bodyLarge)
TextButton(onClick = {
arrowPressed(
freezerItem.freezerItemId!!,
freezerItem.amount!!.plus(1)
)
}) {
Icon(Icons.Default.KeyboardArrowUp, contentDescription = "Plus one")
}
TextButton(onClick = { onEditItem(freezerItem.freezerItemId!!) }) {
Icon(Icons.Default.Edit, contentDescription = "Edit ${freezerItem.item}")
}
}
}
)
}

View File

@@ -0,0 +1,72 @@
package com.mitchelbv.thuis_c.views.freezer.item_list
import android.util.Log
import androidx.lifecycle.*
import com.mitchelbv.thuis_c.network.thuis.ThuisRetrofitHelper
import com.mitchelbv.thuis_c.network.thuis.responses.FreezerItem
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.net.ConnectException
import java.net.SocketTimeoutException
data class FreezerItemListUiState(
val items: MutableLiveData<List<FreezerItem>> = MutableLiveData<List<FreezerItem>>(),
var error: String = ""
)
class FreezerItemListViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
private val _uiState = MutableStateFlow(FreezerItemListUiState())
val uiState: StateFlow<FreezerItemListUiState> = _uiState.asStateFlow()
init {
savedStateHandle.get<Int>("freezer_id")?.let { fillItemList(it) }
}
fun fillItemList(freezer_id: Int) {
viewModelScope.launch(Dispatchers.IO) {
try {
val apiResponse =
ThuisRetrofitHelper.thuis.getFreezerItemsInFreezer(id = freezer_id)
_uiState.value = FreezerItemListUiState(MutableLiveData(apiResponse))
} catch (e: ConnectException) {
// _uiState.value = FreezerItemListUiState(listOf(FreezerItem(item = )))
_uiState.value = FreezerItemListUiState()
}
}
}
// new model instead of this?
fun UpdateItemAmount(item_id: Int, newAmount: Int) {
viewModelScope.launch(Dispatchers.IO) {
try {
val itemList = _uiState.value.items.value!!.toMutableList()
val indexFreezerItem =
itemList.indexOf(uiState.value.items.value!!.find { a -> a.freezerItemId == item_id })
val freezerItemToUpdate = itemList[indexFreezerItem]
freezerItemToUpdate.amount = newAmount
// itemList[indexFreezerItem] = freezerItemToUpdate
val apiResponse =
ThuisRetrofitHelper.thuis.putFreezerItem(item_id, freezerItemToUpdate)
_uiState.update { currentState ->
currentState.copy(items = MutableLiveData(apiResponse))
}
} catch (e: HttpException) {
// TODO Display a snackbar...
Log.d(
this.javaClass.name.toString(),
"HttpException: ${e.code()} ${e.response()!!.errorBody()!!.string()}"
)
} catch (e: SocketTimeoutException) {
// TODO Also display a snackbar
Log.d(this.javaClass.name.toString(), "Timeout")
}
}
}
}

View File

@@ -0,0 +1,179 @@
package com.mitchelbv.thuis_c.views.freezer.item_list.edit_item
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.text.isDigitsOnly
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.mitchelbv.thuis_c.R
import com.mitchelbv.thuis_c.network.thuis.responses.Freezer
import com.mitchelbv.thuis_c.network.thuis.responses.FreezerItem
import com.mitchelbv.thuis_c.network.thuis.responses.FreezerItems
import com.mitchelbv.thuis_c.views.freezer.freezerUiState
@Composable
fun FreezerItemEditScreen(
freezerItemEditViewModel: FreezerItemEditViewModel = viewModel(),
previousScreen: () -> Unit,
// updatedFreezerItem: FreezerItem? = null
) {
val freezerItemUiState by freezerItemEditViewModel.uiState.collectAsStateWithLifecycle()
if (freezerItemUiState.item != null && freezerItemUiState.freezers != null) {
BuildEditView(
freezerItem = freezerItemUiState.item!!,
freezerList = freezerItemUiState.freezers!!,
savePressed = { freezerItemEditViewModel.UpdateFreezerItem(it) },
returnScreen = previousScreen,
modifier = Modifier
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BuildEditView(
freezerItem: FreezerItem,
freezerList: List<Freezer>,
savePressed: (FreezerItem) -> Unit,
returnScreen: () -> Unit,
modifier: Modifier
) {
var expanded by remember {
mutableStateOf(false)
}
var freezerItemLifeCycle by remember {
mutableStateOf(freezerItem)
}
var freezerItemAmount by remember {
mutableStateOf("${freezerItem.amount}")
}
var freezerItemDrawer by remember {
mutableStateOf("${freezerItem.drawer}")
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top,
modifier = modifier
.fillMaxSize()
.padding(16.dp)
) {
TextField(
label = { Text(stringResource(id = R.string.freezer_item_edit_description)) },
value = "${freezerItemLifeCycle.item}",
onValueChange = { value ->
freezerItemLifeCycle = freezerItemLifeCycle.copy(item = value)
},
maxLines = 2,
modifier = modifier.fillMaxWidth()
)
Spacer(modifier = modifier.height(15.dp))
TextField(
label = { Text(stringResource(id = R.string.freezer_item_edit_amount)) },
value = freezerItemAmount,
onValueChange = { value ->
if (value.isBlank()) {
freezerItemAmount = ""
}
if (value.isDigitsOnly() && value.isNotBlank()) {
freezerItemAmount = value
freezerItemLifeCycle = freezerItemLifeCycle.copy(amount = value.toInt())
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
maxLines = 1,
modifier = modifier.fillMaxWidth()
)
Spacer(modifier = modifier.height(15.dp))
TextField(
label = { Text(stringResource(id = R.string.freezer_item_edit_drawer)) },
value = freezerItemDrawer,
onValueChange = { value ->
if (value.isBlank()) {
freezerItemDrawer = ""
}
if (value.isDigitsOnly() && value.isNotBlank()) {
freezerItemDrawer = value
freezerItemLifeCycle = freezerItemLifeCycle.copy(drawer = value.toInt())
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
maxLines = 1,
modifier = modifier.fillMaxWidth()
)
Spacer(modifier = modifier.height(15.dp))
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded },
modifier = modifier.fillMaxWidth()
) {
TextField(
label = { Text(stringResource(id = R.string.freezer_item_edit_freezer)) },
value = "${freezerList[freezerItemLifeCycle.freezerId?.minus(1)!!].name}",
onValueChange = {},
readOnly = true,
modifier = modifier
.menuAnchor()
.fillMaxWidth(),
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
maxLines = 1,
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = modifier.fillMaxWidth()
) {
freezerList.forEach { freezer ->
DropdownMenuItem(
text = { freezer.name?.let { Text(it) } },
onClick = {
freezerItemLifeCycle =
freezerItemLifeCycle.copy(freezerId = freezer.freezerId)
expanded = !expanded
})
}
}
}
Spacer(modifier = modifier.height(15.dp))
Row(modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
Button(onClick = {
// Save and return to overview
returnScreen()
savePressed(freezerItemLifeCycle)
}) {
Text(stringResource(id = R.string.com_save))
}
OutlinedButton(onClick = {
// Cancel all changes and return to last view
returnScreen()
}) {
Text(stringResource(id = R.string.com_cancel))
}
}
}
}
@Preview(showBackground = true)
@Composable
fun EditViewPreview() {
val test = FreezerItem(freezerItemId = 1, item = "Kip", 5, 5, "12-01-01", 1)
val kaas = listOf(
Freezer(freezerId = 1, amountInFreezer = 5, name = "Keuken"),
Freezer(freezerId = 2, amountInFreezer = 5, name = "Berging")
)
BuildEditView(freezerItem = test, kaas, {}, {}, Modifier)
}

View File

@@ -0,0 +1,70 @@
package com.mitchelbv.thuis_c.views.freezer.item_list.edit_item
import android.util.Log
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mitchelbv.thuis_c.network.thuis.ThuisRetrofitHelper
import com.mitchelbv.thuis_c.network.thuis.responses.Freezer
import com.mitchelbv.thuis_c.network.thuis.responses.FreezerItem
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.net.ConnectException
data class FreezerItemEditUiState(
val item: FreezerItem? = null,
var freezerItemAmount: String? = null,
var freezerItemDrawer: String? = null,
val freezers: List<Freezer>? = null,
var error: String = ""
)
class FreezerItemEditViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
private val _uiState = MutableStateFlow(FreezerItemEditUiState())
private val stateHandle = savedStateHandle
val uiState: StateFlow<FreezerItemEditUiState> = _uiState.asStateFlow()
init {
savedStateHandle.get<Int>("freezer_id")?.let { PopulateUiState(it) }
}
fun PopulateUiState(freezer_id: Int) {
viewModelScope.launch(Dispatchers.IO) {
try {
val getFreezers = ThuisRetrofitHelper.thuis.getFreezers()
if (freezer_id == 0) {
// This is when creating a new frezer item
_uiState.value =
FreezerItemEditUiState(item = FreezerItem(), freezers = getFreezers)
return@launch
}
val getFreezerItems =
ThuisRetrofitHelper.thuis.getFreezerItem(id = freezer_id)
_uiState.value =
FreezerItemEditUiState(item = getFreezerItems, freezers = getFreezers)
} catch (e: ConnectException) {
_uiState.value = FreezerItemEditUiState(error = "Oeps")
}
}
}
fun UpdateFreezerItem(freezerItem: FreezerItem) {
// Log.d("TESTINGGG", "hello ${freezerItem.item}")
viewModelScope.launch(Dispatchers.IO) {
try {
val putFreezerItem = ThuisRetrofitHelper.thuis.putFreezerItem(
id = freezerItem.freezerItemId!!,
freezerItem = freezerItem
)
// stateHandle["updatedFreezer"] = freezerItem
} catch (e: ConnectException) {
// Not sure yet..
}
}
}
}

View File

@@ -0,0 +1,51 @@
package com.mitchelbv.thuis_c.views.home
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.mitchelbv.thuis_c.ui.theme.ThuisTypography
import com.mitchelbv.thuis_c.R
@Composable
fun HomeScreen(onClickFreezers: () -> Unit, onClickCards: () -> Unit) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(stringResource(id = R.string.app_name), style = ThuisTypography.bodySmall)
// HomeButton(buttonText = R.string.home_card_freezer, onButtonClick = onClickFreezers)
HomeCard(
cardText = R.string.home_card_freezer,
cardImage = "",
navigateTo = onClickFreezers
)
}
}
@Composable
fun HomeButton(buttonText: Int, onButtonClick: () -> Unit) {
Button(onClick = { onButtonClick() }) {
Text(stringResource(id = buttonText))
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeCard(cardText: Int, cardImage: String, navigateTo: () -> Unit) {
Column {
Card(onClick = { navigateTo() }, modifier = Modifier.size(width = 180.dp, height = 100.dp)) {
Column {
// TODO Some sort of image here.
Text(stringResource(id = cardText), style = ThuisTypography.displayMedium)
}
}
}
}

View File

@@ -0,0 +1,48 @@
package com.mitchelbv.thuis_c.views.home
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mitchelbv.thuis_c.network.feyenoord.FeyenoordRetrofitHelper
import com.mitchelbv.thuis_c.network.feyenoord.FeyenoordService
import com.mitchelbv.thuis_c.views.feyenoord.Match
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
data class HomeUiState(
val upcomingMatch: Match? = null
)
class HomeViewModel : ViewModel() {
private val _uiState = MutableStateFlow(HomeUiState())
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
init {
// getUpcomingMatch()
}
fun notNullHandler(test: String?): String {
if (!test.isNullOrEmpty()) {
return test;
}
return ""
}
// private fun getUpcomingMatch() {
// viewModelScope.launch {
// val apiResponse = FeyenoordRetrofitHelper.feyenoord.getEvents().HeaderItem!!
// val match = Match(
// home_team = notNullHandler(apiResponse.NameHomeTeam),
// away_team = notNullHandler(apiResponse.NameAwayTeam),
// date = notNullHandler(apiResponse.EventStartDateTimeFormatted?.Full),
// begin_time = notNullHandler(apiResponse.EventStartDateTimeFormatted?.Time),
// end_time = notNullHandler(apiResponse.EventEndDateTimeFormatted?.Time),
// away_image = notNullHandler(apiResponse.AwayImageUrl),
// home_image = notNullHandler(apiResponse.HomeImageUrl)
// )
// _uiState.value = HomeUiState(upcomingMatch = match)
// }
// }
}

View File

@@ -1,4 +1,4 @@
package com.mitchelbv.thuis_c.ui.recipe package com.mitchelbv.thuis_c.views.recipe
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.view.ViewGroup import android.view.ViewGroup
@@ -13,7 +13,7 @@ import androidx.navigation.NavHostController
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")
@Composable @Composable
fun RecipeScreen(navHostController: NavHostController) { fun RecipeScreen() {
var backEnabled by remember { mutableStateOf(true)} var backEnabled by remember { mutableStateOf(true)}
var webView: WebView? = null var webView: WebView? = null
AndroidView(modifier = Modifier.fillMaxSize(), AndroidView(modifier = Modifier.fillMaxSize(),

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Thuis</string>
<string name="com_connection_error">Verbindingsproblemen probeer later nog eens.</string>
<string name="com_save">Opslaan</string>
<string name="com_cancel">Annuleer</string>
<string name="nav_matches">Wedstijden</string>
<string name="nav_recipes">Recepten</string>
<string name="nav_home">Thuis</string>
<string name="home_card_cards">Kaarten</string>
<string name="home_card_freezer">Vriezers</string>
<string name="freezer_list_empty">Geen vriezer(s)</string>
<string name="edit_freezer">Vriezer aanpassen</string>
<string name="freezer_item_count_partial">Artikelen</string>
<string name="freezer_item_drawer_partial">La</string>
<string name="freezer_item_edit_description">Omschrijving</string>
<string name="freezer_item_edit_amount">Hoeveelheid</string>
<string name="freezer_item_edit_drawer">La</string>
<string name="freezer_item_edit_freezer">Vriezer</string>
</resources>

View File

@@ -1,13 +1,27 @@
<resources> <resources>
<string name="app_name">Thuis</string> <string name="app_name">Thuis</string>
<string name="nav_feyenoord">Wedstrijden</string> <!-- Common errors -->
<string name="nav_recipes">Recepten</string> <string name="com_connection_error">Connection error, try again later.</string>
<string name="com_save">Save</string>
<string name="com_cancel">Cancel</string>
<string name="nav_matches">Matches</string>
<string name="nav_recipes">Recipes</string>
<string name="nav_home">Home</string> <string name="nav_home">Home</string>
<string name="home_card_cards">Kaarten</string> <string name="home_card_cards">Cards</string>
<string name="home_card_freezer">Vriezer</string> <string name="home_card_freezer">Freezers</string>
<string name="freezer_list_empty">Er zijn geen vriezers</string> <!-- Freezer item list -->
<string name="edit_freezer">Vriezer aanpassen</string> <string name="freezer_list_empty">No freezer(s)</string>
<string name="edit_freezer">Edit freezer</string>
<string name="freezer_item_count_partial">items</string>
<string name="freezer_item_drawer_partial">Drawer</string>
<!-- Freezer item edit view -->
<string name="freezer_item_edit_description">Description</string>
<string name="freezer_item_edit_amount">Amount</string>
<string name="freezer_item_edit_drawer">Drawer</string>
<string name="freezer_item_edit_freezer">Freezer</string>
</resources> </resources>

View File

@@ -1,10 +1,10 @@
buildscript { buildscript {
ext { ext {
compose_version = '1.3.3' compose_version = '1.5.4'
} }
}// Top-level build file where you can add configuration options common to all sub-projects/modules. }// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id 'com.android.application' version '7.4.0' apply false id 'com.android.application' version '8.1.3' apply false
id 'com.android.library' version '7.4.0' apply false id 'com.android.library' version '8.1.3' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
} }

View File

@@ -20,4 +20,7 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the # Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies, # resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library # thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true
android.nonFinalResIds=false
org.gradle.unsafe.configuration-cache=true

View File

@@ -1,6 +1,6 @@
#Mon Oct 31 14:59:45 CET 2022 #Mon Oct 31 14:59:45 CET 2022
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

4
temp.txt Normal file
View File

@@ -0,0 +1,4 @@
https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#NavigationBar(androidx.compose.ui.Modifier,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.unit.Dp,androidx.compose.foundation.layout.WindowInsets,kotlin.Function1)
https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#Scaffold(androidx.compose.ui.Modifier,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,androidx.compose.material3.FabPosition,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.foundation.layout.WindowInsets,kotlin.Function1)