Skip to content

Commit

Permalink
Adding tree style threads (#477)
Browse files Browse the repository at this point in the history
Making 3 different thread view types
- Tree style
- List
- List with only direct replies

Doing some other small refactors
- Some refactors on post card stuff

---------

Co-authored-by: John Oberhauser <j.git-global@obez.io>
  • Loading branch information
JohnOberhauser and John Oberhauser committed Apr 20, 2024
1 parent e9a48c6 commit 92d5a81
Show file tree
Hide file tree
Showing 38 changed files with 1,309 additions and 459 deletions.
25 changes: 25 additions & 0 deletions core/common/src/main/java/social/firefly/common/tree/TreeNode.kt
@@ -0,0 +1,25 @@
package social.firefly.common.tree

class MutableTreeNode<T>(
override val value: T
): TreeNode<T> {
private val _branches: MutableList<TreeNode<T>> = mutableListOf()
override val branches: List<TreeNode<T>> = _branches

override val isLeaf: Boolean
get() = branches.isEmpty()

fun add(tree: TreeNode<T>) {
_branches.add(tree)
}

fun remove(tree: TreeNode<T>) {
_branches.remove(tree)
}
}

interface TreeNode<T> {
val branches: List<TreeNode<T>>
val isLeaf: Boolean
val value: T
}
54 changes: 54 additions & 0 deletions core/common/src/main/java/social/firefly/common/tree/TreeUtils.kt
@@ -0,0 +1,54 @@
package social.firefly.common.tree

fun <T> TreeNode<T>.toDepthList(
depth: Int = 0,
depthLines: List<Int> = listOf(),
): List<DepthItem<T>> = buildList {
var newDepthLines = buildList {
addAll(depthLines)
if (!isLeaf) {
add(depth)
}
}
add(
DepthItem(
value,
depth,
newDepthLines,
)
)
branches.forEachIndexed { index, treeNode ->
if (index == branches.size - 1) {
newDepthLines = newDepthLines.toMutableList().apply {
remove(depth)
}
}
addAll(
treeNode.toDepthList(
depth + 1,
newDepthLines,
)
)
}
}

data class DepthItem<T>(
val value: T,
val depth: Int,
val depthLines: List<Int>,
)

fun <T> List<T>.toTree(
identifier: (T) -> String,
parentIdentifier: (T) -> String?,
): TreeNode<T>? {
val nodes = mutableMapOf<String, MutableTreeNode<T>>()

forEach {
val newNode = MutableTreeNode(it)
nodes[parentIdentifier(it)]?.add(newNode)
nodes[identifier(it)] = newNode
}

return nodes[identifier(first())]
}
Expand Up @@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.mapLatest
import social.firefly.core.datastore.UserPreferences.ThreadType
import timber.log.Timber
import java.io.IOException

Expand Down Expand Up @@ -43,6 +44,11 @@ class UserPreferencesDatastore(context: Context) {
it.lastSeenHomeStatusId
}.distinctUntilChanged()

val threadType: Flow<ThreadType> =
dataStore.data.mapLatest {
it.threadType
}.distinctUntilChanged()

/**
* Preload the data so that it's available in the cache
*/
Expand Down Expand Up @@ -96,6 +102,14 @@ class UserPreferencesDatastore(context: Context) {
}
}

suspend fun saveThreadType(threadType: ThreadType) {
dataStore.updateData {
it.toBuilder()
.setThreadType(threadType)
.build()
}
}

