From dcc3b753704d29ed7d8933fad49cf373d1e46600 Mon Sep 17 00:00:00 2001 From: Flavia Handrea Date: Mon, 29 Jul 2024 16:42:45 +0300 Subject: [PATCH 1/2] Congratulations screen from Archive Onboarding. --- .../ArchiveOnboardingActivity.kt | 53 ++--- .../compose/ArchiveNamePage.kt | 8 +- .../compose/ArchiveOnboardingScreen.kt | 27 ++- .../compose/ArchiveTypePage.kt | 8 +- .../compose/CongratulationsPage.kt | 197 ++++++++++++++++++ .../ui/archiveOnboarding/compose/GoalsPage.kt | 8 +- .../compose/PrioritiesPage.kt | 30 +-- .../archiveOnboarding/compose/WelcomePage.kt | 4 +- .../ui/composeComponents/ArchiveItem.kt | 76 +++++++ .../viewmodels/ArchiveOnboardingViewModel.kt | 84 ++------ .../main/res/drawable/ic_archive_gradient.xml | 21 ++ app/src/main/res/values/strings.xml | 5 + 12 files changed, 382 insertions(+), 139 deletions(-) create mode 100644 app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/CongratulationsPage.kt create mode 100644 app/src/main/java/org/permanent/permanent/ui/composeComponents/ArchiveItem.kt create mode 100644 app/src/main/res/drawable/ic_archive_gradient.xml diff --git a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/ArchiveOnboardingActivity.kt b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/ArchiveOnboardingActivity.kt index ff6b94f4..ff3e990d 100644 --- a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/ArchiveOnboardingActivity.kt +++ b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/ArchiveOnboardingActivity.kt @@ -1,18 +1,24 @@ package org.permanent.permanent.ui.archiveOnboarding +import android.content.Intent import android.content.pm.ActivityInfo import android.os.Bundle import androidx.databinding.DataBindingUtil +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider import org.permanent.permanent.R import org.permanent.permanent.databinding.ActivityArchiveOnboardingBinding import org.permanent.permanent.ui.PREFS_NAME import org.permanent.permanent.ui.PreferencesHelper +import org.permanent.permanent.ui.activities.MainActivity import org.permanent.permanent.ui.activities.PermanentBaseActivity import org.permanent.permanent.ui.computeWindowSizeClasses +import org.permanent.permanent.viewmodels.ArchiveOnboardingViewModel class ArchiveOnboardingActivity : PermanentBaseActivity() { private lateinit var prefsHelper: PreferencesHelper + private lateinit var viewModel: ArchiveOnboardingViewModel private lateinit var binding: ActivityArchiveOnboardingBinding override fun onCreate(savedInstanceState: Bundle?) { @@ -29,53 +35,24 @@ class ArchiveOnboardingActivity : PermanentBaseActivity() { val windowWidthSizeClass = computeWindowSizeClasses().windowWidthSizeClass prefsHelper.saveWindowWidthSizeClass(windowWidthSizeClass) + viewModel = ViewModelProvider(this)[ArchiveOnboardingViewModel::class.java] + binding = DataBindingUtil.setContentView(this, R.layout.activity_archive_onboarding) binding.executePendingBindings() binding.lifecycleOwner = this } - // -// private val onShowMessage = Observer { message -> -// val snackBar = Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG) -// val view: View = snackBar.view -// view.setBackgroundColor(ContextCompat.getColor(this, R.color.deepGreen)) -// snackBar.setTextColor(ContextCompat.getColor(this, R.color.paleGreen)) -// val snackbarTextTextView = view.findViewById(R.id.snackbar_text) as TextView -// snackbarTextTextView.setTypeface(snackbarTextTextView.typeface, Typeface.BOLD) -// snackBar.show() -// } -// -// private val onShowError = Observer { message -> -// val snackBar = Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG) -// val view: View = snackBar.view -// view.setBackgroundColor(ContextCompat.getColor(this, R.color.deepRed)) -// snackBar.setTextColor(ContextCompat.getColor(this, R.color.white)) -// snackBar.show() -// } -// -// private val onShowNextFragment = Observer { -// val transaction: FragmentTransaction = supportFragmentManager.beginTransaction() -// transaction.replace(R.id.frameLayoutContainer, it).commit() -// } -// -// private val onArchiveOnboardingDone = Observer { -// startActivity(Intent(this@ArchiveOnboardingActivity, MainActivity::class.java)) -// finish() -// } -// + private val onArchiveOnboardingDone = Observer { + startActivity(Intent(this@ArchiveOnboardingActivity, MainActivity::class.java)) + finish() + } + override fun connectViewModelEvents() { -// viewModel.getShowMessage().observe(this, onShowMessage) -// viewModel.getShowError().observe(this, onShowError) -// viewModel.getOnShowNextFragment().observe(this, onShowNextFragment) -// viewModel.getOnArchiveOnboardingDone().observe(this, onArchiveOnboardingDone) + viewModel.getOnArchiveOnboardingDone().observe(this, onArchiveOnboardingDone) } - // override fun disconnectViewModelEvents() { -// viewModel.getShowMessage().removeObserver(onShowMessage) -// viewModel.getShowError().removeObserver(onShowError) -// viewModel.getOnShowNextFragment().removeObserver(onShowNextFragment) -// viewModel.getOnArchiveOnboardingDone().removeObserver(onArchiveOnboardingDone) + viewModel.getOnArchiveOnboardingDone().removeObserver(onArchiveOnboardingDone) } override fun onResume() { diff --git a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveNamePage.kt b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveNamePage.kt index fd5cf45a..415ca3bf 100644 --- a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveNamePage.kt +++ b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveNamePage.kt @@ -209,7 +209,7 @@ private fun TabletBody( iconAlignment = ButtonIconAlignment.START ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_TYPE_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_TYPE.value) } } } @@ -221,7 +221,7 @@ private fun TabletBody( ) { if (textFieldValueState.text.isNotEmpty()) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.GOALS_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.GOALS.value) } newArchive.name = textFieldValueState.text } @@ -358,7 +358,7 @@ private fun PhoneBody( iconAlignment = ButtonIconAlignment.START ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_TYPE_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_TYPE.value) } } } @@ -375,7 +375,7 @@ private fun PhoneBody( ) { if (textFieldValueState.text.isNotEmpty()) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.GOALS_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.GOALS.value) } newArchive.name = textFieldValueState.text } diff --git a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveOnboardingScreen.kt b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveOnboardingScreen.kt index c92ff506..6d3d8035 100644 --- a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveOnboardingScreen.kt +++ b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveOnboardingScreen.kt @@ -62,7 +62,7 @@ fun ArchiveOnboardingScreen( ) { val context = LocalContext.current - val pagerState = rememberPagerState(initialPage = OnboardingPage.WELCOME_PAGE.value, + val pagerState = rememberPagerState(initialPage = OnboardingPage.WELCOME.value, pageCount = { OnboardingPage.values().size }) val isTablet = viewModel.isTablet() @@ -145,18 +145,18 @@ fun ArchiveOnboardingScreen( LaunchedEffect(pagerState.currentPage) { when (pagerState.currentPage) { - OnboardingPage.ARCHIVE_NAME_PAGE.value -> { + OnboardingPage.ARCHIVE_NAME.value -> { viewModel.updateFirstProgressBarEmpty(false) viewModel.updateSecondProgressBarEmpty(true) } - OnboardingPage.GOALS_PAGE.value -> { + OnboardingPage.GOALS.value -> { viewModel.updateFirstProgressBarEmpty(true) viewModel.updateSecondProgressBarEmpty(false) viewModel.updateThirdProgressBarEmpty(true) } - OnboardingPage.PRIORITIES_PAGE.value -> { + OnboardingPage.PRIORITIES.value -> { viewModel.updateSecondProgressBarEmpty(true) viewModel.updateThirdProgressBarEmpty(false) } @@ -224,7 +224,7 @@ fun ArchiveOnboardingScreen( state = pagerState, userScrollEnabled = false ) { page -> when (page) { - OnboardingPage.WELCOME_PAGE.value -> { + OnboardingPage.WELCOME.value -> { WelcomePage( isTablet = isTablet, pagerState = pagerState, @@ -232,7 +232,7 @@ fun ArchiveOnboardingScreen( ) } - OnboardingPage.ARCHIVE_TYPE_PAGE.value -> { + OnboardingPage.ARCHIVE_TYPE.value -> { ArchiveTypePage(isTablet = isTablet, pagerState = pagerState, onArchiveTypeClick = { type: ArchiveType, typeName: String -> @@ -240,7 +240,7 @@ fun ArchiveOnboardingScreen( }) } - OnboardingPage.ARCHIVE_NAME_PAGE.value -> { + OnboardingPage.ARCHIVE_NAME.value -> { ArchiveNamePage( isTablet = isTablet, pagerState = pagerState, @@ -248,7 +248,7 @@ fun ArchiveOnboardingScreen( ) } - OnboardingPage.GOALS_PAGE.value -> { + OnboardingPage.GOALS.value -> { GoalsPage( isTablet = isTablet, horizontalPaddingDp = horizontalPaddingDp, @@ -258,7 +258,7 @@ fun ArchiveOnboardingScreen( ) } - OnboardingPage.PRIORITIES_PAGE.value -> { + OnboardingPage.PRIORITIES.value -> { PrioritiesPage( viewModel = viewModel, isTablet = isTablet, @@ -268,6 +268,13 @@ fun ArchiveOnboardingScreen( priorities = priorities ) } + + OnboardingPage.CONGRATULATIONS.value -> { + CongratulationsPage( + viewModel = viewModel, + isTablet = isTablet + ) + } } } } @@ -353,5 +360,5 @@ data class NewArchive( ) enum class OnboardingPage(val value: Int) { - WELCOME_PAGE(0), ARCHIVE_TYPE_PAGE(1), ARCHIVE_NAME_PAGE(2), GOALS_PAGE(3), PRIORITIES_PAGE(4) + WELCOME(0), ARCHIVE_TYPE(1), ARCHIVE_NAME(2), GOALS(3), PRIORITIES(4), CONGRATULATIONS(5) } diff --git a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveTypePage.kt b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveTypePage.kt index 3326899e..8fa1c544 100644 --- a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveTypePage.kt +++ b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveTypePage.kt @@ -140,7 +140,7 @@ private fun TabletBody( iconAlignment = ButtonIconAlignment.START ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.WELCOME_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.WELCOME.value) } } } @@ -157,7 +157,7 @@ private fun TabletBody( style = ButtonColor.LIGHT, text = text ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_NAME_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_NAME.value) } } } @@ -230,7 +230,7 @@ private fun PhoneBody( iconAlignment = ButtonIconAlignment.START ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.WELCOME_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.WELCOME.value) } } } @@ -244,7 +244,7 @@ private fun PhoneBody( ButtonColor.LIGHT, text = stringResource(id = R.string.next) ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_NAME_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_NAME.value) } } } diff --git a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/CongratulationsPage.kt b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/CongratulationsPage.kt new file mode 100644 index 00000000..3dc5b52e --- /dev/null +++ b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/CongratulationsPage.kt @@ -0,0 +1,197 @@ +@file:OptIn(ExperimentalFoundationApi::class) + +package org.permanent.permanent.ui.archiveOnboarding.compose + +import androidx.compose.foundation.ExperimentalFoundationApi +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.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.ClickableText +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +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.platform.LocalContext +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.Dimension +import androidx.core.content.ContextCompat +import org.permanent.permanent.R +import org.permanent.permanent.models.AccessRole +import org.permanent.permanent.models.Archive +import org.permanent.permanent.ui.composeComponents.ArchiveItem +import org.permanent.permanent.ui.composeComponents.ButtonColor +import org.permanent.permanent.ui.composeComponents.SmallTextAndIconButton +import org.permanent.permanent.viewmodels.ArchiveOnboardingViewModel + +@Composable +fun CongratulationsPage( + viewModel: ArchiveOnboardingViewModel, + isTablet: Boolean +) { + val context = LocalContext.current + val regularFont = FontFamily(Font(R.font.open_sans_regular_ttf)) + val whiteSuperTransparentColor = + Color(ContextCompat.getColor(context, R.color.whiteSuperExtraTransparent)) + + val archives by viewModel.archives.collectAsState() + + if (isTablet) { + + } else { + PhoneBody( + viewModel, + whiteSuperTransparentColor, + regularFont, + archives + ) + } +} + +@Composable +private fun PhoneBody( + viewModel: ArchiveOnboardingViewModel, + whiteSuperTransparentColor: Color, + regularFont: FontFamily, + archives: List +) { + val scrollState = rememberScrollState() + val uriHandler = LocalUriHandler.current + + ConstraintLayout( + modifier = Modifier + .fillMaxSize() + .padding(vertical = 32.dp) + ) { + val (content, divider, spacer, buttons) = createRefs() + + Column(modifier = Modifier + .constrainAs(content) { + top.linkTo(parent.top) + bottom.linkTo(divider.top) + height = Dimension.fillToConstraints + } + .verticalScroll(scrollState) + .padding(start = 32.dp, end = 32.dp, bottom = 8.dp), + verticalArrangement = Arrangement.spacedBy(24.dp)) { + + Text( + text = stringResource(id = R.string.congratulations_title), + fontSize = 32.sp, + lineHeight = 48.sp, + color = Color.White, + fontFamily = regularFont + ) + + val annotatedString = buildAnnotatedString { + append(stringResource(id = R.string.congratulations_description_first)) + append(" ") + withStyle( + style = SpanStyle( + color = Color.White, textDecoration = TextDecoration.Underline + ) + ) { + append(stringResource(id = R.string.congratulations_description_second)) + } + addStringAnnotation( + tag = "URL", + annotation = "https://permanent.zohodesk.com/portal/en/kb/permanent-legacy-foundation", + start = this.length - stringResource(id = R.string.congratulations_description_second).length, + end = this.length + ) + } + + ClickableText(text = annotatedString, style = TextStyle( + fontSize = 14.sp, + lineHeight = 24.sp, + color = Color.White, + fontFamily = regularFont + ), onClick = { offset -> + annotatedString.getStringAnnotations("URL", offset, offset).firstOrNull() + ?.let { annotation -> + uriHandler.openUri(annotation.item) + } + }) + + Column( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + archives.forEach { archive -> + val archiveName = archive.fullName + val archiveAccessRole = archive.accessRole + + if (archiveName != null && archiveAccessRole != null) { + ArchiveItem( + title = archiveName, + subtitle = stringResource(id = R.string.invited_as) + " " + archiveAccessRole.toTitleCase(), + showSubtitle = archiveAccessRole != AccessRole.OWNER + ) + } + } + } + } + + HorizontalDivider( + modifier = Modifier.constrainAs(divider) { + bottom.linkTo(spacer.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + width = Dimension.fillToConstraints + }, thickness = 1.dp, color = whiteSuperTransparentColor + ) + + Spacer(modifier = Modifier + .height(32.dp) + .constrainAs(spacer) { + bottom.linkTo(buttons.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + }) + + Row(modifier = Modifier + .constrainAs(buttons) { + bottom.linkTo(parent.bottom) + start.linkTo(parent.start) + end.linkTo(parent.end) + width = Dimension.fillToConstraints + } + .padding(horizontal = 32.dp), horizontalArrangement = Arrangement.spacedBy(24.dp)) { + + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + SmallTextAndIconButton( + buttonColor = ButtonColor.LIGHT, + text = stringResource(id = R.string.done), + icon = painterResource(id = R.drawable.ic_done_white), + ) { + viewModel.completeArchiveOnboarding() + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/GoalsPage.kt b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/GoalsPage.kt index 582d7683..95a56f07 100644 --- a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/GoalsPage.kt +++ b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/GoalsPage.kt @@ -199,7 +199,7 @@ private fun PhoneBody( iconAlignment = ButtonIconAlignment.START ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_NAME_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_NAME.value) } } } @@ -214,7 +214,7 @@ private fun PhoneBody( ) { newArchive.goals = goals coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.PRIORITIES_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.PRIORITIES.value) } } } @@ -327,7 +327,7 @@ private fun TabletBody( iconAlignment = ButtonIconAlignment.START ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_NAME_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_NAME.value) } } } @@ -344,7 +344,7 @@ private fun TabletBody( ) { newArchive.goals = goals coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.PRIORITIES_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.PRIORITIES.value) } } } diff --git a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/PrioritiesPage.kt b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/PrioritiesPage.kt index 08e37e7f..badbfbab 100644 --- a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/PrioritiesPage.kt +++ b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/PrioritiesPage.kt @@ -22,7 +22,10 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.Saver @@ -72,6 +75,15 @@ fun PrioritiesPage( val whiteSuperTransparentColor = Color(ContextCompat.getColor(context, R.color.whiteSuperExtraTransparent)) + val newArchiveCallsSuccess by viewModel.newArchiveCallsSuccess.collectAsState() + + LaunchedEffect(newArchiveCallsSuccess) { + if (newArchiveCallsSuccess) { + pagerState.animateScrollToPage(OnboardingPage.CONGRATULATIONS.value) + viewModel.resetNewArchiveCallsSuccess() + } + } + if (isTablet) { TabletBody( viewModel, @@ -202,7 +214,7 @@ private fun PhoneBody( iconAlignment = ButtonIconAlignment.START ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.GOALS_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.GOALS.value) } } } @@ -216,9 +228,6 @@ private fun PhoneBody( buttonColor = ButtonColor.LIGHT, text = stringResource(id = R.string.next) ) { newArchive.priorities = priorities -// coroutineScope.launch { -// pagerState.animateScrollToPage(OnboardingPage.PRIORITIES_PAGE.value) -// } viewModel.createNewArchive(newArchive) } } @@ -334,7 +343,7 @@ private fun TabletBody( iconAlignment = ButtonIconAlignment.START ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.GOALS_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.GOALS.value) } } } @@ -350,9 +359,6 @@ private fun TabletBody( buttonColor = ButtonColor.LIGHT, text = stringResource(id = R.string.next) ) { newArchive.priorities = priorities -// coroutineScope.launch { -// pagerState.animateScrollToPage(OnboardingPage.PRIORITIES_PAGE.value) -// } viewModel.createNewArchive(newArchive) } } @@ -370,13 +376,11 @@ enum class OnboardingPriorityType { SAFE, NONPROFIT, GENEALOGY, PROFESSIONAL, COLLABORATE, DIGIPRES } -val OnboardingPrioritySaver: Saver = listSaver( - save = { listOf(it.type.ordinal, it.description, it.isChecked.value) }, - restore = { +val OnboardingPrioritySaver: Saver = + listSaver(save = { listOf(it.type.ordinal, it.description, it.isChecked.value) }, restore = { OnboardingPriority( type = OnboardingPriorityType.values()[it[0] as Int], description = it[1] as String, isChecked = mutableStateOf(it[2] as Boolean) ) - } -) \ No newline at end of file + }) \ No newline at end of file diff --git a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/WelcomePage.kt b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/WelcomePage.kt index 543f339f..aa0fb7eb 100644 --- a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/WelcomePage.kt +++ b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/WelcomePage.kt @@ -122,7 +122,7 @@ private fun TabletBody( showButtonEnabled = true ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_TYPE_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_TYPE.value) } } } @@ -179,7 +179,7 @@ private fun PhoneBody( showButtonEnabled = true ) { coroutineScope.launch { - pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_TYPE_PAGE.value) + pagerState.animateScrollToPage(OnboardingPage.ARCHIVE_TYPE.value) } } } diff --git a/app/src/main/java/org/permanent/permanent/ui/composeComponents/ArchiveItem.kt b/app/src/main/java/org/permanent/permanent/ui/composeComponents/ArchiveItem.kt new file mode 100644 index 00000000..c8f7a5af --- /dev/null +++ b/app/src/main/java/org/permanent/permanent/ui/composeComponents/ArchiveItem.kt @@ -0,0 +1,76 @@ +package org.permanent.permanent.ui.composeComponents + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.permanent.permanent.R + +@Composable +fun ArchiveItem( + isTablet: Boolean = false, title: String, subtitle: String, showSubtitle: Boolean = true +) { + val regularFont = FontFamily(Font(R.font.open_sans_regular_ttf)) + val boldFont = FontFamily(Font(R.font.open_sans_bold_ttf)) + + Column { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + ) { + Image( + painter = painterResource(id = R.drawable.ic_archive_gradient), + contentDescription = "", + modifier = Modifier.size(18.dp) + ) + Column( + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 24.dp) + .weight(1.0f, fill = false), + horizontalAlignment = Alignment.Start, + ) { + Text( + text = title, + fontSize = if (isTablet) 18.sp else 14.sp, + lineHeight = 24.sp, + color = Color.White, + fontFamily = boldFont + ) + if (showSubtitle) { + Text( + text = subtitle, + fontSize = if (isTablet) 18.sp else 12.sp, + lineHeight = if (isTablet) 32.sp else 16.sp, + color = Color.White.copy(alpha = 0.5f), + fontFamily = regularFont + ) + } + } + } + HorizontalDivider(color = Color.White.copy(alpha = 0.16f)) + } +} + +@Preview +@Composable +fun ArchiveItemPreview() { + ArchiveItem( + title = "The Flavia Handrea Archive", subtitle = "Invited as viewer" + ) +} \ No newline at end of file diff --git a/app/src/main/java/org/permanent/permanent/viewmodels/ArchiveOnboardingViewModel.kt b/app/src/main/java/org/permanent/permanent/viewmodels/ArchiveOnboardingViewModel.kt index 8439105c..10f05e22 100644 --- a/app/src/main/java/org/permanent/permanent/viewmodels/ArchiveOnboardingViewModel.kt +++ b/app/src/main/java/org/permanent/permanent/viewmodels/ArchiveOnboardingViewModel.kt @@ -2,7 +2,6 @@ package org.permanent.permanent.viewmodels import android.app.Application import android.content.Context -import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import kotlinx.coroutines.flow.MutableStateFlow @@ -13,7 +12,6 @@ import org.permanent.permanent.R import org.permanent.permanent.models.Account import org.permanent.permanent.models.Archive import org.permanent.permanent.models.ArchiveType -import org.permanent.permanent.models.Status import org.permanent.permanent.models.Tags import org.permanent.permanent.network.IDataListener import org.permanent.permanent.network.IResponseListener @@ -28,13 +26,7 @@ import org.permanent.permanent.repositories.StelaAccountRepository import org.permanent.permanent.repositories.StelaAccountRepositoryImpl import org.permanent.permanent.ui.PREFS_NAME import org.permanent.permanent.ui.PreferencesHelper -import org.permanent.permanent.ui.archiveOnboarding.DefaultSelectionFragment -import org.permanent.permanent.ui.archiveOnboarding.NameSettingFragment import org.permanent.permanent.ui.archiveOnboarding.OnboardingArchiveListener -import org.permanent.permanent.ui.archiveOnboarding.OnboardingPage -import org.permanent.permanent.ui.archiveOnboarding.PendingInvitationsFragment -import org.permanent.permanent.ui.archiveOnboarding.TypeSelectionFragment -import org.permanent.permanent.ui.archiveOnboarding.WelcomeFragment import org.permanent.permanent.ui.archiveOnboarding.compose.NewArchive import org.permanent.permanent.ui.archiveOnboarding.compose.OnboardingGoalType import org.permanent.permanent.ui.archiveOnboarding.compose.OnboardingPriorityType @@ -50,23 +42,14 @@ class ArchiveOnboardingViewModel(application: Application) : private var accountName = MutableLiveData("") private lateinit var newArchive: NewArchive private var isTablet = false - private val isArchiveSelected = MutableLiveData(false) private val selectedArchiveType = MutableLiveData() private val selectedArchiveTypeTitle = MutableLiveData() private val selectedArchiveTypeText = MutableLiveData() private val name = MutableLiveData() private val onPendingArchivesRetrieved = MutableLiveData>() - private val onShowNextFragment = SingleLiveEvent() private val onArchiveOnboardingDone = SingleLiveEvent() - private val currentPage = MutableLiveData(OnboardingPage.WELCOME) val progress = MutableLiveData(1) private val confirmationText = MutableLiveData() - private var welcomeFragment = WelcomeFragment() - private var typeSelectionFragment = TypeSelectionFragment() - private var nameSettingFragment = NameSettingFragment() - private var pendingInvitationsFragment = PendingInvitationsFragment() - private var defaultSelectionFragment = DefaultSelectionFragment() - private var areAllArchivesAccepted = false private var archiveRepository: IArchiveRepository = ArchiveRepositoryImpl(application) private var accountRepository: IAccountRepository = AccountRepositoryImpl(application) private var authRepository: IAuthenticationRepository = @@ -84,30 +67,26 @@ class ArchiveOnboardingViewModel(application: Application) : val isBusyState: StateFlow = _isBusyState private val _showError = MutableStateFlow("") val showError: StateFlow = _showError + private val _newArchiveCallsSuccess = MutableStateFlow(false) + val newArchiveCallsSuccess: StateFlow = _newArchiveCallsSuccess + private val _archives = MutableStateFlow>(mutableListOf()) + val archives: StateFlow> = _archives init { accountName.value = prefsHelper.getAccountName() isTablet = prefsHelper.isTablet() - getPendingArchives() + getAllArchives() } - private fun getPendingArchives() { + private fun getAllArchives() { archiveRepository.getAllArchives(object : IDataListener { override fun onSuccess(dataList: List?) { if (!dataList.isNullOrEmpty()) { - val pendingArchives: MutableList = ArrayList() + val allArchives: MutableList = ArrayList() for (data in dataList) { - val archive = Archive(data.ArchiveVO) - if (archive.status == Status.PENDING) pendingArchives.add(archive) + allArchives.add(Archive(data.ArchiveVO)) } - if (pendingArchives.isNotEmpty()) { - showFragment(pendingInvitationsFragment) - onPendingArchivesRetrieved.value = pendingArchives - } else { - showFragment(welcomeFragment) - } - } else { - showFragment(welcomeFragment) + _archives.value = allArchives } } @@ -117,36 +96,6 @@ class ArchiveOnboardingViewModel(application: Application) : }) } - private fun showFragment(fragment: Fragment) { - when (fragment) { - welcomeFragment -> { - currentPage.value = OnboardingPage.WELCOME - prefsHelper.saveArchiveOnboardingDefaultFlow(true) - } - - typeSelectionFragment -> { - currentPage.value = OnboardingPage.TYPE_SELECTION - prefsHelper.saveArchiveOnboardingDefaultFlow(true) - } - - nameSettingFragment -> { - currentPage.value = OnboardingPage.NAME_SETTING - prefsHelper.saveArchiveOnboardingDefaultFlow(true) - } - - pendingInvitationsFragment -> { - currentPage.value = OnboardingPage.PENDING_INVITATIONS - prefsHelper.saveArchiveOnboardingDefaultFlow(false) - } - - defaultSelectionFragment -> { - currentPage.value = OnboardingPage.DEFAULT_SELECTION - prefsHelper.saveArchiveOnboardingDefaultFlow(false) - } - } - onShowNextFragment.value = fragment - } - fun updateFirstProgressBarEmpty(isEmpty: Boolean) { _isFirstProgressBarEmpty.update { isEmpty } } @@ -192,6 +141,7 @@ class ArchiveOnboardingViewModel(application: Application) : object : IArchiveRepository.IArchiveListener { override fun onSuccess(archive: Archive) { _isBusyState.value = false + _archives.value.add(0, archive) setNewArchiveAsDefault(archive) } @@ -302,16 +252,25 @@ class ArchiveOnboardingViewModel(application: Application) : override fun onSuccess(message: String?) { _isBusyState.value = false - // TODO: show congrats screen + _newArchiveCallsSuccess.value = true } override fun onFailed(error: String?) { _isBusyState.value = false + _newArchiveCallsSuccess.value = true error?.let { _showError.value = it } } }) } + fun resetNewArchiveCallsSuccess() { + _newArchiveCallsSuccess.value = false + } + + fun completeArchiveOnboarding() { + onArchiveOnboardingDone.call() + } + // fun onBackBtnClick() { // if (currentPage.value == OnboardingPage.TYPE_SELECTION) { // if (onPendingArchivesRetrieved.value?.isNotEmpty() == true) { @@ -421,14 +380,11 @@ class ArchiveOnboardingViewModel(application: Application) : fun getShowMessage(): LiveData = showMessage fun getAccountName() = accountName fun isTablet() = isTablet - fun getCurrentPage(): MutableLiveData = currentPage - fun getIsArchiveSelected(): MutableLiveData = isArchiveSelected fun getSelectedArchiveType(): MutableLiveData = selectedArchiveType fun getSelectedArchiveTypeTitle(): MutableLiveData = selectedArchiveTypeTitle fun getSelectedArchiveTypeText(): MutableLiveData = selectedArchiveTypeText fun getName(): MutableLiveData = name fun getConfirmationText(): MutableLiveData = confirmationText fun getOnArchivesRetrieved(): LiveData> = onPendingArchivesRetrieved - fun getOnShowNextFragment(): LiveData = onShowNextFragment fun getOnArchiveOnboardingDone(): LiveData = onArchiveOnboardingDone } diff --git a/app/src/main/res/drawable/ic_archive_gradient.xml b/app/src/main/res/drawable/ic_archive_gradient.xml new file mode 100644 index 00000000..44f69619 --- /dev/null +++ b/app/src/main/res/drawable/ic_archive_gradient.xml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d05704e2..ba0474de 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -564,6 +564,9 @@ Let’s set some goals. Everyone has unique goals for preserving their legacy. We want to learn more about how we can help you achieve yours. Tell us what’s\nimportant to you Finally, we’re curious — what brought you to Permanent.org? + Congratulations! + Get started by uploading your first files, or learn more about your new archive by + viewing our help articles here. Capture and preserve memories for storytelling Digitize or transfer my materials securely Collaborate with others to build my archive @@ -579,6 +582,7 @@ Professional business needs/clients Collaborate with a family member, friend, or colleague Interest in digital preservation solutions + Invited as Leave share @@ -644,6 +648,7 @@ Contact email address… Note Permanent will send an automatic email to your Legacy Contact to inform them that they have been designated to activate your Account Legacy Plan, alongside instructions on how to do so. However, we strongly recommend that you also discuss your Legacy Plan with this person. + Done Designate archive steward In order to designate someone as a steward for your archive, they must have a Permanent.org account. From a39796c6d06634b67f2cfeaa813e8a8fd5d8d3d2 Mon Sep 17 00:00:00 2001 From: Flavia Handrea Date: Tue, 30 Jul 2024 11:45:21 +0300 Subject: [PATCH 2/2] Fixed bug that was showing empty snackbar at initialization with empty string. --- .../ui/archiveOnboarding/compose/ArchiveOnboardingScreen.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveOnboardingScreen.kt b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveOnboardingScreen.kt index 6d3d8035..ade06c18 100644 --- a/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveOnboardingScreen.kt +++ b/app/src/main/java/org/permanent/permanent/ui/archiveOnboarding/compose/ArchiveOnboardingScreen.kt @@ -310,7 +310,8 @@ fun ArchiveOnboardingScreen( LaunchedEffect(errorMessage) { coroutineScope.launch { - snackbarHostState.showSnackbar(errorMessage) + if (errorMessage.isNotEmpty()) + snackbarHostState.showSnackbar(errorMessage) } }