-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/develop' into release/1.37
- Loading branch information
Showing
91 changed files
with
1,814 additions
and
205 deletions.
There are no files selected for viewing
13 changes: 13 additions & 0 deletions
13
...p/src/main/java/org/hyperskill/app/android/core/view/ui/widget/compose/HyperskillTheme.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package org.hyperskill.app.android.core.view.ui.widget.compose | ||
|
||
import androidx.compose.runtime.Composable | ||
import com.google.accompanist.themeadapter.material.MdcTheme | ||
|
||
@Composable | ||
fun HyperskillTheme(content: @Composable () -> Unit) { | ||
MdcTheme( | ||
setTextColors = true, | ||
setDefaultFontFamily = true, | ||
content = content | ||
) | ||
} |
74 changes: 74 additions & 0 deletions
74
...yperskill/app/android/first_problem_onboarding/fragment/FirstProblemOnboardingFragment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package org.hyperskill.app.android.first_problem_onboarding.fragment | ||
|
||
import android.os.Bundle | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import androidx.compose.ui.platform.ComposeView | ||
import androidx.compose.ui.platform.ViewCompositionStrategy | ||
import androidx.fragment.app.Fragment | ||
import androidx.fragment.app.viewModels | ||
import androidx.lifecycle.ViewModelProvider | ||
import org.hyperskill.app.android.HyperskillApp | ||
import org.hyperskill.app.android.core.view.ui.navigation.requireAppRouter | ||
import org.hyperskill.app.android.core.view.ui.widget.compose.HyperskillTheme | ||
import org.hyperskill.app.android.first_problem_onboarding.ui.FirstProblemOnboardingScreen | ||
import org.hyperskill.app.core.view.handleActions | ||
import org.hyperskill.app.first_problem_onboarding.presentation.FirstProblemOnboardingFeature.Action.ViewAction | ||
import org.hyperskill.app.first_problem_onboarding.presentation.FirstProblemOnboardingViewModel | ||
import ru.nobird.android.view.base.ui.extension.argument | ||
import ru.nobird.android.view.base.ui.extension.snackbar | ||
|
||
class FirstProblemOnboardingFragment : Fragment() { | ||
companion object { | ||
const val FIRST_PROBLEM_ONBOARDING_FINISHED = "FIRST_PROBLEM_ONBOARDING_FINISHED" | ||
fun newInstance(isNewUserMode: Boolean): FirstProblemOnboardingFragment = | ||
FirstProblemOnboardingFragment().apply { | ||
this.isNewUserMode = isNewUserMode | ||
} | ||
} | ||
|
||
private var isNewUserMode: Boolean by argument() | ||
|
||
private var viewModelFactory: ViewModelProvider.Factory? = null | ||
private val firstProblemOnboardingViewModel: FirstProblemOnboardingViewModel by viewModels { | ||
requireNotNull(viewModelFactory) | ||
} | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
injectComponent() | ||
firstProblemOnboardingViewModel.handleActions(this, block = ::onAction) | ||
} | ||
|
||
private fun injectComponent() { | ||
val platformFirstProblemOnboardingComponent = | ||
HyperskillApp.graph().buildPlatformFirstProblemOnboardingComponent(isNewUserMode) | ||
viewModelFactory = platformFirstProblemOnboardingComponent.reduxViewModelFactory | ||
} | ||
|
||
override fun onCreateView( | ||
inflater: LayoutInflater, | ||
container: ViewGroup?, | ||
savedInstanceState: Bundle? | ||
): View = | ||
ComposeView(requireContext()).apply { | ||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner)) | ||
setContent { | ||
HyperskillTheme { | ||
FirstProblemOnboardingScreen(viewModel = firstProblemOnboardingViewModel) | ||
} | ||
} | ||
} | ||
|
||
private fun onAction(action: ViewAction) { | ||
when (action) { | ||
is ViewAction.CompleteFirstProblemOnboarding -> { | ||
requireAppRouter().sendResult(FIRST_PROBLEM_ONBOARDING_FINISHED, action.stepRoute ?: Any()) | ||
} | ||
ViewAction.ShowNetworkError -> { | ||
requireView().snackbar(org.hyperskill.app.R.string.first_problem_onboarding_network_error) | ||
} | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
...yperskill/app/android/first_problem_onboarding/navigation/FirstProblemOnboardingScreen.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package org.hyperskill.app.android.first_problem_onboarding.navigation | ||
|
||
import androidx.fragment.app.Fragment | ||
import androidx.fragment.app.FragmentFactory | ||
import com.github.terrakok.cicerone.androidx.FragmentScreen | ||
import org.hyperskill.app.android.first_problem_onboarding.fragment.FirstProblemOnboardingFragment | ||
|
||
class FirstProblemOnboardingScreen( | ||
private val isNewUserMode: Boolean | ||
) : FragmentScreen { | ||
override fun createFragment(factory: FragmentFactory): Fragment = | ||
FirstProblemOnboardingFragment.newInstance(isNewUserMode) | ||
} |
174 changes: 174 additions & 0 deletions
174
...a/org/hyperskill/app/android/first_problem_onboarding/ui/FirstProblemOnboardingContent.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
package org.hyperskill.app.android.first_problem_onboarding.ui | ||
|
||
import android.content.res.Configuration | ||
import androidx.annotation.DrawableRes | ||
import androidx.compose.foundation.Image | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.material.MaterialTheme | ||
import androidx.compose.material.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.rememberUpdatedState | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.graphics.painter.Painter | ||
import androidx.compose.ui.res.colorResource | ||
import androidx.compose.ui.res.painterResource | ||
import androidx.compose.ui.text.style.TextAlign | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.tooling.preview.PreviewParameter | ||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider | ||
import androidx.compose.ui.unit.sp | ||
import org.hyperskill.app.R | ||
import org.hyperskill.app.android.core.view.ui.widget.compose.HyperskillButton | ||
import org.hyperskill.app.android.core.view.ui.widget.compose.HyperskillTheme | ||
import org.hyperskill.app.first_problem_onboarding.presentation.FirstProblemOnboardingFeature.Message | ||
import org.hyperskill.app.first_problem_onboarding.presentation.FirstProblemOnboardingFeature.ViewState | ||
|
||
@Composable | ||
fun FirstProblemOnboardingContent( | ||
content: ViewState.Content, | ||
onNewMessage: (Message) -> Unit | ||
) { | ||
val onButtonClicked by rememberUpdatedState { | ||
onNewMessage(Message.CallToActionButtonClicked) | ||
} | ||
FirstProblemOnboardingContent( | ||
title = content.title, | ||
subtitle = content.subtitle, | ||
image = painterResource(id = getImage(content.isNewUserMode)), | ||
buttonText = content.buttonText, | ||
onButtonClicked = onButtonClicked | ||
) | ||
} | ||
|
||
@Composable | ||
fun FirstProblemOnboardingContent( | ||
title: String, | ||
subtitle: String, | ||
image: Painter, | ||
buttonText: String, | ||
onButtonClicked: () -> Unit | ||
) { | ||
FirstProblemOnboardingDefaults.ContentRootColumn { | ||
Column( | ||
verticalArrangement = Arrangement.spacedBy(FirstProblemOnboardingDefaults.HeaderBottomPadding) | ||
) { | ||
Text( | ||
text = title, | ||
textAlign = TextAlign.Center, | ||
style = MaterialTheme.typography.h5, | ||
fontSize = 24.sp, | ||
modifier = Modifier.align(Alignment.CenterHorizontally) | ||
) | ||
Text( | ||
text = subtitle, | ||
textAlign = TextAlign.Center, | ||
style = MaterialTheme.typography.body1, | ||
color = colorResource(id = R.color.text_secondary), | ||
modifier = Modifier.align(Alignment.CenterHorizontally) | ||
) | ||
} | ||
Image( | ||
painter = image, | ||
contentDescription = null, | ||
modifier = Modifier.fillMaxWidth() | ||
) | ||
HyperskillButton( | ||
onClick = onButtonClicked, | ||
modifier = Modifier.fillMaxWidth() | ||
) { | ||
Text(text = buttonText) | ||
} | ||
} | ||
} | ||
|
||
private fun getImage(isNewUserMode: Boolean) = | ||
if (isNewUserMode) { | ||
org.hyperskill.app.android.R.drawable.img_first_problem_onboarding_new_user | ||
} else { | ||
org.hyperskill.app.android.R.drawable.img_first_problem_onboarding_experienced_user | ||
} | ||
|
||
private data class FirstProblemOnboardingData( | ||
val title: String, | ||
val subtitle: String, | ||
val buttonText: String, | ||
@DrawableRes val imageRes: Int | ||
) | ||
|
||
private class SampleFirstProblemOnboardingPreviewDataProvider : | ||
PreviewParameterProvider<FirstProblemOnboardingData> { | ||
override val values: Sequence<FirstProblemOnboardingData> = sequenceOf( | ||
FirstProblemOnboardingData( | ||
title = "Let's keep going", | ||
subtitle = "It seems you've already made progress. Continue learning on '{project(or track).title}'!", | ||
buttonText = "Keep learning", | ||
imageRes = getImage(isNewUserMode = false) | ||
), | ||
FirstProblemOnboardingData( | ||
title = "Great choice!", | ||
subtitle = "Embark on your journey in '{project(or track).title}' right now!", | ||
buttonText = "Start learning", | ||
imageRes = getImage(isNewUserMode = true) | ||
) | ||
) | ||
} | ||
|
||
@Preview( | ||
group = "Light theme", | ||
showBackground = true, | ||
showSystemUi = true | ||
) | ||
@Composable | ||
private fun FirstProblemOnboardingScreenLightThemePreview( | ||
@PreviewParameter(SampleFirstProblemOnboardingPreviewDataProvider::class) | ||
onboardingData: FirstProblemOnboardingData | ||
) { | ||
FirstProblemOnboardingScreenPreview(onboardingData) | ||
} | ||
|
||
@Preview( | ||
group = "Dart theme", | ||
showBackground = true, | ||
showSystemUi = true, | ||
uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL | ||
) | ||
@Composable | ||
private fun FirstProblemOnboardingScreenDarkModePreview( | ||
@PreviewParameter(SampleFirstProblemOnboardingPreviewDataProvider::class) | ||
onboardingData: FirstProblemOnboardingData | ||
) { | ||
FirstProblemOnboardingScreenPreview(onboardingData) | ||
} | ||
|
||
@Preview( | ||
group = "Small device", | ||
showBackground = true, | ||
showSystemUi = true, | ||
device = "spec:parent=Nexus S" | ||
) | ||
@Composable | ||
private fun FirstProblemOnboardingScreenSmallDevicePreview( | ||
@PreviewParameter(SampleFirstProblemOnboardingPreviewDataProvider::class) | ||
onboardingData: FirstProblemOnboardingData | ||
) { | ||
FirstProblemOnboardingScreenPreview(onboardingData) | ||
} | ||
|
||
@Composable | ||
private fun FirstProblemOnboardingScreenPreview( | ||
onboardingData: FirstProblemOnboardingData | ||
) { | ||
HyperskillTheme { | ||
FirstProblemOnboardingContent( | ||
title = onboardingData.title, | ||
subtitle = onboardingData.subtitle, | ||
buttonText = onboardingData.buttonText, | ||
image = painterResource(id = onboardingData.imageRes), | ||
onButtonClicked = {} | ||
) | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
.../org/hyperskill/app/android/first_problem_onboarding/ui/FirstProblemOnboardingDefaults.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package org.hyperskill.app.android.first_problem_onboarding.ui | ||
|
||
import androidx.compose.foundation.background | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.ColumnScope | ||
import androidx.compose.foundation.layout.PaddingValues | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.res.colorResource | ||
import androidx.compose.ui.unit.Dp | ||
import androidx.compose.ui.unit.dp | ||
import org.hyperskill.app.R | ||
|
||
object FirstProblemOnboardingDefaults { | ||
val ContentPadding: PaddingValues = PaddingValues( | ||
top = 24.dp, | ||
bottom = 32.dp, | ||
start = 20.dp, | ||
end = 20.dp | ||
) | ||
|
||
val HeaderBottomPadding: Dp = 8.dp | ||
|
||
@Composable | ||
fun ContentRootColumn( | ||
modifier: Modifier = Modifier, | ||
verticalArrangement: Arrangement.Vertical = Arrangement.SpaceBetween, | ||
content: @Composable ColumnScope.() -> Unit | ||
) { | ||
Column( | ||
modifier = modifier | ||
.fillMaxSize() | ||
.background(colorResource(id = R.color.layer_1)) | ||
.padding(ContentPadding), | ||
verticalArrangement = verticalArrangement, | ||
content = content | ||
) | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
.../org/hyperskill/app/android/first_problem_onboarding/ui/FirstProblemOnboardingSkeleton.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package org.hyperskill.app.android.first_problem_onboarding.ui | ||
|
||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.width | ||
import androidx.compose.material.MaterialTheme | ||
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.res.dimensionResource | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.unit.dp | ||
import org.hyperskill.app.android.R | ||
import org.hyperskill.app.android.core.view.ui.widget.compose.HyperskillTheme | ||
import org.hyperskill.app.android.core.view.ui.widget.compose.ShimmerLoading | ||
|
||
@Composable | ||
fun FirstProblemOnboardingSkeleton() { | ||
FirstProblemOnboardingDefaults.ContentRootColumn { | ||
Column( | ||
modifier = Modifier.weight(1f).fillMaxWidth() | ||
) { | ||
ShimmerLoading( | ||
radius = dimensionResource(id = R.dimen.corner_radius), | ||
modifier = Modifier | ||
.width(160.dp) | ||
.height(28.dp) | ||
.align(Alignment.CenterHorizontally) | ||
) | ||
Spacer(modifier = Modifier.height(FirstProblemOnboardingDefaults.HeaderBottomPadding)) | ||
ShimmerLoading( | ||
radius = dimensionResource(id = R.dimen.corner_radius), | ||
modifier = Modifier | ||
.width(320.dp) | ||
.height(16.dp) | ||
.align(Alignment.CenterHorizontally) | ||
) | ||
Spacer(modifier = Modifier.height(4.dp)) | ||
ShimmerLoading( | ||
radius = dimensionResource(id = R.dimen.corner_radius), | ||
modifier = Modifier | ||
.width(280.dp) | ||
.height(16.dp) | ||
.align(Alignment.CenterHorizontally) | ||
) | ||
} | ||
ShimmerLoading( | ||
radius = dimensionResource(id = R.dimen.corner_radius), | ||
modifier = Modifier | ||
.clip(MaterialTheme.shapes.small) | ||
.fillMaxWidth() | ||
.height(dimensionResource(id = R.dimen.action_button_height)) | ||
) | ||
} | ||
} | ||
|
||
@Preview( | ||
showBackground = true, | ||
showSystemUi = true | ||
) | ||
@Composable | ||
private fun FirstProblemOnboardingSkeletonPreview() { | ||
HyperskillTheme { | ||
FirstProblemOnboardingSkeleton() | ||
} | ||
} |
Oops, something went wrong.