mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-05 08:54:29 +02:00
chore(translation): new strings
This commit is contained in:
parent
b610825a22
commit
1d0456e8a0
@ -78,6 +78,8 @@ class Routes(
|
|||||||
lateinit var routeInfo: RouteInfo
|
lateinit var routeInfo: RouteInfo
|
||||||
lateinit var routes: Routes
|
lateinit var routes: Routes
|
||||||
|
|
||||||
|
val translation by lazy { context.translation.getCategory("manager.sections.${routeInfo.key.substringBefore("/")}")}
|
||||||
|
|
||||||
private fun replaceArguments(id: String, args: Map<String, String>) = args.takeIf { it.isNotEmpty() }?.let {
|
private fun replaceArguments(id: String, args: Map<String, String>) = args.takeIf { it.isNotEmpty() }?.let {
|
||||||
args.entries.fold(id) { acc, (key, value) ->
|
args.entries.fold(id) { acc, (key, value) ->
|
||||||
acc.replace("{$key}", value)
|
acc.replace("{$key}", value)
|
||||||
|
@ -126,7 +126,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
|||||||
LaunchedEffect(Unit, message) {
|
LaunchedEffect(Unit, message) {
|
||||||
runCatching {
|
runCatching {
|
||||||
decodeMessage(message) { senderId, contentType, messageReader, attachments ->
|
decodeMessage(message) { senderId, contentType, messageReader, attachments ->
|
||||||
val senderUsername = senderId?.let { context.modDatabase.getFriendInfo(it)?.mutableUsername } ?: "unknown sender"
|
val senderUsername = senderId?.let { context.modDatabase.getFriendInfo(it)?.mutableUsername } ?: translation["unknown_sender"]
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ContentHeader() {
|
fun ContentHeader() {
|
||||||
@ -134,7 +134,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (contentType == ContentType.CHAT) {
|
if (contentType == ContentType.CHAT) {
|
||||||
val content = messageReader.getString(2, 1) ?: "[empty chat message]"
|
val content = messageReader.getString(2, 1) ?: "[${translation["empty_message"]}]"
|
||||||
contentView = {
|
contentView = {
|
||||||
Column {
|
Column {
|
||||||
Text(content, modifier = Modifier
|
Text(content, modifier = Modifier
|
||||||
@ -166,7 +166,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
|||||||
downloadAttachment(message.timestamp, attachment)
|
downloadAttachment(message.timestamp, attachment)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
context.log.error("Failed to download attachment", it)
|
context.log.error("Failed to download attachment", it)
|
||||||
context.shortToast("Failed to download attachment")
|
context.shortToast(translation["download_attachment_failed_toast"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
@ -175,7 +175,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
|||||||
contentDescription = "Download",
|
contentDescription = "Download",
|
||||||
modifier = Modifier.padding(end = 4.dp)
|
modifier = Modifier.padding(end = 4.dp)
|
||||||
)
|
)
|
||||||
Text("Attachment ${index + 1}")
|
Text(translation.format("chat_attachment", "index" to (index + 1).toString()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,7 +186,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
|||||||
}.onFailure {
|
}.onFailure {
|
||||||
context.log.error("Failed to parse message", it)
|
context.log.error("Failed to parse message", it)
|
||||||
contentView = {
|
contentView = {
|
||||||
Text("[Failed to parse message]")
|
Text("[${translation["message_parse_failed"]}]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,8 +212,10 @@ class LoggerHistoryRoot : Routes.Route() {
|
|||||||
) {
|
) {
|
||||||
fun formatConversationId(conversationId: String?): String? {
|
fun formatConversationId(conversationId: String?): String? {
|
||||||
if (conversationId == null) return null
|
if (conversationId == null) return null
|
||||||
return context.modDatabase.getGroupInfo(conversationId)?.name?.let { "Group $it" } ?: context.modDatabase.findFriend(conversationId)?.let {
|
return context.modDatabase.getGroupInfo(conversationId)?.name?.let {
|
||||||
"Friend " + (it.displayName?.let { name -> "$name (${it.mutableUsername})" } ?: it.mutableUsername)
|
translation.format("list_group_format", "name" to it)
|
||||||
|
} ?: context.modDatabase.findFriend(conversationId)?.let {
|
||||||
|
translation.format("list_friend_format", "name" to (it.displayName?.let { name -> "$name (${it.mutableUsername})" } ?: it.mutableUsername))
|
||||||
} ?: conversationId
|
} ?: conversationId
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +259,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
) {
|
) {
|
||||||
Text("Reverse order")
|
Text(translation["reverse_order_checkbox"])
|
||||||
Checkbox(checked = reverseOrder, onCheckedChange = {
|
Checkbox(checked = reverseOrder, onCheckedChange = {
|
||||||
reverseOrder = it
|
reverseOrder = it
|
||||||
})
|
})
|
||||||
@ -275,7 +277,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
|||||||
item {
|
item {
|
||||||
if (selectedConversation != null) {
|
if (selectedConversation != null) {
|
||||||
if (hasReachedEnd) {
|
if (hasReachedEnd) {
|
||||||
Text("No more messages", modifier = Modifier
|
Text(translation["no_more_messages"], modifier = Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.fillMaxWidth(), textAlign = TextAlign.Center)
|
.fillMaxWidth(), textAlign = TextAlign.Center)
|
||||||
} else {
|
} else {
|
||||||
|
@ -8,8 +8,8 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@ -20,8 +20,8 @@ import androidx.compose.ui.graphics.StrokeCap
|
|||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.navigation.NavBackStackEntry
|
import androidx.navigation.NavBackStackEntry
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.rhunk.snapenhance.bridge.DownloadCallback
|
import me.rhunk.snapenhance.bridge.DownloadCallback
|
||||||
@ -36,8 +36,8 @@ import me.rhunk.snapenhance.task.PendingTaskListener
|
|||||||
import me.rhunk.snapenhance.task.Task
|
import me.rhunk.snapenhance.task.Task
|
||||||
import me.rhunk.snapenhance.task.TaskStatus
|
import me.rhunk.snapenhance.task.TaskStatus
|
||||||
import me.rhunk.snapenhance.task.TaskType
|
import me.rhunk.snapenhance.task.TaskType
|
||||||
import me.rhunk.snapenhance.ui.manager.Routes
|
import me.rhunk.snapenhance.ui.manager.Routes
|
||||||
import me.rhunk.snapenhance.ui.util.OnLifecycleEvent
|
import me.rhunk.snapenhance.ui.util.OnLifecycleEvent
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
@ -99,7 +99,7 @@ class TasksRoot : Routes.Route() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
context.shortToast("Merging ${filesToMerge.size} files")
|
context.shortToast(translation.format("merge_files_toast", "count" to filesToMerge.size.toString()))
|
||||||
FFMpegProcessor.newFFMpegProcessor(context, pendingTask).execute(
|
FFMpegProcessor.newFFMpegProcessor(context, pendingTask).execute(
|
||||||
FFMpegProcessor.Request(FFMpegProcessor.Action.MERGE_MEDIA, filesToMerge.map { it.absolutePath }, mergedFile)
|
FFMpegProcessor.Request(FFMpegProcessor.Action.MERGE_MEDIA, filesToMerge.map { it.absolutePath }, mergedFile)
|
||||||
)
|
)
|
||||||
@ -177,15 +177,15 @@ class TasksRoot : Routes.Route() {
|
|||||||
onDismissRequest = { showConfirmDialog = false },
|
onDismissRequest = { showConfirmDialog = false },
|
||||||
title = {
|
title = {
|
||||||
if (taskSelection.isNotEmpty()) {
|
if (taskSelection.isNotEmpty()) {
|
||||||
Text("Remove ${taskSelection.size} tasks?")
|
Text(translation.format("remove_selected_tasks_confirm", "count" to taskSelection.size.toString()))
|
||||||
} else {
|
} else {
|
||||||
Text("Remove all tasks?")
|
Text(translation["remove_all_tasks_confirm"])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
text = {
|
text = {
|
||||||
Column {
|
Column {
|
||||||
if (taskSelection.isNotEmpty()) {
|
if (taskSelection.isNotEmpty()) {
|
||||||
Text("Are you sure you want to remove selected tasks?")
|
Text(translation["remove_selected_tasks_title"])
|
||||||
Row (
|
Row (
|
||||||
modifier = Modifier.padding(top = 10.dp).fillMaxWidth().clickable {
|
modifier = Modifier.padding(top = 10.dp).fillMaxWidth().clickable {
|
||||||
alsoDeleteFiles = !alsoDeleteFiles
|
alsoDeleteFiles = !alsoDeleteFiles
|
||||||
@ -196,10 +196,10 @@ class TasksRoot : Routes.Route() {
|
|||||||
Checkbox(checked = alsoDeleteFiles, onCheckedChange = {
|
Checkbox(checked = alsoDeleteFiles, onCheckedChange = {
|
||||||
alsoDeleteFiles = it
|
alsoDeleteFiles = it
|
||||||
})
|
})
|
||||||
Text("Also delete files")
|
Text(translation["delete_files_option"])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Text("Are you sure you want to remove all tasks?")
|
Text(translation["remove_all_tasks_title"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -233,7 +233,7 @@ class TasksRoot : Routes.Route() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text("Yes")
|
Text(context.translation["button.positive"])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
@ -242,7 +242,7 @@ class TasksRoot : Routes.Route() {
|
|||||||
showConfirmDialog = false
|
showConfirmDialog = false
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text("No")
|
Text(context.translation["button.negative"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -429,7 +429,7 @@ class TasksRoot : Routes.Route() {
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
context.translation["manager.sections.tasks.no_tasks"].let {
|
translation["no_tasks"].let {
|
||||||
Icon(Icons.Filled.CheckCircle, contentDescription = it, tint = MaterialTheme.colorScheme.primary)
|
Icon(Icons.Filled.CheckCircle, contentDescription = it, tint = MaterialTheme.colorScheme.primary)
|
||||||
Text(it, style = MaterialTheme.typography.bodyLarge)
|
Text(it, style = MaterialTheme.typography.bodyLarge)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import androidx.compose.material.icons.filled.Close
|
|||||||
import androidx.compose.material.icons.filled.FolderOpen
|
import androidx.compose.material.icons.filled.FolderOpen
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.rounded.Save
|
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@ -30,11 +29,13 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
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.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.navigation.NavBackStackEntry
|
import androidx.navigation.NavBackStackEntry
|
||||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavOptions
|
import androidx.navigation.NavOptions
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -53,7 +54,6 @@ class FeaturesRoot : Routes.Route() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var activityLauncherHelper: ActivityLauncherHelper? = null
|
private var activityLauncherHelper: ActivityLauncherHelper? = null
|
||||||
private lateinit var rememberScaffoldState: BottomSheetScaffoldState
|
|
||||||
|
|
||||||
private val allContainers by lazy {
|
private val allContainers by lazy {
|
||||||
val containers = mutableMapOf<String, PropertyPair<*>>()
|
val containers = mutableMapOf<String, PropertyPair<*>>()
|
||||||
@ -444,48 +444,62 @@ class FeaturesRoot : Routes.Route() {
|
|||||||
var showResetConfirmationDialog by remember { mutableStateOf(false) }
|
var showResetConfirmationDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
if (showResetConfirmationDialog) {
|
if (showResetConfirmationDialog) {
|
||||||
Dialog(onDismissRequest = { showResetConfirmationDialog = false }) {
|
AlertDialog(
|
||||||
alertDialogs.ConfirmDialog(
|
title = { Text(text = context.translation["manager.dialogs.reset_config.title"]) },
|
||||||
title = "Reset config",
|
text = { Text(text = context.translation["manager.dialogs.reset_config.content"]) },
|
||||||
message = "Are you sure you want to reset the config?",
|
onDismissRequest = { showResetConfirmationDialog = false },
|
||||||
onConfirm = {
|
confirmButton = {
|
||||||
context.config.reset()
|
Button(
|
||||||
context.shortToast("Config successfully reset!")
|
onClick = {
|
||||||
},
|
context.config.reset()
|
||||||
onDismiss = { showResetConfirmationDialog = false }
|
context.shortToast(context.translation["manager.dialogs.reset_config.success_toast"])
|
||||||
)
|
showResetConfirmationDialog = false
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
Text(text = context.translation["button.positive"])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
showResetConfirmationDialog = false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = context.translation["button.negative"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val actions = remember {
|
val actions = remember {
|
||||||
mapOf(
|
mapOf(
|
||||||
"Export" to {
|
translation["export_option"] to {
|
||||||
activityLauncher {
|
activityLauncher {
|
||||||
saveFile("config.json", "application/json") { uri ->
|
saveFile("config.json", "application/json") { uri ->
|
||||||
context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
|
context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
|
||||||
context.config.writeConfig()
|
context.config.writeConfig()
|
||||||
context.config.exportToString().byteInputStream().copyTo(it)
|
context.config.exportToString().byteInputStream().copyTo(it)
|
||||||
context.shortToast("Config exported successfully!")
|
context.shortToast(translation["config_export_success_toast"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Import" to {
|
translation["import_option"] to {
|
||||||
activityLauncher {
|
activityLauncher {
|
||||||
openFile("application/json") { uri ->
|
openFile("application/json") { uri ->
|
||||||
context.androidContext.contentResolver.openInputStream(Uri.parse(uri))?.use {
|
context.androidContext.contentResolver.openInputStream(Uri.parse(uri))?.use {
|
||||||
runCatching {
|
runCatching {
|
||||||
context.config.loadFromString(it.readBytes().toString(Charsets.UTF_8))
|
context.config.loadFromString(it.readBytes().toString(Charsets.UTF_8))
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
context.longToast("Failed to import config ${it.message}")
|
context.longToast(translation.format("config_import_failure_toast", "error" to it.message.toString()))
|
||||||
return@use
|
return@use
|
||||||
}
|
}
|
||||||
context.shortToast("Config successfully loaded!")
|
context.shortToast(translation["config_import_success_toast"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Reset" to { showResetConfirmationDialog = true }
|
translation["reset_option"] to { showResetConfirmationDialog = true }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,9 +533,7 @@ class FeaturesRoot : Routes.Route() {
|
|||||||
private fun PropertiesView(
|
private fun PropertiesView(
|
||||||
properties: List<PropertyPair<*>>
|
properties: List<PropertyPair<*>>
|
||||||
) {
|
) {
|
||||||
rememberScaffoldState = rememberBottomSheetScaffoldState()
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
snackbarHost = { SnackbarHost(rememberScaffoldState.snackbarHostState) },
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
content = { innerPadding ->
|
content = { innerPadding ->
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
@ -541,23 +553,23 @@ class FeaturesRoot : Routes.Route() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override val floatingActionButton: @Composable () -> Unit = {
|
override val floatingActionButton: @Composable () -> Unit = {
|
||||||
val scope = rememberCoroutineScope()
|
fun saveConfig() {
|
||||||
FloatingActionButton(
|
context.coroutineScope.launch(Dispatchers.IO) {
|
||||||
onClick = {
|
|
||||||
context.config.writeConfig()
|
context.config.writeConfig()
|
||||||
scope.launch {
|
context.log.verbose("saved config!")
|
||||||
rememberScaffoldState.snackbarHostState.showSnackbar("Saved")
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
modifier = Modifier.padding(10.dp),
|
OnLifecycleEvent { _, event ->
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
if (event == Lifecycle.Event.ON_PAUSE || event == Lifecycle.Event.ON_STOP) {
|
||||||
contentColor = MaterialTheme.colorScheme.onPrimary,
|
saveConfig()
|
||||||
shape = RoundedCornerShape(16.dp),
|
}
|
||||||
) {
|
}
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Save,
|
DisposableEffect(Unit) {
|
||||||
contentDescription = null
|
onDispose {
|
||||||
)
|
saveConfig()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -566,10 +578,8 @@ class FeaturesRoot : Routes.Route() {
|
|||||||
private fun Container(
|
private fun Container(
|
||||||
configContainer: ConfigContainer
|
configContainer: ConfigContainer
|
||||||
) {
|
) {
|
||||||
val properties = remember {
|
PropertiesView(remember {
|
||||||
configContainer.properties.map { PropertyPair(it.key, it.value) }
|
configContainer.properties.map { PropertyPair(it.key, it.value) }
|
||||||
}
|
})
|
||||||
|
|
||||||
PropertiesView(properties)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -68,28 +68,27 @@ class HomeLogs : Routes.Route() {
|
|||||||
navigate()
|
navigate()
|
||||||
showDropDown = false
|
showDropDown = false
|
||||||
}, text = {
|
}, text = {
|
||||||
Text(
|
Text(translation["clear_logs_button"])
|
||||||
text = context.translation["manager.sections.home.logs.clear_logs_button"]
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
DropdownMenuItem(onClick = {
|
DropdownMenuItem(onClick = {
|
||||||
activityLauncherHelper.saveFile("snapenhance-logs-${System.currentTimeMillis()}.zip", "application/zip") { uri ->
|
activityLauncherHelper.saveFile("snapenhance-logs-${System.currentTimeMillis()}.zip", "application/zip") { uri ->
|
||||||
context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
|
context.coroutineScope.launch {
|
||||||
runCatching {
|
context.shortToast(translation["saving_logs_toast"])
|
||||||
context.log.exportLogsToZip(it)
|
context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
|
||||||
context.longToast("Saved logs to $uri")
|
runCatching {
|
||||||
}.onFailure {
|
context.log.exportLogsToZip(it)
|
||||||
context.longToast("Failed to save logs to $uri!")
|
context.longToast(translation["saved_logs_success_toast"])
|
||||||
context.log.error("Failed to save logs to $uri!", it)
|
}.onFailure {
|
||||||
|
context.longToast(translation["saved_logs_failure_toast"])
|
||||||
|
context.log.error("Failed to save logs to $uri!", it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
showDropDown = false
|
showDropDown = false
|
||||||
}, text = {
|
}, text = {
|
||||||
Text(
|
Text(translation["export_logs_button"])
|
||||||
text = context.translation["manager.sections.home.logs.export_logs_button"]
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,7 +140,7 @@ class HomeLogs : Routes.Route() {
|
|||||||
item {
|
item {
|
||||||
if (lineCount == 0 && logReader != null) {
|
if (lineCount == 0 && logReader != null) {
|
||||||
Text(
|
Text(
|
||||||
text = "No logs found!",
|
text = translation["no_logs_hint"],
|
||||||
modifier = Modifier.padding(16.dp),
|
modifier = Modifier.padding(16.dp),
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
fontWeight = FontWeight.Light
|
fontWeight = FontWeight.Light
|
||||||
|
@ -198,9 +198,9 @@ class HomeRoot : Routes.Route() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
|
||||||
|
|
||||||
if (latestUpdate != null) {
|
if (latestUpdate != null) {
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
OutlinedCard(
|
OutlinedCard(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(all = cardMargin)
|
.padding(all = cardMargin)
|
||||||
@ -218,13 +218,13 @@ class HomeRoot : Routes.Route() {
|
|||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
Text(
|
Text(
|
||||||
text = "SnapEnhance Update",
|
text = translation["update_title"],
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
text = "Version ${latestUpdate?.versionName} is available!",
|
text = translation.format("update_content", "version" to (latestUpdate?.versionName ?: "unknown")),
|
||||||
lineHeight = 20.sp
|
lineHeight = 20.sp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -235,7 +235,7 @@ class HomeRoot : Routes.Route() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}, modifier = Modifier.height(40.dp)) {
|
}, modifier = Modifier.height(40.dp)) {
|
||||||
Text(text = "Download")
|
Text(text = translation["update_button"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ class HomeSettings : Routes.Route() {
|
|||||||
|
|
||||||
if (requireConfirmation && confirmationDialog) {
|
if (requireConfirmation && confirmationDialog) {
|
||||||
Dialog(onDismissRequest = { confirmationDialog = false }) {
|
Dialog(onDismissRequest = { confirmationDialog = false }) {
|
||||||
dialogs.ConfirmDialog(title = "Are you sure?", onConfirm = {
|
dialogs.ConfirmDialog(title = context.translation["manager.dialogs.action_confirm.title"], onConfirm = {
|
||||||
action()
|
action()
|
||||||
confirmationDialog = false
|
confirmationDialog = false
|
||||||
}, onDismiss = {
|
}, onDismiss = {
|
||||||
@ -148,7 +148,7 @@ class HomeSettings : Routes.Route() {
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.verticalScroll(ScrollState(0))
|
.verticalScroll(ScrollState(0))
|
||||||
) {
|
) {
|
||||||
RowTitle(title = "Actions")
|
RowTitle(title = translation["actions_title"])
|
||||||
EnumAction.entries.forEach { enumAction ->
|
EnumAction.entries.forEach { enumAction ->
|
||||||
RowAction(key = enumAction.key) {
|
RowAction(key = enumAction.key) {
|
||||||
launchActionIntent(enumAction)
|
launchActionIntent(enumAction)
|
||||||
@ -160,7 +160,7 @@ class HomeSettings : Routes.Route() {
|
|||||||
RowAction(key = "change_language") {
|
RowAction(key = "change_language") {
|
||||||
context.checkForRequirements(Requirements.LANGUAGE)
|
context.checkForRequirements(Requirements.LANGUAGE)
|
||||||
}
|
}
|
||||||
RowTitle(title = "Message Logger")
|
RowTitle(title = translation["message_logger_title"])
|
||||||
ShiftedRow {
|
ShiftedRow {
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
@ -184,8 +184,11 @@ class HomeSettings : Routes.Route() {
|
|||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
) {
|
) {
|
||||||
Text(text = "$storedMessagesCount messages")
|
Text(
|
||||||
Text(text = "$storedStoriesCount stories")
|
translation.format("message_logger_summary",
|
||||||
|
"messageCount" to storedMessagesCount.toString(),
|
||||||
|
"storyCount" to storedStoriesCount.toString()
|
||||||
|
), maxLines = 2)
|
||||||
}
|
}
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
runCatching {
|
runCatching {
|
||||||
@ -201,7 +204,7 @@ class HomeSettings : Routes.Route() {
|
|||||||
context.longToast("Failed to export database! ${it.localizedMessage}")
|
context.longToast("Failed to export database! ${it.localizedMessage}")
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Text(text = "Export")
|
Text(text = translation["export_button"])
|
||||||
}
|
}
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
runCatching {
|
runCatching {
|
||||||
@ -212,10 +215,10 @@ class HomeSettings : Routes.Route() {
|
|||||||
context.log.error("Failed to clear messages", it)
|
context.log.error("Failed to clear messages", it)
|
||||||
context.longToast("Failed to clear messages! ${it.localizedMessage}")
|
context.longToast("Failed to clear messages! ${it.localizedMessage}")
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
context.shortToast("Done!")
|
context.shortToast(translation["success_toast"])
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Text(text = "Clear")
|
Text(text = translation["clear_button"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
@ -226,12 +229,12 @@ class HomeSettings : Routes.Route() {
|
|||||||
routes.loggerHistory.navigate()
|
routes.loggerHistory.navigate()
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text(text = "View Message History")
|
Text(translation["view_logger_history_button"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowTitle(title = "Debug")
|
RowTitle(title = translation["debug_title"])
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@ -275,10 +278,10 @@ class HomeSettings : Routes.Route() {
|
|||||||
context.log.error("Failed to clear file", it)
|
context.log.error("Failed to clear file", it)
|
||||||
context.longToast("Failed to clear file! ${it.localizedMessage}")
|
context.longToast("Failed to clear file! ${it.localizedMessage}")
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
context.shortToast("Done!")
|
context.shortToast(translation["success_toast"])
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Text(text = "Clear File")
|
Text(translation["clear_button"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ShiftedRow {
|
ShiftedRow {
|
||||||
|
@ -148,7 +148,7 @@ class LoggedStories : Routes.Route() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Text(text = "Open")
|
Text(text = context.translation["button.open"])
|
||||||
}
|
}
|
||||||
|
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
@ -160,7 +160,7 @@ class LoggedStories : Routes.Route() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}) {
|
}) {
|
||||||
Text(text = "Download")
|
Text(text = context.translation["button.download"])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remember {
|
if (remember {
|
||||||
@ -180,7 +180,7 @@ class LoggedStories : Routes.Route() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}) {
|
}) {
|
||||||
Text(text = "Save from cache")
|
Text(text = translation["save_from_cache_button"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,7 +190,7 @@ class LoggedStories : Routes.Route() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stories.isEmpty()) {
|
if (stories.isEmpty()) {
|
||||||
Text(text = "No stories found", Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
|
Text(text = translation["no_stories"], Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyVerticalGrid(
|
LazyVerticalGrid(
|
||||||
@ -212,7 +212,7 @@ class LoggedStories : Routes.Route() {
|
|||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
) {
|
) {
|
||||||
if (hasFailed) {
|
if (hasFailed) {
|
||||||
Text(text = "Failed to load", Modifier.padding(8.dp), fontSize = 10.sp)
|
Text(text = translation["story_failed_to_load"], Modifier.padding(8.dp), fontSize = 10.sp)
|
||||||
} else {
|
} else {
|
||||||
Image(
|
Image(
|
||||||
painter = rememberAsyncImagePainter(
|
painter = rememberAsyncImagePainter(
|
||||||
|
@ -29,7 +29,6 @@ import kotlin.io.encoding.ExperimentalEncodingApi
|
|||||||
|
|
||||||
class ManageScope: Routes.Route() {
|
class ManageScope: Routes.Route() {
|
||||||
private val dialogs by lazy { AlertDialogs(context.translation) }
|
private val dialogs by lazy { AlertDialogs(context.translation) }
|
||||||
private val translation by lazy { context.translation.getCategory("manager.sections.social") }
|
|
||||||
|
|
||||||
private fun deleteScope(scope: SocialScope, id: String, coroutineScope: CoroutineScope) {
|
private fun deleteScope(scope: SocialScope, id: String, coroutineScope: CoroutineScope) {
|
||||||
when (scope) {
|
when (scope) {
|
||||||
@ -56,7 +55,7 @@ class ManageScope: Routes.Route() {
|
|||||||
deleteConfirmDialog = false
|
deleteConfirmDialog = false
|
||||||
}) {
|
}) {
|
||||||
remember { AlertDialogs(context.translation) }.ConfirmDialog(
|
remember { AlertDialogs(context.translation) }.ConfirmDialog(
|
||||||
title = "Are you sure you want to delete this ${scope.key.lowercase()}?",
|
title = translation.format("delete_scope_confirm_dialog_title", "scope" to context.translation["scopes.${scope.key}"]),
|
||||||
onDismiss = { deleteConfirmDialog = false },
|
onDismiss = { deleteConfirmDialog = false },
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
deleteScope(scope, id, coroutineScope); deleteConfirmDialog = false
|
deleteScope(scope, id, coroutineScope); deleteConfirmDialog = false
|
||||||
@ -94,7 +93,6 @@ class ManageScope: Routes.Route() {
|
|||||||
SectionTitle(translation["rules_title"])
|
SectionTitle(translation["rules_title"])
|
||||||
|
|
||||||
ContentCard {
|
ContentCard {
|
||||||
//manager anti features etc
|
|
||||||
MessagingRuleType.entries.forEach { ruleType ->
|
MessagingRuleType.entries.forEach { ruleType ->
|
||||||
var ruleEnabled by remember {
|
var ruleEnabled by remember {
|
||||||
mutableStateOf(rules.any { it.key == ruleType.key })
|
mutableStateOf(rules.any { it.key == ruleType.key })
|
||||||
@ -110,14 +108,17 @@ class ManageScope: Routes.Route() {
|
|||||||
text = if (ruleType.listMode && ruleState != null) {
|
text = if (ruleType.listMode && ruleState != null) {
|
||||||
context.translation["rules.properties.${ruleType.key}.options.${ruleState.key}"]
|
context.translation["rules.properties.${ruleType.key}.options.${ruleState.key}"]
|
||||||
} else context.translation["rules.properties.${ruleType.key}.name"],
|
} else context.translation["rules.properties.${ruleType.key}.name"],
|
||||||
modifier = Modifier.weight(1f).padding(start = 5.dp, end = 5.dp)
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(start = 5.dp, end = 5.dp)
|
||||||
)
|
)
|
||||||
Switch(checked = ruleEnabled,
|
Switch(checked = ruleEnabled,
|
||||||
enabled = if (ruleType.listMode) ruleState != null else true,
|
enabled = if (ruleType.listMode) ruleState != null else true,
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
context.modDatabase.setRule(id, ruleType.key, it)
|
context.modDatabase.setRule(id, ruleType.key, it)
|
||||||
ruleEnabled = it
|
ruleEnabled = it
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,8 +156,7 @@ class ManageScope: Routes.Route() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//need to display all units?
|
private fun computeStreakETA(timestamp: Long): String? {
|
||||||
private fun computeStreakETA(timestamp: Long): String {
|
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
val stringBuilder = StringBuilder()
|
val stringBuilder = StringBuilder()
|
||||||
val diff = timestamp - now
|
val diff = timestamp - now
|
||||||
@ -180,7 +180,7 @@ class ManageScope: Routes.Route() {
|
|||||||
stringBuilder.append("$seconds seconds ")
|
stringBuilder.append("$seconds seconds ")
|
||||||
return stringBuilder.toString()
|
return stringBuilder.toString()
|
||||||
}
|
}
|
||||||
return "Expired"
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalEncodingApi::class)
|
@OptIn(ExperimentalEncodingApi::class)
|
||||||
@ -234,7 +234,7 @@ class ManageScope: Routes.Route() {
|
|||||||
put("id", id)
|
put("id", id)
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Text("Show Logged Stories")
|
Text(translation["logged_stories_button"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,10 +259,11 @@ class ManageScope: Routes.Route() {
|
|||||||
), maxLines = 1
|
), maxLines = 1
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = translation.format(
|
text = computeStreakETA(streaks.expirationTimestamp)?.let { translation.format(
|
||||||
"streaks_expiration_text",
|
"streaks_expiration_text",
|
||||||
"eta" to computeStreakETA(streaks.expirationTimestamp)
|
"eta" to it
|
||||||
), maxLines = 1
|
) } ?: translation["streaks_expiration_text_expired"],
|
||||||
|
maxLines = 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
@ -282,7 +283,6 @@ class ManageScope: Routes.Route() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
// e2ee section
|
|
||||||
|
|
||||||
if (context.config.root.experimental.e2eEncryption.globalState == true) {
|
if (context.config.root.experimental.e2eEncryption.globalState == true) {
|
||||||
SectionTitle(translation["e2ee_title"])
|
SectionTitle(translation["e2ee_title"])
|
||||||
@ -362,7 +362,6 @@ class ManageScope: Routes.Route() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(10.dp)
|
.padding(10.dp)
|
||||||
|
@ -47,7 +47,6 @@ class MessagingPreview: Routes.Route() {
|
|||||||
private lateinit var messagingBridge: MessagingBridge
|
private lateinit var messagingBridge: MessagingBridge
|
||||||
private lateinit var previewScrollState: LazyListState
|
private lateinit var previewScrollState: LazyListState
|
||||||
|
|
||||||
private val myUserId by lazy { messagingBridge.myUserId }
|
|
||||||
private val contentTypeTranslation by lazy { context.translation.getCategory("content_type") }
|
private val contentTypeTranslation by lazy { context.translation.getCategory("content_type") }
|
||||||
|
|
||||||
private var messages = mutableStateListOf<Message>()
|
private var messages = mutableStateListOf<Message>()
|
||||||
@ -117,7 +116,7 @@ class MessagingPreview: Routes.Route() {
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.spacedBy(5.dp)
|
verticalArrangement = Arrangement.spacedBy(5.dp)
|
||||||
) {
|
) {
|
||||||
Text("Choose content types to process")
|
Text(context.translation["manager.dialogs.messaging_action.title"])
|
||||||
Spacer(modifier = Modifier.height(5.dp))
|
Spacer(modifier = Modifier.height(5.dp))
|
||||||
availableTypes.forEach { contentType ->
|
availableTypes.forEach { contentType ->
|
||||||
Row(
|
Row(
|
||||||
@ -135,7 +134,7 @@ class MessagingPreview: Routes.Route() {
|
|||||||
enabled = !selectAllState,
|
enabled = !selectAllState,
|
||||||
onCheckedChange = { toggleContentType(contentType) }
|
onCheckedChange = { toggleContentType(contentType) }
|
||||||
)
|
)
|
||||||
Text(text = contentType.toString())
|
Text(text = contentTypeTranslation[contentType.name])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
@ -148,7 +147,7 @@ class MessagingPreview: Routes.Route() {
|
|||||||
Switch(checked = selectAllState, onCheckedChange = {
|
Switch(checked = selectAllState, onCheckedChange = {
|
||||||
selectAllState = it
|
selectAllState = it
|
||||||
})
|
})
|
||||||
Text(text = "Select all")
|
Text(text = context.translation["manager.dialogs.messaging_action.select_all_button"])
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -156,13 +155,13 @@ class MessagingPreview: Routes.Route() {
|
|||||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||||
) {
|
) {
|
||||||
Button(onClick = { onDismiss() }) {
|
Button(onClick = { onDismiss() }) {
|
||||||
Text("Cancel")
|
Text(context.translation["button.cancel"])
|
||||||
}
|
}
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
onChoose(if (selectAllState) ContentType.entries.toTypedArray()
|
onChoose(if (selectAllState) ContentType.entries.toTypedArray()
|
||||||
else selectedTypes.toTypedArray())
|
else selectedTypes.toTypedArray())
|
||||||
}) {
|
}) {
|
||||||
Text("Continue")
|
Text(context.translation["button.ok"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,28 +285,28 @@ class MessagingPreview: Routes.Route() {
|
|||||||
shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(50.dp))
|
shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(50.dp))
|
||||||
) {
|
) {
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
expanded = taskSelectionDropdown, onDismissRequest = { taskSelectionDropdown = false }
|
expanded = taskSelectionDropdown && messages.isNotEmpty(), onDismissRequest = { taskSelectionDropdown = false }
|
||||||
) {
|
) {
|
||||||
val hasSelection = selectedMessages.isNotEmpty()
|
val hasSelection = selectedMessages.isNotEmpty()
|
||||||
ActionButton(text = if (hasSelection) "Save selection" else "Save all", icon = Icons.Rounded.BookmarkAdded) {
|
ActionButton(text = translation[if (hasSelection) "save_selection_option" else "save_all_option"], icon = Icons.Rounded.BookmarkAdded) {
|
||||||
launchMessagingTask(MessagingTaskType.SAVE)
|
launchMessagingTask(MessagingTaskType.SAVE)
|
||||||
if (hasSelection) runCurrentTask()
|
if (hasSelection) runCurrentTask()
|
||||||
else selectConstraintsDialog = true
|
else selectConstraintsDialog = true
|
||||||
}
|
}
|
||||||
ActionButton(text = if (hasSelection) "Unsave selection" else "Unsave all", icon = Icons.Rounded.BookmarkBorder) {
|
ActionButton(text = translation[if (hasSelection) "unsave_selection_option" else "unsave_all_option"], icon = Icons.Rounded.BookmarkBorder) {
|
||||||
launchMessagingTask(MessagingTaskType.UNSAVE)
|
launchMessagingTask(MessagingTaskType.UNSAVE)
|
||||||
if (hasSelection) runCurrentTask()
|
if (hasSelection) runCurrentTask()
|
||||||
else selectConstraintsDialog = true
|
else selectConstraintsDialog = true
|
||||||
}
|
}
|
||||||
ActionButton(text = if (hasSelection) "Mark selected Snap as seen" else "Mark all Snaps as seen", icon = Icons.Rounded.RemoveRedEye) {
|
ActionButton(text = translation[if (hasSelection) "mark_selection_as_seen_option" else "mark_all_as_seen_option"], icon = Icons.Rounded.RemoveRedEye) {
|
||||||
launchMessagingTask(MessagingTaskType.READ, listOf(
|
launchMessagingTask(MessagingTaskType.READ, listOf(
|
||||||
MessagingConstraints.NO_USER_ID(myUserId),
|
MessagingConstraints.NO_USER_ID(messagingBridge.myUserId),
|
||||||
MessagingConstraints.CONTENT_TYPE(arrayOf(ContentType.SNAP))
|
MessagingConstraints.CONTENT_TYPE(arrayOf(ContentType.SNAP))
|
||||||
))
|
))
|
||||||
runCurrentTask()
|
runCurrentTask()
|
||||||
}
|
}
|
||||||
ActionButton(text = if (hasSelection) "Delete selected" else "Delete all", icon = Icons.Rounded.DeleteForever) {
|
ActionButton(text = translation[if (hasSelection) "delete_selection_option" else "delete_all_option"], icon = Icons.Rounded.DeleteForever) {
|
||||||
launchMessagingTask(MessagingTaskType.DELETE, listOf(MessagingConstraints.USER_ID(myUserId))) { message ->
|
launchMessagingTask(MessagingTaskType.DELETE, listOf(MessagingConstraints.USER_ID(messagingBridge.myUserId))) { message ->
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
message.contentType = ContentType.STATUS.id
|
message.contentType = ContentType.STATUS.id
|
||||||
}
|
}
|
||||||
@ -377,7 +376,7 @@ class MessagingPreview: Routes.Route() {
|
|||||||
.padding(40.dp),
|
.padding(40.dp),
|
||||||
horizontalArrangement = Arrangement.Center
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Text("No messages")
|
Text(translation["no_message_hint"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
@ -428,12 +427,7 @@ class MessagingPreview: Routes.Route() {
|
|||||||
conversationId!!,
|
conversationId!!,
|
||||||
20,
|
20,
|
||||||
lastMessageId
|
lastMessageId
|
||||||
)?.reversed()
|
)?.reversed() ?: throw IllegalStateException("Failed to fetch messages. Bridge returned null")
|
||||||
|
|
||||||
if (queriedMessages == null) {
|
|
||||||
context.shortToast("Failed to fetch messages")
|
|
||||||
return@cs
|
|
||||||
}
|
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
messages.addAll(queriedMessages)
|
messages.addAll(queriedMessages)
|
||||||
@ -441,7 +435,7 @@ class MessagingPreview: Routes.Route() {
|
|||||||
}
|
}
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
context.log.error("Failed to fetch messages", it)
|
context.log.error("Failed to fetch messages", it)
|
||||||
context.shortToast("Failed to fetch messages: ${it.message}")
|
context.shortToast(translation["message_fetch_failed"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -451,11 +445,7 @@ class MessagingPreview: Routes.Route() {
|
|||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
messagingBridge = context.bridgeService!!.messagingBridge!!
|
messagingBridge = context.bridgeService!!.messagingBridge!!
|
||||||
conversationId = if (scope == SocialScope.FRIEND) messagingBridge.getOneToOneConversationId(scopeId) else scopeId
|
conversationId = (if (scope == SocialScope.FRIEND) messagingBridge.getOneToOneConversationId(scopeId) else scopeId) ?: throw IllegalStateException("Failed to get conversation id")
|
||||||
if (conversationId == null) {
|
|
||||||
context.longToast("Failed to fetch conversation id")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (runCatching { !messagingBridge.isSessionStarted }.getOrDefault(true)) {
|
if (runCatching { !messagingBridge.isSessionStarted }.getOrDefault(true)) {
|
||||||
context.androidContext.packageManager.getLaunchIntentForPackage(
|
context.androidContext.packageManager.getLaunchIntentForPackage(
|
||||||
Constants.SNAPCHAT_PACKAGE_NAME
|
Constants.SNAPCHAT_PACKAGE_NAME
|
||||||
@ -474,7 +464,7 @@ class MessagingPreview: Routes.Route() {
|
|||||||
}
|
}
|
||||||
fetchNewMessages()
|
fetchNewMessages()
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
context.longToast("Failed to initialize messaging bridge")
|
context.longToast(translation["bridge_init_failed"])
|
||||||
context.log.error("Failed to initialize messaging bridge", it)
|
context.log.error("Failed to initialize messaging bridge", it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -511,7 +501,7 @@ class MessagingPreview: Routes.Route() {
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
if (hasBridgeError) {
|
if (hasBridgeError) {
|
||||||
Text("Failed to connect to Snapchat through bridge service")
|
Text(translation["bridge_connection_failed"])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isBridgeConnected && !hasBridgeError) {
|
if (!isBridgeConnected && !hasBridgeError) {
|
||||||
|
@ -164,8 +164,8 @@ class SocialRoot : Routes.Route() {
|
|||||||
else MaterialTheme.colorScheme.primary
|
else MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = context.translation.format(
|
text = translation.format(
|
||||||
"manager.sections.social.streaks_expiration_short",
|
"streaks_expiration_short",
|
||||||
"hours" to (((streaks.expirationTimestamp - System.currentTimeMillis()) / 3600000).toInt().takeIf { it > 0 } ?: 0)
|
"hours" to (((streaks.expirationTimestamp - System.currentTimeMillis()) / 3600000).toInt().takeIf { it > 0 } ?: 0)
|
||||||
.toString()
|
.toString()
|
||||||
),
|
),
|
||||||
|
@ -44,9 +44,9 @@ class AlertDialogs(
|
|||||||
@Composable
|
@Composable
|
||||||
fun DefaultDialogCard(modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) {
|
fun DefaultDialogCard(modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) {
|
||||||
Card(
|
Card(
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = MaterialTheme.shapes.large,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(10.dp, 5.dp, 10.dp, 10.dp)
|
.padding(16.dp)
|
||||||
.then(modifier),
|
.then(modifier),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
@ -21,6 +21,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"scopes": {
|
||||||
|
"friend": "Friend",
|
||||||
|
"group": "Group"
|
||||||
|
},
|
||||||
|
|
||||||
"manager": {
|
"manager": {
|
||||||
"routes": {
|
"routes": {
|
||||||
"tasks": "Tasks",
|
"tasks": "Tasks",
|
||||||
@ -37,27 +42,92 @@
|
|||||||
},
|
},
|
||||||
"sections": {
|
"sections": {
|
||||||
"home": {
|
"home": {
|
||||||
"logs": {
|
"update_title": "SnapEnhance Update",
|
||||||
"clear_logs_button": "Clear Logs",
|
"update_content": "Version {version} is available!",
|
||||||
"export_logs_button": "Export Logs"
|
"update_button": "Download"
|
||||||
}
|
},
|
||||||
|
"home_logs": {
|
||||||
|
"no_logs_hint": "No logs available",
|
||||||
|
"clear_logs_button": "Clear Logs",
|
||||||
|
"export_logs_button": "Export Logs",
|
||||||
|
"saving_logs_toast": "Saving logs, this may take a while ...",
|
||||||
|
"saved_logs_success_toast": "Logs saved successfully",
|
||||||
|
"saved_logs_failure_toast": "Failed to save logs"
|
||||||
|
},
|
||||||
|
"home_settings": {
|
||||||
|
"actions_title": "Actions",
|
||||||
|
"message_logger_title": "Message Logger",
|
||||||
|
"debug_title": "Debug",
|
||||||
|
"success_toast": "Done!",
|
||||||
|
"message_logger_summary": "{messageCount} messages\n{storyCount} stories",
|
||||||
|
"export_button": "Export",
|
||||||
|
"clear_button": "Clear",
|
||||||
|
"view_logger_history_button": "View Logger History"
|
||||||
},
|
},
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"no_tasks": "No tasks"
|
"no_tasks": "No tasks",
|
||||||
|
"merge_files_toast": "Merging {count} files",
|
||||||
|
"remove_selected_tasks_title": "Are you sure you want to remove selected tasks?",
|
||||||
|
"remove_all_tasks_title": "Are you sure you want to remove all tasks?",
|
||||||
|
"delete_files_option": "Also delete files",
|
||||||
|
"remove_selected_tasks_confirm": "Remove {count} tasks?",
|
||||||
|
"remove_all_tasks_confirm": "Remove all tasks?"
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
"disabled": "Disabled"
|
"disabled": "Disabled",
|
||||||
|
"export_option": "Export",
|
||||||
|
"import_option": "Import",
|
||||||
|
"reset_option": "Reset",
|
||||||
|
"config_export_success_toast": "Config exported successfully",
|
||||||
|
"config_import_success_toast": "Config imported successfully",
|
||||||
|
"config_import_failure_toast": "Failed to import config {error}",
|
||||||
|
"saved_config_snackbar": "Config saved"
|
||||||
},
|
},
|
||||||
"social": {
|
"social": {
|
||||||
|
"streaks_expiration_short": "{hours}h"
|
||||||
|
},
|
||||||
|
"manage_scope": {
|
||||||
|
"logged_stories_button": "Show Logged Stories",
|
||||||
"e2ee_title": "End-to-End Encryption",
|
"e2ee_title": "End-to-End Encryption",
|
||||||
"rules_title": "Rules",
|
"rules_title": "Rules",
|
||||||
"participants_text": "{count} participants",
|
"participants_text": "{count} participants",
|
||||||
"not_found": "Not found",
|
"not_found": "Not found",
|
||||||
"streaks_title": "Streaks",
|
"streaks_title": "Streaks",
|
||||||
"streaks_length_text": "Length: {length}",
|
"streaks_length_text": "Length: {length}",
|
||||||
"streaks_expiration_short": "{hours}h",
|
|
||||||
"streaks_expiration_text": "Expires in {eta}",
|
"streaks_expiration_text": "Expires in {eta}",
|
||||||
"reminder_button": "Set Reminder"
|
"streaks_expiration_text_expired": "Expired",
|
||||||
|
"reminder_button": "Set Reminder",
|
||||||
|
"delete_scope_confirm_dialog_title": "Are you sure you want to delete a {scope}?"
|
||||||
|
},
|
||||||
|
"logged_stories": {
|
||||||
|
"story_failed_to_load": "Failed to load",
|
||||||
|
"no_stories": "No stories found",
|
||||||
|
"save_from_cache_button": "Save from Cache"
|
||||||
|
},
|
||||||
|
"messaging_preview": {
|
||||||
|
"bridge_connection_failed": "Failed to connect to Snapchat through bridge service",
|
||||||
|
"bridge_init_failed": "Failed to initialize messaging bridge",
|
||||||
|
"message_fetch_failed": "Failed to fetch messages",
|
||||||
|
"no_message_hint": "No message",
|
||||||
|
"save_selection_option": "Save Selection",
|
||||||
|
"save_all_option": "Save All",
|
||||||
|
"unsave_selection_option": "Unsave Selection",
|
||||||
|
"unsave_all_option": "Unsave All",
|
||||||
|
"mark_selection_as_seen_option": "Mark selected Snap as seen",
|
||||||
|
"mark_all_as_seen_option": "Mark all Snaps as seen",
|
||||||
|
"delete_selection_option": "Delete Selection",
|
||||||
|
"delete_all_option": "Delete All"
|
||||||
|
},
|
||||||
|
"logger_history": {
|
||||||
|
"list_friend_format": "Friend {name}",
|
||||||
|
"list_group_format": "Group {name}",
|
||||||
|
"no_more_messages": "No more messages",
|
||||||
|
"reverse_order_checkbox": "Reverse Order",
|
||||||
|
"chat_attachment": "Attachment {index}",
|
||||||
|
"empty_message": "Empty Chat Message",
|
||||||
|
"message_parse_failed": "Failed to parse message",
|
||||||
|
"unknown_sender": "Unknown Sender",
|
||||||
|
"download_attachment_failed_toast": "Failed to download attachment"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dialogs": {
|
"dialogs": {
|
||||||
@ -71,6 +141,15 @@
|
|||||||
"scripting_warning": {
|
"scripting_warning": {
|
||||||
"title": "Warning",
|
"title": "Warning",
|
||||||
"content": "SnapEnhance includes a scripting tool, allowing the execution of user-defined code on your device. Use extreme caution and only install modules from known, reliable sources. Unauthorized or unverified modules may pose security risks to your system."
|
"content": "SnapEnhance includes a scripting tool, allowing the execution of user-defined code on your device. Use extreme caution and only install modules from known, reliable sources. Unauthorized or unverified modules may pose security risks to your system."
|
||||||
|
},
|
||||||
|
"reset_config": {
|
||||||
|
"title": "Reset config",
|
||||||
|
"content": "Are you sure you want to reset the config?",
|
||||||
|
"success_toast": "Config reset successfully"
|
||||||
|
},
|
||||||
|
"messaging_action": {
|
||||||
|
"title": "Choose content types to process",
|
||||||
|
"select_all_button": "Select All"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user