Skip to content

Commit

Permalink
Merge branch 'rhunk:dev' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
bocajthomas authored Nov 8, 2024
2 parents ccdf04d + 4079745 commit bf5942f
Show file tree
Hide file tree
Showing 18 changed files with 379 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class StreaksReminder(

notifyFriendList.forEach { (streaks, friend) ->
remoteSideContext.coroutineScope.launch {
val bitmojiUrl = BitmojiSelfie.getBitmojiSelfie(friend.selfieId, friend.bitmojiId, BitmojiSelfie.BitmojiSelfieType.THREE_D)
val bitmojiUrl = BitmojiSelfie.getBitmojiSelfie(friend.selfieId, friend.bitmojiId, BitmojiSelfie.BitmojiSelfieType.NEW_THREE_D)
val bitmojiImage = remoteSideContext.imageLoader.execute(
ImageRequestHelper.newBitmojiImageRequest(ctx, bitmojiUrl)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ class BridgeService : Service() {
)
}

override fun getScopeNotes(id: String): String? {
return remoteSideContext.database.getScopeNotes(id)
}

override fun setScopeNotes(id: String, content: String?) {
remoteSideContext.database.setScopeNotes(id, content)
}

override fun getScriptingInterface() = remoteSideContext.scriptManager

override fun getE2eeInterface() = remoteSideContext.e2eeImplementation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ class AppDatabase(
"repositories" to listOf(
"url VARCHAR PRIMARY KEY",
),
"notes" to listOf(
"id CHAR(36) PRIMARY KEY",
"content TEXT",
),
))
}
}
32 changes: 32 additions & 0 deletions app/src/main/kotlin/me/rhunk/snapenhance/storage/ScopeNotes.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package me.rhunk.snapenhance.storage

import androidx.core.database.getStringOrNull

fun AppDatabase.getScopeNotes(id: String): String? {
return database.rawQuery("SELECT content FROM notes WHERE id = ?", arrayOf(id)).use {
if (it.moveToNext()) {
it.getStringOrNull(0)
} else {
null
}
}
}

fun AppDatabase.setScopeNotes(id: String, content: String?) {
if (content == null || content.isEmpty() == true) {
executeAsync {
database.execSQL("DELETE FROM notes WHERE id = ?", arrayOf(id))
}
return
}

executeAsync {
database.execSQL("INSERT OR REPLACE INTO notes (id, content) VALUES (?, ?)", arrayOf(id, content))
}
}

