From ad1612d84abafd4f40b9b00915d3095841c08128 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 26 Feb 2022 11:35:51 +0000 Subject: [PATCH] android: markdown help (#376) --- .../java/chat/simplex/app/MainActivity.kt | 4 + .../java/chat/simplex/app/model/ChatModel.kt | 15 +- .../chat/simplex/app/views/TerminalView.kt | 24 +-- .../app/views/chat/item/TextItemView.kt | 2 +- .../app/views/chatlist/ChatPreviewView.kt | 17 +++ .../app/views/usersettings/HelpView.kt | 2 +- .../views/usersettings/MarkdownHelpView.kt | 110 ++++++++++++++ .../app/views/usersettings/SettingsView.kt | 143 +++++++----------- 8 files changed, 217 insertions(+), 100 deletions(-) create mode 100644 apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt index f5dcef32ed..a935883631 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt @@ -122,6 +122,9 @@ fun Navigation(chatModel: ChatModel) { composable(route = Pages.Help.route) { HelpView(chatModel, nav) } + composable(route = Pages.Markdown.route) { + MarkdownHelpView(nav) + } } val am = chatModel.alertManager if (am.presentAlert.value) am.alertView.value?.invoke() @@ -141,6 +144,7 @@ sealed class Pages(val route: String) { object UserProfile: Pages("user_profile") object UserAddress: Pages("user_address") object Help: Pages("help") + object Markdown: Pages("markdown") } @DelicateCoroutinesApi diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt index 89d3c2146b..82b1dbf827 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt @@ -250,6 +250,13 @@ data class Chat ( @Serializable @SerialName("disconnected") class Disconnected: NetworkStatus() @Serializable @SerialName("error") class Error(val error: String): NetworkStatus() } + + companion object { + val sampleData = Chat( + chatInfo = ChatInfo.Direct.sampleData, + chatItems = arrayListOf(ChatItem.getSampleData()) + ) + } } @Serializable @@ -465,7 +472,13 @@ data class ChatItem ( val isRcvNew: Boolean get() = meta.itemStatus is CIStatus.RcvNew companion object { - fun getSampleData(id: Long, dir: CIDirection, ts: Instant, text: String,status: CIStatus = CIStatus.SndNew()) = + fun getSampleData( + id: Long = 1, + dir: CIDirection = CIDirection.DirectSnd(), + ts: Instant = Clock.System.now(), + text: String = "hello\nthere", + status: CIStatus = CIStatus.SndNew() + ) = ChatItem( chatDir = dir, meta = CIMeta.getSample(id, ts, text, status), diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt index 586029c957..4d83b38b1d 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -66,7 +67,10 @@ fun TerminalLog(terminalItems: List, navigate: (String) -> Unit) { items(terminalItems) { item -> Text("${item.date.toString().subSequence(11, 19)} ${item.label}", style = TextStyle(fontFamily = FontFamily.Monospace, fontSize = 18.sp, color = MaterialTheme.colors.primary), - modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp) + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .padding(horizontal = 8.dp, vertical = 4.dp) .clickable { navigate("details/${item.id}") }) } val len = terminalItems.count() @@ -79,15 +83,17 @@ fun TerminalLog(terminalItems: List, navigate: (String) -> Unit) { } @Composable -fun DetailView(identifier: Long, terminalItems: List, navController: NavController){ - Column( - modifier = Modifier.verticalScroll(rememberScrollState()) +fun DetailView(identifier: Long, terminalItems: List, nav: NavController){ + Surface( + Modifier + .background(MaterialTheme.colors.background) + .fillMaxSize() ) { - Button(onClick = { navController.popBackStack() }) { - Text("Back") - } - SelectionContainer { - Text((terminalItems.firstOrNull { it.id == identifier })?.details ?: "") + Column { + CloseSheetBar(nav::popBackStack) + SelectionContainer(modifier = Modifier.verticalScroll(rememberScrollState())) { + Text((terminalItems.firstOrNull { it.id == identifier })?.details ?: "") + } } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt index c64fb7f38b..5be27d0e57 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt @@ -44,7 +44,7 @@ fun TextItemView(chatItem: ChatItem, uriHandler: UriHandler? = null) { } val reserveTimestampStyle = SpanStyle(color = Color.Transparent) -val boldFont = SpanStyle(fontWeight = FontWeight.Bold) +val boldFont = SpanStyle(fontWeight = FontWeight.Medium) fun appendGroupMember(b: AnnotatedString.Builder, chatItem: ChatItem, groupMemberBold: Boolean) { if (chatItem.chatDir is CIDirection.GroupRcv) { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt index fc2a85c646..caad7bdb1d 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatPreviewView.kt @@ -1,5 +1,6 @@ package chat.simplex.app.views.chatlist +import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape @@ -11,11 +12,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.ExperimentalTextApi import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import chat.simplex.app.model.Chat import chat.simplex.app.model.getTimestampText import chat.simplex.app.ui.theme.HighOrLowlight +import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.item.MarkdownText import chat.simplex.app.views.helpers.ChatInfoImage import chat.simplex.app.views.helpers.badgeLayout @@ -74,3 +77,17 @@ fun ChatPreviewView(chat: Chat) { } } } + +@ExperimentalTextApi +@Preview(showBackground = true) +@Preview( + uiMode = Configuration.UI_MODE_NIGHT_YES, + showBackground = true, + name = "Dark Mode" +) +@Composable +fun PreviewChatPreviewView() { + SimpleXTheme { + ChatPreviewView(Chat.sampleData) + } +} diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt index b882cca8b1..fcc225912e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt @@ -22,7 +22,7 @@ fun HelpView(chatModel: ChatModel, nav: NavController) { if (user != null) { HelpLayout( displayName = user.profile.displayName, - back = { nav.popBackStack() } + back = nav::popBackStack ) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt new file mode 100644 index 0000000000..d7e647e042 --- /dev/null +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt @@ -0,0 +1,110 @@ +package chat.simplex.app.views.usersettings + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.* +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import chat.simplex.app.model.Format +import chat.simplex.app.model.FormatColor +import chat.simplex.app.ui.theme.SimpleXTheme +import chat.simplex.app.views.helpers.CloseSheetBar + +@Composable +fun MarkdownHelpView(nav: NavController) { + MarkdownHelpLayout(nav::popBackStack) +} + +@Composable +fun MarkdownHelpLayout(back: () -> Unit) { + Surface( + Modifier + .background(MaterialTheme.colors.background) + .fillMaxSize() + ) { + Column { + CloseSheetBar(back) + Column(Modifier.padding(horizontal = 16.dp)) { + Text( + "How to use markdown", + style = MaterialTheme.typography.h1, + ) + Text( + "You can use markdown to format messages:", + Modifier.padding(vertical = 16.dp) + ) + MdFormat("*bold*", "bold text", Format.Bold()) + MdFormat("_italic_", "italic text", Format.Italic()) + MdFormat("~strike~", "strikethrough text", Format.StrikeThrough()) + MdFormat("`code`", "a = b + c", Format.Snippet()) + Row { + MdSyntax("!1 colored!") + Text(buildAnnotatedString { + withStyle(Format.Colored(FormatColor.red).style) { append("red text") } + append(" (") + appendColor(this, "1", FormatColor.red, ", ") + appendColor(this, "2", FormatColor.green, ", ") + appendColor(this, "3", FormatColor.blue, ", ") + appendColor(this, "4", FormatColor.yellow, ", ") + appendColor(this, "5", FormatColor.cyan, ", ") + appendColor(this, "6", FormatColor.magenta, ")") + }) + } + Row { + MdSyntax("#secret") + SelectionContainer { + Text(buildAnnotatedString { + withStyle(Format.Secret().style) { append("secret text") } + }) + } + } + } + } + } +} + +@Composable +fun MdSyntax(markdown: String) { + Text(markdown, Modifier + .width(100.dp) + .padding(bottom = 4.dp)) +} + +@Composable +fun MdFormat(markdown: String, example: String, format: Format) { + Row { + MdSyntax(markdown) + Text(buildAnnotatedString { + withStyle(format.style) { append(example) } + }) + } +} + +@Composable +fun appendColor(b: AnnotatedString.Builder, s: String, c: FormatColor, after: String) { + b.withStyle(Format.Colored(c).style) { append(s)} + b.append(after) +} + + +@Preview(showBackground = true) +@Preview( + uiMode = Configuration.UI_MODE_NIGHT_YES, + showBackground = true, + name = "Dark Mode" +) +@Composable +fun PreviewMarkdownHelpView() { + SimpleXTheme { + MarkdownHelpLayout(back = {}) + } +} diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt index a949d06307..9581ca62bc 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt @@ -1,6 +1,7 @@ package chat.simplex.app.views.usersettings import android.content.res.Configuration +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material.* @@ -43,26 +44,28 @@ fun SettingsLayout( navigate: (String) -> Unit ) { val uriHandler = LocalUriHandler.current - Column( + Surface( Modifier + .background(MaterialTheme.colors.background) .fillMaxSize() -// .background(MaterialTheme.colors.background) - .padding(8.dp) - .padding(top = 16.dp) ) { - Text( - "Your Settings", - style = MaterialTheme.typography.h1, - color = MaterialTheme.colors.onBackground - ) - Spacer(Modifier.height(30.dp)) + Column( + Modifier + .fillMaxSize() + .background(MaterialTheme.colors.background) + .padding(8.dp) + .padding(top = 16.dp) + ) { + Text( + "Your Settings", + style = MaterialTheme.typography.h1, + ) + Spacer(Modifier.height(30.dp)) - SettingsSectionView( - content = { + SettingsSectionView({ navigate(Pages.UserProfile.route) }, 60.dp) { Icon( Icons.Outlined.AccountCircle, contentDescription = "Avatar Placeholder", - tint = MaterialTheme.colors.onBackground, ) Spacer(Modifier.padding(horizontal = 4.dp)) Column { @@ -70,138 +73,102 @@ fun SettingsLayout( profile.displayName, style = MaterialTheme.typography.caption, fontWeight = FontWeight.Bold, - color = MaterialTheme.colors.onBackground - ) - Text( - profile.fullName, - color = MaterialTheme.colors.onBackground ) + Text(profile.fullName) } - }, - func = { navigate(Pages.UserProfile.route) }, - height = 60.dp - ) - Divider(Modifier.padding(horizontal = 8.dp)) - SettingsSectionView( - content = { + } + Divider(Modifier.padding(horizontal = 8.dp)) + SettingsSectionView({ navigate(Pages.UserAddress.route) }) { Icon( Icons.Outlined.QrCode, contentDescription = "Address", - tint = MaterialTheme.colors.onBackground, ) Spacer(Modifier.padding(horizontal = 4.dp)) - Text( - "Your SimpleX contact address", - color = MaterialTheme.colors.onBackground - ) - }, - func = { navigate(Pages.UserAddress.route) } - ) - Spacer(Modifier.height(24.dp)) + Text("Your SimpleX contact address") + } + Spacer(Modifier.height(24.dp)) - SettingsSectionView( - content = { + SettingsSectionView({ navigate(Pages.Help.route) }) { Icon( Icons.Outlined.HelpOutline, - contentDescription = "Help", - tint = MaterialTheme.colors.onBackground, + contentDescription = "Chat help", ) Spacer(Modifier.padding(horizontal = 4.dp)) - Text( - "How to use SimpleX Chat", - color = MaterialTheme.colors.onBackground + Text("How to use SimpleX Chat") + } + SettingsSectionView({ navigate(Pages.Markdown.route) }) { + Icon( + Icons.Outlined.TextFormat, + contentDescription = "Markdown help", ) - }, - func = { navigate(Pages.Help.route) } - ) - Divider(Modifier.padding(horizontal = 8.dp)) - SettingsSectionView( - content = { + Spacer(Modifier.padding(horizontal = 4.dp)) + Text("Markdown in messages") + } + Divider(Modifier.padding(horizontal = 8.dp)) + SettingsSectionView({ uriHandler.openUri(simplexTeamUri) }) { Icon( Icons.Outlined.Tag, contentDescription = "SimpleX Team", - tint = MaterialTheme.colors.onBackground, ) Spacer(Modifier.padding(horizontal = 4.dp)) Text( "Get help & advice via chat", color = MaterialTheme.colors.primary ) - }, - func = { uriHandler.openUri(simplexTeamUri) } - ) - Divider(Modifier.padding(horizontal = 8.dp)) - SettingsSectionView( - content = { + } + Divider(Modifier.padding(horizontal = 8.dp)) + SettingsSectionView({ uriHandler.openUri("mailto:chat@simplex.chat") }) { Icon( Icons.Outlined.Email, contentDescription = "Email", - tint = MaterialTheme.colors.onBackground, ) Spacer(Modifier.padding(horizontal = 4.dp)) Text( "Ask questions via email", color = MaterialTheme.colors.primary ) - }, - func = { uriHandler.openUri("mailto:chat@simplex.chat") } - ) - Spacer(Modifier.height(24.dp)) + } + Spacer(Modifier.height(24.dp)) - SettingsSectionView( - content = { + SettingsSectionView({ navigate(Pages.Terminal.route) }) { Icon( painter = painterResource(id = R.drawable.ic_outline_terminal), contentDescription = "Chat console", - tint = MaterialTheme.colors.onBackground, ) Spacer(Modifier.padding(horizontal = 4.dp)) - Text( - "Chat console", - color = MaterialTheme.colors.onBackground - ) - }, - func = { navigate(Pages.Terminal.route) } - ) - Divider(Modifier.padding(horizontal = 8.dp)) - SettingsSectionView( - content = { + Text("Chat console") + } + Divider(Modifier.padding(horizontal = 8.dp)) + SettingsSectionView({ uriHandler.openUri("https://github.com/simplex-chat/simplex-chat") }) { Icon( painter = painterResource(id = R.drawable.ic_github), contentDescription = "GitHub", - tint = MaterialTheme.colors.onBackground, ) Spacer(Modifier.padding(horizontal = 4.dp)) Text( buildAnnotatedString { - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append("Install ") - } + append("Install ") withStyle(SpanStyle(color = MaterialTheme.colors.primary)) { append("SimpleX Chat for terminal") } } ) - }, - func = { uriHandler.openUri("https://github.com/simplex-chat/simplex-chat") } - ) + } + } } } @Composable -fun SettingsSectionView(content: (@Composable () -> Unit), func: () -> Unit, height: Dp = 48.dp) { - Surface( - modifier = Modifier +fun SettingsSectionView(func: () -> Unit, height: Dp = 48.dp, content: (@Composable () -> Unit)) { + Row( + Modifier + .padding(start = 8.dp) .fillMaxWidth() .clickable(onClick = func) .height(height), + verticalAlignment = Alignment.CenterVertically ) { - Row( - Modifier.padding(start = 8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - content.invoke() - } + content.invoke() } }