Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 1.0.0-BETA27

* Improved watch query internals. Added the ability to throttle watched queries.
* Fixed `uploading` and `downloading` sync status indicators.

## 1.0.0-BETA26

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ internal class PowerSyncDatabaseImpl(
currentStatus.update(
connected = it.connected,
connecting = it.connecting,
uploading = it.uploading,
downloading = it.downloading,
lastSyncedAt = it.lastSyncedAt,
hasSynced = it.hasSynced,
Expand Down
23 changes: 20 additions & 3 deletions core/src/commonMain/kotlin/com/powersync/sync/SyncStream.kt
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ internal class SyncStream(
var checkedCrudItem: CrudEntry? = null

while (true) {
status.update(uploading = true)
/**
* This is the first item in the FIFO CRUD queue.
*/
Expand All @@ -146,6 +145,7 @@ internal class SyncStream(
}

checkedCrudItem = nextCrudItem
status.update(uploading = true)
uploadCrud()
} else {
// Uploading is completed
Expand Down Expand Up @@ -256,6 +256,8 @@ internal class SyncStream(
state = handleInstruction(line, value, state)
}

status.update(downloading = false)

return state
}

Expand All @@ -268,7 +270,12 @@ internal class SyncStream(
is SyncLine.FullCheckpoint -> handleStreamingSyncCheckpoint(line, state)
is SyncLine.CheckpointDiff -> handleStreamingSyncCheckpointDiff(line, state)
is SyncLine.CheckpointComplete -> handleStreamingSyncCheckpointComplete(state)
is SyncLine.CheckpointPartiallyComplete -> handleStreamingSyncCheckpointPartiallyComplete(line, state)
is SyncLine.CheckpointPartiallyComplete ->
handleStreamingSyncCheckpointPartiallyComplete(
line,
state,
)

is SyncLine.KeepAlive -> handleStreamingKeepAlive(line, state)
is SyncLine.SyncDataBucket -> handleStreamingSyncData(line, state)
SyncLine.UnknownSyncLine -> {
Expand All @@ -283,6 +290,8 @@ internal class SyncStream(
): SyncStreamState {
val (checkpoint) = line
state.targetCheckpoint = checkpoint
status.update(downloading = true)

val bucketsToDelete = state.bucketSet!!.toMutableList()
val newBuckets = mutableSetOf<String>()

Expand Down Expand Up @@ -323,7 +332,12 @@ internal class SyncStream(
}

state.validatedCheckpoint = state.targetCheckpoint
status.update(lastSyncedAt = Clock.System.now(), hasSynced = true, clearDownloadError = true)
status.update(
lastSyncedAt = Clock.System.now(),
downloading = false,
hasSynced = true,
clearDownloadError = true,
)

return state
}
Expand Down Expand Up @@ -374,6 +388,8 @@ internal class SyncStream(
throw Exception("Checkpoint diff without previous checkpoint")
}

status.update(downloading = true)

val newBuckets = mutableMapOf<String, BucketChecksum>()

state.targetCheckpoint!!.checksums.forEach { checksum ->
Expand Down Expand Up @@ -410,6 +426,7 @@ internal class SyncStream(
data: SyncLine.SyncDataBucket,
state: SyncStreamState,
): SyncStreamState {
status.update(downloading = true)
bucketStorage.saveSyncData(SyncDataBatch(listOf(data)))
return state
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,28 @@ import com.powersync.demos.screens.HomeScreen
import com.powersync.demos.screens.SignInScreen
import com.powersync.demos.screens.SignUpScreen
import com.powersync.demos.screens.TodosScreen
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.runBlocking


@Composable
fun App(factory: DatabaseDriverFactory, modifier: Modifier = Modifier) {
val supabase = remember {
SupabaseConnector(
powerSyncEndpoint = Config.POWERSYNC_URL,
supabaseUrl = Config.SUPABASE_URL,
supabaseKey = Config.SUPABASE_ANON_KEY
)
}
fun App(
factory: DatabaseDriverFactory,
modifier: Modifier = Modifier,
) {
val supabase =
remember {
SupabaseConnector(
powerSyncEndpoint = Config.POWERSYNC_URL,
supabaseUrl = Config.SUPABASE_URL,
supabaseKey = Config.SUPABASE_ANON_KEY,
)
}
val db = remember { PowerSyncDatabase(factory, schema) }
val status by db.currentStatus.asFlow().collectAsState(initial = db.currentStatus)
// Debouncing the status flow prevents flicker
val status by db.currentStatus
.asFlow()
.debounce(200)
.collectAsState(initial = db.currentStatus)

// This assumes that the buckets for lists has a priority of 1 (but it will work fine with sync
// rules not defining any priorities at all too). When giving lists a higher priority than
Expand All @@ -48,9 +56,10 @@ fun App(factory: DatabaseDriverFactory, modifier: Modifier = Modifier) {
}

val navController = remember { NavController(Screen.Home) }
val authViewModel = remember {
AuthViewModel(supabase, db, navController)
}
val authViewModel =
remember {
AuthViewModel(supabase, db, navController)
}

val authState by authViewModel.authState.collectAsState()
val currentScreen by navController.currentScreen.collectAsState()
Expand Down Expand Up @@ -81,7 +90,7 @@ fun App(factory: DatabaseDriverFactory, modifier: Modifier = Modifier) {

when (currentScreen) {
is Screen.Home -> {
if(authState == AuthState.SignedOut) {
if (authState == AuthState.SignedOut) {
navController.navigate(Screen.SignIn)
}

Expand All @@ -93,14 +102,13 @@ fun App(factory: DatabaseDriverFactory, modifier: Modifier = Modifier) {
HomeScreen(
modifier = modifier.background(MaterialTheme.colors.background),
items = items,
isConnected = status.connected,
onSignOutSelected = { handleSignOut() },
inputText = listsInputText,
onItemClicked = handleOnItemClicked,
onItemDeleteClicked = lists.value::onItemDeleteClicked,
onAddItemClicked = lists.value::onAddItemClicked,
onInputTextChanged = lists.value::onInputTextChanged,
hasSynced = hasSyncedLists
syncStatus = status,
)
}

Expand All @@ -113,7 +121,7 @@ fun App(factory: DatabaseDriverFactory, modifier: Modifier = Modifier) {
modifier = modifier.background(MaterialTheme.colors.background),
navController = navController,
items = todoItems,
isConnected = status.connected,
syncStatus = status,
inputText = todosInputText,
onItemClicked = todos.value::onItemClicked,
onItemDoneChanged = todos.value::onItemDoneChanged,
Expand All @@ -133,24 +141,24 @@ fun App(factory: DatabaseDriverFactory, modifier: Modifier = Modifier) {
}

is Screen.SignIn -> {
if(authState == AuthState.SignedIn) {
if (authState == AuthState.SignedIn) {
navController.navigate(Screen.Home)
}

SignInScreen(
navController,
authViewModel
authViewModel,
)
}

is Screen.SignUp -> {
if(authState == AuthState.SignedIn) {
if (authState == AuthState.SignedIn) {
navController.navigate(Screen.Home)
}

SignUpScreen(
navController,
authViewModel
authViewModel,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,29 @@ package com.powersync.demos.components

import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Wifi
import androidx.compose.material.icons.filled.WifiOff
import androidx.compose.material.icons.filled.Cloud
import androidx.compose.material.icons.filled.CloudOff
import androidx.compose.material.icons.filled.CloudSync
import androidx.compose.material.icons.filled.LeakAdd
import androidx.compose.material.icons.filled.Thunderstorm
import androidx.compose.runtime.Composable
import com.powersync.sync.SyncStatusData

@Composable
fun WifiIcon(isConnected: Boolean) {
val icon = if (isConnected) {
Icons.Filled.Wifi
} else {
Icons.Filled.WifiOff
}
fun WifiIcon(status: SyncStatusData) {
val icon =
when {
status.downloading || status.uploading -> Icons.Filled.CloudSync
status.connected -> Icons.Filled.Cloud
!status.connected -> Icons.Filled.CloudOff
status.connecting -> Icons.Filled.LeakAdd
else -> {
Icons.Filled.Thunderstorm
}
}

Icon(
imageVector = icon,
contentDescription = if (isConnected) "Online" else "Offline",
contentDescription = status.toString(),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ import com.powersync.demos.components.ListContent
import com.powersync.demos.components.Menu
import com.powersync.demos.components.WifiIcon
import com.powersync.demos.powersync.ListItem
import com.powersync.sync.SyncStatusData

@Composable
internal fun HomeScreen(
modifier: Modifier = Modifier,
items: List<ListItem>,
inputText: String,
isConnected: Boolean,
hasSynced: Boolean?,
syncStatus: SyncStatusData,
onSignOutSelected: () -> Unit,
onItemClicked: (item: ListItem) -> Unit,
onItemDeleteClicked: (item: ListItem) -> Unit,
Expand All @@ -40,46 +40,49 @@ internal fun HomeScreen(
TopAppBar(
title = {
Text(
"Todo Lists",
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth().padding(end = 36.dp)
) },
navigationIcon = { Menu(
true,
onSignOutSelected
) },
"Todo Lists",
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth().padding(end = 36.dp),
)
},
navigationIcon = {
Menu(
true,
onSignOutSelected,
)
},
actions = {
WifiIcon(isConnected)
WifiIcon(syncStatus)
Spacer(modifier = Modifier.width(16.dp))
}
},
)

when {
hasSynced == null || hasSynced == false -> {
Box(
modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.background),
contentAlignment = Alignment.Center
) {
Text(
text = "Busy with initial sync...",
style = MaterialTheme.typography.h6
)
}
syncStatus.hasSynced == null || syncStatus.hasSynced == false -> {
Box(
modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.background),
contentAlignment = Alignment.Center,
) {
Text(
text = "Busy with initial sync...",
style = MaterialTheme.typography.h6,
)
}
else -> {
}

else -> {
Input(
text = inputText,
onAddClicked = onAddItemClicked,
onTextChanged = onInputTextChanged,
screen = Screen.Home
screen = Screen.Home,
)

Box(Modifier.weight(1F)) {
ListContent(
items = items,
onItemClicked = onItemClicked,
onItemDeleteClicked = onItemDeleteClicked
onItemDeleteClicked = onItemDeleteClicked,
)
}
}
Expand Down
Loading
Loading