diff --git a/CHANGELOG.md b/CHANGELOG.md index 6545f5f6..0b47d09a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.5.1 + +* Fix issue in legacy sync client where local writes made offline could have their upload delayed + until a keepalive event was received. This could also cause downloaded updates to be delayed even + further until all uploads were + completed. +* [Internal] Update core extension to 0.4.5 + ## 1.5.0 * Add `PowerSyncDatabase.getCrudTransactions()`, returning a flow of transactions. This is useful diff --git a/core/src/commonIntegrationTest/kotlin/com/powersync/sync/SyncIntegrationTest.kt b/core/src/commonIntegrationTest/kotlin/com/powersync/sync/SyncIntegrationTest.kt index 25bc868c..4d4db964 100644 --- a/core/src/commonIntegrationTest/kotlin/com/powersync/sync/SyncIntegrationTest.kt +++ b/core/src/commonIntegrationTest/kotlin/com/powersync/sync/SyncIntegrationTest.kt @@ -612,7 +612,6 @@ abstract class BaseSyncIntegrationTest( database.watch("SELECT name FROM users") { it.getString(0)!! }.testIn(scope) query.awaitItem() shouldBe listOf("local write") - syncLines.send(SyncLine.KeepAlive(tokenExpiresIn = 1234)) syncLines.send( SyncLine.FullCheckpoint( Checkpoint( diff --git a/core/src/commonMain/kotlin/com/powersync/sync/SyncStream.kt b/core/src/commonMain/kotlin/com/powersync/sync/SyncStream.kt index 3bacd18a..56b2c4b4 100644 --- a/core/src/commonMain/kotlin/com/powersync/sync/SyncStream.kt +++ b/core/src/commonMain/kotlin/com/powersync/sync/SyncStream.kt @@ -536,9 +536,20 @@ internal class SyncStream( lateinit var receiveLines: Job receiveLines = scope.launch { + var hadLine = false receiveTextLines(JsonUtil.json.encodeToJsonElement(req)).collect { value -> val line = JsonUtil.json.decodeFromString(value) + if (!hadLine) { + // Trigger a crud upload when receiving the first sync line: We could have + // pending local writes made while disconnected, so in addition to listening on + // updates to `ps_crud`, we also need to trigger a CRUD upload in some other + // cases. We do this on the first sync line because the client is likely to be + // online in that case. + hadLine = true + triggerCrudUploadAsync() + } + state = handleInstruction(line, value, state) if (state.abortIteration) { diff --git a/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/MainActivity.kt b/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/MainActivity.kt index 7f2cef02..eed4960f 100644 --- a/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/MainActivity.kt +++ b/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/MainActivity.kt @@ -3,6 +3,7 @@ package com.powersync.androidexample import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import com.powersync.androidexample.ui.CameraService import com.powersync.demos.App @@ -15,6 +16,9 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) + // Needed to render system bar properly + enableEdgeToEdge() + setContent { App( cameraService = cameraService, diff --git a/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/screens/SignInScreen.kt b/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/screens/SignInScreen.kt index 4183e38d..e70e102c 100644 --- a/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/screens/SignInScreen.kt +++ b/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/screens/SignInScreen.kt @@ -1,10 +1,14 @@ package com.powersync.demos.screens import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import com.powersync.demos.AuthViewModel @@ -24,10 +28,12 @@ internal fun SignInScreen( var isLoading by remember { mutableStateOf(false) } val coroutineScope = rememberCoroutineScope() + val scrollState = rememberScrollState() Column( modifier = Modifier .fillMaxSize() + .verticalScroll(scrollState) .padding(16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally @@ -52,6 +58,7 @@ internal fun SignInScreen( onValueChange = { password = it }, label = { Text("Password") }, visualTransformation = PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), modifier = Modifier .fillMaxWidth() .padding(bottom = 16.dp) diff --git a/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/screens/SignUpScreen.kt b/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/screens/SignUpScreen.kt index f51038be..7a03f81f 100644 --- a/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/screens/SignUpScreen.kt +++ b/demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/screens/SignUpScreen.kt @@ -1,10 +1,14 @@ package com.powersync.demos.screens import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import com.powersync.demos.AuthViewModel @@ -24,10 +28,12 @@ internal fun SignUpScreen( var isLoading by remember { mutableStateOf(false) } val coroutineScope = rememberCoroutineScope() + val scrollState = rememberScrollState() Column( modifier = Modifier .fillMaxSize() + .verticalScroll(scrollState) .padding(16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally @@ -48,6 +54,7 @@ internal fun SignUpScreen( onValueChange = { password = it }, label = { Text("Password") }, visualTransformation = PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), modifier = Modifier .fillMaxWidth() .padding(bottom = 16.dp) @@ -58,6 +65,7 @@ internal fun SignUpScreen( onValueChange = { confirmPassword = it }, label = { Text("Confirm Password") }, visualTransformation = PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), modifier = Modifier .fillMaxWidth() .padding(bottom = 16.dp) diff --git a/demos/supabase-todolist/README.md b/demos/supabase-todolist/README.md index 4adbf298..3cf02b90 100644 --- a/demos/supabase-todolist/README.md +++ b/demos/supabase-todolist/README.md @@ -41,7 +41,7 @@ all items have been received. 1. Clone this repo: ```git clone https://github.com/powersync-ja/powersync-kotlin.git``` 2. Open the repo in Android Studio. This creates a `local.properties` file in root and should contain a `sdk.dir=/path/to/android/sdk` line. 3. Sync the project with Gradle (this should happen automatically, or choose File > Sync project with Gradle Files). -4. Insert your Supabase project URL, Supabase Anon Key, and PowerSync instance URL into the `demos/supabase-todlist/local.properties` file: +4. Insert your Supabase project URL, Supabase Anon Key, and PowerSync instance URL into the `demos/supabase-todolist/local.properties` file: ```bash # local.properties diff --git a/demos/supabase-todolist/androidApp/src/androidMain/kotlin/com/powersync/demos/MainActivity.kt b/demos/supabase-todolist/androidApp/src/androidMain/kotlin/com/powersync/demos/MainActivity.kt index acef420d..548e9448 100644 --- a/demos/supabase-todolist/androidApp/src/androidMain/kotlin/com/powersync/demos/MainActivity.kt +++ b/demos/supabase-todolist/androidApp/src/androidMain/kotlin/com/powersync/demos/MainActivity.kt @@ -2,6 +2,7 @@ package com.powersync.demos import android.os.Bundle import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface @@ -11,6 +12,9 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + // Needed to render system bar properly + enableEdgeToEdge() + setContent { MaterialTheme { Surface(color = MaterialTheme.colors.background) { diff --git a/demos/supabase-todolist/shared/src/commonMain/kotlin/com/powersync/demos/screens/SignInScreen.kt b/demos/supabase-todolist/shared/src/commonMain/kotlin/com/powersync/demos/screens/SignInScreen.kt index 4cae23f5..d69753bb 100644 --- a/demos/supabase-todolist/shared/src/commonMain/kotlin/com/powersync/demos/screens/SignInScreen.kt +++ b/demos/supabase-todolist/shared/src/commonMain/kotlin/com/powersync/demos/screens/SignInScreen.kt @@ -1,10 +1,14 @@ package com.powersync.demos.screens import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import com.powersync.demos.AuthViewModel @@ -24,13 +28,16 @@ internal fun SignInScreen( var isLoading by remember { mutableStateOf(false) } val coroutineScope = rememberCoroutineScope() + val scrollState = rememberScrollState() Column( modifier = Modifier .fillMaxSize() + .verticalScroll(scrollState) .padding(16.dp), verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, + ) { Text( "Sign In", @@ -52,6 +59,7 @@ internal fun SignInScreen( onValueChange = { password = it }, label = { Text("Password") }, visualTransformation = PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), modifier = Modifier .fillMaxWidth() .padding(bottom = 16.dp) diff --git a/demos/supabase-todolist/shared/src/commonMain/kotlin/com/powersync/demos/screens/SignUpScreen.kt b/demos/supabase-todolist/shared/src/commonMain/kotlin/com/powersync/demos/screens/SignUpScreen.kt index cc364f73..0301249e 100644 --- a/demos/supabase-todolist/shared/src/commonMain/kotlin/com/powersync/demos/screens/SignUpScreen.kt +++ b/demos/supabase-todolist/shared/src/commonMain/kotlin/com/powersync/demos/screens/SignUpScreen.kt @@ -1,10 +1,14 @@ package com.powersync.demos.screens import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import com.powersync.demos.AuthViewModel @@ -24,10 +28,12 @@ internal fun SignUpScreen( var isLoading by remember { mutableStateOf(false) } val coroutineScope = rememberCoroutineScope() + val scrollState = rememberScrollState() Column( modifier = Modifier .fillMaxSize() + .verticalScroll(scrollState) .padding(16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally @@ -48,6 +54,7 @@ internal fun SignUpScreen( onValueChange = { password = it }, label = { Text("Password") }, visualTransformation = PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), modifier = Modifier .fillMaxWidth() .padding(bottom = 16.dp) @@ -58,6 +65,7 @@ internal fun SignUpScreen( onValueChange = { confirmPassword = it }, label = { Text("Confirm Password") }, visualTransformation = PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), modifier = Modifier .fillMaxWidth() .padding(bottom = 16.dp) diff --git a/gradle.properties b/gradle.properties index 9162055d..539bba29 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,7 @@ development=true RELEASE_SIGNING_ENABLED=true # Library config GROUP=com.powersync -LIBRARY_VERSION=1.5.0 +LIBRARY_VERSION=1.5.1 GITHUB_REPO=https://github.com/powersync-ja/powersync-kotlin.git # POM POM_URL=https://github.com/powersync-ja/powersync-kotlin/ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c490ee18..c86a9edf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ kotlinx-datetime = "0.7.1" kotlinx-io = "0.8.0" ktor = "3.2.3" uuid = "0.8.4" -powersync-core = "0.4.4" +powersync-core = "0.4.5" sqlite-jdbc = "3.50.3.0" sqliter = "1.3.3" turbine = "1.2.1"