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
21 changes: 20 additions & 1 deletion android/src/main/java/io/rownd/android/Rownd.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import io.rownd.android.util.InvalidRefreshTokenException
import io.rownd.android.util.NoAccessTokenPresentException
import io.rownd.android.util.NoRefreshTokenPresentException
import io.rownd.android.util.RowndEvent
import io.rownd.android.util.RowndEventType
import io.rownd.android.util.RowndException
import io.rownd.android.views.HubPageSelector
import io.rownd.android.views.RowndBottomSheetActivity
Expand Down Expand Up @@ -76,6 +77,7 @@ class RowndClient(
internal var eventEmitter = graph.rowndEventEmitter()
internal var signInWithGoogle = graph.signInWithGoogle()
internal var telemetry = graph.telemetry()
internal var superTokensSync = graph.superTokensSync()

var state = stateRepo.state
var user = userRepo
Expand All @@ -89,6 +91,23 @@ class RowndClient(
rowndContext.eventEmitter = eventEmitter
rowndContext.telemetry = telemetry

eventEmitter.addListener { event ->
if (event.event != RowndEventType.SignInCompleted || event.data["user_type"] != "new_user") {
return@addListener
}

if (!this::store.isInitialized) {
return@addListener
}

val accessToken = store.currentState.auth.accessToken ?: return@addListener
val appInfo = config.supertokens?.appInfo ?: return@addListener

CoroutineScope(Dispatchers.IO).launch {
superTokensSync.syncUser(accessToken = accessToken, appInfo = appInfo)
}
}

stateRepo.userRepo = userRepo
stateRepo.authRepo = authRepo
}
Expand Down Expand Up @@ -563,4 +582,4 @@ enum class RowndSignInLoginStep {

enum class RowndSignOutScope {
all
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import io.rownd.android.util.RowndContext
import io.rownd.android.util.RowndEvent
import io.rownd.android.util.RowndEventEmitter
import io.rownd.android.util.SignInWithGoogle
import io.rownd.android.util.SuperTokensSync
import io.rownd.android.util.Telemetry
import io.rownd.android.util.TokenApiClient
import javax.inject.Singleton
Expand All @@ -44,9 +45,10 @@ interface RowndGraph {
fun rowndEventEmitter(): RowndEventEmitter<RowndEvent>
fun signInWithGoogle(): SignInWithGoogle
fun telemetry(): Telemetry
fun superTokensSync(): SuperTokensSync
fun tokenApiClient(): TokenApiClient
fun authenticatedApiClient(): AuthenticatedApiClient
fun httpEngine(): HttpClientEngine
fun config(): RowndConfig
fun inject(rowndConfig: RowndConfig)
}
}
28 changes: 28 additions & 0 deletions android/src/main/java/io/rownd/android/models/RowndConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,31 @@ import javax.inject.Inject

val json = Json { encodeDefaults = true }

@Serializable
data class SuperTokensAppInfo(
val appName: String,
val apiDomain: String,
val apiBasePath: String = "/auth"
) {
val normalizedApiDomain: String
get() = apiDomain.trimEnd('/')

val normalizedApiBasePath: String
get() {
val basePath = apiBasePath.trim().trim('/')
return if (basePath.isEmpty()) "" else "/$basePath"
}

fun migrationUrl(): String {
return "${normalizedApiDomain}${normalizedApiBasePath}/plugin/rownd/migrate"
}
}

@Serializable
data class SuperTokensConfig(
val appInfo: SuperTokensAppInfo
)

@Serializable
data class RowndConfig(
var appKey: String? = null,
Expand All @@ -34,6 +59,9 @@ data class RowndConfig(
@Transient
var enableSmartLinkPasteBehavior: Boolean = true,

@Transient
var supertokens: SuperTokensConfig? = null,

// Internals
@Transient
internal var stateFileName: String = "rownd_state.json"
Expand Down
25 changes: 25 additions & 0 deletions android/src/main/java/io/rownd/android/util/SuperTokensSync.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.rownd.android.util

import android.util.Log
import io.ktor.client.request.header
import io.ktor.client.request.post
import io.rownd.android.models.SuperTokensAppInfo
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class SuperTokensSync @Inject constructor(
private val apiClient: KtorApiClient,
) {
suspend fun syncUser(accessToken: String, appInfo: SuperTokensAppInfo) {
val migrationUrl = appInfo.migrationUrl()

try {
apiClient.client.post(migrationUrl) {
header("Authorization", "Bearer $accessToken")
}
} catch (e: Exception) {
Log.e("Rownd.ST", "[Rownd->ST] migrate failed (non-fatal): ${e.message}")
}
}
}
Loading