diff --git a/androidHyperskillApp/src/main/AndroidManifest.xml b/androidHyperskillApp/src/main/AndroidManifest.xml index 1d19647511..6a8ed9fbf0 100644 --- a/androidHyperskillApp/src/main/AndroidManifest.xml +++ b/androidHyperskillApp/src/main/AndroidManifest.xml @@ -64,5 +64,15 @@ android:name="firebase_analytics_collection_enabled" android:value="false" /> + + + + \ No newline at end of file diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/core/extensions/ShareUtils.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/core/extensions/ShareUtils.kt new file mode 100644 index 0000000000..ecfc0a1f0e --- /dev/null +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/core/extensions/ShareUtils.kt @@ -0,0 +1,58 @@ +package org.hyperskill.app.android.core.extensions + +import android.content.ClipData +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.net.Uri +import androidx.annotation.DrawableRes +import androidx.core.content.FileProvider +import java.io.File +import java.io.FileOutputStream +import org.hyperskill.app.android.BuildConfig +import org.hyperskill.app.android.HyperskillApp + +object ShareUtils { + fun getShareDrawableIntent( + context: Context, + @DrawableRes drawableRes: Int, + text: String, + title: String + ): Intent { + val shareIntent = Intent().apply { + val bitmap = BitmapFactory.decodeResource(context.resources, drawableRes) + + val originImageUri = getUriForBitmap(context, bitmap, "$drawableRes.png") + + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, originImageUri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + putExtra(Intent.EXTRA_TEXT, text) + clipData = ClipData.newRawUri(null, originImageUri) + type = "image/*" + } + return Intent.createChooser( + shareIntent, + title + ) + } + + private fun getUriForBitmap( + context: Context, + bitmap: Bitmap, + name: String + ): Uri { + val file = File(context.cacheDir, name).apply { + createNewFile() + } + FileOutputStream(file).use { stream -> + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream) + } + return FileProvider.getUriForFile( + HyperskillApp.getAppContext(), + "${BuildConfig.APPLICATION_ID}.provider", + file + ) + } +} \ No newline at end of file diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/share_streak/fragment/ShareStreakDialogFragment.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/share_streak/fragment/ShareStreakDialogFragment.kt new file mode 100644 index 0000000000..4dcaf9675c --- /dev/null +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/share_streak/fragment/ShareStreakDialogFragment.kt @@ -0,0 +1,96 @@ +package org.hyperskill.app.android.share_streak.fragment + +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.DrawableRes +import by.kirich1409.viewbindingdelegate.viewBinding +import coil.load +import coil.transform.RoundedCornersTransformation +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import org.hyperskill.app.android.R +import org.hyperskill.app.android.databinding.FragmentShareStreakBinding +import org.hyperskill.app.android.view.base.ui.extension.wrapWithTheme +import ru.nobird.android.view.base.ui.extension.argument + +class ShareStreakDialogFragment : BottomSheetDialogFragment() { + + companion object { + const val TAG = "ShareStreakBottomSheetTag" + + fun newInstance( + streak: Int, + @DrawableRes imageRes: Int + ): ShareStreakDialogFragment = + ShareStreakDialogFragment().apply { + this.streak = streak + this.imageRes = imageRes + } + } + + private var streak: Int by argument() + @get:DrawableRes + private var imageRes: Int by argument() + + private val binding: FragmentShareStreakBinding by viewBinding(FragmentShareStreakBinding::bind) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NORMAL, R.style.TopCornersRoundedBottomSheetDialog) + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = + BottomSheetDialog(requireContext(), theme).also { dialog -> + dialog.setOnShowListener { + dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED + if (savedInstanceState == null) { + (parentFragment as? Callback)?.onShareStreakBottomSheetShown(streak) + } + } + } + + override fun onDismiss(dialog: DialogInterface) { + (parentFragment as? Callback)?.onShareStreakBottomSheetDismissed(streak) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? = + inflater.wrapWithTheme(requireActivity()) + .inflate(R.layout.fragment_share_streak, container, false) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + with(binding) { + shareStreakImage.load(imageRes) { + transformations( + RoundedCornersTransformation( + requireContext().resources.getDimension(R.dimen.corner_radius) + ) + ) + } + shareStreakShareButton.setOnClickListener { + (parentFragment as? Callback)?.onShareClick(streak) + } + shareStreakRefuseButton.setOnClickListener { + (parentFragment as? Callback)?.onRefuseStreakSharingClick(streak) + } + } + } + + interface Callback { + fun onShareStreakBottomSheetShown(streak: Int) + + fun onShareStreakBottomSheetDismissed(streak: Int) + + fun onShareClick(streak: Int) + + fun onRefuseStreakSharingClick(streak: Int) + } +} \ No newline at end of file diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/stage_implementation/view/fragment/StageStepWrapperFragment.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/stage_implementation/view/fragment/StageStepWrapperFragment.kt index 0ba43cac06..8ee6c4b0db 100644 --- a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/stage_implementation/view/fragment/StageStepWrapperFragment.kt +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/stage_implementation/view/fragment/StageStepWrapperFragment.kt @@ -12,6 +12,7 @@ import org.hyperskill.app.android.core.view.ui.fragment.setChildFragment import org.hyperskill.app.android.core.view.ui.navigation.requireRouter import org.hyperskill.app.android.databinding.FragmentStageStepWrapperBinding import org.hyperskill.app.android.main.view.ui.navigation.MainScreenRouter +import org.hyperskill.app.android.share_streak.fragment.ShareStreakDialogFragment import org.hyperskill.app.android.step.view.delegate.StepDelegate import org.hyperskill.app.android.step.view.fragment.StepFragment import org.hyperskill.app.android.step.view.model.StepCompletionHost @@ -41,7 +42,8 @@ class StageStepWrapperFragment : Fragment(R.layout.fragment_stage_step_wrapper), ReduxView, StepCompletionHost, - RequestDailyStudyReminderDialogFragment.Callback { + RequestDailyStudyReminderDialogFragment.Callback, + ShareStreakDialogFragment.Callback { companion object { private const val STEP_DESCRIPTION_FRAGMENT_TAG = "step_content" @@ -162,4 +164,20 @@ class StageStepWrapperFragment : override fun onPermissionResult(isGranted: Boolean) { stepDelegate?.onPermissionResult(isGranted) } + + override fun onShareStreakBottomSheetShown(streak: Int) { + stepViewModel.onShareStreakBottomSheetShown(streak) + } + + override fun onShareStreakBottomSheetDismissed(streak: Int) { + stepViewModel.onShareStreakBottomSheetDismissed(streak) + } + + override fun onShareClick(streak: Int) { + stepViewModel.onShareClick(streak) + } + + override fun onRefuseStreakSharingClick(streak: Int) { + stepViewModel.onRefuseStreakSharingClick(streak) + } } \ No newline at end of file diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step/view/delegate/StepDelegate.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step/view/delegate/StepDelegate.kt index c4ce1018ff..082585f39a 100644 --- a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step/view/delegate/StepDelegate.kt +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step/view/delegate/StepDelegate.kt @@ -1,7 +1,13 @@ package org.hyperskill.app.android.step.view.delegate +import android.content.ActivityNotFoundException +import android.content.Context +import android.util.Log +import androidx.annotation.DrawableRes import androidx.core.app.NotificationManagerCompat import androidx.fragment.app.Fragment +import org.hyperskill.app.R +import org.hyperskill.app.android.core.extensions.ShareUtils import org.hyperskill.app.android.core.extensions.checkNotificationChannelAvailability import org.hyperskill.app.android.core.view.ui.navigation.requireRouter import org.hyperskill.app.android.databinding.ErrorNoConnectionWithButtonBinding @@ -11,6 +17,7 @@ import org.hyperskill.app.android.main.view.ui.navigation.Tabs import org.hyperskill.app.android.main.view.ui.navigation.switch import org.hyperskill.app.android.notification.model.HyperskillNotificationChannel import org.hyperskill.app.android.notification.permission.NotificationPermissionDelegate +import org.hyperskill.app.android.share_streak.fragment.ShareStreakDialogFragment import org.hyperskill.app.android.step.view.dialog.TopicPracticeCompletedBottomSheet import org.hyperskill.app.android.step.view.screen.StepScreen import org.hyperskill.app.android.step_quiz.view.dialog.CompletedStepOfTheDayDialogFragment @@ -25,7 +32,8 @@ class StepDelegate( private val onRequestDailyStudyRemindersPermissionResult: (Boolean) -> Unit ) : RequestDailyStudyReminderDialogFragment.Callback where TFragment : Fragment, - TFragment : RequestDailyStudyReminderDialogFragment.Callback { + TFragment : RequestDailyStudyReminderDialogFragment.Callback, + TFragment : ShareStreakDialogFragment.Callback { private val notificationPermissionDelegate: NotificationPermissionDelegate = NotificationPermissionDelegate(fragment, ::onNotificationPermissionResult) @@ -78,15 +86,22 @@ class StepDelegate( } is StepCompletionFeature.Action.ViewAction.ShowProblemOfDaySolvedModal -> { CompletedStepOfTheDayDialogFragment - .newInstance(earnedGemsText = stepCompletionAction.earnedGemsText) + .newInstance( + earnedGemsText = stepCompletionAction.earnedGemsText, + shareStreakData = stepCompletionAction.shareStreakData + ) .showIfNotExists(fragment.childFragmentManager, CompletedStepOfTheDayDialogFragment.TAG) } is StepCompletionFeature.Action.ViewAction.ShowShareStreakModal -> { - // TODO: ALTAPPS-1028 Show share streak modal + ShareStreakDialogFragment + .newInstance( + streak = stepCompletionAction.streak, + imageRes = getShareStreakDrawableRes(stepCompletionAction.streak) + ) + .showIfNotExists(fragment.childFragmentManager, ShareStreakDialogFragment.TAG) } is StepCompletionFeature.Action.ViewAction.ShowShareStreakSystemModal -> { - // TODO: ALTAPPS-1028 Show system share streak modal (after "Share your streak" button clicked) - // on the problem of day solved modal + shareStreak(stepCompletionAction.streak) } } } @@ -121,4 +136,36 @@ class StepDelegate( .checkNotificationChannelAvailability(context, HyperskillNotificationChannel.DailyReminder) } } + + @DrawableRes + private fun getShareStreakDrawableRes(streak: Int): Int = + when (streak) { + 1 -> org.hyperskill.app.android.R.drawable.img_share_streak_day_1 + 5 -> org.hyperskill.app.android.R.drawable.img_share_streak_day_5 + 10 -> org.hyperskill.app.android.R.drawable.img_share_streak_day_10 + 25 -> org.hyperskill.app.android.R.drawable.img_share_streak_day_25 + 50 -> org.hyperskill.app.android.R.drawable.img_share_streak_day_50 + 100 -> org.hyperskill.app.android.R.drawable.img_share_streak_day_100 + else -> org.hyperskill.app.android.R.drawable.img_share_streak_day_1 + } + + private fun getShareStreakText(context: Context): String { + val title = context.getString(R.string.share_streak_sharing_text) + val link = context.getString(R.string.share_streak_sharing_url) + return "$title\n$link" + } + + private fun shareStreak(streak: Int) { + val shareIntent = ShareUtils.getShareDrawableIntent( + fragment.requireContext(), + getShareStreakDrawableRes(streak), + text = getShareStreakText(fragment.requireContext()), + title = fragment.requireContext().getString(R.string.share_streak_modal_title) + ) + try { + fragment.startActivity(shareIntent) + } catch (e: ActivityNotFoundException) { + Log.e("StepDelegate", "Unable to share streak. Activity not found!") + } + } } \ No newline at end of file diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step/view/fragment/StepFragment.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step/view/fragment/StepFragment.kt index bb8f4679b3..71709b3a35 100644 --- a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step/view/fragment/StepFragment.kt +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step/view/fragment/StepFragment.kt @@ -11,6 +11,7 @@ import org.hyperskill.app.android.core.extensions.argument import org.hyperskill.app.android.core.view.ui.fragment.setChildFragment import org.hyperskill.app.android.databinding.FragmentStepBinding import org.hyperskill.app.android.main.view.ui.navigation.MainScreenRouter +import org.hyperskill.app.android.share_streak.fragment.ShareStreakDialogFragment import org.hyperskill.app.android.step.view.delegate.StepDelegate import org.hyperskill.app.android.step.view.model.StepCompletionHost import org.hyperskill.app.android.step.view.model.StepCompletionView @@ -30,7 +31,8 @@ class StepFragment : Fragment(R.layout.fragment_step), ReduxView, StepCompletionHost, - RequestDailyStudyReminderDialogFragment.Callback { + RequestDailyStudyReminderDialogFragment.Callback, + ShareStreakDialogFragment.Callback { companion object { private const val STEP_TAG = "step" @@ -101,6 +103,16 @@ class StepFragment : } } + private fun initStepContainer(data: StepFeature.State.Data) { + setChildFragment(R.id.stepContainer, STEP_TAG) { + if (data.step.type == Step.Type.PRACTICE) { + StepPracticeFragment.newInstance(data.step, stepRoute) + } else { + StepTheoryFragment.newInstance(data.step, stepRoute, data.isPracticingAvailable) + } + } + } + override fun onDestroyView() { super.onDestroyView() viewStateDelegate = null @@ -121,13 +133,19 @@ class StepFragment : stepDelegate?.onPermissionResult(isGranted) } - private fun initStepContainer(data: StepFeature.State.Data) { - setChildFragment(R.id.stepContainer, STEP_TAG) { - if (data.step.type == Step.Type.PRACTICE) { - StepPracticeFragment.newInstance(data.step, stepRoute) - } else { - StepTheoryFragment.newInstance(data.step, stepRoute, data.isPracticingAvailable) - } - } + override fun onShareStreakBottomSheetShown(streak: Int) { + stepViewModel.onShareStreakBottomSheetShown(streak) + } + + override fun onShareStreakBottomSheetDismissed(streak: Int) { + stepViewModel.onShareStreakBottomSheetDismissed(streak) + } + + override fun onRefuseStreakSharingClick(streak: Int) { + stepViewModel.onRefuseStreakSharingClick(streak) + } + + override fun onShareClick(streak: Int) { + stepViewModel.onShareClick(streak) } } \ No newline at end of file diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz/view/dialog/CompletedStepOfTheDayDialogFragment.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz/view/dialog/CompletedStepOfTheDayDialogFragment.kt index 5cc929a3a7..3273625254 100644 --- a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz/view/dialog/CompletedStepOfTheDayDialogFragment.kt +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz/view/dialog/CompletedStepOfTheDayDialogFragment.kt @@ -7,12 +7,14 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.view.ContextThemeWrapper +import androidx.core.view.isVisible import androidx.fragment.app.viewModels import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import org.hyperskill.app.android.R +import org.hyperskill.app.android.core.extensions.argument import org.hyperskill.app.android.databinding.FragmentCompletedDailyStepBinding import org.hyperskill.app.step.presentation.StepFeature import org.hyperskill.app.step.presentation.StepViewModel @@ -23,9 +25,13 @@ class CompletedStepOfTheDayDialogFragment : BottomSheetDialogFragment() { companion object { const val TAG = "CompletedStepOfTheDayDialogFragment" - fun newInstance(earnedGemsText: String): CompletedStepOfTheDayDialogFragment = + fun newInstance( + earnedGemsText: String, + shareStreakData: StepCompletionFeature.ShareStreakData + ): CompletedStepOfTheDayDialogFragment = CompletedStepOfTheDayDialogFragment().apply { this.earnedGemsText = earnedGemsText + this.shareStreakData = shareStreakData } } @@ -36,6 +42,10 @@ class CompletedStepOfTheDayDialogFragment : BottomSheetDialogFragment() { private var earnedGemsText: String by argument() + private var shareStreakData: StepCompletionFeature.ShareStreakData by argument( + StepCompletionFeature.ShareStreakData.serializer() + ) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setStyle(STYLE_NORMAL, R.style.TopCornersRoundedBottomSheetDialog) @@ -71,6 +81,12 @@ class CompletedStepOfTheDayDialogFragment : BottomSheetDialogFragment() { super.onViewCreated(view, savedInstanceState) with(viewBinding) { completedDailyStepEarnedGemsTextView.text = earnedGemsText + + completedDailyStepStreakTextView.isVisible = + shareStreakData is StepCompletionFeature.ShareStreakData.Content + completedDailyStepStreakTextView.text = + (shareStreakData as? StepCompletionFeature.ShareStreakData.Content)?.streakText + completedDailyStepGoBackButton.setOnClickListener { stepViewModel.onNewMessage( StepFeature.Message.StepCompletionMessage( @@ -79,6 +95,18 @@ class CompletedStepOfTheDayDialogFragment : BottomSheetDialogFragment() { ) dismiss() } + + completedDailyStepShareStreakButton.isVisible = + shareStreakData is StepCompletionFeature.ShareStreakData.Content + (shareStreakData as? StepCompletionFeature.ShareStreakData.Content)?.streak?.let { streak -> + completedDailyStepShareStreakButton.setOnClickListener { + stepViewModel.onNewMessage( + StepFeature.Message.StepCompletionMessage( + StepCompletionFeature.Message.ProblemOfDaySolvedModalShareStreakClicked(streak) + ) + ) + } + } } } diff --git a/androidHyperskillApp/src/main/res/drawable/ic_streak.xml b/androidHyperskillApp/src/main/res/drawable/ic_streak.xml new file mode 100644 index 0000000000..8e8b626be4 --- /dev/null +++ b/androidHyperskillApp/src/main/res/drawable/ic_streak.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_1.png b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_1.png new file mode 100644 index 0000000000..29099f7ef4 Binary files /dev/null and b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_1.png differ diff --git a/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_10.png b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_10.png new file mode 100644 index 0000000000..8cde9d572e Binary files /dev/null and b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_10.png differ diff --git a/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_100.png b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_100.png new file mode 100644 index 0000000000..9755bae702 Binary files /dev/null and b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_100.png differ diff --git a/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_25.png b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_25.png new file mode 100644 index 0000000000..f2337f29a3 Binary files /dev/null and b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_25.png differ diff --git a/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_5.png b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_5.png new file mode 100644 index 0000000000..7e4376db1e Binary files /dev/null and b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_5.png differ diff --git a/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_50.png b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_50.png new file mode 100644 index 0000000000..aef1046d61 Binary files /dev/null and b/androidHyperskillApp/src/main/res/drawable/img_share_streak_day_50.png differ diff --git a/androidHyperskillApp/src/main/res/layout/fragment_completed_daily_step.xml b/androidHyperskillApp/src/main/res/layout/fragment_completed_daily_step.xml index a4d4323ccc..be5964b456 100644 --- a/androidHyperskillApp/src/main/res/layout/fragment_completed_daily_step.xml +++ b/androidHyperskillApp/src/main/res/layout/fragment_completed_daily_step.xml @@ -5,6 +5,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingBottom="20dp" > @@ -51,7 +52,7 @@ android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:text="@string/step_quiz_problem_of_day_solved_modal_text" android:layout_marginTop="20dp" - android:layout_marginHorizontal="20dp" + android:layout_marginHorizontal="16dp" app:layout_constraintTop_toBottomOf="@+id/completedDailyStepTitleTextView" /> @@ -68,10 +69,29 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/completedDailyStepDescriptionTextView" android:layout_marginTop="24dp" - android:layout_marginStart="20dp" + android:layout_marginStart="16dp" tools:text="300" /> + + + + \ No newline at end of file diff --git a/androidHyperskillApp/src/main/res/layout/fragment_share_streak.xml b/androidHyperskillApp/src/main/res/layout/fragment_share_streak.xml new file mode 100644 index 0000000000..7d8d9f1f26 --- /dev/null +++ b/androidHyperskillApp/src/main/res/layout/fragment_share_streak.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/androidHyperskillApp/src/main/res/xml/provider_paths.xml b/androidHyperskillApp/src/main/res/xml/provider_paths.xml new file mode 100644 index 0000000000..ae73680b42 --- /dev/null +++ b/androidHyperskillApp/src/main/res/xml/provider_paths.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/gradle/app.versions.toml b/gradle/app.versions.toml index 0bbe9a07e8..b6c0498b5f 100644 --- a/gradle/app.versions.toml +++ b/gradle/app.versions.toml @@ -2,5 +2,5 @@ minSdk = '24' targetSdk = '33' compileSdk = '33' -versionName = '1.41' -versionCode = '233' \ No newline at end of file +versionName = '1.42' +versionCode = '236' \ No newline at end of file diff --git a/iosHyperskillApp/NotificationServiceExtension/Info.plist b/iosHyperskillApp/NotificationServiceExtension/Info.plist index 6f906799ef..24c7fa57ab 100644 --- a/iosHyperskillApp/NotificationServiceExtension/Info.plist +++ b/iosHyperskillApp/NotificationServiceExtension/Info.plist @@ -9,9 +9,9 @@ CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleVersion - 234 + 236 CFBundleShortVersionString - 1.41 + 1.42 CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleExecutable diff --git a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj index 4a95104204..3b311db39b 100644 --- a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj +++ b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj @@ -4673,7 +4673,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 234; + CURRENT_PROJECT_VERSION = 236; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 3DWS674B2M; GENERATE_INFOPLIST_FILE = NO; @@ -4694,7 +4694,7 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 234; + CURRENT_PROJECT_VERSION = 236; DEVELOPMENT_TEAM = 3DWS674B2M; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = iosHyperskillAppUITests/Info.plist; @@ -4715,7 +4715,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 234; + CURRENT_PROJECT_VERSION = 236; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 3DWS674B2M; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; @@ -4736,7 +4736,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 234; + CURRENT_PROJECT_VERSION = 236; DEVELOPMENT_TEAM = 3DWS674B2M; INFOPLIST_FILE = iosHyperskillAppTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -4757,7 +4757,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 234; + CURRENT_PROJECT_VERSION = 236; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 3DWS674B2M; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; @@ -4785,7 +4785,7 @@ CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 234; + CURRENT_PROJECT_VERSION = 236; DEVELOPMENT_TEAM = 3DWS674B2M; INFOPLIST_FILE = NotificationServiceExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -4930,7 +4930,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 234; + CURRENT_PROJECT_VERSION = 236; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = 3DWS674B2M; ENABLE_PREVIEWS = YES; @@ -4966,7 +4966,7 @@ CODE_SIGN_ENTITLEMENTS = iosHyperskillApp/iosHyperskillApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 234; + CURRENT_PROJECT_VERSION = 236; DEVELOPMENT_ASSET_PATHS = "\"iosHyperskillApp/Preview Content\""; DEVELOPMENT_TEAM = 3DWS674B2M; ENABLE_PREVIEWS = YES; diff --git a/iosHyperskillApp/iosHyperskillApp/Info.plist b/iosHyperskillApp/iosHyperskillApp/Info.plist index ee5abcfe29..459bb92a4c 100644 --- a/iosHyperskillApp/iosHyperskillApp/Info.plist +++ b/iosHyperskillApp/iosHyperskillApp/Info.plist @@ -23,7 +23,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.41 + 1.42 CFBundleURLTypes @@ -36,7 +36,7 @@ CFBundleVersion - 234 + 236 FirebaseAppDelegateProxyEnabled FirebaseMessagingAutoInitEnabled diff --git a/iosHyperskillApp/iosHyperskillAppTests/Info.plist b/iosHyperskillApp/iosHyperskillAppTests/Info.plist index 2de64564ff..e7e159dc56 100644 --- a/iosHyperskillApp/iosHyperskillAppTests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppTests/Info.plist @@ -13,8 +13,8 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.41 + 1.42 CFBundleVersion - 234 + 236 diff --git a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist index 34c5aef57c..f20229aae3 100644 --- a/iosHyperskillApp/iosHyperskillAppUITests/Info.plist +++ b/iosHyperskillApp/iosHyperskillAppUITests/Info.plist @@ -13,8 +13,8 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.41 + 1.42 CFBundleVersion - 234 + 236 diff --git a/shared/src/androidMain/kotlin/org/hyperskill/app/step/presentation/StepViewModel.kt b/shared/src/androidMain/kotlin/org/hyperskill/app/step/presentation/StepViewModel.kt index 95201d6e81..b6cf2d02c1 100644 --- a/shared/src/androidMain/kotlin/org/hyperskill/app/step/presentation/StepViewModel.kt +++ b/shared/src/androidMain/kotlin/org/hyperskill/app/step/presentation/StepViewModel.kt @@ -1,5 +1,6 @@ package org.hyperskill.app.step.presentation +import org.hyperskill.app.step_completion.presentation.StepCompletionFeature import ru.nobird.android.view.redux.viewmodel.ReduxViewModel import ru.nobird.app.presentation.redux.container.ReduxViewContainer @@ -9,4 +10,36 @@ class StepViewModel( init { onNewMessage(StepFeature.Message.Initialize()) } + + fun onShareStreakBottomSheetShown(streak: Int) { + onNewMessage( + StepFeature.Message.StepCompletionMessage( + StepCompletionFeature.Message.ShareStreakModalShownEventMessage(streak) + ) + ) + } + + fun onShareStreakBottomSheetDismissed(streak: Int) { + onNewMessage( + StepFeature.Message.StepCompletionMessage( + StepCompletionFeature.Message.ShareStreakModalHiddenEventMessage(streak) + ) + ) + } + + fun onShareClick(streak: Int) { + onNewMessage( + StepFeature.Message.StepCompletionMessage( + StepCompletionFeature.Message.ShareStreakModalShareClicked(streak) + ) + ) + } + + fun onRefuseStreakSharingClick(streak: Int) { + onNewMessage( + StepFeature.Message.StepCompletionMessage( + StepCompletionFeature.Message.ShareStreakModalNoThanksClickedEventMessage(streak) + ) + ) + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step_completion/presentation/StepCompletionFeature.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step_completion/presentation/StepCompletionFeature.kt index ded4bb998c..51132340cc 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/step_completion/presentation/StepCompletionFeature.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step_completion/presentation/StepCompletionFeature.kt @@ -1,5 +1,6 @@ package org.hyperskill.app.step_completion.presentation +import kotlinx.serialization.Serializable import org.hyperskill.app.analytic.domain.model.AnalyticEvent import org.hyperskill.app.learning_activities.domain.model.LearningActivity import org.hyperskill.app.step.domain.model.Step @@ -53,8 +54,11 @@ interface StepCompletionFeature { * @see Message.ProblemOfDaySolved * @see Action.ViewAction.ShowProblemOfDaySolvedModal */ + @Serializable sealed interface ShareStreakData { + @Serializable object Empty : ShareStreakData + @Serializable data class Content(val streakText: String, val streak: Int) : ShareStreakData }