Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into dependabot/bundler…
Browse files Browse the repository at this point in the history
…/androidHyperskillApp/develop/fastlane-2.214.0
  • Loading branch information
ivan-magda committed Jul 28, 2023
2 parents 3f145c8 + cf05146 commit 61d48db
Show file tree
Hide file tree
Showing 144 changed files with 2,240 additions and 3,115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,23 @@ class AuthSocialWebViewClient(
request?.url?.let { url ->
val urlString = url.toString()
if (urlString.startsWith("https://${networkEndpointConfigInfo.host}/oauth?")) {
val message = try {
val codeQueryParameter = url.getQueryParameter(CodeParameter)
if (codeQueryParameter != null) {
AuthSocialWebViewFeature.Message.AuthCodeSuccess(
codeQueryParameter,
socialAuthProvider
try {
url.getQueryParameter(CodeParameter)?.let { codeQueryParameter ->
onNewMessage(
AuthSocialWebViewFeature.Message.AuthCodeSuccess(
codeQueryParameter,
socialAuthProvider
)
)
} else {
}
} catch (e: UnsupportedOperationException) {
onNewMessage(
AuthSocialWebViewFeature.Message.AuthCodeFailure(
socialError = AuthSocialError.CONNECTION_PROBLEM,
originalError = IllegalStateException("No code query parameter in url")
originalError = e
)
}
} catch (e: UnsupportedOperationException) {
AuthSocialWebViewFeature.Message.AuthCodeFailure(
socialError = AuthSocialError.CONNECTION_PROBLEM,
originalError = e
)
}
onNewMessage(message)
}
}
return false
Expand All @@ -56,12 +53,18 @@ class AuthSocialWebViewClient(
request: WebResourceRequest?,
error: WebResourceError?
) {
onNewMessage(
AuthSocialWebViewFeature.Message.AuthCodeFailure(
socialError = AuthSocialError.CONNECTION_PROBLEM,
originalError = if (error != null) Exception(error.description.toString()) else null
if (request?.isForMainFrame == true) {
onNewMessage(
AuthSocialWebViewFeature.Message.AuthCodeFailure(
socialError = AuthSocialError.CONNECTION_PROBLEM,
originalError = if (error != null) Exception(
"Error code=${error.errorCode}, description=${error.description}"
) else {
null
}
)
)
)
}
}

override fun onPageFinished(view: WebView?, url: String?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ fun NotificationChannel.isFullyEnabled(notificationManager: NotificationManagerC
}

fun NotificationManagerCompat.isChannelNotificationsEnabled(channelId: String): Boolean =
areNotificationsEnabled() && getNotificationChannel(channelId)?.isFullyEnabled(this) == true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
areNotificationsEnabled() && getNotificationChannel(channelId)?.isFullyEnabled(this) == true
} else {
areNotificationsEnabled()
}

fun NotificationManagerCompat.checkNotificationChannelAvailability(
context: Context,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.hyperskill.app.android.core.view.ui.adapter

import org.hyperskill.app.android.R
import org.hyperskill.app.android.databinding.WidgetDataLoadingErrorBinding
import ru.nobird.android.ui.adapterdelegates.AdapterDelegate
import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder
import ru.nobird.android.ui.adapterdelegates.dsl.adapterDelegate

object DataLoadingErrorAdapterDelegate {
// adapterDelegate function is used instead of creating a special class
// because class casting requires a reified type
inline operator fun <D, reified DT : D> invoke(
crossinline onReloadClick: (DT) -> Unit
): AdapterDelegate<D, DelegateViewHolder<D>> =
adapterDelegate<D, DT>(R.layout.widget_data_loading_error) {
val viewBinding = WidgetDataLoadingErrorBinding.bind(itemView)
viewBinding.reloadButton.setOnClickListener {
item?.let(onReloadClick)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelProvider
import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
import by.kirich1409.viewbindingdelegate.viewBinding
import org.hyperskill.app.SharedResources
import org.hyperskill.app.android.HyperskillApp
Expand All @@ -22,10 +20,11 @@ import org.hyperskill.app.android.core.view.ui.updateIsRefreshing
import org.hyperskill.app.android.databinding.FragmentHomeBinding
import org.hyperskill.app.android.gamification_toolbar.view.ui.delegate.GamificationToolbarDelegate
import org.hyperskill.app.android.main.view.ui.navigation.MainScreenRouter
import org.hyperskill.app.android.next_learning_activity.view.delegate.NextLearningActivityDelegate
import org.hyperskill.app.android.problem_of_day.view.delegate.ProblemOfDayCardFormDelegate
import org.hyperskill.app.android.problems_limit.view.ui.delegate.ProblemsLimitDelegate
import org.hyperskill.app.android.stage_implementation.view.dialog.UnsupportedStageBottomSheet
import org.hyperskill.app.android.step.view.screen.StepScreen
import org.hyperskill.app.android.topics.view.delegate.TopicsToDiscoverNextDelegate
import org.hyperskill.app.android.topics_repetitions.view.delegate.TopicsRepetitionCardFormDelegate
import org.hyperskill.app.android.topics_repetitions.view.screen.TopicsRepetitionScreen
import org.hyperskill.app.android.view.base.ui.extension.snackbar
Expand All @@ -35,15 +34,15 @@ import org.hyperskill.app.home.presentation.HomeViewModel
import org.hyperskill.app.problems_limit.domain.model.ProblemsLimitScreen
import org.hyperskill.app.problems_limit.view.mapper.ProblemsLimitViewStateMapper
import org.hyperskill.app.step.domain.model.StepRoute
import org.hyperskill.app.topics_to_discover_next.presentation.TopicsToDiscoverNextFeature
import ru.nobird.android.view.base.ui.delegate.ViewStateDelegate
import ru.nobird.android.view.base.ui.extension.showIfNotExists
import ru.nobird.android.view.redux.ui.extension.reduxViewModel
import ru.nobird.app.presentation.redux.container.ReduxView

class HomeFragment :
Fragment(R.layout.fragment_home),
ReduxView<HomeFeature.State, HomeFeature.Action.ViewAction> {
ReduxView<HomeFeature.State, HomeFeature.Action.ViewAction>,
UnsupportedStageBottomSheet.Callback {
companion object {
fun newInstance(): Fragment =
HomeFragment()
Expand All @@ -70,17 +69,17 @@ class HomeFragment :
private val topicsRepetitionDelegate: TopicsRepetitionCardFormDelegate by lazy(LazyThreadSafetyMode.NONE) {
TopicsRepetitionCardFormDelegate()
}
private var gamificationToolbarDelegate: GamificationToolbarDelegate? = null
private val topicsToDiscoverNextDelegate: TopicsToDiscoverNextDelegate by lazy(LazyThreadSafetyMode.NONE) {
TopicsToDiscoverNextDelegate(loadingItems = 1) { topicId ->

private val nextLearningActivityDelegate: NextLearningActivityDelegate by lazy(LazyThreadSafetyMode.NONE) {
NextLearningActivityDelegate(requireContext()) { nextLearningActivityMessage ->
homeViewModel.onNewMessage(
HomeFeature.Message.TopicsToDiscoverNextMessage(
TopicsToDiscoverNextFeature.Message.TopicToDiscoverNextClicked(topicId)
)
HomeFeature.Message.NextLearningActivityWidgetMessage(nextLearningActivityMessage)
)
}
}

private var gamificationToolbarDelegate: GamificationToolbarDelegate? = null

private val onForegroundObserver =
object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
Expand All @@ -104,10 +103,7 @@ class HomeFragment :
initGamificationToolbarDelegate()
initProblemsLimitDelegate()
problemOfDayCardFormDelegate.setup(viewBinding.homeScreenProblemOfDayCard)
topicsToDiscoverNextDelegate.setup(
requireContext(),
viewBinding.homeTopicsToDiscoverNext.homeTopicsToDiscoverNextRecycler
)
nextLearningActivityDelegate.setup(requireContext(), viewBinding.homeNextLearningActivity)
with(viewBinding) {
homeScreenSwipeRefreshLayout.setHyperskillColors()
homeScreenSwipeRefreshLayout.setOnRefreshListener {
Expand Down Expand Up @@ -171,7 +167,7 @@ class HomeFragment :
viewBinding.homeScreenKeepPracticingTextView,
viewBinding.homeScreenProblemOfDayCard.root,
viewBinding.homeScreenTopicsRepetitionCard.root,
viewBinding.homeScreenKeepLearningInWebButton,
viewBinding.homeScreenKeepLearningInWebButton
)
}
}
Expand Down Expand Up @@ -215,18 +211,20 @@ class HomeFragment :
mainScreenRouter = mainScreenRouter,
router = requireRouter()
)
is HomeFeature.Action.ViewAction.TopicsToDiscoverNextViewAction -> {
when (action.viewAction) {
is TopicsToDiscoverNextFeature.Action.ViewAction.ShowStepScreen -> {
val viewAction =
action.viewAction as TopicsToDiscoverNextFeature.Action.ViewAction.ShowStepScreen
requireRouter().navigateTo(StepScreen(viewAction.stepRoute))
}
}
is HomeFeature.Action.ViewAction.NavigateTo.StepScreen -> {
requireRouter().navigateTo(
StepScreen(action.stepRoute)
)
}
else -> {
is HomeFeature.Action.ViewAction.ProblemsLimitViewAction -> {
// no op
}
is HomeFeature.Action.ViewAction.NextLearningActivityWidgetViewAction -> {
nextLearningActivityDelegate.handleAction(
fragment = this,
action = action.viewAction
)
}
}
}

Expand All @@ -246,7 +244,7 @@ class HomeFragment :
problemsLimitViewStateMapper?.let { mapper ->
problemsLimitDelegate?.render(mapper.mapState(state.problemsLimitState))
}
renderTopicsToDiscoverNext(state.topicsToDiscoverNextState)
nextLearningActivityDelegate.render(state.nextLearningActivityWidgetState)
}

private fun renderSwipeRefresh(state: HomeFeature.State) {
Expand Down Expand Up @@ -297,14 +295,18 @@ class HomeFragment :
}
}

private fun renderTopicsToDiscoverNext(state: TopicsToDiscoverNextFeature.State) {
TransitionManager.beginDelayedTransition(viewBinding.root, AutoTransition())
with(viewBinding) {
homeTopicsToDiscoverNext.homeTopicsToDiscoverNextTitle.isVisible =
state is TopicsToDiscoverNextFeature.State.Content
homeTopicsToDiscoverNext.homeTopicsToDiscoverNextTitleSkeleton.isVisible =
state is TopicsToDiscoverNextFeature.State.Loading
}
topicsToDiscoverNextDelegate.render(state)
// UnsupportedStageBottomSheet.Callback methods
override fun onShow() {
homeViewModel.onNewMessage(HomeFeature.Message.StageImplementUnsupportedModalShownEventMessage)
}

override fun onDismiss() {
homeViewModel.onNewMessage(HomeFeature.Message.StageImplementUnsupportedModalHiddenEventMessage)
}

override fun onHomeClick() {
homeViewModel.onNewMessage(HomeFeature.Message.StageImplementUnsupportedModalGoToHomeClicked)
childFragmentManager
.dismissDialogFragmentIfExists(UnsupportedStageBottomSheet.TAG)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.hyperskill.app.android.next_learning_activity.view.delegate

import android.content.Context
import android.graphics.drawable.Drawable
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import org.hyperskill.app.android.R
import org.hyperskill.app.android.core.view.ui.adapter.DataLoadingErrorAdapterDelegate
import org.hyperskill.app.android.core.view.ui.adapter.decoration.HorizontalMarginItemDecoration
import org.hyperskill.app.android.databinding.LayoutNextLearningActivityBinding
import org.hyperskill.app.android.next_learning_activity.view.model.NextLearningActivityLoadingErrorRecyclerItem
import org.hyperskill.app.android.stage_implementation.view.dialog.UnsupportedStageBottomSheet
import org.hyperskill.app.android.study_plan.adapter.ActivityLoadingAdapterDelegate
import org.hyperskill.app.android.study_plan.adapter.StudyPlanActivityAdapterDelegate
import org.hyperskill.app.android.study_plan.delegate.LearningActivityTargetViewActionHandler
import org.hyperskill.app.android.study_plan.model.StudyPlanRecyclerItem
import org.hyperskill.app.next_learning_activity_widget.presentation.NextLearningActivityWidgetFeature
import org.hyperskill.app.next_learning_activity_widget.presentation.NextLearningActivityWidgetFeature.ViewState.Content
import org.hyperskill.app.next_learning_activity_widget.presentation.NextLearningActivityWidgetFeature.ViewState.Empty
import org.hyperskill.app.next_learning_activity_widget.presentation.NextLearningActivityWidgetFeature.ViewState.Idle
import org.hyperskill.app.next_learning_activity_widget.presentation.NextLearningActivityWidgetFeature.ViewState.Loading
import org.hyperskill.app.next_learning_activity_widget.presentation.NextLearningActivityWidgetFeature.ViewState.NetworkError
import org.hyperskill.app.next_learning_activity_widget.view.mapper.NextLearningActivityWidgetViewStateMapper
import ru.nobird.android.ui.adapters.DefaultDelegateAdapter

class NextLearningActivityDelegate(
context: Context,
onNewMessage: (NextLearningActivityWidgetFeature.Message) -> Unit
) {

private val nextLearningActivityAdapter by lazy(LazyThreadSafetyMode.NONE) {
DefaultDelegateAdapter<StudyPlanRecyclerItem>().apply {
addDelegate(
StudyPlanActivityAdapterDelegate {
onNewMessage(NextLearningActivityWidgetFeature.Message.NextLearningActivityClicked)
}
)
addDelegate(ActivityLoadingAdapterDelegate())
addDelegate(
DataLoadingErrorAdapterDelegate<StudyPlanRecyclerItem, NextLearningActivityLoadingErrorRecyclerItem> {
onNewMessage(NextLearningActivityWidgetFeature.Message.RetryContentLoading)
}
)
}
}

private val nextLearningActivityTitleTextColor: Int =
ContextCompat.getColor(context, StudyPlanRecyclerItem.Activity.activeTextColorRes)
private val nextLearningActivityEndIcon: Drawable? =
ContextCompat.getDrawable(context, StudyPlanRecyclerItem.Activity.nextActivityIconRes)

fun setup(context: Context, viewBinding: LayoutNextLearningActivityBinding) {
with(viewBinding) {
with(homeNextLearningActivityRecycler) {
adapter = nextLearningActivityAdapter
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
addItemDecoration(
HorizontalMarginItemDecoration(
horizontalMargin = context.resources.getDimensionPixelOffset(R.dimen.screen_horizontal_padding)
)
)
}
}
}

fun render(state: NextLearningActivityWidgetFeature.State) {
val viewState = NextLearningActivityWidgetViewStateMapper.map(state.contentState)
nextLearningActivityAdapter.items = when (viewState) {
Idle, Empty -> emptyList()
Loading -> listOf(StudyPlanRecyclerItem.ActivityLoading(0, 0))
is Content -> listOf(mapContentToRecyclerItem(viewState))
NetworkError -> listOf(NextLearningActivityLoadingErrorRecyclerItem)
}
}

private fun mapContentToRecyclerItem(content: Content): StudyPlanRecyclerItem.Activity =
StudyPlanRecyclerItem.Activity(
id = content.id,
title = content.title,
subtitle = content.subtitle,
titleTextColor = nextLearningActivityTitleTextColor,
progress = content.progress,
formattedProgress = content.formattedProgress,
endIcon = nextLearningActivityEndIcon,
isClickable = true,
isIdeRequired = content.isIdeRequired
)

fun <TFragment> handleAction(
fragment: TFragment,
action: NextLearningActivityWidgetFeature.Action.ViewAction
) where TFragment : Fragment, TFragment : UnsupportedStageBottomSheet.Callback {
when (action) {
is NextLearningActivityWidgetFeature.Action.ViewAction.NavigateTo.LearningActivityTarget ->
LearningActivityTargetViewActionHandler.handle(
fragment = fragment,
viewAction = action.viewAction
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.hyperskill.app.android.next_learning_activity.view.model

import org.hyperskill.app.android.study_plan.model.StudyPlanRecyclerItem

object NextLearningActivityLoadingErrorRecyclerItem : StudyPlanRecyclerItem
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,18 @@ class SentryManagerImpl(private val buildKonfig: BuildKonfig) : SentryManager {
Sentry.addBreadcrumb(sentryBreadcrumb)
}

override fun captureMessage(message: String, level: HyperskillSentryLevel) {
Sentry.captureMessage(message, level.toSentryLevel())
override fun captureMessage(message: String, level: HyperskillSentryLevel, data: Map<String, Any>) {
Sentry.captureMessage(message, level.toSentryLevel()) { scope ->
scope.span.let {
data.forEach { (key, value) ->
it?.setData(key, value)
}
}
}
}

override fun captureException(throwable: Throwable) {
Sentry.captureException(throwable)
}

override fun setUsedId(userId: String) {
Expand Down
Loading

0 comments on commit 61d48db

Please sign in to comment.