diff --git a/README.md b/README.md
index 3d3810af1..d403fcfd4 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ Note that Orca isn't a fork of the official app. Its overall structure has been
## Structure
-
+
Each module represents the context to which its underlying structures are related.
diff --git a/composite/status/build.gradle.kts b/composite/status/build.gradle.kts
new file mode 100644
index 000000000..c163f498b
--- /dev/null
+++ b/composite/status/build.gradle.kts
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+}
+
+android {
+ buildFeatures.compose = true
+ composeOptions.kotlinCompilerExtensionVersion = libs.versions.android.compose.compiler.get()
+ defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+}
+
+dependencies {
+ androidTestImplementation(libs.android.compose.ui.test.junit)
+ androidTestImplementation(libs.android.compose.ui.test.manifest)
+ androidTestImplementation(libs.assertk)
+ androidTestImplementation(libs.kotlin.test)
+
+ implementation(project(":core:sample"))
+ implementation(project(":platform:autos"))
+ implementation(libs.android.core)
+}
diff --git a/composite/status/src/androidTest/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardStateExtensionsTests.kt b/composite/status/src/androidTest/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardStateExtensionsTests.kt
new file mode 100644
index 000000000..d072fa682
--- /dev/null
+++ b/composite/status/src/androidTest/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardStateExtensionsTests.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.composite.status.state
+
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.ui.test.junit4.createComposeRule
+import assertk.assertThat
+import assertk.assertions.isSameAs
+import kotlin.test.Test
+import org.junit.Rule
+
+internal class StatusCardStateExtensionsTests {
+ @get:Rule val composeRule = createComposeRule()
+
+ @Test
+ fun remembersEverLoadingState() {
+ composeRule.setContent {
+ val state = rememberStatusCardState()
+
+ DisposableEffect(state) {
+ assertThat(state).isSameAs(StatusCardState.EverLoading)
+ onDispose {}
+ }
+ }
+ }
+}
diff --git a/composite/status/src/androidTest/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardStateTests.kt b/composite/status/src/androidTest/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardStateTests.kt
new file mode 100644
index 000000000..f3293d6dd
--- /dev/null
+++ b/composite/status/src/androidTest/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardStateTests.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.composite.status.state
+
+import assertk.assertThat
+import assertk.assertions.isSameAs
+import com.jeanbarrossilva.orca.composite.status.Status
+import kotlin.test.Test
+
+internal class StatusCardStateTests {
+ @Test
+ fun changesStatusToNonLoadingOnes() {
+ Status.entries
+ .filter { it != Status.Loading }
+ .forEach {
+ assertThat(StatusCardState(targetStatus = it).apply(StatusCardState::loadStatus).status)
+ .isSameAs(it)
+ }
+ }
+
+ @Test
+ fun everLoadingStateNeverLoads() {
+ StatusCardState.EverLoading.loadStatus()
+ assertThat(StatusCardState.EverLoading.status).isSameAs(Status.Loading)
+ }
+}
diff --git a/composite/status/src/main/AndroidManifest.xml b/composite/status/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a51def919
--- /dev/null
+++ b/composite/status/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+
+
+
diff --git a/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/Status.kt b/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/Status.kt
new file mode 100644
index 000000000..190b4c3e8
--- /dev/null
+++ b/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/Status.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.composite.status
+
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.jeanbarrossilva.orca.core.instance.Instance
+import com.jeanbarrossilva.orca.platform.autos.colors.asColor
+import com.jeanbarrossilva.orca.platform.autos.theme.AutosTheme
+
+/** Account registration stage that a [StatusCard] can represent. */
+@Immutable
+enum class Status {
+ /** Denotes that an [Instance] at which an account can be registered is being searched for. */
+ Loading {
+ @Composable
+ override fun Indicator(modifier: Modifier) {
+ CircularProgressIndicator(
+ Modifier.size(24.dp),
+ AutosTheme.colors.secondary.asColor,
+ strokeCap = StrokeCap.Round
+ )
+ }
+
+ @Composable
+ override fun Description(modifier: Modifier) {
+ Text(stringResource(R.string.composite_status_loading))
+ }
+ },
+
+ /**
+ * Denotes that an available [Instance] has been found and an account been registered
+ * successfully.
+ */
+ Succeeded {
+ @Composable
+ override fun Indicator(modifier: Modifier) {
+ StatusIndicator(
+ com.jeanbarrossilva.orca.platform.autos.R.drawable.icon_selected,
+ AutosTheme.colors.activation.reposted.asColor,
+ Color.White
+ )
+ }
+
+ @Composable
+ override fun Description(modifier: Modifier) {
+ Text(stringResource(R.string.composite_status_succeeded))
+ }
+ },
+
+ /** Denotes that an account couldn't be registered at a given [Instance]. */
+ Failed {
+ @Composable
+ override fun Indicator(modifier: Modifier) {
+ StatusIndicator(
+ com.jeanbarrossilva.orca.platform.autos.R.drawable.icon_close,
+ AutosTheme.colors.activation.favorite.asColor,
+ Color.White
+ )
+ }
+
+ @Composable
+ override fun Description(modifier: Modifier) {
+ Text(stringResource(R.string.composite_status_failed))
+ }
+ };
+
+ /**
+ * [StatusIndicator] that matches the [Description] and helps to easily identify whether an
+ * account could be registered or if the process itself hasn't yet completed.
+ */
+ @Composable
+ fun Indicator() {
+ Indicator(Modifier)
+ }
+
+ /**
+ * [Text] that clearly describes whether an account has been registered, hasn't or the process is
+ * still ongoing.
+ */
+ @Composable
+ fun Description() {
+ Description(Modifier)
+ }
+
+ /**
+ * [StatusIndicator] that matches the [Description] and helps to easily identify whether an
+ * account could be registered or if the process itself hasn't yet completed.
+ *
+ * @param modifier [Modifier] that is applied to the [StatusIndicator].
+ */
+ @Composable abstract fun Indicator(modifier: Modifier)
+
+ /**
+ * [Text] that clearly describes whether an account has been registered, hasn't or the process is
+ * still ongoing.
+ *
+ * @param modifier [Modifier] that is applied to the [Text].
+ */
+ @Composable abstract fun Description(modifier: Modifier)
+}
diff --git a/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/StatusCard.kt b/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/StatusCard.kt
new file mode 100644
index 000000000..b2230997a
--- /dev/null
+++ b/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/StatusCard.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.composite.status
+
+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.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.ProvideTextStyle
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.jeanbarrossilva.orca.composite.status.state.StatusCardState
+import com.jeanbarrossilva.orca.composite.status.state.rememberStatusCardState
+import com.jeanbarrossilva.orca.core.instance.domain.Domain
+import com.jeanbarrossilva.orca.core.sample.instance.domain.sample
+import com.jeanbarrossilva.orca.platform.autos.borders.asBorderStroke
+import com.jeanbarrossilva.orca.platform.autos.theme.AutosTheme
+import com.jeanbarrossilva.orca.platform.autos.theme.MultiThemePreview
+
+/**
+ * [Card] for displaying the progress of an account registration process.
+ *
+ * @param state [StatusCardState] that holds the current operation [Status].
+ * @param modifier [Modifier] that is applied to the [Card].
+ * @param title Message shown alongside an indicator and a description of what the current [Status]
+ * is.
+ * @see rememberStatusCardState
+ * @see Status.Indicator
+ * @see Status.Description
+ */
+@Composable
+fun StatusCard(
+ state: StatusCardState,
+ modifier: Modifier = Modifier,
+ title: @Composable () -> Unit
+) {
+ val spacing = AutosTheme.spacings.large.dp
+
+ Card(
+ modifier,
+ colors = CardDefaults.outlinedCardColors(),
+ border = AutosTheme.borders.default.asBorderStroke
+ ) {
+ Row(
+ Modifier.padding(spacing).fillMaxWidth(),
+ Arrangement.spacedBy(spacing),
+ Alignment.CenterVertically
+ ) {
+ state.status.Indicator()
+
+ Column(verticalArrangement = Arrangement.spacedBy(AutosTheme.spacings.small.dp)) {
+ ProvideTextStyle(AutosTheme.typography.titleMedium, title)
+ ProvideTextStyle(AutosTheme.typography.labelMedium) { state.status.Description() }
+ }
+ }
+ }
+}
+
+/**
+ * Preview of a [StatusCard] with a loading [Status].
+ *
+ * @see Status.Loading
+ */
+@Composable
+@MultiThemePreview
+private fun LoadingStatusCardPreview() {
+ AutosTheme { StatusCard(rememberStatusCardState()) { Text("${Domain.sample}") } }
+}
+
+/**
+ * Preview of a [StatusCard] with a succeeded [Status].
+ *
+ * @see Status.Succeeded
+ */
+@Composable
+@MultiThemePreview
+private fun SucceededStatusCardPreview() {
+ AutosTheme { StatusCard(rememberStatusCardState(Status.Succeeded)) { Text("${Domain.sample}") } }
+}
+
+/**
+ * Preview of a [StatusCard] with a failed [Status].
+ *
+ * @see Status.Failed
+ */
+@Composable
+@MultiThemePreview
+private fun FailedStatusCardPreview() {
+ AutosTheme { StatusCard(rememberStatusCardState(Status.Failed)) { Text("${Domain.sample}") } }
+}
diff --git a/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/StatusIndicator.kt b/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/StatusIndicator.kt
new file mode 100644
index 000000000..5b47c76a3
--- /dev/null
+++ b/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/StatusIndicator.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.composite.status
+
+import android.graphics.drawable.Drawable
+import androidx.annotation.DrawableRes
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.size
+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.asImageBitmap
+import androidx.compose.ui.graphics.drawscope.scale
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.core.content.ContextCompat
+import androidx.core.graphics.drawable.toBitmap
+import com.jeanbarrossilva.orca.platform.autos.R
+import com.jeanbarrossilva.orca.platform.autos.colors.asColor
+import com.jeanbarrossilva.orca.platform.autos.theme.AutosTheme
+
+/**
+ * Contained icon for facilitating the understanding of what a given [Status] means.
+ *
+ * @param iconResource [Drawable] resource of the icon.
+ * @param containerColor [Color] by which the container with the icon will be colored.
+ * @param contentColor [Color] of the icon itself.
+ * @param modifier [Modifier] that is applied to the [Canvas].
+ */
+@Composable
+internal fun StatusIndicator(
+ @DrawableRes iconResource: Int,
+ containerColor: Color,
+ contentColor: Color,
+ modifier: Modifier = Modifier
+) {
+ val context = LocalContext.current
+ val contentColorInArgb = remember(contentColor, contentColor::toArgb)
+ val icon =
+ remember(context, iconResource, contentColorInArgb) {
+ ContextCompat.getDrawable(context, iconResource)
+ .let(::checkNotNull)
+ .apply { setTint(contentColorInArgb) }
+ .toBitmap()
+ .asImageBitmap()
+ }
+
+ Canvas(modifier.size(24.dp)) {
+ drawCircle(containerColor)
+ scale(.7f) { drawImage(icon) }
+ }
+}
+
+/** Preview of a [StatusIndicator]. */
+@Composable
+@Preview
+private fun StatusIndicatorPreview() {
+ AutosTheme {
+ StatusIndicator(
+ R.drawable.icon_profile_filled,
+ containerColor = AutosTheme.colors.activation.favorite.asColor,
+ contentColor = Color.White
+ )
+ }
+}
diff --git a/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardState.extensions.kt b/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardState.extensions.kt
new file mode 100644
index 000000000..d0e5957a4
--- /dev/null
+++ b/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardState.extensions.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.composite.status.state
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import com.jeanbarrossilva.orca.composite.status.Status
+import kotlin.time.Duration
+import kotlinx.coroutines.delay
+
+/**
+ * Remembers a [StatusCardState].
+ *
+ * @param targetStatus [Status] to which the current one will be changed when it loads.
+ * @param delay [Duration] to wait for before loading the [Status].
+ * @see StatusCardState.status
+ * @see StatusCardState.loadStatus
+ */
+@Composable
+fun rememberStatusCardState(
+ targetStatus: Status = Status.Loading,
+ delay: Duration = Duration.ZERO
+): StatusCardState {
+ /*
+ * Regardless of the delay, given that the initial status of a state is the loading one, having it
+ * also be the target status means that the actual status won't ever change at all when it is
+ * requested to be "loaded" (that is, to be set as the specified target one).
+ */
+ val isEverLoading = remember(targetStatus) { targetStatus == Status.Loading }
+
+ return remember(isEverLoading) {
+ if (isEverLoading) {
+ StatusCardState.EverLoading
+ } else {
+ StatusCardState(targetStatus)
+ }
+ }
+ .also {
+ LaunchedEffect(it, isEverLoading, delay) {
+ if (!isEverLoading) {
+ delay(delay)
+ it.loadStatus()
+ }
+ }
+ }
+}
diff --git a/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardState.kt b/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardState.kt
new file mode 100644
index 000000000..02e1259cd
--- /dev/null
+++ b/composite/status/src/main/java/com/jeanbarrossilva/orca/composite/status/state/StatusCardState.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.composite.status.state
+
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import com.jeanbarrossilva.orca.composite.status.Status
+import com.jeanbarrossilva.orca.composite.status.StatusCard
+
+/**
+ * State by which the [Status] of a [StatusCard] is held and loaded.
+ *
+ * @param targetStatus [Status] to which the current one will be changed when it loads.
+ * @see status
+ * @see loadStatus
+ */
+class StatusCardState internal constructor(private val targetStatus: Status) {
+ /**
+ * Current [Status] of the [StatusCard]. It starts as loading by default and can be changed to the
+ * target one through [loadStatus].
+ *
+ * @see Status.Loading
+ * @see targetStatus
+ */
+ internal var status by mutableStateOf(Status.Loading)
+ private set
+
+ /**
+ * Defines the current [Status] as the target one with which this [StatusCardState] has been
+ * created if it's loading.
+ *
+ * @see status
+ * @see targetStatus
+ * @see Status.Loading
+ */
+ internal fun loadStatus() {
+ if (status == Status.Loading) {
+ status = targetStatus
+ }
+ }
+
+ companion object {
+ /**
+ * An immutable [StatusCardState], with a never-changing loading [status].
+ *
+ * @see Status.Loading
+ */
+ @Stable internal val EverLoading = StatusCardState(targetStatus = Status.Loading)
+ }
+}
diff --git a/composite/status/src/main/res/values-pt-rBR/strings.xml b/composite/status/src/main/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000..8728ec48b
--- /dev/null
+++ b/composite/status/src/main/res/values-pt-rBR/strings.xml
@@ -0,0 +1,20 @@
+
+
+
+ Tentando criar conta…
+ Conta criada com sucesso.
+ Não foi possível criar uma conta.
+
diff --git a/composite/status/src/main/res/values/strings.xml b/composite/status/src/main/res/values/strings.xml
new file mode 100644
index 000000000..9aa72dba6
--- /dev/null
+++ b/composite/status/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+
+
+
+ Attempting to create account…
+ Account created successfully.
+ Couldn\'t create account.
+
diff --git a/feature/registration/build.gradle.kts b/feature/registration/build.gradle.kts
new file mode 100644
index 000000000..602071efd
--- /dev/null
+++ b/feature/registration/build.gradle.kts
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.symbolProcessor)
+}
+
+android {
+ buildFeatures.compose = true
+ composeOptions.kotlinCompilerExtensionVersion = libs.versions.android.compose.compiler.get()
+ defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ packagingOptions.resources.excludes +=
+ arrayOf("META-INF/LICENSE.md", "META-INF/LICENSE-notice.md")
+}
+
+dependencies {
+ androidTestImplementation(project(":platform:autos-test"))
+ androidTestImplementation(project(":platform:navigation"))
+ androidTestImplementation(project(":platform:navigation-test"))
+ androidTestImplementation(project(":std:injector-test"))
+ androidTestImplementation(libs.android.compose.ui.test.junit)
+ androidTestImplementation(libs.android.fragment.testing)
+ androidTestImplementation(libs.assertk)
+ androidTestImplementation(libs.kotlin.test)
+ androidTestImplementation(libs.mockk)
+
+ api(project(":platform:navigation"))
+ api(project(":std:injector"))
+
+ implementation(project(":composite:composable"))
+ implementation(project(":composite:status"))
+ implementation(project(":core:sample"))
+ implementation(project(":platform:animator"))
+ implementation(project(":platform:autos"))
+ implementation(project(":platform:stack"))
+
+ ksp(project(":std:injector-processor"))
+}
diff --git a/feature/registration/credentials/build.gradle.kts b/feature/registration/credentials/build.gradle.kts
new file mode 100644
index 000000000..2f466d236
--- /dev/null
+++ b/feature/registration/credentials/build.gradle.kts
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+}
+
+android {
+ buildFeatures.compose = true
+ composeOptions.kotlinCompilerExtensionVersion = libs.versions.android.compose.compiler.get()
+ defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+}
+
+dependencies {
+ androidTestImplementation(project(":platform:navigation-test"))
+ androidTestImplementation(libs.assertk)
+ androidTestImplementation(libs.kotlin.test)
+
+ api(project(":composite:composable"))
+ api(project(":platform:navigation"))
+
+ implementation(project(":platform:autos"))
+}
diff --git a/feature/registration/credentials/src/androidTest/java/com/jeanbarrossilva/orca/feature/registration/credentials/CredentialsFragmentTests.kt b/feature/registration/credentials/src/androidTest/java/com/jeanbarrossilva/orca/feature/registration/credentials/CredentialsFragmentTests.kt
new file mode 100644
index 000000000..a573de8e0
--- /dev/null
+++ b/feature/registration/credentials/src/androidTest/java/com/jeanbarrossilva/orca/feature/registration/credentials/CredentialsFragmentTests.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration.credentials
+
+import assertk.assertThat
+import com.jeanbarrossilva.orca.platform.navigation.navigator
+import com.jeanbarrossilva.orca.platform.navigation.test.activity.launchNavigationActivity
+import com.jeanbarrossilva.orca.platform.navigation.test.isAt
+import kotlin.test.Test
+
+internal class CredentialsFragmentTests {
+ @Test
+ fun navigates() {
+ launchNavigationActivity().use { scenario ->
+ scenario.onActivity { activity ->
+ CredentialsFragment.navigate(activity.navigator)
+ assertThat(activity).isAt(CredentialsFragment.ROUTE)
+ }
+ }
+ }
+}
diff --git a/feature/registration/credentials/src/main/AndroidManifest.xml b/feature/registration/credentials/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..e56813681
--- /dev/null
+++ b/feature/registration/credentials/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+
+
+
diff --git a/feature/registration/credentials/src/main/java/com/jeanbarrossilva/orca/feature/registration/credentials/Credentials.kt b/feature/registration/credentials/src/main/java/com/jeanbarrossilva/orca/feature/registration/credentials/Credentials.kt
new file mode 100644
index 000000000..1801bbdd6
--- /dev/null
+++ b/feature/registration/credentials/src/main/java/com/jeanbarrossilva/orca/feature/registration/credentials/Credentials.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration.credentials
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+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.lazy.LazyColumn
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import com.jeanbarrossilva.orca.platform.autos.kit.action.button.PrimaryButton
+import com.jeanbarrossilva.orca.platform.autos.kit.input.text.FormTextField
+import com.jeanbarrossilva.orca.platform.autos.kit.scaffold.Scaffold
+import com.jeanbarrossilva.orca.platform.autos.kit.scaffold.bar.button.ButtonBar
+import com.jeanbarrossilva.orca.platform.autos.kit.scaffold.plus
+import com.jeanbarrossilva.orca.platform.autos.theme.AutosTheme
+import com.jeanbarrossilva.orca.platform.autos.theme.MultiThemePreview
+
+@Composable
+internal fun Credentials(viewModel: CredentialsViewModel, modifier: Modifier = Modifier) {
+ val email by viewModel.emailFlow.collectAsState()
+ val password by viewModel.passwordFlow.collectAsState()
+
+ Credentials(
+ email,
+ onEmailChange = viewModel::setEmail,
+ password,
+ onPasswordChange = viewModel::setPassword,
+ modifier
+ )
+}
+
+@Composable
+private fun Credentials(
+ email: String,
+ onEmailChange: (email: String) -> Unit,
+ password: String,
+ onPasswordChange: (password: String) -> Unit,
+ modifier: Modifier = Modifier
+) {
+ val spacing = AutosTheme.spacings.large.dp
+ val spacerModifier = remember { Modifier.height(spacing) }
+
+ Scaffold(
+ modifier,
+ bottom = {
+ ButtonBar {
+ PrimaryButton(onClick = {}) {
+ Text(stringResource(R.string.feature_registration_credentials_confirm))
+ }
+ }
+ }
+ ) {
+ expanded {
+ LazyColumn(
+ Modifier.fillMaxSize(),
+ contentPadding = it + PaddingValues(spacing),
+ verticalArrangement = Arrangement.SpaceBetween,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ item { Spacer(spacerModifier) }
+
+ item {
+ Column(
+ Modifier.fillMaxWidth(),
+ Arrangement.spacedBy(AutosTheme.spacings.medium.dp),
+ Alignment.CenterHorizontally
+ ) {
+ Text(
+ stringResource(R.string.feature_registration_credentials),
+ textAlign = TextAlign.Center,
+ style = AutosTheme.typography.headlineLarge
+ )
+
+ Text(
+ stringResource(R.string.feature_registration_credentials_explanation),
+ textAlign = TextAlign.Center,
+ style = AutosTheme.typography.headlineSmall
+ )
+ }
+ }
+
+ item {
+ Column(verticalArrangement = Arrangement.spacedBy(AutosTheme.spacings.small.dp)) {
+ FormTextField(email, onEmailChange) {
+ Text(stringResource(R.string.feature_registration_credentials_email))
+ }
+
+ FormTextField(password, onPasswordChange) {
+ Text(stringResource(R.string.feature_registration_credentials_password))
+ }
+ }
+ }
+
+ item { Spacer(spacerModifier) }
+ }
+ }
+ }
+}
+
+@Composable
+@MultiThemePreview
+private fun CredentialsPreview() {
+ AutosTheme {
+ Credentials(
+ email = "jean@orcinus.com.br",
+ onEmailChange = {},
+ password = "password123",
+ onPasswordChange = {}
+ )
+ }
+}
diff --git a/feature/registration/credentials/src/main/java/com/jeanbarrossilva/orca/feature/registration/credentials/CredentialsFragment.kt b/feature/registration/credentials/src/main/java/com/jeanbarrossilva/orca/feature/registration/credentials/CredentialsFragment.kt
new file mode 100644
index 000000000..60d0f54c0
--- /dev/null
+++ b/feature/registration/credentials/src/main/java/com/jeanbarrossilva/orca/feature/registration/credentials/CredentialsFragment.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration.credentials
+
+import androidx.compose.runtime.Composable
+import androidx.fragment.app.viewModels
+import com.jeanbarrossilva.orca.composite.composable.ComposableFragment
+import com.jeanbarrossilva.orca.platform.navigation.Navigator
+import com.jeanbarrossilva.orca.platform.navigation.transition.opening
+
+class CredentialsFragment internal constructor() : ComposableFragment() {
+ private val viewModel by
+ viewModels(factoryProducer = CredentialsViewModel::createFactory)
+
+ @Composable
+ override fun Content() {
+ Credentials(viewModel)
+ }
+
+ companion object {
+ internal const val ROUTE = "credentials"
+
+ fun navigate(navigator: Navigator) {
+ navigator.navigate(opening()) { to(ROUTE, ::CredentialsFragment) }
+ }
+ }
+}
diff --git a/feature/registration/credentials/src/main/java/com/jeanbarrossilva/orca/feature/registration/credentials/CredentialsViewModel.kt b/feature/registration/credentials/src/main/java/com/jeanbarrossilva/orca/feature/registration/credentials/CredentialsViewModel.kt
new file mode 100644
index 000000000..0b540cb68
--- /dev/null
+++ b/feature/registration/credentials/src/main/java/com/jeanbarrossilva/orca/feature/registration/credentials/CredentialsViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration.credentials
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewmodel.initializer
+import androidx.lifecycle.viewmodel.viewModelFactory
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+internal class CredentialsViewModel : ViewModel() {
+ private val emailMutableFlow = MutableStateFlow("")
+ private val passwordMutableFlow = MutableStateFlow("")
+
+ val emailFlow = emailMutableFlow.asStateFlow()
+ val passwordFlow = passwordMutableFlow.asStateFlow()
+
+ fun setEmail(email: String) {
+ emailMutableFlow.value = email
+ }
+
+ fun setPassword(password: String) {
+ passwordMutableFlow.value = password
+ }
+
+ companion object {
+ fun createFactory(): ViewModelProvider.Factory {
+ return viewModelFactory { initializer { CredentialsViewModel() } }
+ }
+ }
+}
diff --git a/feature/registration/credentials/src/main/res/values-pt-rBR/strings.xml b/feature/registration/credentials/src/main/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000..53bffa905
--- /dev/null
+++ b/feature/registration/credentials/src/main/res/values-pt-rBR/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+ Criar conta
+ Confirmar
+ E-mail
+
+ Insira as credenciais a serem usadas para tentar criar uma conta numa instância que esteja
+ disponível.
+
+ Senha
+
\ No newline at end of file
diff --git a/feature/registration/credentials/src/main/res/values/strings.xml b/feature/registration/credentials/src/main/res/values/strings.xml
new file mode 100644
index 000000000..3c614f6d1
--- /dev/null
+++ b/feature/registration/credentials/src/main/res/values/strings.xml
@@ -0,0 +1,24 @@
+
+
+
+ Create account
+ Confirm
+ E-mail
+
+ Enter the credentials to be used for trying to create an account at an available instance.
+
+ Password
+
\ No newline at end of file
diff --git a/feature/registration/ongoing/build.gradle.kts b/feature/registration/ongoing/build.gradle.kts
new file mode 100644
index 000000000..f15099f59
--- /dev/null
+++ b/feature/registration/ongoing/build.gradle.kts
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+}
+
+android {
+ buildFeatures.compose = true
+ composeOptions.kotlinCompilerExtensionVersion = libs.versions.android.compose.compiler.get()
+ defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+}
+
+dependencies {
+ androidTestImplementation(project(":platform:navigation-test"))
+ androidTestImplementation(libs.assertk)
+ androidTestImplementation(libs.kotlin.test)
+
+ api(project(":composite:composable"))
+ api(project(":platform:navigation"))
+
+ implementation(project(":composite:status"))
+ implementation(project(":core:sample"))
+ implementation(project(":platform:autos"))
+ implementation(project(":platform:stack"))
+}
diff --git a/feature/registration/ongoing/src/androidTest/java/com/jeanbarrossilva/orca/feature/registration/ongoing/OngoingFragmentTests.kt b/feature/registration/ongoing/src/androidTest/java/com/jeanbarrossilva/orca/feature/registration/ongoing/OngoingFragmentTests.kt
new file mode 100644
index 000000000..328eb0717
--- /dev/null
+++ b/feature/registration/ongoing/src/androidTest/java/com/jeanbarrossilva/orca/feature/registration/ongoing/OngoingFragmentTests.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration.ongoing
+
+import assertk.assertThat
+import com.jeanbarrossilva.orca.platform.navigation.navigator
+import com.jeanbarrossilva.orca.platform.navigation.test.activity.launchNavigationActivity
+import com.jeanbarrossilva.orca.platform.navigation.test.isAt
+import kotlin.test.Test
+
+internal class OngoingFragmentTests {
+ @Test
+ fun navigates() {
+ launchNavigationActivity().use { scenario ->
+ scenario.onActivity { activity ->
+ OngoingFragment.navigate(activity.navigator)
+ assertThat(activity).isAt(OngoingFragment.ROUTE)
+ }
+ }
+ }
+}
diff --git a/feature/registration/ongoing/src/main/AndroidManifest.xml b/feature/registration/ongoing/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..e56813681
--- /dev/null
+++ b/feature/registration/ongoing/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+
+
+
diff --git a/feature/registration/ongoing/src/main/java/com/jeanbarrossilva/orca/feature/registration/ongoing/Ongoing.kt b/feature/registration/ongoing/src/main/java/com/jeanbarrossilva/orca/feature/registration/ongoing/Ongoing.kt
new file mode 100644
index 000000000..1840ccd14
--- /dev/null
+++ b/feature/registration/ongoing/src/main/java/com/jeanbarrossilva/orca/feature/registration/ongoing/Ongoing.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration.ongoing
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.scaleIn
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import com.jeanbarrossilva.orca.composite.status.Status
+import com.jeanbarrossilva.orca.composite.status.StatusCard
+import com.jeanbarrossilva.orca.composite.status.state.rememberStatusCardState
+import com.jeanbarrossilva.orca.core.instance.domain.Domain
+import com.jeanbarrossilva.orca.core.sample.instance.domain.sample
+import com.jeanbarrossilva.orca.core.sample.instance.domain.samples
+import com.jeanbarrossilva.orca.platform.autos.kit.scaffold.Scaffold
+import com.jeanbarrossilva.orca.platform.autos.template.onboarding.Onboarding
+import com.jeanbarrossilva.orca.platform.autos.theme.AutosTheme
+import com.jeanbarrossilva.orca.platform.autos.theme.MultiThemePreview
+import com.jeanbarrossilva.orca.platform.stack.Stack
+import com.jeanbarrossilva.orca.platform.stack.StackScope
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+@Composable
+internal fun Ongoing(viewModel: OngoingViewModel, modifier: Modifier = Modifier) {
+ val indexedDomain by viewModel.indexedDomainFlow.collectAsState()
+ Ongoing(indexedDomain, OngoingViewModel.perDomainDelay, modifier)
+}
+
+@Composable
+private fun Ongoing(
+ indexedDomain: IndexedValue?,
+ perDomainDelay: Duration,
+ modifier: Modifier = Modifier
+) {
+ var statusCardStackScope by remember { mutableStateOf(null) }
+ val statusCardAnimationSpec = tween()
+ val statusCardEnterTransition =
+ remember(statusCardAnimationSpec) {
+ fadeIn(statusCardAnimationSpec) + scaleIn(statusCardAnimationSpec, initialScale = .8f)
+ }
+
+ LaunchedEffect(indexedDomain, statusCardStackScope, statusCardEnterTransition) {
+ if (statusCardStackScope != null && indexedDomain != null) {
+ statusCardStackScope?.item {
+ AnimatedVisibility(
+ remember { MutableTransitionState(false).apply { targetState = true } },
+ enter = statusCardEnterTransition
+ ) {
+ StatusCard(
+ rememberStatusCardState(
+ targetStatus =
+ if (indexedDomain.index == Domain.samples.lastIndex) {
+ Status.Succeeded
+ } else {
+ Status.Failed
+ },
+ perDomainDelay / 2
+ )
+ ) {
+ Text("${indexedDomain.value}")
+ }
+ }
+ }
+ }
+ }
+
+ Scaffold(modifier) {
+ expanded {
+ Onboarding(
+ illustration = { Stack { statusCardStackScope = this } },
+ title = { Text(stringResource(R.string.feature_registration_ongoing)) },
+ description = { Text(stringResource(R.string.feature_registration_ongoing_description)) },
+ contentPadding = it
+ )
+ }
+ }
+}
+
+@Composable
+@MultiThemePreview
+private fun OngoingPreview() {
+ AutosTheme { Ongoing(IndexedValue(0, Domain.sample), perDomainDelay = 4.seconds) }
+}
diff --git a/feature/registration/ongoing/src/main/java/com/jeanbarrossilva/orca/feature/registration/ongoing/OngoingFragment.kt b/feature/registration/ongoing/src/main/java/com/jeanbarrossilva/orca/feature/registration/ongoing/OngoingFragment.kt
new file mode 100644
index 000000000..296f4a0ee
--- /dev/null
+++ b/feature/registration/ongoing/src/main/java/com/jeanbarrossilva/orca/feature/registration/ongoing/OngoingFragment.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration.ongoing
+
+import androidx.compose.runtime.Composable
+import androidx.fragment.app.viewModels
+import com.jeanbarrossilva.orca.composite.composable.ComposableFragment
+import com.jeanbarrossilva.orca.platform.navigation.Navigator
+import com.jeanbarrossilva.orca.platform.navigation.transition.opening
+
+internal class OngoingFragment : ComposableFragment() {
+ private val viewModel by
+ viewModels(factoryProducer = OngoingViewModel::createFactory)
+
+ @Composable
+ override fun Content() {
+ Ongoing(viewModel)
+ }
+
+ companion object {
+ const val ROUTE = "ONGOING"
+
+ fun navigate(navigator: Navigator) {
+ navigator.navigate(opening()) { to(ROUTE, ::OngoingFragment) }
+ }
+ }
+}
diff --git a/feature/registration/ongoing/src/main/java/com/jeanbarrossilva/orca/feature/registration/ongoing/OngoingViewModel.kt b/feature/registration/ongoing/src/main/java/com/jeanbarrossilva/orca/feature/registration/ongoing/OngoingViewModel.kt
new file mode 100644
index 000000000..b57554a70
--- /dev/null
+++ b/feature/registration/ongoing/src/main/java/com/jeanbarrossilva/orca/feature/registration/ongoing/OngoingViewModel.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration.ongoing
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.initializer
+import androidx.lifecycle.viewmodel.viewModelFactory
+import com.jeanbarrossilva.orca.core.instance.domain.Domain
+import com.jeanbarrossilva.orca.core.sample.instance.domain.samples
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.asFlow
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.withIndex
+
+internal class OngoingViewModel : ViewModel() {
+ val indexedDomainFlow =
+ Domain.samples
+ .asFlow()
+ .withIndex()
+ .onEach { (index, _) ->
+ if (index > 0) {
+ delay(perDomainDelay)
+ }
+ }
+ .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = null)
+
+ companion object {
+ val perDomainDelay = 4.seconds
+
+ fun createFactory(): ViewModelProvider.Factory {
+ return viewModelFactory { initializer { OngoingViewModel() } }
+ }
+ }
+}
diff --git a/feature/registration/ongoing/src/main/res/values-pt-rBR/strings.xml b/feature/registration/ongoing/src/main/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000..d5664f959
--- /dev/null
+++ b/feature/registration/ongoing/src/main/res/values-pt-rBR/strings.xml
@@ -0,0 +1,23 @@
+
+
+
+ Criando uma conta para você…
+
+ O Orca está acessando cada uma das instâncias conhecidas e criará sua conta em uma que
+ estiver disponível.
+
+ Concluir
+
diff --git a/feature/registration/ongoing/src/main/res/values/strings.xml b/feature/registration/ongoing/src/main/res/values/strings.xml
new file mode 100644
index 000000000..836a1e4fb
--- /dev/null
+++ b/feature/registration/ongoing/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+
+
+
+ Creating an account for you…
+
+ Orca is going through each of its known instances and will create your account at one that
+ is available.
+
+ Done
+
diff --git a/feature/registration/src/androidTest/java/com/jeanbarrossilva/orca/feature/registration/RegistrationFragmentTests.kt b/feature/registration/src/androidTest/java/com/jeanbarrossilva/orca/feature/registration/RegistrationFragmentTests.kt
new file mode 100644
index 000000000..5d42214f9
--- /dev/null
+++ b/feature/registration/src/androidTest/java/com/jeanbarrossilva/orca/feature/registration/RegistrationFragmentTests.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration
+
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.runComposeUiTest
+import androidx.fragment.app.testing.launchFragmentInContainer
+import assertk.assertThat
+import com.jeanbarrossilva.orca.platform.autos.test.kit.action.button.onPrimaryButton
+import com.jeanbarrossilva.orca.platform.navigation.navigator
+import com.jeanbarrossilva.orca.platform.navigation.test.activity.launchNavigationActivity
+import com.jeanbarrossilva.orca.platform.navigation.test.isAt
+import com.jeanbarrossilva.orca.std.injector.module.injection.injectionOf
+import com.jeanbarrossilva.orca.std.injector.test.InjectorTestRule
+import io.mockk.mockkObject
+import io.mockk.verify
+import kotlin.test.Test
+import org.junit.Rule
+
+internal class RegistrationFragmentTests {
+ @get:Rule
+ val injectorRule = InjectorTestRule {
+ register(RegistrationModule(injectionOf { NoOpRegistrationBoundary }))
+ }
+
+ @Test
+ fun navigates() {
+ launchNavigationActivity().use { scenario ->
+ scenario.onActivity { activity ->
+ RegistrationFragment.navigate(activity.navigator)
+ assertThat(activity).isAt(RegistrationFragment.ROUTE)
+ }
+ }
+ }
+
+ @Test
+ fun navigatesToCredentialsWhenContinuing() {
+ @OptIn(ExperimentalTestApi::class)
+ runComposeUiTest {
+ mockkObject(NoOpRegistrationBoundary) {
+ launchFragmentInContainer(instantiate = ::RegistrationFragment).use {
+ onPrimaryButton().performClick()
+ verify { NoOpRegistrationBoundary.navigateToCredentials() }
+ }
+ }
+ }
+ }
+}
diff --git a/feature/registration/src/main/AndroidManifest.xml b/feature/registration/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..e56813681
--- /dev/null
+++ b/feature/registration/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+
+
+
diff --git a/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/NoOpRegistrationBoundary.kt b/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/NoOpRegistrationBoundary.kt
new file mode 100644
index 000000000..439431cb8
--- /dev/null
+++ b/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/NoOpRegistrationBoundary.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration
+
+internal object NoOpRegistrationBoundary : RegistrationBoundary {
+ override fun navigateToCredentials() {}
+}
diff --git a/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/Registration.kt b/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/Registration.kt
new file mode 100644
index 000000000..4c43ea5a3
--- /dev/null
+++ b/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/Registration.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration
+
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.scaleIn
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.jeanbarrossilva.orca.composite.status.Status
+import com.jeanbarrossilva.orca.composite.status.StatusCard
+import com.jeanbarrossilva.orca.composite.status.state.rememberStatusCardState
+import com.jeanbarrossilva.orca.platform.animator.Animator
+import com.jeanbarrossilva.orca.platform.animator.animation.Motion
+import com.jeanbarrossilva.orca.platform.animator.animation.timing.after
+import com.jeanbarrossilva.orca.platform.autos.colors.asColor
+import com.jeanbarrossilva.orca.platform.autos.kit.action.button.PrimaryButton
+import com.jeanbarrossilva.orca.platform.autos.kit.scaffold.Scaffold
+import com.jeanbarrossilva.orca.platform.autos.kit.scaffold.bar.button.ButtonBar
+import com.jeanbarrossilva.orca.platform.autos.kit.scaffold.plus
+import com.jeanbarrossilva.orca.platform.autos.theme.AutosTheme
+import com.jeanbarrossilva.orca.platform.autos.theme.MultiThemePreview
+import com.jeanbarrossilva.orca.platform.stack.Stack
+import kotlin.time.Duration.Companion.seconds
+
+@Composable
+internal fun Registration(boundary: RegistrationBoundary, modifier: Modifier = Modifier) {
+ Registration(boundary, Motion.Moving, modifier)
+}
+
+@Composable
+private fun Registration(
+ boundary: RegistrationBoundary,
+ motion: Motion,
+ modifier: Modifier = Modifier
+) {
+ val spacing = AutosTheme.spacings.large.dp
+ val spacerModifier = remember(spacing) { Modifier.height(spacing) }
+ val backdropColor =
+ if (isSystemInDarkTheme()) {
+ AutosTheme.colors.surface.container.asColor
+ } else {
+ AutosTheme.colors.placeholder.asColor
+ }
+ val statusCardAnimationSpec = remember { tween() }
+ val statusCardEnterTransition =
+ remember(statusCardAnimationSpec) {
+ fadeIn(statusCardAnimationSpec) + scaleIn(statusCardAnimationSpec, initialScale = .8f)
+ }
+ val statusCardDelay = remember { 2.seconds }
+
+ Scaffold(
+ modifier,
+ bottom = {
+ ButtonBar { PrimaryButton(onClick = boundary::navigateToCredentials) { Text("Continue") } }
+ }
+ ) {
+ expanded {
+ LazyColumn(
+ Modifier.fillMaxHeight(),
+ verticalArrangement = Arrangement.SpaceBetween,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ contentPadding = it + PaddingValues(spacing)
+ ) {
+ item { Spacer(spacerModifier) }
+
+ item {
+ Box(contentAlignment = Alignment.Center) {
+ Canvas(Modifier.size(256.dp)) { drawCircle(backdropColor) }
+
+ Animator(motion) { (failedStatusCard, succeededStatusCard) ->
+ Stack {
+ item {
+ failedStatusCard.Animate {
+ StatusCard(rememberStatusCardState(Status.Failed, statusCardDelay)) {
+ Text("Instance 1")
+ }
+ }
+ }
+
+ item {
+ succeededStatusCard.Animate(
+ statusCardEnterTransition,
+ after(failedStatusCard) + statusCardDelay * 1.5
+ ) {
+ StatusCard(rememberStatusCardState(Status.Succeeded, statusCardDelay)) {
+ Text("Instance 2")
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ item { Spacer(spacerModifier) }
+
+ item {
+ Column(verticalArrangement = Arrangement.spacedBy(AutosTheme.spacings.large.dp)) {
+ Text(
+ stringResource(R.string.feature_registration),
+ style = AutosTheme.typography.headlineLarge
+ )
+
+ Text(
+ stringResource(R.string.feature_registration_explanation),
+ style = AutosTheme.typography.headlineSmall
+ )
+ }
+ }
+
+ item { Spacer(spacerModifier) }
+ }
+ }
+ }
+}
+
+@Composable
+@MultiThemePreview
+private fun StillRegistrationPreview() {
+ AutosTheme { Registration(NoOpRegistrationBoundary, Motion.Still) }
+}
+
+@Composable
+@MultiThemePreview
+private fun MovingRegistrationPreview() {
+ AutosTheme { Registration(NoOpRegistrationBoundary) }
+}
diff --git a/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/RegistrationBoundary.kt b/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/RegistrationBoundary.kt
new file mode 100644
index 000000000..fe0f609b9
--- /dev/null
+++ b/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/RegistrationBoundary.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration
+
+interface RegistrationBoundary {
+ fun navigateToCredentials()
+}
diff --git a/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/RegistrationFragment.kt b/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/RegistrationFragment.kt
new file mode 100644
index 000000000..77cc3ba9c
--- /dev/null
+++ b/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/RegistrationFragment.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration
+
+import androidx.compose.runtime.Composable
+import com.jeanbarrossilva.orca.composite.composable.ComposableFragment
+import com.jeanbarrossilva.orca.platform.navigation.Navigator
+import com.jeanbarrossilva.orca.platform.navigation.transition.opening
+import com.jeanbarrossilva.orca.std.injector.Injector
+
+class RegistrationFragment internal constructor() : ComposableFragment() {
+ private val module by lazy { Injector.from() }
+
+ @Composable
+ override fun Content() {
+ Registration(module.boundary())
+ }
+
+ companion object {
+ internal const val ROUTE = "registration"
+
+ fun navigate(navigator: Navigator) {
+ navigator.navigate(opening()) { to(ROUTE, ::RegistrationFragment) }
+ }
+ }
+}
diff --git a/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/RegistrationModule.kt b/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/RegistrationModule.kt
new file mode 100644
index 000000000..96f33de59
--- /dev/null
+++ b/feature/registration/src/main/java/com/jeanbarrossilva/orca/feature/registration/RegistrationModule.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2024 Orca
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If
+ * not, see https://www.gnu.org/licenses.
+ */
+
+package com.jeanbarrossilva.orca.feature.registration
+
+import com.jeanbarrossilva.orca.std.injector.module.Inject
+import com.jeanbarrossilva.orca.std.injector.module.Module
+import com.jeanbarrossilva.orca.std.injector.module.injection.Injection
+
+open class RegistrationModule(@Inject val boundary: Injection) : Module()
diff --git a/feature/registration/src/main/res/values-pt-rBR/strings.xml b/feature/registration/src/main/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000..cd032a218
--- /dev/null
+++ b/feature/registration/src/main/res/values-pt-rBR/strings.xml
@@ -0,0 +1,23 @@
+
+
+
+ Criar uma conta
+
+ O Orca tentará encontrar uma instância que está disponível e criar uma conta nela para você
+ com as credenciais que inserir. Tudo isso de uma forma completamente segura, sem armazenar
+ nenhuma das informações privadas fornecidas (como a sua senha desejada).
+
+
\ No newline at end of file
diff --git a/feature/registration/src/main/res/values/strings.xml b/feature/registration/src/main/res/values/strings.xml
new file mode 100644
index 000000000..8a49ff7ac
--- /dev/null
+++ b/feature/registration/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+
+
+
+ Create an account
+
+ Orca will try to find an instance that is available and create an account for you with the
+ credentials you pass in. All of it in a completely secure way, not storing any of the given
+ private information (such as your desired password).
+
+
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 03db4d51c..6a0949167 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -26,6 +26,7 @@ includeBuild("build-src")
include(
":app",
":composite:composable",
+ ":composite:status",
":composite:timeline",
":composite:timeline-test",
":core",
@@ -46,6 +47,9 @@ include(
":feature:gallery-test",
":feature:post-details",
":feature:profile-details",
+ ":feature:registration",
+ ":feature:registration:credentials",
+ ":feature:registration:ongoing",
":feature:search",
":feature:settings",
":feature:settings:term-muting",