diff --git a/composeApp/src/commonMain/composeResources/values/string.xml b/composeApp/src/commonMain/composeResources/values/string.xml
index b094b75..613b3c5 100644
--- a/composeApp/src/commonMain/composeResources/values/string.xml
+++ b/composeApp/src/commonMain/composeResources/values/string.xml
@@ -2,7 +2,7 @@
Home
Favourite
- Profile
+ Gemini
Cart
RATING
TYPE
@@ -18,4 +18,5 @@
Category
All
Number Of People
+ Gemini ChatBot
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/App.kt b/composeApp/src/commonMain/kotlin/App.kt
index 48745d6..7f0e70b 100644
--- a/composeApp/src/commonMain/kotlin/App.kt
+++ b/composeApp/src/commonMain/kotlin/App.kt
@@ -9,7 +9,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import cafe.adriel.voyager.navigator.tab.CurrentTab
-import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
import cafe.adriel.voyager.navigator.tab.TabNavigator
import coil3.ImageLoader
import coil3.PlatformContext
@@ -26,7 +25,7 @@ import ui.component.tabs
import ui.screen.CartTab
import ui.screen.FavoriteTab
import ui.screen.HomeTab
-import ui.screen.ProfileTab
+import ui.screen.GeminiTab
import ui.viewmodel.HomeViewModel
import util.AnimateVisibility
@@ -58,7 +57,7 @@ internal fun App(
HomeTab -> LocalNavigator.currentOrThrow.push(HomeTab)
FavoriteTab -> LocalNavigator.currentOrThrow.push(FavoriteTab)
CartTab -> LocalNavigator.currentOrThrow.push(CartTab)
- ProfileTab -> LocalNavigator.currentOrThrow.push(ProfileTab)
+ GeminiTab -> LocalNavigator.currentOrThrow.push(GeminiTab)
}
}
}
diff --git a/composeApp/src/commonMain/kotlin/model/ChatMessage.kt b/composeApp/src/commonMain/kotlin/model/ChatMessage.kt
new file mode 100644
index 0000000..29e29bc
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/model/ChatMessage.kt
@@ -0,0 +1,3 @@
+package model
+
+data class ChatMessage(val text: String, val isSender: Boolean)
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/ui/component/BottomNavigationBar.kt b/composeApp/src/commonMain/kotlin/ui/component/BottomNavigationBar.kt
index 1f542f3..29271f7 100644
--- a/composeApp/src/commonMain/kotlin/ui/component/BottomNavigationBar.kt
+++ b/composeApp/src/commonMain/kotlin/ui/component/BottomNavigationBar.kt
@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
-import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -40,7 +39,7 @@ import theme.SecondTextColor
import ui.screen.CartTab
import ui.screen.FavoriteTab
import ui.screen.HomeTab
-import ui.screen.ProfileTab
+import ui.screen.GeminiTab
interface Tabx: Tab {
fun defaultTitle(): StringResource
@@ -51,7 +50,7 @@ val tabs = arrayListOf().apply {
add(HomeTab)
add(FavoriteTab)
add(CartTab)
- add(ProfileTab)
+ add(GeminiTab)
}
@Composable
diff --git a/composeApp/src/commonMain/kotlin/ui/component/Shimmer.kt b/composeApp/src/commonMain/kotlin/ui/component/Shimmer.kt
new file mode 100644
index 0000000..c3925b0
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/ui/component/Shimmer.kt
@@ -0,0 +1,82 @@
+package ui.component
+
+import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.RepeatMode
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.infiniteRepeatable
+import androidx.compose.animation.core.rememberInfiniteTransition
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import theme.PrimaryColor
+
+val ShimmerColorShades = listOf(
+ Color.LightGray.copy(0.9f),
+ PrimaryColor.copy(0.2f),
+ Color.LightGray.copy(0.9f)
+)
+@Composable
+fun ShimmerAnimation(
+) {
+ /*
+ Create InfiniteTransition
+ which holds child animation like [Transition]
+ animations start running as soon as they enter
+ the composition and do not stop unless they are removed
+ */
+ val transition = rememberInfiniteTransition()
+ val translateAnim by transition.animateFloat(
+ /*
+ Specify animation positions,
+ initial Values 0F means it
+ starts from 0 position
+ */
+ initialValue = 0f,
+ targetValue = 1000f,
+ animationSpec = infiniteRepeatable(
+
+
+ // Tween Animates between values over specified [durationMillis]
+ tween(durationMillis = 1200, easing = FastOutSlowInEasing),
+ RepeatMode.Reverse
+ )
+ )
+
+ /*
+ Create a gradient using the list of colors
+ Use Linear Gradient for animating in any direction according to requirement
+ start=specifies the position to start with in cartesian like system Offset(10f,10f) means x(10,0) , y(0,10)
+ end = Animate the end position to give the shimmer effect using the transition created above
+ */
+ val brush = Brush.linearGradient(
+ colors = ShimmerColorShades,
+ start = Offset(10f, 10f),
+ end = Offset(translateAnim, translateAnim)
+ )
+
+ ShimmerItem(brush = brush)
+}
+
+@Composable
+fun ShimmerItem(
+ brush: Brush
+) {
+ Spacer(
+ modifier = Modifier
+ .padding(top = 8.dp)
+ .fillMaxWidth()
+ .size(20.dp)
+ .background(brush = brush, shape = RoundedCornerShape(8.dp))
+ )
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/ui/screen/CartTab.kt b/composeApp/src/commonMain/kotlin/ui/screen/CartTab.kt
index 98496d6..8db7c8f 100644
--- a/composeApp/src/commonMain/kotlin/ui/screen/CartTab.kt
+++ b/composeApp/src/commonMain/kotlin/ui/screen/CartTab.kt
@@ -78,7 +78,7 @@ fun CartScreenView(
modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 36.dp),
text = stringResource(Res.string.cart_tab),
color = TextColor,
- style = MaterialTheme.typography.bodySmall,
+ style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Center
)
}
diff --git a/composeApp/src/commonMain/kotlin/ui/screen/FavoriteTab.kt b/composeApp/src/commonMain/kotlin/ui/screen/FavoriteTab.kt
index 8f8be4d..723a2da 100644
--- a/composeApp/src/commonMain/kotlin/ui/screen/FavoriteTab.kt
+++ b/composeApp/src/commonMain/kotlin/ui/screen/FavoriteTab.kt
@@ -88,7 +88,7 @@ fun FavoriteScreenView(
modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 36.dp),
text = stringResource(Res.string.favorite_destination),
color = TextColor,
- style = MaterialTheme.typography.bodySmall,
+ style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Center
)
}
diff --git a/composeApp/src/commonMain/kotlin/ui/screen/ProfileTab.kt b/composeApp/src/commonMain/kotlin/ui/screen/GeminiTab.kt
similarity index 87%
rename from composeApp/src/commonMain/kotlin/ui/screen/ProfileTab.kt
rename to composeApp/src/commonMain/kotlin/ui/screen/GeminiTab.kt
index 8997a94..a02b338 100644
--- a/composeApp/src/commonMain/kotlin/ui/screen/ProfileTab.kt
+++ b/composeApp/src/commonMain/kotlin/ui/screen/GeminiTab.kt
@@ -1,5 +1,11 @@
package ui.screen
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.animation.expandIn
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.shrinkOut
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
@@ -18,7 +24,6 @@ import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
@@ -32,29 +37,24 @@ import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Clear
-import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.TextFieldColors
import androidx.compose.material3.TextFieldDefaults
-import androidx.compose.material3.rememberModalBottomSheetState
-import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.mikepenz.markdown.m3.Markdown
import com.mikepenz.markdown.m3.markdownColor
-import com.mikepenz.markdown.m3.markdownTypography
-import com.mikepenz.markdown.model.MarkdownTypography
import data.GeminiApi
import dev.shreyaspatil.ai.client.generativeai.type.GenerateContentResponse
import io.github.vinceglb.filekit.compose.rememberFilePickerLauncher
@@ -63,7 +63,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
-import org.jetbrains.compose.ui.tooling.preview.Preview
import theme.BorderColor
import theme.CodeBackground
import theme.LinkColor
@@ -71,12 +70,14 @@ import theme.PrimaryColor
import theme.TextColor
import toComposeImageBitmap
import travelbuddy.composeapp.generated.resources.Res
+import travelbuddy.composeapp.generated.resources.gemini
import travelbuddy.composeapp.generated.resources.menu_profile
import travelbuddy.composeapp.generated.resources.profile_tab
+import ui.component.ShimmerAnimation
import ui.component.Tabx
import util.BOTTOM_NAV_SPACE
-data object ProfileTab : Tabx {
+data object GeminiTab : Tabx {
override fun defaultTitle(): StringResource = Res.string.profile_tab
override fun defaultIcon(): DrawableResource = Res.drawable.menu_profile
@@ -98,13 +99,13 @@ data object ProfileTab : Tabx {
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
- ProfileScreenView(navigator)
+ GeminiScreenView(navigator)
}
}
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable
-fun ProfileScreenView(navigator: Navigator){
+fun GeminiScreenView(navigator: Navigator){
val api = remember { GeminiApi() }
val coroutineScope = rememberCoroutineScope()
var prompt by remember { mutableStateOf("") }
@@ -113,11 +114,7 @@ fun ProfileScreenView(navigator: Navigator){
var showProgress by remember { mutableStateOf(false) }
var filePath by remember { mutableStateOf("") }
var image by remember { mutableStateOf(null) }
- val canClearPrompt by remember {
- derivedStateOf {
- prompt.isNotBlank()
- }
- }
+ val canClearPrompt by remember { derivedStateOf { prompt.isNotBlank() } }
Surface(modifier = Modifier.fillMaxWidth().padding(bottom = BOTTOM_NAV_SPACE)) {
val imagePickerLauncher = rememberFilePickerLauncher(PickerType.Image) { selectedImage ->
@@ -134,7 +131,17 @@ fun ProfileScreenView(navigator: Navigator){
.verticalScroll(rememberScrollState())
.fillMaxWidth().padding(16.dp)
) {
- FlowRow {
+ Text(
+ modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 26.dp),
+ text = stringResource(Res.string.gemini),
+ color = TextColor,
+ style = MaterialTheme.typography.headlineMedium,
+ textAlign = TextAlign.Center
+ )
+
+ FlowRow(
+ modifier = Modifier.fillMaxWidth().padding(top = 16.dp),
+ ) {
OutlinedTextField(
value = prompt,
onValueChange = { prompt = it },
@@ -181,6 +188,7 @@ fun ProfileScreenView(navigator: Navigator){
.onStart { showProgress = true }
.onCompletion { showProgress = false }
.collect {
+ showProgress = false
println("response = ${it.text}")
content += it.text
}
@@ -237,7 +245,13 @@ fun ProfileScreenView(navigator: Navigator){
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
- CircularProgressIndicator()
+ Column(
+ modifier = Modifier.fillMaxSize()
+ ) {
+ repeat(5) {
+ ShimmerAnimation()
+ }
+ }
}
} else {
SelectionContainer {
@@ -268,4 +282,4 @@ fun generateContentAsFlow(
api.generateContent(prompt, imageByteArray)
} ?: run {
api.generateContent(prompt)
-}
\ No newline at end of file
+}