Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Auth UI composables #256

Merged
merged 30 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6c0a85d
Initial commit
jan-tennert Jul 29, 2023
8aa1d4f
Update android namespace
jan-tennert Jul 29, 2023
3b03f2e
Remove unnecessary LaunchedEffect
jan-tennert Jul 29, 2023
fd019b5
Improve PasswordField further
jan-tennert Jul 30, 2023
923a9c7
Fix module
jan-tennert Aug 8, 2023
150ac53
Update README
jan-tennert Aug 8, 2023
245a5ba
Add Email Field & AuthState
jan-tennert Aug 8, 2023
4bddd49
Add comments
jan-tennert Aug 8, 2023
25107e9
Improve AuthState implementation
jan-tennert Aug 11, 2023
0c23c9b
Add ProviderButtonContent
jan-tennert Aug 15, 2023
ae67256
Fix Notion SVG
jan-tennert Aug 15, 2023
7b0c4e9
Extract ProviderIcon
jan-tennert Aug 16, 2023
89b907e
Extract content description
jan-tennert Aug 16, 2023
8195f1b
Add PhoneField & improve implementations
jan-tennert Aug 17, 2023
10ebe6d
PhoneField improvements & cleanup
jan-tennert Aug 21, 2023
ed9e2b3
Add missing icons & docs
jan-tennert Aug 21, 2023
8c92559
Update PhoneField
jan-tennert Aug 21, 2023
65d9404
Add FormComponent, mandatory option & more improvements
jan-tennert Aug 22, 2023
25a2bb3
Update README
jan-tennert Aug 22, 2023
fb954a3
Add missing docs
jan-tennert Aug 22, 2023
5faf864
Fix Android implementation
jan-tennert Aug 23, 2023
52560dd
Remove Compose annotation
jan-tennert Aug 23, 2023
51be97b
Add missing annotation
jan-tennert Aug 23, 2023
bfe2def
Merge branch 'development' into compose-ui
jan-tennert Aug 23, 2023
34b8398
Fix compose version error
jan-tennert Aug 23, 2023
d6116a5
Update compile sdk
jan-tennert Aug 23, 2023
cc04229
Fix MagicNumber error
jan-tennert Aug 23, 2023
3624b73
Fix more documentation errors
jan-tennert Aug 23, 2023
638f8e4
Fix more documentation errors
jan-tennert Aug 23, 2023
15d9e91
Fix DrawCache
jan-tennert Aug 23, 2023
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
2 changes: 2 additions & 0 deletions detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ naming:
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/mingwX64Test/**', '**/macosTest/**', '**/appleTest/**', '**/linuxTest/**']
functionPattern: '[a-z][a-zA-Z0-9]*'
excludeClassPattern: '$^'
ignoreAnnotated:
- Composable
FunctionParameterNaming:
active: true
parameterPattern: '[a-z][A-Za-z0-9]*'
Expand Down
6 changes: 5 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ detekt = "1.23.1"
moshi = "1.15.0"
jackson = "2.15.2"
browser = "1.6.0"
compose = "1.4.3"
compose = "1.5.0-rc04"
googleid = "1.0.1"
androidsvg = "1.4"

[plugins]
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
Expand Down Expand Up @@ -71,13 +72,16 @@ javalin = { module = "io.javalin:javalin", version.ref = "javalin" }
apollo-kotlin = { module = "com.apollographql.apollo3:apollo-runtime", version.ref = "apollo-kotlin" }

krypto = { module = "com.soywiz.korlibs.krypto:krypto", version.ref = "korlibs" }
korio = { module = "com.soywiz.korlibs.korio:korio", version.ref = "korlibs" }

moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" }

jackson = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jackson" }
jackson-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }

androidsvg = { module = "com.caverock:androidsvg-aar", version.ref = "androidsvg" }

[bundles]
ktor-client = ["ktor-client-core", "ktor-client-content-negotiation", "ktor-json"]
multiplatform-settings = ["multiplatform-settings-no-arg", "multiplatform-settings-coroutines"]
Expand Down
7 changes: 6 additions & 1 deletion plugins/ComposeAuth/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,9 @@ android {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
}

compose {
kotlinCompilerPlugin.set(dependencies.compiler.forKotlin("1.9.0"))
kotlinCompilerPluginArgs.add("suppressKotlinVersionCompatibilityCheck=1.9.10")
}
191 changes: 191 additions & 0 deletions plugins/ComposeAuthUI/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# Supabase-kt Compose Auth UI

Extends Supabase-kt with UI composables

Newest version: [![Maven Central](https://img.shields.io/maven-central/v/io.github.jan-tennert.supabase/supabase-kt)](https://search.maven.org/search?q=g%3Aio.github.jan-tennert.supabase)

# Installation

```kotlin
dependencies {
implementation("io.github.jan-tennert.supabase:compose-auth-ui:VERSION")
}
```

# Full Example

<details>

<summary>Full Compose for Desktop Example</summary>

```kotlin
singleWindowApplication {
MaterialTheme(
darkColorScheme()
) {
Box(Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background), contentAlignment = Alignment.Center) {
AuthForm {
var password by remember { mutableStateOf("") }
var email by remember { mutableStateOf("") }
var phone by remember { mutableStateOf("") }
val state = LocalAuthState.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
OutlinedEmailField(
value = email,
onValueChange = { email = it },
label = { Text("E-Mail") },
mandatory = email.isNotBlank() //once an email is entered, it is mandatory. (which enable validation)
)
OutlinedPhoneField(
value = phone,
onValueChange = { phone = it },
label = { Text("Phone Number") }
)
OutlinedPasswordField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
rules = rememberPasswordRuleList(PasswordRule.minLength(6), PasswordRule.containsSpecialCharacter(), PasswordRule.containsDigit(), PasswordRule.containsLowercase(), PasswordRule.containsUppercase())
)
FormComponent("accept_terms") { valid ->
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Checkbox(
checked = valid.value,
onCheckedChange = { valid.value = it },
)
Text("Accept Terms", color = MaterialTheme.colorScheme.onBackground)
}
}
Button(
onClick = {}, //Login with email and password,
enabled = state.validForm,
) {
Text("Login")
}
OutlinedButton(
onClick = {}, //Login with Google,
content = { ProviderButtonContent(Google) }
)
Button(
onClick = {}, //Login with Twitch,
content = { ProviderButtonContent(Twitch) }
)
}
}
}
}
}
```

https://github.com/supabase-community/supabase-kt/assets/26686035/91e5d533-3b01-4093-9585-a35e59b66927

</details>

# Usage

### PasswordField
You can set `rules` for the password like this:
```kotlin
OutlinedPasswordField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
rules = rememberPasswordRuleList(PasswordRule.minLength(6), PasswordRule.containsSpecialCharacter(), PasswordRule.containsDigit(), PasswordRule.containsLowercase(), PasswordRule.containsUppercase())
)
```
Once set, the field will automatically display an error supporting text if the password doesn't match the criteria.
Note that the built-in roles also accept a custom description as a parameter.
You can also easily create your own rules:
```kotlin
val myRule = PasswordRule("description") { password ->
password.contains("-")
}
```

### EmailField
The email field has a validator parameter which defaults to a regex solution.
Example:
```kotlin
OutlinedEmailField(
value = email,
onValueChange = { email = it },
label = { Text("E-Mail") },
validator = EmailValidator {
//validate email
}
)
```

### Phone Field
The phone field also has a validator parameter, which just checks whether the phone number only consists of digits.
On top of that you can provide a `mask` parameter, which changes how the phone number gets displayed. The value is just the raw number.
Example with the default mask:
```kotlin
OutlinedPhoneField(
value = phone,
onValueChange = { phone = it },
label = { Text("Phone") }
)
```

![image](https://github.com/supabase-community/supabase-kt/assets/26686035/5405772b-f6f8-45e7-a28d-a55003f48e75)

Example with custom mask: (you can also set it to null, if you don't want a mask)
```kotlin
OutlinedPhoneField(
value = phone,
onValueChange = { phone = it },
label = { Text("Phone") },
mask = "(###) ###-####"
)
```

![image](https://github.com/supabase-community/supabase-kt/assets/26686035/13251358-9147-4f49-8116-9776ec3266b8)

**Note: You can customize the fields completely, they just all have default values. They also have a `mandatory´ option, you can use that to have optional fields. It is also possible to make fields only mandatory (=validation enabled) once the field is not empty. (See example)**

### Provider Button
This module provides a function to generate the button content independently of the variation:
```kotlin
OutlinedButton(
onClick = {}, //Login with Google,
content = { ProviderButtonContent(Google) }
)
Button(
onClick = {}, //Login with Twitch,
content = { ProviderButtonContent(Twitch) }
)
```

![image](https://github.com/supabase-community/supabase-kt/assets/26686035/fb5263e7-272c-4a79-a3c4-1f6755922752)

You can also only use the icon if you want a custom layout:
```kotlin
Button(
onClick = {},
) {
ProviderIcon(Google)
Text("Login with Google")
}
```

### Custom Form Components
You can also easily make your own form components:
```kotlin
FormComponent(formKey = "accept_terms", mandatory = true) { valid ->
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Checkbox(
checked = valid.value,
onCheckedChange = { valid.value = it },
)
Text("Accept Terms", color = MaterialTheme.colorScheme.onBackground)
}
}
```
Mandatory changes whether the `AuthState#validForm` property is affected by this component.
89 changes: 89 additions & 0 deletions plugins/ComposeAuthUI/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.android.library)
alias(libs.plugins.compose)
}

description = "Extends supabase-kt with a Apollo GraphQL Client"

repositories {
mavenCentral()
}

@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
targetHierarchy.default()
jvmToolchain(8)
jvm {
compilations.all {
kotlinOptions.freeCompilerArgs = listOf(
"-Xjvm-default=all", // use default methods in interfaces,
"-Xlambdas=indy" // use invokedynamic lambdas instead of synthetic classes
)
}
}
androidTarget {
publishLibraryVariants("release", "debug")
}
js(IR) {
browser {
testTask {
enabled = false
}
}
nodejs {
testTask {
enabled = false
}
}
}
ios()
iosSimulatorArm64()
sourceSets {
all {
languageSettings.optIn("kotlin.RequiresOptIn")
languageSettings.optIn("io.github.jan.supabase.annotations.SupabaseInternal")
languageSettings.optIn("io.github.jan.supabase.annotations.SupabaseExperimental")
}
val commonMain by getting {
dependencies {
api(compose.ui)
implementation(project(":gotrue-kt"))
implementation(libs.korio)
implementation(compose.material3)
}
}
val nonJvmMain by creating {
dependsOn(commonMain)
}
val androidMain by getting {
dependencies {
implementation(libs.androidsvg)
}
}
val iosMain by getting {
dependsOn(nonJvmMain)
}
val jsMain by getting {
dependsOn(nonJvmMain)
}
}
}

android {
compileSdk = 34
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
namespace = "io.github.jan.supabase.compose.auth.ui.library"
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}

compose {
kotlinCompilerPlugin.set(dependencies.compiler.forKotlin("1.9.0"))
kotlinCompilerPluginArgs.add("suppressKotlinVersionCompatibilityCheck=1.9.10")
}
2 changes: 2 additions & 0 deletions plugins/ComposeAuthUI/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest/>
Loading
Loading