Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,7 @@ fun AuthScreen(applicationId: String) {
onForgotPassword = { showSheet = "recover" },
onSignUp = { showSheet = "signup" },
)
"signup" -> SignUpSheetContent(
onSignIn = { showSheet = "login" },
)
"signup" -> SignUpSheetContent()
"recover" -> RecoverSheetContent(
onBackToSignIn = { showSheet = "login" },
)
Expand Down Expand Up @@ -251,7 +249,7 @@ private fun LoginSheetContent(
}

@Composable
private fun SignUpSheetContent(onSignIn: () -> Unit) {
private fun SignUpSheetContent() {
Column(
modifier = Modifier
.fillMaxWidth()
Expand All @@ -262,13 +260,6 @@ private fun SignUpSheetContent(onSignIn: () -> Unit) {
SheetTitle("Create account")
Spacer(Modifier.height(8.dp))
SignUp(modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp))
Spacer(Modifier.height(8.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Already have an account?", fontSize = 14.sp, color = TextMuted)
TextButton(onClick = onSignIn) {
Text("Sign in", color = PrimaryBlue, fontSize = 14.sp, fontWeight = FontWeight.SemiBold)
}
}
}
}

Expand Down
23 changes: 22 additions & 1 deletion src/main/kotlin/dev/thunderid/android/Models.kt
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ enum class FlowStatus { PROMPT_ONLY, INCOMPLETE, COMPLETE, ERROR }
data class FlowStepData(
val actions: List<FlowAction>? = null,
val inputs: List<FlowInput>? = null,
val meta: Map<String, Any?>? = null,
val meta: FlowMeta? = null,
)

data class FlowAction(
Expand All @@ -112,10 +112,31 @@ data class FlowAction(
val nextNode: String? = null,
val type: String? = null,
val label: String? = null,
val eventType: String? = null,
val variant: String? = null,
@SerializedName("image") val icon: String? = null,
)

data class FlowInput(
@SerializedName("identifier") val name: String,
val type: String? = null,
val required: Boolean? = null,
)

data class FlowMeta(
val components: List<FlowComponent>? = null,
)

data class FlowComponent(
val id: String? = null,
val ref: String? = null,
val type: String? = null,
val category: String? = null,
val label: String? = null,
val placeholder: String? = null,
val variant: String? = null,
val eventType: String? = null,
val align: String? = null,
@SerializedName("image") val icon: String? = null,
val components: List<FlowComponent>? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package dev.thunderid.compose.components.actions.adapters

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.dp

/**
* "Continue with GitHub" federated sign-in trigger, styled to match the outlined action
* buttons rendered below a SignIn form's "Or" divider.
*/
@Composable
fun GitHubButton(
label: String,
isLoading: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
TriggerButtonStyle(
label = label,
isLoading = isLoading,
onClick = onClick,
modifier = modifier,
icon = { GitHubGlyph() },
)
}

/**
* Simplified GitHub "octocat" silhouette, drawn with plain [Canvas] circles rather than ported
* SVG path data — a rounded head with two ears, visually recognizable without pixel-perfect
* brand fidelity.
*/
@Composable
private fun GitHubGlyph() {
val glyphColor = LocalContentColor.current
Canvas(modifier = Modifier.size(18.dp)) {
val headRadius = size.minDimension * 0.42f
val center = Offset(size.width / 2f, size.height / 2f)
val earRadius = size.minDimension * 0.14f

// Ears.
drawCircle(
color = glyphColor,
radius = earRadius,
center = Offset(center.x - headRadius * 0.62f, center.y - headRadius * 0.62f),
)
drawCircle(
color = glyphColor,
radius = earRadius,
center = Offset(center.x + headRadius * 0.62f, center.y - headRadius * 0.62f),
)
// Head/body.
drawCircle(color = glyphColor, radius = headRadius, center = center)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package dev.thunderid.compose.components.actions.adapters

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.unit.dp

/**
* "Continue with Google" federated sign-in trigger, styled to match the outlined action
* buttons rendered below a SignIn form's "Or" divider.
*/
@Composable
fun GoogleButton(
label: String,
isLoading: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
TriggerButtonStyle(
label = label,
isLoading = isLoading,
onClick = onClick,
modifier = modifier,
icon = { GoogleGlyph() },
)
}

/**
* Simplified four-color Google "G" mark, drawn with plain [Canvas] arcs rather than ported SVG
* path data — visually recognizable without pixel-perfect brand fidelity.
*/
@Composable
private fun GoogleGlyph() {
Canvas(modifier = Modifier.size(18.dp)) {
val strokeWidth = size.minDimension * 0.22f
val radius = (size.minDimension - strokeWidth) / 2f
val center = Offset(size.width / 2f, size.height / 2f)
val topLeft = Offset(center.x - radius, center.y - radius)
val arcSize = Size(radius * 2f, radius * 2f)
val stroke = Stroke(width = strokeWidth)

// Red: top arc.
drawArc(
color = Color(0xFFEA4335),
startAngle = 270f,
sweepAngle = 90f,
useCenter = false,
topLeft = topLeft,
size = arcSize,
style = stroke,
)
// Green: bottom-right arc.
drawArc(
color = Color(0xFF34A853),
startAngle = 0f,
sweepAngle = 90f,
useCenter = false,
topLeft = topLeft,
size = arcSize,
style = stroke,
)
// Yellow: bottom-left arc.
drawArc(
color = Color(0xFFFBBC05),
startAngle = 90f,
sweepAngle = 90f,
useCenter = false,
topLeft = topLeft,
size = arcSize,
style = stroke,
)
// Blue: top-left arc, plus the crossbar of the "G".
drawArc(
color = Color(0xFF4285F4),
startAngle = 180f,
sweepAngle = 90f,
useCenter = false,
topLeft = topLeft,
size = arcSize,
style = stroke,
)
drawLine(
color = Color(0xFF4285F4),
start = center,
end = Offset(center.x + radius, center.y),
strokeWidth = strokeWidth,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package dev.thunderid.compose.components.actions.adapters

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

/**
* Generic outlined trigger button for `eventType: TRIGGER` actions with no dedicated brand
* adapter, using the label supplied by the flow schema and no icon.
*/
@Composable
fun OutlinedTriggerButton(
label: String,
isLoading: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
TriggerButtonStyle(
label = label,
isLoading = isLoading,
onClick = onClick,
modifier = modifier,
icon = null,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package dev.thunderid.compose.components.actions.adapters

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
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

/**
* Shared outlined-button chrome for `eventType: TRIGGER` (federated login) actions: an icon
* slot, a label, and a rounded-rect stroke — matching the "Continue with X" buttons rendered
* below a SignIn form's "Or" divider.
*/
@Composable
fun TriggerButtonStyle(
label: String,
isLoading: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
icon: (@Composable () -> Unit)? = null,
) {
OutlinedButton(
onClick = onClick,
enabled = !isLoading,
modifier = modifier.fillMaxWidth().height(48.dp),
shape = RoundedCornerShape(8.dp),
border = BorderStroke(1.dp, LocalContentColor.current.copy(alpha = 0.4f)),
colors =
ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colorScheme.onSurface,
),
) {
if (isLoading) {
CircularProgressIndicator(modifier = Modifier.height(20.dp), strokeWidth = 2.dp)
} else {
Row(
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
icon?.invoke()
Text(text = label, style = MaterialTheme.typography.bodyLarge)
}
}
}
}
Loading
Loading