Skip to content

Commit

Permalink
Merge branch 'develop' into dependabot/bundler/androidHyperskillApp/d…
Browse files Browse the repository at this point in the history
…evelop/fastlane-2.216.0
  • Loading branch information
ivan-magda authored Sep 22, 2023
2 parents 80f3dd5 + d0f8700 commit 570db5a
Show file tree
Hide file tree
Showing 90 changed files with 1,548 additions and 180 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.hyperskill.app.android.core.view.ui.widget.compose

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material.Button
import androidx.compose.material.ButtonColors
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.ButtonElevation
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ProvideTextStyle
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import org.hyperskill.app.R

object HyperskillButtonDefaults {
private val ButtonVerticalPadding = 14.dp
private val ButtonHorizontalPadding = 16.dp

private val TextButtonVerticalPadding = 8.dp

val ContentPadding = PaddingValues(
vertical = ButtonVerticalPadding,
horizontal = ButtonHorizontalPadding
)

val TextButtonContentPadding = PaddingValues(
vertical = TextButtonVerticalPadding,
horizontal = ButtonHorizontalPadding
)

@Composable
fun buttonColors(
backgroundColor: Color = colorResource(id = R.color.button_primary)
): ButtonColors =
ButtonDefaults.buttonColors(
backgroundColor = backgroundColor
)

@Composable
fun textButtonColors(): ButtonColors =
ButtonDefaults.textButtonColors()
}

@Composable
fun HyperskillButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = null,
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = null,
colors: ButtonColors = HyperskillButtonDefaults.buttonColors(),
contentPadding: PaddingValues = HyperskillButtonDefaults.ContentPadding,
content: @Composable RowScope.() -> Unit
) {
Button(
onClick = onClick,
modifier = modifier,
enabled = enabled,
interactionSource = interactionSource,
elevation = elevation,
shape = shape,
border = border,
colors = colors,
contentPadding = contentPadding,
content = content
)
}

@Composable
fun HyperskillTextButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = null,
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = null,
colors: ButtonColors = HyperskillButtonDefaults.textButtonColors(),
contentPadding: PaddingValues = HyperskillButtonDefaults.TextButtonContentPadding,
content: @Composable RowScope.() -> Unit
) {
TextButton(
onClick = onClick,
modifier = modifier,
enabled = enabled,
interactionSource = interactionSource,
elevation = elevation,
shape = shape,
border = border,
colors = colors,
contentPadding = contentPadding,
content = {
ProvideTextStyle(value = MaterialTheme.typography.textButton) {
content()
}
}
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.hyperskill.app.android.core.view.ui.widget.compose

import androidx.compose.material.Typography
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.TextStyle

val Typography.textButton: TextStyle
@Composable
get() = button.copy(
color = colorResource(id = org.hyperskill.app.R.color.button_ghost)
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.hyperskill.app.android.main.view.ui.activity

import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
Expand Down Expand Up @@ -34,6 +36,8 @@ import org.hyperskill.app.android.notification.model.ClickedNotificationData
import org.hyperskill.app.android.notification.model.DailyStudyReminderClickedData
import org.hyperskill.app.android.notification.model.DefaultNotificationClickedData
import org.hyperskill.app.android.notification.model.PushNotificationClickedData
import org.hyperskill.app.android.notification_onboarding.fragment.NotificationsOnboardingFragment
import org.hyperskill.app.android.notification_onboarding.navigation.NotificationsOnboardingScreen
import org.hyperskill.app.android.onboarding.navigation.OnboardingScreen
import org.hyperskill.app.android.profile_settings.view.mapper.ThemeMapper
import org.hyperskill.app.android.streak_recovery.view.delegate.StreakRecoveryViewActionDelegate
Expand Down Expand Up @@ -87,6 +91,7 @@ class MainActivity :
)
}

@SuppressLint("InlinedApi")
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()

Expand Down Expand Up @@ -116,15 +121,8 @@ class MainActivity :

startupViewModel(intent)

lifecycleScope.launch {
router
.observeResult(AuthFragment.AUTH_SUCCESS)
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collectLatest {
val profile = (it as? Profile) ?: return@collectLatest
mainViewModel.onNewMessage(AppFeature.Message.UserAuthorized(profile))
}
}
observeAuthFlowSuccess()
observeNotificationsOnboardingFlowFinished()

AppCompatDelegate.setDefaultNightMode(ThemeMapper.getAppCompatDelegate(profileSettings.theme))

Expand Down Expand Up @@ -158,6 +156,38 @@ class MainActivity :
}
}

