diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_notification_add_outlined.xml b/composeApp/src/commonMain/composeResources/drawable/ic_notification_add_outlined.xml
new file mode 100644
index 0000000..a745721
--- /dev/null
+++ b/composeApp/src/commonMain/composeResources/drawable/ic_notification_add_outlined.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_notifications_active.xml b/composeApp/src/commonMain/composeResources/drawable/ic_notifications_active.xml
new file mode 100644
index 0000000..9890419
--- /dev/null
+++ b/composeApp/src/commonMain/composeResources/drawable/ic_notifications_active.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_notifications_add.xml b/composeApp/src/commonMain/composeResources/drawable/ic_notifications_add.xml
new file mode 100644
index 0000000..fc0ceb1
--- /dev/null
+++ b/composeApp/src/commonMain/composeResources/drawable/ic_notifications_add.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_notifications_none.xml b/composeApp/src/commonMain/composeResources/drawable/ic_notifications_none.xml
new file mode 100644
index 0000000..e5e2603
--- /dev/null
+++ b/composeApp/src/commonMain/composeResources/drawable/ic_notifications_none.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/HomeScreen.kt
index 1674b0a..837fd25 100644
--- a/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/HomeScreen.kt
+++ b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/HomeScreen.kt
@@ -32,7 +32,7 @@ fun HomeScreenRoot(
innerPadding: PaddingValues,
onSettingClick: () -> Unit = {},
) {
- val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
+ val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val state by viewModel.state.collectAsStateWithLifecycle()
val keyboardController = LocalSoftwareKeyboardController.current
@@ -69,8 +69,7 @@ fun HomeScreenRoot(
HomeScreen(
modifier = Modifier
.fillMaxWidth()
- .padding(innerPadding)
- .verticalScroll(rememberScrollState()),
+ .padding(innerPadding),
state = state,
onAction = { action ->
viewModel.onAction(action)
@@ -87,12 +86,11 @@ private fun HomeScreen(
onAction: (HomeAction) -> Unit = {}
) {
Column(modifier = modifier) {
- UpcomingContestCardMostRecent()
ContestHorizontalPager(
onAction = {
onAction(it)
},
- state = state
+ state = state,
)
}
}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/components/ContestHorizontalPager.kt b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/components/ContestHorizontalPager.kt
index 1caa79a..bcf7f05 100644
--- a/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/components/ContestHorizontalPager.kt
+++ b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/components/ContestHorizontalPager.kt
@@ -1,12 +1,12 @@
package org.onedroid.greedycoder.app.presentation.home.components
import androidx.compose.foundation.background
+import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -19,12 +19,10 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
-import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import org.onedroid.greedycoder.app.presentation.home.HomeAction
import org.onedroid.greedycoder.app.presentation.home.HomeState
-import org.onedroid.greedycoder.core.theme.extraSmall
-import org.onedroid.greedycoder.core.theme.small
@Composable
fun ContestHorizontalPager(
@@ -39,13 +37,18 @@ fun ContestHorizontalPager(
onAction(HomeAction.OnTabSelected(pagerState.currentPage))
}
Column(
- modifier = Modifier.fillMaxWidth().padding(16.dp),
+ modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
TabRow(
modifier = Modifier
- .padding(horizontal = extraSmall)
- .clip(RoundedCornerShape(8.dp))
+ .padding(horizontal = 16.dp, vertical = 8.dp)
+ .clip(RoundedCornerShape(12.dp))
+ .border(
+ 1.dp,
+ MaterialTheme.colorScheme.outline.copy(alpha = 0.1f),
+ RoundedCornerShape(12.dp)
+ )
.fillMaxWidth(),
selectedTabIndex = state.selectedTabIndex,
containerColor = MaterialTheme.colorScheme.surfaceContainer,
@@ -74,7 +77,8 @@ fun ContestHorizontalPager(
.align(Alignment.Center)
.padding(vertical = 12.dp),
text = "Upcoming",
- style = MaterialTheme.typography.labelLarge
+ style = MaterialTheme.typography.titleSmall,
+ fontWeight = FontWeight.Bold
)
}
}
@@ -101,27 +105,24 @@ fun ContestHorizontalPager(
.align(Alignment.Center)
.padding(vertical = 12.dp),
text = "Completed",
- style = MaterialTheme.typography.labelLarge
+ style = MaterialTheme.typography.titleSmall,
+ fontWeight = FontWeight.Bold
)
}
}
}
HorizontalPager(
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier.padding().fillMaxWidth(),
state = pagerState
) { pageIndex ->
- Box(
- modifier = Modifier.fillMaxSize(),
- contentAlignment = Alignment.Center
- ) {
- when (pageIndex) {
- 0 -> {
- Text(text = "Upcoming")
- }
- 1 -> {
- Text(text = "Completed")
- }
+ when (pageIndex) {
+ 0 -> {
+ UpComingContestList()
+ }
+
+ 1 -> {
+ Text(text = "Completed")
}
}
}
diff --git a/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/components/UpcomingContest.kt b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/components/UpcomingContest.kt
index a1971c5..b40f11b 100644
--- a/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/components/UpcomingContest.kt
+++ b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/home/components/UpcomingContest.kt
@@ -1,39 +1,57 @@
package org.onedroid.greedycoder.app.presentation.home.components
import androidx.compose.foundation.background
+import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
+import greedycoder.composeapp.generated.resources.Res
+import greedycoder.composeapp.generated.resources.ic_notification_add_outlined
+import greedycoder.composeapp.generated.resources.ic_notifications_active
+import greedycoder.composeapp.generated.resources.ic_notifications_add
+import org.jetbrains.compose.resources.painterResource
@Composable
fun UpcomingContestCardMostRecent() {
Card(
modifier = Modifier
.fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 4.dp),
+ .padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
+ .border(
+ 1.dp,
+ MaterialTheme.colorScheme.outline.copy(alpha = 0.1f),
+ RoundedCornerShape(12.dp)
+ ),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainer
- )
+ ),
+ //elevation = CardDefaults.cardElevation(1.dp)
) {
Column(
modifier = Modifier
@@ -47,7 +65,7 @@ fun UpcomingContestCardMostRecent() {
Column {
Text(
text = "Upcoming Contest",
- color = Color.Gray,
+ color = MaterialTheme.colorScheme.onSecondaryContainer,
style = MaterialTheme.typography.bodySmall
)
Spacer(modifier = Modifier.height(4.dp))
@@ -64,23 +82,26 @@ fun UpcomingContestCardMostRecent() {
}
}
- Spacer(modifier = Modifier.height(16.dp))
+ Spacer(modifier = Modifier.height(12.dp))
// Title
Text(
text = "Codeforces Round 1003 (Div. 4)",
color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.titleMedium,
+ style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold
)
- Spacer(modifier = Modifier.height(8.dp))
+ Spacer(modifier = Modifier.height(10.dp))
// Difficulty and Tags
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
- .background(Color(0xFF2E2E2E), RoundedCornerShape(12.dp))
+ .background(
+ color = MaterialTheme.colorScheme.secondaryContainer,
+ RoundedCornerShape(12.dp)
+ )
.padding(horizontal = 8.dp, vertical = 4.dp)
.padding(end = 4.dp)
) {
@@ -93,7 +114,10 @@ fun UpcomingContestCardMostRecent() {
Spacer(modifier = Modifier.width(5.dp))
Box(
modifier = Modifier
- .background(Color(0xFF2E2E2E), RoundedCornerShape(12.dp))
+ .background(
+ color = MaterialTheme.colorScheme.secondaryContainer,
+ RoundedCornerShape(12.dp)
+ )
.padding(horizontal = 8.dp, vertical = 4.dp)
.padding(end = 4.dp)
) {
@@ -106,7 +130,10 @@ fun UpcomingContestCardMostRecent() {
Spacer(modifier = Modifier.width(5.dp))
Box(
modifier = Modifier
- .background(Color(0xFF2E2E2E), RoundedCornerShape(12.dp))
+ .background(
+ color = MaterialTheme.colorScheme.secondaryContainer,
+ RoundedCornerShape(12.dp)
+ )
.padding(horizontal = 8.dp, vertical = 4.dp)
.padding(end = 4.dp)
) {
@@ -126,13 +153,140 @@ fun TimerDisplay(value: Int, unit: String) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = value.toString().padStart(2, '0'),
- color = Color.White,
+ color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelLarge
)
Text(
text = unit,
- color = Color.Gray,
+ color = MaterialTheme.colorScheme.onSecondaryContainer,
style = MaterialTheme.typography.bodySmall
)
}
-}
\ No newline at end of file
+}
+
+@Composable
+fun UpComingContestCard() {
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
+ .border(
+ 1.dp,
+ MaterialTheme.colorScheme.outline.copy(alpha = 0.1f),
+ RoundedCornerShape(12.dp)
+ ),
+ shape = RoundedCornerShape(12.dp),
+ colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.surfaceContainer
+ ),
+ //elevation = CardDefaults.cardElevation(1.dp)
+ ) {
+ Column(
+ modifier = Modifier.padding(16.dp)
+ ) {
+ // Header
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text(
+ text = "Upcoming Contest",
+ color = MaterialTheme.colorScheme.onSecondaryContainer,
+ style = MaterialTheme.typography.bodySmall
+ )
+ Column {
+ Text(
+ text = "Feb, 09, 2025 10:30 PM",
+ color = MaterialTheme.colorScheme.onSecondaryContainer,
+ style = MaterialTheme.typography.bodySmall
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.height(12.dp))
+
+ // Title
+ Text(
+ text = "Codeforces Round 1003 (Div. 4)",
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.bodyMedium,
+ fontWeight = FontWeight.Bold
+ )
+
+ Spacer(modifier = Modifier.height(10.dp))
+
+ // Difficulty and Tags
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Box(
+ modifier = Modifier
+ .background(
+ color = MaterialTheme.colorScheme.secondaryContainer,
+ RoundedCornerShape(12.dp)
+ )
+ .padding(horizontal = 8.dp, vertical = 4.dp)
+ .padding(end = 4.dp)
+ ) {
+ Text(
+ text = "Type: ICPC",
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.labelSmall
+ )
+ }
+ Spacer(modifier = Modifier.width(5.dp))
+ Box(
+ modifier = Modifier
+ .background(
+ color = MaterialTheme.colorScheme.secondaryContainer,
+ RoundedCornerShape(12.dp)
+ )
+ .padding(horizontal = 8.dp, vertical = 4.dp)
+ .padding(end = 4.dp)
+ ) {
+ Text(
+ text = "Duration 2 Hrs",
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.labelSmall
+ )
+ }
+ }
+
+ IconButton(
+ colors = IconButtonDefaults.iconButtonColors(
+ containerColor = MaterialTheme.colorScheme.secondaryContainer
+ ),
+ onClick = {},
+ modifier = Modifier
+ ) {
+ Icon(
+ modifier = Modifier.size(25.dp).padding(2.dp),
+ painter = painterResource(Res.drawable.ic_notification_add_outlined),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onSecondaryContainer
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun UpComingContestList() {
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ ) {
+ item {
+ UpcomingContestCardMostRecent()
+ }
+ items(3) {
+ UpComingContestCard()
+ }
+ }
+}
+
diff --git a/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/profile/components/ProfileSection.kt b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/profile/components/ProfileSection.kt
index 343bcca..656039a 100644
--- a/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/profile/components/ProfileSection.kt
+++ b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/app/presentation/profile/components/ProfileSection.kt
@@ -38,12 +38,13 @@ fun ProfileSection(
avatar: String,
) {
Card(
- modifier = modifier.fillMaxWidth().padding(top = 4.dp),
+ modifier = modifier.fillMaxWidth().padding(top = 4.dp)
+ .border(1.dp, MaterialTheme.colorScheme.outline.copy(alpha = 0.1f), RoundedCornerShape(8.dp)),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainer,
),
shape = RoundedCornerShape(8.dp),
- elevation = CardDefaults.cardElevation(0.5.dp)
+ //elevation = CardDefaults.cardElevation(0.5.dp)
) {
Row(
modifier = Modifier.fillMaxWidth()
diff --git a/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/core/components/Feed.kt b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/core/components/Feed.kt
new file mode 100644
index 0000000..eaa8a48
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/core/components/Feed.kt
@@ -0,0 +1,225 @@
+package org.onedroid.greedycoder.core.components
+
+import androidx.compose.foundation.focusGroup
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.foundation.lazy.grid.LazyGridItemScope
+import androidx.compose.foundation.lazy.grid.LazyGridItemSpanScope
+import androidx.compose.foundation.lazy.grid.LazyGridState
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.lazy.grid.rememberLazyGridState
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun Feed(
+ modifier: Modifier = Modifier,
+ columns: GridCells = GridCells.Fixed(1),
+ state: LazyGridState = rememberLazyGridState(),
+ contentPadding: PaddingValues = PaddingValues(0.dp),
+ verticalArrangement: Arrangement.Vertical = Arrangement.Top,
+ horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
+ flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
+ content: @ExtensionFunctionType FeedScope.() -> Unit
+) {
+ val feedScope = FeedScopeImpl().apply(content)
+ LazyVerticalGrid(
+ columns = columns,
+ modifier = modifier,
+ state = state,
+ contentPadding = contentPadding,
+ verticalArrangement = verticalArrangement,
+ horizontalArrangement = horizontalArrangement,
+ flingBehavior = flingBehavior
+ ) {
+ feedScope.items.forEach { feedItem ->
+ items(
+ count = feedItem.count,
+ key = feedItem.key,
+ contentType = feedItem.contentType,
+ span = feedItem.span,
+ itemContent = feedItem.itemContent
+ )
+ }
+ }
+}
+
+interface FeedScope {
+ fun item(
+ key: Any? = null,
+ span: (@ExtensionFunctionType LazyGridItemSpanScope.() -> GridItemSpan)? = null,
+ contentType: Any? = null,
+ content: @Composable LazyGridItemScope.() -> Unit
+ )
+
+ fun items(
+ count: Int,
+ key: ((index: Int) -> Any)? = null,
+ span: (@ExtensionFunctionType LazyGridItemSpanScope.(index: Int) -> GridItemSpan)? = null,
+ contentType: (index: Int) -> Any? = { null },
+ itemContent: @Composable LazyGridItemScope.(index: Int) -> Unit
+ )
+}
+
+inline fun FeedScope.items(
+ items: List,
+ noinline key: ((item: T) -> Any)? = null,
+ noinline span: (@ExtensionFunctionType LazyGridItemSpanScope.(item: T) -> GridItemSpan)? = null,
+ noinline contentType: (item: T) -> Any? = { null },
+ crossinline itemContent: @Composable LazyGridItemScope.(item: T) -> Unit
+) = items(
+ count = items.size,
+ key = if (key != null) {
+ { index: Int -> key(items[index]) }
+ } else {
+ null
+ },
+ span = if (span != null) {
+ { index: Int -> span(items[index]) }
+ } else {
+ null
+ },
+ contentType = { index: Int -> contentType(items[index]) }
+) { index ->
+ itemContent(items[index])
+}
+
+inline fun FeedScope.itemsIndexed(
+ items: List,
+ noinline key: ((index: Int, item: T) -> Any)? = null,
+ noinline span: (@ExtensionFunctionType LazyGridItemSpanScope.(index: Int, item: T) -> GridItemSpan)? = null,
+ noinline contentType: (index: Int, item: T) -> Any? = { _, _ -> null },
+ crossinline itemContent: @Composable LazyGridItemScope.(index: Int, item: T) -> Unit
+) = items(
+ count = items.size,
+ key = if (key != null) {
+ { index: Int -> key(index, items[index]) }
+ } else {
+ null
+ },
+ span = if (span != null) {
+ { index: Int -> span(index, items[index]) }
+ } else {
+ null
+ },
+ contentType = { index: Int -> contentType(index, items[index]) }
+) { index ->
+ itemContent(index, items[index])
+}
+
+inline fun FeedScope.items(
+ items: Array,
+ noinline key: ((item: T) -> Any)? = null,
+ noinline span: (@ExtensionFunctionType LazyGridItemSpanScope.(item: T) -> GridItemSpan)? = null,
+ noinline contentType: (item: T) -> Any? = { null },
+ crossinline itemContent: @Composable LazyGridItemScope.(item: T) -> Unit
+) = items(
+ count = items.size,
+ key = if (key != null) {
+ { index: Int -> key(items[index]) }
+ } else {
+ null
+ },
+ span = if (span != null) {
+ { index: Int -> span(items[index]) }
+ } else {
+ null
+ },
+ contentType = { index: Int -> contentType(items[index]) }
+) { index ->
+ itemContent(items[index])
+}
+
+inline fun FeedScope.itemsIndexed(
+ items: Array,
+ noinline key: ((index: Int, item: T) -> Any)? = null,
+ noinline span: (@ExtensionFunctionType LazyGridItemSpanScope.(index: Int, item: T) -> GridItemSpan)? = null,
+ noinline contentType: (index: Int, item: T) -> Any? = { _, _ -> null },
+ crossinline itemContent: @Composable LazyGridItemScope.(index: Int, item: T) -> Unit
+) = items(
+ count = items.size,
+ key = if (key != null) {
+ { index: Int -> key(index, items[index]) }
+ } else {
+ null
+ },
+ span = if (span != null) {
+ { index: Int -> span(index, items[index]) }
+ } else {
+ null
+ },
+ contentType = { index: Int -> contentType(index, items[index]) }
+) { index ->
+ itemContent(index, items[index])
+}
+
+inline fun FeedScope.row(
+ key: Any? = null,
+ contentType: Any? = null,
+ crossinline content: @Composable LazyGridItemScope.() -> Unit
+) = item(
+ key = key,
+ span = { GridItemSpan(maxLineSpan) },
+ contentType = contentType
+) {
+ content()
+}
+
+inline fun FeedScope.title(
+ key: Any? = null,
+ contentType: Any? = null,
+ crossinline content: @Composable LazyGridItemScope.() -> Unit
+) = row(key = key, contentType = contentType) { content() }
+
+inline fun FeedScope.action(
+ key: Any? = null,
+ contentType: Any? = null,
+ horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
+ crossinline content: @Composable RowScope.() -> Unit
+) = row(
+ key = key,
+ contentType = contentType
+) {
+ Row(
+ modifier = Modifier
+ .focusGroup()
+ .horizontalScroll(rememberScrollState()),
+ horizontalArrangement = horizontalArrangement
+ ) {
+ content()
+ }
+}
+
+inline fun FeedScope.single(
+ key: Any? = null,
+ contentType: Any? = null,
+ crossinline content: @Composable LazyGridItemScope.() -> Unit
+) = item(
+ key = key,
+ span = { GridItemSpan(maxLineSpan) },
+ contentType = contentType
+) {
+ content()
+}
+
+
+inline fun FeedScope.footer(
+ key: Any? = null,
+ contentType: Any? = null,
+ crossinline content: @Composable LazyGridItemScope.() -> Unit
+) = item(
+ key = key,
+ span = { GridItemSpan(maxLineSpan) },
+ contentType = contentType
+) {
+ content()
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/core/components/FeedScopeImpl.kt b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/core/components/FeedScopeImpl.kt
new file mode 100644
index 0000000..3c3a027
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/org/onedroid/greedycoder/core/components/FeedScopeImpl.kt
@@ -0,0 +1,61 @@
+package org.onedroid.greedycoder.core.components
+
+
+import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.foundation.lazy.grid.LazyGridItemScope
+import androidx.compose.foundation.lazy.grid.LazyGridItemSpanScope
+import androidx.compose.runtime.Composable
+
+internal class FeedScopeImpl : FeedScope {
+ val items = mutableListOf()
+ override fun item(
+ key: Any?,
+ span: (LazyGridItemSpanScope.() -> GridItemSpan)?,
+ contentType: Any?,
+ content: @Composable LazyGridItemScope.() -> Unit
+ ) {
+ items.add(
+ FeedItem(
+ count = 1,
+ key = if (key != null) {
+ { key }
+ } else {
+ null
+ },
+ span = if (span != null) {
+ { span() }
+ } else {
+ null
+ },
+ contentType = { contentType },
+ itemContent = { content() }
+ )
+ )
+ }
+
+ override fun items(
+ count: Int,
+ key: ((index: Int) -> Any)?,
+ span: (LazyGridItemSpanScope.(index: Int) -> GridItemSpan)?,
+ contentType: (index: Int) -> Any?,
+ itemContent: @Composable LazyGridItemScope.(index: Int) -> Unit
+ ) {
+ items.add(
+ FeedItem(
+ count = count,
+ key = key,
+ span = span,
+ contentType = contentType,
+ itemContent = itemContent
+ )
+ )
+ }
+}
+
+internal data class FeedItem(
+ val count: Int,
+ val key: ((index: Int) -> Any)?,
+ val span: (LazyGridItemSpanScope.(index: Int) -> GridItemSpan)?,
+ val contentType: (index: Int) -> Any?,
+ val itemContent: @Composable LazyGridItemScope.(index: Int) -> Unit
+)
\ No newline at end of file