mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 05:37:48 +02:00
feat: scope content
- refactor image loader - rules
This commit is contained in:
@ -5,6 +5,10 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import coil.ImageLoader
|
||||
import coil.decode.VideoFrameDecoder
|
||||
import coil.memory.MemoryCache
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import me.rhunk.snapenhance.bridge.BridgeService
|
||||
import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper
|
||||
import me.rhunk.snapenhance.bridge.wrapper.MappingsWrapper
|
||||
@ -35,6 +39,17 @@ class RemoteSideContext(
|
||||
val downloadTaskManager = DownloadTaskManager()
|
||||
val modDatabase = ModDatabase(this)
|
||||
|
||||
//used to load bitmoji selfies and download previews
|
||||
val imageLoader by lazy {
|
||||
ImageLoader.Builder(androidContext)
|
||||
.dispatcher(Dispatchers.IO)
|
||||
.memoryCache {
|
||||
MemoryCache.Builder(androidContext)
|
||||
.maxSizePercent(0.25)
|
||||
.build()
|
||||
}.components { add(VideoFrameDecoder.Factory()) }.build()
|
||||
}
|
||||
|
||||
init {
|
||||
runCatching {
|
||||
config.loadFromContext(androidContext)
|
||||
|
@ -12,7 +12,7 @@ import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper
|
||||
import me.rhunk.snapenhance.bridge.wrapper.MessageLoggerWrapper
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingScope
|
||||
import me.rhunk.snapenhance.core.messaging.SocialScope
|
||||
import me.rhunk.snapenhance.database.objects.FriendInfo
|
||||
import me.rhunk.snapenhance.download.DownloadProcessor
|
||||
import me.rhunk.snapenhance.util.SerializableDataObject
|
||||
@ -112,7 +112,7 @@ class BridgeService : Service() {
|
||||
}
|
||||
|
||||
override fun getRules(objectType: String, uuid: String): MutableList<String> {
|
||||
remoteSideContext.modDatabase.getRulesFromId(MessagingScope.valueOf(objectType), uuid)
|
||||
remoteSideContext.modDatabase.getRulesFromId(SocialScope.valueOf(objectType), uuid)
|
||||
.let { rules ->
|
||||
return rules.map { it.toJson() }.toMutableList()
|
||||
}
|
||||
|
@ -7,8 +7,7 @@ import me.rhunk.snapenhance.core.messaging.FriendStreaks
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingRule
|
||||
import me.rhunk.snapenhance.core.messaging.Mode
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingScope
|
||||
import me.rhunk.snapenhance.core.messaging.SocialScope
|
||||
import me.rhunk.snapenhance.database.objects.FriendInfo
|
||||
import me.rhunk.snapenhance.util.SQLiteDatabaseHelper
|
||||
import me.rhunk.snapenhance.util.ktx.getInteger
|
||||
@ -50,8 +49,7 @@ class ModDatabase(
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT",
|
||||
"scope VARCHAR",
|
||||
"targetUuid VARCHAR",
|
||||
"enabled BOOLEAN",
|
||||
"mode VARCHAR",
|
||||
//"mode VARCHAR",
|
||||
"subject VARCHAR"
|
||||
),
|
||||
"streaks" to listOf(
|
||||
@ -153,16 +151,14 @@ class ModDatabase(
|
||||
}
|
||||
}
|
||||
|
||||
fun getRulesFromId(type: MessagingScope, targetUuid: String): List<MessagingRule> {
|
||||
return database.rawQuery("SELECT * FROM rules WHERE objectType = ? AND targetUuid = ?", arrayOf(type.name, targetUuid)).use { cursor ->
|
||||
fun getRulesFromId(type: SocialScope, targetUuid: String): List<MessagingRule> {
|
||||
return database.rawQuery("SELECT * FROM rules WHERE scope = ? AND targetUuid = ?", arrayOf(type.name, targetUuid)).use { cursor ->
|
||||
val rules = mutableListOf<MessagingRule>()
|
||||
while (cursor.moveToNext()) {
|
||||
rules.add(MessagingRule(
|
||||
id = cursor.getInteger("id"),
|
||||
messagingScope = MessagingScope.valueOf(cursor.getStringOrNull("scope")!!),
|
||||
socialScope = SocialScope.valueOf(cursor.getStringOrNull("scope")!!),
|
||||
targetUuid = cursor.getStringOrNull("targetUuid")!!,
|
||||
enabled = cursor.getInteger("enabled") == 1,
|
||||
mode = Mode.valueOf(cursor.getStringOrNull("mode")!!),
|
||||
subject = cursor.getStringOrNull("subject")!!
|
||||
))
|
||||
}
|
||||
@ -170,6 +166,24 @@ class ModDatabase(
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleRuleFor(type: SocialScope, targetUuid: String, subject: String, enabled: Boolean) {
|
||||
executeAsync {
|
||||
if (enabled) {
|
||||
database.execSQL("INSERT OR REPLACE INTO rules (scope, targetUuid, subject) VALUES (?, ?, ?)", arrayOf(
|
||||
type.name,
|
||||
targetUuid,
|
||||
subject
|
||||
))
|
||||
} else {
|
||||
database.execSQL("DELETE FROM rules WHERE scope = ? AND targetUuid = ? AND subject = ?", arrayOf(
|
||||
type.name,
|
||||
targetUuid,
|
||||
subject
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFriendInfo(userId: String): MessagingFriendInfo? {
|
||||
return database.rawQuery("SELECT * FROM friends WHERE userId = ?", arrayOf(userId)).use { cursor ->
|
||||
if (!cursor.moveToFirst()) return@use null
|
||||
|
@ -15,7 +15,6 @@ 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.requiredWidthIn
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
@ -49,34 +48,19 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.ImageLoader
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import coil.decode.VideoFrameDecoder
|
||||
import coil.memory.MemoryCache
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.Precision
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.R
|
||||
import me.rhunk.snapenhance.data.FileType
|
||||
import me.rhunk.snapenhance.download.data.DownloadObject
|
||||
import me.rhunk.snapenhance.download.data.MediaFilter
|
||||
import me.rhunk.snapenhance.ui.manager.Section
|
||||
import me.rhunk.snapenhance.ui.util.BitmojiImage
|
||||
import me.rhunk.snapenhance.ui.util.ImageRequestHelper
|
||||
|
||||
class DownloadsSection : Section() {
|
||||
private val loadedDownloads = mutableStateOf(mapOf<Int, DownloadObject>())
|
||||
private var currentFilter = mutableStateOf(MediaFilter.NONE)
|
||||
|
||||
private val imageLoader by lazy {
|
||||
ImageLoader.Builder(context.androidContext)
|
||||
.dispatcher(Dispatchers.IO)
|
||||
.memoryCache {
|
||||
MemoryCache.Builder(context.androidContext)
|
||||
.maxSizePercent(0.25)
|
||||
.build()
|
||||
}.components { add(VideoFrameDecoder.Factory()) }.build()
|
||||
}
|
||||
|
||||
override fun onResumed() {
|
||||
super.onResumed()
|
||||
loadByFilter(currentFilter.value)
|
||||
@ -156,12 +140,11 @@ class DownloadsSection : Section() {
|
||||
Box(modifier = Modifier.height(100.dp)) {
|
||||
Image(
|
||||
painter = rememberAsyncImagePainter(
|
||||
model = ImageRequest.Builder(context.androidContext)
|
||||
.data(download.outputFile)
|
||||
.memoryCacheKey(download.outputFile)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
imageLoader = imageLoader
|
||||
model = ImageRequestHelper.newDownloadPreviewImageRequest(
|
||||
context.androidContext,
|
||||
download.outputFile
|
||||
),
|
||||
imageLoader = context.imageLoader
|
||||
),
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
@ -187,25 +170,7 @@ class DownloadsSection : Section() {
|
||||
.padding(15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painter = rememberAsyncImagePainter(
|
||||
model = ImageRequest.Builder(context.androidContext)
|
||||
.data(download.metadata.iconUrl)
|
||||
.fallback(R.drawable.bitmoji_blank)
|
||||
.precision(Precision.INEXACT)
|
||||
.crossfade(true)
|
||||
.memoryCacheKey(download.metadata.iconUrl)
|
||||
.build(),
|
||||
imageLoader = imageLoader
|
||||
),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.requiredWidthIn(min = 0.dp, max = 48.dp)
|
||||
.height(48.dp)
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
)
|
||||
|
||||
BitmojiImage(context = context, url = download.metadata.iconUrl, size = 48)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 10.dp),
|
||||
|
@ -0,0 +1,116 @@
|
||||
package me.rhunk.snapenhance.ui.manager.sections.social
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.RemoteSideContext
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingRuleType
|
||||
import me.rhunk.snapenhance.core.messaging.SocialScope
|
||||
|
||||
class ScopeContent(
|
||||
private val context: RemoteSideContext,
|
||||
private val section: SocialSection,
|
||||
private val navController: NavController,
|
||||
private val scope: SocialScope,
|
||||
private val id: String
|
||||
) {
|
||||
|
||||
@Composable
|
||||
private fun DeleteScopeEntityButton() {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
OutlinedButton(onClick = {
|
||||
when (scope) {
|
||||
SocialScope.FRIEND -> context.modDatabase.deleteFriend(id)
|
||||
SocialScope.GROUP -> context.modDatabase.deleteGroup(id)
|
||||
}
|
||||
context.modDatabase.executeAsync {
|
||||
coroutineScope.launch {
|
||||
section.onResumed()
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Text(text = "Delete ${scope.key}")
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Content() {
|
||||
Column {
|
||||
when (scope) {
|
||||
SocialScope.FRIEND -> Friend()
|
||||
SocialScope.GROUP -> Group()
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
val scopeRules = context.modDatabase.getRulesFromId(scope, id)
|
||||
|
||||
Text(text = "Rules", maxLines = 1)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
//manager anti features etc
|
||||
MessagingRuleType.values().forEach { feature ->
|
||||
var featureEnabled by remember {
|
||||
mutableStateOf(scopeRules.any { it.subject == feature.key })
|
||||
}
|
||||
val featureEnabledText = if (featureEnabled) "Enabled" else "Disabled"
|
||||
Row {
|
||||
Text(text = "${feature.key}: $featureEnabledText", maxLines = 1)
|
||||
Switch(checked = featureEnabled, onCheckedChange = {
|
||||
context.modDatabase.toggleRuleFor(scope, id, feature.key, it)
|
||||
featureEnabled = it
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private 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))
|
||||
DeleteScopeEntityButton()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
private 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))
|
||||
DeleteScopeEntityButton()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -2,9 +2,9 @@ package me.rhunk.snapenhance.ui.manager.sections.social
|
||||
|
||||
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.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
@ -26,12 +25,13 @@ import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.TabRowDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -41,8 +41,11 @@ import androidx.navigation.navigation
|
||||
import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo
|
||||
import me.rhunk.snapenhance.core.messaging.SocialScope
|
||||
import me.rhunk.snapenhance.ui.manager.Section
|
||||
import me.rhunk.snapenhance.ui.util.BitmojiImage
|
||||
import me.rhunk.snapenhance.ui.util.pagerTabIndicatorOffset
|
||||
import me.rhunk.snapenhance.util.snap.BitmojiSelfie
|
||||
|
||||
class SocialSection : Section() {
|
||||
private lateinit var friendList: List<MessagingFriendInfo>
|
||||
@ -54,7 +57,7 @@ class SocialSection : Section() {
|
||||
const val GROUP_INFO_ROUTE = "group_info/{id}"
|
||||
}
|
||||
|
||||
private var currentScopeTab: ScopeTab? = null
|
||||
private var currentScopeContent: ScopeContent? = null
|
||||
|
||||
private val addFriendDialog by lazy {
|
||||
AddFriendDialog(context, this)
|
||||
@ -69,23 +72,96 @@ class SocialSection : Section() {
|
||||
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) {
|
||||
SocialScope.values().forEach { scope ->
|
||||
composable(scope.tabRoute) {
|
||||
val id = it.arguments?.getString("id") ?: return@composable
|
||||
remember { switchTab(id) }.Friend()
|
||||
remember {
|
||||
ScopeContent(context, this@SocialSection, navController, scope, id).also { tab ->
|
||||
currentScopeContent = tab
|
||||
}
|
||||
}.Content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
composable(GROUP_INFO_ROUTE) {
|
||||
val id = it.arguments?.getString("id") ?: return@composable
|
||||
remember { switchTab(id) }.Group()
|
||||
|
||||
@Composable
|
||||
private fun ScopeList(scope: SocialScope) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(2.dp)
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
//check if scope list is empty
|
||||
val listSize = when (scope) {
|
||||
SocialScope.GROUP -> groupList.size
|
||||
SocialScope.FRIEND -> friendList.size
|
||||
}
|
||||
|
||||
if (listSize == 0) {
|
||||
item {
|
||||
//TODO: i18n
|
||||
Text(text = "No ${scope.key.lowercase()}s found")
|
||||
}
|
||||
}
|
||||
|
||||
items(listSize) { index ->
|
||||
val id = when (scope) {
|
||||
SocialScope.GROUP -> groupList[index].conversationId
|
||||
SocialScope.FRIEND -> friendList[index].userId
|
||||
}
|
||||
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
.fillMaxWidth()
|
||||
.height(80.dp)
|
||||
.clickable {
|
||||
navController.navigate(
|
||||
scope.tabRoute.replace("{id}", id)
|
||||
)
|
||||
},
|
||||
) {
|
||||
when (scope) {
|
||||
SocialScope.GROUP -> {
|
||||
val group = groupList[index]
|
||||
Column {
|
||||
Text(text = group.name, maxLines = 1)
|
||||
Text(text = "participantsCount: ${group.participantsCount}", maxLines = 1)
|
||||
}
|
||||
}
|
||||
SocialScope.FRIEND -> {
|
||||
val friend = friendList[index]
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
.fillMaxSize(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
val bitmojiUrl = (friend.selfieId to friend.bitmojiId).let { (selfieId, bitmojiId) ->
|
||||
if (selfieId == null || bitmojiId == null) return@let null
|
||||
BitmojiSelfie.getBitmojiSelfie(selfieId, bitmojiId, BitmojiSelfie.BitmojiSelfieType.STANDARD)
|
||||
}
|
||||
BitmojiImage(context = context, url = bitmojiUrl)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(text = friend.displayName ?: friend.mutableUsername, maxLines = 1)
|
||||
Text(text = friend.userId, maxLines = 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -145,60 +221,10 @@ class SocialSection : Section() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HorizontalPager(modifier = Modifier.padding(paddingValues), state = pagerState) { page ->
|
||||
when (page) {
|
||||
0 -> {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
if (friendList.isEmpty()) {
|
||||
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 -> {
|
||||
Column(
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0 -> ScopeList(SocialScope.FRIEND)
|
||||
1 -> ScopeList(SocialScope.GROUP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
package me.rhunk.snapenhance.ui.util
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.requiredWidthIn
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.Precision
|
||||
import me.rhunk.snapenhance.R
|
||||
import me.rhunk.snapenhance.RemoteSideContext
|
||||
|
||||
@Composable
|
||||
fun BitmojiImage(context: RemoteSideContext, modifier: Modifier = Modifier, size: Int = 48, url: String?) {
|
||||
Image(
|
||||
painter = rememberAsyncImagePainter(
|
||||
model = ImageRequestHelper.newBitmojiImageRequest(
|
||||
context.androidContext,
|
||||
url
|
||||
),
|
||||
imageLoader = context.imageLoader
|
||||
),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.requiredWidthIn(min = 0.dp, max = size.dp)
|
||||
.height(size.dp)
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.then(modifier)
|
||||
)
|
||||
}
|
||||
|
||||
object ImageRequestHelper {
|
||||
fun newBitmojiImageRequest(context: Context, url: String?) = ImageRequest.Builder(context)
|
||||
.data(url)
|
||||
.fallback(R.drawable.bitmoji_blank)
|
||||
.precision(Precision.INEXACT)
|
||||
.crossfade(true)
|
||||
.memoryCacheKey(url)
|
||||
.build()
|
||||
|
||||
fun newDownloadPreviewImageRequest(context: Context, filePath: String?) = ImageRequest.Builder(context)
|
||||
.data(filePath)
|
||||
.memoryCacheKey(filePath)
|
||||
.crossfade(true)
|
||||
.build()
|
||||
}
|
@ -16,7 +16,7 @@ import me.rhunk.snapenhance.bridge.types.BridgeFileType
|
||||
import me.rhunk.snapenhance.bridge.types.FileActionType
|
||||
import me.rhunk.snapenhance.core.BuildConfig
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingRule
|
||||
import me.rhunk.snapenhance.core.messaging.MessagingScope
|
||||
import me.rhunk.snapenhance.core.messaging.SocialScope
|
||||
import me.rhunk.snapenhance.data.LocalePair
|
||||
import me.rhunk.snapenhance.util.SerializableDataObject
|
||||
import java.util.concurrent.CompletableFuture
|
||||
@ -136,7 +136,7 @@ class BridgeClient(
|
||||
|
||||
fun passGroupsAndFriends(groups: List<String>, friends: List<String>) = service.passGroupsAndFriends(groups, friends)
|
||||
|
||||
fun getRulesFromId(type: MessagingScope, targetUuid: String): List<MessagingRule> {
|
||||
fun getRulesFromId(type: SocialScope, targetUuid: String): List<MessagingRule> {
|
||||
return service.getRules(type.name, targetUuid).map {
|
||||
SerializableDataObject.fromJson(it, MessagingRule::class.java)
|
||||
}.toList()
|
||||
|
@ -8,18 +8,21 @@ enum class Mode {
|
||||
WHITELIST
|
||||
}
|
||||
|
||||
enum class MessagingScope {
|
||||
FRIEND,
|
||||
GROUP
|
||||
enum class SocialScope(
|
||||
val key: String,
|
||||
val tabRoute: String,
|
||||
) {
|
||||
FRIEND("friend", "friend_info/{id}"),
|
||||
GROUP("group", "group_info/{id}"),
|
||||
}
|
||||
|
||||
enum class ConversationFeature(
|
||||
val value: String,
|
||||
val messagingScope: MessagingScope,
|
||||
enum class MessagingRuleType(
|
||||
val key: String,
|
||||
val socialScope: SocialScope,
|
||||
) {
|
||||
DOWNLOAD("download", MessagingScope.FRIEND),
|
||||
STEALTH("stealth", MessagingScope.GROUP),
|
||||
AUTO_SAVE("auto_save", MessagingScope.GROUP);
|
||||
DOWNLOAD("download", SocialScope.FRIEND),
|
||||
STEALTH("stealth", SocialScope.GROUP),
|
||||
AUTO_SAVE("auto_save", SocialScope.GROUP);
|
||||
}
|
||||
|
||||
data class FriendStreaks(
|
||||
@ -47,9 +50,8 @@ data class MessagingFriendInfo(
|
||||
|
||||
data class MessagingRule(
|
||||
val id: Int,
|
||||
val messagingScope: MessagingScope,
|
||||
val socialScope: SocialScope,
|
||||
val targetUuid: String,
|
||||
val enabled: Boolean,
|
||||
val mode: Mode?,
|
||||
//val mode: Mode?,
|
||||
val subject: String
|
||||
) : SerializableDataObject()
|
Reference in New Issue
Block a user