diff --git a/src/main/kotlin/org/briarproject/briar/desktop/Main.kt b/src/main/kotlin/org/briarproject/briar/desktop/Main.kt index c922146d59965f19569f421f3847039e27688cc4..5b1f9a998c6f19ef555c3e95cc0f111f62b16cd2 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/Main.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/Main.kt @@ -1,6 +1,7 @@ package org.briarproject.briar.desktop import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.window.application import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.parameters.options.counted import com.github.ajalt.clikt.parameters.options.default @@ -63,7 +64,9 @@ private class Main : CliktCommand( BrambleCoreEagerSingletons.Helper.injectEagerSingletons(app) BriarCoreEagerSingletons.Helper.injectEagerSingletons(app) - app.getBriarUi().start() + application { + app.getBriarUi().start(this) + } } private fun getDataDir(): Path { diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt index 3d46cd5d7ce2685ec0e1dd18e9f64131bcedff7d..b2bbafbf3c5a4967bced65d401983f34db2bf44c 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt @@ -1,11 +1,12 @@ package org.briarproject.briar.desktop.ui +import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.window.ApplicationScope import androidx.compose.ui.window.Window -import androidx.compose.ui.window.application import org.briarproject.bramble.api.account.AccountManager import org.briarproject.bramble.api.lifecycle.LifecycleManager import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING @@ -34,7 +35,8 @@ enum class Screen { interface BriarUi { - fun start() + @Composable + fun start(applicationScope: ApplicationScope) fun stop() } @@ -67,51 +69,51 @@ constructor( } } - override fun start() { - application { - val (isDark, setDark) = remember { mutableStateOf(true) } - val title = i18n("main.title") - var screenState by remember { - mutableStateOf( - if (accountManager.hasDatabaseKey()) { - // this should only happen during testing when we launch the main UI directly - // without a need to enter the password. - contactListViewModel.loadContacts() - Screen.MAIN - } else if (accountManager.accountExists()) { - Screen.LOGIN - } else { - Screen.REGISTRATION - } - ) - } - Window( - title = title, - onCloseRequest = { stop(); exitApplication() }, - ) { - window.minimumSize = Dimension(800, 600) - BriarTheme(isDarkTheme = isDark) { - when (screenState) { - Screen.REGISTRATION -> - Registration(registrationViewModel) { - screenState = Screen.MAIN - } - Screen.LOGIN -> - Login(loginViewModel) { - contactListViewModel.loadContacts() - screenState = Screen.MAIN - } - else -> - MainScreen( - contactListViewModel, - conversationViewModel, - addContactViewModel, - introductionViewModel, - sidebarViewModel, - isDark, - setDark - ) - } + @Composable + override fun start(applicationScope: ApplicationScope) { + val (isDark, setDark) = remember { mutableStateOf(true) } + val title = i18n("main.title") + var screenState by remember { + mutableStateOf( + if (accountManager.hasDatabaseKey()) { + // this should only happen during testing when we launch the main UI directly + // without a need to enter the password. + contactListViewModel.loadContacts() + Screen.MAIN + } else if (accountManager.accountExists()) { + Screen.LOGIN + } else { + Screen.REGISTRATION + } + ) + } + Window( + title = title, + onCloseRequest = { stop(); applicationScope.exitApplication() }, + ) { + window.minimumSize = Dimension(800, 600) + BriarTheme(isDarkTheme = isDark) { + when (screenState) { + Screen.REGISTRATION -> + Registration(registrationViewModel) { + contactListViewModel.loadContacts() + screenState = Screen.MAIN + } + Screen.LOGIN -> + Login(loginViewModel) { + contactListViewModel.loadContacts() + screenState = Screen.MAIN + } + else -> + MainScreen( + contactListViewModel, + conversationViewModel, + addContactViewModel, + introductionViewModel, + sidebarViewModel, + isDark, + setDark + ) } } } diff --git a/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt b/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt index 280dcef78d68238ac474d73059ef31aa23c2bb4b..13370267c3eab041ee2a993963da627fa5b85560 100644 --- a/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt +++ b/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt @@ -1,6 +1,7 @@ package org.briarproject.briar.desktop import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.window.application import org.briarproject.bramble.BrambleCoreEagerSingletons import org.briarproject.briar.BriarCoreEagerSingletons import org.briarproject.briar.desktop.utils.FileUtils @@ -56,7 +57,9 @@ internal class RunWithTemporaryAccount(val customization: BriarDesktopTestApp.() // list yet, we need to wait a moment in order for that to finish (hopefully). Thread.sleep(1000) - app.getBriarUi().start() + application { + app.getBriarUi().start(this) + } } private fun getDataDir(): Path { diff --git a/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoTemporaryAccounts.kt b/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoTemporaryAccounts.kt new file mode 100644 index 0000000000000000000000000000000000000000..88a3ab6770f3aed3a2136e04f4bf98112151e55f --- /dev/null +++ b/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoTemporaryAccounts.kt @@ -0,0 +1,85 @@ +package org.briarproject.briar.desktop + +import androidx.compose.runtime.Composable +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.window.ApplicationScope +import androidx.compose.ui.window.application +import org.briarproject.bramble.BrambleCoreEagerSingletons +import org.briarproject.briar.BriarCoreEagerSingletons +import org.briarproject.briar.desktop.utils.FileUtils +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Path +import java.util.logging.Level.INFO +import java.util.logging.LogManager +import java.util.logging.Logger +import kotlin.io.path.absolute + +fun main(args: Array<String>) = TestWithTwoTemporaryAccounts().run() + +internal class TestWithTwoTemporaryAccounts() { + + companion object { + private val LOG = Logger.getLogger(TestWithTwoTemporaryAccounts::class.java.name) + } + + @OptIn(ExperimentalComposeUiApi::class) + fun run() { + LogManager.getLogManager().getLogger("").level = INFO + + application { + app(this, "alice") + app(this, "bob") + } + } + + @Composable + private fun app(applicationScope: ApplicationScope, name: String) { + val dataDir = getDataDir() + LOG.info("Using data directory '$dataDir'") + + val app = + DaggerBriarDesktopTestApp.builder().desktopTestModule( + DesktopTestModule(dataDir.toFile()) + ).build() + + app.getShutdownManager().addShutdownHook { + LOG.info("deleting temporary account at $dataDir") + org.apache.commons.io.FileUtils.deleteDirectory(dataDir.toFile()) + } + + // We need to load the eager singletons directly after making the + // dependency graphs + BrambleCoreEagerSingletons.Helper.injectEagerSingletons(app) + BriarCoreEagerSingletons.Helper.injectEagerSingletons(app) + + val lifecycleManager = app.getLifecycleManager() + val accountManager = app.getAccountManager() + + val password = "verySecret123!" + accountManager.createAccount(name, password) + + val dbKey = accountManager.databaseKey ?: throw AssertionError() + lifecycleManager.startServices(dbKey) + lifecycleManager.waitForStartup() + + app.getDeterministicTestDataCreator().createTestData(5, 20, 50) + + // Creating test data happens on a background thread. As we do not get notified about updates to the conact + // list yet, we need to wait a moment in order for that to finish (hopefully). + Thread.sleep(1000) + + app.getBriarUi().start(applicationScope) + } + + private fun getDataDir(): Path { + val dataDir = Files.createTempDirectory("briar") + if (!Files.exists(dataDir)) { + throw IOException("Could not create directory: ${dataDir.absolute()}") + } else if (!Files.isDirectory(dataDir)) { + throw IOException("Data dir is not a directory: ${dataDir.absolute()}") + } + FileUtils.setRWX(dataDir) + return dataDir + } +}