mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 05:37:48 +02:00
feat: scope tab base logic
This commit is contained in:
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
package me.rhunk.snapenhance.ui.manager.sections.social
|
|
||||||
|
|
||||||
class FriendTab {
|
|
||||||
}
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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() }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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()
|
||||||
|
@ -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?,
|
||||||
|
Reference in New Issue
Block a user