From 365bf7567657641edeb4b4b86a61ca9adfe5e953 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Tue, 19 Nov 2024 15:15:05 +0300 Subject: [PATCH 01/12] Update gradle version --- gradle/wrapper/gradle-wrapper.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bdc9a83b..1af9e093 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From d33b71321da4bf0e3e2a0e6161f858355a756783 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Tue, 19 Nov 2024 15:15:20 +0300 Subject: [PATCH 02/12] Removed search from bottom navigation bar --- .../kotlin/com/vickbt/composeApp/ui/screens/main/MainScreen.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/main/MainScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/main/MainScreen.kt index 14bd88a8..9f2b754a 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/main/MainScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/main/MainScreen.kt @@ -34,7 +34,6 @@ fun MainScreen(viewModel: MainViewModel = koinViewModel()) { val topLevelDestinations = listOf( NavigationItem.Home, NavigationItem.Favorites, - NavigationItem.Search, NavigationItem.Settings ) From ab92ff55ace24f2e077f633f08b0955921d4a369 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Tue, 19 Nov 2024 17:52:53 +0300 Subject: [PATCH 03/12] Set up home screen filters --- .../vickbt/composeApp/domain/utils/Enums.kt | 4 + .../composeApp/ui/components/FilterHome.kt | 92 +++++++++++++++++++ .../composeApp/ui/screens/home/HomeScreen.kt | 4 +- 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/utils/Enums.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/utils/Enums.kt index 3222fa87..aeb8fc2e 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/utils/Enums.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/utils/Enums.kt @@ -4,4 +4,8 @@ object Enums { enum class MovieCategories { NOW_PLAYING, POPULAR, TRENDING, UPCOMING } + + enum class ShowType { + TV_SHOW, MOVIE + } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt new file mode 100644 index 00000000..d96f8bed --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt @@ -0,0 +1,92 @@ +@file:OptIn(ExperimentalLayoutApi::class, ExperimentalLayoutApi::class) + +package com.vickbt.composeApp.ui.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.ArrowDownward +import androidx.compose.material.icons.rounded.ArrowDropDown +import androidx.compose.material.icons.rounded.Close +import androidx.compose.material3.AssistChip +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.SuggestionChip +import androidx.compose.material3.Text +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.unit.dp +import com.vickbt.composeApp.domain.utils.Enums +import com.vickbt.composeApp.utils.titleCase + +@Composable +fun FilterHome( + modifier: Modifier = Modifier, + genres: List = listOf(), + onFilterClicked: (Enums.ShowType?) -> Unit = {}, + onCategoriesClicked: (String) -> Unit = {} +) { + + var selectedShowType by remember { mutableStateOf(null) } + + FlowRow( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(6.dp, Alignment.Start), + verticalArrangement = Arrangement.Center + ) { + AnimatedVisibility(visible = selectedShowType != null) { + FloatingActionButton( + modifier = Modifier.size(36.dp), + shape = CircleShape, + onClick = { selectedShowType = null }) { + Icon(imageVector = Icons.Rounded.Close, contentDescription = null) + } + } + + AnimatedVisibility(visible = (selectedShowType == null) || selectedShowType == Enums.ShowType.TV_SHOW) { + SuggestionChip( + modifier = Modifier.padding(vertical = 8.dp), + onClick = { + selectedShowType = Enums.ShowType.TV_SHOW + onFilterClicked(selectedShowType) + }, + label = { Text("TV Shows") }, + shape = RoundedCornerShape(16.dp) + ) + } + + AnimatedVisibility(visible = (selectedShowType == null) || selectedShowType == Enums.ShowType.MOVIE) { + SuggestionChip( + modifier = Modifier.padding(vertical = 8.dp), + onClick = { + selectedShowType = Enums.ShowType.MOVIE + onFilterClicked(selectedShowType) + }, + label = { Text("Movies") }, + shape = RoundedCornerShape(16.dp) + ) + } + + AssistChip( + modifier = Modifier.padding(vertical = 8.dp), + onClick = { onCategoriesClicked("") }, + label = { Text("Categories") }, + shape = RoundedCornerShape(16.dp), + trailingIcon = { + Icon(imageVector = Icons.Rounded.ArrowDropDown, contentDescription = null) + } + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt index 2eaaeab7..b99aaf6c 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt @@ -29,12 +29,12 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import app.cash.paging.compose.collectAsLazyPagingItems import com.kmpalette.loader.rememberNetworkLoader +import com.vickbt.composeApp.ui.components.FilterHome import com.vickbt.composeApp.ui.components.MovieCardLandscape import com.vickbt.composeApp.ui.components.MovieCardPager import com.vickbt.composeApp.ui.components.MovieCardPagerIndicator import com.vickbt.composeApp.ui.components.MovieCardPortraitCompact import com.vickbt.composeApp.ui.components.SectionSeparator -import com.vickbt.composeApp.ui.components.appbars.AppBar import com.vickbt.composeApp.ui.theme.DarkPrimaryColor import com.vickbt.composeApp.utils.WindowSize import com.vickbt.shared.resources.Res @@ -63,7 +63,7 @@ fun HomeScreen( modifier = Modifier .fillMaxSize() .padding(mainPaddingValues), - topBar = { AppBar("Home") } + topBar = { FilterHome(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) } ) { paddingValues -> // region Home section From 6c796e22ecf90e769428e22e68482082aa7324a9 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Wed, 20 Nov 2024 14:58:17 +0300 Subject: [PATCH 04/12] Linting --- .../kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt index d96f8bed..1eceb66b 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.ArrowDownward import androidx.compose.material.icons.rounded.ArrowDropDown import androidx.compose.material.icons.rounded.Close import androidx.compose.material3.AssistChip @@ -29,7 +28,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.vickbt.composeApp.domain.utils.Enums -import com.vickbt.composeApp.utils.titleCase @Composable fun FilterHome( From ece15022cf6ef8d01f0daa762bde36e179459ef3 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Fri, 22 Nov 2024 17:29:33 +0300 Subject: [PATCH 05/12] Completed setting up home filter component --- .../composeResources/values/strings.xml | 4 + .../composeApp/ui/components/BottomNavBar.kt | 6 +- .../composeApp/ui/components/FilterHome.kt | 99 ++++++++++++++----- .../composeApp/ui/components/NavRailBar.kt | 6 +- .../composeApp/ui/screens/home/HomeScreen.kt | 7 +- .../com/vickbt/composeApp/ui/theme/Type.kt | 6 +- 6 files changed, 96 insertions(+), 32 deletions(-) diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index 5f89b3f4..ab70a03c 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -6,6 +6,10 @@ Settings Details + Movies + TV Shows + Categories + Trending Movies Popular Movies Upcoming Movies diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/BottomNavBar.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/BottomNavBar.kt index a612b830..04e8ec37 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/BottomNavBar.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/BottomNavBar.kt @@ -9,6 +9,7 @@ import androidx.compose.material3.NavigationBarItemDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.navigation.NavHostController import androidx.navigation.compose.currentBackStackEntryAsState import com.vickbt.composeApp.ui.navigation.NavigationItem @@ -48,8 +49,9 @@ fun BottomNavBar( }, alwaysShowLabel = true, colors = NavigationBarItemDefaults.colors( - selectedIconColor = MaterialTheme.colorScheme.primary, - unselectedIconColor = Gray + selectedIconColor = MaterialTheme.colorScheme.onSurface, + unselectedIconColor = Gray, + indicatorColor = Color.Transparent ), selected = isSelected, onClick = { diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt index 1eceb66b..a5687f1d 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt @@ -1,22 +1,24 @@ -@file:OptIn(ExperimentalLayoutApi::class, ExperimentalLayoutApi::class) - package com.vickbt.composeApp.ui.components import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.ArrowDropDown import androidx.compose.material.icons.rounded.Close +import androidx.compose.material.icons.rounded.KeyboardArrowDown import androidx.compose.material3.AssistChip import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SuggestionChip import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -26,65 +28,114 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.vickbt.composeApp.domain.utils.Enums +import com.vickbt.shared.resources.Res +import com.vickbt.shared.resources.categories +import com.vickbt.shared.resources.movies +import com.vickbt.shared.resources.tv_shows +import org.jetbrains.compose.resources.stringResource @Composable fun FilterHome( modifier: Modifier = Modifier, - genres: List = listOf(), onFilterClicked: (Enums.ShowType?) -> Unit = {}, - onCategoriesClicked: (String) -> Unit = {} + onCategoriesClicked: () -> Unit = {} ) { var selectedShowType by remember { mutableStateOf(null) } - FlowRow( + Row( modifier = modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(6.dp, Alignment.Start), - verticalArrangement = Arrangement.Center + horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start), + verticalAlignment = Alignment.CenterVertically ) { - AnimatedVisibility(visible = selectedShowType != null) { + AnimatedVisibility(visible = selectedShowType != null, enter = fadeIn(), exit = fadeOut()) { FloatingActionButton( - modifier = Modifier.size(36.dp), + modifier = Modifier.border(1.dp, MaterialTheme.colorScheme.onSurface, CircleShape) + .size(32.dp), shape = CircleShape, + containerColor = Color.Transparent, + contentColor = MaterialTheme.colorScheme.onSurface, onClick = { selectedShowType = null }) { - Icon(imageVector = Icons.Rounded.Close, contentDescription = null) + Icon( + modifier = Modifier.size(18.dp), + imageVector = Icons.Rounded.Close, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurface + ) } } AnimatedVisibility(visible = (selectedShowType == null) || selectedShowType == Enums.ShowType.TV_SHOW) { SuggestionChip( - modifier = Modifier.padding(vertical = 8.dp), onClick = { selectedShowType = Enums.ShowType.TV_SHOW onFilterClicked(selectedShowType) }, - label = { Text("TV Shows") }, - shape = RoundedCornerShape(16.dp) + label = { + Text( + modifier = Modifier.padding(vertical = 2.dp), + text = stringResource(Res.string.tv_shows), + style = MaterialTheme.typography.labelMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.onSurface, + textAlign = TextAlign.Center + ) + }, + shape = RoundedCornerShape(16.dp), + border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.onSurface) ) } AnimatedVisibility(visible = (selectedShowType == null) || selectedShowType == Enums.ShowType.MOVIE) { SuggestionChip( - modifier = Modifier.padding(vertical = 8.dp), onClick = { selectedShowType = Enums.ShowType.MOVIE onFilterClicked(selectedShowType) }, - label = { Text("Movies") }, - shape = RoundedCornerShape(16.dp) + label = { + Text( + modifier = Modifier.padding(vertical = 2.dp), + text = stringResource(Res.string.movies), + style = MaterialTheme.typography.labelMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.onSurface, + textAlign = TextAlign.Center + ) + }, + shape = RoundedCornerShape(16.dp), + border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.onSurface) ) } AssistChip( - modifier = Modifier.padding(vertical = 8.dp), - onClick = { onCategoriesClicked("") }, - label = { Text("Categories") }, + onClick = { onCategoriesClicked() }, + label = { + Text( + modifier = Modifier.padding(vertical = 2.dp), + text = stringResource(Res.string.categories), + style = MaterialTheme.typography.labelMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.onSurface, + textAlign = TextAlign.Center + ) + }, shape = RoundedCornerShape(16.dp), trailingIcon = { - Icon(imageVector = Icons.Rounded.ArrowDropDown, contentDescription = null) - } + Icon( + imageVector = Icons.Rounded.KeyboardArrowDown, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurface + ) + }, + border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.onSurface) ) } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/NavRailBar.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/NavRailBar.kt index bd0d6264..b2482ef0 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/NavRailBar.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/NavRailBar.kt @@ -10,6 +10,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color import androidx.navigation.NavHostController import androidx.navigation.compose.currentBackStackEntryAsState import com.vickbt.composeApp.ui.navigation.NavigationItem @@ -50,8 +51,9 @@ fun NavRailBar( ) }, colors = NavigationRailItemDefaults.colors( - selectedIconColor = PrimaryColor, - unselectedIconColor = Gray + selectedIconColor = MaterialTheme.colorScheme.onSurface, + unselectedIconColor = Gray, + indicatorColor = Color.Transparent ), alwaysShowLabel = false, selected = isSelected, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt index f84414f3..dfa09838 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt @@ -36,6 +36,7 @@ import com.vickbt.composeApp.ui.components.MovieCardPager import com.vickbt.composeApp.ui.components.MovieCardPagerIndicator import com.vickbt.composeApp.ui.components.MovieCardPortraitCompact import com.vickbt.composeApp.ui.components.SectionSeparator +import com.vickbt.composeApp.ui.components.appbars.AppBar import com.vickbt.composeApp.ui.theme.DarkPrimaryColor import com.vickbt.composeApp.utils.WindowSize import com.vickbt.shared.resources.Res @@ -64,7 +65,7 @@ fun HomeScreen( modifier = Modifier .fillMaxSize() .padding(mainPaddingValues), - topBar = { FilterHome(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) } + topBar = { AppBar(title = "") } ) { paddingValues -> // region Home section @@ -90,6 +91,10 @@ fun HomeScreen( .align(Alignment.Center), horizontalAlignment = Alignment.CenterHorizontally ) { + //region Home Filters + FilterHome(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) + //endregion + //region Now Playing Movies homeUiState.nowPlayingMovies?.let { nowPlayingMovies -> val pagerState = diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/theme/Type.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/theme/Type.kt index a3fa9a68..b8898595 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/theme/Type.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/theme/Type.kt @@ -90,19 +90,19 @@ internal fun Typography(): Typography { labelLarge = TextStyle( fontFamily = nunito, fontWeight = FontWeight.Medium, - fontSize = 14.sp, + fontSize = 16.sp, lineHeight = 20.sp ), labelMedium = TextStyle( fontFamily = nunito, fontWeight = FontWeight.SemiBold, - fontSize = 12.sp, + fontSize = 14.sp, lineHeight = 16.sp ), labelSmall = TextStyle( fontFamily = nunito, fontWeight = FontWeight.Medium, - fontSize = 11.sp, + fontSize = 12.sp, lineHeight = 16.sp ) ) From 566d4eebbb54de61d517aa2a1deb68383e7e570e Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Fri, 22 Nov 2024 17:39:19 +0300 Subject: [PATCH 06/12] Set up app theme to onBackground instead of onSurface --- .../composeApp/ui/components/BottomNavBar.kt | 4 ++-- .../composeApp/ui/components/FilterHome.kt | 20 +++++++++---------- .../composeApp/ui/components/ItemMovieCast.kt | 2 +- .../ui/components/MovieRatingSection.kt | 8 ++++---- .../ui/screens/details/DetailsScreen.kt | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/BottomNavBar.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/BottomNavBar.kt index 04e8ec37..d9a6e87b 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/BottomNavBar.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/BottomNavBar.kt @@ -24,7 +24,7 @@ fun BottomNavBar( ) { NavigationBar( modifier = modifier.fillMaxWidth(), - containerColor = MaterialTheme.colorScheme.surface.copy(alpha = .85f) + containerColor = MaterialTheme.colorScheme.background.copy(alpha = .85f) ) { bottomNavItems.iterator().forEach { item -> @@ -49,7 +49,7 @@ fun BottomNavBar( }, alwaysShowLabel = true, colors = NavigationBarItemDefaults.colors( - selectedIconColor = MaterialTheme.colorScheme.onSurface, + selectedIconColor = MaterialTheme.colorScheme.onBackground, unselectedIconColor = Gray, indicatorColor = Color.Transparent ), diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt index a5687f1d..964f4c83 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt @@ -55,17 +55,17 @@ fun FilterHome( ) { AnimatedVisibility(visible = selectedShowType != null, enter = fadeIn(), exit = fadeOut()) { FloatingActionButton( - modifier = Modifier.border(1.dp, MaterialTheme.colorScheme.onSurface, CircleShape) + modifier = Modifier.border(1.dp, MaterialTheme.colorScheme.onBackground, CircleShape) .size(32.dp), shape = CircleShape, containerColor = Color.Transparent, - contentColor = MaterialTheme.colorScheme.onSurface, + contentColor = MaterialTheme.colorScheme.onBackground, onClick = { selectedShowType = null }) { Icon( modifier = Modifier.size(18.dp), imageVector = Icons.Rounded.Close, contentDescription = null, - tint = MaterialTheme.colorScheme.onSurface + tint = MaterialTheme.colorScheme.onBackground ) } } @@ -83,12 +83,12 @@ fun FilterHome( style = MaterialTheme.typography.labelMedium, maxLines = 1, overflow = TextOverflow.Ellipsis, - color = MaterialTheme.colorScheme.onSurface, + color = MaterialTheme.colorScheme.onBackground, textAlign = TextAlign.Center ) }, shape = RoundedCornerShape(16.dp), - border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.onSurface) + border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.onBackground) ) } @@ -105,12 +105,12 @@ fun FilterHome( style = MaterialTheme.typography.labelMedium, maxLines = 1, overflow = TextOverflow.Ellipsis, - color = MaterialTheme.colorScheme.onSurface, + color = MaterialTheme.colorScheme.onBackground, textAlign = TextAlign.Center ) }, shape = RoundedCornerShape(16.dp), - border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.onSurface) + border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.onBackground) ) } @@ -123,7 +123,7 @@ fun FilterHome( style = MaterialTheme.typography.labelMedium, maxLines = 1, overflow = TextOverflow.Ellipsis, - color = MaterialTheme.colorScheme.onSurface, + color = MaterialTheme.colorScheme.onBackground, textAlign = TextAlign.Center ) }, @@ -132,10 +132,10 @@ fun FilterHome( Icon( imageVector = Icons.Rounded.KeyboardArrowDown, contentDescription = null, - tint = MaterialTheme.colorScheme.onSurface + tint = MaterialTheme.colorScheme.onBackground ) }, - border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.onSurface) + border = BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.onBackground) ) } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/ItemMovieCast.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/ItemMovieCast.kt index 48f653ad..9b4bff5b 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/ItemMovieCast.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/ItemMovieCast.kt @@ -44,7 +44,7 @@ fun ItemMovieCast(modifier: Modifier = Modifier, actor: Actor) { overflow = TextOverflow.Ellipsis, textAlign = TextAlign.Center, maxLines = 1, - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onBackground ) Text( diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieRatingSection.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieRatingSection.kt index 5eabb791..ac52b671 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieRatingSection.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieRatingSection.kt @@ -45,13 +45,13 @@ fun MovieRatingSection(popularity: String?, voteAverage: String?) { modifier = Modifier, text = if (popularity.isNullOrEmpty()) "N/A" else popularity, style = MaterialTheme.typography.titleLarge.copy(fontSize = 42.sp), - color = MaterialTheme.colorScheme.onSurface, + color = MaterialTheme.colorScheme.onBackground, ) Text( text = stringResource(Res.string.popularity), style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold), - color = MaterialTheme.colorScheme.onSurface, + color = MaterialTheme.colorScheme.onBackground, ) } //endregion @@ -60,7 +60,7 @@ fun MovieRatingSection(popularity: String?, voteAverage: String?) { modifier = Modifier .fillMaxHeight() .width(2.dp), - color = MaterialTheme.colorScheme.onSurface + color = MaterialTheme.colorScheme.onBackground ) //region Rating @@ -80,7 +80,7 @@ fun MovieRatingSection(popularity: String?, voteAverage: String?) { modifier = Modifier, text = if (voteAverage.isNullOrEmpty()) "N/A" else "$voteAverage/5.0", style = MaterialTheme.typography.titleLarge.copy(fontSize = 20.sp), - color = MaterialTheme.colorScheme.onSurface, + color = MaterialTheme.colorScheme.onBackground, ) } //endregion diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsScreen.kt index a23459e6..4191d92b 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsScreen.kt @@ -132,7 +132,7 @@ fun DetailsScreen( .padding(horizontal = 16.dp), text = movieDetailsUiState.movieDetails?.overview ?: "", style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurface, + color = MaterialTheme.colorScheme.onBackground, fontSize = 15.sp, textAlign = TextAlign.Start, overflow = TextOverflow.Ellipsis, From 2c08f4efe954aadf8e3dc6db36b6e219000bb5f1 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Fri, 22 Nov 2024 17:39:31 +0300 Subject: [PATCH 07/12] Added action to TopAppBar component --- .../ui/components/appbars/TopAppBar.kt | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/appbars/TopAppBar.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/appbars/TopAppBar.kt index 9a94d698..56bfbda5 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/appbars/TopAppBar.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/appbars/TopAppBar.kt @@ -1,7 +1,12 @@ package com.vickbt.composeApp.ui.components.appbars import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Search import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar @@ -14,19 +19,27 @@ import androidx.compose.ui.unit.sp @OptIn(ExperimentalMaterial3Api::class) @Composable -fun AppBar(title: String) { +fun AppBar(title: String, onActionClicked: () -> Unit = {}) { TopAppBar( - colors = TopAppBarDefaults.mediumTopAppBarColors(MaterialTheme.colorScheme.surface), + colors = TopAppBarDefaults.mediumTopAppBarColors(MaterialTheme.colorScheme.background), title = { Text( modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), text = title, color = MaterialTheme.colorScheme.onSurface, - style = MaterialTheme.typography.titleMedium, - fontSize = 28.sp, + style = MaterialTheme.typography.titleMedium.copy(fontSize = 28.sp), maxLines = 1, overflow = TextOverflow.Ellipsis ) + }, + actions = { + IconButton(modifier = Modifier.padding(6.dp), onClick = { onActionClicked() }) { + Icon( + modifier = Modifier.size(28.dp), + imageVector = Icons.Rounded.Search, + contentDescription = null + ) + } } ) } From 8f1f9a7ae3003a6132ca3bd9f941a616e98341b1 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Sat, 23 Nov 2024 15:33:39 +0300 Subject: [PATCH 08/12] Added search and n logo in the top app bar --- .../composeResources/drawable/logo_n.png | Bin 0 -> 82193 bytes .../ui/components/appbars/TopAppBar.kt | 20 ++++++++++++++++-- .../ui/screens/favorites/FavoritesScreen.kt | 2 +- .../composeApp/ui/screens/home/HomeScreen.kt | 8 ++++++- .../ui/screens/settings/SettingsScreen.kt | 2 +- 5 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 composeApp/src/commonMain/composeResources/drawable/logo_n.png diff --git a/composeApp/src/commonMain/composeResources/drawable/logo_n.png b/composeApp/src/commonMain/composeResources/drawable/logo_n.png new file mode 100644 index 0000000000000000000000000000000000000000..ff197a1fca922f20ffcb78c701a8ce09f4c77a5b GIT binary patch literal 82193 zcmZs@1yoeq`vyD~1}e5RDy5`^ARr+~3?ZTpNF(C_N=iy&qEb4j?qEP$T zP^bfihiJf0n1s@;z<>6e-_*E?LS+WgZazE!{(q0dU1fO`v5|QK{Lclf%3Tc<%8d(! z@_B_qt%D!=jH6IaS5c@*6BJ7F6AE?SE~-fD2KWVyshaXF)DHaTb7@jA_{m{A6M8o%F41WuhY`fYGqWf(huAZ zpe%QFY#477c0^YfWp|w0Y4^!Jk4Xsi-pbfvBS*3x^3m>TSwi_}3oF&s1dDTM%B*#K z_1*i~uu-K)OsZI@ZAlJ=l9N`?;9gv)bNS@bUzj(MiLwZ~)vAkT!XJ`Bp)3w(7o`kW z94zl!x{k`Ji%2+t_B`oLp9sZk&1&plIdtk z@w@=|>?`?ebCoU}Nw@oY;c}JZ9BVp$XU@O{s22vXZH<#_7VO(9QO%d6(Mh2+7xC?d z3xX8~QK)-q;Q2Z$bgY|kZl!RC6yH=-Uq>F*JsPapFMZuz%C$Rxv>yy&U z=WZJ--Vg1TE~^ny?UMxwHydS;hcypQ>n_K5Z>!b(ly6hm(=1$FE#sn8#A9lJ zt1?yj9aZK!(fx@^-2^6Z$!qc!OM%pDjG4ZQ1 z*f#$J5quGrEQLkR_R}RZSKXHzl$}b%NIE$T?|79}pMX2w_LDwb$GEhkWK$SEa55^P zjy=|wrGZm*W-sb$H2&60ww=}3j~2&2-`+cz_MwG-$MjuBO^&?6mez=CaycOBu_pO6i3y-RJ5$ZL>NG=raY!8TAT9PAJ5BgBA zx`_{9&qzSq%3gLl#`G_^k~W zOI#w{{*TZ5rKW^>S`6Zl`(APV`&w{v?r&4wr#V09Z&?I`fNhT2BgiXv$JLL1t$)6+ zJUZ%n1)dj`E6MfE{3$cCWnJwJ^LJW?IP^LPgU9BJ zTDY6l7TX8mIq9q0Q>O!G2exNi;6XX--ixqc&FT(@PMQAb>K$83|G*@hjJhoyD!1sf zD2jnGy5%!+=RZqsJq469?@r|SqJVaT9A4uHuCpoa%2^wFweCN(T!R>+xA+T>T z35sYYh7Bcx0ck%w3{F-jx5Ni}JH1CjLC}UP*6JBx7tF6TJA5wDc@L`Fv~zo9XUqGx zwoW7~jn6qDRdeaaAnSZXd&Nfhz8`e;?yk&Nm&bXPBPK!wPKfvSG#j=go4kT=&Hs60 zbtpglUXl%rrTb87OsZDW?uav6wI;@99d2SEtM*^w) z&dTZ)#P7N-v0GyW#B1a%PGEyZKKxBXzMDmDnq`7C;q5CrU9T2=N-yeLYk0)UueT zCE6#EyQ|nX#V{STE%#JF_JGn+Tuu){&-def!pWKN3tIAIfv%pH(Ij|vA+7S z?bQb)f_>Vqc-cjFoNc@0KVx#k-yWIfwd8y7CJLH!`)*cuT^QZXOd&LEuIM2}vXz;1 z6@qWR5C1%{Or7m(0AbTfDoOOO$9-jrrl?Ewo*JnbxS4O3k29~T?~D*ACN*b$BpJ}k zBQh`lMm)O>T{8G5{j!nUgdcG0N8q=50HVm|rQcg9v~njsB^US28nn?J|7?ioXqlXx z%%!D$EET`DwwBz*W+Z;Ds(LkrIo5TdeMn2o?l9(bf02@TpH>ND;+>#9Svh@bGq&$=SU=dWIizBo|1D zaTu)+Ej1FcaFayqfkE=SPJYV}gPGJpeqy@%`jFkRSk?>39WJ6D1+tcMl8S*xz}xeJ zpuvSDE1O@DH@opZkQ12f=fg})sxh8am)w%xB^6-wuAzd3wdC2;K6jp+NBMkBPqVNC zfvX#Wz9tEDU#7JpOJnxx{U4K#lKY@lF@9i{bo7&O9Ix8V*Bpke8w3M1Wz@ZZBbIk4 zDsBiGgvF0R+3o5b%{D9ux=O8zi4trIUo`eF1~$+t!QUr`>l$e{lY>A z;(BQ+lF|#LuiVO=<~l=N$WC#MF5oy<`c@3n(_R@LMe%Wh-3-hZ%{?Y+wf8jn!fR+# z5ueT*6?8;NDZ|tRQN+jGl3H91CMe}|dL7Cw8)M6f2pGGDHot%4rIuo7 zYTBtXy$N}EGH6$6`x5g^es0q%9C23NgxLro|;1pXq3s>gs!byKdq}%JL23A=GvtnsIz065(>W2IL80~?-ZV)BlX(R*M*KdXA~Tst}2+{JzZxk5N8M7xNm zcFInJQ7|PWG=CUDE$xr&Hdl!a%4}P1FhI$0hYA!jGScjE5-eogLQ0dACAUeo8_(P+ zBLrC_MuU3Bq1N^ieT2G^V?bQhYq){;Ie;y#{PistIMuMHeLANVm7wB3`a&^)Hv0rf z1b03TPm@xM`3m>m$-ebid9mT6wW}KOeE|)ZE+(g{3C5?-M_xV`V{@NQt(hO{g2`?s zi|;Y|@d@E4j$xN_UW97)`yw{OS#Ru)jeeH~J>A!ysJByD3E2`GrUJmf4rY>W!{6U}DPo?p27Hj7 zk#=u#i(G45zy8RR8+Q(clkBt{5qN#ldVAX3g$tBtd_%3Zw5Xqsq0ZOn_-75zd_uwn zx)p$skIH3X1_gFbNXVH|EsmfIprPS1ckdh%CaMvU;C$kp>)HsD+Xi(;EHV-TM8BgP zEIJW(FbXcB0ca8(SxVWg5 zAzNa@=N~jr>b?O27_j#37L%)QC1p^T9SkN~Q{&`*a_6(>$o^TSE-x<|Lfm`e&(g*u z+x5BM+q@Z|EPQ~4uh+)1)4+VVX)s8a0e zd<{1=uM2l}=Ke;Cv3YM=!dSDx+_nM0@tEnaL+Vyx+N9wiXr?EAq-DTc%x?d!>=Ktg z2W(JaB4lA-08f}fA*(EF2y)Q{h*^SU)Nq^+6e+qXPR;go)?y4cMf1h zue&W(HE_pnK0(f8A3BM1St;+B@p|>XeX*yC#H{L;0%4&15lrg6zp@J=cm1?ZXS=R; zg?>v%V#B(dhD%@txvXpSYY_;92Q}!x>Z3m9#4YFsdcORGnm?1vbMINB{K|Dfi%8! z#-(cjWECutu_qin)Img{=Dx3WV^==?{ynvuPE;jRSEy9G znjyPr(p;aBqbH2g%%4U^axw@1nZg~vr1aQ0$bp?KiQQN%;0hmWh$yK4It)$W$4$zQ z=aK8abUQ~Nk?{C)PNs>@NcKq~9=(8Uv{-)OIk>Jo{H@5K8wGu(7>zXZ}A_10Ub5((PTYL0oi|)HFLzA-kBjgQI*M&p@s+XEtp#DKM(!qK9!m z495K*O8LBtJ(l@XOB0q5#5qr(-KrP5o5KTJW(3r7vZy!~HE0U2hkw0eF&ik8()bTo zyP}zDy~4$_=q`AG91&*l-*o};%C=~uXMbnE`T|=Hee_k z^jy`J^4TIgs3b`7~jJ_D=>vooiQvjU5jhNM#sjGK3LiQ3wk$0>> zD%JE}=Z8FonV;&0yZ=E(-RCcw>Xl;X0}d@B3FQYL{}tDz7F#4lIq0=U{HwPbwr=A6 zrhi|tZq!J(;*K)ROJ`JLTt@{K8>K)>fxHJXC*IZek$%5g+0?1z6nu+cl>$Vb=V-jw z2s-Va#n|03(CXQ$1IM8-q@IqMjVh&r6?*pBkc%A<@Aq| z8RFpySBR+owCq&SZObt+!E0bpoDtF+sZAmTQR$hq><g28MU2ejejD ztc*UaLLMXedY4)L|FUlQ_w{UV=?s@(C0J?Q^)1!TgE`y17$g*YCzh(VzrOQ&k+Y~if ze2>p5rPis1RI{_};k{-xp|-AlUqE7zFa!Ht@YA3yt-rstIJhkddPCR*x^}%LxGN}r z`g)|N5aanGqez(SdDk<_y*xoBCIzDhRU%%r<%ZzN-ms}@MWeIn^sekkE&U$h?$NE56ibB5}s?H#L%vB87!XP;o(i%nA)z2hQ$lz)PI1K-a(HLN%KAj?wk(1zw zGh6J+L|ZpLcxA%P8lf+tL{qK+a-c|R2_#!TFV`<*iFwVvl#Pp*@F|MM=OnK*${jWC ztTyKa9&OMlSj%>$C=5nWdmpCZ^>#)YQphuA z{$ks+l(O(!Hb#d~H~?SnL{Nmz@Ya;1P2MNvVtYj_rQn16{9CG4%2os+i9#*BJYio2 z%V+y;bN3Rp6Q{CnKseR>1ZmkAIf5ypOw6L0f5SsIOMY}L78fQ0SI-62!_6lwZRZqXIiq31Ude_wev2C!AHk8@+ zUQ4YrTF1isV_F7$f%I$7mRh9ne&;7<_I%csMiW9By&f?LynEl$(3^Wn+rR2H&vjq8 zxZ9;Mcu1!{0%YJvKoDTi=2dxx^}>#-jo)cbT^eYJ;1DAcX^e&;g&zW@cuh;^O(&Wn~4?dJi7Fb=&&ugnj7ICSKMd zKCt<-HWMqZ%tDU}QQ?R#SfJc74A`Djy(`GL1K66v`g(Ab7`($+JGp#E9G{p7iO-j1 zt%C|Yg6>oJ#fQbiwP&JEi?Ah(lVB)IZzbnb!ukQVO;jJ>dk|&kp#Hc~`5Ylu1@|01 zZl!8*^*+SO&5R(NQ9^ZWrr4z26d(mA@GgN}r_5P{k*?wIho6gRtJ%jQUT{AwL5Jbn z%2Jz4FbL~51Wv>HZlf-IYD?3X&@2y7y~2~vyQ&1-Kqh-_sSQ-6payWbJ29=?Nn5D( zfT*Z@)r6Tiq_@z8i#i1jle2y@#jQzZV`O+~A~lkg*2kF?;%Gogw-5czdEt*_Klj1- zSsp!jTWysvLhf32tY;!*W@FV<35Eepa*pTXo zwl}blcdfr$oHw*|SRHQTy{5FZWcLA2Z>0Osq>JXolHUkUDyk7pX=t*#XWgS3G9k~E zQ#*QEvsdQ&Q&S>B9zRc@RW+AQnZ6uTEf&jRcDf=f9P&v&+VyvucGirqNz;J6(QSPJ zs)dyItgHfOGp&f$?wVw*GR&Y~dj^*t!;T0MfvD#=_;FTrk9)d0$BDVeJ+5J&J9toE8n(;|eU$74Fv9j`_pTEe`%zKAgBHa&2fg@B3Wo@Sguo z#^?91-+&nJ3dFJDeNS0Rz0vnS#;IxxbR=8xnufAi>i6ec+yLB?z*#v|I}iF)I+18p z{Qdg{bVO`y(0>l5Z|m`Oplfixnli~I>!r{hxliuh^MoPJ>R%Bgi@2u`--3KZC%m`^ z^+|?OlFbg=1D#!wtcQKVbUTUxm>@6*_z7hTH9m_9uI9Yh=M2ND$>gV(2fZtqZ>%Qn zmJw=`Bh$3Uj(YHD;8@PbzxzBRT^&3H!(1B4D{pDXt$J4uGNds;VQh2>S+R!Jj$nwNVAV5rYT zZsE7#zrNlBRyJ>pX;I7oSUr*XU@V|)ao`G>Q-@gdS8?o&VQO;1Rk*Q|X=xU7bCr;5 zC@ujwR6ou&EG`iv<0)%Zbt=Nb$dM0T#dEZ5rIhl-wGgr9=Lw7W&gE^?g#__8n#v9Y z_rm^auI5an5-_O~*(#%18Q6(N4objqEh8=aPCda}KM&Ll6p6CoCmvs+KYd>YEVqIE zAPTnTYA+wVArNF8)bjhr)UyGGg9MI6`Yo9SA{JI^pZ30F+Z@zv-=%!pFpM6K7-Qx6 z7VMOj!FbUtK03HUOMSr3wZ(d=y}a4R4lEr+OulZ}qsKiWXh%naLJTXDx_ld)tOHpD zuc(7=#E*6~b*A`tl!QaDI_bS7>MggHV3PayHy}MocCojT#Pl49< zOFAzyAFSXYS*7q8aAEzdGMLnbVM8g~urK4xWcRiDuiXNBV3lK~Tl0}!ibk-+jvyu$ z0g;w&sH2+|d7O5|!tO-zl%YFZg|D4ZLM99tD5|M-=-Yh88d##kND3+qD`&yN&YQui)>rn z)zs9U)s~>$u1pgSO{@CyLmYwH%z4jsj2zbWwk5O|ydwfM_1{Q1P4@IVG|ku`KsX^G z*JsgiZ&(Vx(r2AJp zl7OuTymH;U2Qm72HYF1Q7B z8^9vqJ10kztarAA)05o{dkgd_>%yvim#Fkx(~&w4Py7E87@0b?1VL)vs|xQm!JV)` znAo`{;#w1R;+4!rJ@)P!7{VGh6(ixPS3@hcu#eMB1B?Ri-dUS5Riw8S@FF3wl3AJN2w_Bf#fgCs2Po*6`L8sWO! zG1c(5vOL90cGDC!Xrl2r%QdEjQ|F7P$U>OG@7pRX48EG^zc*m?t-<-@&0|ZI#~S;( zVbe{`cp^>N#j3c8PZ9H6zCSm`!eCrq(XtnmYHB2TRIV!iR%&W$mjEB13Vw8KEPl5| zXzVbg))>{&n%%T*v-9tFGIp{ZQWm1V#(xdNlJOrnZVb~Xrr&Mzi~kX zJo{aTl-*$1w!Up2qVH(Jx1K6?RU%yYZDc>f$Z@}Pm?xtsY~z^ZVd$K~%Wji1eZ}r} zOXC{Qjf*)z`!wD8T5aCl`?`mF=y9$icF}Phc4YHf-YW*8=Om-{q4NAIYJw+c$S%IR z`Vdg)eHEOz)mRpo!*zDHyRVIq4|3l&AVU(eNne7DE%{!ZA~|xmUp(ipi4po? z>=*~UWmLttdp(<^rm~cZ3&?R8I+senc@g}bzcEP5kk^qhD+m_Wk_weG4iUnLokmi2V8qRn`aUd zYiaRe%l#zZr;22Sg^RU}=X(M#R!QiJNC~*II$8S`!^+)r8`U!MK9knQ8=lD)Z zp1$LFEH?43%o>}RmAP9jk6QrDQ0i|gu`po^ z@AcITU+m(B*S^}0w4EIP4~H zVv3ZRbvl9*Vp?_S1_0uSF2{zZIZ0P@K1hDD+spcds%ga+0dLJe8gO7wyOqY{vg(2s zT@<()SFPQm$!>=3|Ma{Ss(3y>Ed|;woZWP{c_;@oYdureik)_dN%Qn~waCk{pYh2F0qY;m!HMPF|Z3g54 zLN-3{LZP_-Z?duMNi~64BFlQN{hmuC#*1Z7vW`w`O_ZmQ@pNka#-Y-r83z(aD#4mu z=rAS>eO3w3IbPfn_QS^+$mjG?CmL1s?s$RpAXMr31!rz;&LB@PDCGKaKKeBKID-uq z6t$mS`-nh(LlI5V6o;5qtmq1ok&}Ynva|(`7#IK=XBawFLh& z)4>p0S-@4;onQEM+Sn4NC-a+tzvZj!^*tdIuq_{X=nc)`_*kr{XuSeBhGgRmZim&m zv0#7~zQI%(8?5T?q4HGfeQ~4kY8Ki#h$SZ^+IC|ahS{EQNC3#JU0ZfT!nacEVRTJ5 zHI3x^pszH$@k$-{y!9n|fQhx|c{XC8vrf8Dr-=aRmUkenUo@IPOATsL{lyor-!ZM= zpIJZ^oL#P^YfvDA~7O(!t>ivv^8Oxa7 zNk16BYMQ66thEGfCqqMyy@oyyL|*4=_3h8D|75}cU@3lQ(l0h-mp0{KW(|F$y|!eF zN_;d>hG=W35`vGHYrBZ=28G-_KSj;tkxfTTM0M|e0xohVwIaxCNmDiaCn z?LsbCMOzu7<0_1|I?7u?Y=oGZ2{4}N`pis=ugTQwv7Gn6WM7d{Bg$_Cs=xz^_~bAa za$~Wck}^OCIuF+i(nLuf2L8Zfv(IDr3g3q^vocz&ML|fHthlf`AI?3484C5TJrvZu3C|N zU4Idu!$N72r6M00{}XP7YIq%?128^;!#`gmu1EvaQnfr%PQ%on*1V6nDiUSis-5L0 zY=+TS#lD0G`K`e`c58Z6q^UO@5v5#z@X=A&+_?VyMoO6a?}DV62c|SEg-s-c_2X%$ zqO+ij4KNF%9Po<50(NFK9pHvfwX;E z(<676?ZGcRS^52pM9gZ5Riir*J2pSTbZR|H0d^KsGRp)?Hy2o5p5`W6BX#K8_vWnc zPh}_=k4}w?&I;!>$8gDr$hlTSu+H;P!0_(EQk!vyNi!l=`r2CcqH-`knc2$U<84}o z0#MuZi)55!sGXRg)`v>cB>wDkUt_^RK|v^ONlK z_6+7mUVcr$Tg?|eA=&b)Ylg>l_@nI;Pw7oP)ZmZHhKO@xM3`{cLk}HddG@}2P&2ML zpbsOXqnQ(?rjpJ(6?t7ww4Z9KVL)o!v^B|iOJU=2fiq*%DQr!sZk3jh_OHK^@Q?t~n#dl3=Qnj-jmhq#Ecz>WUNRQOnifvj9QlegM>Npq&)C*r zTFPMGF^uPRugK`pK*~xb72&cA!U&-^77jti#pZ9V_q*SjLPiCKYR&5<^}eiZ@tGzq zR_~{};XSoDgQWAw1|ZN1_%Q;04%TP#Gst2KJDh?gUObRR6 zT(G^Isk?+i>KEp$HLEi2p7=zd4=xtHjy=M*?i5Twq*u@m^6LZA}C%HK0{tP$^1q zB!!;g-a`(X!V^VhIf1)Mxq+;_X^9^pz{$6uH z{xUM4d25f$m{J?;B+65!bTCiEDN)^OMCaLFguuPbl*rQ&tq699ivcbc+mB3I|NF+<2zH19C0l>9;sK)L(6&H)#6Tvya_b<`&60 zvNeZ@&Aj5ygp~-TEbu`CJOOK1&<)wJEp^)EfR$PwCaPt;4?O&FMVKwS=zfc|XX6Tk zU&BHgeaBCD{x@mBwN5~mJ(~bJTt%GtzyJ?t{)0?d^!?L|rNl&M&kW+vy#|RXfLilkG$cC1wlpWLm zv@i0uq0{0=+(`mQXi`-FP$gs?`NRTftwW<>QlAq8I`zp}&~aQw7#?lTXofZB)cxsx zlAyBJ?#e1u8mE^h!oKQU8zmKJ-phLi%6E3DvnYH{c((10^OZ?aM9Y2^Bl#k=pnzsw zI_%SunOy9CX{57c1jHmobTX@&|5G{2J+=+{T!*#ujWKEVNO+jHd6g%N9{DYe9D zcvo`c;_IgCtNZumVRAh7MwM89k)S6YJC>>h4)=Q)(6P_52W`ecxp{+jXjnJDDp6o+ zjzy~JqTt0o-zjxPt*d7w?+q12HT*{Un9q)(?C>1#v~_ec3?DrBz|qjuq`ceFFLU9f zlPM_nUKloy>py`~rB6Id6p)A-il93gKN-@nrns{7S2C~LQ`sQqp|!R3d4RJ~QlQ?4 zc+p||hIbB-=Efx4bIZ?H#=PrOtk%vP=Ajfkz}aGl^M4_$#h0v&=8g_l4BYvz#s3vd zjksEYu#@FkBdBMOQ67>9*g=xbd*DwR^+Rx)384Ax~E%mmRt zB!Uh#Z~VG$O#BOnESZg^x$g|v&Zn-Nudy7yV+W-9iua=0VH>&v0FGS>u?(OBbcIkJ zCfVTkQFV`|dpuPG&tf~a=n@&HKygz7c|Mn}P*#C1u|Z;>$Mu#Xzg`Gsk(amAdsb~()VenH<< zCaGbq493@R_4172jGcAwCrC}B0cx78MtK1dCV>oaI(ypB z4Qe3#KvY=H^#xMWWN@AFW?h)U>qh%mjlu;Xdjij1$DF+btw{LT_LAm~AtIdHmpI~- zElGRK`0bRDJ!UDFlIxdO9Cjv;Ma`o5^ga-m20dZ-0EnPek*EY3Zw8^IR+>NYsgg!L z*pM^Mr8=Q?f8c^CSRmAJc9`v5Mtl`k#9>N#7>T;8>%y`!btj5SQpaOtx@siFa=Lu$ zgwr4Ffeg{0Ck-MF-HkbpoItPpF;md8reoasY!ueLq`|3ncB~a!32nK{#kVY``N0Xf1~HE44t zQ+G8VnXk_=0Oik)3TOv_@>h5Y=wgoIO8?L~u$c-L^3-zIL_B_*IhMPbI-Tb>sZ6<7Hjip`ceh-BX7tq2PB@1iA zK+}z=!OStOYk2Gej|quKe{|UXcA81D1>X>;x?Zw&WfrqqLcPF+wwYRH$t6V=DJASp zRIZf3mDGZK>zG@H;IdSOLYfm_*7$|YluRMBqcYaS>pGFxXXcO%@zxiYBh=rYaLMsS z_x&x4Wz45dk^_-xiJnIKaL{kO22??y`vg5pwbir+xM6JVqtfrydU_+p1Ebxa&~s3> zFKP)ebAPEzxEdh>H0sVVQf9Qy9DOBrwjRqz)au|XLZUl__=aKqEHC0yE zVJ5^!tW+f9HwioR-iaCZHJEx!a&KGGySOG$0=|SjT8nIskFG-jP26A-sBD)baI>`y zN+Y4-pGnYyE&(4cK4>XQUBtXb8+8fm@+sXh>nseqOtI;7F9JFIYSt*&gwc+t~n z)Npn}m4<16ER-vPTVf;IjMJ3=7*8$Rw-n)!CVOHt z!>0!wC8i+N`kYe(YrhSm&Mr29idp6(o?6rz-^;3^`C7Q+ZyKaMs~FtU?|>-StX-At zO)1@xN1yWI2i4{`(beuhB)0gSTmC8cgk~r3bq^z-QSRM!xw{t%CoHbONh@Er?I(zb z10vqwET?<5k^R}$eev(jJa9LIDEQP*p2{h5fFnQo`&%_|`obw>yqXuBv)NtKphT%ShEVpr6cE;D9 zw;`jd#(EUVi>gP8j~L1;!L)L&CN-nqr2pBF*aAos)`}DL%B-D^;W!ccSE2PWZiV`` zBRH;>Fdj!(YaV-d_JO)2+`sS!^O!X04jQ5m%(p6oG2~x{;0R+FC%;0z3AGc*f%pirgT3zdV$#_KMzuxa(EU_UpM=x?$~NQaW$mp8fW!c&kTbqVf1r2 zTH5n{I7JtXFm3??kUmPH4KA?xY2A?NR-&wEEQculyoA7LD-msgFw}CEM*~?Rr)Egm z`u|0?$XYrX4<41@5A4tKe#$I$wm2={HNPff-T~kKg>KNBV>FX2l8)rj#ijugQc*J` zMmPAq+K!?3d`LGjH*@!=nD&Rk);12&!~LThcRArUKY#R8s}$^Xok3YIvLG$onE2}b z=p$Oob$OPklko}PXSUJddjR^mBZWOGa`75yZiJIPmRbgOX+cE_7&+3x2``VA#SPnZ zB|MGI${$+5Px+bI8Bj=$UdphC70TXh@gO6qA4uuk!R~!U+&?~xGV`}??6s6=bF!pF z@3I6oAn^(Cv?sV*SMQ2vmk|SiI7u6EAJ0UVh7ES|d$X$s1X>c!k*a@;WtbF?un(_8 z&QLXov9EOLAS?Z~e-Ieh_EOTof7Dn)U9I<;OS(PIJ>V}jE&K%!ugY(g8%<6W{D)bV_1I z3#LFVV&8Nl4`e=K>DgU95u;pTpPM6Se&h|;hesm)HX&7lKL6;n?UNczKehRlRqWTd zGKE|h2O`l~p3ctBxf3^0pKON8P5ysct8$>VBE261T7Y$+1xOzTRfU!<4T{qZVj4^# zPmfGllet;t16D$Q#w6!GqQsRvtY9toV@W&Ge>^(RLwgyKAuT$fMwJ2y)?0bl9hFRq z01YtR6tBN|g~lz;1otA=NS(Xal}`Uo&l*;RwX8a4ws{l5X|kPP_?0_qQpCXinj{;O zo?@+e)lcWnN^bi1g*W&fxsPXR0Mk9u_bimxOnMS0CLVfYrMGXiw-90T*71$sZnSt@ zr#-RpTFNXgZ)(eL)|{{DX?ilIz2JLoYsE!iBqlw~H=Zc~D9AFof~2AeRmq5KLG7z| zFt$etLOn+8kHQj8Kif_KIpq#Rir%#gXwmNMF#(_z0)2jva7{t#I>Q-JT|aY3Lv-cv z8~J^HJPvpARP8_q;{wl60n5|y*9XGEcuVrg2n;{*@#m_~@E0E8zp0-WRl^N@-Mhc6 z8Nh`o4M$5l*i+T6e{;W%S;ixnRKAnHTMKJRE`I!-w10-9L{40nnpZo5(*Wx^5pZSA z|4Dd*M)=Evl&e7P=>XXgnQBh!K#xYZlXP39gN&aFI3B4{D?X?xrhRaKVnGTvs>=2XTT%It;}v7Qu-&%eY^K6I1lP>sjkvPPu5=Q za_JH-7RWvd7!_-xeon^t^-E`jH{N0_ZKdS>W&v-$zH) zR=1j`0&!Vt(Kw^w3pE6K8qIn61lndj@x3j!=G)Bi;kp4GlEKJx} zY&SE!jw4LYd&-E=5G(EgT*n-nTS~xLs^~>Th$Xw8`on>1LPXR(3V049aUU(qf-UP97w-nNCRV;O> z!F+5!KihoEI(BGrFKy(l#VjGRe*-lp7%;g2bKW`p;sb~q9&-ZO-|Z041m@>nEHKfk zk_zcEU+f`g0L7qGRo2t08{WJJ7+n8{Buu6pbnQbCKEQ?lf8Fvx92ltOa`J#e$&G;> z_Vjc`7B*Cft`1P!_B?35MxSg7$XR*kdM>ZbR6CMKMxNf){~?L~g%rK;7ufLSx8u!U znCbzvA%bb;|1_*DfpngKsT0;GC10KtpGfea?p&T;nAHp(q@E@A{7wj{u)o(6urys53k0 zK{)k#Sbnup+rM$?6^+D~ca!>HHqA%%&PeeZ1R z&0bD=9;xya9=*1^@?Fr2F5oow9~S*5LMdTi{O4DmzP!BKq`(J;FyHHRY*GX*dO&=# zjNEmqb2^HVHopgq^g3_d{GTH#hvY8_DUAk*+W=;M0Vwl9UC+le^CSkhG$NkS(m&^L z?=VTosM#9qeJ_aMoShD$;ML^P56SOMPru?i|67n#InbaB0UA;%USVI-Zdi}=y_}J* z+w1qiy~n_T_aHs2oc3Rd`1>dNqR^WQzwX(O)&~y%KJQ(Oa9d>wL+7fGd6(&E1$+xM zVk2S?gNYRbm1`Mj5aWKld%Eeiw3s_j z{5{D#euZg_1IEevuhj>ChW}OsbDaQz!%3(#bWy@|>;%SX{J6CNz5X9h!xu61E0K=? zLMa*M{~{b%U5OVG26V030`j#HSXrr4jO{UDktcwbd&vK{L}qZMr<>0&KB1CObBQdF z+P~k`HtKSg+Wcgsxdl|C1I;bWa(y&-@BtM*iAJugmwVVEEDX;b{dMF`708|k^kqs_ zt;${Wh3J9557y@e9o(aGOHEA}tg!*9aRgNL!ukN&srEKP{RUn1vBR!ad4Rn*puK#c3C+cz?dV@0tqVtVNWWS>>0)k5o84OtXosukbkvA-U{E8R8}Z}5|__P*p>Xb;!}-WfRAuKND`QO7gfpT3y_2Em-*UKK$ir96i{0&Xy_Xt4K)nSAdj zP-E(CQ2R*Gv4DJqQzzbF-vL5-_ZSPvsE1wOxrA5L)Y-o4o0lFggx-a+6?0|X&-fP~ zyS=mm1_oAN1F3`@P*~Yl2n`ATtF+F0e<}+b(&KdO&E)jk+4ZQlQ$AL)(a|H~j6Mdq z=PxzSru{cC+VsB}{Xka`QW#lcg^E^zsM#WLA{>2G5?^}38tUI7_?wm8CR%yKlG=^IC27q--8?t`!yjx?x zHIc!cCh1rq3!I2~@b2uL6F%KcfLQGZGO5Sf+PmUGf}^RGem|drVX2V8?AfW9GS1fu zyn1WH`BrCv+1Za=JCzf?e4+VJoBQOqKA;GkF|fKW%_ueZ=S@ zwlQ+zTh_aOqWp#M(>wdG9^d%0V~6eR%3YhzHdehIQ$mCv=OH{#xR<3Snoo+!&ES)* z#D#}iEAniVab$u{?N zd8ld|7FW-tet9^)Xp4i6b*AA>LCDbW4Nii4$;AftTnNr3-!fGMH_@hbJYZ{jXgh+R!i znVz{!<}~?Ip<6!|bss#hKZ(x{ghGHt6zK8a26RAo$2%7^GInvBgZ$&5Aikl19r=i< zHRgd|b@>|Tzuirw_Uuj=3GXXDKm1pz@X^DYaO&}Fc2N(Eh3c0k3jS@T2^>*3T^N{3&KA|5M&OVua!r! z)U2u(7=9=F^f=7#>phYp0Al`#)WGltFADZISpm!j$WJ;^dN?%B3?$2f-zav0wu`c{ zmH}+$_k?&mr2%QMQGb86eo$TiD){5UF;gmauGAg-FyVzK`h3o9Jw>m(Q%(DNHk!~8XuPSlu*iRC;U@EkyTvgV49 z<6Q6B_dXL)!gPECqHHm#oYJvXk>W;^`yKlp>45iiSfG(#zfn0=b$4wQPFOT63|s=OlRoVb>dIyJ7FI(qj^D>);6UwN&?z*L+4{=y%2mxy3)Z}b}qhtE1>eyes2hO&X~lkF!p$+(`d+MJc| z4=o>A4drg+((4c?Thn z7hgVgx$Xglp@t5 zL9FDk&sZRUw~!E_<~CUQ&Xn<4Z(P)`y0P4@(=a~-90!aO;#KrY76y`wikyS8tPLLM zxDLz@nI1SSlwzR)zta#7f+oS_6)A9UU7aeJ#;`Fg>4v=#b>r5jnDYI}wVcXAsg?on zK@sqXxedT^pZqxRZ4f2SoWPA7w2}8#6@*nupj}f0K81k+-(EOq(^WoQefc93kXUwZ zXO(<>a5lf^j3bb&Ere&fG?l6=#SWK-GeAfpIrkX15{d5q|woRiEl zjwn$w$~s1NRQ5cw^R;qp#lguaD?5AZejj~*zx#3D|95iE=ej=E^&YR+^IfCusZ7n# z3=K~F?6T|lRawYIk}HX9OC(T(pP2f3YUSTjBma(59M&SBPO=UMuqz`k%%_^-OIcl`Hx*?6k0y`opEvTNX zGU106r(Bo?Kuj3HqKe0TED{p*!#=C-dTy7W-LTEJHuP|s6*N~FZ2Bcv`}TTbQu8=R z6uJ_s$I9s`#(@=l2JVf-9|0f3skY3OZ{%2N4$`L&0)GsBfAr<0YZm zAaiI@4N~I59lNSkB2wru8WFINQhO!goGve%V_Ud;i^Iz9j z@KxGhVdwZr5kB*Z=jUaEf}Pr>0MQdzY@_(h^!o=CG<#kZXeS05xgQ52hne~IO;Gee zZ!{!MmX=$&`P)g_p!T;N7e>u?E)1^w$vqtI`J2>fm=s?`_Owry#T{&5v52%z_H%%3 zW0&6noj;0y|8niO9;$J!y>;AXFBaO*bm4SRJ}t>L>a2eTLzme+H&4fX@CYW2p~G9v zY^BwtxjU!tjr?!k(}?S^D?GY^Sk*S20R9^8$&+{v8uViq)jBe@zEg`}&OIzVjgRke#Lx zm(QvHCuEG?1GIr_nsWkN({(I~Ais{&R$M{B%jRmTYceLRZ628k%8M~vXnxB_J5S># z3j%V=xUu1J+r5&%trwrD4=Fz*nwKl`FWOS+Cd;m76_5XlD))nhFshGpEcPH*DmlRM zV$W)s(u96I#~{(?UpTHibooat66Z#7$Az)L9BR_`Uc?pZ_877tewq+mZf#U5HZYB$ zTol?IOZ{JUY=@>G=Ok@}(VHPfN8$KE=P+e0Zh96eN=W8bdElLve~Gaj`5O)Cv3ZSt zOr^y|mNF3=4P^>{eD%wlJ*eapC8R0ceyLGq&?3Tw)sH20)b{e0KZx}6_Y`GNy9JAw zDrN^$EqGD^^yA@$UuytA$0vFwx;Y-sX(;V;_==v-_8CU8n{o{FgOPB7R zI<9uORwYvWdp?ApE$Md;T*Fwuil-gw@d0C>GJhPOVS|%r9N$032V*(poc>nyL<(}9 zP^bgx^3AOcA6|@kh8mF_;H0x8J*PVe!n%Ssd(7^Q-1m2Z_$J#Jf={7-QD&Y=PM`y* zmKyAdNI{_e zab@Lits0ti84N|lEofO4cs&?&OB}k4TIRe5Ckmo8kR~{bH8N!88LM>*F+DNY$)U>K zw7_9Vac^6rZ^MH{-q8|F?A1zITwn7{nYJ&oJ?*ab@ zLgNQ*2{>xe<(FCm5l&7P@n&P+D+Wy2@}#YP^GgP<*({Z}sF9AHB;iaQvE(1!@w%6p z;m`%)r#7c?W?kx~?fm*O&955;p45%W=kPtxe0+=Ta1~e#I~b5-ZUGdtve>T_>+4}( zz^P&T56IjG_YzD;fb^t#HCOa6?|vyKYRj2Y^8KQ+aaQ!Ky0qM%BMwc@o*b!C2|n^( z>5Q>)=WyqZ>h4Lmy<#1!;87*)IQoX|KC4G(kI$~$@#LmBUlqR9*Mg|m7^bdL(g7?% zGv+NGQ+S5RiKF~0D*8oXp$-mCsP2~v-pjYFZq_WYNBl^E#9O6Ce{ept1CiB+L4Wmu z!;CQw!KW%WDAvpp17&?JxSZ0dDLDR?rf{5dt116<=hJ1R8gW+`Tjisjb#o zy+v1O7Yr5`^zJ6%TEjA-kqsoTRC95vs?G8mG-r&f0_ImM@Ha)%oKBW)H`&_w@fmKw z-aq|t)^e^t{yV>1r5GHbVF38D*(;GV^MX6JefoF&qpZwMXRS83M0*NvWhmD~myyJ> zoHc(<$pKhb%+x^|^s`{u1815-pLm5Dn;fEwQ_DUqHarc(Gi4}9mWPpGkBxA%1wtgNOI5q}xwD_v$Ky&= zD&-JHpIpYO&mo(B$ddDnolbcQg=vq}76=rGtz3=Yt??>7w`3UJYw)|_C*V)v2 zK`G>8;w(!(HA182Dk?r@kE;UYU{a_d#HiK*E za4)hl&^U$sNWl~}Upn!CmE+Q?F`xQ6h{(cpV&@(6hoh+~)O)`S-NY!?nPqM*6MpP) z7#yniC_N4iz1c#}R4UOYVRU=rui0f%D1Iexa`wl0ksnf;cbte6>=L-E#Mq3g&NQ*P)}`L)>}6huW3043Z~;_*;fA`s z#VLk;OC8g-aLP*Xd2M?3zSS!l6&~gKdUvTwV1A~0wSV?J?y(8r#RTJ5j6^S{OF3bC z+$>ZIk5rKl$yJ5)ovOP6e>leICLKr~BFCQi@fSHTQOkAp%#V&k2DyiKg$%MJ*K1a9 z)j$r~Uf8<_E}&@XZ!RM~PMP4Sd)>Eyd8eSO1mk<9TaMS%_)js}ZN=QnvE)B#dkLe5Fs|I}6!B^sQx2U)dcwE0` zgK%KJ#I|t{5LTQXn2rfMct(PPXnEv!)7BY@Ciow;>YMS3-vDw$ccsL_`ZitpRci`|8=EZuKbvwB)oZHCV8E5i$3+x9ls zC)*tJhPujGjT zPL^Zmd^N3g?e?9_t&%5OxWa-isz;zDohn^vQ5igM%&b&%4F&a=#(N4mj8ZzBZg7eU z>5@fNbi`k$Jt(0rl#mz?uw2Z~=N%_~~Bb@^ZnKYv?@4}PdvC3sLs!)}PwQtGS$ zOl5m&+X}3yrJ|D+KM9q=oo5FkLD-P^Rn}2)l$BokU&xzHjdUV~$7Y_JO ztM-#7s%7@b&`HD!c=7bve1b$wPwT7Do1|`^Ne(fNqDsB+P8Y9g9!drAP~LO>1yY=0 z%7y6wcVmgKqt5!R%N`mDUW@keX=xxc1dkfXL(+PZmht%h-GAz+|W_Uh_+_=<6%NTPxj_>j>A&0P(=$^R2OT&2;O|rdK+`uCZ%+C zwS#1l)5uulM|}Z&a1T`b$V!oURw1roeA)^No=u&pC3{8m^S5K|=9X+jjrOUTr&f35 zuVck8_3+!)$?7P&{gkH$5U2HTaEFG}%IOVxJp@0+BYNb9B)mnh3T{e>%MVU)rEY085kDSO;Ef@hZ(Vl+#$Uu= z7Tc?^HWQ8sfMJbo7mRq>9KC9*w+P0^)uZ`L9!k_3srMX-G*L#&Bgsxzrc@hsSZxze z#VZ#;_Ge0sg;(8gbWd1Bw3_=>Jv8h)S`nCEiY8dral(VI2Vp}bQ}mJWb?oA0uP8LD z^S@`$417)9g`jJw))m#zVyI`EbPCVA#|jFeiw4GGew$OD z!EhKY*QG1pFvdF2#MA|AcVi(1IpDA__rol#p@;BrQJR8W}|7 zzQ3Ol+hA{nr6ayJ@>L#mqlA^`6mpr$oKT0ZBNdr@af<6;tsyfRoE9Dg?tPVW=}^ zy2HjdS$rn{IVkb)w}xgP?%foaea~m0v9IG@t8j)((Tf_h2F7Nu#tc@V8$i5BhGgFv7QCKqnwQEMp8tD- zAt&FyyIIPoK&a#mpyO9rb1`;eGKm+-J#XCtlIIH6Q}D$3BeZWUv8?6O+br7LO~2D{ zEv$uV(%b&Qy;r^|Omb<83{I(=?qDc#6}Y=aOHIZz=Qu*Ke_^A~5Vu=o$^R1db3}76 zmyiU>9j`b^i0tAm{zvP(<9#%DS_I(P6gr1$YVo6(O-(*mdi{mjQ=*;cprhm;AAQ3< z)7V3|i2oUM7{9Fi<83#5yGODo-LteAFMwCQ``hBrG&Z`1QO})u6>PS+ccz_g#3J{BP}jhV{Bhwn7HRsLTQlv+#^3e|CnX2}m>Y ze?TXdH2R4VFPc~IOp>KUFl#QX&O!DQv(`4L-mne)7UH{q3*qe<&lKHn>Td9F0h7}J zns{@K*5@MV=0)ZB9bRIyVvZ*I`{X}M?!*~Gtdej+dgA%#cWl&;XJ$N&(3Z)r@N-MV zn1p|+*qk~#{^gCSjG(FGQN#j^x(Hy>`wrpr+bD)ASIg&D8bif1|7F@#I6Lu~3&Oh^ zQk3KdIJTLm17_CO(G9XfbZ`R7IRC8MldqO9N49y~jVu-Pd~+NZO}Nl}TZMio1+UUr zzrKU&`2_jF9e~V7(?EIM|4*>?k8;t*SlRIwRzA7S)O1tU!L;Xy#(PVP zUwC{%w)%K>i5oBVGU)H!3;k@&y55LbZ0Qq7BjLwSZZ64JWn**HU+Wx)v;1ne z&K1)8&_-!m_Ib{vH*y}!21_K7zd~tiK&6+_e$1&J4W*-Z1zP1H|0xjHY+jrG==)Ge6({Ozd7li034Wkj zNZhfaqj9GMJh^iCo|4f-A*$*Bf$0{Wnbd7?qd4Wc1EV^lAklvstAPqWY}Ocjk@~YO z*)K-SW_oBTH5Xw+agaN6D0*lX0IqQ_6AcjoZ+sL4f5bqWE7+2)?_XDp2$q8L&XI-c zzo|#yKBQ)PV8%@*nUIY8TB$W%bvtKt84insZA(YSMBQrH zA|eDUE;Ou5swCng#_)ayox+FwuZo~b6?lBr@ziLejsGA^drh8DXWXNz*&S<63#P(~ ztkI9}=l)v2-oE2|HIe}aeECbmY&3M1xe__bXGemM987KIl6!* z{?_}{($doB8yoKGUH|@j^_wk=dC8Z}{l~xe1$$W_1c6GC^6o)H70_35Fb=4P+n@Le zXt<5s+}p}*bZ%q+3jrDHZ1}C9^Ii32UH(TI4PhaFEbSUlnltvfz~uD)WO9w&O$`t3M05*~Bf z(+%q7gjBsZ?AY{UT}P6kptn&GIqG+CW5g%1vMy}XnV};>S4(+7=7iPR$?jzL}>YvRO| zy5-TQr~~dE%YXY^WZE^kN^YKB+u<9HEt)H@5Q(DF6R3SRu+7lj*q0RC!y7LIr+O0# z1&M+?f3o-nRI|(yDre+y#8jYbDc5G)YT!!53u>MSl}i_E&mo$&YJ@bnepGe+!9V*O5*4Jdq|8yghT|_k9`77baJH<)K^bLe*=(7FO3%i z$(k>Ai{tVe&72VKp`J^Rf4i-C91WpSW*5_hPK|3z^-Ou2Ro&wnx0stRWttiKKb;7N z4yT>kOjwce#2BAfIqli1LNUe_%^Dz)dpSOupL{=06&m(WxPqUp0kgA$zbJCDtmO-_ z&)*5&4I>>xy z`eEuruJl8DgxDk9dogAo`(ZViGSH9UZ@l8lA2j-o|43n|tZJ8e z1K~ADp$)0+?7gO~W{OLX_3M0bSk4|LLrGxK#Bj`;=w7rrAZDW&3x}k1i%rzO#ARHT z1=@Wre{!T#s8n?zYz#vk*y0e~-R%#pARH3-OLJYd|Gw|i>vV;a7A-Q`Yb3|g5`K#hTx8>r)mb2kirpIlz^y@R$pJ(>%FO|QUBJT8=xFHW$~A5MAFNW}scCJDB6n4p($6GIE+vy$D!v78)Vk)x^J5@4>ivN#Wcz)fXwus}-bx ze1K5k=z#SR3yd!jbBP!CtstjToquc#M zwZ+L6#``N?j~V537og`SJ-k~KLOYj?oMeE1=Ogwx{dz9$YPtRI5qTuJApphp_F{7I z{F6WIGDD}8n^|EH&bU{^%g(b3^U)bmw>Qk~w>|aUDm&(nHjRbf^DLpn+k2e&EG>1~ zf;ry0aA0+}iu{uEB8xSQ{(#o$e|WLSuJ;}f)L86}JxVcWL5OB)lzk{_Z2TllKx53F z6gl4MyBJ6;=Q8YuJ15N?#oNG>^Is4tQ_rAe$Xf)c5xDa=n zs5qLC(|!sZ+S5xXe6+Zznf7_7?Df0 z>s2C-3Y?)=JWcJz3g4Es*I9i!x4CEzZdkWI!p&=SEZwC+k{Rc?d5qED#;N)ns#pv_ z2feRdt}et*Kj#l;7HceKz0pFunle4nI^64|?j|4+bA)=Tqcw*@9%Op(*T`IyJl&{& z@Au~Pg=GsUXnIt#&Cwy|$^yy>fODc6r%EZtpqBV-t;SZDKy*BLr(7Re?){+V#sQF1YIUs5kd-F7S??nI&9 zr}@?BQhxWf=4J;+dM>oB`9Upy=zZOmBD2yH&R14N=AlE|52?l8lIDhXivab8d;n74 z4-_5s9TAK2sBBkyI#QG!YUQb!Yhhi*2t7+WxN2{|>3uTLUS?n(dNgsdGzSin`m)gO zH5+HWO6oiEha_?V<#HJDMH{e0%Mm20O7MSyWXeqmp|HRxpz5lvU#UYMeo^)zN^hyL5v5&c`*EgEiNE?W zYpyyf>SpALW!6hI^*zwtzs`{PG$Ok~Xf=f+sQ2p*lAJlS)j4m!6k|K5(e}1ar&|nh z2EhKWGzjLwB2&L-w+hg!|2`;Ow*S;xjLrs{htZk~-kV^bEC-vTJ`iq>c*d||afb%0 zZD(^`_xHp7M62EN5>!v`xWlf*PriMhNye3+(X#y*-s)z|#qG>rGghTClE1Ta!nuiw zA39;TeLKDfQD521-=^)9es$f=BY6uv3m15_46`vp0=ZccgFHHHUJ){FaEdv=c&2Ox+}6r@gf!F&DIwIagjyI|9BqIEGgH%z5|!oc1S-jdN7` z!ZHiIS#vIH;pf9;25&XxpB>`iPNzg}EQD4w{?J8OwyFU#bKRDaV3`CNh3odh7nxu5 zt+*yiV_0T3j8^-gu6E*fk!bUnl4Ju`?|Jj;#?=@_Po?m;N_aCB0tkoVG#Fbih=v4L zW__99pgOfDnly9PV)`tbx?LdSpoxr(6uTtQvt!2Ck9!uwGDOHo!2iDRWJ$bxT7^pv zajHB^dOZ*C_$a0rS)PuwdSsVEY=7kylehm0;j?`5mIqpQ>~zf+}h5aDW}j1 z%ZB$A58iZG92u{pryh}^n>%VeG(Ju*yT%Ma3$68c5$GG|`o`yDZbfc-=gHYQU0fq!cyw&6#ZwXR!;<~0$r=N21PR#Q||B-S;C z_)L;m|H4pMk!}Cr`B5^T8Iqh|TGhUhKlhS)?_AekW^gFaw)5<(su;OO6JKD?nM_`L zGc)w0@)+0l(ZW*J$9#x90?=?|W^{(-87-aBgH;6;$I15P-@jr`*RF;d$&wEr^_PeN zxtVsAct?&D1Co9=U)Utlta)51Qmhovh*acx2=^Z?CRii|MfDq~1my$*53|%n^2_a$ zagM>>t4F!4Ryk{^IQerQtf8&YB@lO}-c~Cg@*Fx|DtQRRzm4b$7Wgs`3;8F}ZB^>v z#FFyw;P*l9yPmaJC0g1a3vq@T)vq1#Gq`dz16z%|cAa`LUiFp(Ht1z*=&=%gGyw)s z!gq9ZH-!WKjvcczJJDB#D^b`mzr5Ibk(wPQK_Q|iE8r$~Xve}`Jgz+Vm+{kOkMmn= zLosmYv@$8mBkn>~3Z^d2q?N$G{l)MBST$vX&1obiUmD)0$Ilq)oFkqRR~C#r$y#k; zY`7dP#flse%h+e((F)?4ibaxx9bU0ZaNp-PzO>c@g{O8_sxk&ok35ME?2N3KS!ZcZ zg!0@NUrm6LKrAfLf z7$@KGP(b7roSm%uG9gU3-KSqvke$L5J7ey8{KSy`9ecRRY3j93h|4z?J0?QHUNBw9 zVc!v!S!f%%Ju2`gm2Q8#2*M{(FRbiXxUEzj*f zdDtH}Fyh}aaQbQt_0yipPe{emVa`G2na6p&AtMUFz6f#QRzPofNnC9IE@(D)=yYm}Y2w?wj~FMcllU&+&{b%eMuFL}cP9@6lyD4STT1*;D zB-+HU?da4c@AHY&42#P3;w+;T>=9Mz7N>;ks~r9cX1L$OVOx^hGhyK;b;EjGvqsSH zLVf31W>$ltUylUd-k+c_s-eyR0my7TkXj6$PYkH&G@S zkJMwW)FcXIV4*{>PyH%zvRmlTN9wfVB7KznivkhND|C&a_Rxr~KBsrXL+%KfF~D}6etj}vK1M8X28Jn7H<4Sc$y6X@ryb)YO50BJYhBmwQF@h3jKcV&JKBh z6%{Xp0Y%x6lNTz;x@?4L3b>GU%T*{=m7|8B$}{Kw3fGR{Nl%cjtrR?cn)L+7P-mw3 zvuyYFBlly8er_<9bPi-oE2E2;#y9(XE~iR2ZNL2vDYgFlu*L;RwoNc&3C&!R(jSvb zhH|gZe)1wwsq~mRQ?kB7+#A=O3g!1x#>}QW*8+0px#gThHh{9F>=lG1}!KjL9+6kHsd`7whT`$Q?$BY9A63;;w7QWO-WQel&LAg}Fwm4P^W%O*o&2Z`iK3ITfGH-AvhS?;I zgz==#$H{LH^t1=t9OrS(oPNkVUbJ9Ttz1kSf6Cv4dgYYo8_!!MnY)_ect?Qy9ev}! z4`Bd(|Ek5}*5-!;0A`z`QtjNZC{SrN`c6VrX7oaa=e8T(x>Q`;f_L?NUP1o&0EPcG zN$T;|EGK-IKe)pDAM0kcJ`#<5uFk8^qbgTGeB^04*fu;HJV zR{IPRGd5YVVLjIT5ft75X0yUSAU0Vx_gctY@qUj`mUkPoNF;2({!W`%#h!1=gB^`L z_29lc8?{HCL_-{?5BM|IdP<+OXmsF`n*-zY0$?q6K~KQ-R!(mr9x+^r6uK$PegFJD zdDR=s!r`@Xza*bd!hs(ls8JWSZA*upm+}s|`O=*184%*C@Om+RAKP2S%+~jbyXUF0 zsM$|l=gW-)qYl*1;Ur_Vt>V^nP<@on9irxE^s@vQB-P@fB>>mawHDslXr_1Y8fBB1 zrZ>EZA~3mhsnzrXH+~t}c;kT28JIGdyKTS4{HG#AyS5#*-17UQ-Izyc%97QXQ_=u+ zmsDMOF&pGJ9VRk#2<1*DZQ4g^rq-p{BH6!gg_r?(q2`}IFZ;gl?UVR7)ERFAI+sG* z4}Y3%Z)0itk<*Tbqq7JCmfPptv_mRp&x&uI#VdF<#-)uow%sl$dPk)N9c?^Iqx||h zIIl5oxS-v)ndc;$OlTrY25gTYCjf)78dLM}{ykP|CL+;LTsO0>M6iDz^6?i-;uIPc zdf|fQc~uwzeyjwDdyKp9Z{s@C_g9}CJ(xq}X zZ&t)L%$3i-s%xv0m=1{LsE-jB=foOkQz==5LoV|FcZMyx;zR47r9a!`H(W;%S@DD- zg~d`*-CQRwIe6_V7%h04%W3%~3AT4UX1*#VXRix}d2L*gl!ft| z?aaZG6I;xu?{Yoa&O8}Fhe-3#cy=6!+qw(w1xC$zFji0B_Hsz{$XSUPB%}q$;IvTT ztOm3pDfA#~X6tH}(5hDdQj0WOERY?Soaym{;* zf$y$y`}}#jINQ*e;ZUkF1+8<#T9GpTtoDhS{F^O>YeBOZUws{f^tV6SezP|3Nbpf{EGy6e5`?L@8Fcvnn)ix@H z#AnFb_K5#8RnRo;d$Vc@fH$B;SV}F8ytp&hD$-#d> zwBGUgspZ{Gc(bjs1jg=@2TuX*e^tN!@e<9)0?AFPON(WYZSqguk+P2YwTT=SI`64#(xgYw;_T$7Vp5EB#4dv z!)z0D{(LX(n&(E;xfpjn7qOsht0=i4{k2_%DE3fKB(Pq2wYeNX>{OR`8nY)e|6u{4 zER@7x8OCp|)1Id4OK8M~Ed7U$QE%_OyWzCv(s~_DrRLouoP!j%7*w})nMRlEPL&P& zr^`NW?Ap@ehdDs_XsV>Ba5a|`9B$9jP!F&CD|g#BB|}@5DS0tujh+qdEK8b;FZOH3 zsE$1gd+^YU4)Nj#XC;M_8Z=#y6b&Z+lJf6T)9X~rtnh=AK}AsUFlh!*PM=fO-sxM? z+AJ7Xx&RgV#hlJ04B_4J8*KX#y(R`=!2SXSWG21`g}c#=Ch;U&j#Mc{Vw#&-?!4Z~ zBh;Ym%Sn^X9-H)}K;+~I#fzb}kl4sFeN3G@yh!Y)^M0S;LXriv1@C1DR`blG69aWs z$R+fzGmNHGoRz`kq&(fn>r!sszM>=xH)VsaifhgSU@k>cw{qXOgd>v7Rl7hr7S%W* zGXN8eKMr(whCB(yhJ^NI$0w!5A5`klKwj}_mXoEJHR?+z7%5&yZ$zA$Q!6v?Lcf!Q zX~u^Q*^@OxGQ94zX9D+tL7_(NQj1Hu+28>mNl6XLv63wp=}^hRPNz@A zv}d?Pa%0dti=g!ugk`NDPWY2-fmj1nW~*liW&(F1Y*(~i{w>(N^&)0DWU}Nh38csz z`ksL{LRcG#DZ%H{Pz}K}V3vF1HI@_+Nw?TxbVbhCLh1$!O7saS4)X$)K%F9oBBy^t z2A=RrawUPMDyp`1Q=x6;pX}M0XHe0T-wf>aPUM}`1MDn9IUXqtnCH^5hwJ+B_pO9< zvJfx%3_l#M`DgfxjD#b-ja6{w2g88o)dk$y5x&0`Oc*(D#KF!Z@#bIK{iLt~Gh!c? zy;IxA5xG}wH7@YzGuL?o8h=&lxQHe48M^XDhFXZ^jyQBi2T*dtU2t`d`+S0B(lpzW zl?giybJML&nGATeJesxh)jBtWFJHT@F@bAM><+T$qW8c^bzG>`Gg!!A3M3}+~2 z;%@hA@wmzt#=4Kc3-I9{RVX3+ObkpW?|+f8O%5cu$0Zi$UrI4Vtw#CKD#1eDPY$Ln zB!-IXEl%eDkT<7RR;8pk8=X~4+QILm0_-5yDbSP}l_)4l$MjFUWvNmArMYW|Ql(58 ziI!eH`1E*@DlmmetIz4^ArUI2l~x+64HY@6Onv-ex6C)n0^wVcgHEnPQMC(d(E9q7`pXDx>Cq(Oh+W1(wZ zMO7MEo9#adYq$v*fyH?^SMoT(h zCCwF@f$g;WP1OaM01@=A8OHRqM*fajc$kf3N_D(rm#Rb=`sb|r@8ls#ys&2OIOsn87|*Ne4CH{uF)U+c6P*K>w_nMlAz$rlSg`G z&(bdbHuo`b6myvFm$*$zG%ul7#WwPW;*k^UUWpCW|8R%9R5m_FoT>Bg8s^ddRluLf zPy{U@j1(IxqEbwx*uL3ygSj7;A)9GheA^Z1&Nod$<>GXgo0ILd459l}#nZJ$hrm{* zJ$fSBr*B9yDVsW1lxz`M%Hcjig%ZlgUCr}7ON}MJK-jLFG{cpjP{XfY@qrYnuV5Ws zva{c+XTS0e6>`h)${Gg)sZ`{?3yT;=##2zPn@P^pVA_xC=NiQ$@)|siipUnhuSdnC3#O zmER9b-_Mp}tY0!MpKv(?2SIuijFn@;zH%H|m?h#>0^5Y*x<6zzCAF?n0f0F#rRiY) zgE`PBE2?Lh#b9*Sg<7|$8kw^Ls7_~=SJwF4d0n%^v$9eUefBS(@!|g@nksyVCNMqE zZ2!JQe{bVzeXA9uBj>!VdLlzL8u6`JO`h-G^7C|(vA?{2@wcY5<7(mUp_4^_y*Cmo zUglYNP-yED0ws|{l6?=38#}FBQBQR7;zt}T{vh3_kV`_%82eXf7^iMw*_k(aizsdB zK?RVMn#h-AuySaL!@f)_AS`Do*G$w8+G@c#HI(6ma9IcpH_*OOb{WxoUSoeKu$d`U zze9HO({hmGH(NxtbQ!NaJqgg~&s(R&8cF@;2IeIs$76rWY~pU4yJX*%3VV>X9<$H# zdwSRPrQ$a@$>wpe1Fq<(I6dyG{Pn}P?1Y!)6u7c_SI1bqVYXGSZXcu;`FQ<%PpCT} zuxq$B&HmA^_MsGJH-g_xhKSnpRr!w@yyE?EqwBTWs9Jhi3~m^spL8^sIF0GOA8^$& zUPH2d6@SSsh?t6`;(Z^EIrnIKtF=89U46dV^-%0hT*<(goONqZ-NB3wr3#UxZnVZw znTwcsnfFY7Kb;ZAdS#g-8PvCu zke?Imb_TZd0zFN}2R~Gly^d@G3&sK8wnA_6PWpFJ$v@yP6cWo9DFt>5L+ov?{rZ9tHvGW!!ceF{4cxTR)r}JSVgze)Lr%y^lhZ8+RFE{4p4~q-O?;w22{^NKp z-uq=+I55U0dpQ+PxLX?4pPf<@0_c6@aT@YJA9@8D`-93Ie6~*QRQ;{MtXwBog}CK?X(rmBp+;gw_Ox@I zY=3g)%pg!~$M3UAA&z8=z~Ha+IF4@ zVao8cAlxOft>;DJJ|~CdjJT7F=`91Rj!BA7*8cp5-eg$dQMgI&NK}3N1_>$H3QHg;u`#-_OcguOGBp6?h~iR{J&V0gNK9c5%dq@D*AO1 zI&Y|WO?B{a$!u`|8Mu)>Eoz-B*1WytN|$}{w!-`HE)6Qqx*%T?^ z;wx}Ahp_V}tt*t=MM2N8G^5K#jVEN6ci}#iV&k{=>>(3LZI7DpFJd^j%3q$Tp#^}O zuUM|2@)t_fqpIKv-l`PPb&78TD81ljRSD-;Ss=JBagFTrIq_cqd#8f*h2&pj6o_XA z@v(yQ)3IxJE}c%i^&0_al7X?$38#3Y5!L}_wlnL3rcRL;(lhz+qCYPpJ~t@e``uZ+ z*te3VI8J>G(;tF>^h`oUw$leu!;{jA-{o{PsI_S@%`ky-q5Uo7THhRuVcg02g6t3k zZ^KIFePO$}>)cNB9oD`&wA{U2SNGEzy3-F)fyWHP&2zY=O2K|5j`sCMNlHNlC1TuT zW_j}%jZgJgsGMr7itnnRavItfgs#N7?y&GV6|P2#ISZG0H~#ERnOD!UrLid+K(@Y= zEJicby?W}`Yu43Fj69%Qh4C;8X=nd>Oud?AVQU{k)&nk=G9C)r;)R)r;`6HOTg%+^ zmEOjAsQRT7*v~zG4L%YwUgl*u^NIkAiPlqfXb+S=n`Dtwl+J<_C1iSk3Rr(P&0f2x zd-L7J_qMz^h3Z^n)wFXTc%7d&ddT(c`sb*zuFCSMm{&hmS=`TpZN}d6{1F>0_?IoF zzyAE^A0H1^@OO`iB$|1c)kWW7skSvgAY_acXRo%G8|cJU)!a7jU`!>`OVnMWO^<@k z6@Yh%WXbdeJb*~8TvUgUlZ12Lmtk3AyNpA3zYsGtI(qorXv%IrWZ06Um)?7;lOcv0 ziLW=a)t`dHjX`=N>i}0}TO;ToX?*0x)Z{1NeFi01@D_;r0=q@?NPPOV90yfkH#PA+ zq{45swU6ojE}dGe%-1;dCuO~Uf08*qt&l5o53o;SfB^*}5-)3t9g{UxMB|*NJZ@=$ zgc0kGq$k%t!NF`(2w#m=n}3uct4WNDuY4PxB4YFSCxZqrvu6jjC-d1Y<-93wH{!A_ z93mRt=yVz_cce2Fzp zeH^qXSD)no}kk1!|5mKc)B6c){XV)N4hva6<&JR|{ZR9>fs| zikf|uI2%)}{cw5EBu`il(`5<7w@i`A$4!zdW0uzZ0ioi6=qR>#9$27~?@2kBX4&~? z2->R0gwIjBH(nZ(HH`9Uf_vM4EMhTNnjvHidiVs9hl${iwWwI^^xW9>_D-=ag2)CM zIe|#JGwua8*TLadUX?ci+aI34XM4p&kYl@%FN5zvG@Oae1@S^GLxy8U(1k)VsRcin zH%hp$%W(;;0MsgONvd@sUj8`++ulF1WHl#McnAK2CEVelx8NvJ94=Ux2ZSdZs z7`)Cnr<^xQ|C4HY_WWsOPM!N)%?DJKYd1&v;oZyd5Jj~q)EQl+^P@g2J=d%5omyAS zJHj4ev%VjR>kBXF;5}?^?7teRALOnj7)pOq>vmjTJv+;wBuwGR%C;NB$_a(xlM7=6-Sw zSWjL1c|f!tVWHdW)`A&%weVIMxP#bx@Vcs&)aTsCc!n1EzsOPlDcL~LiM`tflO0_z z@!WGAbvDlNZ+0_H_Vt}NntI$8-A=bo{S6GzD(E@9^DsS0@K0?7y3tlE?2kn?+DVmE z71hOO2!VPr1nQp&Z%kp9PP=5>I3~DxoK2Fn6{V=n1ci&TV#ZIO$t^Q8GxygqK2x7~ zeaWa}CD^yJn$=huzuTBGfu#nQ$amVPjaC|;BbgePV%KuLh^;pz4m6fWM7qXc9FKYg z&P{q`X-8%XFI`8NUV1c#wi(}Vn$X;pSz-L@$CHtblliu?fo;uYk09ovq#5P731zz+ zK)6Eh9fOUtcXA0rjw_K%j(TMXcbnEL`Ut~I)hy`2+DA7A&^rjfk_locJF{ z;}5_jQ}m@L0m#*Yo+z8^W?qS~Y73o@lFXSBvX=Eze>wpX+H$f4t(q3F#Y2AeW+jdf?Ht4MsD z1=pY?U$tP_knWYMvspVluikII@M)i=zG0V;I*{L|vz8+sd2v#qsuP&HZka~&b?SchgfH$<4z5H>RT2&R@#yk@v*@1PD}p6 z61OYt?YfsJjh>6SN5S8wzdtxNSm-`112gkVDKoYp%SiIoqHWC2-=376f`mIbi(H znRl+xK7Nv55mhymdaFSxPI5NFC7wz)GFMc0XntbD)1$e&E^F<6OYOwS;#=a&`JAES z9-c`;@C+CVqNF2Rbw}g%T-DKpDKuv6VRO!%ts^QEc%ys3%XfudF>bWb{(*lpvA{YL z#-J@Na*y=qw6i+lJ_9d-UzZcFuxN7#%wocgqh$j!)tK*Y5RRbhCiWxP9fY#oA_eTD zYSN4Xv|+MuJAI|?V16=FwpV#Mqf62;>MtNl9T?fV3cY6W21w&Q%Q@y3n;&~=Ifvyi zUp|QVW`DCwZsX6L#+B!nRL{F+f%2b{29xJq`D*r~hU51$)=uyZq}5?(G<4j_j`v;| z5!+V=bF4Rg_EKjTbXUWd3He{D;^ zTWm;cW*AF3_dk_Nx8MI&ds?P|OuK}hVG8_j7p(7?9xFM<9VR_AytA)J&mb~0x|If( zb5g3Y214x?)}Yt(hp!xng#yq^-TEU9>l0LcRXhpbXhJL!&eeZt9=7$X@NV!&J1?m) zB{6RVY~-?cOCEp1GIf%{8QgaME~f=F<-p^xpCi$jk>-^C^GC;}+tSe3nM1lrYp2;; z<`Y@|vuoBD+c=>Y1iA0Ok$)CrQ$yGWE_ir-={f`QOjJuy1Sk8*+Kui)Q6Fl)u0V#V zkT4{%DKOtjqG2tVPSd#Xlx*Qy_jZQ!gY4H+3|t`04qHjK8n^_Ep^&6{+)ZZ>LNY7Ks!YC(jlKhLo7C7VF)6X$C*xDIi#{ zL9;!qc7r<0GwA&k?_*0{xJFVyGApxPV(lK%ssy!1cmd23#6grGuF*eEJ-|Du_%kJ< zqK*6alo+25)>8yNy|eU_snzc@NLQN51ZoaV!AgjU-6f_9eyqp{G&~C`_yV$kM1PJ; z&$B-Z`@Y=+RK(3T%?B`wW~PE=LqPHwR}}h=?(bty8zT5D;Kp#BUFYPvohR05N)&hN zu)36P&o{eQX=xO^dA|B$K`W6X>&-xm>R>U6*ykowax(0=uI$!u9FMM>QuW~uAe>_9 zk>bCboRtJ&sO-l)_W1dc_d&j_j_4m9hjYL8vmc7~fRnCtxMSHh1%a}oo zarjO8pDgY7oH=g8Jag%k5aUB}TS4AP7#U{2dQQ5rP9XYCSJH?F2;&h+NW8j($b+`4 zsF&mUbIjJ*S>?h%{k4Jn| zkvy@LSn^!^Y8fu+q_a)Etk1z&>A6_jpNn{!sjI(z`|Gj42qi-pNEHrzOv#T3elHpW zsuPtOt-iGh>oBDglXCk$`@eCye&_O9=B;nY&9XrLrw~si$hq8OsyP@7nd?hE!!sv< zn$%+JOdodyOFx#v*yRiFIGb4j{o!b=72So;083O(MK@D6r6-n- zG6b5h)df~8o9YT^<}f)h$9??JD~7#gWwj0rh>$bR78D_f(Z7&qAd{4hajhh#)yf*;wHfGXD6OZi@bT(uqI8(3|g zhgoBr2ctM0JZB3kN;~@YB^rmm-aef;cxD@^{1tAKlW+<*guXOOA*Y>k=(&iEQj9bc z_fMH`MdpK?WG6(upJM~cTZ*&sJ;*-^bPq#c1cE6W$J*KheyzpwfCdV9N(Sv78s)9EPLb0g{$ zk}{_k$ozrwUzCWrK&({6u3?(u%*Cgff5wV1i0rHS<@s~-u=V5kOum6(M`#LM6q|Hv zl0-h~<9=`$zwv))`VL2`|L^~cB$bp|gfhzNA}i~rWRvVY-ehIPHNw3}L&)BQvNErI zxn|yG))ndEa#i-Y_P)mNdEcM!?+-|?`+7Z}k8{rBobz}{Q+I>@>I;Otq0tWa_{{{{ z|7{R`mI51Hy~zTYz3#&5m@qf@_t<93E?#w<;C~4s*`#fS2XEWb4_qABOvXMjHoBJU z&{{KKD@Vzf`>|v-F;$UUyu0_!hmf}o!p^$RSMT@UF>c>~ zhl%cRNl7_v(7cGhsTuoP9bk&t7_V!IjXCp-r@9lC-F(#U-o?C#ETwo<+|IrrCl};n zBbXNA*GptI9<|=mZmXPm{V3^>m#hR+8ltQidIvlKcRjCh3iXxBWCe`W4R3)k^LLwK zNUdRusC}9cZD8;SxEMntyNGeBcA&t-MtgKlRxFFriJ^_=1+&oA9=uhu)|yvYtfRN{ z{Zyd+C6RiVuZE?9puu{^x-PHp{n~-rsCG$<^)x?MvOFral48OI*fW`wTb~CZZF3D^ zi*8xX3osUv8je8j9jdbARq~@N6fM%4D~|i}eF@bJL%g}2eXIQ|kVc1JsRPSu3BYGO z3a*WU23+$qT`@-k8dEGF@PMtA;K)lP_ci#dzSQ&@dbkEI_Py+9s8M#cp0|VEE^+A! zRisJ)vO=|6{!6}Kd4%zSgqUVrEfS2o=?qEa*7P#Bx`Im4(An-0t5|PagYUVRAN3W* zgG+|KVl;O^8%fYGInUBJdcYS?it(uCc%!4#k=on4A5Hp@NB>ho3KBc~bbeu%AZV?A+9JTp_t4w*rrwWY4$rp+`NqESlGFR=BcHH* zF9qbf{1Ep0J){P((w~(HCVJ~s1Fk?SDxpiIlW*wstEP>0W1krUeP5L@Ln93L)kok) zKD&|8ave4Ia=YHhpwi9DY2<3-aCRD||AV|2z!uN=1>8Z&(@MYgytJ<+09m)7+bA8IMY+8L;bz z{B~LD4MvHem7~sV?`Aauww$4*(jg$E43rc})u8FvhZI9EOm*kWrg+F$q-@s9<9Fj> ztFLq*7j`2EXEcdpfMC`bVvDqkc-vpl;}!S|`PfT4b?%!Hz+$s{qgb|Sj|BO(#+!Y+ zGT=KN4OWe<_Q?e`f=Em@ZvUzc<{8}V2UfdMX)n;8f9$A)*wR6)n|8w~4FM(s6+f)c zc}g^Cd2w9VNbdo!)8XgTUC>U^2kZOY7I&Dl0av~VJtI>GQ?7H$^0*lz2 zFHO)j^kb?^lvlNifyTWYZs(S0riE-Gi7gwsVd*tzKHBS(txYY2x4PSY0*^n<>OP>j z)gc3vc_PN{dK?{7}kDr-Z>ab@yBsqhmC&inxj^N+Jzr}B8P^d z2N^fQn@}^RxVRY@ZpP96JwN|4y;iBX*MPNyK_^s ztNP!4@iSEpH*EQyHwwcA@$>Aw^~n>|cYu~^jsVG}*)6nwWmgGhr@tv!(rZY78-C$; z!aN#w?F5jh>jHo(004Twd}5~cetbz25sJN$9t@bs|Gtk3vta}f!Z9?g70Q19!JfH1 zBCEWx$vw^53n2x%OShInCmjSVtp6%$4kc&mcPf-z5niMUbp}|@54Z{-J0JyY)&(yK zFT2B>5{9y*3o_$jW}62P8w2ZMKJ^kn<;o$tQ*R5H4JS!P1N{f7{hFyPT&IEqLlf}< zt(IswxYw6b^s4M1Juc-cK+{g=&JZ71@Sr!vPU7>3l2h!EJc-2zxj-9)AIG{L08e zYLKBu3P%SFpL(gn_9Q$xE{Ot3_N)Dp7kWT`+q?>jm}fqRgr;qa6bm}W0Y85N7X-1B zxVJZUb9^4Sse1&cRoQ&wmlxes7(OH(+sfjS_O0vyWPoMt*f%5aBD==VQJ`sw z31D-;p>bU#0=z*ekZ0Q!(e)2fwE%N}Zqx5rUyig4&6hp_ncipQ( z4G2LQ(My#rBIteeR8VssW^q~)(&Nb63UZy>!&kU zgBqfNLX`g&dLfAkwdHrc?K~QIjA>oQ%_E@U_%Aa8>?P}7BDhNAv*9p4w{6g!-0)Nx zZkUlVt!5Cjjz@+9qnZvU78AodU!2Xw)F2=ld2Y&y}3YhDGwKSuT-<~6~wUtZ9zE8mx*kiUWv+s7nDS`JvNQxjsO=PmJy$?&d zmlhC=)=aa)ZO*$*^DW74_E9S~N2@IhAy^*FYkC=0)5Gj4?g?DJ1=x2RF~@TjF(NL{ zO%gNyNszZPC~uBXdGBxS$2%Z2uumqf0rALdhc9tmToCX3ox2P)Cc81`l@cd9Yi=JX zGNCL7EZ>AwA-~LUo>)i_t}~Y-P6J*g4I^So1P?eNidpb@XkUwxe<{#%{oF7zt`}Hk*44aR{^%_f-IsHtbz0@tr zq8u?FUdzC_hmgX)VzAY;tq{?%IHHS2e)6n;?*f*&7ivIHdE~Z&3riPY5m&0jIj)y1 zW>ZC_s_%7!Ze48*P3(Jx^ir|?=p2@(^Wn^t;^)ezJo`gqwW{~Fzwu4NU%cY?nM+CkLn;ecmL?}~icxn#+l>Wyx+>iA(T3YR{ud1q3u4b@b zD0yxTI-J+%kdax_yE5A-^*4{SfQ;dTN&&FPgjfqaDJ651Dfpni!lc4oTH}qBMcqAyB(baeD z^8@=3io>c3UG$Ka2GCL;_=qG=V;v@b9!R$@Y;5xQfgXDQ9@s)ymeXrdhSHzA<${D?(-eT2o6>_O?=V8{Y75G+%r@z-&ytYdFC`#h0~1_Qinco+a@A7Z z5lE{jK{;BKW_K(2(`Bw?a%>a{0}IQ#hzOJ03lMFPTQJ#k^mGGB>bt7rdsXMQO`QtwWj!smYQ@SiLtR%#Nj)M3skK`npvbulzvWTCh~{s9`Df_&z_ z^clT|{@uw@U&%eH0lQ z6!}xWWH2M32l$mY?r+PPthKsD z5vF%ytpO|f2fv#I#d6xo7*8GYDvrEreRMk`<-{bIqAK&xOKqpRrI|lRO0XS*vfWYP zFytI$`F{U??4*za^IRPe9+Z-RZ2r;Ld*Bi1G<}Vd(G^IeR~P|gF4xVv7#%K&iiP(w zbR*aP$Q2OdQH;X=_3)yJ0mF*ew5omLGSsB$VCR*L0|kl@~W#%`c=d&V~)cs z&}yd2gf2@*v)-Ke{hAqvpvg`PecA@Y15pNa{UmOWkQoo(>e~57eH%Lc0CJ(`&QdAi4F14@7Bt~1 zOPmf9H^FJRE+YV2xSDEH!gCN(TSd4&Lqbq8W`FM74FDn-N^+Ta!r2Ru*rB|R0XTk0 zh?nX{W9A(UfJ)jz7TWea$jRWDQ@6&x)ub)`PjY)i#6#s@--25n<~^@0y4XmZrt`d@ zNiI&QnD#nvhAdRYcG`pj4IF|@r32SibZzG5hX2#)N(%G;Q;&RB98EqYH>OikZdr8- zzayG#H)P|36mkKD6?U_m3UZr;EYxjwBxu+Oj02ZkdLjdpG z=@rCNN(L<;JIYjC92R!tkgPc{`dY)(7>*y0|KTVgCxr#0E5^r75YZRf&f!zqn7d(( z$A~@CXnW!EnTaPAiwHmiUa!8>GRdHE3S-`gB&m?Cpemv z@n=L#;a9x{+9P2{^DpbAkfY20^<%D0EDlQEmQ$f642g9$yw`!!4Edhp>S3OcXOBJNy6JmCFP5!hr*SKAFT{W&d|?iC4JcnP zj{ZoP%R`h=YquLLsA-B!xhS8YlFg@YY6J#XBbEl@%0Z9Cbx7yv+C`B7w4j9Plk zjuynmZr+Y-q~GJjXvx#i03(>u_R`dAj_q=%$zv8F$hK+pc`cwDJ$ql~Y~C%>r?m6qPL`>uOS#DA*GKDdcKGmd`pOH0{_Y%d*p zv4(kFp=VaV^E+Uxb*ubF?KoQNz=YZ~pOG>?tXbh8`>LX)teoGAd7yEay^`+Ia@>Mc zUa&`S*rnwN}@)ayAJcP?Lq;lIsnmy`?vNn*tUvz84Xwk zSui^hyvDCBk-b|l4LN97zGQu%yU@kXcbN1u%FsvS#zMvg94)4`&v$y|ofT{9c`{_x zNPM5`Yyr4y%Eyr5#QUJ-1n@wB4!a6j_8NG2^7V|c`)SCE73ghXpt})2>e?S<&1}1( zN4r9t$?PWBSew^pnv1N5DUEL`i_0rp$DHbI2?!jEfptp|JMck&Sfw0-7yPo=r;I?| zlT!|+f8faMZREOX&9RX8A=B#%1t5C(1d@oFud9I6`F~w@D=>GPF2* z(|G5mp~REpSYWhvjHUSu%^WwiJV`1)F533LNV;JHRJB&%A8*7Ar66WrHTBh(^kz_? zFA{>y2K|!%-90GPYclP|@62wfJFP$9`D6dF4?h>}vc zAki-A(f;*MmSzrSrM7P{Jnr)vJJX!o+2`R?PRO$idZb#0-{fXWWOG(c#b=;#G%zq= z2@Sk42()vAuJ@289Mg=(Jz+E6&B?&aUTVnb|60}n&U4z-M$cM8f$j`6vQN#4Ze_2n_aOB}9mr<}raJp;_cFl9jyD_KN zN`MbGF!SO~^=+cL9Z|*5>@QATDWo1{J?&dsxgKZ#l-#qc#Vi#hh}TVSy>*z*b~a*p zP!AkmGHGAZUbYYfYe8XQ^UZG)Z=9b1`t@L(>x;;;6s}VJD%i=%s%u>(nD%wMCVLE! zZ^xwr7-dLFnl-65%mKEYCf!w+()^N?*qk&|`Q?xowqK*rQVe;%V-k7|RCubI@1SW_ z1K1B@1vxWr8T4IQ&YlD6<9C)A zZrO|`GH7tf#m2;dSk{DzTLe4vvDefFjm*{6JW88I(jK6u<7eDE3VCIo{n zxU7(#BcK^F6({3M3Hr9%uD}<}HH-OM;5EdpfY4V2-xStY_$OOKl?}XKSq)bO8@mVM z(Q;XrAOr^oiS7Nf)2u;oG{tcGV8lDwQfLKk8Fi^|Fy`9jaZbnWX6gctHq}exv;1Q% z2i?WOa|s#$nCH}yXUVY&4Z$kIBFYWY5R=12Rmts65kg5x`JODYNvr-MEG0ZGmV@+e zs$gAh5gs3VrHQpoHS$nur`nC}fvo;-*~n`%<5j2WhKbLzq@Usj*Jog*HJDqFpE)DC z3yj&XeCD}Ma!H!>1BVkwMmMr8Oa2#W{O6D*R$j`FWuATfoojCq8d9849Y%7E?^0U3 z=F-$|(`BEQ3h0Gt%oH2dI!U@JN5q>gw)X^6!AgZ z(yi-W5iUiR87}o%T-!&e(R^E9jTnDn(Wncs-yM>o0+$??O@izjo1OM@W?q$jVSfT7 z6T!Cw+CttzGt!)%FzjZ%VCd@~#LDyw4f4TOgfODkJFLkCl_B7ZaL<EWGT!qJ22N_631uUnn-!?;hpF&)3^%f>5QncSIQ= z(Z|M>ffZH;a5!In+%0k&QafI&DSmdqHN6sGW<_r2x}sS*R`h&|i%pXOjcrJM*ISYX z2*TdG3Rn5O8UT$VJ?^rR>&h9wlu?dMu^5O1Gjz+6)q(rXnK8(IOkLRX=@sr6m(k)X z_~Uiwz-X7@dQE2N^d{9vG&pdG^rOzK8x`XV!yb9AK(IFf*av+vRp-Kx$HwgO3j4=2 z#K|ROiyPz**kImqgqNabfg$gWXIGsd>k|Z`ud`A}_V!Z9T6)Gmf$LRBphMvr`08!6 z!1@8b`cdJ5J=JFz{;cic1AiiL^%2KEJo+(g$I8M2#FUO6EuB<1idogWB^!;m%q|Sv zuXn{ZBxBd9vKNE{Sw6e{Z@FA-&Q;I=8=**Env99>l=D7Xq*jYrpkR5%Iw4hgy7 zH_G8RDF;89%^C2q_)`!(p_HV zKNi*!Kcz66Sdx1fXLb~YnvOW=00EB89z?9#jap25{kJ&Gx&tE}zs8zp}lW=-s@~4Efd_4FI(pjXWChIQV4#U#RaxS=KZQUz>FNo-}az z*{aP2C#ujGmdG@8-A>?8-FMLN@oVWtFjB5z3O5mh417xZ^a$S3;q|`RtK3s2% z3G4Hc;30D#GK5Qz$F7Zk&Jz!g7qVAZy-3xn%$AMS(UQ@)La+Yz&F35|j3#69tt$s1 zQnwC5c+XhZ#_R)-T^W*^x<2;2aQzo7rSjn^b{@y^U%1#K9nlA_vG03l2G@ zr+#j?y#L#3L0@kAvtHEqAzg;UYxLng6!5)M{|6!v8Y3{LXZ!2xj@xR;o%L7vyshGl z%M8h?`&fb-X%?u+n-EczPSw(i78 zQ%k}1v8J?8lcb6WRMIZ4)}&dpw@YH#3=CXTd}hgvMo)QUB^Aj4>7-Uscq+;>sFLBtg(QjH#{2`Zb<~KNb zb{6=q82~Faa9uSoQoUkr>t^!0Cng7mw#>fRu}xX~fwf%F{B~~4v;N@i0jUt%{zGs5 z>0~SA?EWkps#$Pr&#)QLntVat^W45!;VY@MDj~q2iqOp`dIQi#jVjD`{SAJ)^f6Tn z+q?hEEm_3HCr-z)u(}g?w)sx;TQ^g@;P!3XS)KZpSrTtCDVknU3cSmiEn7=voJYpf zS&$ss3iMfen!==x5xK+TU<5?TvHObUNnq5N?1H}klERd}V-qXbixj`>Le9}(J zrjH2yJ~(RYudAIc5kQIdIC{vGQQQl7MokIIwlqdFI$Z(}^Vi%6x628bB3EqPIG4Ur zm~V>XirP{A|A-bR2Nk*26JoG+XTD{M#JiS?Mov~BzcM<_3YohAD=;ucz0-DA1B^t~ z;0-Q6U5}XJ5_lA0p*?=S^Xv&^x%y;2L&thI=FF3?Ss?;A$!bl2&56K?=mb9FkcWhf z4Y<9`6=lo~WgM6oy-&zd>~%0|_xnDo%8L~JyA4rH8xLGNJahJy>aBe&q>KUP$fMT+ zu%5YXv2Nwpk}W7kxernqP(Yi6gjn~uh#oikx7ejg8FSlyzuC;WrCX9543%XYeVzB# z8qb=l?NpClQV^WeDLx3omcg{xK^9{*H%m3UBI5rmeEx-7dfAVhd%3eB7<4evk7fd< z5t(k4bT3r8Yc!X@5NV<20xpPHIl59FZ0F`IuqeB|y$#mQ8;@I77N(3hQ#8gc?uy7v z?j7fhyW=+THx~dwGx6eoZTjEH2#u-qc)Q-cqUV7( z#_C7ZF`gi!!Xnp3)6VuFE3gbZ?N%^Qa}Z|6voj+>=#nZvSSy zRo{|F0>4qRAm?eV)F`H_+o0xciIYVu|7jS-v+T6P!!|?fem>7Q{zZX?zoJt98uBV@q8gd z-2w|}6ut#jp1s@Ng3yeOu{UFF&LeGZw$srg%og9XtFYgfFn3aV-#~lG-3g7RzVWY9 zor?STi;)_BedV}P+o`IpCbc_BS`JT2nJc+@N|s3!5~UFhos`Mmey3+fw2_|PyBX79UeVcdqcPTjvMyqW+QLZ&SEVDq7N7m?&uPJh4sgi z*p71*?F&WPgT5EzS7_FLtxpvwsDJ;i^2;$igPWyJVQqY*MQqie8Wc=Bz2CrBu@sk0 zCn2knKl?Pl@7sjt3ee?ecqJ{sDHo$>?Gl!h!M21R8Wq{K{ajpdR#w~fYu<}TSSp)9 zwu1>=i}lZD{KE>zr9W}=$EE6_n(#JxAzO+MDqEG|n4k~7+6)$FsScT5qb<`ot}O|5 zQ_`NgWME(gfl9I500;~wZTxPVvzo^3t)m8}ou)7KZUYY<2Qk_3^RaZ_yYDO<{h4sg zFOm;A0rfipCJtO!RnH2@dPQ-YnUD?qg_Dt>3sRveldf73} zi`ggWA@mBiIzcW(F3HHq=zZiB$~9M&i?aLQZOuurl!t|$`##zZNB{I1DhR;u+gQxduNV+QJann6c!eC^7Pz9KeEI>;aT&jS($dn6&X&U9S`sKb^i^3 zxF;tkcU?amQ;9ED!jum51nzL7m^?%ZET~_g_ER^GKS8cK+bFi_>onVRbzQ_X$Gi{H zB_XWv^Cn!m0y{^Ltw&px>d(`b6X&fwrvrRWSMUED%hWi4bgSzwyP@9D6_T!xB2O(E zhqi}JAc>mSUGQeNSFawL^vUj%@pJ)fp>9%* z<}nvd2tn-b(@*qkjq8$WgrG?u0Y{fW1?y2Typq$IDD>F`(K7LNYmFFZY=NeYz`mLx z+|03Mi|(e@I_o0hMJ?|IRhGo$i&i^JTg_Q7HRF z0g^{PcJ-y672~+5@CPE!ZfX_`Hta4F(v(1BQ~hCB{FS_qRFF^9ulp_inwcm4rm>M% zI%_9v>b5Pv-aT}+aN`B4>52&hG#%#DtcFpx{eh~`x$)Md;MUtyYAh*x?y4b+8^#^ z!qi$^O^<@6Rv_Q5H7MxJ4MoM_GOuRiQnX?~$0rdt(C|U7m`imS>-<-TJy-tbrEhfK zRk^B(Qk=B|-AK7Q{G^ocv}mMiISsdABsRJiAF|{cB#Cmhmq*tuH}7NXnP~ zPIElfc=JYC^>1bj{UWT75HAWjc{6LrsD|%upZkH1$jTzdO%82 zjb+M{u-ROCKIC?`pGUL=9o2}|$kwhZfsf!W7NwESK!&fU8#^N+PS5ieMR_rjxN4b) zM5HCyj=3K%Sw#8!{cJ84BV6x(;$jYr$?hkBT?S5WDST3erN(~OP^<(`fdvJ4_KfM{ z?Hs$spQq9iYmqsnneDlIoqV)(hng2$amZL#^6yv_cP=)w$EXfVz zu{BD(T#$h7PqMpDU+pZ)f$(F`h3siSw}UVijUDb7Qay$=2pN`)cILbZQLvtSncJeQ zF)}LuVuyQ878TrsOk!G_gcUh>&=Wu$dn?9v2ttWmlV5p%>=cgk(xppQN`<2@8YJWq z_sWZurFb}6N;Ky(cOV;fPEL!0JX}a&=byJ-7_R!o)WZ=9kuHE`(FaD|X2bQ}U?J{7 zTfbKG@i_K$jA_zDbWJIU#QMY^MN`R<=F~Z3XO;*oNFk?jh#jCd5i&>`kvS!@wChV5E0N_} zwHFAE!<6LvsXdr~8Kuc5%(dC!rZaLdw9xbH^SpNntqztBqORdL#_%jxf; zbKbS&DW7EtJR_#|Ix1HHWjU@c6++-XTx2x;D$8eC4Q~WWdIDnVBe$pKThRFGL?%?B z%(AZ6M=r>);KQw+zWiL(>3g!#{8Oid243ctUV=*5de6i+1MwE9q#r*VnRX zDZihOeHb4DOF@5hU%=|Rr~$lTbNQwBt=XiM6FkQcuI6_Xt~B|9>&TMh9-iRx&u0YR z>N_`4JhaXQTKPzpVp9r0gGoa%Z>F>5l(6szHxv6G*@++i_8eG ze@gIF0fJJzYeEF%3n)nQtuE}ZAse@56GT`zRHgk5GJ7XP@{obaZ$q9_F6w|6*x{G( z2=AS7+kVL>sMZBZ2AC+eO+W^2tyu?ZuG>><=A0(&>3eFAks`M zxTGia?OGPt<_pwn{-vA+r_082M-Sdsl3$M=0g zc%PYjB4(z&t)!YB?7 zd8xGZqemiF(1Gm_&#_+F z0Rh3+J%PQ3zf|5r0wQN~Z$ZP~tj9&V<7(2p=I&pd*{)qY9+ymWE|mEz#v{AvC0s(NWVb2_)t?*)9xdJso$3v>f*PRTz|@@?m6xni6;;V z&g^^zme#|uHJblr)R!ElTT13zNI@u=hwv*Qt8SutH|A+sFDO^t{4F8P;M>^wBkH3k z9IBnB_7jCtmh_wxZln~M8A>vOgot7>>9>&eVRTwXqn`IoPgj!FbC6WmHN&j~Td!F+ zD_BJH+@J^mryEi&s1U0}W(Yl!xacCt~BSgKO#Hvk3pw z=wP=Poxo4n`z?@wH?z5l&MFrXF`s(2n(}IIWmft`>x~&TZJpd?j6WVsJ|E&V2OLl_ zz$1wDqEt8D<(KnQG!Hw}a$uw*zGf^FII8UW9vKG zSW|ev6@;lx%CuM|6cW)MSBPSCniE6;DTo@w+C=`=8~QBV9Z>u9p=Qg7Q6FZu*y7X$ zPcGdeW9>KeMAFL7uY<@ITc4;poc8(r`Ll|MkDLqTV)ISMIHdJ##5qck!2~`}%Qd22L7lL3`2p915Q6BNH-gzR+b=+2h+^T0 z)Sr~OnUNUVVL0K{Y-EMKcfq(%8m`eF@(KuSH^i{xLK za0ClPO=z-l-*#?wJVjd5gI=Je$$gk+x5nNB@ADM1B^mmN75=$34#6Z$>`|$6YY|c& zC-A%&L}3FPJD&3rnShAp&IhrJ^c(@zi1~grH~_U%jfvg?uFN^xc`v;n#7YQ~zh9(K3X2FVI_KLB;HoQs( zeXVkej|CUU%92NQ@*3n;^&erpyL%gw#P3oCd*{C3o42!kM+bt`|Do8YuArlStr2$R zB*z=8kP>-?ucRr!^*}JN&p^<|1!P3Gr@6YSjUqJIOnbx&@<1*bZZL`53k(47&9>N( z4so(FEe4Xec^wYXCZ-SE) zab|5`R}cHSx(SYq=*A>`n%-}fU-o0at11^FXmP)CNS;_t!GGnuD*d5g3BF~j*7}2O zH1!K*a(^CJBokW|Xa%pMQC5oN%-&Ld8 zXdkj#J`#Kptf$nGSC81c>3Kt8{5CoJ&D{9*T66SkRzUOOgClK&B1>zq&LHedLw;VE zeWO#;ZeY5AaSOxi!WM3gu{EB8z3aZkGDrVC?rQb4vH$kd9W9OX4Nvp~^x1nE#DN<%e#Y&m&m;d|4zHoz z_vbE-S)jYE2VlP&h20j^9!>fn_BVTEqk8?)2+szh=asUuu7Q6Htk!W3UR8^t_u*;x zlXgM+nJ6^9U!xwNrV?_hxD`i*yJ+aSz&q8wYlhZGS)gWCKJC>Cf!Lm!m;TC*Vu;uc#X>Z5)VS4bN+G>yjyah{S zmLGvVZv`>YTkx&flJqQ4(TX6XuenbDYAL_nX;jnerGw+V1&L7JisMOval+Pb_;96w zRo~80x4`5)`zHfn7G&sBTO7|iHF#y>`cuc&{?9pzDirSa9rDtB`vu|!k1lh$Y>X$z zJvCC843wg>XfVm4*D!w2Q9a^2$P51qVz!`?&+eb{^Y|`c?7ur!fjsll4nXX1Pz(i< zq0;9>^{3kFJ}1?QRN4J;EldPSMw3z>lyB~f2de2y7!u4-jEafz*FjhaF_DP!Re92#a(8dI;1DvwVA&f88(=YkQ+I)wS2uFakE|a9AV{vt19rekK$6SRD;P^n)dy!?r?SKHMG#V zn(-q^B0p%PgMB~!I);yjPW_E75We9QQ*m%#oBBP8VU4_!Uy<_dpTIij8X~RcuQ=M2 zB@13&#WMgqrghg|ON#(Zg<04U+%0($myf4KaYOWtXO?C8nb#>RBZTz?wT8}PJ&mv5HofY z63}+@!G`-m503{Ehw4Ar^$LYzuYb~c+k9r<2^fEySKRtRUm#KxFZla?`6LrD#+S$-CX;W&1V{{&Q22;-?q@ zh2z$|+RP|-9wkqJ)yTE`)YxakodT)xF%#;6>cnTH^x_#{+A6Bt8&mCe4Jh9rMfN8b|h)L*J&x# zCk|@u3{_`)d;9vuTWE2xLk(R~m5srQ8{vvfoC*Br`363aahfMJtw;y)=aNWrLBXd% zbAWVgs%1vo0nP8;QQw0?qwnxB`9pv2fjW8YCYfSHui<1AVnO1VADXy+u1!j}qHJ(Z z9Gw@a>Xx1Sq~P*r<6=PX@4w-YOOA}@p*Ll5Yt#z&QfK(9qJl`AqaNY{N6;(H{W<Q2%w(P7v$R%z&T=*OR9jTa$eX_zx%R;g&9wz`)Nz9N z(kF>-ibZ-}E`d1(jiH7xBENFF@{Dsf*1_7Kidz}?Iwmms8X$v3=PoLWyj6{9F$V6i ztb&`ycf(gEqD#L&w&FvOLr@qE3Lq9^X#K+iROBzq0*l(1fH4Ao&&O*DCh%XpUv_** z%6;j+rt!mb_Im-h;jh{o=x#o341h1l$-CM1gqpdRa>4tfcpia*dXt2|+QqM!SzRpd zGv?!?AsssHLQ};#21q55TsYwd9k$dM@YGQxY{xLKp{rJ*qMP*(2TNWGKw0BzVf5zN_o$OV}lxJ+My z*qX(*D+ESd$@jyq{yA1jahpPA&Re08s%i!y10Y$Yu@ExJzah{QcrTm?*io@}@p}WuFL5Z%q`TS2alLanN=rqhxg4JOnYHmUJ!bWD6}y`lXg-K@&PRduiOo?*3ImJp zTs-0miKuV`(q2^_Qoxo5;@HKXnh{v%wYQ2_4$~XcwAqA{r0`XBFco>Vry*r;X4_xK z=v6tRvZb=}kp5a2jge`dWdb}0$oLUJjZq9Ety)(OO=|y4EU70wFC+8DHgF!0pbZ?w z&x_h&-oA{_+R3+Lx1bL5+M*|lK`S`{q;)`G&Uu9^4>G} zI!#j{8H;$%Bt6$_U&Fm_#ZflxrfL&06m-^{Lv@D??{B6Ly6PgYl_#;H(Bbt_L1(4^HO-xH}~-C{T`l0_gDa9$!YSMiS8%x2$((MzYGOmGCLA zcwvP~(YVWL1ADp($Zhc8cdj06@xGd&&Z(|Q66@N!i4V;4houDa z-jnb%)D=G*v%1#;Vak_(c(ykDBiDlfK+bo-tZZJgi#n#V_OJPM&Xzm&BIYh~Re!vq zYAPX%!AeR>#y1Ytes5<4TQAf@o%YAO+a;}V@&Ag)pND_A1ZgOOogMoPY`pyOkLyKK zO=3aja?;!b2%uQo9}P>NO`ZBIMgC*EXCGA!w|3+na_h)99xb0(7Z)N!OO%c6pJ+#4;nh1suc#l#Q>fGb^^IG zH~d9uo&R^)vtAe=r?~ihC~7uXrB(F&C-Z>8jqBM^1JtRmXmIXG6}Hlxi`FSIox2DL z__Ec(C_*jg%fz8F{e9wzsKA#Vvll5H=f)=0U^nZHay1fC4?fx2!=GAkGuJ6RO_zSJ zIGjl46>fR_c}->9dsnB$;e6e?V0GRW+B1r}8fvgEf7hLk_WSvY+E~Psp6Wzbl2pe0 zo2lr5?L*hKgo1Ll$=xtM?TE8S;P-B&iHoE}b0W>($Dq}R6DOkN9`<=2pRrUNZkl34 z`_x{~`t5K#KjCwBQ{nj|i;A4A-eV3w!rTe}P7eb5WxArRgPAkcSHBg1d|`sFOnh_) z{6UiK6ir2QV#F1jYBl{wy>qh8@o9OyK%mkcBttOP))c@jo!9#WI9Cn7=@a+EgH7p( z=v?c1l+3F9-GP7MI3fgnMFrN-V+XF6M6r8jr`*iq9BAwne3%+k&p^;=Ow4T_LT>3@ zFP3AUTdm)rer6eGNpdi2YPGTTCjXm z^+dK`;3KkZpFwt1>K(C-fb6w~%0>tM(VG)=v5mt$_JL$v`mTD}n)cBo_e*lmVI&Iy z%O=(okt2&(5-(BxnjED5eo}-6NS&*Pk^7cV+2o+u8h0LT`k0!1nhInrg7+o4d>+{4 z1b!*1|NLc1B1Pa#y6~JMZ_`ImkXs%Oh$gEOFzfv5@-xD}Ms=+#QjzT=AKg24Id1Dx zps&QoTrq{M;yemy8d$Lu)M3S<>_$_`aRBtd$gsQ+u0*xjN+SVQQopR_SmQ5P4we?>Mo+L zs#3$kYFOYC`fVPxTJ97*vi>z(*PpX zc6wsELLR%2PNt5`*}to*NacID)!m)q(bke84JTgNtGpiE8sguD^pyUrTa|cg9)A9sa!c?a?X)LTBylAN+V#eGE+!~`b()V`cA+gUlK$kgjHFbjp04Pp60UOnrmsqZpp- z?)A(xIHRe?WXW_~yE09sX`Z@NLsa3Dq`X~IFC6+NY9O&hzSxqOY~U*oi1rJByV`YW zZ+9^>S!`%ED)Dcge%yf^?&WDAQ*!loX6wwJJnS$AYC&5HeBbc!@O2(8ud^ZI45uef zq>8CrlYuYa+^I!rIPX<1#Sm2&|8efaBdl$2Qc@`oGx58|uVs@aRfsv`ch#^w`V-Rv z3wyWgiws}2oBXxtH6w6p69Udd+enJf?A4Cg(chKbRt9O6zn+@*5(>00r@B*z%(m+# zKhH%51Jcb$C@$5ZhVMmSx^!f5o@pg$$$OlOd3b84UviI?quXKR^qsC;wmK!B&ugMH z{w|k)a(q-{mbURu{Y5>~fatNC9vOLnHOoqrh+k$t2T@g0x;U6g`p3>TXHiY+U44X6 z)tI~ZT<^TB)BX*awa6Y%GeF4e7RZ2xd zzruc#+ZtxUr#-Pm4Qhz0x^vE7YlgDoFc$IPrMg5;0cg&_F&-1Ia`!M#F&Zw<24>&H zP(T81BR1Ny)0+cDn8Bm}mOc7CfzRFci>I?8P5k;Ewvrw$jH}i*7#4QYk?r1%L0IXd zUVvshB%~=V?M52oT`Nf~Ow}*9VuNI76V!gc7Y8Jx8~qeE!IxA=-1A6(_uIb|`$6um zfYtOUCg+@OGsq%n*P8chZGujlC<*fx2L}v&wLhT%W1C&^t-b!j{4wStwhgPAvJd;< z2dlKUq$W=smmWYGkNXfWV?p(O0;%B&SBsf9|B0|>(Uf~P=|S}V1)CK-`n#%}H4h~e zr_Es4>l$C9k+^sj<~Aoc%`tm}@a^8McrDxap5h^(@r zke#hz9HX+5T^V(7>=6!0pOS>Jg8}ovJ1Z6FxNP#JNp^^6 zF)k8c@JIDfd{Oz;KO3b+?DHeo7K-w3#K-I;mv>Bv26y;5sl7je!opgP?9mE?<}b2v z*s`!-Q-nx__shO>c*d@Qi{!v`)HiBlYMT*T+@Rf-hq*myT#Xe;0oUHh_qkMWm=(y< z!n*2ECTE}~PM$eSOnhm?f=J-Xr`TG{j)r4ZDxVJ0_30W2?6^_uq}&SVL%a&SfxzUx zZu@cWnrKoc%N~Xq=^u1%QuJHm))_t%oEqd^kz#;ZhT@v!k`GTSq(D;s7xagXsfwK0 zU5OL4al!A9H+fkEDwsyNhE$Q90~4usbhn@V2@#%7*H?A0a2BQX8Y1=Q(n|fkRw>OW zuU1N!-nfpo#cFAKS$woiw>#pSiO!1)L(F39O-YWAdP^jWokobs6WYvvOPgUfscS@{Q2lVUAVib$}oP82s&}<$?bf$y3MtMga+VzhWwdzNS zx$+8gr6H%y^!JiBbUj6_Kb4Z!yS{bfo)>fL*yWm+!VpTXlP?I5%#oQ0|Ngr=dNMi1 zFTk`58^<}sw-CQvQpn8CffNCS9Wx*4}pXZ|uEUN8j| zcUhdawazwjyc!*aA3Pe*ybf-5@_s}ORVl* zSo(;Hhzh6xV>oI}Q?f|0EDmToUM70Cc(0X^kk3iMRBGa*r4N?NZIr$9>Vh|d@=$ZmuT1$NHLyPRt47^&-C2F#wV%`{hLB6gfwG+PB zWB7c-WWr+CoBlZA^=2nWHXJ#;T~@4KD@0Pf&$y=bbFqB%YJ2sVn``h<_i*}xWF!a^^tlgcVN*`zdkxJ@c{mY)yOxnghd6mZz;=BQ$8iF$t3#Vpf!z#cv1wSaJ^O{#zb6)vS@zXIi zn}>5^Y)RuaUmcK`BCC~g{cg&=*YS$S+&ZkTS>%?s3OUNC=zU705SP7zC=uuxG}d7! z#&Sc8Xx3u)bCV3g;P?DQ+!XxHVnJQV16OCQs1YLC@Q*9e`}~f5Y5ZK+MGd3ACmfl*l{k0bKUcYj_})AIKpDOyEsZ}*d&B!0 zj_38&qw2g($>kXICAQxGeLfaAxTqK3*H`ILKN$PHe19lQ(RPvHUY?+p zGd{mHBevhwIF(3COIu-7j3TKK^EWnjc6QygzX(FPI5p5h@^o0_S_bmP61m1qL)*97;nM@sIR>u1C4hbq} z-Ya@P%x|kTpJKPn3Ak!U4A>}0Ho&Dl5<)yORwCQ~UY4>Qv+uC8(#8@f3C9quRoYa7 zVK0w1rYG58e{L|lMrpg0c+(-8^QBDRyMkY+X>(0s-Ou>%3wnBPi4*Ls7s=k6ws5jw zOV~NHo*T;k+O#06AUWLDZdEQrE2jEmI=Q{C<%?woo?|ciRFiEb$}pP(2#`uW$-JEPLAhco$?#))*bLmzp69WXOYeBIDO?BFS+Q>J-<22|w?q)Hhvo`Nl+4d~ zBO)$Kgz$2yuzeA(1Qmi=7Z*#FtU-BLos+IUK@>Lg;Mxyi#r&_L}4M_3rNutSFs=741q5a>ibBe#ebX6H0mTL3C zwhuek7s!USIH#G7Bo?Lx2eN~X!%XcEU?^#~4$r`=9IwjhwS=}1F9Z4n(7qP8#BF`w z0#tKH3FNq6!!R=Oy?TE>*qfaRC{{DDx?*2AS{x2?v&t}q)|Ileu@i{bW%H*>p$@Y~ z$6QvN6shwdEX|kf^b>gH7%SQr#LU{pgw9Y>{CY_&8<3_^_c})yW0??%!;_OOsBi-m z3%Q2^r;q}x!Kqph*Z}z@#2kK1!!CDQ?`j-P4#`2d+hE}aj5AA*lb_t{ zVDacXaq=8fQxT1bKhMJ4KL=81;EPXr80I4kcNeE1>yZEy|b5k|n zh@IpxO>OBeXyTspI*}VQ8T**R>-sp{#yNW86fM)YqM#7dI$ln$8)WZ$lT~Em`!fqa z8@}u?G7?ieF$NR7Fy>C!-_guNz%jwO|hFIYur)tA2 zA5Rdz{|OMVum~k>Y!8?U+R;UWd=*NEZ=TR z^Brx3=~TBk$?k%WWuh-PHw0`R08@k3w%(U8S`$r3fOY}L9@^|bcsHJmHA5{LpZF;H z5C}u2uV9qAYHM|DsL~=GRh4;?uG1j~hp1%#5=wU##Z3P^)n_k@pqP857K~5a`aHS- zv`OElG}t;f&LI+!7TPs`|5}>&Wy2;41KIb-K2bHa)vU-;)6S4eHYimk zy-jyEdmNxvR&80KPE^@;tOUAe{>zKwHBG@e>A(z}83OwCtto}v{b;Cv2|>T}EmvzQ z9{ZKM7?{+Q5Gu116xb{_HpkSCKszw(vL6QunGrw;BtEs!D%IFn|5E9SNB`K&yQ=gWCsiLWR zvoUf*;@_6_?CeSnxU6xnNejVicu~#l*Bq_MZzvFhh-JH1P>});4x$oYYAU4i$7Yiv zSD({aERVbyuB{Ij*Aj0Zy^5==d<3`8VLF*1@luZJdz;ZeGMlB<+!F6d0o>47;JP?i zNci;uTMX2`b$bwJ5x&Nx6E@8%*LZGdl$oSm!1-Na0ObqonJ0~rzqm=>6cjr$ll8vbFuK@!>d}^ zimY?{HyC}(?uR6b`B?Wo>ZSmlSz1~>%tia&22d26z^K%yU%1I9hi@ciM^E;GJQun8 z?2fXYL7%xJaYQ$_MH_=kTG{^2>r|V3qklr8D_W32zWDqPusBW%nl-vq2c(kt&Ly(% zfONyeUpX<}(;65TR0`qmE4!1wua(`S5m){B^O#7i-s=aVmUuruzXTFSp?i(z%vnT+ zmzUQc7ZntE2jeT(?H1IA)1||LX6CHTU}udw5%*Y-a4H#Zj@YV8c>dk(tlUKPD~O!H zw#=52kj7ZqXUX)2*X=MKLa&CJogbRn&eFQo`pw&&v7N$8`1M}O{?C20Bb4xb#02ul zKK5-3ZrO4R5cvWAwv5cEFE@jJIA@#;eP4UtO&IOA75h7W=m*ut0GD_a+U^bm@z#Vdx8ql=VKiphY0Vs9Yi=Wb@WJZu)L!8Kb+Gth;4^UlVV6>)p@Mr zj>&wPT*Z1mTp550?D*`9eT&VCqby|**1Ji!eE34{#Qu$>|Cntg`^gtZT?-3SY?e+b zsqAuj7X}*LrF|D*cBAeuY(r~?=_2qj$DKvWxZ`AYl}nybX=iWWo{c)y>w?UMRw>IE z1p6m?s5g`krHtEFweCr=b8x8>xj6iN#psbO!O6*VsjaEN+S1baTQ`mYPWYXcLkM)X zn>J0F>v)@p4t|TI30`pI5ASQ_9O+$MA_xafR3T&W_@zplkU*B_hBrp5rpGwX zoJ3rS8cT;n;qIU-MoILqeEnKPgY20q-*;B(o=6j4LG>1;$8Lq*`S6Ta%K6vhj!$FG z$&<$Ne@`JY>uM{H@~=xQG8%8Tmwl98TB#(VGTzvG2Y5m4ZpPmqw{nh5PQs(4QM!%% z=@n~IOvywgBqX-vR1?XwWTVd;yVw<8L^UqBCT%I*x(=WwU zYBC09P<%52eN>*GG}g@SJRc&=#=Z7T4Vg3LITy*MIC6rDF_n(#cp2LO^25?22+yvT zzfvm7X1!Kp)~p>BVYO&kE)C}OutyllLx^~EtkJ1hxA`2^SKcGL0iLjllK2UMcd{3x z@O-BN%X0#M8e$1yB@5C2?#7T~L`3L5P{_r8Ut615*3)n2 zv|55nxaB`MaM#@QHhQX7&s{&wo8E2REsFg9^gAd}nC(na(ld{5bAvrnD&7yTV4JlQ zT9)1G;WtX#uFYMTsiEYjVLI{)#_@BZU!lPzfIeJ3P*^jklT$M0dA}i@FIT}tBDZCmg=on!Dq@g$0=mXS6n+HZ( zkU=?CFtoveU9EJ*aJgADd3W&fEkk5mV{5s=_}Jzmuf&N5m*5Gicr5D*Bv>^KwQ3Q%+BJ<{tu~7b)FRTL^aF3oc&VO*)&))-CX-| zCm*&5-nx|zc_y;Q6cqhV9X_fg1!KSW?FmJr{S8df(^XhE`#QDiyn)395oKlbG92+@ z+B`QO@lBpWnpHO&CEcA!|3sjbE3eW`_$R)6Fi%C&DAjn25L_ca6*{lVj{8 zg4XgH10okPAAEPu@UzWmPy8f;tmVW4p&fYM(dbHgv7*6%zOk=d6v>X`WtZwWgJym) z<>)Q)M$PMDDJ!?Ac5fYM8$O#}@;u@tbxvlbqe-VQLl_g#5|Uw*NoVIPX0a3UzoNCX z=$_=bIc(GPTYwfho%NgZC12BPuv&3fvE!m11Q)sJPc3$uUgJx{tU5ENmyWzVQx2Fc zT{{%C%qipJwSMvqmE9v@cZ|; zaR}6&{JDFuaC2<>Q0!+FO{Kh-Y$89*m5oPjXxcE3YevhdL}Z70D2v-%k8OKw{4FxX zid2@snJ<@v$&#^sHMc5>D5AjLDj`J&pKWo(+f#>;&*C&PBpyp{ z-Dppy%FJiy&KYaH6U}*PscI&oC=KFw*1ORE0N&sGM9;t@L4$nZ><9*)WJN2AYTAyN zp!};$wRD9kc)>Xw00)thLtuwNZp3?ex&DRh-sBP;6D9I+^|IAqXiJsRJKDs^!JdrU zJd1lVvkW2^j(3`zmm6tDeIK?;Lncn_`t1MS=aV>+cx!^}+M*NWy8pHR;u>IulFOG|2y8sH@)j-2ua~AW!z?D-4GoEV8li`F$}w z^RLw7w~`|pjH3c&?@o!Zx26qDQXnfdC>)YkbU?fk)O1KZ=_hhI2N0*(6RmK`k%iG8p4}LzaS|H&n zJ%W@hxW)yxk@$8ddIKXO7E0oM^Cy8()YzRZN^!x7m( znDo~Qs3ShkV&0Am#W~O*4{k1n-X5k$Tw$K--R`+TIhdP(F7fE@GR91q7-QMf`Bqr_<_ZaC+ zm@5@fW&vYv-kk|ID!B?$$^7XW14@$b;1l(%0#QQ5fmuM+7t8{kAy+1!-HCdD%NFAG zO^QS8MI?9}Ch9WGor~s_;PXUmPO&X%l*Y_P{JW5t#u~brU&=0o>0`JLMF+*{+}9oF zhR5oBSC7>m?6}9$#;fBQ6ze_yqLUZwTA&z&#xQ&AeoN)HSWW=Pw2sJ`h9{g|X zQZaf*lE?T;jihxVE38*yIj{X)cU~5O!xjN{m7p@%ao>`Ht-@A{b ztUH`?RF=!aP8IEzuDb*OgBy)VWE#B9(Slehn>Pfwyrg9LyMxwFE6>u6t;UQB+>ms_ zqKV`*4|m(8xK9o{5eFk_{$#z-(*3=-pWy3_H-VDj`6_G6R`$F491gg@7w2Y} zNKb0~dfTd)#Byenk&ojQW%mdB!$@gVWa;S-k}g$q*T!_NN#T>UGR`p_@xom$=6qSj zqa>e;Aty{I3k^PAy~<}-2<3r+wu#c~!>dnUVPG$O_ALClcGdH}6bktW&Bympu)9*M z6hy6-EYDi@?)))2+evc!lvoFU5<=k z-swZ#%7=dFqZ*dVztg#q&D|}>G_c7Y80UoZRV|xkVg0z&0WI6+(U!xA;mNg}GTgI#RNUC{J# zVLd)sz-dwQYdiLF9=%8|cNcMly+bs}V$>+;K*wpT?K^ z9{V2?bsL_LAv$PzUx1;?+Y`o42=cc%1PFkcQJpgc$6eu=`h1y}S3pmpkzZk5-&F~- zZq2N8(9Fo5;^YD#fqXtZZA5A=d}r02i}+Zz{zo7!Yy$8P zg$6*x*v-KRa^&x-1c|4$s$_DK_Qhrym!d%?=(6?R4JjBf5EMB4GbsHW5i8?Imsfw+ z^7~Ly8F8Z*|IqFTyoV==;O*r!^)$-3n_;q2zzqYCn+vqgz!>g;a zFMWOE*VGJq*RPJL&o#>k zCXBQ%15G)9ejpPGrc(Yz-smqNNJ+IPc-?&?^q{!l+L{zQeV+6M`AaRD=pSuaAHhzx zY9zZDbUMC;+wTU)K6(dpwMXlsk8~xpwCSVenfETiHL0r*lb%GM)W~2m>zrV|g6b_@ z1-rU*UOtK2wLbW^GAS^bIg_t`R2c-49MTR3s(gM!h9{Jwg0z~q=*g^1Zm{ONEJ@aQ zY92sR(TW?W!}%Bb^w7iCHCMG5otIm8oaqx-CtI@@R<7o^Z$cO2mP-CMU{dn5fv zD)_Ir3r_Pa{ZzHfEeoktW6M^xE{-6_k3F6Gad+Vn)LF+W z28ub*syMq(kU=cCy`^A;Z)dZD;YA!lraTYncnR7 zUP`6fu_Ib-7LO8VFRL!N6(#VU#IT{F(hs36>7H9H{O1W&h#b{==Aib@UWLjUr=bwf zNaCxR$~%a>W4jo=X<5$1jw$RuZGx8Con| z2A6*7BCN{b$*H1KRfGDBJdDNw;gD>=Z64j?sgLb-oidDRnI5qdP{S~6LTZ+{!-lQL!W*bl{tIm}Y<&M3&EB!999KEn^D;;M2DuAP_ zLj(r+k04$Yu&`Qz56Yeq?MaFcXm2(Pxr8lW(099~q22ib1k9|8Ndz7syi~@kJk}5X zj%;p|kD87Rv3{}&G~Ai3wlSB&MDG)C^H}VhIv8iDlsw;0T3Md7iFoni;3sZHCL3~i zc)4Iy@oJ@czQsb+bN5g6i4LrQPg?U}h8ez+Hhn!2;E1#W=JpLO&780Rmgo6%*AaxM zc~OKG7qvRDvD{crldaM;*6SQ>Yb|z*z{wG6@5zi1!|7Rcra$=@ot&LHfX8a`oGh5p zzvWkj?{1%tHTSsP?HoRDfL&yw3nn#UiX)P{tsvsb_2?$aJqN$N=K3o0Dg{DQ>o!&E ze^PQ3mGIeFBdOxOyI2 zWgYU>sgA8>W3Y7}Vdbw0U|Q0N!>=^JloX+9ns3>FT4}{2KP@ob%z;&o@(f*Hf1^o@ z!kdx6XPqVv9R8Jw^TaGth!J{0HBTy8J46uscC#%nk4j zQSPzmu{b~CoUX2+J5Ult9xlSHJ4yOgACwHghdQD+B*XsO0wVX}TcnWeR^55*qni0Y zdaX-51&3R91}|0pTJBL)p!N0=CrWcq zAPC)f-0r}BK6-w?znX05v=^N;7?RU*yiYx%1mtpwk>7fFa*xBSTP$0!zb3nX1Fem$ zPV6i)il+_S4FN4qXE1~=z10zIpd@Ux82dOObDV#Ai}!RwBZrP6b5zBIz>>p5!vS^! zeQKyA+4xPX_lEFM%_yqBe11!~Ghu|NCZN8de;S8o*lUCt@$!5$YQCT4VhAba^~eOKuZDeSi1 ztSAlsx1t$;+G&0GxwF06Fb9x7W#8p>@#EV zj8Muz)aGdgc@uHaJ9>FOmWHD~U3*qso$WBZ^ubwsyoR_zv|uGk?Mzoy(o!%@GPI=aqbNn_EiSOR5UzEW!2TM{XVy|p_#_4b$B1`X3y@a@|M*s!eiBFGoz(~TcwcHfHIPwk>5 z;bZV>6xsoEe5e5XXCY ziPnZtz|{GK!+-6!6Y{w=q(u;vv|w_z1o@%;QGu-h$m7y>`5~nnVTVbg9#RI1-1vjs25zi` zycF^f*ATGzvVqyXbnY60ys;-1WLz#fxjT_loevVp@PA#Eg<@e9cb^GIQ^H-b+ zj;!6Qd4r)j9d8QQcwo_uwtf&fF)6#(*4P$f#8-?m_7xy9Uw5ku zm#$jq%jpvp6|FFWg>w}s`)NT^`fhEN?*N^XC$oUrz)0kmk~)1zqKTa;CX=Hxx4lR4 ze1+?v9YhF2%aPa97_)6eLyHU~({|pb1pA7M&WnT9@)^Do7}+f-WWEj|}k&l~Z0kGlGj#Il~HbSy8KOGvZO{cEVR5j!?!N zxGYUI>C)<2*QnSn)x|@I)v3t0*T7w)thfIt_@6(q_UBw!&}u0pAe0C;!uWrP3!Hh}Q5SST7Xw>5%)|gj_ z@LIEi7J1n6y}g?D*XZ+7^u^c)^_%|KpujK8rV^VDdrL^_~SXXwi>Cr&ekJrzUc}#cWVJ3?@a* zBhD%`R*K5;%`2SsMBL8jL=5UqxZ^E1SiHov`h@~4=CB#D2lbG(y?ijVb&WIn*QQL1HTh*T5fBm#kmYZ%uIQK;#`36ATv_Z$)aB!Jic3ucc+CcHLRl2P_hlX7r1EHV`$Xr|X-M_Q@ z5+95_Nmx!e;a%`deApETYP=; zpZ>ox!fo0l>#a(~rzro!*v-SN{NtZ)g`@tjgO=~THlNy&;Eg00%~;Z~S=*J7gtIpy znh6`_)uvEu$1II^RD=Z7fqOtqX@wd5S*@8AQiLsq3^+oUNX{Qn_q{MDSXLU<&!=yL z8C*bju7l64wR^SG-;1Hb$vM(PY2IDO}xKd7n35SPxNdnyHtZ41AMV^c5x_e1y0`Rjt=x7`r=1akg>{;srRQ@ORsro zKzI~=y|5@UVs-Lyb<3m!nOw@s&o2Jg?wfn*=r;V#(TAynnL{3+W66<{>y#X=Q;bwz zq?Qw>((#}v9FLV=M@gnH;c|J#;TAK&a|cRGp$@^nQf8T(jfIuMRX8PXvVuP9I9 z@5$Ir#C`wod>d1nilT}pW&7{obt;KZqd2F!~FcTit2TPq-{zMOSdeyaXvd z3HEDh5V`SDrEZVO#h7X4qQSt?qyulkt)D+Wxio{)9&}~!2&31-H~B(88(c9$@$k&7 zaFc54%uNThaLA`6q9&-Y*;$xN!9J}zT;w5OwMm4FZlep@pV&|nNoU3O(b=}tp~*>} zIO(0|5#!?r54yau{Rj9YB#Nh#gl_nj7ppw658oXQ0N-ahKR`@#b3U!=wIrC=0|;I@ zO*H-NQQqp~4o##hSni17-5kRlk=z-jKR&>O!e0Hq#KX{2jF%h68CbeyAZpG5(NU06ZCLlZDhoQQ?Z`g2Xh-% z{`+c2R{63PnmO&>M4y?Fn7=5ZvaonBA1w_R|9%tOGk}&KPBb~|GEw?poEGUp3v=EJ zdhY>+RyD7EBiSf1Xh7^2G8J!d{|{sk!1N5hKTT@KKkVJ4AM#9GY34hDI#tU$VqM3!@c_$b^ zq1Sy|2QJW^aj~8))SqQU!#Aeb|DnNewTz0yx{Cvn3nV8V9#zgY6lmaby4en~@C&!4 zUF18h?`|IPx&ulPzwxHv{f`!f=por?Xc`x|(KewQMa4<-lc>`79WJT=rQX*Rb&aIs z&d70GdRYy(N?1vEy(HZu8q2ej=4cQm}4PTG-r0bxo4*-d(=#x}pu| z4f0v-Hqd>A-)ah*KB|Zt7{@=3dM_NaS_vx5nf~13my-?8lkH2%7@IKCK%YIPBRp55gZMw2`db+zSq1{FKXzJS2WjcRp9x4v|%$S*oa0YntC+{FGQJ zwtpB|GB>A@`u!yX1qr~p&qZc;WgED;v*U_?vc?Ke^Qc9RWNMe2bDZ=;$8Xf&LJt$S zo6rUw2T~OLvE!-dl=IKl@$E;Wp`^>N)atLWZL;6C5T#}O4Thp0ZCV+ER8qgGKs*G> zR%j-S^5BDjeN%M&b0lFpZ+P8Eec8Eu&nt?J1!$4?eoal&W8Wh|*9+&NQ90YzZ(Xo@ zHft}8*d;LA_s4>opjwllfR-t1lXnFUvpY4BLOBQ9z0x^jP#ZO<%1^txaJley5w{$` zF$euR;AG^Bm}c5#h>73=na}~qpM_@DsdQas@X*)fc`(ZHj!wHvs{GGt};>d@NzKim9O+l`GkPn*sQoj}@N!`h;&+!4!{P>SyieVunM7@>d%F0Z#?_#_Fu?_;`Kj{JO zqH=suitbJd#OiLbKfYvR^C?Lsoi#M%b0V*g<%PSmPVt`1_w$?2X>XEE)c_4wy`Hsi zfG#bXB+KBM%h;9?XJS~v(_wP~cLS;HqqlFhPMJv53z2)BTm>yeG}_O>WBqyv6g5F+ zHj^9jYDe?4zVvWw<<~8yraVEs?u@hSb{PNYU_?I03^Qygxk(ln{*EzcuiYfyY{MF0 zWW~Czu9oyUFd{!5qm3xD`>zHN0(p_HZc4akH(_cg&W{Svi$ou5o7sJg^cQ{Qiv9D+ z`PT!OZ6e59n)C93G$m_OO3~-*JSfkLRc7dG)+vN93^?k-)_seffF^M72a83-MiNS9 z6a`Tcsdhk!5@5kTEXLbc#rh)gWvmI}6$beW;3$GXOU;=pfSNbLEDJLdErEB$z$Kpx ztxqNHQ1aNhzT4$%H>SgHcw*zI_e-~0q$p)T7wkf-TBYR3$EIzEVwC}6NnAqnI0%t=ui&P_#IMmpNB4>nM=n#+n=4%xdWf~X?-Fm^h3lG0pv)cX&+U7;y90VI*ma(E zSGH_Plb_y9uIh0S>I#c}*h#^{_wH37MJ3Yie3AV4@g1Jfw*iL`uc}8Plb2Nj{o8mn zAvlK)yB$=}AaE6{(Q2WCjD-pax^GlmfIv^@gU0tRd3Y=n_{HyDRE}K+sTu&aOU}~&XTBIX%I4gku=eYij!qw zmIlK#yzI4TD2@1lhjkphb_q11Cn+b3>LWv@Hr?5g67GM5e?yqX=)v?1ksGy3ckY3} zpDS+Nm*ofRXZU(b2aCgdRHe`_Lj{J8o*ex2f3R0JFWY$<^1H2>Ubn1wX%)ULZ%<8R zQm($o1jB9ovfuNwiz9|8=v+%`ghA(uwP3#|RkRZ)-|~5k392eUmf+TEH zP;5D$3Nz%g(Lkp@;b}dacTLK-H6$Z7t_50eGfq(eD()KwS?E19=wu4nZ7v(s{$SnY zt8z1#QvkDVl&h~wL5Mg3iolocvinRzx2bIqB;FB3OUcp$wz|>OE=yN2DwfSXx3~L_ zA(tY@A8A+rou>OAuKQ7Jo#8yKaf4_6NnDZ}F2!!3jz&2uSZjsm0#a~=Np{a1r1Fx- zDo$LcKt6Mrd4@aw^cvajE6fYE6zn4X0kL!@)p3OSNUZzf&iLZ+I@(EpjFZ1#zS0xS z269Q2yhoCPXU#NPv9NUXKRe9o0q~29{10<|%sEVOKg``^>^&wi_=vF`Tkm)q7|-{C z%>aPI0+`%`;kZHST7*f><{^nfFP1^ClBB4F0edf?KrjWN4ghk1@W}hQ-bQc%9{+|0 zX#~VNhAsN5xRpsa@L{^+$lm+>{_%;bIpuDg7zG8&9?6ZCoU6x8BoYMvd7!mw&m!-Xr_A}oz{+mxf)GWzrdMH-p zS4TX1XwugIx^9N42s_hzt>q1SvM*{kKK|~{6>FYts|4>5jE-r8JY)@YWE1!q9T_?fEx0?hQID^t%6$S4C0$H%)z6(pd<)R*AER&N^iBgWnpQ z4-=9YWCXOQT`A8k0+{pYNsdf4B9#5|J~vun@TJvRH^Nl!y7HS%aaZwNzjT*CmQJ z?WCW`wQ}pApPO>ETl2Zew}t}{pvL7CT$4zO*|AdDNFEr)YITOdc4uWt0*%&lUWp^{ zt`f4F6r*m}TxYf^Z>*$t$V#<*hl%<=yH5VO6$bu%hrm7Ri5Bb6^oKRdvS(!6p4DOI zm#CfM$VAGcx@^wNF@K$n+xI&Ha1c@A&@8;t_iz0)KHOyRWct7;1%ekUjPiDv^)yA} zH_*3zY=TF{Nx9sdZSjxwru75HKGKr}$TbOjLEd=l_gn&hG<1i-kK{q*fjuOea2Ciw zhPT})&mujVFB$HB}*{Y=XG&V`qMz<4Mr&Gv~ zDX1+h7OVF~z;KLsbuTJ`fAp$bM?-BR4nWap4B_>vTM}Yr2`ETbfRd^T6%Zkv<-A*O zlttr9MCre~?;J>2Po--|DzbQV{4%#Xhj?8j7y0!~z)F$Zs?CnK%QE3A);E6=CDt5(DSnY z*W(mJc@9qXolezKG&^+fuMrzUd;mVdgBa^6(}RWj(=6)N4?W|cLZYjd4V8WZA$ir^ zAF(olCUbu9FFi-)dprU#2@D+lq+qP^=jkx zJ@*`98Zr}=myGxkV*S|1gVlDhRjxE?L3wlhDoXFGCv+yfKs8+)8UXz9jiu-O&^sJ7 z)m~Xs0aFRo%Qg#7MlG)xBwH3Xn06a12}JE%9(s7;SgglJb*eHGEM5FtoZ$JwVW*Hw zYERbKUlNkpL)}*d$@;Cm`8|F@=snP!_a_U+q%6AG)ao-b&D18y7K>A$EmQh@+6e#nmK!kGqc^pl838IPgRsIMm(EnA0P86`P#Gf7Xk1=^02T zTL1h3n*{92bHnqi)P=qA4$&ajwwsl&r%Ba)?2_q4cl=AQIp|nH{p9`a&v6Pwjybar z?|32Uw_#1Qf_!WC3^F$Sf`ZN)bcC&-)&ZB$1Kwy`!+h!<`pS+C<3Vm63Y6~)92@sB zKIObrrFF2nv~MJIyfF;#C^cLWn;7q;OAP%C`Th-*$TJb)#Id!XI>O?*2cKQ{hDLpQ zs(p#ByL;jzb`t4l3&q3of8c}=2C_6647=#M&#TZXx7YnJqHG>dh%K=^`F4sz?$kT= zYqHN}uG>$6U>Uw(sJPR08`7#_fS85*?B1@ujq*Q;ulW-*h+(sYOloMl`a7*H1^d%H zU=Zk7yYV1gZj){U-dyun&?^ZAO08%dJg6SsyXBC&ACqXBOe}gGl69Sj;*Y7U&OWalVX+hd>G^zPm8Hw<(tBbgnR@%d0#h3*qSQ`&tB^`;)7G1E5sex*7Lb&B}x|4s5- zw2?{?VPVG}*P7CdOv{@Lu;~2X*3Te+1z)~Q_nZ;pe`h* zm4Y6*NO0hIyfFu&A3)FC`vv~nK{xhL(l=&qDE6Oz)%>^H;<(U^ix?5G7CqH)@{WmJnnc09wO~B0KK)kh5MkJ)ko%x-O)N0!AxI!wkbM zJ*yF(+zVVh7;>qtbz`hA`v+|@-%mo8euah4`Eg*R-hJs=Q#fAUx0ktXKI{j2$ZEp zKW#XdJN#f4zR0tEj_mVvM9q!RJF$3PDaX`I9{@`i;q_Zj5vYLXc)a*CkbNHDO4h>{{1v9U(}j<>c4;*c5-SPm^3@DT5v_?S z9HqlU&-Y)U7y}7YJ2JT;BFKCN^O9iB9(wSbc#{ZU(QJh%82v)c@M4TUu{|=vARnoY z@(h1WU%#fo@2x_l2XqQg%d*;iTGujC|Gfka#eGDw-i->`I}A_5)Z83p0g$1Pv=Xgb z%b_{lX6`AEFlRwckL-Vy-R**;X;_b6QoP%~F<>(c24effr!6#ErvM0n!pTS!5|fP3 zLOP~-V`%ObsU7>uf|G?!z8XB{AJFmCLsX#-f1n^Y*4a$cSqyvwvl|ydB?Vahcba+@ z&$I2HbXrMX(EjXB1+;_JR#^3WRY{_mL8nzm4&uejCFQa0k4i@Goj+JQPtPiSa}6p{ zu?f7)fzgCR8)<*1Zo4=X_5`FbwNSWqRP74+zRlL@QN?MPFQq4gl_M01o7jSpQ zbN*hSs(!c#@)utVXecJUrv{SE>p7a3+W};jGrC8;BoF&52r<3Umtq!)>WF;Sf2ZqM z_b-b!jeCd;c;==Xn0I`E@3q*<) z2+nAR;Re;X%r|Y&9qB~UekfNhwZMrH7WuZ*;4dVVFS->&n)>q;uc9PqFf$uCX6$J0+utQRZmp{>8lcQqB2Sp7vOKMT|>%jcMkJxon6vZ_=5 zFhNC>HfrVbA;NCs@w*a~ngHYNzV}Cfh0enls2F5TY#~S5AnJqyj$dU^f9)(qzy0^_ z?xZoeZprB2nLTSW$<|fig;vJ^qUMj@`nk7b-1UM2m92bimk%5ChY_J9L<-o zvmg@RWH4)f0LQj&9p81ExwfBL{t+hS+?c2k;RkEMYJULLoXGD_b{$MM1KK#*Tr>@Z7v1|RJ4zbeX$S?3DT$d+FSGpQ|{+^ z6Vbo(804V`VR!L9GI-{ot+FOCbsP7b1f5Gs2fU=dw15VA7s$KYm@6P`_!mxggu%U9y z&r<@?e>+gVS&23R=YEL)gLt*37Ubm#esH`8GQGCl7m4uZ=)JP)&ugDz@pPCND&1^t zRQmPl0C5nvCl_SH)-B_aH)r93%6W(O%J(79;F0V( z>935Vq5OlP>`Xk{X;6yCL0WacE!N`z=*qc8a@}#^ym$`9AYi@wy0sM-x>!|ig!bO7 zl^=;u6*XLkuS1e4?XDJ5L?d)PwPrTf9Wu3G@rnY7R27g&(Wu{L?8Do}MV&lC_9Yxd zxG$SE??S)?EUx#hOhBa5MXAJbGT$O3Pd(zJLkiNsI!E=gsR>2@2)$G5*$LW^2DSRZ zG;?xtMCK&4RQqvn1KMjBg;KIq+(WAfYk9v$xu~8m z#2_C<>zOgd{@&<*DU9#0!FQ)DR*=!_eCMsynVHxTA9v(S*kc7nk>At&)gQfbL;!Cd z^WHc-6XVw!OnBd0&4CF#wuAr_Z-YyM>7YY(2}bbgo2nm}0L`Qv8`LD10S+ zggF6FASGhl4CL#bEhscPX4Go_9jvEi7($Zge}Dd4-EHY-bDzRRqz;q^tdv0@5}sJapy5eg{0Gs*zFeLM zeF`f}an{zHXgyCeHF!|HWZ@zv+fff$F*J1;pnIclPeDc-dt#mYxRL8O-l zcwbYQ`IVD;dtTWb>EsVQWCM@uxGq z3RO-btk3VL9dS|mzrLP5p6UH>r_<>w>i88+S?3oeY_{1Z3Uw+SL}>0t%$B)q%za^} zob;TNQEDkA2^F&~F`C!R^3#p#@;PDIc#WAI`2vLz1$S;@OE_7ttQ^)d1{MgdCL=P-kwICK??S zpQ*)c&dN{p@{a4t_hMwR7>n17NTf9!W%Hes@tKGUeVS=KZL z(~REkE#|gvW9FK$h$3ly69vQL0gPFe^mo+pWtXR|?V+!a*4&$r6S<=vY=wXJ2_TKM&Se$?%-1 z`tFVR-I6MAZs~=3T1`;3vo<@GTUzS&&%et!37}sbWXTJz$5vK0?0su{Cw;JWLMdkF z^eWR*{1lN=TyXvC+jqdGb{5|C;OVQ9wUoZ(goK2f(w)bh#$+>DtT85+b4c z$5dMpEjrn^+}i}BpPqMu^behNn4}etM(XVsUPF6rC0E4Tw59&gosiZ~{svsiCFfem z_bcM!#;-nl6qy^=d@rcK>SLkdS)E>4uxHg#_dB4dmXjxNR4SiwMwCCNKCT^lZwQsX z5~^w~9k;&p4VKE9Ph&4%rbNH-4cro-YJm<@w#E&%zLGtKjzf$Ld@q$SS*%m69D7-9 zWsUdcl2)1QoxId#0V|P;a;w1`H_*RMaa)y2z1-^bv3$vg&X%V zG@c&dATG9Az1pOsEi`%gzAqAEyzVkFHG6l(Wl?!fwsS7HUFm*au;#F%@#j=>)acZ= zmT$}VH~vJ9NuF=_Uk?~3EiER-qqMu+xGXRTE{@1+z6RYEb|h`4=xpeo?1iP6A8rb- zfR;yl%)9VgbGeC>Eaz;jM3Ue3q~pSQrr3*v7yO8oEB58eU0hleY@a)Z%*^V_4HX~~ zfNcqP)5O_1bu;Js9r_+zebB=TDhR5NCl{7j{?a|gV{lqKHsk?FRwsV;?6Taf!bbH+Z=o}{1)#>dP!-XQk*4$?l|FSq>VOJT2Kw2H9{qt4-n zW%+M@3~)StLK}_6 zHuD`8L!PHjwN)5%f(0l(ZJ@b}sZh`e2?;TiQsIp^zCmy$aam0P=mRzT1a!sq)mdX) z?^_m*_uQ_{N7*+?42*)#mka=lk&i~kwk?0uI-fSDN`0X(oZs%0pDQyTN)+0s3?q?Zu&Hscog2AK zv3MBGb1eQk-7&bS|K#|D2H&F45dWnPYPKa~e!$+zf8kTHN(37G76F`lC<6%%BWjg! zy1?Rm7v}}4S01oseWTqy45;|BaPt~;KHyww2BJX`=gVooBvRCsw z_TdSgUl(=yW)R2?leY6^Po_S6c=B44sF!haj_cIjHhbYrO}X3c=z=q+-_7jalh(I3?Z$tg2}w+!L!yYWHJMebwNf(YIBPu zFCKdP$ZK8&Mx=}AK3!ekAtB1@)JOma4>PA$Qv-;nJIBm4kB8g2({cr?eJZykva~y* z*MQ^0K6lRg4u+L9Mi(|v`}gFc(`H9=T0xbS@~p0V1dB%!}kWBdPA3 z^81l_X7!1UC*xk$=RSX0P@niY*Bc&m-K)`vn?67Ah@WGhf3aj}uJ7c{>ZTxa1!tYb zlH6yf+?t|oEs?B{i$pCE+0V73EG%D?cew)&$5bQ$xbiC-DkPtD?!CTri#F=By8&2yp_>J_5EXfIj`fiu@MsPEQW0#?^iRm9Yi)v4w zI(6vGfTP_ifc`x`J9ogHx*y)K8^3(wgv0rg$+(Yw<~bPYrspNPtHH(Zu^U(1Q$lYk zMm9Q8`xd``EO^RITa2wTo|5Hzt0&I>uI%o9wG*2DfCo^h9$>-{KgXeupRSN!l1R-* ze7MgjfRuTF>~VnLe{YrLOY9fjJ*z@}RdTO$pMD*!;|9anexZ2^qkEPABv%GX9M_`{ zo0WT!Bbk%@i5vx{thiIiqDPfxRfbx>l*W1?xjte>_6@0R@U4Gyylu$3pZ#h*Am7A@D{PH z^(7jIzLx+})9Md8in`w`nGc*Kg*L9HJ1~fJ6F=bbKriA9e928v=nH}woQq)CCZ<o@hH+(&_U}TJOLkCU)n69P_3qzZ28i;>Z$-|W zUBMk;#OP|9>6@&IKvoHmqxFiRp=sHx&Q^JZm7gH?0F#afBqsLpu3l=}sb`%&eQnT? z&CZaj$JGnZfRN=6F;voe>f)EAvr>Bv|DmQ^dCW}TGDylw)h@Tmxskq1HR63MJPWeF zvtr^OnGwINS-Aq27jL4?4>S{NP?!U-K<5O>{_Yk#rDFnVxc21I%u)4yGjd6S9RcmZ z#)uwf79Ck{YMN(4z`>OvAiOOYS(@v-ef#7}48#Wl64vt}>o9VKk3TV!XJLUhPl%2R zClC)=FAW9jP%Pltz4IR#_Em{P?V~acu_~I+v;fpV*DCA#xAyr4T$C$FWrIxSQPDIqJZ|P)3T#;qPb5!0S&r)Sp;!@aJFkKlI~pGg9Qrp4TJ4*RXLY;r72m7oi!xm zOSl_XQ-X7MfWkqwb$HTX>pGY!M~&pi0`+GKwA21w0mxB6A7~x`vi~-vVXkj=70`Do z@1q(3W9jq_xF++(ivd|etkp!Ovs&OaR$fF(U~lz&ztX3q_9rrIRdq-f*3mJ)SbO_+ zGbXdq0bJQHlnxBAT>ukzTpv}z%(4hiU8E7m`;yG6s-W68_I{nNEIv$hHen)w1OitP zkKWyM9R6tOQnz<@qGdek3r`&3FG%(<&x4=QP*>bgan5W-+DuoI7%=VtK|EQl;fKe8 zN^l?GOBKNU?-iw`g9<}yTyP++I7kWvt-k8N-aL=4VrQC&d!8M<@I14Gh3Lsd`Wr$o zIultWQXlln)vw3H`wBc&rs<0FkR$oy^I)IE(PTzc`ch~D$+0H^ zn0@z-_3~ZB_A0dr zcTSvlxmBW(>Ogsa#s$*m2;jt@fhczaFX*pQjh{mGXp!FW@l*YI#U5vU+}XatH@x*?PO2pAYK@iR1HVvSLDRy^k) z*!_jT5dF1D56AB4(sN?Je5o%U{aHKQVS5Wc(jkm=jtp%vd>a4Xf&6#{5e?%sAyZe=9(18K=Huff{1k+U~10 zPQYuc%r^c|h>Z(u`r6NuWCBPIFPmd+{FfTgDwjXh{GLbp1MZo_#S2g;RRu{#LBhmn zxiWxiSDC0%K4$hWlB9`&>lq_)GY|4j60I)=JP~J+K|wr}HQU^Rs=b*H893H zkY@su?%2_DR~k%>in?3bwB$)39ANVKB*8m6UL;gEUS=%3X))J_Z}DGL@N)X+#XPs( z`3~wAKc(g)z}LXNI5r?91Ow@~-*WB2tpHGV?5xs3J)OaZrYFY?+;Q9;x_z zRrhpT+qsOL!!LB9f(3oOJ4gFx+u_wroJFz$|87c9@xb;S;ELw2pEr(^9na0K6f~j} z-#-@R#+P_6)EGxsaL8Yqf8YRRaiW{YNP8iJE`bK@K0^BOAzj9m073HP@ia2I0+_)u z8bu6X2Yy7#B@=)#Y-rtJU&z+@a7>Da|2h5oI<;m~X1QT8v&#e1PV@q4b@hR4Jj6D6 zqJZT|P7wqKNr0Yy9B@blsHVA6cS|zk!;O<`(oTWn{54(gk_ciOv+ux49SgU;ixY+s z-s5zST%bq!3*wz3^DHC3HlN9e1ZotVG4aSw^A8u)EpO78O%fiS&4F2r1}@DH>M4%e zd#FgX?zN@+I`~Fjy7~duj*+Fg634@(10iSXlZ1YKm4nvbpVbv^&dv%X1G^N7jCC5C zl6_LmsR=2h;r2f(33e&KKc{t^Z3S8kB#uiSe(8lYs+Z!9o z%JAd?YoSfkEYSHRK*cBn##w#MJw&}m36$t>BKa`X3p;`CLLTcjiLT(%&+%}VfiJGq z`A{V54=jlkwqs{M7o_vFc7kx^1{5aYr&TRntX+B?=w zXZc`PS_iE{?zYcVo4tGqu}@9*c$(cC@#ibwU>^V<38O{BB(RjX20=o=id2I+N!+ES zxs^V^{c~J(@#yW#4AsaW_|8fncwkI+5e^R`H@t@;OjcEc#2m%dtL7RfaAbcI!zg96 z5~^ziY6z^)Q~-%#4L0P;uQPBvVv*PSlvpn2+QBuunXx=(R)$6vbwB>i7*;M=6_nfK z8*nU*?8F=z^6tM_sc|>}xuG`#q=EogRwyBT^8F(vm2onDth+Q#3e3eD0EqQ{chG&| zpDvc|!hnobj=boAa7zQ=vq$ZwpXrMBBnDEJpMr##9y9yg{{GhKqMv?Xtyv9jpFuSE z6`Eh^WGp$gf&W?Fp@sv#)gN8K6Uux8UcQ=$sgkwh+1);hkxGYbm#yUX@kYWP;lfH%P4R` zuBE3L5l<$OY<)dJv$CiJ&EB-EQ9ACiXa4rs?{AMU2A`h>k4-hBlcQs74}3engMXc& z6>!esP)N-gA`#{YD{zL|*i|er2L87(R2q&-jndk$If!lU|2~LqC?p)Q&D4iX38N;( z?Yhv>2jX?K%>F&nl^7oaz!vf!>wSY63M_-Mwc7nZ>#fRZ>if?$^5L(6u5P3uI7iw~g?h+tZ9esD6FG6b=kfWU;HVOSy-1;ZdU z6WI_5FbokHLPDbmD68Ez6!=64nCQ@76CI_IPmn1gQU9?|&4giKLLv$qhDD-mU>FiQ n1V%yuWDY@-5HOTA5ecRvp)jG?{DYitbnSG=?I8E( Unit = {}) { +fun AppBar( + headerIcon: DrawableResource? = null, + title: String? = null, + onActionClicked: () -> Unit = {} +) { TopAppBar( colors = TopAppBarDefaults.mediumTopAppBarColors(MaterialTheme.colorScheme.background), + navigationIcon = { + if (headerIcon != null) { + Icon( + modifier = Modifier.size(32.dp), + painter = painterResource(headerIcon), + contentDescription = null, + tint = MaterialTheme.colorScheme.primary + ) + } + }, title = { Text( modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), - text = title, + text = title ?: "", color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.titleMedium.copy(fontSize = 28.sp), maxLines = 1, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/favorites/FavoritesScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/favorites/FavoritesScreen.kt index 42452b07..0e177c90 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/favorites/FavoritesScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/favorites/FavoritesScreen.kt @@ -44,7 +44,7 @@ fun FavoritesScreen( Scaffold( modifier = Modifier.padding(mainPaddingValues), - topBar = { AppBar(stringResource(Res.string.title_favorites)) } + topBar = { AppBar(title = stringResource(Res.string.title_favorites)) } ) { paddingValues -> Box(modifier = Modifier.fillMaxSize().padding(paddingValues)) { if (favoritesUiState.isLoading) { diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt index dfa09838..28fdd8c7 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt @@ -37,9 +37,11 @@ import com.vickbt.composeApp.ui.components.MovieCardPagerIndicator import com.vickbt.composeApp.ui.components.MovieCardPortraitCompact import com.vickbt.composeApp.ui.components.SectionSeparator import com.vickbt.composeApp.ui.components.appbars.AppBar +import com.vickbt.composeApp.ui.navigation.NavigationItem import com.vickbt.composeApp.ui.theme.DarkPrimaryColor import com.vickbt.composeApp.utils.WindowSize import com.vickbt.shared.resources.Res +import com.vickbt.shared.resources.logo_n import com.vickbt.shared.resources.popular_movies import com.vickbt.shared.resources.trending_movies import com.vickbt.shared.resources.upcoming_movies @@ -65,7 +67,11 @@ fun HomeScreen( modifier = Modifier .fillMaxSize() .padding(mainPaddingValues), - topBar = { AppBar(title = "") } + topBar = { + AppBar( + headerIcon = Res.drawable.logo_n, + onActionClicked = { navigator.navigate(NavigationItem.Search.route) }) + } ) { paddingValues -> // region Home section diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/settings/SettingsScreen.kt index 5dbc5551..9fac7869 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/settings/SettingsScreen.kt @@ -52,7 +52,7 @@ fun SettingsScreen( Scaffold( modifier = Modifier.padding(mainPaddingValues), - topBar = { AppBar(stringResource(Res.string.title_settings)) }, + topBar = { AppBar(title = stringResource(Res.string.title_settings)) }, ) { paddingValues -> Column(modifier = Modifier.padding(paddingValues)) { PreferencesGroup(title = stringResource(Res.string.title_personalisation)) { From e1cd4a09f6e6e2c0131252ba89428042100f9e35 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Sat, 23 Nov 2024 15:33:56 +0300 Subject: [PATCH 09/12] Removed redundant date classes in the domain --- .../composeApp/domain/models/MovieResults.kt | 12 ------------ .../composeApp/domain/models/PopularMovies.kt | 12 ------------ .../composeApp/domain/models/TrendingMovies.kt | 11 ----------- .../composeApp/domain/models/UpcomingMovies.kt | 14 -------------- 4 files changed, 49 deletions(-) delete mode 100644 composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/MovieResults.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/PopularMovies.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/TrendingMovies.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/UpcomingMovies.kt diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/MovieResults.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/MovieResults.kt deleted file mode 100644 index 4c3eac16..00000000 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/MovieResults.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.vickbt.composeApp.domain.models - -data class MovieResults( - - val page: Int? = null, - - val movies: List? = null, - - val totalPages: Int? = null, - - val totalResults: Int? = null -) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/PopularMovies.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/PopularMovies.kt deleted file mode 100644 index 12dc8d66..00000000 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/PopularMovies.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.vickbt.composeApp.domain.models - -data class PopularMovies( - - val page: Int? = null, - - val movies: List? = null, - - val totalPages: Int? = null, - - val totalResults: Int? = null -) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/TrendingMovies.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/TrendingMovies.kt deleted file mode 100644 index a57fb96b..00000000 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/TrendingMovies.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.vickbt.composeApp.domain.models - -data class TrendingMovies( - val page: Int? = null, - - val movies: List? = null, - - val totalPages: Int? = null, - - val totalResults: Int? = null -) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/UpcomingMovies.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/UpcomingMovies.kt deleted file mode 100644 index 6104a2d9..00000000 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/UpcomingMovies.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.vickbt.composeApp.domain.models - -data class UpcomingMovies( - - val dates: Dates? = null, - - val page: Int? = null, - - val movies: List? = null, - - val totalPages: Int? = null, - - val totalResults: Int? = null -) From b8a656daa7ea5bc86c1c1943d5310a012ea27c3c Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Sat, 23 Nov 2024 19:24:03 +0300 Subject: [PATCH 10/12] Displaying top rated tv shows --- .../composeResources/values/strings.xml | 4 + .../data/datasources/TvShowsRepositoryImpl.kt | 86 ++++++++++++++ .../composeApp/data/mappers/DtoToDomain.kt | 20 ++++ .../data/network/models/TvShowDto.kt | 41 +++++++ .../data/network/models/TvShowResultsDto.kt | 19 +++ .../com/vickbt/composeApp/di/CommonModule.kt | 3 + .../vickbt/composeApp/domain/models/TvShow.kt | 17 +++ .../domain/repositories/TVShowsRepository.kt | 33 ++++++ .../vickbt/composeApp/domain/utils/Enums.kt | 5 +- .../composeApp/ui/components/FilterHome.kt | 20 ++-- .../ui/components/MovieCardLandscape.kt | 22 ++-- .../composeApp/ui/screens/home/HomeScreen.kt | 110 +++++++++++++++++- .../ui/screens/home/HomeViewModel.kt | 63 +++++++++- .../com/vickbt/composeApp/utils/UiStates.kt | 6 + 14 files changed, 423 insertions(+), 26 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/TvShowsRepositoryImpl.kt create mode 100644 composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/TvShowDto.kt create mode 100644 composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/TvShowResultsDto.kt create mode 100644 composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/TvShow.kt create mode 100644 composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/TVShowsRepository.kt diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index ab70a03c..82159e82 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -15,6 +15,10 @@ Upcoming Movies View All + Trending TV Shows + Top Rated TV Shows + Popular TV Shows + Share diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/TvShowsRepositoryImpl.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/TvShowsRepositoryImpl.kt new file mode 100644 index 00000000..5e4146ed --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/TvShowsRepositoryImpl.kt @@ -0,0 +1,86 @@ +package com.vickbt.composeApp.data.datasources + +import app.cash.paging.Pager +import app.cash.paging.PagingConfig +import app.cash.paging.PagingData +import com.vickbt.composeApp.data.mappers.toDomain +import com.vickbt.composeApp.data.network.models.TvShowResultsDto +import com.vickbt.composeApp.data.network.utils.safeApiCall +import com.vickbt.composeApp.data.paging.BasePagingSource +import com.vickbt.composeApp.domain.models.TvShow +import com.vickbt.composeApp.domain.repositories.TvShowsRepository +import io.ktor.client.HttpClient +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.request.parameter +import kotlinx.coroutines.flow.Flow + +class TvShowsRepositoryImpl(private val httpClient: HttpClient) : TvShowsRepository { + + private val pagingConfig = PagingConfig(pageSize = 20, enablePlaceholders = false) + + override suspend fun fetchAiringTodayTvShows( + language: String, + sortBy: String + ): Result?>> { + return safeApiCall { + val response = + httpClient.get(urlString = "discover/tv?include_adult=true&language=$language&page=1&sort_by=$sortBy&air_date.lte={max_date}&air_date.gte={min_date}").body() + + response.tvShows?.map { it.toDomain() } + } + } + + override suspend fun fetchTrendingTVShows( + timeWindow: String, + language: String + ): Result>> { + val pagingSource = BasePagingSource { page -> + val response = + httpClient.get(urlString = "trending/tv/$timeWindow?language=$language") { + parameter("page", page) + }.body().tvShows + + response?.map { it.toDomain() } + } + + return runCatching { + Pager(config = pagingConfig, pagingSourceFactory = { pagingSource }).flow + } + } + + override suspend fun fetchTopRatedTvShows( + language: String, + sortBy: String, + voteCount: String + ): Result>> { + val pagingSource = BasePagingSource { page -> + val response = + httpClient.get(urlString = "discover/tv?include_adult=true&language=$language&page=$page&sort_by=$sortBy&vote_count.gte=$voteCount") + .body().tvShows + + response?.map { it.toDomain() } + } + + return runCatching { + Pager(config = pagingConfig, pagingSourceFactory = { pagingSource }).flow + } + } + + override suspend fun fetchPopularTvShows( + language: String, + sortBy: String + ): Result>> { + val pagingSource = BasePagingSource { page -> + val response = + httpClient.get(urlString = "discover/tv?include_adult=true&language=$language&page=$page&sort_by=$sortBy") + .body().tvShows + + response?.map { it.toDomain() } + } + + return runCatching { + Pager(config = pagingConfig, pagingSourceFactory = { pagingSource }).flow + } + } +} diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/mappers/DtoToDomain.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/mappers/DtoToDomain.kt index a21ac7e2..32904668 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/mappers/DtoToDomain.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/mappers/DtoToDomain.kt @@ -5,12 +5,14 @@ import com.vickbt.composeApp.data.network.models.CastDto import com.vickbt.composeApp.data.network.models.ErrorResponseDto import com.vickbt.composeApp.data.network.models.MovieDetailsDto import com.vickbt.composeApp.data.network.models.MovieDto +import com.vickbt.composeApp.data.network.models.TvShowDto import com.vickbt.composeApp.data.network.models.VideoDto import com.vickbt.composeApp.domain.models.Actor import com.vickbt.composeApp.domain.models.Cast import com.vickbt.composeApp.domain.models.ErrorResponse import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.models.MovieDetails +import com.vickbt.composeApp.domain.models.TvShow import com.vickbt.composeApp.domain.models.Video fun MovieDto.toDomain(): Movie { @@ -32,6 +34,24 @@ fun MovieDto.toDomain(): Movie { ) } +fun TvShowDto.toDomain(): TvShow { + return TvShow( + adult = this.adult, + backdropPath = this.backdropPath, + id = this.id, + name = this.name, + overview = this.overview, + posterPath = this.posterPath, + mediaType = this.mediaType, + genreIds = this.genreIds, + popularity = this.popularity, + firstAirDate = this.firstAirDate, + voteAverage = this.voteAverage, + voteCount = this.voteCount, + originCountry = this.originCountry + ) +} + fun MovieDetailsDto.toDomain(): MovieDetails { return MovieDetails( adult = this.adult, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/TvShowDto.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/TvShowDto.kt new file mode 100644 index 00000000..96f55757 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/TvShowDto.kt @@ -0,0 +1,41 @@ +package com.vickbt.composeApp.data.network.models + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TvShowDto( + val adult: Boolean? = null, + + @SerialName("backdrop_path") + val backdropPath: String? = null, + + val id: Int, + + val name: String? = null, + + val overview: String? = null, + + @SerialName("poster_path") + val posterPath: String? = null, + + @SerialName("media_type") + val mediaType: String? = null, + + @SerialName("genre_ids") + val genreIds: List? = null, + + val popularity: Double? = null, + + @SerialName("first_air_date") + val firstAirDate: String? = null, + + @SerialName("vote_average") + val voteAverage: Double? = null, + + @SerialName("vote_count") + val voteCount: Int? = null, + + @SerialName("origin_country") + val originCountry: List? = null, +) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/TvShowResultsDto.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/TvShowResultsDto.kt new file mode 100644 index 00000000..caeb1d58 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/TvShowResultsDto.kt @@ -0,0 +1,19 @@ +package com.vickbt.composeApp.data.network.models + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TvShowResultsDto( + @SerialName("page") + val page: Int? = null, + + @SerialName("results") + val tvShows: List? = null, + + @SerialName("total_pages") + val totalPages: Int? = null, + + @SerialName("total_results") + val totalResults: Int? = null +) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/di/CommonModule.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/di/CommonModule.kt index 7b8e517b..ac2d5ed2 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/di/CommonModule.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/di/CommonModule.kt @@ -5,11 +5,13 @@ import com.vickbt.composeApp.data.datasources.MovieDetailsRepositoryImpl import com.vickbt.composeApp.data.datasources.MoviesRepositoryImpl import com.vickbt.composeApp.data.datasources.SearchRepositoryImpl import com.vickbt.composeApp.data.datasources.SettingsRepositoryImpl +import com.vickbt.composeApp.data.datasources.TvShowsRepositoryImpl import com.vickbt.composeApp.domain.repositories.FavoritesRepository import com.vickbt.composeApp.domain.repositories.MovieDetailsRepository import com.vickbt.composeApp.domain.repositories.MoviesRepository import com.vickbt.composeApp.domain.repositories.SearchRepository import com.vickbt.composeApp.domain.repositories.SettingsRepository +import com.vickbt.composeApp.domain.repositories.TvShowsRepository import com.vickbt.composeApp.domain.utils.Constants.BASE_URL import com.vickbt.composeApp.domain.utils.Constants.URL_PATH import com.vickbt.composeApp.ui.screens.details.DetailsViewModel @@ -80,6 +82,7 @@ fun commonModule(enableNetworkLogs: Boolean) = module { } single { MoviesRepositoryImpl(httpClient = get()) } + single { TvShowsRepositoryImpl(httpClient = get()) } single { MovieDetailsRepositoryImpl(httpClient = get(), appDatabase = get()) } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/TvShow.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/TvShow.kt new file mode 100644 index 00000000..a85cd082 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/TvShow.kt @@ -0,0 +1,17 @@ +package com.vickbt.composeApp.domain.models + +data class TvShow( + val adult: Boolean? = null, + val backdropPath: String? = null, + val id: Int, + val name: String? = null, + val overview: String? = null, + val posterPath: String? = null, + val mediaType: String? = null, + val genreIds: List? = null, + val popularity: Double? = null, + val firstAirDate: String? = null, + val voteAverage: Double? = null, + val voteCount: Int? = null, + val originCountry: List? = null, +) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/TVShowsRepository.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/TVShowsRepository.kt new file mode 100644 index 00000000..7694e61e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/TVShowsRepository.kt @@ -0,0 +1,33 @@ +package com.vickbt.composeApp.domain.repositories + +import app.cash.paging.PagingData +import com.vickbt.composeApp.domain.models.TvShow +import kotlinx.coroutines.flow.Flow + +interface TvShowsRepository { + + /** Fetch Tv Shows airing today from data source*/ + suspend fun fetchAiringTodayTvShows( + language: String = "en-US", + sortBy: String = "popularity.desc" + ): Result?>> + + /** Fetch trending Tv Shows from data source*/ + suspend fun fetchTrendingTVShows( + timeWindow: String = "week", + language: String = "en-US" + ): Result>> + + /** Fetch top rated Tv Shows from data source*/ + suspend fun fetchTopRatedTvShows( + language: String = "en-US", + sortBy: String = "vote_average.desc", + voteCount: String = "200" + ): Result>> + + /** Fetch popular Tv Shows from data source*/ + suspend fun fetchPopularTvShows( + language: String = "en-US", + sortBy: String = "popularity.desc" + ): Result>> +} diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/utils/Enums.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/utils/Enums.kt index aeb8fc2e..edda16eb 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/utils/Enums.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/utils/Enums.kt @@ -1,11 +1,8 @@ package com.vickbt.composeApp.domain.utils object Enums { - enum class MovieCategories { - NOW_PLAYING, POPULAR, TRENDING, UPCOMING - } - enum class ShowType { + enum class MediaType { TV_SHOW, MOVIE } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt index 964f4c83..940cd5ca 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt @@ -42,25 +42,25 @@ import org.jetbrains.compose.resources.stringResource @Composable fun FilterHome( modifier: Modifier = Modifier, - onFilterClicked: (Enums.ShowType?) -> Unit = {}, + onFilterClicked: (Enums.MediaType?) -> Unit = {}, onCategoriesClicked: () -> Unit = {} ) { - var selectedShowType by remember { mutableStateOf(null) } + var selectedMediaType by remember { mutableStateOf(null) } Row( modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start), verticalAlignment = Alignment.CenterVertically ) { - AnimatedVisibility(visible = selectedShowType != null, enter = fadeIn(), exit = fadeOut()) { + AnimatedVisibility(visible = selectedMediaType != null, enter = fadeIn(), exit = fadeOut()) { FloatingActionButton( modifier = Modifier.border(1.dp, MaterialTheme.colorScheme.onBackground, CircleShape) .size(32.dp), shape = CircleShape, containerColor = Color.Transparent, contentColor = MaterialTheme.colorScheme.onBackground, - onClick = { selectedShowType = null }) { + onClick = { selectedMediaType = null }) { Icon( modifier = Modifier.size(18.dp), imageVector = Icons.Rounded.Close, @@ -70,11 +70,11 @@ fun FilterHome( } } - AnimatedVisibility(visible = (selectedShowType == null) || selectedShowType == Enums.ShowType.TV_SHOW) { + AnimatedVisibility(visible = (selectedMediaType == null) || selectedMediaType == Enums.MediaType.TV_SHOW) { SuggestionChip( onClick = { - selectedShowType = Enums.ShowType.TV_SHOW - onFilterClicked(selectedShowType) + selectedMediaType = Enums.MediaType.TV_SHOW + onFilterClicked(selectedMediaType) }, label = { Text( @@ -92,11 +92,11 @@ fun FilterHome( ) } - AnimatedVisibility(visible = (selectedShowType == null) || selectedShowType == Enums.ShowType.MOVIE) { + AnimatedVisibility(visible = (selectedMediaType == null) || selectedMediaType == Enums.MediaType.MOVIE) { SuggestionChip( onClick = { - selectedShowType = Enums.ShowType.MOVIE - onFilterClicked(selectedShowType) + selectedMediaType = Enums.MediaType.MOVIE + onFilterClicked(selectedMediaType) }, label = { Text( diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt index 821c21d3..c86f312b 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt @@ -50,9 +50,13 @@ import org.jetbrains.compose.resources.stringResource @Composable fun MovieCardLandscape( modifier: Modifier = Modifier, - movie: Movie, + movieId:Int, + backdropPath: String? = null, + title:String?=null, + voteAverage:Double?=null, + releaseDate:String?=null, networkLoader: NetworkLoader = rememberNetworkLoader(), - onClickItem: (Movie) -> Unit + onClickItem: (Int) -> Unit ) { val dominantColorState = rememberDominantColorState( loader = networkLoader, @@ -61,14 +65,14 @@ fun MovieCardLandscape( coroutineContext = Dispatchers.IO ) - movie.backdropPath?.loadImage()?.let { + backdropPath?.loadImage()?.let { LaunchedEffect(it) { dominantColorState.updateFrom(Url(it)) } } Card( - modifier = modifier.clickable { onClickItem(movie) }, + modifier = modifier.clickable { onClickItem(movieId) }, elevation = CardDefaults.cardElevation(8.dp), shape = RoundedCornerShape(4.dp) ) { @@ -77,7 +81,7 @@ fun MovieCardLandscape( modifier = Modifier .fillMaxSize() .align(Alignment.Center), - model = movie.backdropPath?.loadImage(), + model = backdropPath?.loadImage(), contentDescription = null, contentScale = ContentScale.Crop, alignment = Alignment.Center, @@ -110,7 +114,7 @@ fun MovieCardLandscape( //region Movie Title Text( modifier = Modifier, - text = movie.title ?: stringResource(Res.string.unknown_movie), + text = title ?: stringResource(Res.string.unknown_movie), maxLines = 2, style = MaterialTheme.typography.headlineLarge, overflow = TextOverflow.Ellipsis, @@ -129,7 +133,7 @@ fun MovieCardLandscape( ) { RatingBar( modifier = Modifier, - value = movie.voteAverage?.getRating()?.toFloat() ?: 0f, + value = voteAverage?.getRating()?.toFloat() ?: 0f, numOfStars = 5, size = 15.dp, stepSize = StepSize.HALF, @@ -137,7 +141,7 @@ fun MovieCardLandscape( style = RatingBarStyle.Fill() ) - movie.releaseDate?.let { + releaseDate?.let { HorizontalDivider( modifier = Modifier .padding(horizontal = 4.dp) @@ -148,7 +152,7 @@ fun MovieCardLandscape( Text( modifier = Modifier, - text = movie.releaseDate.getReleaseDate().capitalizeEachWord(), + text = releaseDate.getReleaseDate().capitalizeEachWord(), maxLines = 1, style = MaterialTheme.typography.bodyMedium, overflow = TextOverflow.Ellipsis, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt index 28fdd8c7..7a8268fa 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt @@ -43,6 +43,7 @@ import com.vickbt.composeApp.utils.WindowSize import com.vickbt.shared.resources.Res import com.vickbt.shared.resources.logo_n import com.vickbt.shared.resources.popular_movies +import com.vickbt.shared.resources.top_rated_tv_shows import com.vickbt.shared.resources.trending_movies import com.vickbt.shared.resources.upcoming_movies import org.jetbrains.compose.resources.stringResource @@ -163,7 +164,45 @@ fun HomeScreen( } //endregion - //region Upcoming Movies + //region Top Rated Tv Shows + homeUiState.topRatedTvShows?.let { movies -> + val topRatedTvShows = movies.collectAsLazyPagingItems() + + SectionSeparator( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + sectionTitle = stringResource(Res.string.top_rated_tv_shows) + ) + + LazyRow( + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(14.dp), + modifier = Modifier.wrapContentHeight() + ) { + items(topRatedTvShows.itemCount) { index -> + topRatedTvShows[index]?.let { + MovieCardLandscape( + modifier = Modifier + .width(300.dp) + .height(245.dp), + movieId = it.id, + backdropPath = it.backdropPath, + title = it.name, + voteAverage = it.voteAverage, + releaseDate = it.firstAirDate, + networkLoader = networkLoader, + onClickItem = { id -> + navigator.navigate("/details/${id}") + } + ) + } + } + } + } + //endregion + + /*//region Upcoming Movies homeUiState.upcomingMovies?.let { movies -> val upcomingMovies = movies.collectAsLazyPagingItems() @@ -195,7 +234,39 @@ fun HomeScreen( } } } - //endregion + //endregion*/ + + /*//region Trending Tv Shows + homeUiState.trendingTvShows?.let { movies -> + val trendingMovies = movies.collectAsLazyPagingItems() + + SectionSeparator( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + sectionTitle = stringResource(Res.string.trending_movies) + ) + + LazyRow( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + items(trendingMovies.itemCount) { index -> + trendingMovies[index]?.let { + MovieCardPortraitCompact( + movie = it, + onItemClick = { movie -> + navigator.navigate("/details/${movie.id}") + } + ) + } + } + } + } + //endregion*/ //region Popular Movies homeUiState.popularMovies?.let { movies -> @@ -228,6 +299,41 @@ fun HomeScreen( } //endregion } + //endregion + + /*//region Popular Tv Shows + homeUiState.upcomingMovies?.let { movies -> + val upcomingMovies = movies.collectAsLazyPagingItems() + + SectionSeparator( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + sectionTitle = stringResource(Res.string.upcoming_movies) + ) + + LazyRow( + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(14.dp), + modifier = Modifier.wrapContentHeight() + ) { + items(upcomingMovies.itemCount) { index -> + upcomingMovies[index]?.let { + MovieCardLandscape( + modifier = Modifier + .width(300.dp) + .height(245.dp), + movie = it, + networkLoader = networkLoader, + onClickItem = { movie -> + navigator.navigate("/details/${movie.id}") + } + ) + } + } + } + } + //endregion*/ } } // endregion diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt index 4fcd87c7..ffbd8c5f 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn import com.vickbt.composeApp.domain.repositories.MoviesRepository +import com.vickbt.composeApp.domain.repositories.TvShowsRepository import com.vickbt.composeApp.utils.HomeUiState import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.MutableStateFlow @@ -12,7 +13,10 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -class HomeViewModel(private val moviesRepository: MoviesRepository) : ViewModel() { +class HomeViewModel( + private val moviesRepository: MoviesRepository, + private val tvShowsRepository: TvShowsRepository +) : ViewModel() { private val _homeUiState = MutableStateFlow(HomeUiState(isLoading = true)) val homeUiState = _homeUiState.asStateFlow() @@ -26,6 +30,11 @@ class HomeViewModel(private val moviesRepository: MoviesRepository) : ViewModel( fetchTrendingMovies() fetchUpcomingMovies() fetchPopularMovies() + + fetchAiringTodayTvShows() + fetchTrendingTvShows() + fetchTopRatedTvShows() + fetchPopularTvShows() } fun fetchNowPlayingMovies() = viewModelScope.launch(coroutineExceptionHandler) { @@ -81,4 +90,56 @@ class HomeViewModel(private val moviesRepository: MoviesRepository) : ViewModel( _homeUiState.update { it.copy(error = error.message, isLoading = false) } } } + + fun fetchAiringTodayTvShows() = viewModelScope.launch(coroutineExceptionHandler) { + tvShowsRepository.fetchAiringTodayTvShows().onSuccess { data -> + data.collectLatest { tvShows -> + _homeUiState.update { + it.copy(airingTodayTvShows = tvShows?.take(5), isLoading = false) + } + } + }.onFailure { error -> + _homeUiState.update { it.copy(error = error.message, isLoading = false) } + } + } + + fun fetchTrendingTvShows() = viewModelScope.launch(coroutineExceptionHandler) { + tvShowsRepository.fetchTrendingTVShows().onSuccess { data -> + _homeUiState.update { + it.copy( + trendingTvShows = data.cachedIn(viewModelScope), + isLoading = false + ) + } + }.onFailure { error -> + _homeUiState.update { it.copy(error = error.message, isLoading = false) } + } + } + + fun fetchTopRatedTvShows() = viewModelScope.launch(coroutineExceptionHandler) { + tvShowsRepository.fetchTopRatedTvShows().onSuccess { data -> + _homeUiState.update { + it.copy( + topRatedTvShows = data.cachedIn(viewModelScope), + isLoading = false + ) + } + }.onFailure { error -> + _homeUiState.update { it.copy(error = error.message, isLoading = false) } + } + } + + fun fetchPopularTvShows() = viewModelScope.launch(coroutineExceptionHandler) { + tvShowsRepository.fetchPopularTvShows().onSuccess { data -> + _homeUiState.update { + it.copy( + popularTvShows = data.cachedIn(viewModelScope), + isLoading = false + ) + } + }.onFailure { error -> + _homeUiState.update { it.copy(error = error.message, isLoading = false) } + } + } + } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt index 8a14d4db..b5a1ed4e 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt @@ -4,6 +4,7 @@ import androidx.paging.PagingData import com.vickbt.composeApp.domain.models.Actor import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.models.MovieDetails +import com.vickbt.composeApp.domain.models.TvShow import kotlinx.coroutines.flow.Flow data class MainUiState( @@ -17,6 +18,11 @@ data class HomeUiState( val trendingMovies: Flow>? = null, val popularMovies: Flow>? = null, val upcomingMovies: Flow>? = null, + + val airingTodayTvShows: List? = null, + val trendingTvShows: Flow>? = null, + val topRatedTvShows: Flow>? = null, + val popularTvShows: Flow>? = null ) data class DetailsUiState( From a759c11bd4980135c69072465be05b786a7a2f59 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Sat, 23 Nov 2024 20:17:47 +0300 Subject: [PATCH 11/12] Fetching and displaying tv shows --- .../composeApp/ui/components/FilterHome.kt | 18 +- .../ui/components/MovieCardPortrait.kt | 4 +- .../ui/components/MovieCardPortraitCompact.kt | 13 +- .../ui/components/SectionSeparator.kt | 4 +- .../composeApp/ui/screens/home/HomeScreen.kt | 350 ++++++++++-------- 5 files changed, 226 insertions(+), 163 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt index 940cd5ca..2f0c6a3b 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt @@ -43,6 +43,7 @@ import org.jetbrains.compose.resources.stringResource fun FilterHome( modifier: Modifier = Modifier, onFilterClicked: (Enums.MediaType?) -> Unit = {}, + onFilterClosed: () -> Unit = {}, onCategoriesClicked: () -> Unit = {} ) { @@ -53,14 +54,25 @@ fun FilterHome( horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start), verticalAlignment = Alignment.CenterVertically ) { - AnimatedVisibility(visible = selectedMediaType != null, enter = fadeIn(), exit = fadeOut()) { + AnimatedVisibility( + visible = selectedMediaType != null, + enter = fadeIn(), + exit = fadeOut() + ) { FloatingActionButton( - modifier = Modifier.border(1.dp, MaterialTheme.colorScheme.onBackground, CircleShape) + modifier = Modifier.border( + 1.dp, + MaterialTheme.colorScheme.onBackground, + CircleShape + ) .size(32.dp), shape = CircleShape, containerColor = Color.Transparent, contentColor = MaterialTheme.colorScheme.onBackground, - onClick = { selectedMediaType = null }) { + onClick = { + selectedMediaType = null + onFilterClosed() + }) { Icon( modifier = Modifier.size(18.dp), imageVector = Icons.Rounded.Close, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortrait.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortrait.kt index bbf32a7b..147d430d 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortrait.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortrait.kt @@ -16,7 +16,9 @@ import com.vickbt.composeApp.utils.getRating fun MovieCardPortrait(modifier: Modifier = Modifier, movie: Movie, onItemClick: (Movie) -> Unit) { Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(1.dp)) { MovieCardPortraitCompact( - movie = movie, + movieId = movie.id, + posterPath = movie.posterPath, + title = movie.title, onItemClick = { onItemClick(movie) } ) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt index 5494ce91..5a99d8a7 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt @@ -22,7 +22,6 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil3.compose.AsyncImage -import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.utils.loadImage import com.vickbt.shared.resources.Res import com.vickbt.shared.resources.unknown_movie @@ -31,8 +30,10 @@ import org.jetbrains.compose.resources.stringResource @Composable fun MovieCardPortraitCompact( modifier: Modifier = Modifier, - movie: Movie, - onItemClick: (Movie) -> Unit + movieId: Int, + posterPath: String? = null, + title: String? = null, + onItemClick: (Int) -> Unit ) { Column( modifier = modifier, @@ -42,7 +43,7 @@ fun MovieCardPortraitCompact( modifier = Modifier .width(150.dp) .fillMaxHeight() - .clickable { onItemClick(movie) }, + .clickable { onItemClick(movieId) }, elevation = CardDefaults.cardElevation(8.dp), shape = RoundedCornerShape(4.dp) ) { @@ -51,7 +52,7 @@ fun MovieCardPortraitCompact( .fillMaxWidth() .height(220.dp) .sizeIn(minHeight = 30.dp), - model = movie.posterPath?.loadImage(), + model = posterPath?.loadImage(), contentDescription = "Trending movie poster", contentScale = ContentScale.Crop, alignment = Alignment.Center, @@ -60,7 +61,7 @@ fun MovieCardPortraitCompact( Text( modifier = Modifier.width(145.dp), - text = movie.title ?: stringResource(Res.string.unknown_movie), + text = title ?: stringResource(Res.string.unknown_movie), style = MaterialTheme.typography.headlineSmall, color = MaterialTheme.colorScheme.onSurface, fontSize = 14.sp, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/SectionSeparator.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/SectionSeparator.kt index 2560f6fa..187721b8 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/SectionSeparator.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/SectionSeparator.kt @@ -17,12 +17,12 @@ fun SectionSeparator( sectionTitle: String ) { Row( - modifier = modifier.padding(horizontal = 16.dp, vertical = 4.dp), + modifier = modifier, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - modifier = Modifier, + modifier = Modifier.padding(all = 16.dp), text = sectionTitle, color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.headlineMedium, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt index 7a8268fa..46f7b318 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt @@ -1,5 +1,6 @@ package com.vickbt.composeApp.ui.screens.home +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -22,6 +23,10 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +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.graphics.Color @@ -30,6 +35,7 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import app.cash.paging.compose.collectAsLazyPagingItems import com.kmpalette.loader.rememberNetworkLoader +import com.vickbt.composeApp.domain.utils.Enums import com.vickbt.composeApp.ui.components.FilterHome import com.vickbt.composeApp.ui.components.MovieCardLandscape import com.vickbt.composeApp.ui.components.MovieCardPager @@ -45,6 +51,7 @@ import com.vickbt.shared.resources.logo_n import com.vickbt.shared.resources.popular_movies import com.vickbt.shared.resources.top_rated_tv_shows import com.vickbt.shared.resources.trending_movies +import com.vickbt.shared.resources.trending_tv_shows import com.vickbt.shared.resources.upcoming_movies import org.jetbrains.compose.resources.stringResource import org.koin.compose.koinInject @@ -64,6 +71,8 @@ fun HomeScreen( val networkLoader = rememberNetworkLoader(httpClient = koinInject()) + var mediaTypeSelected by remember { mutableStateOf(null) } + Scaffold( modifier = Modifier .fillMaxSize() @@ -99,7 +108,11 @@ fun HomeScreen( horizontalAlignment = Alignment.CenterHorizontally ) { //region Home Filters - FilterHome(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) + FilterHome( + modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), + onFilterClicked = { mediaTypeSelected = it }, + onFilterClosed = { mediaTypeSelected = null } + ) //endregion //region Now Playing Movies @@ -114,7 +127,7 @@ fun HomeScreen( pageSpacing = 8.dp ) { currentPage -> MovieCardPager( - modifier = Modifier.fillMaxWidth().height(280.dp), + modifier = Modifier.fillMaxWidth().height(420.dp), networkLoader = networkLoader, movie = nowPlayingMovies[currentPage] ) { movie -> @@ -133,31 +146,37 @@ fun HomeScreen( //endregion //region Trending Movies - homeUiState.trendingMovies?.let { movies -> - val trendingMovies = movies.collectAsLazyPagingItems() + AnimatedVisibility(visible = mediaTypeSelected == null || mediaTypeSelected == Enums.MediaType.MOVIE) { + homeUiState.trendingMovies?.let { movies -> + val trendingMovies = movies.collectAsLazyPagingItems() - SectionSeparator( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - sectionTitle = stringResource(Res.string.trending_movies) - ) + Column { + SectionSeparator( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + sectionTitle = stringResource(Res.string.trending_movies) + ) - LazyRow( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - contentPadding = PaddingValues(horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(10.dp) - ) { - items(trendingMovies.itemCount) { index -> - trendingMovies[index]?.let { - MovieCardPortraitCompact( - movie = it, - onItemClick = { movie -> - navigator.navigate("/details/${movie.id}") + LazyRow( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + items(trendingMovies.itemCount) { index -> + trendingMovies[index]?.let { + MovieCardPortraitCompact( + movieId = it.id, + posterPath = it.posterPath, + title = it.title, + onItemClick = { id -> + navigator.navigate("/details/${id}") + } + ) } - ) + } } } } @@ -165,175 +184,204 @@ fun HomeScreen( //endregion //region Top Rated Tv Shows - homeUiState.topRatedTvShows?.let { movies -> - val topRatedTvShows = movies.collectAsLazyPagingItems() + AnimatedVisibility(visible = mediaTypeSelected == null || mediaTypeSelected == Enums.MediaType.TV_SHOW) { + homeUiState.topRatedTvShows?.let { movies -> + val topRatedTvShows = movies.collectAsLazyPagingItems() - SectionSeparator( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - sectionTitle = stringResource(Res.string.top_rated_tv_shows) - ) + Column { + SectionSeparator( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + sectionTitle = stringResource(Res.string.top_rated_tv_shows) + ) - LazyRow( - contentPadding = PaddingValues(horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(14.dp), - modifier = Modifier.wrapContentHeight() - ) { - items(topRatedTvShows.itemCount) { index -> - topRatedTvShows[index]?.let { - MovieCardLandscape( - modifier = Modifier - .width(300.dp) - .height(245.dp), - movieId = it.id, - backdropPath = it.backdropPath, - title = it.name, - voteAverage = it.voteAverage, - releaseDate = it.firstAirDate, - networkLoader = networkLoader, - onClickItem = { id -> - navigator.navigate("/details/${id}") + LazyRow( + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(14.dp), + modifier = Modifier.wrapContentHeight() + ) { + items(topRatedTvShows.itemCount) { index -> + topRatedTvShows[index]?.let { + MovieCardLandscape( + modifier = Modifier + .width(300.dp) + .height(245.dp), + movieId = it.id, + backdropPath = it.backdropPath, + title = it.name, + voteAverage = it.voteAverage, + releaseDate = it.firstAirDate, + networkLoader = networkLoader, + onClickItem = { id -> + navigator.navigate("/details/${id}") + } + ) } - ) + } } } } } //endregion - /*//region Upcoming Movies - homeUiState.upcomingMovies?.let { movies -> - val upcomingMovies = movies.collectAsLazyPagingItems() + //region Upcoming Movies + AnimatedVisibility(visible = mediaTypeSelected == null || mediaTypeSelected == Enums.MediaType.MOVIE) { + homeUiState.upcomingMovies?.let { movies -> + val upcomingMovies = movies.collectAsLazyPagingItems() - SectionSeparator( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - sectionTitle = stringResource(Res.string.upcoming_movies) - ) + Column(modifier = Modifier) { + SectionSeparator( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + sectionTitle = stringResource(Res.string.upcoming_movies) + ) - LazyRow( - contentPadding = PaddingValues(horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(14.dp), - modifier = Modifier.wrapContentHeight() - ) { - items(upcomingMovies.itemCount) { index -> - upcomingMovies[index]?.let { - MovieCardLandscape( - modifier = Modifier - .width(300.dp) - .height(245.dp), - movie = it, - networkLoader = networkLoader, - onClickItem = { movie -> - navigator.navigate("/details/${movie.id}") + LazyRow( + modifier = Modifier.wrapContentHeight(), + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp), + ) { + items(upcomingMovies.itemCount) { index -> + upcomingMovies[index]?.let { + MovieCardPortraitCompact( + movieId = it.id, + posterPath = it.posterPath, + title = it.title, + onItemClick = { id -> + navigator.navigate("/details/${id}") + } + ) } - ) + } } } } } - //endregion*/ + //endregion - /*//region Trending Tv Shows - homeUiState.trendingTvShows?.let { movies -> - val trendingMovies = movies.collectAsLazyPagingItems() + //region Trending Tv Shows + AnimatedVisibility(visible = mediaTypeSelected == null || mediaTypeSelected == Enums.MediaType.TV_SHOW) { + homeUiState.trendingTvShows?.let { tvShows -> + val trendingTvShows = tvShows.collectAsLazyPagingItems() - SectionSeparator( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - sectionTitle = stringResource(Res.string.trending_movies) - ) + Column { + SectionSeparator( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + sectionTitle = stringResource(Res.string.trending_tv_shows) + ) - LazyRow( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - contentPadding = PaddingValues(horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(10.dp) - ) { - items(trendingMovies.itemCount) { index -> - trendingMovies[index]?.let { - MovieCardPortraitCompact( - movie = it, - onItemClick = { movie -> - navigator.navigate("/details/${movie.id}") + LazyRow( + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(14.dp), + modifier = Modifier.wrapContentHeight() + ) { + items(trendingTvShows.itemCount) { index -> + trendingTvShows[index]?.let { + MovieCardLandscape( + modifier = Modifier + .width(300.dp) + .height(245.dp), + movieId = it.id, + backdropPath = it.backdropPath, + title = it.name, + voteAverage = it.voteAverage, + releaseDate = it.firstAirDate, + networkLoader = networkLoader, + onClickItem = { id -> + navigator.navigate("/details/${id}") + } + ) } - ) + } } } } } - //endregion*/ + //endregion //region Popular Movies - homeUiState.popularMovies?.let { movies -> - val popularMovies = movies.collectAsLazyPagingItems() + AnimatedVisibility(visible = mediaTypeSelected == null || mediaTypeSelected == Enums.MediaType.MOVIE) { + homeUiState.popularMovies?.let { movies -> + val popularMovies = movies.collectAsLazyPagingItems() - Column(modifier = Modifier.padding(bottom = 90.dp)) { - SectionSeparator( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - sectionTitle = stringResource(Res.string.popular_movies) - ) + Column { + SectionSeparator( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + sectionTitle = stringResource(Res.string.popular_movies) + ) - LazyRow( - modifier = Modifier.wrapContentHeight(), - contentPadding = PaddingValues(horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(10.dp), - ) { - items(popularMovies.itemCount) { index -> - popularMovies[index]?.let { - MovieCardPortraitCompact( - movie = it, - onItemClick = { movie -> - navigator.navigate("/details/${movie.id}") - } - ) + LazyRow( + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(14.dp), + modifier = Modifier.wrapContentHeight() + ) { + items(popularMovies.itemCount) { index -> + popularMovies[index]?.let { + MovieCardLandscape( + modifier = Modifier + .width(300.dp) + .height(245.dp), + movieId = it.id, + backdropPath = it.backdropPath, + title = it.title, + voteAverage = it.voteAverage, + releaseDate = it.releaseDate, + networkLoader = networkLoader, + onClickItem = { id -> + navigator.navigate("/details/${id}") + } + ) + } } } } } - //endregion } //endregion - /*//region Popular Tv Shows - homeUiState.upcomingMovies?.let { movies -> - val upcomingMovies = movies.collectAsLazyPagingItems() + //region Popular Tv Shows + AnimatedVisibility(visible = mediaTypeSelected == null || mediaTypeSelected == Enums.MediaType.TV_SHOW) { + homeUiState.popularTvShows?.let { tvShows -> + val popularTvShows = tvShows.collectAsLazyPagingItems() - SectionSeparator( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - sectionTitle = stringResource(Res.string.upcoming_movies) - ) + Column { + SectionSeparator( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + sectionTitle = stringResource(Res.string.trending_tv_shows) + ) - LazyRow( - contentPadding = PaddingValues(horizontal = 16.dp), - horizontalArrangement = Arrangement.spacedBy(14.dp), - modifier = Modifier.wrapContentHeight() - ) { - items(upcomingMovies.itemCount) { index -> - upcomingMovies[index]?.let { - MovieCardLandscape( - modifier = Modifier - .width(300.dp) - .height(245.dp), - movie = it, - networkLoader = networkLoader, - onClickItem = { movie -> - navigator.navigate("/details/${movie.id}") + LazyRow( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + items(popularTvShows.itemCount) { index -> + popularTvShows[index]?.let { + MovieCardPortraitCompact( + movieId = it.id, + posterPath = it.posterPath, + title = it.name, + onItemClick = { id -> + navigator.navigate("/details/${id}") + } + ) } - ) + } } } } } - //endregion*/ + //endregion } } // endregion From 776522b408f5227e7b8d2da1fa31f6eb35ebef7e Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Sat, 23 Nov 2024 20:25:56 +0300 Subject: [PATCH 12/12] Linting --- ...{TVShowsRepository.kt => TvShowsRepository.kt} | 1 - .../vickbt/composeApp/ui/components/FilterHome.kt | 4 ++-- .../ui/components/MovieCardLandscape.kt | 9 ++++----- .../composeApp/ui/screens/home/HomeScreen.kt | 15 ++++++++------- .../composeApp/ui/screens/home/HomeViewModel.kt | 1 - 5 files changed, 14 insertions(+), 16 deletions(-) rename composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/{TVShowsRepository.kt => TvShowsRepository.kt} (99%) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/TVShowsRepository.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/TvShowsRepository.kt similarity index 99% rename from composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/TVShowsRepository.kt rename to composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/TvShowsRepository.kt index 7694e61e..ded82cfe 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/TVShowsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/TvShowsRepository.kt @@ -5,7 +5,6 @@ import com.vickbt.composeApp.domain.models.TvShow import kotlinx.coroutines.flow.Flow interface TvShowsRepository { - /** Fetch Tv Shows airing today from data source*/ suspend fun fetchAiringTodayTvShows( language: String = "en-US", diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt index 2f0c6a3b..ea16f483 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/FilterHome.kt @@ -46,7 +46,6 @@ fun FilterHome( onFilterClosed: () -> Unit = {}, onCategoriesClicked: () -> Unit = {} ) { - var selectedMediaType by remember { mutableStateOf(null) } Row( @@ -72,7 +71,8 @@ fun FilterHome( onClick = { selectedMediaType = null onFilterClosed() - }) { + } + ) { Icon( modifier = Modifier.size(18.dp), imageVector = Icons.Rounded.Close, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt index c86f312b..4f8b9a4b 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt @@ -32,7 +32,6 @@ import coil3.compose.AsyncImage import com.kmpalette.loader.NetworkLoader import com.kmpalette.loader.rememberNetworkLoader import com.kmpalette.rememberDominantColorState -import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.ui.components.ratingbar.RatingBar import com.vickbt.composeApp.ui.components.ratingbar.RatingBarStyle import com.vickbt.composeApp.ui.components.ratingbar.StepSize @@ -50,11 +49,11 @@ import org.jetbrains.compose.resources.stringResource @Composable fun MovieCardLandscape( modifier: Modifier = Modifier, - movieId:Int, + movieId: Int, backdropPath: String? = null, - title:String?=null, - voteAverage:Double?=null, - releaseDate:String?=null, + title: String? = null, + voteAverage: Double? = null, + releaseDate: String? = null, networkLoader: NetworkLoader = rememberNetworkLoader(), onClickItem: (Int) -> Unit ) { diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt index 46f7b318..9c765c58 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt @@ -80,7 +80,8 @@ fun HomeScreen( topBar = { AppBar( headerIcon = Res.drawable.logo_n, - onActionClicked = { navigator.navigate(NavigationItem.Search.route) }) + onActionClicked = { navigator.navigate(NavigationItem.Search.route) } + ) } ) { paddingValues -> @@ -172,7 +173,7 @@ fun HomeScreen( posterPath = it.posterPath, title = it.title, onItemClick = { id -> - navigator.navigate("/details/${id}") + navigator.navigate("/details/$id") } ) } @@ -214,7 +215,7 @@ fun HomeScreen( releaseDate = it.firstAirDate, networkLoader = networkLoader, onClickItem = { id -> - navigator.navigate("/details/${id}") + navigator.navigate("/details/$id") } ) } @@ -250,7 +251,7 @@ fun HomeScreen( posterPath = it.posterPath, title = it.title, onItemClick = { id -> - navigator.navigate("/details/${id}") + navigator.navigate("/details/$id") } ) } @@ -292,7 +293,7 @@ fun HomeScreen( releaseDate = it.firstAirDate, networkLoader = networkLoader, onClickItem = { id -> - navigator.navigate("/details/${id}") + navigator.navigate("/details/$id") } ) } @@ -334,7 +335,7 @@ fun HomeScreen( releaseDate = it.releaseDate, networkLoader = networkLoader, onClickItem = { id -> - navigator.navigate("/details/${id}") + navigator.navigate("/details/$id") } ) } @@ -372,7 +373,7 @@ fun HomeScreen( posterPath = it.posterPath, title = it.name, onItemClick = { id -> - navigator.navigate("/details/${id}") + navigator.navigate("/details/$id") } ) } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt index ffbd8c5f..328e37a2 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt @@ -141,5 +141,4 @@ class HomeViewModel( _homeUiState.update { it.copy(error = error.message, isLoading = false) } } } - }