Reorganize share UI with Easy Share on top
Move Easy Share (.casera file) section above Share Code section in the invite users dialog. Both platforms now have consistent UI with both buttons using the same filled button style. iOS changes: - Move share functionality into ManageUsersView - Remove share button from ResidenceDetailView toolbar - Redesign ShareCodeCard with Easy Share on top Android changes: - Update ManageUsersDialog with matching layout - Connect share package callback to existing share function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,13 +5,21 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.ContentCopy
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.PersonAdd
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.casera.models.ResidenceUser
|
||||
import com.example.casera.models.ResidenceShareCode
|
||||
import com.example.casera.network.ApiResult
|
||||
@@ -26,7 +34,8 @@ fun ManageUsersDialog(
|
||||
isPrimaryOwner: Boolean,
|
||||
residenceOwnerId: Int,
|
||||
onDismiss: () -> Unit,
|
||||
onUserRemoved: () -> Unit = {}
|
||||
onUserRemoved: () -> Unit = {},
|
||||
onSharePackage: () -> Unit = {}
|
||||
) {
|
||||
var users by remember { mutableStateOf<List<ResidenceUser>>(emptyList()) }
|
||||
val ownerId = residenceOwnerId
|
||||
@@ -37,6 +46,7 @@ fun ManageUsersDialog(
|
||||
|
||||
val residenceApi = remember { ResidenceApi() }
|
||||
val scope = rememberCoroutineScope()
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
|
||||
// Load users
|
||||
LaunchedEffect(residenceId) {
|
||||
@@ -69,7 +79,16 @@ fun ManageUsersDialog(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text("Manage Users")
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
Icons.Default.PersonAdd,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(28.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text("Invite Others")
|
||||
}
|
||||
IconButton(onClick = onDismiss) {
|
||||
Icon(Icons.Default.Close, "Close")
|
||||
}
|
||||
@@ -91,70 +110,147 @@ fun ManageUsersDialog(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
} else {
|
||||
// Share code section (primary owner only)
|
||||
// Share sections (primary owner only)
|
||||
if (isPrimaryOwner) {
|
||||
// Easy Share section (on top - recommended)
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp),
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = "Easy Share",
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Button(
|
||||
onClick = { onSharePackage() },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Icon(Icons.Default.Share, "Share", modifier = Modifier.size(18.dp))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text("Send Invite Link")
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "Send a .casera file via Messages, Email, or share. They just tap to join.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(top = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Divider with "or"
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
HorizontalDivider(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
text = "or",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
)
|
||||
HorizontalDivider(modifier = Modifier.weight(1f))
|
||||
}
|
||||
|
||||
// Share Code section
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = "Share Code",
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
if (shareCode != null) {
|
||||
Text(
|
||||
text = "Share Code",
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
text = shareCode!!.code,
|
||||
style = MaterialTheme.typography.headlineMedium.copy(
|
||||
fontFamily = FontFamily.Monospace,
|
||||
letterSpacing = 4.sp
|
||||
),
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
if (shareCode != null) {
|
||||
Text(
|
||||
text = shareCode!!.code,
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = "No active code",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
IconButton(
|
||||
onClick = {
|
||||
clipboardManager.setText(AnnotatedString(shareCode!!.code))
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.ContentCopy,
|
||||
contentDescription = "Copy code",
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
text = "No active code",
|
||||
style = MaterialTheme.typography.bodyMedium.copy(
|
||||
fontStyle = FontStyle.Italic
|
||||
),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
FilledTonalButton(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
isGeneratingCode = true
|
||||
val token = TokenStorage.getToken()
|
||||
if (token != null) {
|
||||
when (val result = residenceApi.generateShareCode(token, residenceId)) {
|
||||
is ApiResult.Success -> {
|
||||
shareCode = result.data.shareCode
|
||||
}
|
||||
is ApiResult.Error -> {
|
||||
error = result.message
|
||||
}
|
||||
else -> {}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
isGeneratingCode = true
|
||||
val token = TokenStorage.getToken()
|
||||
if (token != null) {
|
||||
when (val result = residenceApi.generateShareCode(token, residenceId)) {
|
||||
is ApiResult.Success -> {
|
||||
shareCode = result.data.shareCode
|
||||
}
|
||||
is ApiResult.Error -> {
|
||||
error = result.message
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
isGeneratingCode = false
|
||||
}
|
||||
},
|
||||
enabled = !isGeneratingCode
|
||||
) {
|
||||
Icon(Icons.Default.Share, "Generate", modifier = Modifier.size(18.dp))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(if (shareCode != null) "New Code" else "Generate")
|
||||
isGeneratingCode = false
|
||||
}
|
||||
},
|
||||
enabled = !isGeneratingCode,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
if (isGeneratingCode) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(18.dp),
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
} else {
|
||||
Icon(Icons.Default.Refresh, "Generate", modifier = Modifier.size(18.dp))
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(if (shareCode != null) "Generate New Code" else "Generate Code")
|
||||
}
|
||||
|
||||
if (shareCode != null) {
|
||||
Text(
|
||||
text = "Share this code with others to give them access to $residenceName",
|
||||
text = "Share this 6-character code. They can enter it in the app to join.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(top = 8.dp)
|
||||
@@ -172,7 +268,7 @@ fun ManageUsersDialog(
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth().height(300.dp)
|
||||
modifier = Modifier.fillMaxWidth().height(200.dp)
|
||||
) {
|
||||
items(users) { user ->
|
||||
UserListItem(
|
||||
|
||||
@@ -260,6 +260,10 @@ fun ResidenceDetailScreen(
|
||||
residenceViewModel.getResidence(residenceId) { result ->
|
||||
residenceState = result
|
||||
}
|
||||
},
|
||||
onSharePackage = {
|
||||
// Use the existing share function with the residence
|
||||
shareResidence(residence)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user