fun AppDatabase.deleteScopeNotes(id: String) {
executeAsync {
database.execSQL("DELETE FROM notes WHERE id = ?", arrayOf(id))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavBackStackEntry
import androidx.navigation.compose.currentBackStackEntryAsState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import me.rhunk.snapenhance.common.data.FriendStreaks
import me.rhunk.snapenhance.common.data.MessagingFriendInfo
import me.rhunk.snapenhance.common.data.MessagingGroupInfo
import me.rhunk.snapenhance.common.data.MessagingRuleType
import me.rhunk.snapenhance.common.data.SocialScope
import me.rhunk.snapenhance.common.ui.AutoClearKeyboardFocus
import me.rhunk.snapenhance.common.ui.EditNoteTextField
import me.rhunk.snapenhance.common.ui.rememberAsyncMutableState
import me.rhunk.snapenhance.common.ui.rememberAsyncMutableStateList
import me.rhunk.snapenhance.common.util.snap.BitmojiSelfie
Expand Down Expand Up @@ -89,6 +93,9 @@ class ManageScope: Routes.Route() {
.verticalScroll(rememberScrollState())
.fillMaxSize()
) {
var bottomComposable by remember {
mutableStateOf(null as (@Composable () -> Unit)?)
}
var hasScope by remember {
mutableStateOf(null as Boolean?)
}
Expand All @@ -103,7 +110,7 @@ class ManageScope: Routes.Route() {
}
}
friend?.let {
Friend(id, it, streaks)
Friend(id, it, streaks) { bottomComposable = it }
}
}
SocialScope.GROUP -> {
Expand All @@ -113,13 +120,17 @@ class ManageScope: Routes.Route() {
}
}
group?.let {
Group(it)
Group(it) { bottomComposable = it }
}
}
}
if (hasScope == true) {
if (context.config.root.experimental.friendNotes.get()) {
NotesCard(id)
}
RulesCard(id)
}
bottomComposable?.invoke()
if (hasScope == false) {
Column(
modifier = Modifier.fillMaxSize(),
Expand All @@ -136,6 +147,33 @@ class ManageScope: Routes.Route() {
}
}

@Composable
private fun NotesCard(
id: String
) {
val coroutineScope = rememberCoroutineScope { Dispatchers.IO }
var scopeNotes by rememberAsyncMutableState(null) {
context.database.getScopeNotes(id)
}

AutoClearKeyboardFocus()

EditNoteTextField(
modifier = Modifier.padding(8.dp),
primaryColor = Color.White,
translation = context.translation,
content = scopeNotes,
setContent = { scopeNotes = it }
)

DisposableEffect(Unit) {
onDispose {
coroutineScope.launch {
context.database.setScopeNotes(id, scopeNotes)
}
}
}
}

@Composable
private fun RulesCard(
Expand Down Expand Up @@ -183,7 +221,7 @@ class ManageScope: Routes.Route() {

@Composable
private fun ContentCard(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Card(
ElevatedCard(
modifier = Modifier
.padding(10.dp)
.fillMaxWidth()
Expand Down Expand Up @@ -244,26 +282,105 @@ class ManageScope: Routes.Route() {
private fun Friend(
id: String,
friend: MessagingFriendInfo,
streaks: FriendStreaks?
streaks: FriendStreaks?,
setBottomComposable: ((@Composable () -> Unit)?) -> Unit = {}
) {
LaunchedEffect(Unit) {
setBottomComposable {
Spacer(modifier = Modifier.height(16.dp))

if (context.config.root.experimental.e2eEncryption.globalState == true) {
SectionTitle(translation["e2ee_title"])
var hasSecretKey by rememberAsyncMutableState(defaultValue = false) {
context.e2eeImplementation.friendKeyExists(friend.userId)
}
var importDialog by remember { mutableStateOf(false) }

if (importDialog) {
Dialog(
onDismissRequest = { importDialog = false }
) {
dialogs.RawInputDialog(onDismiss = { importDialog = false }, onConfirm = { newKey ->
importDialog = false
runCatching {
val key = Base64.decode(newKey)
if (key.size != 32) {
context.longToast("Invalid key size (must be 32 bytes)")
return@runCatching
}

context.coroutineScope.launch {
context.e2eeImplementation.storeSharedSecretKey(friend.userId, key)
context.longToast("Successfully imported key")
}

hasSecretKey = true
}.onFailure {
context.longToast("Failed to import key: ${it.message}")
context.log.error("Failed to import key", it)
}
})
}
}

ContentCard {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
if (hasSecretKey) {
OutlinedButton(onClick = {
context.coroutineScope.launch {
val secretKey = Base64.encode(context.e2eeImplementation.getSharedSecretKey(friend.userId) ?: return@launch)
//TODO: fingerprint auth
context.activity!!.startActivity(Intent.createChooser(Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, secretKey)
type = "text/plain"
}, "").apply {
putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(
Intent().apply {
putExtra(Intent.EXTRA_TEXT, secretKey)
putExtra(Intent.EXTRA_SUBJECT, secretKey)
})
)
})
}
}) {
Text(
text = "Export Base64",
maxLines = 1
)
}
}

OutlinedButton(onClick = { importDialog = true }) {
Text(
text = "Import Base64",
maxLines = 1
)
}
}
}
}
}
}
Column(
modifier = Modifier
.padding(10.dp)
.padding(5.dp)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
val bitmojiUrl = BitmojiSelfie.getBitmojiSelfie(
friend.selfieId, friend.bitmojiId, BitmojiSelfie.BitmojiSelfieType.THREE_D
friend.selfieId, friend.bitmojiId, BitmojiSelfie.BitmojiSelfieType.NEW_THREE_D
)
BitmojiImage(context = context, url = bitmojiUrl, size = 100)
Spacer(modifier = Modifier.height(16.dp))
BitmojiImage(context = context, url = bitmojiUrl, size = 120)
Text(
text = friend.displayName ?: friend.mutableUsername,
maxLines = 1,
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(5.dp))
Text(
text = friend.mutableUsername,
maxLines = 1,
Expand All @@ -272,8 +389,6 @@ class ManageScope: Routes.Route() {
)
}

Spacer(modifier = Modifier.height(16.dp))

if (context.config.root.experimental.storyLogger.get()) {
Row(
modifier = Modifier.fillMaxWidth(),
Expand Down Expand Up @@ -332,87 +447,14 @@ class ManageScope: Routes.Route() {
}
}
}
Spacer(modifier = Modifier.height(16.dp))

if (context.config.root.experimental.e2eEncryption.globalState == true) {
SectionTitle(translation["e2ee_title"])
var hasSecretKey by rememberAsyncMutableState(defaultValue = false) {
context.e2eeImplementation.friendKeyExists(friend.userId)
}
var importDialog by remember { mutableStateOf(false) }

if (importDialog) {
Dialog(
onDismissRequest = { importDialog = false }
) {
dialogs.RawInputDialog(onDismiss = { importDialog = false }, onConfirm = { newKey ->
importDialog = false
runCatching {
val key = Base64.decode(newKey)
if (key.size != 32) {
context.longToast("Invalid key size (must be 32 bytes)")
return@runCatching
}

context.coroutineScope.launch {
context.e2eeImplementation.storeSharedSecretKey(friend.userId, key)
context.longToast("Successfully imported key")
}

hasSecretKey = true
}.onFailure {
context.longToast("Failed to import key: ${it.message}")
context.log.error("Failed to import key", it)
}
})
}
}

ContentCard {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
if (hasSecretKey) {
OutlinedButton(onClick = {
context.coroutineScope.launch {
val secretKey = Base64.encode(context.e2eeImplementation.getSharedSecretKey(friend.userId) ?: return@launch)
//TODO: fingerprint auth
context.activity!!.startActivity(Intent.createChooser(Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, secretKey)
type = "text/plain"
}, "").apply {
putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(
Intent().apply {
putExtra(Intent.EXTRA_TEXT, secretKey)
putExtra(Intent.EXTRA_SUBJECT, secretKey)
})
)
})
}
}) {
Text(
text = "Export Base64",
maxLines = 1
)
}
}

OutlinedButton(onClick = { importDialog = true }) {
Text(
text = "Import Base64",
maxLines = 1
)
}
}
}
}
}
}

@Composable
private fun Group(group: MessagingGroupInfo) {
private fun Group(
group: MessagingGroupInfo,
setBottomComposable: ((@Composable () -> Unit)?) -> Unit = {}
) {
Column(
modifier = Modifier
.padding(10.dp)
Expand All @@ -422,7 +464,6 @@ class ManageScope: Routes.Route() {
Text(
text = group.name, maxLines = 1, fontSize = 20.sp, fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(5.dp))
Text(
text = translation.format(
"participants_text", "count" to group.participantsCount.toString()
Expand Down
Loading

0 comments on commit bf5942f

Please sign in to comment.