suspend fun clearData() {
dataStore.updateData {
it.toBuilder()
Expand Down
Expand Up @@ -8,7 +8,9 @@ import java.io.InputStream
import java.io.OutputStream

internal object UserPreferencesSerializer : Serializer<UserPreferences> {
override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance()
override val defaultValue: UserPreferences = UserPreferences.newBuilder()
.setThreadType(UserPreferences.ThreadType.TREE)
.build()

override suspend fun readFrom(input: InputStream): UserPreferences =
UserPreferences.parseFrom(input)
Expand Down
Expand Up @@ -4,11 +4,18 @@ option java_package = "social.firefly.core.datastore";
option java_multiple_files = true;

message UserPreferences {
enum ThreadType {
LIST = 0;
DIRECT_REPLIES_LIST = 1;
TREE = 2;
}

reserved 4, 5, 7;
reserved "client_id", "client_secret";
string access_token = 1;
string account_id = 2;
string domain = 3;
string serialized_push_keys = 6;
string last_seen_home_status_id = 8;
ThreadType thread_type = 9;
}
Expand Up @@ -121,6 +121,9 @@ object FfIcons {
@Composable
fun listChecks() = painterResource(id = R.drawable.list_checks)

@Composable
fun listPlus() = painterResource(id = R.drawable.list_plus)

@Composable
fun lock() = painterResource(id = R.drawable.lock)

Expand Down Expand Up @@ -169,6 +172,9 @@ object FfIcons {
@Composable
fun trash() = painterResource(id = R.drawable.trash)

@Composable
fun treeView() = painterResource(id = R.drawable.tree_view)

@Composable
fun userCircle() = painterResource(id = R.drawable.user_circle)

Expand Down
4 changes: 2 additions & 2 deletions core/designsystem/src/main/res/drawable/list.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:width="32dp"
android:height="32dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
Expand Down
9 changes: 9 additions & 0 deletions core/designsystem/src/main/res/drawable/list_plus.xml
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:pathData="M32,64a8,8 0,0 1,8 -8L216,56a8,8 0,0 1,0 16L40,72A8,8 0,0 1,32 64ZM40,136L216,136a8,8 0,0 0,0 -16L40,120a8,8 0,0 0,0 16ZM144,184L40,184a8,8 0,0 0,0 16L144,200a8,8 0,0 0,0 -16ZM232,184L216,184L216,168a8,8 0,0 0,-16 0v16L184,184a8,8 0,0 0,0 16h16v16a8,8 0,0 0,16 0L216,200h16a8,8 0,0 0,0 -16Z"
android:fillColor="#000000"/>
</vector>
9 changes: 9 additions & 0 deletions core/designsystem/src/main/res/drawable/tree_view.xml
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:pathData="M176,152h32a16,16 0,0 0,16 -16L224,104a16,16 0,0 0,-16 -16L176,88a16,16 0,0 0,-16 16v8L88,112L88,80h8a16,16 0,0 0,16 -16L112,32A16,16 0,0 0,96 16L64,16A16,16 0,0 0,48 32L48,64A16,16 0,0 0,64 80h8L72,192a24,24 0,0 0,24 24h64v8a16,16 0,0 0,16 16h32a16,16 0,0 0,16 -16L224,192a16,16 0,0 0,-16 -16L176,176a16,16 0,0 0,-16 16v8L96,200a8,8 0,0 1,-8 -8L88,128h72v8A16,16 0,0 0,176 152ZM64,32L96,32L96,64L64,64ZM176,192h32v32L176,224ZM176,104h32v32L176,136Z"
android:fillColor="#000000"/>
</vector>
Expand Up @@ -19,9 +19,7 @@ fun FfVerticalDivider(
thickness: Dp = 1.dp,
) {
Box(
modifier =
modifier
.fillMaxHeight()
modifier = modifier
.width(thickness)
.background(color = color),
)
Expand Down
Expand Up @@ -62,15 +62,6 @@ fun FfDropDownMenu(
.clickable { if (canExpand) expanded.value = true },
) {
content()
Spacer(modifier = Modifier.padding(start = 8.dp))
Icon(
modifier = Modifier
.size(FfIcons.Sizes.small)
.align(Alignment.CenterVertically),
painter = FfIcons.caretDown(),
contentDescription = null,
tint = FfTheme.colors.iconPrimary,
)
}
}
FfDropdownMenu(
Expand Down
@@ -1,25 +1,37 @@
package social.firefly.core.ui.common.dropdown

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun FfDropDownItem(
text: String,
icon: (@Composable () -> Unit)? = null,
expanded: MutableState<Boolean>,
onClick: () -> Unit,
) {
DropdownMenuItem(
text = {
Row {
Spacer(modifier = Modifier.padding(start = 8.dp))
Spacer(modifier = Modifier.width(8.dp))
icon?.let {
Box(
modifier = Modifier.align(Alignment.CenterVertically)
) {
icon()
}
Spacer(modifier = Modifier.width(8.dp))
}
Text(text = text)
}
},
Expand Down
Expand Up @@ -88,6 +88,15 @@ fun VisibilityDropDownButton(
text = stringResource(id = R.string.visibility_direct),
)
}
Spacer(modifier = Modifier.padding(start = 8.dp))
Icon(
modifier = Modifier
.size(FfIcons.Sizes.small)
.align(Alignment.CenterVertically),
painter = FfIcons.caretDown(),
contentDescription = null,
tint = FfTheme.colors.iconPrimary,
)
}
}

Expand Down
Expand Up @@ -9,8 +9,8 @@ import social.firefly.core.ui.notifications.NotificationInteractionsNoOp
import social.firefly.core.ui.notifications.NotificationUiState
import social.firefly.core.ui.postcard.PostCardInteractions
import social.firefly.core.ui.postcard.PostCardInteractionsNoOp
import social.firefly.core.ui.postcard.PostContent
import social.firefly.core.ui.postcard.PostContentUiState
import social.firefly.core.ui.postcard.components.PostContent

@Composable
internal fun FavoriteNotificationContent(
Expand Down
Expand Up @@ -9,8 +9,8 @@ import social.firefly.core.ui.notifications.NotificationInteractionsNoOp
import social.firefly.core.ui.notifications.NotificationUiState
import social.firefly.core.ui.postcard.PostCardInteractions
import social.firefly.core.ui.postcard.PostCardInteractionsNoOp
import social.firefly.core.ui.postcard.PostContent
import social.firefly.core.ui.postcard.PostContentUiState
import social.firefly.core.ui.postcard.components.PostContent

@Composable
internal fun MentionNotificationContent(
Expand Down
Expand Up @@ -9,8 +9,8 @@ import social.firefly.core.ui.notifications.NotificationInteractionsNoOp
import social.firefly.core.ui.notifications.NotificationUiState
import social.firefly.core.ui.postcard.PostCardInteractions
import social.firefly.core.ui.postcard.PostCardInteractionsNoOp
import social.firefly.core.ui.postcard.PostContent
import social.firefly.core.ui.postcard.PostContentUiState
import social.firefly.core.ui.postcard.components.PostContent

@Composable
internal fun NewStatusNotificationContent(
Expand Down
Expand Up @@ -11,8 +11,8 @@ import social.firefly.core.ui.poll.PollOptionUiState
import social.firefly.core.ui.poll.PollUiState
import social.firefly.core.ui.postcard.PostCardInteractions
import social.firefly.core.ui.postcard.PostCardInteractionsNoOp
import social.firefly.core.ui.postcard.PostContent
import social.firefly.core.ui.postcard.PostContentUiState
import social.firefly.core.ui.postcard.components.PostContent

@Composable
internal fun PollEndedNotificationContent(
Expand Down
Expand Up @@ -9,8 +9,8 @@ import social.firefly.core.ui.notifications.NotificationInteractionsNoOp
import social.firefly.core.ui.notifications.NotificationUiState
import social.firefly.core.ui.postcard.PostCardInteractions
import social.firefly.core.ui.postcard.PostCardInteractionsNoOp
import social.firefly.core.ui.postcard.PostContent
import social.firefly.core.ui.postcard.PostContentUiState
import social.firefly.core.ui.postcard.components.PostContent

@Composable
internal fun RepostNotificationContent(
Expand Down
Expand Up @@ -9,8 +9,8 @@ import social.firefly.core.ui.notifications.NotificationInteractionsNoOp
import social.firefly.core.ui.notifications.NotificationUiState
import social.firefly.core.ui.postcard.PostCardInteractions
import social.firefly.core.ui.postcard.PostCardInteractionsNoOp
import social.firefly.core.ui.postcard.PostContent
import social.firefly.core.ui.postcard.PostContentUiState
import social.firefly.core.ui.postcard.components.PostContent

@Composable
internal fun StatusUpdatedNotificationContent(
Expand Down

0 comments on commit 92d5a81

Please sign in to comment.