mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 08:47:33 +00:00
feat: lean, color-coded redesign of the new-conversation chooser
Reworks the "Start a conversation" chooser to be scannable instead of a wall of text. Each conversation type is now a one-line row — a color-coded icon tile, the name, a short tagline, and a single chip naming its deciding axis (1:1–5 rooms / Device-bound / Workspaces / Unmoderated / Moderated / Live now). Tapping a row expands it in place to reveal "Best for" and a compact Good / Trade-offs split, then a Create button that routes to that type's existing flow (accordion: one open at a time). Copy now leads with each protocol's real differentiator — Marmot's device-bound gotcha (chats don't follow an nsec to another app), Concord as channel-split workspaces, and the relay-aware section grouping Public Chat + Relay Group by the fact the relay can see and moderate who's there. Per-type accent colors are lightened in dark theme for legibility; the solid icon tile keeps the saturated hue in both themes.
This commit is contained in:
+169
-128
@@ -21,27 +21,39 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
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.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
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.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.lerp
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -54,21 +66,34 @@ 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
|
||||
|
||||
// Per-type accent colors. Each protocol gets its own hue so private-vs-public reads at a glance
|
||||
// instead of via text. Used at full strength for the icon tile (white glyph on top) and, tint-mixed,
|
||||
// for the type's chip / expanded accents. In dark theme the accent is lightened for legibility on the
|
||||
// dark ground (see [ConversationRow]); the solid tile keeps the saturated base either way.
|
||||
private val ColorPrivate = Color(0xFF7C3AED)
|
||||
private val ColorMarmot = Color(0xFF4F46E5)
|
||||
private val ColorConcord = Color(0xFF0D9488)
|
||||
private val ColorPublic = Color(0xFFD97706)
|
||||
private val ColorRelay = Color(0xFF2563EB)
|
||||
private val ColorEphemeral = Color(0xFFEA580C)
|
||||
|
||||
/**
|
||||
* 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
|
||||
* One selectable conversation type. Collapsed, a row shows only the icon, name, a short tagline, and
|
||||
* a one-word [chip] naming its deciding axis (scale / device-bound / moderation / live). Tapping the
|
||||
* row expands it to reveal [bestFor] and the [pros]/[cons] before the [cta] button routes to that
|
||||
* type's existing creation (or browse) flow.
|
||||
*/
|
||||
private class ConversationType(
|
||||
val icon: MaterialSymbol,
|
||||
val color: Color,
|
||||
@StringRes val title: Int,
|
||||
@StringRes val tagline: Int,
|
||||
@StringRes val chip: Int,
|
||||
@StringRes val bestFor: Int,
|
||||
@StringRes val cta: Int,
|
||||
val pros: List<Int>,
|
||||
val cons: List<Int>,
|
||||
val route: Route,
|
||||
@@ -79,8 +104,8 @@ private class ConversationSection(
|
||||
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.
|
||||
// Grouped by intent so the six protocols read as three simple buckets: talk privately, run an
|
||||
// encrypted group, or use a relay-aware room (where the relay can see/moderate who's there).
|
||||
private val conversationSections =
|
||||
listOf(
|
||||
ConversationSection(
|
||||
@@ -89,20 +114,14 @@ private val conversationSections =
|
||||
listOf(
|
||||
ConversationType(
|
||||
icon = MaterialSymbols.Mail,
|
||||
color = ColorPrivate,
|
||||
title = R.string.new_conversation_dm_title,
|
||||
tagline = R.string.new_conversation_dm_tagline,
|
||||
chip = R.string.new_conversation_dm_chip,
|
||||
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,
|
||||
),
|
||||
cta = R.string.new_conversation_dm_cta,
|
||||
pros = listOf(R.string.new_conversation_dm_pro_1, R.string.new_conversation_dm_pro_2),
|
||||
cons = listOf(R.string.new_conversation_dm_con_1),
|
||||
route = Route.NewGroupDM(),
|
||||
),
|
||||
),
|
||||
@@ -113,95 +132,68 @@ private val conversationSections =
|
||||
listOf(
|
||||
ConversationType(
|
||||
icon = MaterialSymbols.Lock,
|
||||
color = ColorMarmot,
|
||||
title = R.string.new_conversation_marmot_title,
|
||||
tagline = R.string.new_conversation_marmot_tagline,
|
||||
chip = R.string.new_conversation_marmot_chip,
|
||||
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,
|
||||
),
|
||||
cta = R.string.new_conversation_marmot_cta,
|
||||
pros = listOf(R.string.new_conversation_marmot_pro_1, R.string.new_conversation_marmot_pro_2),
|
||||
cons = listOf(R.string.new_conversation_marmot_con_1),
|
||||
route = Route.CreateMarmotGroup,
|
||||
),
|
||||
ConversationType(
|
||||
icon = MaterialSymbols.Groups,
|
||||
color = ColorConcord,
|
||||
title = R.string.new_conversation_concord_title,
|
||||
tagline = R.string.new_conversation_concord_tagline,
|
||||
chip = R.string.new_conversation_concord_chip,
|
||||
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,
|
||||
),
|
||||
cta = R.string.new_conversation_concord_cta,
|
||||
pros = listOf(R.string.new_conversation_concord_pro_1, R.string.new_conversation_concord_pro_2),
|
||||
cons = listOf(R.string.new_conversation_concord_con_1),
|
||||
route = Route.ConcordCreate,
|
||||
),
|
||||
),
|
||||
),
|
||||
ConversationSection(
|
||||
header = R.string.new_conversation_section_public,
|
||||
header = R.string.new_conversation_section_relay,
|
||||
types =
|
||||
listOf(
|
||||
ConversationType(
|
||||
icon = MaterialSymbols.Public,
|
||||
color = ColorPublic,
|
||||
title = R.string.new_conversation_public_chat_title,
|
||||
tagline = R.string.new_conversation_public_chat_tagline,
|
||||
chip = R.string.new_conversation_public_chat_chip,
|
||||
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,
|
||||
),
|
||||
cta = R.string.new_conversation_public_chat_cta,
|
||||
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,
|
||||
color = ColorRelay,
|
||||
title = R.string.new_conversation_relay_group_title,
|
||||
tagline = R.string.new_conversation_relay_group_tagline,
|
||||
chip = R.string.new_conversation_relay_group_chip,
|
||||
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,
|
||||
),
|
||||
cta = R.string.new_conversation_relay_group_cta,
|
||||
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),
|
||||
route = Route.RelayGroupBrowse,
|
||||
),
|
||||
ConversationType(
|
||||
icon = MaterialSymbols.Timer,
|
||||
color = ColorEphemeral,
|
||||
title = R.string.new_conversation_ephemeral_title,
|
||||
tagline = R.string.new_conversation_ephemeral_tagline,
|
||||
chip = R.string.new_conversation_ephemeral_chip,
|
||||
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,
|
||||
),
|
||||
cta = R.string.new_conversation_ephemeral_cta,
|
||||
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,
|
||||
),
|
||||
),
|
||||
@@ -210,13 +202,15 @@ private val conversationSections =
|
||||
|
||||
@Composable
|
||||
fun NewConversationScreen(nav: INav) {
|
||||
// Accordion: at most one row expanded, keyed by its (unique) title resource id. Survives config
|
||||
// changes so an opened card stays open on rotation.
|
||||
var expandedId by rememberSaveable { mutableStateOf(0) }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBarWithBackButton(stringRes(R.string.new_conversation_title), nav)
|
||||
},
|
||||
topBar = { TopBarWithBackButton(stringRes(R.string.new_conversation_title), nav) },
|
||||
) { pad ->
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentPadding =
|
||||
PaddingValues(
|
||||
start = 12.dp,
|
||||
@@ -224,31 +218,27 @@ fun NewConversationScreen(nav: INav) {
|
||||
top = pad.calculateTopPadding() + 4.dp,
|
||||
bottom = pad.calculateBottomPadding() + 16.dp,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(9.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 {
|
||||
item(key = section.header) {
|
||||
Text(
|
||||
text = stringRes(section.header),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.padding(start = 4.dp, top = 6.dp),
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
modifier = Modifier.padding(start = 6.dp, top = 8.dp),
|
||||
)
|
||||
}
|
||||
|
||||
section.types.forEach { type ->
|
||||
item(key = type.title) {
|
||||
ConversationTypeCard(type) { nav.nav(type.route) }
|
||||
ConversationRow(
|
||||
type = type,
|
||||
expanded = expandedId == type.title,
|
||||
onToggle = { expandedId = if (expandedId == type.title) 0 else type.title },
|
||||
onCreate = { nav.nav(type.route) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,33 +247,39 @@ fun NewConversationScreen(nav: INav) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ConversationTypeCard(
|
||||
private fun ConversationRow(
|
||||
type: ConversationType,
|
||||
onClick: () -> Unit,
|
||||
expanded: Boolean,
|
||||
onToggle: () -> Unit,
|
||||
onCreate: () -> Unit,
|
||||
) {
|
||||
// Lighten the accent in dark mode so chip/label/checkmark text stays legible on the dark ground;
|
||||
// the solid icon tile keeps the saturated base color in both themes.
|
||||
val accent = if (isSystemInDarkTheme()) lerp(type.color, Color.White, 0.42f) else type.color
|
||||
|
||||
ElevatedCard(
|
||||
onClick = onClick,
|
||||
onClick = onToggle,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp),
|
||||
) {
|
||||
Column(Modifier.padding(16.dp)) {
|
||||
Column(Modifier.animateContentSize().padding(13.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.primaryContainer,
|
||||
shape = RoundedCornerShape(13.dp),
|
||||
color = type.color,
|
||||
modifier = Modifier.size(44.dp),
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Icon(
|
||||
symbol = type.icon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.size(14.dp))
|
||||
Spacer(Modifier.width(13.dp))
|
||||
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(
|
||||
@@ -298,54 +294,99 @@ private fun ConversationTypeCard(
|
||||
)
|
||||
}
|
||||
|
||||
Icon(
|
||||
symbol = MaterialSymbols.AutoMirrored.KeyboardArrowRight,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.grayText,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
|
||||
AxisChip(stringRes(type.chip), accent)
|
||||
}
|
||||
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
if (expanded) {
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(top = 13.dp, bottom = 12.dp),
|
||||
color = accent.copy(alpha = 0.22f),
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
Column {
|
||||
ColumnHeader(stringRes(R.string.new_conversation_best_for))
|
||||
Text(
|
||||
text = stringRes(type.bestFor),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
modifier = Modifier.padding(top = 3.dp, bottom = 12.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(14.dp)) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
ColumnHeader(stringRes(R.string.new_conversation_pros))
|
||||
Spacer(Modifier.height(4.dp))
|
||||
type.pros.forEach { ProConRow(it, accent, isPro = true) }
|
||||
}
|
||||
Column(Modifier.weight(1f)) {
|
||||
ColumnHeader(stringRes(R.string.new_conversation_cons))
|
||||
Spacer(Modifier.height(4.dp))
|
||||
type.cons.forEach { ProConRow(it, accent, isPro = false) }
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = onCreate,
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 13.dp),
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
colors = ButtonDefaults.buttonColors(containerColor = type.color, contentColor = Color.White),
|
||||
) {
|
||||
Text(stringRes(type.cta), fontWeight = FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
|
||||
type.pros.forEach { ProConRow(it, isPro = true) }
|
||||
type.cons.forEach { ProConRow(it, isPro = false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AxisChip(
|
||||
label: String,
|
||||
accent: Color,
|
||||
) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(999.dp),
|
||||
color = accent.copy(alpha = 0.14f),
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = accent,
|
||||
modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnHeader(text: String) {
|
||||
Text(
|
||||
text = text.uppercase(),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProConRow(
|
||||
@StringRes text: Int,
|
||||
accent: Color,
|
||||
isPro: Boolean,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.padding(vertical = 1.dp),
|
||||
verticalAlignment = Alignment.Top,
|
||||
modifier = Modifier.padding(vertical = 2.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),
|
||||
tint = if (isPro) accent else MaterialTheme.colorScheme.grayText,
|
||||
modifier = Modifier.size(15.dp).padding(top = 1.dp),
|
||||
)
|
||||
Spacer(Modifier.size(8.dp))
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Text(
|
||||
text = stringRes(text),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
|
||||
@@ -2050,64 +2050,71 @@
|
||||
<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) -->
|
||||
<!-- New conversation chooser (full-screen picker opened from the Messages FAB).
|
||||
Each row stays lean; the "best for" line and the pros/cons appear only when a row is tapped. -->
|
||||
<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_pros">Good</string>
|
||||
<string name="new_conversation_cons">Trade-offs</string>
|
||||
<string name="new_conversation_section_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_section_relay">Relay-aware</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_dm_tagline">Direct messages, end-to-end encrypted</string>
|
||||
<string name="new_conversation_dm_chip">1:1–5 rooms</string>
|
||||
<string name="new_conversation_dm_best">One-on-one and small group chats with people you know.</string>
|
||||
<string name="new_conversation_dm_cta">New message</string>
|
||||
<string name="new_conversation_dm_pro_1">1-on-1 & small groups</string>
|
||||
<string name="new_conversation_dm_pro_2">End-to-end encrypted</string>
|
||||
<string name="new_conversation_dm_con_1">Not for big groups</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_marmot_tagline">Bigger private groups</string>
|
||||
<string name="new_conversation_marmot_chip">Device-bound</string>
|
||||
<string name="new_conversation_marmot_best">Larger private groups that outgrow small-group DMs.</string>
|
||||
<string name="new_conversation_marmot_cta">Create group</string>
|
||||
<string name="new_conversation_marmot_pro_1">Scales past small DMs</string>
|
||||
<string name="new_conversation_marmot_pro_2">Encrypted group chat</string>
|
||||
<string name="new_conversation_marmot_con_1">Device-bound — won\'t appear if you sign in elsewhere</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_tagline">Communities split by channel</string>
|
||||
<string name="new_conversation_concord_chip">Workspaces</string>
|
||||
<string name="new_conversation_concord_best">Massive communities, organized by channels or workstreams.</string>
|
||||
<string name="new_conversation_concord_cta">Create community</string>
|
||||
<string name="new_conversation_concord_pro_1">Scales to huge communities</string>
|
||||
<string name="new_conversation_concord_pro_2">Split by channel / workstream</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_tagline">Public messages on a relay</string>
|
||||
<string name="new_conversation_public_chat_chip">Unmoderated</string>
|
||||
<string name="new_conversation_public_chat_best">Open public rooms where anyone can post.</string>
|
||||
<string name="new_conversation_public_chat_cta">Create public chat</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_public_chat_pro_2">Simple & discoverable</string>
|
||||
<string name="new_conversation_public_chat_con_1">Public only</string>
|
||||
<string name="new_conversation_public_chat_con_2">No 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_relay_group_tagline">Relay-run, public or private</string>
|
||||
<string name="new_conversation_relay_group_chip">Moderated</string>
|
||||
<string name="new_conversation_relay_group_best">Groups moderated by the relay that hosts them.</string>
|
||||
<string name="new_conversation_relay_group_cta">Browse relay groups</string>
|
||||
<string name="new_conversation_relay_group_pro_1">Moderated by the relay</string>
|
||||
<string name="new_conversation_relay_group_pro_2">Public or private</string>
|
||||
<string name="new_conversation_relay_group_con_1">Tied to that one relay</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="new_conversation_ephemeral_tagline">Whoever\'s online, right now</string>
|
||||
<string name="new_conversation_ephemeral_chip">Live now</string>
|
||||
<string name="new_conversation_ephemeral_best">In-the-moment chat with whoever is online right now.</string>
|
||||
<string name="new_conversation_ephemeral_cta">Start chat</string>
|
||||
<string name="new_conversation_ephemeral_pro_1">Talk to who\'s online now</string>
|
||||
<string name="new_conversation_ephemeral_pro_2">Nothing is saved</string>
|
||||
<string name="new_conversation_ephemeral_con_1">No history</string>
|
||||
<string name="new_conversation_ephemeral_con_2">Relay-scoped</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