mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-09-14 00:55:08 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3342b6ad1f |
@@ -270,6 +270,7 @@ fun NoteCompose(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
moreOptions: (@Composable () -> Unit)? = null,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
WatchNoteEvent(
|
||||
baseNote = baseNote,
|
||||
@@ -299,6 +300,7 @@ fun NoteCompose(
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
moreOptions = moreOptions,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -319,6 +321,7 @@ fun AcceptableNote(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
moreOptions: (@Composable () -> Unit)?,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
if (isQuotedNote || isBoostedNote) {
|
||||
val noteEvent = baseNote.event
|
||||
@@ -374,6 +377,7 @@ fun AcceptableNote(
|
||||
showPopup = showPopup,
|
||||
nav = nav,
|
||||
moreOptions = moreOptions,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -431,6 +435,7 @@ fun AcceptableNote(
|
||||
showPopup = showPopup,
|
||||
nav = nav,
|
||||
moreOptions = moreOptions,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -492,6 +497,7 @@ private fun CheckNewAndRenderNote(
|
||||
showPopup: () -> Unit,
|
||||
nav: INav,
|
||||
moreOptions: (@Composable () -> Unit)? = null,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
val backgroundColor =
|
||||
calculateBackgroundColor(
|
||||
@@ -508,6 +514,7 @@ private fun CheckNewAndRenderNote(
|
||||
accountViewModel = accountViewModel,
|
||||
showPopup = showPopup,
|
||||
nav = nav,
|
||||
onClick = onClick,
|
||||
) {
|
||||
InnerNoteWithReactions(
|
||||
baseNote = baseNote,
|
||||
@@ -534,30 +541,33 @@ fun ClickableNote(
|
||||
accountViewModel: AccountViewModel,
|
||||
showPopup: () -> Unit,
|
||||
nav: INav,
|
||||
onClick: (() -> Unit)? = null,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val defaultOnClick: () -> Unit = {
|
||||
val redirectToNote =
|
||||
if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) {
|
||||
baseNote.replyTo?.lastOrNull() ?: baseNote
|
||||
} else {
|
||||
baseNote
|
||||
}
|
||||
|
||||
nav.nav {
|
||||
if (redirectToNote.event is DraftWrapEvent) {
|
||||
withContext(Dispatchers.IO) {
|
||||
routeEditDraftTo(redirectToNote, accountViewModel.account)
|
||||
}
|
||||
} else {
|
||||
routeFor(redirectToNote, accountViewModel.account)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val updatedModifier =
|
||||
remember(baseNote, modifier) {
|
||||
remember(baseNote, modifier, onClick) {
|
||||
modifier
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
val redirectToNote =
|
||||
if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) {
|
||||
baseNote.replyTo?.lastOrNull() ?: baseNote
|
||||
} else {
|
||||
baseNote
|
||||
}
|
||||
|
||||
nav.nav {
|
||||
if (redirectToNote.event is DraftWrapEvent) {
|
||||
withContext(Dispatchers.IO) {
|
||||
routeEditDraftTo(redirectToNote, accountViewModel.account)
|
||||
}
|
||||
} else {
|
||||
routeFor(redirectToNote, accountViewModel.account)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = onClick ?: defaultOnClick,
|
||||
onLongClick = showPopup,
|
||||
)
|
||||
}
|
||||
|
||||
+121
-17
@@ -38,6 +38,8 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
@@ -287,27 +289,21 @@ fun RenderThreadFeed(
|
||||
nav: INav,
|
||||
) {
|
||||
val items by loaded.feed.collectAsStateWithLifecycle()
|
||||
val levelCache by viewModel.levelCacheFlow.collectAsStateWithLifecycle()
|
||||
val foldedNotes = remember { mutableStateOf(setOf<String>()) }
|
||||
|
||||
val position = items.list.indexOfFirst { it.idHex == noteId }
|
||||
val visibleItems by remember(items, foldedNotes.value, levelCache) {
|
||||
derivedStateOf {
|
||||
computeVisibleItems(items.list, foldedNotes.value, levelCache)
|
||||
}
|
||||
}
|
||||
|
||||
val position = visibleItems.indexOfFirst { it.note.idHex == noteId }
|
||||
|
||||
LaunchedEffect(noteId, position) {
|
||||
// hack to allow multiple scrolls to Item while posts on the screen load.
|
||||
// This is important when clicking on a reply of an older thread in Notifications
|
||||
// In that case, this screen will open with 0-1 items, and the scrollToItem below
|
||||
// will not change the state of the screen (too few items, scroll is not available)
|
||||
// as the app loads the reaming of the thread the position of the reply changes
|
||||
// and because there wasn't a possibility to scroll before and now there is one,
|
||||
// the screen stays at the top. Once the thread has enough replies, the lazy column
|
||||
// updates with new items correctly. It just needs a few items to start the scroll.
|
||||
//
|
||||
// This hack allows the list 1 second to fill up with more
|
||||
// records before setting up the position on the feed.
|
||||
//
|
||||
// It jumps around, but it is the best we can do.
|
||||
|
||||
if (position >= 0 && !viewModel.hasDragged.value) {
|
||||
val offset =
|
||||
if (position > items.list.size - 3) {
|
||||
if (position > visibleItems.size - 3) {
|
||||
0
|
||||
} else {
|
||||
-200
|
||||
@@ -322,7 +318,8 @@ fun RenderThreadFeed(
|
||||
contentPadding = FeedPadding,
|
||||
state = listState,
|
||||
) {
|
||||
itemsIndexed(items.list, key = { _, item -> item.idHex }) { index, item ->
|
||||
itemsIndexed(visibleItems, key = { _, item -> item.note.idHex }) { index, threadItem ->
|
||||
val item = threadItem.note
|
||||
val level = viewModel.levelFlowForItem(item).collectAsStateWithLifecycle(0)
|
||||
|
||||
val modifier =
|
||||
@@ -348,12 +345,29 @@ fun RenderThreadFeed(
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val isFolded = item.idHex in foldedNotes.value
|
||||
val hasChildren = threadItem.childCount > 0
|
||||
val selectedNoteColor = MaterialTheme.colorScheme.selectedNote
|
||||
val background =
|
||||
remember {
|
||||
if (item.idHex == noteId) mutableStateOf(selectedNoteColor) else null
|
||||
}
|
||||
|
||||
val onClickHandler: (() -> Unit)? =
|
||||
if (hasChildren) {
|
||||
{
|
||||
val current = foldedNotes.value
|
||||
foldedNotes.value =
|
||||
if (isFolded) {
|
||||
current - item.idHex
|
||||
} else {
|
||||
current + item.idHex
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
NoteCompose(
|
||||
baseNote = item,
|
||||
modifier = modifier,
|
||||
@@ -363,7 +377,15 @@ fun RenderThreadFeed(
|
||||
parentBackgroundColor = background,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
onClick = onClickHandler,
|
||||
)
|
||||
|
||||
if (isFolded && hasChildren) {
|
||||
FoldedRepliesSummary(
|
||||
hiddenCount = threadItem.childCount,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalDivider(
|
||||
@@ -373,6 +395,88 @@ fun RenderThreadFeed(
|
||||
}
|
||||
}
|
||||
|
||||
data class ThreadItem(
|
||||
val note: Note,
|
||||
val childCount: Int,
|
||||
)
|
||||
|
||||
private fun computeVisibleItems(
|
||||
allItems: List<Note>,
|
||||
foldedNotes: Set<String>,
|
||||
levelCache: Map<Note, Int>,
|
||||
): List<ThreadItem> {
|
||||
if (allItems.isEmpty()) return emptyList()
|
||||
|
||||
val result = mutableListOf<ThreadItem>()
|
||||
var i = 0
|
||||
|
||||
while (i < allItems.size) {
|
||||
val item = allItems[i]
|
||||
val itemLevel = levelCache[item] ?: 0
|
||||
|
||||
// Count children that would follow this note
|
||||
var childCount = 0
|
||||
var j = i + 1
|
||||
while (j < allItems.size) {
|
||||
val nextLevel = levelCache[allItems[j]] ?: 0
|
||||
if (nextLevel > itemLevel) {
|
||||
childCount++
|
||||
j++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
result.add(ThreadItem(item, childCount))
|
||||
|
||||
if (item.idHex in foldedNotes && childCount > 0) {
|
||||
// Skip all children
|
||||
i = j
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FoldedRepliesSummary(
|
||||
hiddenCount: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val text =
|
||||
if (hiddenCount == 1) {
|
||||
"$hiddenCount reply hidden"
|
||||
} else {
|
||||
"$hiddenCount replies hidden"
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ExpandMore,
|
||||
contentDescription = stringRes(R.string.expand),
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.padding(horizontal = 4.dp))
|
||||
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun NoteMaster(
|
||||
|
||||
Reference in New Issue
Block a user