feat: scope tab base logic

This commit is contained in:
rhunk
2023-08-19 20:09:35 +02:00
parent 2db332c3cd
commit 6cabb92c04
10 changed files with 331 additions and 141 deletions

View File

@ -12,7 +12,7 @@ import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper
import me.rhunk.snapenhance.bridge.wrapper.MessageLoggerWrapper import me.rhunk.snapenhance.bridge.wrapper.MessageLoggerWrapper
import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo
import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo
import me.rhunk.snapenhance.core.messaging.RuleScope import me.rhunk.snapenhance.core.messaging.MessagingScope
import me.rhunk.snapenhance.database.objects.FriendInfo import me.rhunk.snapenhance.database.objects.FriendInfo
import me.rhunk.snapenhance.download.DownloadProcessor import me.rhunk.snapenhance.download.DownloadProcessor
import me.rhunk.snapenhance.util.SerializableDataObject import me.rhunk.snapenhance.util.SerializableDataObject
@ -112,7 +112,7 @@ class BridgeService : Service() {
} }
override fun getRules(objectType: String, uuid: String): MutableList<String> { override fun getRules(objectType: String, uuid: String): MutableList<String> {
remoteSideContext.modDatabase.getRulesFromId(RuleScope.valueOf(objectType), uuid) remoteSideContext.modDatabase.getRulesFromId(MessagingScope.valueOf(objectType), uuid)
.let { rules -> .let { rules ->
return rules.map { it.toJson() }.toMutableList() return rules.map { it.toJson() }.toMutableList()
} }
@ -122,14 +122,14 @@ class BridgeService : Service() {
Logger.debug("Syncing remote") Logger.debug("Syncing remote")
syncCallback = callback syncCallback = callback
measureTimeMillis { measureTimeMillis {
remoteSideContext.modDatabase.getFriendsIds().forEach { friendId -> remoteSideContext.modDatabase.getFriends().map { it.userId } .forEach { friendId ->
runCatching { runCatching {
triggerFriendSync(friendId) triggerFriendSync(friendId)
}.onFailure { }.onFailure {
Logger.error("Failed to sync friend $friendId", it) Logger.error("Failed to sync friend $friendId", it)
} }
} }
remoteSideContext.modDatabase.getGroupsIds().forEach { groupId -> remoteSideContext.modDatabase.getGroups().map { it.conversationId }.forEach { groupId ->
runCatching { runCatching {
triggerGroupSync(groupId) triggerGroupSync(groupId)
}.onFailure { }.onFailure {

View File

@ -8,7 +8,7 @@ import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo
import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo
import me.rhunk.snapenhance.core.messaging.MessagingRule import me.rhunk.snapenhance.core.messaging.MessagingRule
import me.rhunk.snapenhance.core.messaging.Mode import me.rhunk.snapenhance.core.messaging.Mode
import me.rhunk.snapenhance.core.messaging.RuleScope import me.rhunk.snapenhance.core.messaging.MessagingScope
import me.rhunk.snapenhance.database.objects.FriendInfo import me.rhunk.snapenhance.database.objects.FriendInfo
import me.rhunk.snapenhance.util.SQLiteDatabaseHelper import me.rhunk.snapenhance.util.SQLiteDatabaseHelper
import me.rhunk.snapenhance.util.ktx.getInteger import me.rhunk.snapenhance.util.ktx.getInteger
@ -33,14 +33,16 @@ class ModDatabase(
database = context.androidContext.openOrCreateDatabase("main.db", 0, null) database = context.androidContext.openOrCreateDatabase("main.db", 0, null)
SQLiteDatabaseHelper.createTablesFromSchema(database, mapOf( SQLiteDatabaseHelper.createTablesFromSchema(database, mapOf(
"friends" to listOf( "friends" to listOf(
"userId VARCHAR PRIMARY KEY", "id INTEGER PRIMARY KEY AUTOINCREMENT",
"userId VARCHAR UNIQUE",
"displayName VARCHAR", "displayName VARCHAR",
"mutableUsername VARCHAR", "mutableUsername VARCHAR",
"bitmojiId VARCHAR", "bitmojiId VARCHAR",
"selfieId VARCHAR" "selfieId VARCHAR"
), ),
"groups" to listOf( "groups" to listOf(
"conversationId VARCHAR PRIMARY KEY", "id INTEGER PRIMARY KEY AUTOINCREMENT",
"conversationId VARCHAR UNIQUE",
"name VARCHAR", "name VARCHAR",
"participantsCount INTEGER" "participantsCount INTEGER"
), ),
@ -73,26 +75,6 @@ class ModDatabase(
)) ))
} }
fun getFriendsIds(): List<String> {
return database.rawQuery("SELECT userId FROM friends", null).use { cursor ->
val ids = mutableListOf<String>()
while (cursor.moveToNext()) {
ids.add(cursor.getString(0))
}
ids
}
}
fun getGroupsIds(): List<String> {
return database.rawQuery("SELECT conversationId FROM groups", null).use { cursor ->
val ids = mutableListOf<String>()
while (cursor.moveToNext()) {
ids.add(cursor.getString(0))
}
ids
}
}
fun getGroups(): List<MessagingGroupInfo> { fun getGroups(): List<MessagingGroupInfo> {
return database.rawQuery("SELECT * FROM groups", null).use { cursor -> return database.rawQuery("SELECT * FROM groups", null).use { cursor ->
val groups = mutableListOf<MessagingGroupInfo>() val groups = mutableListOf<MessagingGroupInfo>()
@ -107,8 +89,8 @@ class ModDatabase(
} }
} }
fun getFriends(): List<MessagingFriendInfo> { fun getFriends(descOrder: Boolean = false): List<MessagingFriendInfo> {
return database.rawQuery("SELECT * FROM friends", null).use { cursor -> return database.rawQuery("SELECT * FROM friends ORDER BY id ${if (descOrder) "DESC" else "ASC"}", null).use { cursor ->
val friends = mutableListOf<MessagingFriendInfo>() val friends = mutableListOf<MessagingFriendInfo>()
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
runCatching { runCatching {
@ -131,7 +113,7 @@ class ModDatabase(
fun syncGroupInfo(conversationInfo: MessagingGroupInfo) { fun syncGroupInfo(conversationInfo: MessagingGroupInfo) {
executeAsync { executeAsync {
try { try {
database.execSQL("INSERT OR REPLACE INTO groups VALUES (?, ?, ?)", arrayOf( database.execSQL("INSERT OR REPLACE INTO groups (conversationId, name, participantsCount) VALUES (?, ?, ?)", arrayOf(
conversationInfo.conversationId, conversationInfo.conversationId,
conversationInfo.name, conversationInfo.name,
conversationInfo.participantsCount conversationInfo.participantsCount
@ -145,13 +127,16 @@ class ModDatabase(
fun syncFriend(friend: FriendInfo) { fun syncFriend(friend: FriendInfo) {
executeAsync { executeAsync {
try { try {
database.execSQL("INSERT OR REPLACE INTO friends VALUES (?, ?, ?, ?, ?)", arrayOf( database.execSQL(
friend.userId, "INSERT OR REPLACE INTO friends (userId, displayName, mutableUsername, bitmojiId, selfieId) VALUES (?, ?, ?, ?, ?)",
friend.displayName, arrayOf(
friend.usernameForSorting!!, friend.userId,
friend.bitmojiAvatarId, friend.displayName,
friend.bitmojiSelfieId friend.usernameForSorting!!,
)) friend.bitmojiAvatarId,
friend.bitmojiSelfieId
)
)
//sync streaks //sync streaks
if (friend.streakLength > 0) { if (friend.streakLength > 0) {
database.execSQL("INSERT OR REPLACE INTO streaks (userId, expirationTimestamp, count) VALUES (?, ?, ?)", arrayOf( database.execSQL("INSERT OR REPLACE INTO streaks (userId, expirationTimestamp, count) VALUES (?, ?, ?)", arrayOf(
@ -168,13 +153,13 @@ class ModDatabase(
} }
} }
fun getRulesFromId(type: RuleScope, targetUuid: String): List<MessagingRule> { fun getRulesFromId(type: MessagingScope, targetUuid: String): List<MessagingRule> {
return database.rawQuery("SELECT * FROM rules WHERE objectType = ? AND targetUuid = ?", arrayOf(type.name, targetUuid)).use { cursor -> return database.rawQuery("SELECT * FROM rules WHERE objectType = ? AND targetUuid = ?", arrayOf(type.name, targetUuid)).use { cursor ->
val rules = mutableListOf<MessagingRule>() val rules = mutableListOf<MessagingRule>()
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
rules.add(MessagingRule( rules.add(MessagingRule(
id = cursor.getInteger("id"), id = cursor.getInteger("id"),
ruleScope = RuleScope.valueOf(cursor.getStringOrNull("scope")!!), messagingScope = MessagingScope.valueOf(cursor.getStringOrNull("scope")!!),
targetUuid = cursor.getStringOrNull("targetUuid")!!, targetUuid = cursor.getStringOrNull("targetUuid")!!,
enabled = cursor.getInteger("enabled") == 1, enabled = cursor.getInteger("enabled") == 1,
mode = Mode.valueOf(cursor.getStringOrNull("mode")!!), mode = Mode.valueOf(cursor.getStringOrNull("mode")!!),
@ -185,6 +170,42 @@ class ModDatabase(
} }
} }
fun getFriendInfo(userId: String): MessagingFriendInfo? {
return database.rawQuery("SELECT * FROM friends WHERE userId = ?", arrayOf(userId)).use { cursor ->
if (!cursor.moveToFirst()) return@use null
MessagingFriendInfo(
userId = cursor.getStringOrNull("userId")!!,
displayName = cursor.getStringOrNull("displayName"),
mutableUsername = cursor.getStringOrNull("mutableUsername")!!,
bitmojiId = cursor.getStringOrNull("bitmojiId"),
selfieId = cursor.getStringOrNull("selfieId")
)
}
}
fun deleteFriend(userId: String) {
executeAsync {
database.execSQL("DELETE FROM friends WHERE userId = ?", arrayOf(userId))
}
}
fun deleteGroup(conversationId: String) {
executeAsync {
database.execSQL("DELETE FROM groups WHERE conversationId = ?", arrayOf(conversationId))
}
}
fun getGroupInfo(conversationId: String): MessagingGroupInfo? {
return database.rawQuery("SELECT * FROM groups WHERE conversationId = ?", arrayOf(conversationId)).use { cursor ->
if (!cursor.moveToFirst()) return@use null
MessagingGroupInfo(
conversationId = cursor.getStringOrNull("conversationId")!!,
name = cursor.getStringOrNull("name")!!,
participantsCount = cursor.getInteger("participantsCount")
)
}
}
fun getFriendStreaks(userId: String): FriendStreaks? { fun getFriendStreaks(userId: String): FriendStreaks? {
return database.rawQuery("SELECT * FROM streaks WHERE userId = ?", arrayOf(userId)).use { cursor -> return database.rawQuery("SELECT * FROM streaks WHERE userId = ?", arrayOf(userId)).use { cursor ->
if (!cursor.moveToFirst()) return@use null if (!cursor.moveToFirst()) return@use null

View File

@ -1,8 +1,12 @@
package me.rhunk.snapenhance.ui.manager package me.rhunk.snapenhance.ui.manager
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
@ -16,7 +20,10 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.lerp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.navigation.NavDestination import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavDestination.Companion.hierarchy
@ -63,8 +70,19 @@ class Navigation(
TopAppBar(title = { TopAppBar(title = {
Text(text = currentSection.sectionTopBarName()) Text(text = currentSection.sectionTopBarName())
}, navigationIcon = { }, navigationIcon = {
if (currentSection.canGoBack()) { val backButtonAnimation by animateFloatAsState(if (currentSection.canGoBack()) 1f else 0f,
IconButton(onClick = { navHostController.popBackStack() }) { label = "backButtonAnimation"
)
Box(
modifier = Modifier
.graphicsLayer { alpha = backButtonAnimation }
.width(lerp(0.dp, 48.dp, backButtonAnimation))
.height(48.dp)
) {
IconButton(
onClick = { navHostController.popBackStack() }
) {
Icon(Icons.Filled.ArrowBack, contentDescription = null) Icon(Icons.Filled.ArrowBack, contentDescription = null)
} }
} }

View File

@ -42,7 +42,9 @@ class AddFriendDialog(
@Composable @Composable
private fun ListCardEntry(name: String, modifier: Modifier = Modifier) { private fun ListCardEntry(name: String, modifier: Modifier = Modifier) {
Card( Card(
modifier = Modifier.padding(5.dp).then(modifier), modifier = Modifier
.padding(5.dp)
.then(modifier),
) { ) {
Text(text = name, modifier = Modifier.padding(10.dp)) Text(text = name, modifier = Modifier.padding(10.dp))
} }
@ -86,47 +88,49 @@ class AddFriendDialog(
timeoutJob?.cancel() timeoutJob?.cancel()
dismiss() dismiss()
}) { }) {
if (hasFetchError) { Card {
Text(text = "Failed to load friends and groups. Make sure Snapchat is installed and logged in.") if (hasFetchError) {
return@Dialog Text(text = "Failed to load friends and groups. Make sure Snapchat is installed and logged in.")
} return@Card
if (cachedGroups == null || cachedFriends == null) { }
CircularProgressIndicator( if (cachedGroups == null || cachedFriends == null) {
modifier = Modifier CircularProgressIndicator(
.padding() modifier = Modifier
.size(30.dp), .padding()
strokeWidth = 3.dp, .size(30.dp),
color = MaterialTheme.colorScheme.onPrimary strokeWidth = 3.dp,
) color = MaterialTheme.colorScheme.onPrimary
return@Dialog )
} return@Card
}
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
item { item {
Text(text = "Groups", fontSize = 20.sp) Text(text = "Groups", fontSize = 20.sp)
Spacer(modifier = Modifier.padding(5.dp)) Spacer(modifier = Modifier.padding(5.dp))
} }
items(cachedGroups!!.size) { items(cachedGroups!!.size) {
ListCardEntry(name = cachedGroups!![it].name, modifier = Modifier.clickable { ListCardEntry(name = cachedGroups!![it].name, modifier = Modifier.clickable {
context.bridgeService.triggerGroupSync(cachedGroups!![it].conversationId) context.bridgeService.triggerGroupSync(cachedGroups!![it].conversationId)
context.modDatabase.executeAsync { context.modDatabase.executeAsync {
section.onResumed() section.onResumed()
} }
}) })
} }
item { item {
Text(text = "Friends", fontSize = 20.sp) Text(text = "Friends", fontSize = 20.sp)
Spacer(modifier = Modifier.padding(5.dp)) Spacer(modifier = Modifier.padding(5.dp))
} }
items(cachedFriends!!.size) { items(cachedFriends!!.size) {
ListCardEntry(name = cachedFriends!![it].displayName ?: cachedFriends!![it].mutableUsername, modifier = Modifier.clickable { ListCardEntry(name = cachedFriends!![it].displayName ?: cachedFriends!![it].mutableUsername, modifier = Modifier.clickable {
context.bridgeService.triggerFriendSync(cachedFriends!![it].userId) context.bridgeService.triggerFriendSync(cachedFriends!![it].userId)
context.modDatabase.executeAsync { context.modDatabase.executeAsync {
section.onResumed() section.onResumed()
} }
}) })
}
} }
} }
} }

View File

@ -1,4 +0,0 @@
package me.rhunk.snapenhance.ui.manager.sections.social
class FriendTab {
}

View File

@ -0,0 +1,72 @@
package me.rhunk.snapenhance.ui.manager.sections.social
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import me.rhunk.snapenhance.RemoteSideContext
class ScopeTab(
private val context: RemoteSideContext,
private val section: SocialSection,
private val navController: NavController,
private val id: String
) {
@Composable
fun Friend() {
//fetch the friend from the database
val friend = remember { context.modDatabase.getFriendInfo(id) } ?: run {
Text(text = "Friend not found")
return
}
Column {
Text(text = friend.displayName ?: "No display name", maxLines = 1)
Text(text = "bitmojiId: ${friend.bitmojiId ?: "No bitmojiId"}", maxLines = 1)
Text(text = "selfieId: ${friend.selfieId ?: "No selfieId"}", maxLines = 1)
Spacer(modifier = Modifier.height(16.dp))
OutlinedButton(onClick = {
context.modDatabase.deleteFriend(id)
section.onResumed()
navController.popBackStack()
}) {
Text(text = "Delete friend")
}
}
}
@Composable
fun Group() {
//fetch the group from the database
val group = remember { context.modDatabase.getGroupInfo(id) } ?: run {
Text(text = "Group not found")
return
}
Column {
Text(text = group.name, maxLines = 1)
Text(text = "participantsCount: ${group.participantsCount}", maxLines = 1)
Spacer(modifier = Modifier.height(16.dp))
OutlinedButton(onClick = {
context.modDatabase.deleteGroup(id)
section.onResumed()
navController.popBackStack()
}) {
Text(text = "Delete group")
}
}
}
}

View File

@ -1,14 +1,22 @@
package me.rhunk.snapenhance.ui.manager.sections.social package me.rhunk.snapenhance.ui.manager.sections.social
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Add import androidx.compose.material.icons.rounded.Add
import androidx.compose.material3.Card
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -18,6 +26,7 @@ import androidx.compose.material3.TabRow
import androidx.compose.material3.TabRowDefaults import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -26,6 +35,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import androidx.navigation.navigation
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo
import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo
@ -36,15 +48,48 @@ class SocialSection : Section() {
private lateinit var friendList: List<MessagingFriendInfo> private lateinit var friendList: List<MessagingFriendInfo>
private lateinit var groupList: List<MessagingGroupInfo> private lateinit var groupList: List<MessagingGroupInfo>
companion object {
const val MAIN_ROUTE = "social_route"
const val FRIEND_INFO_ROUTE = "friend_info/{id}"
const val GROUP_INFO_ROUTE = "group_info/{id}"
}
private var currentScopeTab: ScopeTab? = null
private val addFriendDialog by lazy { private val addFriendDialog by lazy {
AddFriendDialog(context, this) AddFriendDialog(context, this)
} }
//FIXME: don't reload the entire list when a friend is added/deleted
override fun onResumed() { override fun onResumed() {
friendList = context.modDatabase.getFriends() friendList = context.modDatabase.getFriends(descOrder = true)
groupList = context.modDatabase.getGroups() groupList = context.modDatabase.getGroups()
} }
override fun canGoBack() = navController.currentBackStackEntry?.destination?.route != MAIN_ROUTE
override fun build(navGraphBuilder: NavGraphBuilder) {
fun switchTab(id: String) = ScopeTab(context, this, navController, id).also { tab ->
currentScopeTab = tab
}
navGraphBuilder.navigation(route = enumSection.route, startDestination = MAIN_ROUTE) {
composable(MAIN_ROUTE) {
Content()
}
composable(FRIEND_INFO_ROUTE) {
val id = it.arguments?.getString("id") ?: return@composable
remember { switchTab(id) }.Friend()
}
composable(GROUP_INFO_ROUTE) {
val id = it.arguments?.getString("id") ?: return@composable
remember { switchTab(id) }.Group()
}
}
}
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
@ -100,23 +145,55 @@ class SocialSection : Section() {
} }
} }
HorizontalPager(modifier = Modifier.padding(paddingValues), state = pagerState) { page -> HorizontalPager(modifier = Modifier.padding(paddingValues), state = pagerState) { page ->
Column( when (page) {
modifier = Modifier.fillMaxSize(), 0 -> {
) { LazyColumn(
when (page) { modifier = Modifier
0 -> { .padding(10.dp)
Text(text = "Friends") .fillMaxWidth()
Column { ) {
friendList.forEach { if (friendList.isEmpty()) {
Text(text = it.displayName ?: it.mutableUsername) item {
Text(text = "No friends found")
}
}
items(friendList.size) { index ->
val friend = friendList[index]
Card(
modifier = Modifier
.padding(10.dp)
.fillMaxWidth()
.height(100.dp)
.clickable {
navController.navigate(
FRIEND_INFO_ROUTE.replace(
"{id}",
friend.userId
)
)
},
) {
Text(text = friend.displayName ?: friend.mutableUsername)
} }
} }
} }
1 -> { }
Text(text = "Groups") 1 -> {
Column { Column(
groupList.forEach { modifier = Modifier
.padding(10.dp)
.fillMaxSize()
.scrollable(rememberScrollState(), Orientation.Vertical)
) {
groupList.forEach {
Card(
modifier = Modifier
.padding(10.dp)
.fillMaxWidth()
.height(100.dp),
) {
Text(text = it.name) Text(text = it.name)
} }
} }

View File

@ -130,49 +130,51 @@ class SnapEnhance {
private fun syncRemote() { private fun syncRemote() {
val database = appContext.database val database = appContext.database
appContext.bridgeClient.sync(object : SyncCallback.Stub() { appContext.executeAsync {
override fun syncFriend(uuid: String): String? { appContext.bridgeClient.sync(object : SyncCallback.Stub() {
return database.getFriendInfo(uuid)?.toJson() override fun syncFriend(uuid: String): String? {
} return database.getFriendInfo(uuid)?.toJson()
}
override fun syncGroup(uuid: String): String? { override fun syncGroup(uuid: String): String? {
return database.getFeedEntryByConversationId(uuid)?.let { return database.getFeedEntryByConversationId(uuid)?.let {
MessagingGroupInfo(
it.key!!,
it.feedDisplayName!!,
it.participantsSize
).toJson()
}
}
})
appContext.event.subscribe(SnapWidgetBroadcastReceiveEvent::class) { event ->
if (event.action != BridgeClient.BRIDGE_SYNC_ACTION) return@subscribe
event.canceled = true
val feedEntries = appContext.database.getFeedEntries(Int.MAX_VALUE)
val groups = feedEntries.filter { it.friendUserId == null }.map {
MessagingGroupInfo( MessagingGroupInfo(
it.key!!, it.key!!,
it.feedDisplayName!!, it.feedDisplayName!!,
it.participantsSize it.participantsSize
).toJson() )
} }
}
})
appContext.event.subscribe(SnapWidgetBroadcastReceiveEvent::class) { event -> val friends = feedEntries.filter { it.friendUserId != null }.map {
if (event.action != BridgeClient.BRIDGE_SYNC_ACTION) return@subscribe MessagingFriendInfo(
event.canceled = true it.friendUserId!!,
val feedEntries = appContext.database.getFeedEntries(Int.MAX_VALUE) it.friendDisplayName,
it.friendDisplayUsername!!.split("|")[1],
it.bitmojiAvatarId,
it.bitmojiSelfieId
)
}
val groups = feedEntries.filter { it.friendUserId == null }.map { appContext.bridgeClient.passGroupsAndFriends(
MessagingGroupInfo( groups.map { it.toJson() },
it.key!!, friends.map { it.toJson() }
it.feedDisplayName!!,
it.participantsSize
) )
} }
val friends = feedEntries.filter { it.friendUserId != null }.map {
MessagingFriendInfo(
it.friendUserId!!,
it.friendDisplayName,
it.friendDisplayUsername!!.split("|")[1],
it.bitmojiAvatarId,
it.bitmojiSelfieId
)
}
appContext.bridgeClient.passGroupsAndFriends(
groups.map { it.toJson() },
friends.map { it.toJson() }
)
} }
} }
} }

View File

@ -16,7 +16,7 @@ import me.rhunk.snapenhance.bridge.types.BridgeFileType
import me.rhunk.snapenhance.bridge.types.FileActionType import me.rhunk.snapenhance.bridge.types.FileActionType
import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.core.BuildConfig
import me.rhunk.snapenhance.core.messaging.MessagingRule import me.rhunk.snapenhance.core.messaging.MessagingRule
import me.rhunk.snapenhance.core.messaging.RuleScope import me.rhunk.snapenhance.core.messaging.MessagingScope
import me.rhunk.snapenhance.data.LocalePair import me.rhunk.snapenhance.data.LocalePair
import me.rhunk.snapenhance.util.SerializableDataObject import me.rhunk.snapenhance.util.SerializableDataObject
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
@ -136,7 +136,7 @@ class BridgeClient(
fun passGroupsAndFriends(groups: List<String>, friends: List<String>) = service.passGroupsAndFriends(groups, friends) fun passGroupsAndFriends(groups: List<String>, friends: List<String>) = service.passGroupsAndFriends(groups, friends)
fun getRulesFromId(type: RuleScope, targetUuid: String): List<MessagingRule> { fun getRulesFromId(type: MessagingScope, targetUuid: String): List<MessagingRule> {
return service.getRules(type.name, targetUuid).map { return service.getRules(type.name, targetUuid).map {
SerializableDataObject.fromJson(it, MessagingRule::class.java) SerializableDataObject.fromJson(it, MessagingRule::class.java)
}.toList() }.toList()

View File

@ -8,18 +8,18 @@ enum class Mode {
WHITELIST WHITELIST
} }
enum class RuleScope { enum class MessagingScope {
FRIEND, FRIEND,
GROUP GROUP
} }
enum class ConversationFeature( enum class ConversationFeature(
val value: String, val value: String,
val ruleScope: RuleScope, val messagingScope: MessagingScope,
) { ) {
DOWNLOAD("download", RuleScope.FRIEND), DOWNLOAD("download", MessagingScope.FRIEND),
STEALTH("stealth", RuleScope.GROUP), STEALTH("stealth", MessagingScope.GROUP),
AUTO_SAVE("auto_save", RuleScope.GROUP); AUTO_SAVE("auto_save", MessagingScope.GROUP);
} }
data class FriendStreaks( data class FriendStreaks(
@ -47,7 +47,7 @@ data class MessagingFriendInfo(
data class MessagingRule( data class MessagingRule(
val id: Int, val id: Int,
val ruleScope: RuleScope, val messagingScope: MessagingScope,
val targetUuid: String, val targetUuid: String,
val enabled: Boolean, val enabled: Boolean,
val mode: Mode?, val mode: Mode?,