diff --git a/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt index 089f04e6a4d1931eba92dc4d6339c9a6f1c4fc5c..5182f2959dc571e88b9231c74b68344b4a548321 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt @@ -1,7 +1,5 @@ package org.briarproject.briar.desktop -import androidx.compose.foundation.background -import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.compositionLocalOf @@ -9,7 +7,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier import androidx.compose.ui.window.Window import org.briarproject.bramble.api.account.AccountManager import org.briarproject.bramble.api.contact.Contact @@ -21,8 +18,7 @@ import org.briarproject.briar.api.conversation.ConversationManager import org.briarproject.briar.api.messaging.MessagingManager import org.briarproject.briar.desktop.dialogs.Login import org.briarproject.briar.desktop.dialogs.Registration -import org.briarproject.briar.desktop.paul.theme.DarkColorPallet -import org.briarproject.briar.desktop.paul.theme.briarBlack +import org.briarproject.briar.desktop.paul.theme.BriarTheme import org.briarproject.briar.desktop.paul.views.BriarUIStateManager import java.awt.Dimension import javax.annotation.concurrent.Immutable @@ -76,6 +72,7 @@ constructor( conversationManager: ConversationManager, messagingManager: MessagingManager ) { + val (isDark, setDark) = remember { mutableStateOf(true) } val title = "Briar Desktop" var screenState by remember { mutableStateOf( @@ -91,11 +88,10 @@ constructor( onCloseRequest = { exitProcess(0) }, ) { window.minimumSize = Dimension(800, 600) - MaterialTheme(colors = DarkColorPallet) { + BriarTheme(isDarkTheme = isDark) { when (screenState) { Screen.REGISTRATION -> Registration( - modifier = Modifier.background(briarBlack), onSubmit = { username, password -> accountManager.createAccount(username, password) signedIn() @@ -104,7 +100,6 @@ constructor( ) Screen.LOGIN -> Login( - modifier = Modifier.background(briarBlack), onResult = { try { accountManager.signIn(it) @@ -122,7 +117,7 @@ constructor( CTM provides contactManager, MM provides messagingManager ) { - BriarUIStateManager(contacts) + BriarUIStateManager(contacts, isDark, setDark) } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Login.kt b/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Login.kt index c895738ec93d322a583a7e514b44631e68d3aaa3..7be9e7caca821cedc3d161b3a9740e8ae6937ea7 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Login.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Login.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.Button import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -26,14 +27,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.type import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation @@ -48,39 +47,40 @@ fun Login( ) { var password by remember { mutableStateOf("") } val initialFocusRequester = remember { FocusRequester() } - Column( - modifier = modifier.padding(16.dp).fillMaxSize(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - BriarLogo() - Spacer(Modifier.height(32.dp)) - OutlinedTextField( - value = password, - onValueChange = { password = it }, - label = { Text("Password") }, - singleLine = true, - textStyle = TextStyle(color = Color.White), - visualTransformation = PasswordVisualTransformation(), - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done), - keyboardActions = KeyboardActions(onDone = { onResult.invoke(password) }), - modifier = Modifier - .focusRequester(initialFocusRequester) - .onPreviewKeyEvent { - if (it.type == KeyEventType.KeyUp && it.key == Key.Enter) { - onResult.invoke(password) - } - false - }, - ) - Spacer(Modifier.height(16.dp)) - Button(onClick = { onResult.invoke(password) }) { - Text("Login", color = Color.Black) - } + Surface { + Column( + modifier = modifier.padding(16.dp).fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + BriarLogo() + Spacer(Modifier.height(32.dp)) + OutlinedTextField( + value = password, + onValueChange = { password = it }, + label = { Text("Password") }, + singleLine = true, + visualTransformation = PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = { onResult.invoke(password) }), + modifier = Modifier + .focusRequester(initialFocusRequester) + .onPreviewKeyEvent { + if (it.type == KeyEventType.KeyUp && it.key == Key.Enter) { + onResult.invoke(password) + } + false + }, + ) + Spacer(Modifier.height(16.dp)) + Button(onClick = { onResult.invoke(password) }) { + Text("Login") + } - DisposableEffect(Unit) { - initialFocusRequester.requestFocus() - onDispose { } + DisposableEffect(Unit) { + initialFocusRequester.requestFocus() + onDispose { } + } } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/Colors.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/Colors.kt new file mode 100644 index 0000000000000000000000000000000000000000..ec1908c844e0ef3586f1b2c37656b409b0d64ca8 --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/Colors.kt @@ -0,0 +1,26 @@ +package org.briarproject.briar.desktop.paul.theme + +import androidx.compose.ui.graphics.Color + +val Night500 = Color(0xff435B77) +val Night700 = Color(0xff2E3D4F) + +val Gray50 = Color(0xfffafafa) +val Gray100 = Color(0xfff2f2f2) +val Gray200 = Color(0xffdfdfdf) +val Gray300 = Color(0xffcccccc) +val Gray400 = Color(0xffbababa) +val Gray500 = Color(0xffa7a7a7) +val Gray700 = Color(0xff707070) +val Gray800 = Color(0xff4f4f4f) +val Gray900 = Color(0xff2e2e2e) +val Gray950 = Color(0xff1f1f1f) +val materialDarkBg = Color(0xff121212) + +val Blue500 = Color(0xff1f78d1) +val Blue800 = Color(0xff134a81) + +val Lime300 = Color(0xff95DE2D) +val Lime500 = Color(0xff74B816) + +val briarError = Color(0xffb00020) diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/Theme.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/Theme.kt new file mode 100644 index 0000000000000000000000000000000000000000..e9e9b049bafa1824d21774002efb5e2f659b20d9 --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/Theme.kt @@ -0,0 +1,56 @@ +package org.briarproject.briar.desktop.paul.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.Colors +import androidx.compose.material.MaterialTheme +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +val Colors.divider: Color get() = if (isLight) Gray300 else Gray800 +val Colors.outline: Color get() = Color.White +val Colors.surfaceVariant: Color get() = if (isLight) Gray100 else Gray950 +val Colors.sidebarSurface: Color get() = if (isLight) Gray200 else Gray900 +val Colors.selectedCard: Color get() = if (isLight) Gray400 else Gray700 +val Colors.localMsgBubble: Color get() = Blue500 +val Colors.awayMsgBubble: Color get() = if (isLight) Gray300 else Gray800 + +val DarkColors = darkColors( + primary = Blue500, + primaryVariant = Night500, + secondary = Lime500, + background = materialDarkBg, + surface = materialDarkBg, + onPrimary = Color.White, + onSecondary = Color.White, + onBackground = Color.White, + onSurface = Color.White, + error = briarError +) +val LightColors = lightColors( + primary = Blue500, + primaryVariant = Night500, + secondary = Lime300, + background = Color.White, + surface = Color.White, + onPrimary = Color.White, + onSecondary = Color.Black, + onBackground = Color.Black, + onSurface = Color.Black, + error = briarError +) + +@Composable +fun BriarTheme( + isDarkTheme: Boolean = isSystemInDarkTheme(), + colors: Colors? = null, + content: @Composable () -> Unit +) { + val myColors = colors ?: if (isDarkTheme) DarkColors else LightColors + + MaterialTheme( + colors = myColors, + content = content, + ) +} diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/colors.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/colors.kt deleted file mode 100644 index f3b864eab9cb573e0129088bc91e85a81ab6906a..0000000000000000000000000000000000000000 --- a/src/main/kotlin/org/briarproject/briar/desktop/paul/theme/colors.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.briarproject.briar.desktop.paul.theme - -import androidx.compose.material.darkColors -import androidx.compose.ui.graphics.Color - -val graySurface = Color(0xFF2A2A2A) -val lightGray = Color(0xFFD3D3D3) -val briarGray = Color(0xff222222) -val briarBlack = Color(0xff1E2228) -val briarSelBlack = Color(0xff495261) -val briarDarkGray = Color(0xFF3D4552) -val darkGray = Color(0xFF151515) -val divider = Color(0xff35383D) -val briarBlue = Color(0xFF2D3E50) -val briarLightBlue = Color(0xFFEBEFF2) -val briarGreen = Color(0xFF97D323) -val briarBlueMsg = Color(0xFF1b69b6) -val briarBlueSpecialMsg = Color(0xFF134a80) -val briarGrayMsg = Color(0xff3b4047) -val briarGraySpecialMsg = Color(0xFF212d3b) - -val DarkColorPallet = darkColors( - primary = Color.White, - secondary = graySurface, - background = briarLightBlue, - surface = briarBlue, - onPrimary = Color.White, - onSecondary = lightGray, - onBackground = Color.White, - onSurface = Color.White, - error = Color.Red, -) diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt index 886920f197c3e73321b1e32ecb7bf135ddf71a69..d9d4441b92e0dff1cbc90cf4583e260d4b3c712e 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarSidebar.kt @@ -1,7 +1,6 @@ package org.briarproject.briar.desktop.paul.views import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -12,6 +11,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.Icon import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ChromeReaderMode @@ -30,14 +30,13 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.loadImageBitmap import androidx.compose.ui.res.useResource import androidx.compose.ui.unit.dp -import org.briarproject.briar.desktop.paul.theme.briarBlack -import org.briarproject.briar.desktop.paul.theme.briarBlue +import org.briarproject.briar.desktop.paul.theme.sidebarSurface val SIDEBAR_WIDTH = 56.dp @Composable fun BriarSidebar(uiMode: UiModes, setUiMode: (UiModes) -> Unit) { - Surface(modifier = Modifier.width(SIDEBAR_WIDTH).fillMaxHeight(), color = briarBlue) { + Surface(modifier = Modifier.width(SIDEBAR_WIDTH).fillMaxHeight(), color = MaterialTheme.colors.sidebarSurface) { Column(verticalArrangement = Arrangement.Top) { IconButton( modifier = Modifier.align(Alignment.CenterHorizontally).padding(top = 5.dp, bottom = 4.dp), @@ -101,14 +100,14 @@ fun BriarSidebar(uiMode: UiModes, setUiMode: (UiModes) -> Unit) { @Composable fun BriarSidebarButton(uiMode: UiModes, setUiMode: (UiModes) -> Unit, thisMode: UiModes, icon: ImageVector) { - val bg = if (uiMode == thisMode) briarBlack else briarBlue + val tint = if (uiMode == thisMode) MaterialTheme.colors.primary else MaterialTheme.colors.onSurface Column { IconButton( - modifier = Modifier.align(Alignment.CenterHorizontally).background(color = bg) + modifier = Modifier.align(Alignment.CenterHorizontally) .padding(vertical = 4.dp, horizontal = 12.dp), onClick = { setUiMode(thisMode) } ) { - Icon(icon, thisMode.toString(), tint = Color.White, modifier = Modifier.size(30.dp)) + Icon(icon, thisMode.toString(), tint = tint, modifier = Modifier.size(30.dp)) } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarUIStateManager.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarUIStateManager.kt index bf954316027b18a8f5a3f49a5227a330c94c9108..3babe30d44caa4dd82254063e75c54cd64e9b5b1 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarUIStateManager.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/BriarUIStateManager.kt @@ -3,21 +3,16 @@ package org.briarproject.briar.desktop.paul.views import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.width -import androidx.compose.material.Divider +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp import org.briarproject.bramble.api.contact.Contact -import org.briarproject.briar.desktop.paul.theme.briarBlack -import org.briarproject.briar.desktop.paul.theme.divider enum class UiModes { CONTACTS, @@ -36,7 +31,9 @@ enum class UiModes { */ @Composable fun BriarUIStateManager( - contacts: List<Contact> + contacts: List<Contact>, + isDark: Boolean, + setDark: (Boolean) -> Unit ) { // current selected mode, changed using the sidebar buttons val (uiMode, setUiMode) = remember { mutableStateOf(UiModes.CONTACTS) } @@ -56,15 +53,27 @@ fun BriarUIStateManager( // Other global state that we need to track should go here also Row { BriarSidebar(uiMode, setUiMode) - Divider(color = divider, modifier = Modifier.fillMaxHeight().width(1.dp)) + VerticalDivider() when (uiMode) { UiModes.CONTACTS -> if (contact != null) PrivateMessageView( contact, contacts, setContact ) - else -> Box(modifier = Modifier.fillMaxSize().background(briarBlack)) { - Text("TBD", modifier = Modifier.align(Alignment.Center), color = Color.White) + UiModes.SETTINGS -> PlaceHolderSettingsView(isDark, setDark) + else -> Surface(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.background)) { + Text("TBD") + } + } + } +} + +@Composable +fun PlaceHolderSettingsView(isDark: Boolean, setDark: (Boolean) -> Unit) { + Surface(Modifier.fillMaxSize()) { + Box { + Button(onClick = { setDark(!isDark) }) { + Text("Change Theme") } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt index 0810db51b33b2bfd9c2c7cc6d59f43dbeae4e9b0..11108b19463b963f836195b244991fccc7884baf 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt @@ -30,6 +30,8 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.AlertDialog +import androidx.compose.material.Button +import androidx.compose.material.Card import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Divider import androidx.compose.material.DropdownMenu @@ -39,9 +41,11 @@ import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold +import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.material.TextField +import androidx.compose.material.TextFieldDefaults import androidx.compose.material.icons.Icons.Filled import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.ArrowBack @@ -91,16 +95,13 @@ import org.briarproject.briar.desktop.chat.ChatHistoryConversationVisitor import org.briarproject.briar.desktop.chat.ConversationMessageHeaderComparator import org.briarproject.briar.desktop.chat.SimpleMessage import org.briarproject.briar.desktop.chat.UiState -import org.briarproject.briar.desktop.paul.theme.DarkColorPallet -import org.briarproject.briar.desktop.paul.theme.briarBlack -import org.briarproject.briar.desktop.paul.theme.briarBlue -import org.briarproject.briar.desktop.paul.theme.briarBlueMsg -import org.briarproject.briar.desktop.paul.theme.briarDarkGray -import org.briarproject.briar.desktop.paul.theme.briarGrayMsg -import org.briarproject.briar.desktop.paul.theme.briarGreen -import org.briarproject.briar.desktop.paul.theme.darkGray +import org.briarproject.briar.desktop.paul.theme.DarkColors +import org.briarproject.briar.desktop.paul.theme.awayMsgBubble import org.briarproject.briar.desktop.paul.theme.divider -import org.briarproject.briar.desktop.paul.theme.lightGray +import org.briarproject.briar.desktop.paul.theme.localMsgBubble +import org.briarproject.briar.desktop.paul.theme.outline +import org.briarproject.briar.desktop.paul.theme.selectedCard +import org.briarproject.briar.desktop.paul.theme.surfaceVariant import java.security.GeneralSecurityException import java.time.Instant import java.time.LocalDateTime @@ -120,22 +121,32 @@ enum class ContactInfoDrawerState { ConnectRD } +@Composable +fun HorizontalDivider(modifier: Modifier = Modifier) { + Divider(color = MaterialTheme.colors.divider, thickness = 1.dp, modifier = modifier.fillMaxWidth()) +} + +@Composable +fun VerticalDivider() { + Divider(color = MaterialTheme.colors.divider, modifier = Modifier.fillMaxHeight().width(1.dp)) +} + @Composable fun PrivateMessageView( contact: Contact, contacts: List<Contact>, onContactSelect: (Contact) -> Unit ) { - val (addContactDialog, onContactAdd) = remember { mutableStateOf(false) } + val (isDialogVisible, setDialogVisibility) = remember { mutableStateOf(false) } val (dropdownExpanded, setExpanded) = remember { mutableStateOf(false) } val (infoDrawer, setInfoDrawer) = remember { mutableStateOf(false) } val (contactDrawerState, setDrawerState) = remember { mutableStateOf(ContactInfoDrawerState.MakeIntro) } - AddContactDialog(addContactDialog, onContactAdd) + AddContactDialog(isDialogVisible, setDialogVisibility) Row(modifier = Modifier.fillMaxWidth()) { - ContactList(contact, contacts, onContactSelect, onContactAdd) - Divider(color = divider, modifier = Modifier.fillMaxHeight().width(1.dp)) - Column(modifier = Modifier.weight(1f).fillMaxHeight().background(color = darkGray)) { - DrawMessageRow( + ContactList(contact, contacts, onContactSelect, setDialogVisibility) + VerticalDivider() + Column(modifier = Modifier.weight(1f).fillMaxHeight()) { + Conversation( contact, contacts, dropdownExpanded, @@ -166,7 +177,6 @@ fun AddContactDialog(isVisible: Boolean, setDialogVisibility: (Boolean) -> Unit) Text( text = "Add Contact at a Distance", fontSize = 24.sp, - color = Color.White, modifier = Modifier.align(Alignment.CenterVertically) ) } @@ -174,7 +184,6 @@ fun AddContactDialog(isVisible: Boolean, setDialogVisibility: (Boolean) -> Unit) Text( "Contact's Link", Modifier.width(128.dp).align(Alignment.CenterVertically), - color = lightGray ) TextField(contactLink, onValueChange = { contactLink = it }, modifier = Modifier.fillMaxWidth()) } @@ -182,7 +191,6 @@ fun AddContactDialog(isVisible: Boolean, setDialogVisibility: (Boolean) -> Unit) Text( "Contact's Name", Modifier.width(128.dp).align(Alignment.CenterVertically), - color = lightGray ) TextField(contactAlias, onValueChange = { contactAlias = it }, modifier = Modifier.fillMaxWidth()) } @@ -190,7 +198,6 @@ fun AddContactDialog(isVisible: Boolean, setDialogVisibility: (Boolean) -> Unit) Text( "Your Link", modifier = Modifier.width(128.dp).align(Alignment.CenterVertically), - color = lightGray ) TextField( ownLink, @@ -201,32 +208,28 @@ fun AddContactDialog(isVisible: Boolean, setDialogVisibility: (Boolean) -> Unit) } }, confirmButton = { - TextButton( + Button( onClick = { if (ownLink.equals(contactLink)) { println("Please enter contact's link, not your own") setDialogVisibility(false) - return@TextButton + return@Button } addPendingContact(contactManager, contactAlias, contactLink) setDialogVisibility(false) }, - modifier = Modifier.background(briarGreen) ) { Text("Add") } }, dismissButton = { TextButton( - onClick = { setDialogVisibility(false) }, modifier = Modifier.background(briarBlack) + onClick = { setDialogVisibility(false) } ) { - Text("Cancel") + Text("Cancel", color = MaterialTheme.colors.onSurface) } }, - - backgroundColor = briarBlue, - contentColor = Color.White, - modifier = Modifier.border(1.dp, color = divider).size(600.dp, 300.dp), + modifier = Modifier.size(600.dp, 300.dp), ) } @@ -271,29 +274,33 @@ fun SearchTextField(searchValue: String, onValueChange: (String) -> Unit, onCont value = searchValue, onValueChange = onValueChange, singleLine = true, - modifier = Modifier.padding(horizontal = 8.dp), - textStyle = TextStyle(color = Color.White, fontSize = 16.sp), - placeholder = { Text("Contacts", color = Color.Gray) }, + textStyle = TextStyle(fontSize = 16.sp, color = MaterialTheme.colors.onSurface), + placeholder = { Text("Contacts") }, + shape = RoundedCornerShape(0.dp), leadingIcon = { - val padding = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 16.dp) - Icon(Filled.Search, "search contacts", padding, Color.White) + val padding = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 12.dp) + Icon(Filled.Search, "search contacts", padding) }, trailingIcon = { IconButton( onClick = { onContactAdd(true) }, - modifier = Modifier.padding(end = 4.dp).size(32.dp).background( - briarBlueMsg, CircleShape - ) + modifier = Modifier.padding(end = 10.dp).size(32.dp) + .background(MaterialTheme.colors.primary, CircleShape) ) { Icon(Filled.PersonAdd, "add contact", tint = Color.White, modifier = Modifier.size(20.dp)) } - } + }, + modifier = Modifier.fillMaxSize() ) } @Composable fun ProfileCircle(bitmap: ImageBitmap, size: Dp) { - Image(bitmap, "image", Modifier.size(size).clip(CircleShape).border(2.dp, Color.White, CircleShape)) + Image( + bitmap, + "image", + Modifier.size(size).clip(CircleShape).border(2.dp, MaterialTheme.colors.outline, CircleShape) + ) } @Composable @@ -304,60 +311,66 @@ fun ContactCard( selected: Boolean, drawNotifications: Boolean ) { - // selContact.id == contact.id && drawNotifications - val bgColor = if (selected) darkGray else briarBlack - Row( - modifier = Modifier.fillMaxWidth().height(HEADER_SIZE).background(bgColor) - .clickable(onClick = { onSel(contact) }), - horizontalArrangement = Arrangement.SpaceBetween + val bgColor = if (selected) MaterialTheme.colors.selectedCard else MaterialTheme.colors.surfaceVariant + val outlineColor = MaterialTheme.colors.outline + val briarPrimary = MaterialTheme.colors.primary + val briarSecondary = MaterialTheme.colors.secondary + val briarSurfaceVar = MaterialTheme.colors.surfaceVariant + + Card( + modifier = Modifier.fillMaxWidth().height(HEADER_SIZE).clickable(onClick = { onSel(contact) }), + shape = RoundedCornerShape(0.dp), + backgroundColor = bgColor, + contentColor = MaterialTheme.colors.onSurface ) { - Row(modifier = Modifier.align(Alignment.CenterVertically).padding(horizontal = 16.dp)) { - // TODO Pull profile pictures - ProfileCircle(useResource("images/profile_images/p0.png") { loadImageBitmap(it) }, 36.dp) - // Draw notification badges - if (drawNotifications) { - Canvas( - modifier = Modifier.align(Alignment.CenterVertically), - onDraw = { - val size = 10.dp.toPx() - withTransform({ translate(left = -6f, top = -12f) }) { - drawCircle(color = Color.White, radius = (size + 2.dp.toPx()) / 2f) - drawCircle(color = briarBlueMsg, radius = size / 2f) + Row(horizontalArrangement = Arrangement.SpaceBetween) { + Row(modifier = Modifier.align(Alignment.CenterVertically).padding(horizontal = 16.dp)) { + // TODO Pull profile pictures + ProfileCircle(useResource("images/profile_images/p0.png") { loadImageBitmap(it) }, 36.dp) + // Draw notification badges + if (drawNotifications) { + Canvas( + modifier = Modifier.align(Alignment.CenterVertically), + onDraw = { + val size = 10.dp.toPx() + withTransform({ translate(left = -6f, top = -12f) }) { + drawCircle(color = outlineColor, radius = (size + 2.dp.toPx()) / 2f) + drawCircle(color = briarPrimary, radius = size / 2f) + } } - } - ) - } - Column(modifier = Modifier.align(Alignment.CenterVertically).padding(start = 12.dp)) { - Text( - contact.author.name, - fontSize = 14.sp, - color = Color.White, - modifier = Modifier.align(Alignment.Start).padding(bottom = 2.dp) - ) - val latestMsgTime = CVM.current.getGroupCount(contact.id).latestMsgTime - Text( - getFormattedTimestamp(latestMsgTime), - fontSize = 10.sp, - color = Color.LightGray, - modifier = Modifier.align(Alignment.Start) - ) - } - } - Canvas( - modifier = Modifier.padding(start = 32.dp, end = 18.dp).size(22.dp).align(Alignment.CenterVertically), - onDraw = { - val size = 16.dp.toPx() - drawCircle(color = Color.White, radius = size / 2f) - // TODO check if contact online - if (true) { - drawCircle(color = briarGreen, radius = 14.dp.toPx() / 2f) - } else { - drawCircle(color = briarBlack, radius = 14.dp.toPx() / 2f) + ) + } + + Column(modifier = Modifier.align(Alignment.CenterVertically).padding(start = 12.dp)) { + Text( + contact.author.name, + fontSize = 14.sp, + modifier = Modifier.align(Alignment.Start).padding(bottom = 2.dp) + ) + val latestMsgTime = CVM.current.getGroupCount(contact.id).latestMsgTime + Text( + getFormattedTimestamp(latestMsgTime), + fontSize = 10.sp, + modifier = Modifier.align(Alignment.Start) + ) } } - ) + Canvas( + modifier = Modifier.padding(start = 32.dp, end = 18.dp).size(22.dp).align(Alignment.CenterVertically), + onDraw = { + val size = 16.dp.toPx() + drawCircle(color = outlineColor, radius = size / 2f) + // TODO check if contact online + if (true) { + drawCircle(color = briarSecondary, radius = 14.dp.toPx() / 2f) + } else { + drawCircle(color = briarSurfaceVar, radius = 14.dp.toPx() / 2f) + } + } + ) + } } - Divider(color = divider, thickness = 1.dp, modifier = Modifier.fillMaxWidth()) + HorizontalDivider() } fun getFormattedTimestamp(timestamp: Long): String { @@ -396,15 +409,12 @@ fun ContactList( } Scaffold( modifier = Modifier.fillMaxHeight().width(CONTACTLIST_WIDTH), - backgroundColor = briarBlack, + backgroundColor = MaterialTheme.colors.surfaceVariant, topBar = { Column( - modifier = Modifier.fillMaxWidth().background(briarBlack), + modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp), ) { - Row(Modifier.height(HEADER_SIZE), verticalAlignment = Alignment.CenterVertically) { - SearchTextField(searchValue, onValueChange = { searchValue = it }, onContactAdd) - } - Divider(color = divider, thickness = 1.dp, modifier = Modifier.fillMaxWidth()) + SearchTextField(searchValue, onValueChange = { searchValue = it }, onContactAdd) } }, content = { @@ -420,31 +430,40 @@ fun ContactList( @Composable fun TextBubble(m: SimpleMessage) { if (m.local) { - TextBubble(m, Alignment.End, briarBlueMsg) + TextBubble( + m, + Alignment.End, + MaterialTheme.colors.localMsgBubble, + RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp, bottomStart = 10.dp) + ) } else { - TextBubble(m, Alignment.Start, briarGrayMsg) + TextBubble( + m, + Alignment.Start, + MaterialTheme.colors.awayMsgBubble, + RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp, bottomEnd = 10.dp) + ) } } @Composable -fun TextBubble(m: SimpleMessage, alignment: Alignment.Horizontal, color: Color) { +fun TextBubble(m: SimpleMessage, alignment: Alignment.Horizontal, color: Color, shape: RoundedCornerShape) { Column(Modifier.fillMaxWidth()) { Column(Modifier.fillMaxWidth(fraction = 0.8f).align(alignment)) { - Column( - Modifier.background( - color, - RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp, bottomStart = 10.dp) - ).padding(8.dp).align(alignment) - ) { - Text(m.message, fontSize = 14.sp, color = Color.White, modifier = Modifier.align(Alignment.Start)) - Row(modifier = Modifier.padding(top = 4.dp)) { - Text(m.time, Modifier.padding(end = 4.dp), fontSize = 10.sp, color = Color.LightGray) - if (m.delivered) { - val modifier = Modifier.size(12.dp).align(Alignment.CenterVertically) - Icon(Filled.DoneAll, "sent", modifier, Color.LightGray) - } else { - val modifier = Modifier.size(12.dp).align(Alignment.CenterVertically) - Icon(Filled.Schedule, "sending", modifier, Color.LightGray) + Card(Modifier.align(alignment), backgroundColor = color, shape = shape) { + Column( + Modifier.padding(8.dp) + ) { + Text(m.message, fontSize = 14.sp, modifier = Modifier.align(Alignment.Start)) + Row(modifier = Modifier.padding(top = 4.dp)) { + Text(m.time, Modifier.padding(end = 4.dp), fontSize = 10.sp) + if (m.delivered) { + val modifier = Modifier.size(12.dp).align(Alignment.CenterVertically) + Icon(Filled.DoneAll, "sent", modifier) + } else { + val modifier = Modifier.size(12.dp).align(Alignment.CenterVertically) + Icon(Filled.Schedule, "sending", modifier) + } } } } @@ -461,7 +480,7 @@ fun DrawTextBubbles(chat: UiState<Chat>) { LazyColumn( verticalArrangement = Arrangement.spacedBy(8.dp), reverseLayout = true, - contentPadding = PaddingValues(top = 8.dp, start = 8.dp, end = 8.dp) + contentPadding = PaddingValues(8.dp) ) { items(chat.data.messages) { m -> TextBubble(m) } } @@ -489,7 +508,6 @@ fun ContactDropDown( DropdownMenu( expanded = expanded, onDismissRequest = { isExpanded(false) }, - modifier = Modifier.background(briarBlack) ) { DropdownMenuItem(onClick = { setInfoDrawer(true); isExpanded(false) }) { Text("Make Introduction", fontSize = 14.sp) @@ -517,10 +535,9 @@ fun ContactDropDown( DropdownMenu( expanded = connectionMode, onDismissRequest = { connectionMode = false }, - modifier = Modifier.background(briarBlack) ) { DropdownMenuItem(onClick = { false }) { - Text("Connections", color = lightGray, fontSize = 12.sp) + Text("Connections", fontSize = 12.sp) } DropdownMenuItem(onClick = { false }) { Text("Connect via Bluetooth", fontSize = 14.sp) @@ -534,10 +551,9 @@ fun ContactDropDown( DropdownMenu( expanded = contactMode, onDismissRequest = { contactMode = false }, - modifier = Modifier.background(briarBlack) ) { DropdownMenuItem(onClick = { false }) { - Text("Contact", color = lightGray, fontSize = 12.sp) + Text("Contact", fontSize = 12.sp) } DropdownMenuItem(onClick = { false }) { Text("Change contact name", fontSize = 14.sp) @@ -550,12 +566,16 @@ fun ContactDropDown( } @Composable -fun MsgColumnHeader( +fun ConversationHeader( contact: Contact, expanded: Boolean, isExpanded: (Boolean) -> Unit, setInfoDrawer: (Boolean) -> Unit ) { + // TODO hook up online indicator logic + val onlineColor = MaterialTheme.colors.secondary + val outlineColor = MaterialTheme.colors.outline + Box(modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp)) { Row(modifier = Modifier.align(Alignment.Center)) { ProfileCircle(useResource("images/profile_images/p0.png") { loadImageBitmap(it) }, 36.dp) @@ -563,17 +583,14 @@ fun MsgColumnHeader( modifier = Modifier.align(Alignment.CenterVertically), onDraw = { val size = 10.dp.toPx() - // TODO hook up online indicator logic - val onlineColor = if (true) briarGreen else briarBlack withTransform({ translate(left = -6f, top = 12f) }) { - drawCircle(color = Color.White, radius = (size + 2.dp.toPx()) / 2f) + drawCircle(color = outlineColor, radius = (size + 2.dp.toPx()) / 2f) drawCircle(color = onlineColor, radius = size / 2f) } } ) Text( contact.author.name, - color = Color.White, modifier = Modifier.align(Alignment.CenterVertically).padding(start = 12.dp), fontSize = 20.sp ) @@ -582,32 +599,36 @@ fun MsgColumnHeader( onClick = { isExpanded(!expanded) }, modifier = Modifier.align(Alignment.CenterEnd).padding(end = 16.dp) ) { - Icon(Filled.MoreVert, "contact info", tint = Color.White, modifier = Modifier.size(24.dp)) + Icon(Filled.MoreVert, "contact info", modifier = Modifier.size(24.dp)) ContactDropDown(expanded, isExpanded, setInfoDrawer) } - Divider(color = divider, thickness = 1.dp, modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter)) + HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) } } @Composable -fun MsgInput() { +fun ConversationInput() { var text by remember { mutableStateOf("") } - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(8.dp) - ) { + Column { + HorizontalDivider() TextField( value = text, onValueChange = { text = it }, maxLines = 10, - textStyle = TextStyle(color = Color.White, fontSize = 16.sp, lineHeight = 16.sp), - placeholder = { Text("Message", color = Color.Gray) }, + textStyle = TextStyle(fontSize = 16.sp, lineHeight = 16.sp), + placeholder = { Text("Message") }, modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(0.dp), + colors = TextFieldDefaults.textFieldColors( + backgroundColor = MaterialTheme.colors.background, + focusedIndicatorColor = MaterialTheme.colors.background, + unfocusedIndicatorColor = MaterialTheme.colors.background + ), leadingIcon = { IconButton( onClick = {}, Modifier.padding(4.dp).size(32.dp) - .background(briarBlueMsg, CircleShape), + .background(MaterialTheme.colors.primary, CircleShape), ) { Icon(Filled.Add, "add attachment", Modifier.size(24.dp), Color.White) } @@ -616,7 +637,12 @@ fun MsgInput() { IconButton( onClick = { }, modifier = Modifier.padding(4.dp).size(32.dp), ) { - Icon(Filled.Send, "send message", tint = briarGreen, modifier = Modifier.size(24.dp)) + Icon( + Filled.Send, + "send message", + tint = MaterialTheme.colors.secondary, + modifier = Modifier.size(24.dp) + ) } } ) @@ -626,9 +652,9 @@ fun MsgInput() { @Preview @Composable fun PreviewMsgInput() { - MaterialTheme(colors = DarkColorPallet) { - Row(Modifier.background(briarBlack)) { - MsgInput() + MaterialTheme(colors = DarkColors) { + Surface { + ConversationInput() } } } @@ -638,22 +664,21 @@ fun ContactDrawerMakeIntro(contact: Contact, contacts: List<Contact>, setInfoDra var introNextPg by remember { mutableStateOf(false) } val (introContact, onCancelSel) = remember { mutableStateOf(contact) } if (!introNextPg) { - Column { + Surface { Row(Modifier.fillMaxWidth().height(HEADER_SIZE)) { IconButton( onClick = { setInfoDrawer(false) }, Modifier.padding(horizontal = 11.dp).size(32.dp).align(Alignment.CenterVertically) ) { - Icon(Filled.Close, "close make intro screen", tint = Color.White) + Icon(Filled.Close, "close make intro screen") } Text( text = "Introduce " + contact.author.name + " to:", - color = Color.White, fontSize = 16.sp, modifier = Modifier.align(Alignment.CenterVertically) ) } - Divider(color = divider, modifier = Modifier.fillMaxWidth().height(1.dp)) + HorizontalDivider() Column(Modifier.verticalScroll(rememberScrollState())) { for (c in contacts) { if (c.id != contact.id) { @@ -673,22 +698,20 @@ fun ContactDrawerMakeIntro(contact: Contact, contacts: List<Contact>, setInfoDra } Text( text = "Introduce Contacts", - color = Color.White, fontSize = 16.sp, modifier = Modifier.align(Alignment.CenterVertically) ) } - // Divider(color = divider, modifier = Modifier.fillMaxWidth().height(1.dp) ) Row(Modifier.fillMaxWidth().padding(12.dp), horizontalArrangement = Arrangement.SpaceAround) { Column(Modifier.align(Alignment.CenterVertically)) { ProfileCircle(useResource("images/profile_images/p0.png") { loadImageBitmap(it) }, 40.dp) - Text(contact.author.name, Modifier.padding(top = 4.dp), Color.White, 16.sp) + Text(contact.author.name, Modifier.padding(top = 4.dp), fontSize = 16.sp) } - Icon(Filled.SwapHoriz, "swap", tint = Color.White, modifier = Modifier.size(48.dp)) + Icon(Filled.SwapHoriz, "swap", modifier = Modifier.size(48.dp)) Column(Modifier.align(Alignment.CenterVertically)) { // TODO Profile pic again ProfileCircle(useResource("images/profile_images/p0.png") { loadImageBitmap(it) }, 40.dp) - Text(introContact.author.name, Modifier.padding(top = 4.dp), Color.White, 16.sp) + Text(introContact.author.name, Modifier.padding(top = 4.dp), fontSize = 16.sp) } } var introText by remember { mutableStateOf(TextFieldValue("")) } @@ -697,13 +720,12 @@ fun ContactDrawerMakeIntro(contact: Contact, contacts: List<Contact>, setInfoDra introText, { introText = it }, placeholder = { Text(text = "Add a message (optional)") }, - textStyle = TextStyle(color = Color.White) ) } Row(Modifier.padding(8.dp)) { TextButton( onClick = { setInfoDrawer(false); introNextPg = false; }, - Modifier.fillMaxWidth().background(briarDarkGray) + Modifier.fillMaxWidth() ) { Text("MAKE INTRODUCTION") } @@ -725,7 +747,7 @@ fun ContactInfoDrawer( } @Composable -fun DrawMessageRow( +fun Conversation( contact: Contact, contacts: List<Contact>, expanded: Boolean, @@ -737,16 +759,14 @@ fun DrawMessageRow( BoxWithConstraints(Modifier.fillMaxSize()) { val animatedInfoDrawerOffsetX by animateDpAsState(if (infoDrawer) (-275).dp else 0.dp) Scaffold( - topBar = { MsgColumnHeader(contact, expanded, setExpanded, setInfoDrawer) }, + topBar = { ConversationHeader(contact, expanded, setExpanded, setInfoDrawer) }, content = { padding -> Box(modifier = Modifier.padding(padding)) { val chat = ChatState(contact.id) DrawTextBubbles(chat.value) } }, - bottomBar = { MsgInput() }, - backgroundColor = darkGray, - modifier = Modifier.offset() + bottomBar = { ConversationInput() }, ) if (infoDrawer) { // TODO Find non-hacky way of setting scrim on entire app @@ -763,7 +783,10 @@ fun DrawMessageRow( Column( modifier = Modifier.fillMaxHeight().width(CONTACTLIST_WIDTH) .offset(maxWidth + animatedInfoDrawerOffsetX) - .background(briarBlack, RoundedCornerShape(topStart = 10.dp, bottomStart = 10.dp)) + .background( + MaterialTheme.colors.surfaceVariant, + RoundedCornerShape(topStart = 10.dp, bottomStart = 10.dp) + ) ) { ContactInfoDrawer(contact, contacts, setInfoDrawer, drawerState) }