@SuppressLint("InlinedApi")
private fun observeAuthFlowSuccess() {
lifecycleScope.launch {
router
.observeResult(AuthFragment.AUTH_SUCCESS)
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collectLatest {
val profile = (it as? Profile) ?: return@collectLatest
mainViewModel.onNewMessage(
AppFeature.Message.UserAuthorized(
profile = profile,
isNotificationPermissionGranted = ContextCompat.checkSelfPermission(
this@MainActivity,
android.Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
)
)
}
}
}

private fun observeNotificationsOnboardingFlowFinished() {
lifecycleScope.launch {
router
.observeResult(NotificationsOnboardingFragment.NOTIFICATIONS_ONBOARDING_FINISHED)
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collectLatest {
mainViewModel.onNewMessage(AppFeature.Message.NotificationOnboardingCompleted)
}
}
}

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent != null) {
Expand Down Expand Up @@ -195,6 +225,8 @@ class MainActivity :
TrackSelectionListParams(isNewUserMode = true)
)
)
is AppFeature.Action.ViewAction.NavigateTo.NotificationOnBoardingScreen ->
router.newRootScreen(NotificationsOnboardingScreen)
is AppFeature.Action.ViewAction.StreakRecoveryViewAction ->
StreakRecoveryViewActionDelegate.handleViewAction(
fragmentManager = supportFragmentManager,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.hyperskill.app.android.notification_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.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import com.google.accompanist.themeadapter.material.MdcTheme
import org.hyperskill.app.android.HyperskillApp
import org.hyperskill.app.android.core.view.ui.navigation.requireAppRouter
import org.hyperskill.app.android.notification.permission.NotificationPermissionDelegate
import org.hyperskill.app.android.notification_onboarding.ui.NotificationsOnboardingScreen
import org.hyperskill.app.core.view.handleActions
import org.hyperskill.app.notifications_onboarding.presentation.NotificationsOnboardingFeature.Action.ViewAction
import org.hyperskill.app.notifications_onboarding.presentation.NotificationsOnboardingFeature.Message
import org.hyperskill.app.notifications_onboarding.presentation.NotificationsOnboardingViewModel

class NotificationsOnboardingFragment : Fragment() {

companion object {
const val NOTIFICATIONS_ONBOARDING_FINISHED = "NOTIFICATIONS_ONBOARDING_FINISHED"
fun newInstance(): NotificationsOnboardingFragment =
NotificationsOnboardingFragment()
}

private var viewModelFactory: ViewModelProvider.Factory? = null
private val notificationsOnboardingViewModel: NotificationsOnboardingViewModel by viewModels {
requireNotNull(viewModelFactory)
}

private val notificationPermissionDelegate: NotificationPermissionDelegate =
NotificationPermissionDelegate(this)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
injectComponent()
notificationsOnboardingViewModel.handleActions(this, block = ::onAction)
}

private fun injectComponent() {
val notificationOnboardingComponent =
HyperskillApp.graph().buildPlatformNotificationOnboardingComponent()
viewModelFactory = notificationOnboardingComponent.reduxViewModelFactory
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View =
ComposeView(requireContext()).apply {
setContent {
MdcTheme(
setTextColors = true,
setDefaultFontFamily = true
) {
NotificationsOnboardingScreen(notificationsOnboardingViewModel)
}
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
notificationsOnboardingViewModel.onNewMessage(Message.ViewedEventMessage)
}

private fun onAction(action: ViewAction) {
when (action) {
ViewAction.CompleteNotificationOnboarding -> {
requireAppRouter().sendResult(NOTIFICATIONS_ONBOARDING_FINISHED, Any())
}
ViewAction.RequestNotificationPermission -> {
notificationPermissionDelegate.requestNotificationPermission { result ->
val isPermissionGranted = when (result) {
NotificationPermissionDelegate.Result.GRANTED -> true
NotificationPermissionDelegate.Result.DENIED,
NotificationPermissionDelegate.Result.DONT_ASK -> false
}
notificationsOnboardingViewModel.onNewMessage(
Message.NotificationPermissionRequestResult(isPermissionGranted)
)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.hyperskill.app.android.notification_onboarding.navigation

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import com.github.terrakok.cicerone.androidx.FragmentScreen
import org.hyperskill.app.android.notification_onboarding.fragment.NotificationsOnboardingFragment

object NotificationsOnboardingScreen : FragmentScreen {
override fun createFragment(factory: FragmentFactory): Fragment =
NotificationsOnboardingFragment.newInstance()
}
Loading

0 comments on commit 570db5a

Please sign in to comment.