mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-12 01:07:46 +00:00
feat: replace Messages FAB speed-dial with a full-screen conversation chooser
The Messages "+" button used to fan out four cryptic one-word FABs (Private / Public / Group / Find groups). The labels didn't explain what each protocol does, and two conversation types (Disappearing Chat and Concord communities) had no entry point at all. Replace the speed-dial with a single "+" that opens a new full-screen "Start a conversation" chooser. It groups the six conversation types into three intent buckets — Direct & private, Encrypted groups, Public & open — and presents each as a card with an icon, a one-line tagline, a "Best for" hint, and short pros/cons lists, so users can pick the right message protocol for what they're building. Each card routes to that type's existing creation (or browse) flow. - New Route.NewConversation + NewConversationScreen - ChannelFabColumn simplified to a single button opening the chooser - Adds the previously-unreachable Disappearing Chat and Concord entries
This commit is contained in:
@@ -132,6 +132,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayG
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.RelayGroupMembersScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.RelayGroupThreadsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.MessagesScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.NewConversationScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.share.ShareToDMScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessGameScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessLobbyScreen
|
||||
@@ -744,6 +745,7 @@ fun BuildNavigation(
|
||||
|
||||
composableFromBottomArgs<Route.ChannelMetadataEdit> { ChannelMetadataScreen(it.id, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.NewEphemeralChat> { NewEphemeralChatScreen(accountViewModel, nav) }
|
||||
composableFromBottom<Route.NewConversation> { NewConversationScreen(nav) }
|
||||
composableFromBottomArgs<Route.NewGroupDM> { NewGroupDMScreen(it.message, it.attachment, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.ShareToDM> { ShareToDMScreen(it.message, it.attachment, accountViewModel, nav) }
|
||||
|
||||
|
||||
@@ -748,6 +748,11 @@ sealed class Route {
|
||||
val attachment: String? = null,
|
||||
) : Route()
|
||||
|
||||
// Full-screen chooser opened from the Messages FAB: explains each conversation type
|
||||
// (DM, Marmot group, Concord, public chat, relay group, disappearing chat) with its
|
||||
// pros/cons and routes to that type's creation flow.
|
||||
@Serializable object NewConversation : Route()
|
||||
|
||||
@Serializable data class ShareToDM(
|
||||
val message: String? = null,
|
||||
val attachment: String? = null,
|
||||
|
||||
+17
-124
@@ -20,148 +20,41 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
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.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route.NewGroupDM
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
|
||||
/**
|
||||
* The Messages "+" button. Instead of a speed-dial that fanned out several cryptic one-word FABs
|
||||
* (Private / Public / Group / Find groups), a single button opens the full-screen
|
||||
* [NewConversationScreen] chooser, which explains every conversation type and its trade-offs before
|
||||
* the user commits to one.
|
||||
*/
|
||||
@Composable
|
||||
fun ChannelFabColumn(nav: INav) {
|
||||
var isOpen by remember { mutableStateOf(false) }
|
||||
|
||||
Column {
|
||||
AnimatedVisibility(
|
||||
visible = isOpen,
|
||||
enter = slideInVertically(initialOffsetY = { it / 2 }) + fadeIn(),
|
||||
exit = slideOutVertically(targetOffsetY = { it / 2 }) + fadeOut(),
|
||||
) {
|
||||
Column {
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
nav.nav(NewGroupDM())
|
||||
isOpen = false
|
||||
},
|
||||
modifier = Size55Modifier,
|
||||
shape = CircleShape,
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.messages_new_message),
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = Font12SP,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
nav.nav(Route.ChannelMetadataEdit())
|
||||
isOpen = false
|
||||
},
|
||||
modifier = Size55Modifier,
|
||||
shape = CircleShape,
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.messages_create_public_chat),
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = Font12SP,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
nav.nav(Route.CreateMarmotGroup)
|
||||
isOpen = false
|
||||
},
|
||||
modifier = Size55Modifier,
|
||||
shape = CircleShape,
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.messages_create_group),
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = Font12SP,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
nav.nav(Route.RelayGroupBrowse)
|
||||
isOpen = false
|
||||
},
|
||||
modifier = Size55Modifier,
|
||||
shape = CircleShape,
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.relay_group_browse_title),
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = Font12SP,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
}
|
||||
}
|
||||
|
||||
val rotationDegree by animateFloatAsState(
|
||||
targetValue = if (isOpen) 45f else 0f,
|
||||
FloatingActionButton(
|
||||
onClick = { nav.nav(Route.NewConversation) },
|
||||
modifier = Size55Modifier,
|
||||
shape = CircleShape,
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.Add,
|
||||
contentDescription = stringRes(R.string.messages_create_public_private_chat_description),
|
||||
modifier = Modifier.size(26.dp),
|
||||
tint = Color.White,
|
||||
)
|
||||
|
||||
FloatingActionButton(
|
||||
onClick = { isOpen = !isOpen },
|
||||
modifier = Size55Modifier,
|
||||
shape = CircleShape,
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.Add,
|
||||
contentDescription = stringRes(R.string.messages_create_public_private_chat_description),
|
||||
modifier =
|
||||
Modifier.size(26.dp).graphicsLayer {
|
||||
rotationZ = rotationDegree
|
||||
},
|
||||
tint = Color.White,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+362
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.ui.theme.grayText
|
||||
|
||||
/**
|
||||
* One selectable conversation type on the [NewConversationScreen] chooser: an icon, a name, a
|
||||
* one-line tagline, a "best for" hint, and short pros/cons lists. Tapping the card routes to that
|
||||
* type's existing creation (or browse) flow.
|
||||
*/
|
||||
private class ConversationType(
|
||||
val icon: MaterialSymbol,
|
||||
@StringRes val title: Int,
|
||||
@StringRes val tagline: Int,
|
||||
@StringRes val bestFor: Int,
|
||||
val pros: List<Int>,
|
||||
val cons: List<Int>,
|
||||
val route: Route,
|
||||
)
|
||||
|
||||
private class ConversationSection(
|
||||
@StringRes val header: Int,
|
||||
val types: List<ConversationType>,
|
||||
)
|
||||
|
||||
// Grouped by intent so the six protocols read as three simple buckets instead of a flat wall of
|
||||
// options: talk privately, run an encrypted group, or open something to the public.
|
||||
private val conversationSections =
|
||||
listOf(
|
||||
ConversationSection(
|
||||
header = R.string.new_conversation_section_direct,
|
||||
types =
|
||||
listOf(
|
||||
ConversationType(
|
||||
icon = MaterialSymbols.Mail,
|
||||
title = R.string.new_conversation_dm_title,
|
||||
tagline = R.string.new_conversation_dm_tagline,
|
||||
bestFor = R.string.new_conversation_dm_best,
|
||||
pros =
|
||||
listOf(
|
||||
R.string.new_conversation_dm_pro_1,
|
||||
R.string.new_conversation_dm_pro_2,
|
||||
R.string.new_conversation_dm_pro_3,
|
||||
),
|
||||
cons =
|
||||
listOf(
|
||||
R.string.new_conversation_dm_con_1,
|
||||
R.string.new_conversation_dm_con_2,
|
||||
),
|
||||
route = Route.NewGroupDM(),
|
||||
),
|
||||
),
|
||||
),
|
||||
ConversationSection(
|
||||
header = R.string.new_conversation_section_encrypted,
|
||||
types =
|
||||
listOf(
|
||||
ConversationType(
|
||||
icon = MaterialSymbols.Lock,
|
||||
title = R.string.new_conversation_marmot_title,
|
||||
tagline = R.string.new_conversation_marmot_tagline,
|
||||
bestFor = R.string.new_conversation_marmot_best,
|
||||
pros =
|
||||
listOf(
|
||||
R.string.new_conversation_marmot_pro_1,
|
||||
R.string.new_conversation_marmot_pro_2,
|
||||
R.string.new_conversation_marmot_pro_3,
|
||||
),
|
||||
cons =
|
||||
listOf(
|
||||
R.string.new_conversation_marmot_con_1,
|
||||
R.string.new_conversation_marmot_con_2,
|
||||
),
|
||||
route = Route.CreateMarmotGroup,
|
||||
),
|
||||
ConversationType(
|
||||
icon = MaterialSymbols.Groups,
|
||||
title = R.string.new_conversation_concord_title,
|
||||
tagline = R.string.new_conversation_concord_tagline,
|
||||
bestFor = R.string.new_conversation_concord_best,
|
||||
pros =
|
||||
listOf(
|
||||
R.string.new_conversation_concord_pro_1,
|
||||
R.string.new_conversation_concord_pro_2,
|
||||
R.string.new_conversation_concord_pro_3,
|
||||
),
|
||||
cons =
|
||||
listOf(
|
||||
R.string.new_conversation_concord_con_1,
|
||||
R.string.new_conversation_concord_con_2,
|
||||
),
|
||||
route = Route.ConcordCreate,
|
||||
),
|
||||
),
|
||||
),
|
||||
ConversationSection(
|
||||
header = R.string.new_conversation_section_public,
|
||||
types =
|
||||
listOf(
|
||||
ConversationType(
|
||||
icon = MaterialSymbols.Public,
|
||||
title = R.string.new_conversation_public_chat_title,
|
||||
tagline = R.string.new_conversation_public_chat_tagline,
|
||||
bestFor = R.string.new_conversation_public_chat_best,
|
||||
pros =
|
||||
listOf(
|
||||
R.string.new_conversation_public_chat_pro_1,
|
||||
R.string.new_conversation_public_chat_pro_2,
|
||||
),
|
||||
cons =
|
||||
listOf(
|
||||
R.string.new_conversation_public_chat_con_1,
|
||||
R.string.new_conversation_public_chat_con_2,
|
||||
),
|
||||
route = Route.ChannelMetadataEdit(),
|
||||
),
|
||||
ConversationType(
|
||||
icon = MaterialSymbols.Dns,
|
||||
title = R.string.new_conversation_relay_group_title,
|
||||
tagline = R.string.new_conversation_relay_group_tagline,
|
||||
bestFor = R.string.new_conversation_relay_group_best,
|
||||
pros =
|
||||
listOf(
|
||||
R.string.new_conversation_relay_group_pro_1,
|
||||
R.string.new_conversation_relay_group_pro_2,
|
||||
),
|
||||
cons =
|
||||
listOf(
|
||||
R.string.new_conversation_relay_group_con_1,
|
||||
R.string.new_conversation_relay_group_con_2,
|
||||
),
|
||||
route = Route.RelayGroupBrowse,
|
||||
),
|
||||
ConversationType(
|
||||
icon = MaterialSymbols.Timer,
|
||||
title = R.string.new_conversation_ephemeral_title,
|
||||
tagline = R.string.new_conversation_ephemeral_tagline,
|
||||
bestFor = R.string.new_conversation_ephemeral_best,
|
||||
pros =
|
||||
listOf(
|
||||
R.string.new_conversation_ephemeral_pro_1,
|
||||
R.string.new_conversation_ephemeral_pro_2,
|
||||
),
|
||||
cons =
|
||||
listOf(
|
||||
R.string.new_conversation_ephemeral_con_1,
|
||||
R.string.new_conversation_ephemeral_con_2,
|
||||
),
|
||||
route = Route.NewEphemeralChat,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun NewConversationScreen(nav: INav) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBarWithBackButton(stringRes(R.string.new_conversation_title), nav)
|
||||
},
|
||||
) { pad ->
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding =
|
||||
PaddingValues(
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
top = pad.calculateTopPadding() + 4.dp,
|
||||
bottom = pad.calculateBottomPadding() + 16.dp,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = stringRes(R.string.new_conversation_intro),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
modifier = Modifier.padding(horizontal = 4.dp, vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
|
||||
conversationSections.forEach { section ->
|
||||
item {
|
||||
Text(
|
||||
text = stringRes(section.header),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.padding(start = 4.dp, top = 6.dp),
|
||||
)
|
||||
}
|
||||
|
||||
section.types.forEach { type ->
|
||||
item(key = type.title) {
|
||||
ConversationTypeCard(type) { nav.nav(type.route) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ConversationTypeCard(
|
||||
type: ConversationType,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
ElevatedCard(
|
||||
onClick = onClick,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp),
|
||||
) {
|
||||
Column(Modifier.padding(16.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.primaryContainer,
|
||||
modifier = Modifier.size(44.dp),
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Icon(
|
||||
symbol = type.icon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.size(14.dp))
|
||||
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = stringRes(type.title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
Text(
|
||||
text = stringRes(type.tagline),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
)
|
||||
}
|
||||
|
||||
Icon(
|
||||
symbol = MaterialSymbols.AutoMirrored.KeyboardArrowRight,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.grayText,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
|
||||
Row {
|
||||
Text(
|
||||
text = "${stringRes(R.string.new_conversation_best_for)}: ",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Text(
|
||||
text = stringRes(type.bestFor),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
|
||||
type.pros.forEach { ProConRow(it, isPro = true) }
|
||||
type.cons.forEach { ProConRow(it, isPro = false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProConRow(
|
||||
@StringRes text: Int,
|
||||
isPro: Boolean,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.padding(vertical = 1.dp),
|
||||
) {
|
||||
Icon(
|
||||
symbol = if (isPro) MaterialSymbols.Check else MaterialSymbols.Close,
|
||||
contentDescription = null,
|
||||
tint = if (isPro) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.grayText,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
Spacer(Modifier.size(8.dp))
|
||||
Text(
|
||||
text = stringRes(text),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun NewConversationScreenPreview() {
|
||||
ThemeComparisonColumn {
|
||||
NewConversationScreen(nav = EmptyNav())
|
||||
}
|
||||
}
|
||||
@@ -2049,6 +2049,65 @@
|
||||
<string name="messages_create_public_chat">Public</string>
|
||||
<string name="messages_create_group">Group</string>
|
||||
<string name="messages_create_public_private_chat_description">New Public or Private Group</string>
|
||||
|
||||
<!-- New conversation chooser (full-screen picker opened from the Messages FAB) -->
|
||||
<string name="new_conversation_title">Start a conversation</string>
|
||||
<string name="new_conversation_intro">Pick the kind of conversation that fits what you need. They differ in who can join, how private they are, and where the messages live.</string>
|
||||
<string name="new_conversation_best_for">Best for</string>
|
||||
<string name="new_conversation_section_direct">Direct & private</string>
|
||||
<string name="new_conversation_section_encrypted">Encrypted groups</string>
|
||||
<string name="new_conversation_section_public">Public & open</string>
|
||||
|
||||
<string name="new_conversation_dm_title">Private Message</string>
|
||||
<string name="new_conversation_dm_tagline">Encrypted direct messages with one person or a small group.</string>
|
||||
<string name="new_conversation_dm_best">Talking privately with people you already know.</string>
|
||||
<string name="new_conversation_dm_pro_1">End-to-end encrypted</string>
|
||||
<string name="new_conversation_dm_pro_2">Hides who you\'re talking to</string>
|
||||
<string name="new_conversation_dm_pro_3">Works across all your relays</string>
|
||||
<string name="new_conversation_dm_con_1">Best for small groups</string>
|
||||
<string name="new_conversation_dm_con_2">No shared admins or moderation</string>
|
||||
|
||||
<string name="new_conversation_marmot_title">Marmot Group</string>
|
||||
<string name="new_conversation_marmot_tagline">Strongly encrypted group chat built on the MLS standard.</string>
|
||||
<string name="new_conversation_marmot_best">A private team or friend group that needs the strongest encryption.</string>
|
||||
<string name="new_conversation_marmot_pro_1">Forward-secret group encryption</string>
|
||||
<string name="new_conversation_marmot_pro_2">Relays can\'t read the messages</string>
|
||||
<string name="new_conversation_marmot_pro_3">Controlled membership</string>
|
||||
<string name="new_conversation_marmot_con_1">Newer, still maturing</string>
|
||||
<string name="new_conversation_marmot_con_2">Everyone needs a Marmot-capable app</string>
|
||||
|
||||
<string name="new_conversation_concord_title">Concord Community</string>
|
||||
<string name="new_conversation_concord_tagline">An encrypted community with multiple channels, like a private server.</string>
|
||||
<string name="new_conversation_concord_best">Running a community or team with topic channels and roles.</string>
|
||||
<string name="new_conversation_concord_pro_1">Many channels and roles</string>
|
||||
<string name="new_conversation_concord_pro_2">Encrypted, with invite links</string>
|
||||
<string name="new_conversation_concord_pro_3">Scales to larger groups</string>
|
||||
<string name="new_conversation_concord_con_1">More to set up</string>
|
||||
<string name="new_conversation_concord_con_2">Members need a Concord-capable app</string>
|
||||
|
||||
<string name="new_conversation_public_chat_title">Public Chat</string>
|
||||
<string name="new_conversation_public_chat_tagline">An open channel anyone can find, read, and join.</string>
|
||||
<string name="new_conversation_public_chat_best">A public topic room open to everyone.</string>
|
||||
<string name="new_conversation_public_chat_pro_1">Anyone can join</string>
|
||||
<string name="new_conversation_public_chat_pro_2">Discoverable and simple</string>
|
||||
<string name="new_conversation_public_chat_con_1">Not private — messages are public</string>
|
||||
<string name="new_conversation_public_chat_con_2">Limited moderation</string>
|
||||
|
||||
<string name="new_conversation_relay_group_title">Relay Group</string>
|
||||
<string name="new_conversation_relay_group_tagline">A group hosted and moderated by a specific relay.</string>
|
||||
<string name="new_conversation_relay_group_best">A managed group with admins, roles, and moderation.</string>
|
||||
<string name="new_conversation_relay_group_pro_1">Real moderation and roles</string>
|
||||
<string name="new_conversation_relay_group_pro_2">Can be open or invite-only</string>
|
||||
<string name="new_conversation_relay_group_con_1">Lives on a single relay</string>
|
||||
<string name="new_conversation_relay_group_con_2">That relay can see the group</string>
|
||||
|
||||
<string name="new_conversation_ephemeral_title">Disappearing Chat</string>
|
||||
<string name="new_conversation_ephemeral_tagline">A temporary room tied to a relay — messages aren\'t stored.</string>
|
||||
<string name="new_conversation_ephemeral_best">Quick, in-the-moment chats you don\'t want kept.</string>
|
||||
<string name="new_conversation_ephemeral_pro_1">Nothing is saved</string>
|
||||
<string name="new_conversation_ephemeral_pro_2">Lightweight and instant</string>
|
||||
<string name="new_conversation_ephemeral_con_1">History disappears</string>
|
||||
<string name="new_conversation_ephemeral_con_2">Only people on that relay see it</string>
|
||||
<string name="relay_groups_title">Relay Groups</string>
|
||||
<string name="relay_group_view_inline">Inline</string>
|
||||
<string name="relay_group_view_grouped">By relay</string>
|
||||
|
||||
Reference in New Issue
Block a user