A cheap attempt at a native Bluesky client for Android
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

SkeetView: Fix timestamp alignment and compact label chips

Pin timestamp to right edge of header row, truncate long names with
ellipsis. Replace SuggestionChip labels with compact Surface-based
chips using surfaceContainerHigh for less visual bulk.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

geesawra c7f125b4 0607a770

+70 -54
+1 -1
app/src/main/java/industries/geesawra/monarch/MainView.kt
··· 330 330 TopAppBar( 331 331 colors = TopAppBarDefaults.topAppBarColors( 332 332 containerColor = MaterialTheme.colorScheme.surface, 333 - scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainer, 333 + scrolledContainerColor = MaterialTheme.colorScheme.surface, 334 334 navigationIconContentColor = MaterialTheme.colorScheme.onSurface, 335 335 titleContentColor = MaterialTheme.colorScheme.onSurface, 336 336 actionIconContentColor = MaterialTheme.colorScheme.onSurface,
+69 -53
app/src/main/java/industries/geesawra/monarch/SkeetView.kt
··· 41 41 import androidx.compose.material3.Icon 42 42 import androidx.compose.material3.MaterialTheme 43 43 import androidx.compose.material3.OutlinedCard 44 - import androidx.compose.material3.SuggestionChip 45 - import androidx.compose.material3.SuggestionChipDefaults 44 + import androidx.compose.ui.text.style.TextOverflow 46 45 import androidx.compose.material3.VerticalDivider 47 46 import androidx.compose.material3.ModalBottomSheet 48 47 import androidx.compose.material3.Surface ··· 708 707 verticalAlignment = Alignment.CenterVertically, 709 708 modifier = Modifier.fillMaxWidth() 710 709 ) { 711 - Text( 712 - text = authorName, 713 - color = MaterialTheme.colorScheme.onSurface, 714 - style = MaterialTheme.typography.titleSmall, 715 - fontWeight = FontWeight.Bold, 716 - modifier = Modifier.weight(1f, fill = false) 717 - ) 718 - if (skeet.verified) { 719 - Spacer(modifier = Modifier.width(4.dp)) 720 - Icon( 721 - imageVector = Icons.Filled.Verified, 722 - contentDescription = "Verified", 723 - modifier = Modifier.size(16.dp), 724 - tint = MaterialTheme.colorScheme.primary 725 - ) 726 - } 727 - if (isBot) { 728 - Spacer(modifier = Modifier.width(4.dp)) 729 - Icon( 730 - imageVector = Icons.Filled.SmartToy, 731 - contentDescription = "Bot account", 732 - modifier = Modifier.size(16.dp), 733 - tint = MaterialTheme.colorScheme.onSurfaceVariant 710 + Row( 711 + verticalAlignment = Alignment.CenterVertically, 712 + modifier = Modifier.weight(1f) 713 + ) { 714 + Text( 715 + text = authorName, 716 + color = MaterialTheme.colorScheme.onSurface, 717 + style = MaterialTheme.typography.titleSmall, 718 + fontWeight = FontWeight.Bold, 719 + maxLines = 1, 720 + overflow = TextOverflow.Ellipsis, 734 721 ) 722 + if (skeet.verified) { 723 + Spacer(modifier = Modifier.width(4.dp)) 724 + Icon( 725 + imageVector = Icons.Filled.Verified, 726 + contentDescription = "Verified", 727 + modifier = Modifier.size(16.dp), 728 + tint = MaterialTheme.colorScheme.primary 729 + ) 730 + } 731 + if (isBot) { 732 + Spacer(modifier = Modifier.width(4.dp)) 733 + Icon( 734 + imageVector = Icons.Filled.SmartToy, 735 + contentDescription = "Bot account", 736 + modifier = Modifier.size(16.dp), 737 + tint = MaterialTheme.colorScheme.onSurfaceVariant 738 + ) 739 + } 735 740 } 736 741 skeet.createdAt?.let { 737 - Spacer(modifier = Modifier.weight(1f)) 738 742 Text( 739 743 text = HumanReadable.timeAgo(it), 740 744 color = MaterialTheme.colorScheme.onSurfaceVariant, 741 745 style = MaterialTheme.typography.labelSmall, 746 + modifier = Modifier.padding(start = 8.dp) 742 747 ) 743 748 } 744 749 } ··· 774 779 } 775 780 val description = labelDescription(it) 776 781 777 - val chipIcon: @Composable () -> Unit = { 778 - val avatarUrl = labelerAvatar(it) 779 - if (avatarUrl != null) { 780 - AsyncImage( 781 - model = ImageRequest.Builder(LocalContext.current) 782 - .data(avatarUrl) 783 - .crossfade(true) 784 - .build(), 785 - placeholder = ColorPainter(MaterialTheme.colorScheme.surfaceVariant), 786 - contentDescription = definition.plaintext, 787 - modifier = Modifier 788 - .size(SuggestionChipDefaults.IconSize) 789 - .clip(CircleShape) 790 - ) 791 - } else { 792 - Icon( 793 - imageVector = definition.icon, 794 - contentDescription = definition.plaintext, 795 - modifier = Modifier.size(SuggestionChipDefaults.IconSize), 796 - tint = MaterialTheme.colorScheme.onSurfaceVariant 782 + val chipContent = @Composable { 783 + Row( 784 + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), 785 + verticalAlignment = Alignment.CenterVertically 786 + ) { 787 + val avatarUrl = labelerAvatar(it) 788 + if (avatarUrl != null) { 789 + AsyncImage( 790 + model = ImageRequest.Builder(LocalContext.current) 791 + .data(avatarUrl) 792 + .crossfade(true) 793 + .build(), 794 + placeholder = ColorPainter(MaterialTheme.colorScheme.surfaceVariant), 795 + contentDescription = definition.plaintext, 796 + modifier = Modifier 797 + .size(14.dp) 798 + .clip(CircleShape) 799 + ) 800 + } else { 801 + Icon( 802 + imageVector = definition.icon, 803 + contentDescription = definition.plaintext, 804 + modifier = Modifier.size(14.dp), 805 + tint = MaterialTheme.colorScheme.onSurfaceVariant 806 + ) 807 + } 808 + Spacer(modifier = Modifier.width(4.dp)) 809 + Text( 810 + text = definition.plaintext, 811 + style = MaterialTheme.typography.labelSmall, 797 812 ) 798 813 } 799 814 } ··· 826 841 } 827 842 } 828 843 829 - SuggestionChip( 844 + Surface( 830 845 onClick = { showSheet = true }, 831 - label = { Text(text = definition.plaintext) }, 832 - icon = chipIcon, 846 + shape = CircleShape, 847 + color = MaterialTheme.colorScheme.surfaceContainerHigh, 848 + content = chipContent, 833 849 ) 834 850 } else { 835 - SuggestionChip( 836 - onClick = {}, 837 - label = { Text(text = definition.plaintext) }, 838 - icon = chipIcon, 851 + Surface( 852 + shape = CircleShape, 853 + color = MaterialTheme.colorScheme.surfaceContainerHigh, 854 + content = chipContent, 839 855 ) 840 856 } 841 857 }