mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-05 00:44: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 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 {
|
||||
args.entries.fold(id) { acc, (key, value) ->
|
||||
acc.replace("{$key}", value)
|
||||
|
@ -126,7 +126,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
||||
LaunchedEffect(Unit, message) {
|
||||
runCatching {
|
||||
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
|
||||
fun ContentHeader() {
|
||||
@ -134,7 +134,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
||||
}
|
||||
|
||||
if (contentType == ContentType.CHAT) {
|
||||
val content = messageReader.getString(2, 1) ?: "[empty chat message]"
|
||||
val content = messageReader.getString(2, 1) ?: "[${translation["empty_message"]}]"
|
||||
contentView = {
|
||||
Column {
|
||||
Text(content, modifier = Modifier
|
||||
@ -166,7 +166,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
||||
downloadAttachment(message.timestamp, attachment)
|
||||
}.onFailure {
|
||||
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",
|
||||
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 {
|
||||
context.log.error("Failed to parse message", it)
|
||||
contentView = {
|
||||
Text("[Failed to parse message]")
|
||||
Text("[${translation["message_parse_failed"]}]")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -212,8 +212,10 @@ class LoggerHistoryRoot : Routes.Route() {
|
||||
) {
|
||||
fun formatConversationId(conversationId: String?): String? {
|
||||
if (conversationId == null) return null
|
||||
return context.modDatabase.getGroupInfo(conversationId)?.name?.let { "Group $it" } ?: context.modDatabase.findFriend(conversationId)?.let {
|
||||
"Friend " + (it.displayName?.let { name -> "$name (${it.mutableUsername})" } ?: it.mutableUsername)
|
||||
return context.modDatabase.getGroupInfo(conversationId)?.name?.let {
|
||||
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
|
||||
}
|
||||
|
||||
@ -257,7 +259,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Text("Reverse order")
|
||||
Text(translation["reverse_order_checkbox"])
|
||||
Checkbox(checked = reverseOrder, onCheckedChange = {
|
||||
reverseOrder = it
|
||||
})
|
||||
@ -275,7 +277,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
||||
item {
|
||||
if (selectedConversation != null) {
|
||||
if (hasReachedEnd) {
|
||||
Text("No more messages", modifier = Modifier
|
||||
Text(translation["no_more_messages"], modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.fillMaxWidth(), textAlign = TextAlign.Center)
|
||||
} else {
|
||||
|
@ -8,8 +8,8 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
@ -20,8 +20,8 @@ import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.core.net.toUri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
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.TaskStatus
|
||||
import me.rhunk.snapenhance.task.TaskType
|
||||
import me.rhunk.snapenhance.ui.manager.Routes
|
||||
import me.rhunk.snapenhance.ui.util.OnLifecycleEvent
|
||||
import me.rhunk.snapenhance.ui.manager.Routes
|
||||
import me.rhunk.snapenhance.ui.util.OnLifecycleEvent
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
import kotlin.math.absoluteValue
|
||||
@ -99,7 +99,7 @@ class TasksRoot : Routes.Route() {
|
||||
}
|
||||
|
||||
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.Request(FFMpegProcessor.Action.MERGE_MEDIA, filesToMerge.map { it.absolutePath }, mergedFile)
|
||||
)
|
||||
@ -177,15 +177,15 @@ class TasksRoot : Routes.Route() {
|
||||
onDismissRequest = { showConfirmDialog = false },
|
||||
title = {
|
||||
if (taskSelection.isNotEmpty()) {
|
||||
Text("Remove ${taskSelection.size} tasks?")
|
||||
Text(translation.format("remove_selected_tasks_confirm", "count" to taskSelection.size.toString()))
|
||||
} else {
|
||||
Text("Remove all tasks?")
|
||||
Text(translation["remove_all_tasks_confirm"])
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Column {
|
||||
if (taskSelection.isNotEmpty()) {
|
||||
Text("Are you sure you want to remove selected tasks?")
|
||||
Text(translation["remove_selected_tasks_title"])
|
||||
Row (
|
||||
modifier = Modifier.padding(top = 10.dp).fillMaxWidth().clickable {
|
||||
alsoDeleteFiles = !alsoDeleteFiles
|
||||
@ -196,10 +196,10 @@ class TasksRoot : Routes.Route() {
|
||||
Checkbox(checked = alsoDeleteFiles, onCheckedChange = {
|
||||
alsoDeleteFiles = it
|
||||
})
|
||||
Text("Also delete files")
|
||||
Text(translation["delete_files_option"])
|
||||
}
|
||||
} 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 = {
|
||||
@ -242,7 +242,7 @@ class TasksRoot : Routes.Route() {
|
||||
showConfirmDialog = false
|
||||
}
|
||||
) {
|
||||
Text("No")
|
||||
Text(context.translation["button.negative"])
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -429,7 +429,7 @@ class TasksRoot : Routes.Route() {
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
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)
|
||||
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.MoreVert
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.rounded.Save
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
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.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
@ -53,7 +54,6 @@ class FeaturesRoot : Routes.Route() {
|
||||
}
|
||||
|
||||
private var activityLauncherHelper: ActivityLauncherHelper? = null
|
||||
private lateinit var rememberScaffoldState: BottomSheetScaffoldState
|
||||
|
||||
private val allContainers by lazy {
|
||||
val containers = mutableMapOf<String, PropertyPair<*>>()
|
||||
@ -444,48 +444,62 @@ class FeaturesRoot : Routes.Route() {
|
||||
var showResetConfirmationDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showResetConfirmationDialog) {
|
||||
Dialog(onDismissRequest = { showResetConfirmationDialog = false }) {
|
||||
alertDialogs.ConfirmDialog(
|
||||
title = "Reset config",
|
||||
message = "Are you sure you want to reset the config?",
|
||||
onConfirm = {
|
||||
context.config.reset()
|
||||
context.shortToast("Config successfully reset!")
|
||||
},
|
||||
onDismiss = { showResetConfirmationDialog = false }
|
||||
)
|
||||
}
|
||||
AlertDialog(
|
||||
title = { Text(text = context.translation["manager.dialogs.reset_config.title"]) },
|
||||
text = { Text(text = context.translation["manager.dialogs.reset_config.content"]) },
|
||||
onDismissRequest = { showResetConfirmationDialog = false },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
context.config.reset()
|
||||
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 {
|
||||
mapOf(
|
||||
"Export" to {
|
||||
translation["export_option"] to {
|
||||
activityLauncher {
|
||||
saveFile("config.json", "application/json") { uri ->
|
||||
context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
|
||||
context.config.writeConfig()
|
||||
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 {
|
||||
openFile("application/json") { uri ->
|
||||
context.androidContext.contentResolver.openInputStream(Uri.parse(uri))?.use {
|
||||
runCatching {
|
||||
context.config.loadFromString(it.readBytes().toString(Charsets.UTF_8))
|
||||
}.onFailure {
|
||||
context.longToast("Failed to import config ${it.message}")
|
||||
context.longToast(translation.format("config_import_failure_toast", "error" to it.message.toString()))
|
||||
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(
|
||||
properties: List<PropertyPair<*>>
|
||||
) {
|
||||
rememberScaffoldState = rememberBottomSheetScaffoldState()
|
||||
Scaffold(
|
||||
snackbarHost = { SnackbarHost(rememberScaffoldState.snackbarHostState) },
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
content = { innerPadding ->
|
||||
LazyColumn(
|
||||
@ -541,23 +553,23 @@ class FeaturesRoot : Routes.Route() {
|
||||
}
|
||||
|
||||
override val floatingActionButton: @Composable () -> Unit = {
|
||||
val scope = rememberCoroutineScope()
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
fun saveConfig() {
|
||||
context.coroutineScope.launch(Dispatchers.IO) {
|
||||
context.config.writeConfig()
|
||||
scope.launch {
|
||||
rememberScaffoldState.snackbarHostState.showSnackbar("Saved")
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(10.dp),
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimary,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Save,
|
||||
contentDescription = null
|
||||
)
|
||||
context.log.verbose("saved config!")
|
||||
}
|
||||
}
|
||||
|
||||
OnLifecycleEvent { _, event ->
|
||||
if (event == Lifecycle.Event.ON_PAUSE || event == Lifecycle.Event.ON_STOP) {
|
||||
saveConfig()
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
saveConfig()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -566,10 +578,8 @@ class FeaturesRoot : Routes.Route() {
|
||||
private fun Container(
|
||||
configContainer: ConfigContainer
|
||||
) {
|
||||
val properties = remember {
|
||||
PropertiesView(remember {
|
||||
configContainer.properties.map { PropertyPair(it.key, it.value) }
|
||||
}
|
||||
|
||||
PropertiesView(properties)
|
||||
})
|
||||
}
|
||||
}
|
@ -68,28 +68,27 @@ class HomeLogs : Routes.Route() {
|
||||
navigate()
|
||||
showDropDown = false
|
||||
}, text = {
|
||||
Text(
|
||||
text = context.translation["manager.sections.home.logs.clear_logs_button"]
|
||||
)
|
||||
Text(translation["clear_logs_button"])
|
||||
})
|
||||
|
||||
DropdownMenuItem(onClick = {
|
||||
activityLauncherHelper.saveFile("snapenhance-logs-${System.currentTimeMillis()}.zip", "application/zip") { uri ->
|
||||
context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
|
||||
runCatching {
|
||||
context.log.exportLogsToZip(it)
|
||||
context.longToast("Saved logs to $uri")
|
||||
}.onFailure {
|
||||
context.longToast("Failed to save logs to $uri!")
|
||||
context.log.error("Failed to save logs to $uri!", it)
|
||||
context.coroutineScope.launch {
|
||||
context.shortToast(translation["saving_logs_toast"])
|
||||
context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
|
||||
runCatching {
|
||||
context.log.exportLogsToZip(it)
|
||||
context.longToast(translation["saved_logs_success_toast"])
|
||||
}.onFailure {
|
||||
context.longToast(translation["saved_logs_failure_toast"])
|
||||
context.log.error("Failed to save logs to $uri!", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
showDropDown = false
|
||||
}, text = {
|
||||
Text(
|
||||
text = context.translation["manager.sections.home.logs.export_logs_button"]
|
||||
)
|
||||
Text(translation["export_logs_button"])
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -141,7 +140,7 @@ class HomeLogs : Routes.Route() {
|
||||
item {
|
||||
if (lineCount == 0 && logReader != null) {
|
||||
Text(
|
||||
text = "No logs found!",
|
||||
text = translation["no_logs_hint"],
|
||||
modifier = Modifier.padding(16.dp),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Light
|
||||
|
@ -198,9 +198,9 @@ class HomeRoot : Routes.Route() {
|
||||
}
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
if (latestUpdate != null) {
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
OutlinedCard(
|
||||
modifier = Modifier
|
||||
.padding(all = cardMargin)
|
||||
@ -218,13 +218,13 @@ class HomeRoot : Routes.Route() {
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = "SnapEnhance Update",
|
||||
text = translation["update_title"],
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
Text(
|
||||
fontSize = 12.sp,
|
||||
text = "Version ${latestUpdate?.versionName} is available!",
|
||||
text = translation.format("update_content", "version" to (latestUpdate?.versionName ?: "unknown")),
|
||||
lineHeight = 20.sp
|
||||
)
|
||||
}
|
||||
@ -235,7 +235,7 @@ class HomeRoot : Routes.Route() {
|
||||
}
|
||||
)
|
||||
}, modifier = Modifier.height(40.dp)) {
|
||||
Text(text = "Download")
|
||||
Text(text = translation["update_button"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ class HomeSettings : Routes.Route() {
|
||||
|
||||
if (requireConfirmation && confirmationDialog) {
|
||||
Dialog(onDismissRequest = { confirmationDialog = false }) {
|
||||
dialogs.ConfirmDialog(title = "Are you sure?", onConfirm = {
|
||||
dialogs.ConfirmDialog(title = context.translation["manager.dialogs.action_confirm.title"], onConfirm = {
|
||||
action()
|
||||
confirmationDialog = false
|
||||
}, onDismiss = {
|
||||
@ -148,7 +148,7 @@ class HomeSettings : Routes.Route() {
|
||||
.fillMaxSize()
|
||||
.verticalScroll(ScrollState(0))
|
||||
) {
|
||||
RowTitle(title = "Actions")
|
||||
RowTitle(title = translation["actions_title"])
|
||||
EnumAction.entries.forEach { enumAction ->
|
||||
RowAction(key = enumAction.key) {
|
||||
launchActionIntent(enumAction)
|
||||
@ -160,7 +160,7 @@ class HomeSettings : Routes.Route() {
|
||||
RowAction(key = "change_language") {
|
||||
context.checkForRequirements(Requirements.LANGUAGE)
|
||||
}
|
||||
RowTitle(title = "Message Logger")
|
||||
RowTitle(title = translation["message_logger_title"])
|
||||
ShiftedRow {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
@ -184,8 +184,11 @@ class HomeSettings : Routes.Route() {
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Text(text = "$storedMessagesCount messages")
|
||||
Text(text = "$storedStoriesCount stories")
|
||||
Text(
|
||||
translation.format("message_logger_summary",
|
||||
"messageCount" to storedMessagesCount.toString(),
|
||||
"storyCount" to storedStoriesCount.toString()
|
||||
), maxLines = 2)
|
||||
}
|
||||
Button(onClick = {
|
||||
runCatching {
|
||||
@ -201,7 +204,7 @@ class HomeSettings : Routes.Route() {
|
||||
context.longToast("Failed to export database! ${it.localizedMessage}")
|
||||
}
|
||||
}) {
|
||||
Text(text = "Export")
|
||||
Text(text = translation["export_button"])
|
||||
}
|
||||
Button(onClick = {
|
||||
runCatching {
|
||||
@ -212,10 +215,10 @@ class HomeSettings : Routes.Route() {
|
||||
context.log.error("Failed to clear messages", it)
|
||||
context.longToast("Failed to clear messages! ${it.localizedMessage}")
|
||||
}.onSuccess {
|
||||
context.shortToast("Done!")
|
||||
context.shortToast(translation["success_toast"])
|
||||
}
|
||||
}) {
|
||||
Text(text = "Clear")
|
||||
Text(text = translation["clear_button"])
|
||||
}
|
||||
}
|
||||
OutlinedButton(
|
||||
@ -226,12 +229,12 @@ class HomeSettings : Routes.Route() {
|
||||
routes.loggerHistory.navigate()
|
||||
}
|
||||
) {
|
||||
Text(text = "View Message History")
|
||||
Text(translation["view_logger_history_button"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowTitle(title = "Debug")
|
||||
RowTitle(title = translation["debug_title"])
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
@ -275,10 +278,10 @@ class HomeSettings : Routes.Route() {
|
||||
context.log.error("Failed to clear file", it)
|
||||
context.longToast("Failed to clear file! ${it.localizedMessage}")
|
||||
}.onSuccess {
|
||||
context.shortToast("Done!")
|
||||
context.shortToast(translation["success_toast"])
|
||||
}
|
||||
}) {
|
||||
Text(text = "Clear File")
|
||||
Text(translation["clear_button"])
|
||||
}
|
||||
}
|
||||
ShiftedRow {
|
||||
|
@ -148,7 +148,7 @@ class LoggedStories : Routes.Route() {
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Text(text = "Open")
|
||||
Text(text = context.translation["button.open"])
|
||||
}
|
||||
|
||||
Button(onClick = {
|
||||
@ -160,7 +160,7 @@ class LoggedStories : Routes.Route() {
|
||||
)
|
||||
)
|
||||
}) {
|
||||
Text(text = "Download")
|
||||
Text(text = context.translation["button.download"])
|
||||
}
|
||||
|
||||
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()) {
|
||||
Text(text = "No stories found", Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
|
||||
Text(text = translation["no_stories"], Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
|
||||
}
|
||||
|
||||
LazyVerticalGrid(
|
||||
@ -212,7 +212,7 @@ class LoggedStories : Routes.Route() {
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
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 {
|
||||
Image(
|
||||
painter = rememberAsyncImagePainter(
|
||||
|
@ -29,7 +29,6 @@ import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
|
||||
class ManageScope: Routes.Route() {
|
||||
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) {
|
||||
when (scope) {
|
||||
@ -56,7 +55,7 @@ class ManageScope: Routes.Route() {
|
||||
deleteConfirmDialog = false
|
||||
}) {
|
||||
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 },
|
||||
onConfirm = {
|
||||
deleteScope(scope, id, coroutineScope); deleteConfirmDialog = false
|
||||
@ -94,7 +93,6 @@ class ManageScope: Routes.Route() {
|
||||
SectionTitle(translation["rules_title"])
|
||||
|
||||
ContentCard {
|
||||
//manager anti features etc
|
||||
MessagingRuleType.entries.forEach { ruleType ->
|
||||
var ruleEnabled by remember {
|
||||
mutableStateOf(rules.any { it.key == ruleType.key })
|
||||
@ -110,14 +108,17 @@ class ManageScope: Routes.Route() {
|
||||
text = if (ruleType.listMode && ruleState != null) {
|
||||
context.translation["rules.properties.${ruleType.key}.options.${ruleState.key}"]
|
||||
} 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,
|
||||
enabled = if (ruleType.listMode) ruleState != null else true,
|
||||
onCheckedChange = {
|
||||
context.modDatabase.setRule(id, ruleType.key, 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 stringBuilder = StringBuilder()
|
||||
val diff = timestamp - now
|
||||
@ -180,7 +180,7 @@ class ManageScope: Routes.Route() {
|
||||
stringBuilder.append("$seconds seconds ")
|
||||
return stringBuilder.toString()
|
||||
}
|
||||
return "Expired"
|
||||
return null
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
@ -234,7 +234,7 @@ class ManageScope: Routes.Route() {
|
||||
put("id", id)
|
||||
}
|
||||
}) {
|
||||
Text("Show Logged Stories")
|
||||
Text(translation["logged_stories_button"])
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,10 +259,11 @@ class ManageScope: Routes.Route() {
|
||||
), maxLines = 1
|
||||
)
|
||||
Text(
|
||||
text = translation.format(
|
||||
text = computeStreakETA(streaks.expirationTimestamp)?.let { translation.format(
|
||||
"streaks_expiration_text",
|
||||
"eta" to computeStreakETA(streaks.expirationTimestamp)
|
||||
), maxLines = 1
|
||||
"eta" to it
|
||||
) } ?: translation["streaks_expiration_text_expired"],
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
Row(
|
||||
@ -282,7 +283,6 @@ class ManageScope: Routes.Route() {
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
// e2ee section
|
||||
|
||||
if (context.config.root.experimental.e2eEncryption.globalState == true) {
|
||||
SectionTitle(translation["e2ee_title"])
|
||||
@ -362,7 +362,6 @@ class ManageScope: Routes.Route() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
|
@ -47,7 +47,6 @@ class MessagingPreview: Routes.Route() {
|
||||
private lateinit var messagingBridge: MessagingBridge
|
||||
private lateinit var previewScrollState: LazyListState
|
||||
|
||||
private val myUserId by lazy { messagingBridge.myUserId }
|
||||
private val contentTypeTranslation by lazy { context.translation.getCategory("content_type") }
|
||||
|
||||
private var messages = mutableStateListOf<Message>()
|
||||
@ -117,7 +116,7 @@ class MessagingPreview: Routes.Route() {
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
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))
|
||||
availableTypes.forEach { contentType ->
|
||||
Row(
|
||||
@ -135,7 +134,7 @@ class MessagingPreview: Routes.Route() {
|
||||
enabled = !selectAllState,
|
||||
onCheckedChange = { toggleContentType(contentType) }
|
||||
)
|
||||
Text(text = contentType.toString())
|
||||
Text(text = contentTypeTranslation[contentType.name])
|
||||
}
|
||||
}
|
||||
Row(
|
||||
@ -148,7 +147,7 @@ class MessagingPreview: Routes.Route() {
|
||||
Switch(checked = selectAllState, onCheckedChange = {
|
||||
selectAllState = it
|
||||
})
|
||||
Text(text = "Select all")
|
||||
Text(text = context.translation["manager.dialogs.messaging_action.select_all_button"])
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@ -156,13 +155,13 @@ class MessagingPreview: Routes.Route() {
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
Button(onClick = { onDismiss() }) {
|
||||
Text("Cancel")
|
||||
Text(context.translation["button.cancel"])
|
||||
}
|
||||
Button(onClick = {
|
||||
onChoose(if (selectAllState) ContentType.entries.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))
|
||||
) {
|
||||
DropdownMenu(
|
||||
expanded = taskSelectionDropdown, onDismissRequest = { taskSelectionDropdown = false }
|
||||
expanded = taskSelectionDropdown && messages.isNotEmpty(), onDismissRequest = { taskSelectionDropdown = false }
|
||||
) {
|
||||
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)
|
||||
if (hasSelection) runCurrentTask()
|
||||
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)
|
||||
if (hasSelection) runCurrentTask()
|
||||
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(
|
||||
MessagingConstraints.NO_USER_ID(myUserId),
|
||||
MessagingConstraints.NO_USER_ID(messagingBridge.myUserId),
|
||||
MessagingConstraints.CONTENT_TYPE(arrayOf(ContentType.SNAP))
|
||||
))
|
||||
runCurrentTask()
|
||||
}
|
||||
ActionButton(text = if (hasSelection) "Delete selected" else "Delete all", icon = Icons.Rounded.DeleteForever) {
|
||||
launchMessagingTask(MessagingTaskType.DELETE, listOf(MessagingConstraints.USER_ID(myUserId))) { message ->
|
||||
ActionButton(text = translation[if (hasSelection) "delete_selection_option" else "delete_all_option"], icon = Icons.Rounded.DeleteForever) {
|
||||
launchMessagingTask(MessagingTaskType.DELETE, listOf(MessagingConstraints.USER_ID(messagingBridge.myUserId))) { message ->
|
||||
coroutineScope.launch {
|
||||
message.contentType = ContentType.STATUS.id
|
||||
}
|
||||
@ -377,7 +376,7 @@ class MessagingPreview: Routes.Route() {
|
||||
.padding(40.dp),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text("No messages")
|
||||
Text(translation["no_message_hint"])
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
@ -428,12 +427,7 @@ class MessagingPreview: Routes.Route() {
|
||||
conversationId!!,
|
||||
20,
|
||||
lastMessageId
|
||||
)?.reversed()
|
||||
|
||||
if (queriedMessages == null) {
|
||||
context.shortToast("Failed to fetch messages")
|
||||
return@cs
|
||||
}
|
||||
)?.reversed() ?: throw IllegalStateException("Failed to fetch messages. Bridge returned null")
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
messages.addAll(queriedMessages)
|
||||
@ -441,7 +435,7 @@ class MessagingPreview: Routes.Route() {
|
||||
}
|
||||
}.onFailure {
|
||||
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 {
|
||||
messagingBridge = context.bridgeService!!.messagingBridge!!
|
||||
conversationId = if (scope == SocialScope.FRIEND) messagingBridge.getOneToOneConversationId(scopeId) else scopeId
|
||||
if (conversationId == null) {
|
||||
context.longToast("Failed to fetch conversation id")
|
||||
return
|
||||
}
|
||||
conversationId = (if (scope == SocialScope.FRIEND) messagingBridge.getOneToOneConversationId(scopeId) else scopeId) ?: throw IllegalStateException("Failed to get conversation id")
|
||||
if (runCatching { !messagingBridge.isSessionStarted }.getOrDefault(true)) {
|
||||
context.androidContext.packageManager.getLaunchIntentForPackage(
|
||||
Constants.SNAPCHAT_PACKAGE_NAME
|
||||
@ -474,7 +464,7 @@ class MessagingPreview: Routes.Route() {
|
||||
}
|
||||
fetchNewMessages()
|
||||
}.onFailure {
|
||||
context.longToast("Failed to initialize messaging bridge")
|
||||
context.longToast(translation["bridge_init_failed"])
|
||||
context.log.error("Failed to initialize messaging bridge", it)
|
||||
}
|
||||
}
|
||||
@ -511,7 +501,7 @@ class MessagingPreview: Routes.Route() {
|
||||
.fillMaxSize()
|
||||
) {
|
||||
if (hasBridgeError) {
|
||||
Text("Failed to connect to Snapchat through bridge service")
|
||||
Text(translation["bridge_connection_failed"])
|
||||
}
|
||||
|
||||
if (!isBridgeConnected && !hasBridgeError) {
|
||||
|
@ -164,8 +164,8 @@ class SocialRoot : Routes.Route() {
|
||||
else MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(
|
||||
text = context.translation.format(
|
||||
"manager.sections.social.streaks_expiration_short",
|
||||
text = translation.format(
|
||||
"streaks_expiration_short",
|
||||
"hours" to (((streaks.expirationTimestamp - System.currentTimeMillis()) / 3600000).toInt().takeIf { it > 0 } ?: 0)
|
||||
.toString()
|
||||
),
|
||||
|
@ -44,9 +44,9 @@ class AlertDialogs(
|
||||
@Composable
|
||||
fun DefaultDialogCard(modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) {
|
||||
Card(
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
shape = MaterialTheme.shapes.large,
|
||||
modifier = Modifier
|
||||
.padding(10.dp, 5.dp, 10.dp, 10.dp)
|
||||
.padding(16.dp)
|
||||
.then(modifier),
|
||||
) {
|
||||
Column(
|
||||
|
@ -21,6 +21,11 @@
|
||||
}
|
||||
},
|
||||
|
||||
"scopes": {
|
||||
"friend": "Friend",
|
||||
"group": "Group"
|
||||
},
|
||||
|
||||
"manager": {
|
||||
"routes": {
|
||||
"tasks": "Tasks",
|
||||
@ -37,27 +42,92 @@
|
||||
},
|
||||
"sections": {
|
||||
"home": {
|
||||
"logs": {
|
||||
"clear_logs_button": "Clear Logs",
|
||||
"export_logs_button": "Export Logs"
|
||||
}
|
||||
"update_title": "SnapEnhance Update",
|
||||
"update_content": "Version {version} is available!",
|
||||
"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": {
|
||||
"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": {
|
||||
"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": {
|
||||
"streaks_expiration_short": "{hours}h"
|
||||
},
|
||||
"manage_scope": {
|
||||
"logged_stories_button": "Show Logged Stories",
|
||||
"e2ee_title": "End-to-End Encryption",
|
||||
"rules_title": "Rules",
|
||||
"participants_text": "{count} participants",
|
||||
"not_found": "Not found",
|
||||
"streaks_title": "Streaks",
|
||||
"streaks_length_text": "Length: {length}",
|
||||
"streaks_expiration_short": "{hours}h",
|
||||
"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": {
|
||||
@ -71,6 +141,15 @@
|
||||
"scripting_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."
|
||||
},
|
||||
"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