diff --git a/.github/workflows/ci-dev.yml b/.github/workflows/ci-dev.yml index c4cfedf..9a69b06 100644 --- a/.github/workflows/ci-dev.yml +++ b/.github/workflows/ci-dev.yml @@ -2,31 +2,37 @@ name: ci-dev.yml on: push: - branches: [ dev ] + branches: + - dev + workflow_dispatch: jobs: unit-test: + if: ${{ github.actor != 'dependabot[bot]' }} name: Run Unit Tests runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: 11 + java-version: '17.0.8' + distribution: 'temurin' + cache: gradle - name: Unit tests run: bash ./gradlew test --stacktrace integration-test: + if: ${{ github.actor != 'dependabot[bot]' }} name: Run Integration Tests runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '17.0.8' distribution: 'temurin' cache: gradle - name: Unit tests @@ -35,22 +41,28 @@ jobs: run: chmod +x gradlew - name: Build with Gradle run: ./gradlew build -# apk: -# name: Generate APK -# runs-on: ubuntu-latest -# steps: -# - name: Checkout -# uses: actions/checkout@v3 -# - name: set up JDK 11 -# uses: actions/setup-java@v1 -# with: -# java-version: '11' -# distribution: 'temurin' -# cache: gradle -# - name: Build debug APK -# run: bash ./gradlew assembleDebug --stacktrace -# - name: Upload APK -# uses: actions/upload-artifact@v3 -# with: -# name: app -# path: app/build/outputs/apk/debug/app-debug.apk + apk: + if: ${{ github.actor != 'dependabot[bot]' }} + name: Generate APK + runs-on: ubuntu-latest + needs: [ unit-test, integration-test ] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17.0.8' + distribution: 'temurin' + cache: gradle + - name: Build debug APK + run: bash ./gradlew assembleRelease --stacktrace + - name: Upload APK to App Center + uses: wzieba/AppCenter-Github-Action@v1 + with: + appName: Code-Crow-Corp/Mage-Android + token: ${{secrets.APP_CENTER_TOKEN}} + group: Testers + file: app/build/outputs/apk/release/app-release-unsigned.apk + notifyTesters: true + debug: false diff --git a/.gitignore b/.gitignore index 1bc29aa..06e7f5a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ .externalNativeBuild .cxx local.properties +app/google-services.json diff --git a/app/.gitignore b/app/.gitignore index 42afabf..65d12b9 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1 +1,2 @@ -/build \ No newline at end of file +/build +google-services.json \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 647598f..f527460 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -29,7 +29,7 @@ android:supportsRtl="true" android:theme="@style/Theme.MageApplication" android:name=".MageApplication" - tools:targetApi="31"> + tools:targetApi="33"> >> + + fun getChannelByID(channelId: String) : Flow> } \ No newline at end of file diff --git a/app/src/main/java/io/codecrow/mage/data/datasource/ChannelRemoteImpl.kt b/app/src/main/java/io/codecrow/mage/data/datasource/ChannelRemoteImpl.kt index 38e3c02..3643dc5 100644 --- a/app/src/main/java/io/codecrow/mage/data/datasource/ChannelRemoteImpl.kt +++ b/app/src/main/java/io/codecrow/mage/data/datasource/ChannelRemoteImpl.kt @@ -27,4 +27,13 @@ class ChannelRemoteImpl @Inject constructor( } } + override fun getChannelByID(channelId: String): Flow> { + return flow { + emit(Resource.loading()) + emit(Resource.success(data = channelMapper.map(channelApi.getChannel(channelId)))) + }.catch { e -> + emit(Resource.error(e)) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/io/codecrow/mage/data/service/ChannelApi.kt b/app/src/main/java/io/codecrow/mage/data/service/ChannelApi.kt index 7ad04b5..578220f 100644 --- a/app/src/main/java/io/codecrow/mage/data/service/ChannelApi.kt +++ b/app/src/main/java/io/codecrow/mage/data/service/ChannelApi.kt @@ -17,7 +17,7 @@ interface ChannelApi { suspend fun deleteChannel(@Query("channelId") channelId: String): List @GET("channel") - suspend fun getChannel(@Query("channelId") channelId: String): Channel + suspend fun getChannel(@Query("channelId") channelId: String): ChannelResponseItem @GET("channels/friend") suspend fun getFriendChannel(@Query("title") title: String): Channel diff --git a/app/src/main/java/io/codecrow/mage/model/Channel.kt b/app/src/main/java/io/codecrow/mage/model/Channel.kt new file mode 100644 index 0000000..6037f29 --- /dev/null +++ b/app/src/main/java/io/codecrow/mage/model/Channel.kt @@ -0,0 +1,46 @@ +package io.codecrow.mage.model +import com.google.gson.annotations.Expose +import com.google.gson.annotations.SerializedName +import io.codecrow.mage.remote.model.UserDetails + +data class Channel( + @SerializedName("_id") + @Expose + var _id: String, + + @SerializedName("title") + @Expose + var title: String, + + @SerializedName("description") + @Expose + var description: String, + + @SerializedName("thumbnail") + @Expose + var thumbnail: String, + + @SerializedName("category") + @Expose + var category: List, + + @SerializedName("tags") + @Expose + var tags: List, + + @SerializedName("user") + @Expose + var user: String, + + @SerializedName("memberCount") + @Expose + var memberCount: Int, + + @SerializedName("channelType") + @Expose + var channelType: String, + + @SerializedName("userDetails") + @Expose + var userDetails: UserDetails, +) diff --git a/app/src/main/java/io/codecrow/mage/remote/mapper/ChannelMapper.kt b/app/src/main/java/io/codecrow/mage/remote/mapper/ChannelMapper.kt index 580253c..1879a85 100644 --- a/app/src/main/java/io/codecrow/mage/remote/mapper/ChannelMapper.kt +++ b/app/src/main/java/io/codecrow/mage/remote/mapper/ChannelMapper.kt @@ -13,13 +13,15 @@ constructor() : override suspend fun map(input: ChannelResponseItem): Channel { return Channel( + id = input.id.orEmpty(), title = input.title.orEmpty(), avatar = input.userDetails?.avatar.orEmpty(), - createdByUsername = input.userDetails?.username.orEmpty() + createdByUsername = input.userDetails?.username.orEmpty(), + memberCount = input.memberCount.toString() ) } - suspend fun mapAllChannels(botItems : ArrayList) : ArrayList{ + suspend fun mapAllChannels(botItems: ArrayList): ArrayList { val allBots = ArrayList() Log.i("__TAG", GsonBuilder().setPrettyPrinting().create().toJson(botItems)) botItems.forEachIndexed { index, channelResponseItem -> diff --git a/app/src/main/java/io/codecrow/mage/remote/model/Channel.kt b/app/src/main/java/io/codecrow/mage/remote/model/Channel.kt index cf1334d..76ff7fd 100644 --- a/app/src/main/java/io/codecrow/mage/remote/model/Channel.kt +++ b/app/src/main/java/io/codecrow/mage/remote/model/Channel.kt @@ -3,7 +3,9 @@ import com.google.gson.annotations.Expose import com.google.gson.annotations.SerializedName data class Channel( + var id : String, var title: String, var createdByUsername: String, var avatar: String, + var memberCount : String ) diff --git a/app/src/main/java/io/codecrow/mage/remote/model/UserDetails.kt b/app/src/main/java/io/codecrow/mage/remote/model/UserDetails.kt new file mode 100644 index 0000000..0f40fd3 --- /dev/null +++ b/app/src/main/java/io/codecrow/mage/remote/model/UserDetails.kt @@ -0,0 +1,17 @@ +package io.codecrow.mage.remote.model +import com.google.gson.annotations.Expose +import com.google.gson.annotations.SerializedName + +data class UserDetails( + @SerializedName("avatar") + @Expose + var avatar: String, + + @SerializedName("displayName") + @Expose + var displayName: String, + + @SerializedName("username") + @Expose + var username: String, +) diff --git a/app/src/main/java/io/codecrow/mage/ui/MainActivity.kt b/app/src/main/java/io/codecrow/mage/ui/MainActivity.kt index 8dfea49..42dc7b9 100644 --- a/app/src/main/java/io/codecrow/mage/ui/MainActivity.kt +++ b/app/src/main/java/io/codecrow/mage/ui/MainActivity.kt @@ -71,3 +71,8 @@ class MainActivity : ComponentActivity() { } } +// +//composable("channel/{channelId}") { backStackEntry -> +// val channelId = backStackEntry.arguments?.getString("channelId") +// ChannelScreen(channelId) +//} \ No newline at end of file diff --git a/app/src/main/java/io/codecrow/mage/ui/Navigation.kt b/app/src/main/java/io/codecrow/mage/ui/Navigation.kt index 404e94b..7d0743d 100644 --- a/app/src/main/java/io/codecrow/mage/ui/Navigation.kt +++ b/app/src/main/java/io/codecrow/mage/ui/Navigation.kt @@ -20,17 +20,34 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import io.codecrow.mage.ui.browse.BrowseScreen +import io.codecrow.mage.ui.channel.ChannelScreen +import io.codecrow.mage.ui.channel.ChannelViewModel @Composable fun MainNavigation() { val navController = rememberNavController() NavHost(navController = navController, startDestination = "main") { - composable("main") { BrowseScreen(modifier = Modifier.padding(16.dp)) } - // TODO: Add more destinations + composable("main") { + BrowseScreen( + navController = navController, + modifier = Modifier.padding(16.dp) + ) + } + composable("channel/{channelId}") { backStackEntry -> + val channelId = backStackEntry.arguments?.getString("channelId") ?: "" + val viewModel = hiltViewModel() + ChannelScreen( + navController = navController, + modifier = Modifier.padding(16.dp), + viewModel = viewModel, + channelID = channelId + ) + } } } diff --git a/app/src/main/java/io/codecrow/mage/ui/browse/BrowseScreen.kt b/app/src/main/java/io/codecrow/mage/ui/browse/BrowseScreen.kt index 7d137f0..ab4076b 100644 --- a/app/src/main/java/io/codecrow/mage/ui/browse/BrowseScreen.kt +++ b/app/src/main/java/io/codecrow/mage/ui/browse/BrowseScreen.kt @@ -16,7 +16,6 @@ package io.codecrow.mage.ui.browse -import android.widget.Toast import androidx.compose.foundation.* import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior @@ -28,31 +27,23 @@ import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.Lifecycle.State.STARTED -import androidx.lifecycle.repeatOnLifecycle import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp +import androidx.navigation.NavController import io.codecrow.mage.remote.model.Channel import io.codecrow.mage.remote.utils.DataStatus import io.codecrow.mage.ui.components.TitleTextStyle import io.codecrow.mage.ui.theme.* - @Composable -fun BrowseScreen(modifier: Modifier = Modifier, viewModel: BrowseViewModel = hiltViewModel()) { - val lifecycle = LocalLifecycleOwner.current.lifecycle +fun BrowseScreen(navController: NavController, modifier: Modifier = Modifier, viewModel: BrowseViewModel = hiltViewModel()) { val context = LocalContext.current - val channelListDetail by viewModel.channelListState.collectAsState() when (channelListDetail.status) { @@ -63,11 +54,9 @@ fun BrowseScreen(modifier: Modifier = Modifier, viewModel: BrowseViewModel = hil channelListDetail.data?.let { BrowseScreen( items = it, - enterChannel = viewModel::enterChannel, modifier = modifier, onClick = { - Toast.makeText(context, it.title, Toast.LENGTH_LONG).show() - Toast.makeText(context, it.avatar, Toast.LENGTH_LONG).show() + navController.navigate("channel/$it") } ) } @@ -79,30 +68,24 @@ fun BrowseScreen(modifier: Modifier = Modifier, viewModel: BrowseViewModel = hil @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable internal fun BrowseScreen( - items: ArrayList, - enterChannel: (_id: String) -> Unit, - modifier: Modifier = Modifier, - onClick: (Channel) -> Unit = {} + items: List, modifier: Modifier = Modifier, onClick: (String) -> Unit ) { val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState()) - val state = rememberLazyListState() - val snappingLayout = remember(state) { SnapLayoutInfoProvider(state) } + val snappingLayout = + remember(state) { SnapLayoutInfoProvider(state) { _: Int, _: Int, _: Int -> 0 } } val flingBehavior = rememberSnapFlingBehavior(snappingLayout) Scaffold( - - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), - topBar = { + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { CenterAlignedTopAppBar( - title = { + title = { TitleTextStyle() }, colors = TopAppBarDefaults.mediumTopAppBarColors(containerColor = MaterialTheme.colorScheme.surface), scrollBehavior = scrollBehavior ) - }, - content = { + }, content = { // var nameBrowse by remember { mutableStateOf("Compose") } LazyColumn( modifier = Modifier @@ -111,9 +94,9 @@ internal fun BrowseScreen( state = state, flingBehavior = flingBehavior, - ) { + ) { items(items) { it: Channel -> - ChannelItem(it) + ChannelItem(it) { channelId -> onClick(channelId) } } } }) @@ -127,13 +110,15 @@ private fun PortraitPreview() { val channels = arrayListOf( Channel( - "", + "1", + "Test", "VideoTitle", "des", + "0" ) ) MyApplicationTheme { - BrowseScreen(channels, enterChannel = {}) + BrowseScreen(channels, onClick = {}) } } @@ -143,13 +128,15 @@ private fun LandscapePreview() { val channels = arrayListOf( Channel( + "2", "", "VideoTitle", "des", + "0" ) ) MyApplicationTheme { - BrowseScreen(channels, enterChannel = {}) + BrowseScreen(channels, onClick = {}) } } @@ -161,8 +148,8 @@ fun LoadingView() { Column( modifier = Modifier.fillMaxSize() ) { - repeat(5) { - LoadingChannelItem() - } + repeat(5) { + LoadingChannelItem() + } } -} +} \ No newline at end of file diff --git a/app/src/main/java/io/codecrow/mage/ui/browse/BrowseViewModel.kt b/app/src/main/java/io/codecrow/mage/ui/browse/BrowseViewModel.kt index 31d5803..6eea000 100644 --- a/app/src/main/java/io/codecrow/mage/ui/browse/BrowseViewModel.kt +++ b/app/src/main/java/io/codecrow/mage/ui/browse/BrowseViewModel.kt @@ -29,7 +29,7 @@ import javax.inject.Inject @HiltViewModel class BrowseViewModel @Inject constructor( -private val channelRemote: ChannelRemote + private val channelRemote: ChannelRemote ) : ViewModel() { private val _channelListState: MutableStateFlow>> = diff --git a/app/src/main/java/io/codecrow/mage/ui/browse/ChannelItem.kt b/app/src/main/java/io/codecrow/mage/ui/browse/ChannelItem.kt index 3ee84f8..32f41c3 100644 --- a/app/src/main/java/io/codecrow/mage/ui/browse/ChannelItem.kt +++ b/app/src/main/java/io/codecrow/mage/ui/browse/ChannelItem.kt @@ -23,14 +23,14 @@ import io.codecrow.mage.ui.components.ChannelViewersItem import io.codecrow.mage.ui.components.UserProfileImage @Composable -fun ChannelItem(channel: Channel) { +fun ChannelItem(channel: Channel, onClick: (channelId: String) -> Unit) { Column( modifier = Modifier .fillMaxWidth() .height(400.dp) //TODO: set min height .padding(10.dp) .clip(RoundedCornerShape(4.dp)) -// .clickable { onClick(it) } + .clickable { onClick(channel.id) } ) { Card( elevation = CardDefaults.cardElevation(), @@ -59,7 +59,7 @@ fun ChannelItem(channel: Channel) { verticalAlignment = Alignment.Top, horizontalArrangement = Arrangement.Start ) { - ChannelViewersItem() + ChannelViewersItem(channel) } Row( modifier = Modifier @@ -121,7 +121,7 @@ fun ChannelItem(channel: Channel) { ) ) { Text( - text = "@"+channel.createdByUsername, + text = "@" + channel.createdByUsername, color = MaterialTheme.colorScheme.onPrimary, style = TextStyle( // fontFamily = FontFamily("Montserrat"), diff --git a/app/src/main/java/io/codecrow/mage/ui/browse/LoadingChannelItem.kt b/app/src/main/java/io/codecrow/mage/ui/browse/LoadingChannelItem.kt index ce7ecad..c5de17b 100644 --- a/app/src/main/java/io/codecrow/mage/ui/browse/LoadingChannelItem.kt +++ b/app/src/main/java/io/codecrow/mage/ui/browse/LoadingChannelItem.kt @@ -106,20 +106,20 @@ fun LoadingChannelItem() { verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center ) { - Box{ - Spacer( - modifier = Modifier - .size(48.dp) - .clip(CircleShape) - .background(brush) - ) //{ + Box { + Spacer( + modifier = Modifier + .size(48.dp) + .clip(CircleShape) + .background(brush) + ) //{ Icon( imageVector = Icons.Default.PlayArrow, contentDescription = "Play button", modifier = Modifier.size(48.dp), tint = Color.White.copy(alpha = 0.2f), ) - } + } } Row( modifier = Modifier @@ -185,6 +185,7 @@ private fun PortraitPreview() { LoadingChannelItem() } } + @Preview(showBackground = true) @Composable private fun LandscapePreview() { diff --git a/app/src/main/java/io/codecrow/mage/ui/channel/ChannelDetail.kt b/app/src/main/java/io/codecrow/mage/ui/channel/ChannelDetail.kt new file mode 100644 index 0000000..7d3faea --- /dev/null +++ b/app/src/main/java/io/codecrow/mage/ui/channel/ChannelDetail.kt @@ -0,0 +1,109 @@ +package io.codecrow.mage.ui.channel + +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.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +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.draw.clip +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.codecrow.mage.remote.model.Channel +import io.codecrow.mage.ui.components.ChannelViewersItem + +@Composable +fun ChannelDetail(channel: Channel, onClick: () -> Unit) { + Column( + modifier = Modifier + .fillMaxWidth() + .height(400.dp) //TODO: set min height + .padding(10.dp) + .clip(RoundedCornerShape(4.dp)) +// .clickable { onClick(it) } + ) { + Card( + elevation = CardDefaults.cardElevation(), + shape = RoundedCornerShape(8.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), + ) { + Box { + Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + ) { + //TODO: add video thumbnail here + } + Column( + modifier = Modifier.padding( + start = 10.dp, + end = 10.dp, + bottom = 10.dp + ) + ) { + Row( + modifier = Modifier + .weight(1F) + .fillMaxWidth(), + verticalAlignment = Alignment.Top, + horizontalArrangement = Arrangement.Start + ) { + ChannelViewersItem(channel) + } + Row( + modifier = Modifier + .weight(1F) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) {} + Row( + modifier = Modifier + .weight(1F) + .fillMaxWidth(), + verticalAlignment = Alignment.Top, + horizontalArrangement = Arrangement.Start + ) { + repeat(5) { + Column( + Modifier + .fillMaxWidth() + .padding( + top = 25.dp, + bottom = 25.dp + ) + ) { + Text( + text = "@" + channel.createdByUsername, + color = MaterialTheme.colorScheme.onPrimary, + style = TextStyle( + // fontFamily = FontFamily("Montserrat"), + fontSize = 17.sp, + fontWeight = FontWeight.W500, + lineHeight = 22.sp, + letterSpacing = 0.sp, + textAlign = TextAlign.Left + ) + ) + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/codecrow/mage/ui/channel/ChannelScreen.kt b/app/src/main/java/io/codecrow/mage/ui/channel/ChannelScreen.kt new file mode 100644 index 0000000..3cfa1fe --- /dev/null +++ b/app/src/main/java/io/codecrow/mage/ui/channel/ChannelScreen.kt @@ -0,0 +1,109 @@ +package io.codecrow.mage.ui.channel + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavController +import io.codecrow.mage.remote.model.Channel +import io.codecrow.mage.remote.utils.DataStatus +import io.codecrow.mage.ui.browse.LoadingView +import io.codecrow.mage.ui.theme.MyApplicationTheme + +@Composable +fun ChannelScreen( + modifier: Modifier = Modifier, + navController: NavController, + viewModel: ChannelViewModel = hiltViewModel(), + channelID : String +) { + val channelDetail by viewModel.channelState.collectAsState() + + LaunchedEffect(Unit) { + viewModel.getChannelById(channelID) + } + + when (channelDetail.status) { + DataStatus.LOADING -> { + LoadingView() + } + DataStatus.SUCCESS -> { + channelDetail.data.let { + it?.let { it1 -> + ChannelScreen( + items = it1, + modifier = modifier, + onClick = { + navController.navigate("channel/$it") + } + ) + } + } + } + else -> {} + } +} + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) +@Composable +internal fun ChannelScreen( + items: Channel, + modifier: Modifier = Modifier, + onClick: (String) -> Unit = {} +) { + Text( + text = "Yo", + color = Color.Red, + style = TextStyle( + // fontFamily = FontFamily(Font(R.font.montserrat)), + fontSize = 22.sp, + fontWeight = FontWeight.W600, + lineHeight = 22.sp, + letterSpacing = 0.sp, + textAlign = TextAlign.Start + ) + ) +} + +// Previews + +@Preview(showBackground = true) +@Composable +private fun PortraitPreview() { + val channel = Channel( + "", + "VideoTitle", + "des", + "", + "" + ) + MyApplicationTheme { + ChannelScreen(channel) + } +} + +@Preview(showBackground = true, widthDp = 480) +@Composable +private fun LandscapePreview() { + val channel = Channel( + "", + "VideoTitle", + "des", + "", + "" + ) + MyApplicationTheme { + ChannelScreen(channel) + } +} diff --git a/app/src/main/java/io/codecrow/mage/ui/channel/ChannelViewModel.kt b/app/src/main/java/io/codecrow/mage/ui/channel/ChannelViewModel.kt new file mode 100644 index 0000000..61fdf34 --- /dev/null +++ b/app/src/main/java/io/codecrow/mage/ui/channel/ChannelViewModel.kt @@ -0,0 +1,30 @@ +package io.codecrow.mage.ui.channel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import io.codecrow.mage.data.datasource.ChannelRemote +import io.codecrow.mage.remote.model.Channel +import io.codecrow.mage.remote.utils.Resource +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +@HiltViewModel +class ChannelViewModel @Inject constructor( + private val channelRemote: ChannelRemote +) : ViewModel() { + + private val _channelState: MutableStateFlow> = + MutableStateFlow(Resource.loading()) + val channelState = _channelState.asStateFlow() + + fun getChannelById(channelID : String) { + channelRemote.getChannelByID(channelID).map { + _channelState.value = it + }.launchIn(viewModelScope) + } + +} diff --git a/app/src/main/java/io/codecrow/mage/ui/components/ChannelViewersItem.kt b/app/src/main/java/io/codecrow/mage/ui/components/ChannelViewersItem.kt index 65abbc4..400fda6 100644 --- a/app/src/main/java/io/codecrow/mage/ui/components/ChannelViewersItem.kt +++ b/app/src/main/java/io/codecrow/mage/ui/components/ChannelViewersItem.kt @@ -13,11 +13,12 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import io.codecrow.mage.R +import io.codecrow.mage.remote.model.Channel @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ChannelViewersItem() { +fun ChannelViewersItem(channel: Channel) { AssistChip( label = { Text(text = "LIVE") }, colors = AssistChipDefaults.assistChipColors( @@ -30,7 +31,7 @@ fun ChannelViewersItem() { Spacer(modifier = Modifier.width(5.dp)) AssistChip( - label = { Text(text = "158") }, + label = { Text(text = channel.memberCount.toString()) }, leadingIcon = { Image( painter = painterResource(id = R.drawable.ic_viewers_24), @@ -45,4 +46,5 @@ fun ChannelViewersItem() { border = null, onClick = {} ) -} \ No newline at end of file +} + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 680cede..0fc1fc3 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,46 +1,20 @@ - - - - - - - - - - - - - - - + + + + - \ No newline at end of file + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index cdb66c1..0000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..d27b325 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index cee84d3..0000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b68724f Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index fb130b2..0000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..b8e7494 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index e5e81ce..0000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1c7c342 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 78a0fcf..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..ad89030 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 5d2cde0..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..eae7a64 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index f99dcb5..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..b5323fe Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 63553f7..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..80fa1c8 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index aa9f00d..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..61220f3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 579e8bc..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..a7829d8 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bd8a4b3..456c861 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -15,5 +15,5 @@ --> - MageApplication + Mage diff --git a/build.gradle.kts b/build.gradle.kts index 6339c05..3ed3ba4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,4 +14,4 @@ * limitations under the License. */ -// Root build.gradle.kts +// Root build.gradle.kts \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c9814b7..8fdf4cd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,13 +1,13 @@ [versions] -androidGradlePlugin = "8.0.1" +androidGradlePlugin = "8.1.0" androidxCore = "1.10.1" androidxLifecycle = "2.6.1" androidxActivity = "1.7.2" -androidxComposeUi = "1.4.3" +androidxComposeUi = "1.5.0" androidxComposeCompiler = "1.5.1" androidxComposeMaterial3 = "1.1.1" androidxHilt = "1.0.0" -androidxNavigation = "2.6.0" +androidxNavigation = "2.7.0" androidxRoom = "2.5.2" androidxTest = "1.5.0" # https://github.com/android/android-test/issues/1412 androidxTestExt = "1.1.5" @@ -63,4 +63,4 @@ android-library = { id = "com.android.library", version.ref = "androidGradlePlug kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp"} -hilt-gradle = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } \ No newline at end of file +hilt-gradle = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ff5e513..712bb2d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ #Thu May 04 23:18:07 CDT 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists