Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding max depth for tree style threads #478

Merged
merged 2 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,31 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.datetime.Instant
import social.firefly.common.utils.StringFactory
import social.firefly.common.utils.timeSinceNow
import social.firefly.core.designsystem.utils.NoRipple
import social.firefly.core.ui.common.TransparentNoTouchOverlay
import social.firefly.core.ui.common.text.MediumTextLabel
import social.firefly.core.ui.common.utils.PreviewTheme
import social.firefly.core.ui.common.utils.getMaxWidth
import social.firefly.core.ui.postcard.components.Avatar
import social.firefly.core.ui.postcard.components.BottomRow
import social.firefly.core.ui.postcard.components.DepthLines
import social.firefly.core.ui.postcard.components.MetaData
import social.firefly.core.ui.postcard.components.PostContent
import social.firefly.core.ui.postcard.components.TopRowMetaData

/**
* @param threadId if viewing this post from a thread, pass the threadId in to prevent
* the user from being able to click on the same status as the thread's root status
*/
@Composable
fun PostCard(
modifier: Modifier = Modifier,
post: PostCardUiState,
postCardInteractions: PostCardInteractions,
threadId: String? = null,
) {
NoRipple {
Box(modifier = modifier) {
Expand All @@ -49,15 +45,17 @@ fun PostCard(
.fillMaxWidth()
.height(IntrinsicSize.Min),
) {
DepthLines(post = post)
if (post.depthLinesUiState != null) {
DepthLines(depthLinesUiState = post.depthLinesUiState)
} else {
Spacer(modifier = Modifier.width(8.dp))
}

Column(
modifier = Modifier
.padding(end = 8.dp, bottom = 8.dp, top = 8.dp)
.clickable {
// prevent the user from being able to click on the same status
// as the root thread status
if (post.mainPostCardUiState.statusId != threadId) {
if (post.isClickable) {
postCardInteractions.onPostCardClicked(post.mainPostCardUiState.statusId)
}
},
Expand All @@ -68,7 +66,11 @@ fun PostCard(
)
Spacer(modifier = Modifier.height(8.dp))
}
Post(post.mainPostCardUiState, postCardInteractions)
Post(
post = post.mainPostCardUiState,
showViewMoreReplies = post.depthLinesUiState?.showViewMoreRepliesText ?: false,
postCardInteractions = postCardInteractions,
)
}
}

Expand All @@ -87,6 +89,7 @@ fun PostCard(
@Composable
private fun Post(
post: MainPostCardUiState,
showViewMoreReplies: Boolean,
postCardInteractions: PostCardInteractions,
) {
Row {
Expand All @@ -102,10 +105,22 @@ private fun Post(
postCardInteractions = postCardInteractions,
)

BottomRow(
post = post,
postCardInteractions = postCardInteractions,
)
Box(
modifier = Modifier.height(36.dp)
) {
BottomRow(
post = post,
postCardInteractions = postCardInteractions,
)
}

if (showViewMoreReplies) {
MediumTextLabel(
modifier = Modifier
.padding(vertical = 16.dp),
text = stringResource(id = R.string.view_more_replies)
)
}
}
}
}
Expand Down Expand Up @@ -237,6 +252,7 @@ private fun PostCardPreviewWithDepth() {
2,
),
startingDepth = 0,
showViewMoreRepliesText = true,
),
),
postCardInteractions = PostCardInteractionsNoOp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,11 @@ fun PostCardListItem(
postCardInteractions: PostCardInteractions,
index: Int,
itemCount: Int,
threadId: String? = null,
showDividers: Boolean = true,
) {
PostCard(
post = uiState,
postCardInteractions = postCardInteractions,
threadId = threadId,
)
if (index < itemCount && showDividers) {
FfDivider()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ fun Status.toPostCardUiState(
postCardInteractions: PostCardInteractions,
depthLinesUiState: DepthLinesUiState? = null,
showTopRowMetaData: Boolean = true,
isClickable: Boolean = true,
): PostCardUiState =
PostCardUiState(
statusId = statusId,
Expand All @@ -27,6 +28,7 @@ fun Status.toPostCardUiState(
mainPostCardUiState = boostedStatus?.toMainPostCardUiState(currentUserAccountId, postCardInteractions)
?: toMainPostCardUiState(currentUserAccountId, postCardInteractions),
depthLinesUiState = depthLinesUiState,
isClickable = isClickable,
)

private fun Status.toMainPostCardUiState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ data class PostCardUiState(
val topRowMetaDataUiState: TopRowMetaDataUiState?,
val mainPostCardUiState: MainPostCardUiState,
val depthLinesUiState: DepthLinesUiState?,
val isClickable: Boolean = true,
)

/**
Expand Down Expand Up @@ -57,6 +58,7 @@ data class DepthLinesUiState(
val postDepth: Int,
val depthLines: List<Int>,
val startingDepth: Int,
val showViewMoreRepliesText: Boolean = false,
)

enum class TopRowIconType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import social.firefly.common.utils.toPx
import social.firefly.core.designsystem.theme.FfTheme
import social.firefly.core.ui.postcard.DepthLinesUiState
import social.firefly.core.ui.postcard.PostCardUiState

/**
* These are the lines that show in a thread in tree view
*/
@Composable
internal fun DepthLines(
post: PostCardUiState,
depthLinesUiState: DepthLinesUiState,
) {
val spacingWidth = 8

val postDepth = post.depthLinesUiState?.postDepth ?: -1
val startingDepth = post.depthLinesUiState?.startingDepth ?: 0
val postDepth = depthLinesUiState.postDepth
val startingDepth = depthLinesUiState.startingDepth

val width = maxOf(((postDepth - startingDepth + 1) * spacingWidth), 0)
val lineColor = FfTheme.colors.borderPrimary
Expand All @@ -43,10 +44,12 @@ internal fun DepthLines(
) {
val height = size.height
if (postDepth >= startingDepth) {

// depth lines from other posts
for (i in startingDepth until postDepth) {
val drawDepth = i - startingDepth + 1
val x = (spacingWidth * drawDepth).toFloat() + 1
if (post.depthLinesUiState?.depthLines?.contains(i) == true) {
if (depthLinesUiState.depthLines.contains(i)) {
drawLine(
color = lineColor,
start = Offset(x.toPx(context), 0f.toPx(context)),
Expand All @@ -56,6 +59,7 @@ internal fun DepthLines(
}
}

// curved line leading into the avatar
if (postDepth > startingDepth) {
val drawDepth = postDepth - startingDepth
val x = (spacingWidth * drawDepth).toFloat() + 1
Expand All @@ -79,16 +83,50 @@ internal fun DepthLines(
)
}

if (post.depthLinesUiState?.depthLines?.contains(post.depthLinesUiState.postDepth) == true) {
// new depth line coming out of avatar
if (depthLinesUiState.depthLines.contains(depthLinesUiState.postDepth)) {
val drawDepth = postDepth - startingDepth + 1
val x = (spacingWidth * drawDepth).toFloat() + 1
drawLine(
color = lineColor,
start = Offset(x.toPx(context), 26f.toPx(context)),
end = Offset(x.toPx(context), height),
strokeWidth = 2f.toPx(context),
cap = StrokeCap.Round
)
if (depthLinesUiState.showViewMoreRepliesText) {
// goes to the view more replies text
val endingHeight = height - 32f.toPx(context)
val path = Path().apply {
moveTo(
x = x.toPx(context),
y = 26f.toPx(context),
)
lineTo(
x = x.toPx(context),
y = endingHeight - 18f.toPx(context),
)
quadraticBezierTo(
x1 = x.toPx(context),
y1 = endingHeight,
x2 = (x + 8).toPx(context),
y2 = endingHeight,
)
lineTo(
x = (x + 40).toPx(context),
y = endingHeight,
)
}
drawPath(
path = path,
color = lineColor,
style = Stroke(
width = 2f.toPx(context),
)
)
} else {
// goes to the next post
drawLine(
color = lineColor,
start = Offset(x.toPx(context), 26f.toPx(context)),
end = Offset(x.toPx(context), height),
strokeWidth = 2f.toPx(context),
cap = StrokeCap.Round
)
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions core/ui/postcard/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@
<string name="mute_user">Mute %s</string>
<string name="block_user">Block %s</string>
<string name="report_user">Report %s</string>

<string name="view_more_replies">View more replies</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ internal fun ThreadScreen(

ThreadScreen(
threadType = threadType,
threadStatusId = threadStatusId,
statuses = statuses,
postCardDelegate = viewModel.postCardDelegate,
threadInteractions = viewModel,
Expand All @@ -53,7 +52,6 @@ internal fun ThreadScreen(
@Composable
private fun ThreadScreen(
threadType: ThreadType,
threadStatusId: String,
statuses: List<PostCardUiState>,
postCardDelegate: PostCardDelegate,
threadInteractions: ThreadInteractions,
Expand Down Expand Up @@ -84,7 +82,6 @@ private fun ThreadScreen(
postCardInteractions = postCardDelegate,
index = index,
itemCount = statuses.count(),
threadId = threadStatusId,
showDividers = threadType != ThreadType.TREE,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class ThreadViewModel(
it.toPostCardUiState(
currentUserAccountId = loggedInAccountId,
postCardInteractions = postCardDelegate,
isClickable = it.statusId != mainStatusId,
)
}
}
Expand All @@ -76,6 +77,7 @@ class ThreadViewModel(
currentUserAccountId = loggedInAccountId,
postCardInteractions = postCardDelegate,
showTopRowMetaData = false,
isClickable = it.statusId != mainStatusId,
)
}
}
Expand All @@ -86,16 +88,22 @@ class ThreadViewModel(
)
val depthList = rootNode?.toDepthList() ?: emptyList()
val mainStatusDepth = depthList.find { it.value.statusId == mainStatusId }?.depth ?: 0
depthList.map { statusWithDepth ->
depthList.filter {
it.depth - mainStatusDepth <= MAX_DEPTH
}.map { statusWithDepth ->
val isAtMaxDepth = statusWithDepth.depth - mainStatusDepth == MAX_DEPTH
val hasReplies = statusWithDepth.value.repliesCount > 0
statusWithDepth.value.toPostCardUiState(
currentUserAccountId = loggedInAccountId,
postCardInteractions = postCardDelegate,
depthLinesUiState = DepthLinesUiState(
postDepth = statusWithDepth.depth,
depthLines = statusWithDepth.depthLines,
startingDepth = mainStatusDepth + 1,
showViewMoreRepliesText = isAtMaxDepth && hasReplies,
),
showTopRowMetaData = false,
isClickable = statusWithDepth.value.statusId != mainStatusId,
)
}
}
Expand All @@ -121,4 +129,8 @@ class ThreadViewModel(
val postCardDelegate: PostCardDelegate by KoinJavaComponent.inject(
PostCardDelegate::class.java,
) { parametersOf(viewModelScope, FeedLocation.THREAD) }

companion object {
private const val MAX_DEPTH = 10
}
}