@ -1,13 +0,0 @@
|
||||
package com.example.jetsnack |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.graphics.painter.Painter |
||||
|
||||
@Composable |
||||
actual fun painterResource(id: Int): Painter { |
||||
return androidx.compose.ui.res.painterResource(id) |
||||
} |
||||
|
||||
actual val MppR.drawable.empty_state_search: Int |
||||
get() = R.drawable.empty_state_search |
||||
|
@ -1,114 +0,0 @@
|
||||
@file:Suppress("PrivatePropertyName") |
||||
|
||||
package com.example.jetsnack |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
|
||||
@Composable |
||||
actual fun stringResource(id: Int): String { |
||||
return androidx.compose.ui.res.stringResource(id) |
||||
} |
||||
|
||||
@Composable |
||||
actual fun stringResource(id: Int, part: String): String { |
||||
return androidx.compose.ui.res.stringResource(id, part) |
||||
} |
||||
|
||||
@Composable |
||||
actual fun stringResource(id: Int, count: Int): String { |
||||
return androidx.compose.ui.res.stringResource(id, count) |
||||
} |
||||
|
||||
|
||||
// Filters |
||||
actual val MppR.string.label_filters: Int get() = R.string.label_filters |
||||
|
||||
// Qty |
||||
actual val MppR.string.quantity: Int get() = R.string.quantity |
||||
|
||||
actual val MppR.string.label_decrease: Int get() = R.string.label_decrease |
||||
|
||||
actual val MppR.string.label_increase: Int get() = R.string.label_increase |
||||
|
||||
|
||||
// Snack detail |
||||
actual val MppR.string.label_back: Int get() = R.string.label_back |
||||
|
||||
actual val MppR.string.detail_header: Int get() = R.string.detail_header |
||||
|
||||
actual val MppR.string.detail_placeholder: Int get() = R.string.detail_placeholder |
||||
|
||||
actual val MppR.string.see_more: Int get() = R.string.see_more |
||||
|
||||
actual val MppR.string.see_less: Int get() = R.string.see_less |
||||
|
||||
actual val MppR.string.ingredients: Int get() = R.string.ingredients |
||||
|
||||
actual val MppR.string.ingredients_list: Int get() = R.string.ingredients_list |
||||
|
||||
actual val MppR.string.add_to_cart: Int get() = R.string.add_to_cart |
||||
|
||||
// Home |
||||
actual val MppR.string.label_select_delivery: Int get() = R.string.label_select_delivery |
||||
|
||||
|
||||
// Filter |
||||
actual val MppR.string.max_calories: Int get() = R.string.max_calories |
||||
|
||||
actual val MppR.string.per_serving: Int get() = R.string.per_serving |
||||
|
||||
actual val MppR.string.sort: Int get() = R.string.sort |
||||
|
||||
actual val MppR.string.lifestyle: Int get() = R.string.lifestyle |
||||
|
||||
actual val MppR.string.category: Int get() = R.string.category |
||||
|
||||
actual val MppR.string.price: Int get() = R.string.price |
||||
|
||||
actual val MppR.string.reset: Int get() = R.string.reset |
||||
|
||||
actual val MppR.string.close: Int get() = R.string.close |
||||
|
||||
// Profile |
||||
|
||||
actual val MppR.string.work_in_progress: Int get() = R.string.work_in_progress |
||||
|
||||
actual val MppR.string.grab_beverage: Int get() = R.string.grab_beverage |
||||
|
||||
// Home |
||||
actual val MppR.string.home_feed: Int get() = R.string.home_feed |
||||
|
||||
actual val MppR.string.home_search: Int get() = R.string.home_search |
||||
|
||||
actual val MppR.string.home_cart: Int get() = R.string.home_cart |
||||
|
||||
actual val MppR.string.home_profile: Int get() = R.string.home_profile |
||||
|
||||
|
||||
// Search |
||||
actual val MppR.string.search_no_matches: Int get() = R.string.search_no_matches |
||||
|
||||
actual val MppR.string.search_no_matches_retry: Int get() = R.string.search_no_matches_retry |
||||
|
||||
actual val MppR.string.label_add: Int get() = R.string.label_add |
||||
|
||||
actual val MppR.string.search_count: Int get() = R.string.search_count |
||||
|
||||
actual val MppR.string.label_search: Int get() = R.string.label_search |
||||
|
||||
actual val MppR.string.search_jetsnack: Int get() = R.string.search_jetsnack |
||||
|
||||
actual val MppR.string.cart_increase_error: Int get() = R.string.cart_increase_error |
||||
actual val MppR.string.cart_decrease_error: Int get() = R.string.cart_decrease_error |
||||
|
||||
|
||||
// Cart |
||||
actual val MppR.plurals.cart_order_count: Int get() = R.plurals.cart_order_count |
||||
actual val MppR.string.cart_order_header: Int get() = R.string.cart_order_header |
||||
actual val MppR.string.remove_item: Int get() = R.string.remove_item |
||||
actual val MppR.string.cart_summary_header: Int get() = R.string.cart_summary_header |
||||
actual val MppR.string.cart_subtotal_label: Int get() = R.string.cart_subtotal_label |
||||
actual val MppR.string.cart_shipping_label: Int get() = R.string.cart_shipping_label |
||||
actual val MppR.string.cart_total_label: Int get() = R.string.cart_total_label |
||||
actual val MppR.string.cart_checkout: Int get() = R.string.cart_checkout |
||||
actual val MppR.string.label_remove: Int get() = R.string.label_remove |
@ -1,74 +0,0 @@
|
||||
package com.example.jetsnack.ui |
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues |
||||
import androidx.compose.foundation.layout.padding |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.navigation.NavBackStackEntry |
||||
import androidx.navigation.NavGraphBuilder |
||||
import androidx.navigation.NavType |
||||
import androidx.navigation.compose.NavHost |
||||
import androidx.navigation.compose.composable |
||||
import androidx.navigation.compose.navigation |
||||
import androidx.navigation.navArgument |
||||
import com.example.jetsnack.ui.home.Feed |
||||
import com.example.jetsnack.ui.home.HomeSections |
||||
import com.example.jetsnack.ui.home.Profile |
||||
import com.example.jetsnack.ui.home.cart.Cart |
||||
import com.example.jetsnack.ui.home.search.Search |
||||
import com.example.jetsnack.ui.snackdetail.SnackDetail |
||||
|
||||
@Composable |
||||
actual fun JetsnackScaffoldContent( |
||||
innerPaddingModifier: PaddingValues, |
||||
appState: MppJetsnackAppState |
||||
) { |
||||
NavHost( |
||||
navController = appState.navController, |
||||
startDestination = MainDestinations.HOME_ROUTE, |
||||
modifier = Modifier.padding(innerPaddingModifier) |
||||
) { |
||||
jetsnackNavGraph( |
||||
onSnackSelected = appState::navigateToSnackDetail, |
||||
upPress = appState::upPress |
||||
) |
||||
} |
||||
} |
||||
|
||||
private fun NavGraphBuilder.jetsnackNavGraph( |
||||
onSnackSelected: (Long, NavBackStackEntry) -> Unit, |
||||
upPress: () -> Unit, |
||||
) { |
||||
navigation( |
||||
route = MainDestinations.HOME_ROUTE, |
||||
startDestination = HomeSections.FEED.route |
||||
) { |
||||
addHomeGraph(onSnackSelected) |
||||
} |
||||
composable( |
||||
"${MainDestinations.SNACK_DETAIL_ROUTE}/{${MainDestinations.SNACK_ID_KEY}}", |
||||
arguments = listOf(navArgument(MainDestinations.SNACK_ID_KEY) { type = NavType.LongType }) |
||||
) { backStackEntry -> |
||||
val arguments = requireNotNull(backStackEntry.arguments) |
||||
val snackId = arguments.getLong(MainDestinations.SNACK_ID_KEY) |
||||
SnackDetail(snackId, upPress, onSnackClick = { onSnackSelected(snackId, backStackEntry) }) |
||||
} |
||||
} |
||||
|
||||
fun NavGraphBuilder.addHomeGraph( |
||||
onSnackSelected: (Long, NavBackStackEntry) -> Unit, |
||||
modifier: Modifier = Modifier |
||||
) { |
||||
composable(HomeSections.FEED.route) { from -> |
||||
Feed(onSnackClick = { id -> onSnackSelected(id, from) }, modifier) |
||||
} |
||||
composable(HomeSections.SEARCH.route) { from -> |
||||
Search(onSnackClick = { id -> onSnackSelected(id, from) }, modifier) |
||||
} |
||||
composable(HomeSections.CART.route) { from -> |
||||
Cart(onSnackClick = { id -> onSnackSelected(id, from) }, modifier) |
||||
} |
||||
composable(HomeSections.PROFILE.route) { |
||||
Profile(modifier) |
||||
} |
||||
} |
@ -1,123 +0,0 @@
|
||||
package com.example.jetsnack.ui |
||||
|
||||
import android.content.res.Resources |
||||
import androidx.compose.material3.ScaffoldState |
||||
import androidx.compose.material3.rememberScaffoldState |
||||
import androidx.compose.runtime.* |
||||
import androidx.compose.runtime.remember |
||||
import androidx.compose.ui.platform.LocalConfiguration |
||||
import androidx.compose.ui.platform.LocalContext |
||||
import androidx.lifecycle.Lifecycle |
||||
import androidx.navigation.* |
||||
import androidx.navigation.compose.currentBackStackEntryAsState |
||||
import androidx.navigation.compose.rememberNavController |
||||
import com.example.jetsnack.model.SnackbarManager |
||||
import com.example.jetsnack.ui.home.HomeSections |
||||
import kotlinx.coroutines.CoroutineScope |
||||
import kotlinx.coroutines.launch |
||||
|
||||
@Stable |
||||
actual class MppJetsnackAppState( |
||||
actual val scaffoldState: ScaffoldState, |
||||
actual val snackbarManager: SnackbarManager, |
||||
actual val coroutineScope: CoroutineScope, |
||||
val navController: NavHostController, |
||||
val resources: Resources |
||||
) { |
||||
|
||||
init { |
||||
coroutineScope.launch { |
||||
snackbarManager.messages.collect { currentMessages -> |
||||
if (currentMessages.isNotEmpty()) { |
||||
val message = currentMessages[0] |
||||
val text = resources.getText(message.message).toString() |
||||
|
||||
// Display the snackbar on the screen. `showSnackbar` is a function |
||||
// that suspends until the snackbar disappears from the screen |
||||
scaffoldState.snackbarHostState.showSnackbar(text) |
||||
// Once the snackbar is gone or dismissed, notify the SnackbarManager |
||||
snackbarManager.setMessageShown(message.id) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private val bottomBarRoutes = bottomBarTabs.map { it.route } |
||||
|
||||
actual val bottomBarTabs: Array<HomeSections> |
||||
get() = HomeSections.values() |
||||
actual val currentRoute: String? |
||||
get() = navController.currentDestination?.route |
||||
|
||||
|
||||
@Composable |
||||
actual fun shouldShowBottomBar(): Boolean { |
||||
return navController |
||||
.currentBackStackEntryAsState().value?.destination?.route in bottomBarRoutes |
||||
} |
||||
|
||||
actual fun navigateToBottomBarRoute(route: String) { |
||||
if (route != currentRoute) { |
||||
navController.navigate(route) { |
||||
launchSingleTop = true |
||||
restoreState = true |
||||
// Pop up backstack to the first destination and save state. This makes going back |
||||
// to the start destination when pressing back in any other bottom tab. |
||||
popUpTo(findStartDestination(navController.graph).id) { |
||||
saveState = true |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
fun navigateToSnackDetail(snackId: Long, from: NavBackStackEntry) { |
||||
// In order to discard duplicated navigation events, we check the Lifecycle |
||||
if (from.lifecycleIsResumed()) { |
||||
navController.navigate("${MainDestinations.SNACK_DETAIL_ROUTE}/$snackId") |
||||
} |
||||
} |
||||
|
||||
fun upPress() { |
||||
navController.navigateUp() |
||||
} |
||||
} |
||||
|
||||
@Suppress("UsePropertyAccessSyntax") |
||||
private fun NavBackStackEntry.lifecycleIsResumed() = |
||||
this.getLifecycle().currentState == Lifecycle.State.RESUMED |
||||
|
||||
private val NavGraph.startDestination: NavDestination? |
||||
get() = findNode(startDestinationId) |
||||
|
||||
/** |
||||
* Copied from similar function in NavigationUI.kt |
||||
* |
||||
* https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.kt |
||||
*/ |
||||
private tailrec fun findStartDestination(graph: NavDestination): NavDestination { |
||||
return if (graph is NavGraph) findStartDestination(graph.startDestination!!) else graph |
||||
} |
||||
|
||||
@Composable |
||||
actual fun rememberMppJetsnackAppState(): MppJetsnackAppState { |
||||
val scaffoldState = rememberScaffoldState() |
||||
val navController = rememberNavController() |
||||
val resources = resources() |
||||
val snackbarManager = SnackbarManager |
||||
val coroutineScope = rememberCoroutineScope() |
||||
|
||||
return remember(scaffoldState, navController, snackbarManager, resources, coroutineScope) { |
||||
MppJetsnackAppState(scaffoldState, snackbarManager, coroutineScope, navController, resources) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* A composable function that returns the [Resources]. It will be recomposed when `Configuration` |
||||
* gets updated. |
||||
*/ |
||||
@Composable |
||||
@ReadOnlyComposable |
||||
private fun resources(): Resources { |
||||
LocalConfiguration.current |
||||
return LocalContext.current.resources |
||||
} |
@ -1,69 +0,0 @@
|
||||
package com.example.jetsnack.ui.components |
||||
|
||||
import android.annotation.SuppressLint |
||||
import android.graphics.Bitmap |
||||
import android.graphics.BitmapFactory |
||||
import androidx.compose.animation.AnimatedContent |
||||
import androidx.compose.animation.ExperimentalAnimationApi |
||||
import androidx.compose.animation.core.TweenSpec |
||||
import androidx.compose.animation.fadeIn |
||||
import androidx.compose.animation.fadeOut |
||||
import androidx.compose.animation.with |
||||
import androidx.compose.foundation.Image |
||||
import androidx.compose.foundation.layout.Box |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.LaunchedEffect |
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.remember |
||||
import androidx.compose.runtime.setValue |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.graphics.ImageBitmap |
||||
import androidx.compose.ui.graphics.asImageBitmap |
||||
import androidx.compose.ui.layout.ContentScale |
||||
import com.example.common.generated.resources.Res |
||||
import kotlinx.coroutines.Dispatchers |
||||
import kotlinx.coroutines.withContext |
||||
import org.jetbrains.compose.resources.ExperimentalResourceApi |
||||
|
||||
private val imagesCache = mutableMapOf<String, ImageBitmap>() |
||||
|
||||
@SuppressLint("UnusedContentLambdaTargetStateParameter") |
||||
@OptIn(ExperimentalAnimationApi::class, ExperimentalResourceApi::class) |
||||
@Composable |
||||
actual fun SnackAsyncImage(imageUrl: String, contentDescription: String?, modifier: Modifier) { |
||||
var img: ImageBitmap? by remember(imageUrl) { mutableStateOf(null) } |
||||
|
||||
|
||||
AnimatedContent(img, transitionSpec = { |
||||
fadeIn(TweenSpec()) with fadeOut(TweenSpec()) |
||||
}) { |
||||
if (img != null) { |
||||
Image(img!!, contentDescription = contentDescription, modifier = modifier, contentScale = ContentScale.Crop) |
||||
} else { |
||||
Box(modifier = modifier) |
||||
} |
||||
} |
||||
|
||||
LaunchedEffect(imageUrl) { |
||||
if (imagesCache.contains(imageUrl)) { |
||||
img = imagesCache[imageUrl] |
||||
} else { |
||||
withContext(Dispatchers.IO) { |
||||
img = try { |
||||
Res.readBytes(imageUrl).toAndroidBitmap().asImageBitmap().also { |
||||
imagesCache[imageUrl] = it |
||||
img = it |
||||
} |
||||
} catch (e: Throwable) { |
||||
e.printStackTrace() |
||||
null |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
fun ByteArray.toAndroidBitmap(): Bitmap { |
||||
return BitmapFactory.decodeByteArray(this, 0, size) |
||||
} |
@ -1,9 +0,0 @@
|
||||
package com.example.jetsnack.ui.home |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.window.Dialog |
||||
|
||||
@Composable |
||||
actual fun SnackDialog(onCloseRequest: () -> Unit, content: @Composable () -> Unit) { |
||||
Dialog(onDismissRequest = onCloseRequest, content = content) |
||||
} |
@ -1,189 +0,0 @@
|
||||
/* |
||||
* Copyright 2020 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package com.example.jetsnack.ui.home.cart |
||||
|
||||
import androidx.compose.foundation.background |
||||
import androidx.compose.foundation.clickable |
||||
import androidx.compose.foundation.layout.* |
||||
import androidx.compose.material3.Icon |
||||
import androidx.compose.material3.IconButton |
||||
import androidx.compose.material3.MaterialTheme |
||||
import androidx.compose.material3.Text |
||||
import androidx.compose.material3.icons.Icons |
||||
import androidx.compose.material3.icons.filled.Close |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.remember |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.platform.LocalContext |
||||
import androidx.compose.ui.res.stringResource |
||||
import androidx.compose.ui.unit.dp |
||||
import androidx.constraintlayout.compose.ChainStyle |
||||
import androidx.constraintlayout.compose.ConstraintLayout |
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle |
||||
import androidx.lifecycle.viewmodel.compose.viewModel |
||||
import com.example.jetsnack.R |
||||
import com.example.jetsnack.model.OrderLine |
||||
import com.example.jetsnack.model.SnackRepo |
||||
import com.example.jetsnack.ui.components.JetsnackDivider |
||||
import com.example.jetsnack.ui.components.QuantitySelector |
||||
import com.example.jetsnack.ui.components.SnackImage |
||||
import com.example.jetsnack.ui.theme.JetsnackTheme |
||||
import com.example.jetsnack.ui.utils.formatPrice |
||||
|
||||
@Composable |
||||
actual fun provideCartViewModel(): CartViewModel { |
||||
return viewModel(factory = CartViewModel.provideFactory()) |
||||
} |
||||
|
||||
@Composable |
||||
actual fun ActualCartItem( |
||||
orderLine: OrderLine, |
||||
removeSnack: (Long) -> Unit, |
||||
increaseItemCount: (Long) -> Unit, |
||||
decreaseItemCount: (Long) -> Unit, |
||||
onSnackClick: (Long) -> Unit, |
||||
modifier: Modifier |
||||
) { |
||||
val snack = orderLine.snack |
||||
ConstraintLayout( |
||||
modifier = modifier |
||||
.fillMaxWidth() |
||||
.clickable { onSnackClick(snack.id) } |
||||
.background(JetsnackTheme.colors.uiBackground) |
||||
.padding(horizontal = 24.dp) |
||||
|
||||
) { |
||||
val (divider, image, name, tag, priceSpacer, price, remove, quantity) = createRefs() |
||||
createVerticalChain(name, tag, priceSpacer, price, chainStyle = ChainStyle.Packed) |
||||
SnackImage( |
||||
imageUrl = snack.imageUrl, |
||||
contentDescription = null, |
||||
modifier = Modifier |
||||
.size(100.dp) |
||||
.constrainAs(image) { |
||||
top.linkTo(parent.top, margin = 16.dp) |
||||
bottom.linkTo(parent.bottom, margin = 16.dp) |
||||
start.linkTo(parent.start) |
||||
} |
||||
) |
||||
Text( |
||||
text = snack.name, |
||||
style = MaterialTheme.typography.subtitle1, |
||||
color = JetsnackTheme.colors.textSecondary, |
||||
modifier = Modifier.constrainAs(name) { |
||||
linkTo( |
||||
start = image.end, |
||||
startMargin = 16.dp, |
||||
end = remove.start, |
||||
endMargin = 16.dp, |
||||
bias = 0f |
||||
) |
||||
} |
||||
) |
||||
IconButton( |
||||
onClick = { removeSnack(snack.id) }, |
||||
modifier = Modifier |
||||
.constrainAs(remove) { |
||||
top.linkTo(parent.top) |
||||
end.linkTo(parent.end) |
||||
} |
||||
.padding(top = 12.dp) |
||||
) { |
||||
Icon( |
||||
imageVector = Icons.Filled.Close, |
||||
tint = JetsnackTheme.colors.iconSecondary, |
||||
contentDescription = stringResource(R.string.label_remove) |
||||
) |
||||
} |
||||
Text( |
||||
text = snack.tagline, |
||||
style = MaterialTheme.typography.body1, |
||||
color = JetsnackTheme.colors.textHelp, |
||||
modifier = Modifier.constrainAs(tag) { |
||||
linkTo( |
||||
start = image.end, |
||||
startMargin = 16.dp, |
||||
end = parent.end, |
||||
endMargin = 16.dp, |
||||
bias = 0f |
||||
) |
||||
} |
||||
) |
||||
Spacer( |
||||
Modifier |
||||
.height(8.dp) |
||||
.constrainAs(priceSpacer) { |
||||
linkTo(top = tag.bottom, bottom = price.top) |
||||
} |
||||
) |
||||
Text( |
||||
text = formatPrice(snack.price), |
||||
style = MaterialTheme.typography.subtitle1, |
||||
color = JetsnackTheme.colors.textPrimary, |
||||
modifier = Modifier.constrainAs(price) { |
||||
linkTo( |
||||
start = image.end, |
||||
end = quantity.start, |
||||
startMargin = 16.dp, |
||||
endMargin = 16.dp, |
||||
bias = 0f |
||||
) |
||||
} |
||||
) |
||||
QuantitySelector( |
||||
count = orderLine.count, |
||||
decreaseItemCount = { decreaseItemCount(snack.id) }, |
||||
increaseItemCount = { increaseItemCount(snack.id) }, |
||||
modifier = Modifier.constrainAs(quantity) { |
||||
baseline.linkTo(price.baseline) |
||||
end.linkTo(parent.end) |
||||
} |
||||
) |
||||
JetsnackDivider( |
||||
Modifier.constrainAs(divider) { |
||||
linkTo(start = parent.start, end = parent.end) |
||||
top.linkTo(parent.bottom) |
||||
} |
||||
) |
||||
} |
||||
} |
||||
|
||||
@Composable |
||||
private fun CartPreview() { |
||||
JetsnackTheme { |
||||
Cart( |
||||
orderLines = SnackRepo.getCart(), |
||||
removeSnack = {}, |
||||
increaseItemCount = {}, |
||||
decreaseItemCount = {}, |
||||
inspiredByCart = SnackRepo.getInspiredByCart(), |
||||
onSnackClick = {} |
||||
) |
||||
} |
||||
} |
||||
|
||||
@Composable |
||||
actual fun rememberQuantityString(res: Int, qty: Int, vararg args: Any): String { |
||||
val resources = LocalContext.current.resources |
||||
return remember(qty, resources) { resources.getQuantityString(R.plurals.cart_order_count, qty, qty) } |
||||
} |
||||
|
||||
@Composable |
||||
actual fun getCartContentInsets(): WindowInsets { |
||||
return WindowInsets.statusBars.add(WindowInsets(top = 56.dp)) |
||||
} |
@ -1,48 +0,0 @@
|
||||
/* |
||||
* Copyright 2020 The Android Open Source Project |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package com.example.jetsnack.ui.home.cart |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.State |
||||
import androidx.lifecycle.ViewModel |
||||
import androidx.lifecycle.ViewModelProvider |
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle |
||||
import com.example.jetsnack.model.OrderLine |
||||
import com.example.jetsnack.model.SnackRepo |
||||
import com.example.jetsnack.model.SnackbarManager |
||||
import kotlinx.coroutines.flow.StateFlow |
||||
|
||||
/** |
||||
* Factory for CartViewModel that takes SnackbarManager as a dependency |
||||
*/ |
||||
fun CartViewModel.Companion.provideFactory( |
||||
snackbarManager: SnackbarManager = SnackbarManager, |
||||
snackRepository: SnackRepo = SnackRepo |
||||
): ViewModelProvider.Factory = object : ViewModelProvider.Factory { |
||||
@Suppress("UNCHECKED_CAST") |
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T { |
||||
return CartViewModel(snackbarManager, snackRepository) as T |
||||
} |
||||
} |
||||
|
||||
@OptIn(kotlin.ExperimentalMultiplatform::class) |
||||
actual abstract class JetSnackCartViewModel actual constructor() : ViewModel() { |
||||
@Composable |
||||
actual fun collectOrderLinesAsState(flow: StateFlow<List<OrderLine>>): State<List<OrderLine>> { |
||||
return flow.collectAsStateWithLifecycle() |
||||
} |
||||
} |
@ -1,12 +0,0 @@
|
||||
package com.example.jetsnack.ui.home |
||||
|
||||
import androidx.compose.foundation.layout.WindowInsets |
||||
import androidx.compose.foundation.layout.add |
||||
import androidx.compose.foundation.layout.statusBars |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.unit.dp |
||||
|
||||
@Composable |
||||
actual fun snackCollectionListItemWindowInsets(): WindowInsets { |
||||
return WindowInsets.statusBars.add(WindowInsets(top = 56.dp)) |
||||
} |
@ -1,14 +0,0 @@
|
||||
package com.example.jetsnack.ui.snackdetail |
||||
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding |
||||
import androidx.compose.foundation.layout.statusBarsPadding |
||||
import androidx.compose.foundation.layout.systemBarsPadding |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Modifier |
||||
|
||||
@Composable |
||||
actual fun Modifier.jetSnackNavigationBarsPadding(): Modifier = this.navigationBarsPadding() |
||||
@Composable |
||||
actual fun Modifier.jetSnackStatusBarsPadding(): Modifier = this.statusBarsPadding() |
||||
@Composable |
||||
actual fun Modifier.jetSnackSystemBarsPadding(): Modifier = this.systemBarsPadding() |
Before Width: | Height: | Size: 332 KiB |
Before Width: | Height: | Size: 228 KiB |
Before Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 199 KiB |
Before Width: | Height: | Size: 248 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 159 KiB |
Before Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 111 KiB |
Before Width: | Height: | Size: 382 KiB |
Before Width: | Height: | Size: 653 KiB |
Before Width: | Height: | Size: 496 KiB |
Before Width: | Height: | Size: 212 KiB |
Before Width: | Height: | Size: 380 KiB |
Before Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 198 KiB |
Before Width: | Height: | Size: 198 KiB |
Before Width: | Height: | Size: 250 KiB |
Before Width: | Height: | Size: 210 KiB |
Before Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 137 KiB |
Before Width: | Height: | Size: 182 KiB |
Before Width: | Height: | Size: 300 KiB |
Before Width: | Height: | Size: 129 KiB |
Before Width: | Height: | Size: 217 KiB |
Before Width: | Height: | Size: 218 KiB |
Before Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 187 KiB |
@ -1,10 +1,9 @@
|
||||
package com.example.jetsnack.ui |
||||
|
||||
import androidx.compose.ui.window.ComposeUIViewController |
||||
import com.example.jetsnack.JetSnackAppEntryPoint |
||||
import platform.UIKit.UIViewController |
||||
|
||||
fun MainViewController(): UIViewController = |
||||
ComposeUIViewController { |
||||
JetSnackAppEntryPoint() |
||||
JetsnackApp() |
||||
} |