Further splitting the translation class into more methods to simplify recompositions

pull/444/head^2
Vitor Pamplona 2023-06-06 16:47:14 -04:00
rodzic c10db10430
commit b9654d164f
3 zmienionych plików z 242 dodań i 180 usunięć

Wyświetl plik

@ -30,6 +30,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
@ -95,7 +96,7 @@ fun JoinUserOrChannelView(searchBarViewModel: SearchBarViewModel, onClose: () ->
Dialog(
onDismissRequest = {
NostrSearchEventOrUserDataSource.clear()
searchBarViewModel.clean()
searchBarViewModel.clear()
onClose()
},
properties = DialogProperties(
@ -114,7 +115,7 @@ fun JoinUserOrChannelView(searchBarViewModel: SearchBarViewModel, onClose: () ->
verticalAlignment = Alignment.CenterVertically
) {
CloseButton(onCancel = {
searchBarViewModel.clean()
searchBarViewModel.clear()
NostrSearchEventOrUserDataSource.clear()
onClose()
})
@ -225,8 +226,10 @@ private fun SearchEditTextForJoin(
val keyboardController = LocalSoftwareKeyboardController.current
LaunchedEffect(Unit) {
delay(100)
focusRequester.requestFocus()
launch {
delay(100)
focusRequester.requestFocus()
}
}
Row(
@ -272,7 +275,7 @@ private fun SearchEditTextForJoin(
if (searchBarViewModel.isSearching) {
IconButton(
onClick = {
searchBarViewModel.clean()
searchBarViewModel.clear()
NostrSearchEventOrUserDataSource.clear()
}
) {
@ -316,47 +319,57 @@ private fun RenderSearchResults(
users,
key = { _, item -> "u" + item.pubkeyHex }
) { _, item ->
UserComposeForChat(
item,
accountViewModel = accountViewModel,
nav = nav
)
UserComposeForChat(item, accountViewModel) {
nav("Room/${item.pubkeyHex}")
searchBarViewModel.clear()
}
}
itemsIndexed(
channels,
key = { _, item -> "c" + item.idHex }
) { _, item ->
ChannelName(
channelIdHex = item.idHex,
channelPicture = item.profilePicture(),
channelTitle = {
Text(
"${item.info.name}",
fontWeight = FontWeight.Bold
)
},
channelLastTime = null,
channelLastContent = item.info.about,
false,
onClick = { nav("Channel/${item.idHex}") }
)
RenderChannel(item) {
nav("Channel/${item.idHex}")
searchBarViewModel.clear()
}
}
}
}
}
}
@Composable
private fun RenderChannel(
item: com.vitorpamplona.amethyst.model.Channel,
onClick: () -> Unit
) {
ChannelName(
channelIdHex = item.idHex,
channelPicture = item.profilePicture(),
channelTitle = {
Text(
"${item.info.name}",
fontWeight = FontWeight.Bold
)
},
channelLastTime = null,
channelLastContent = item.info.about,
false,
onClick = onClick
)
}
@Composable
fun UserComposeForChat(
baseUser: User,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
onClick: () -> Unit
) {
Column(
modifier =
Modifier.clickable(
onClick = { nav("Room/${baseUser.pubkeyHex}") }
onClick = onClick
)
) {
Row(
@ -368,7 +381,7 @@ fun UserComposeForChat(
),
verticalAlignment = Alignment.CenterVertically
) {
UserPicture(baseUser, nav, accountViewModel, 55.dp)
UserPicture(baseUser, 55.dp, accountViewModel)
Column(
modifier = Modifier
@ -379,15 +392,7 @@ fun UserComposeForChat(
UsernameDisplay(baseUser)
}
val baseUserState by baseUser.live().metadata.observeAsState()
val user = baseUserState?.user ?: return
Text(
user.info?.about ?: "",
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
DisplayUserAboutInfo(baseUser)
}
}
@ -397,3 +402,20 @@ fun UserComposeForChat(
)
}
}
@Composable
private fun DisplayUserAboutInfo(baseUser: User) {
val baseUserState by baseUser.live().metadata.observeAsState()
val about by remember(baseUserState) {
derivedStateOf {
baseUserState?.user?.info?.about ?: ""
}
}
Text(
text = about,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}

Wyświetl plik

@ -187,7 +187,7 @@ class SearchBarViewModel(val account: Account) : ViewModel() {
_searchResultsChannels.emit(LocalCache.findChannelsStartingWith(searchValue))
}
fun clean() {
fun clear() {
searchValue = ""
_searchResultsUsers.value = emptyList()
_searchResultsChannels.value = emptyList()
@ -317,7 +317,7 @@ private fun SearchTextField(
if (searchBarViewModel.isSearching) {
IconButton(
onClick = {
searchBarViewModel.clean()
searchBarViewModel.clear()
NostrSearchEventOrUserDataSource.clear()
}
) {

Wyświetl plik

@ -49,33 +49,32 @@ fun TranslatableRichTextViewer(
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
var translatedTextState by remember {
mutableStateOf(ResultOrError(content, null, null, null))
}
var showOriginal by remember { mutableStateOf(false) }
var langSettingsPopupExpanded by remember { mutableStateOf(false) }
TranslateAndWatchLanguageChanges(content, accountViewModel) { result, newShowOriginal ->
if (translatedTextState.result != result.result ||
translatedTextState.sourceLang != result.sourceLang ||
translatedTextState.targetLang != result.targetLang
) {
translatedTextState = result
}
if (showOriginal != newShowOriginal) {
showOriginal = newShowOriginal
}
}
val toBeViewed by remember(translatedTextState) {
derivedStateOf {
if (showOriginal) content else translatedTextState.result ?: content
}
}
Column {
var translatedTextState by remember {
mutableStateOf(ResultOrError(content, null, null, null))
}
var showOriginal by remember { mutableStateOf(false) }
TranslateAndWatchLanguageChanges(content, accountViewModel) { result, newShowOriginal ->
if (translatedTextState.result != result.result ||
translatedTextState.sourceLang != result.sourceLang ||
translatedTextState.targetLang != result.targetLang
) {
translatedTextState = result
}
if (showOriginal != newShowOriginal) {
showOriginal = newShowOriginal
}
}
val toBeViewed by remember(translatedTextState) {
derivedStateOf {
if (showOriginal) content else translatedTextState.result ?: content
}
}
ExpandableRichTextViewer(
toBeViewed,
canPreview,
@ -86,69 +85,174 @@ fun TranslatableRichTextViewer(
nav
)
val target = translatedTextState.targetLang
val source = translatedTextState.sourceLang
if (source != null && target != null && source != target) {
val scope = rememberCoroutineScope()
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 5.dp)
if (translatedTextState.sourceLang != null &&
translatedTextState.targetLang != null &&
translatedTextState.sourceLang != translatedTextState.targetLang
) {
TranslationMessage(
translatedTextState.sourceLang!!,
translatedTextState.targetLang!!,
accountViewModel
) {
val clickableTextStyle =
SpanStyle(color = MaterialTheme.colors.primary.copy(alpha = 0.52f))
showOriginal = it
}
}
}
}
val annotatedTranslationString = buildAnnotatedString {
withStyle(clickableTextStyle) {
pushStringAnnotation("langSettings", true.toString())
append(stringResource(R.string.translations_auto))
}
@Composable
private fun TranslationMessage(
source: String,
target: String,
accountViewModel: AccountViewModel,
onChangeWhatToShow: (Boolean) -> Unit
) {
var langSettingsPopupExpanded by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
append("-${stringResource(R.string.translations_translated_from)} ")
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 5.dp)
) {
val clickableTextStyle =
SpanStyle(color = MaterialTheme.colors.primary.copy(alpha = 0.52f))
withStyle(clickableTextStyle) {
pushStringAnnotation("showOriginal", true.toString())
append(Locale(source).displayName)
}
val annotatedTranslationString = buildAnnotatedString {
withStyle(clickableTextStyle) {
pushStringAnnotation("langSettings", true.toString())
append(stringResource(R.string.translations_auto))
}
append(" ${stringResource(R.string.translations_to)} ")
append("-${stringResource(R.string.translations_translated_from)} ")
withStyle(clickableTextStyle) {
pushStringAnnotation("showOriginal", false.toString())
append(Locale(target).displayName)
withStyle(clickableTextStyle) {
pushStringAnnotation("showOriginal", true.toString())
append(Locale(source).displayName)
}
append(" ${stringResource(R.string.translations_to)} ")
withStyle(clickableTextStyle) {
pushStringAnnotation("showOriginal", false.toString())
append(Locale(target).displayName)
}
}
ClickableText(
text = annotatedTranslationString,
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.onSurface.copy(
alpha = 0.32f
)
),
overflow = TextOverflow.Visible,
maxLines = 3
) { spanOffset ->
annotatedTranslationString.getStringAnnotations(spanOffset, spanOffset)
.firstOrNull()
?.also { span ->
if (span.tag == "showOriginal") {
onChangeWhatToShow(span.item.toBoolean())
} else {
langSettingsPopupExpanded = !langSettingsPopupExpanded
}
}
}
ClickableText(
text = annotatedTranslationString,
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)),
overflow = TextOverflow.Visible,
maxLines = 3
) { spanOffset ->
annotatedTranslationString.getStringAnnotations(spanOffset, spanOffset)
.firstOrNull()
?.also { span ->
if (span.tag == "showOriginal") {
showOriginal = span.item.toBoolean()
} else {
langSettingsPopupExpanded = !langSettingsPopupExpanded
}
}
DropdownMenu(
expanded = langSettingsPopupExpanded,
onDismissRequest = { langSettingsPopupExpanded = false }
) {
DropdownMenuItem(onClick = {
scope.launch(Dispatchers.IO) {
accountViewModel.dontTranslateFrom(source)
langSettingsPopupExpanded = false
}
}) {
if (source in accountViewModel.account.dontTranslateFrom) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
} else {
Spacer(modifier = Modifier.size(24.dp))
}
DropdownMenu(
expanded = langSettingsPopupExpanded,
onDismissRequest = { langSettingsPopupExpanded = false }
) {
Spacer(modifier = Modifier.size(10.dp))
Text(
stringResource(
R.string.translations_never_translate_from_lang,
Locale(source).displayName
)
)
}
Divider()
DropdownMenuItem(onClick = {
scope.launch(Dispatchers.IO) {
accountViewModel.prefer(source, target, source)
langSettingsPopupExpanded = false
}
}) {
if (accountViewModel.account.preferenceBetween(source, target) == source) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
} else {
Spacer(modifier = Modifier.size(24.dp))
}
Spacer(modifier = Modifier.size(10.dp))
Text(
stringResource(
R.string.translations_show_in_lang_first,
Locale(source).displayName
)
)
}
DropdownMenuItem(onClick = {
scope.launch(Dispatchers.IO) {
accountViewModel.prefer(source, target, target)
langSettingsPopupExpanded = false
}
}) {
if (accountViewModel.account.preferenceBetween(source, target) == target) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
} else {
Spacer(modifier = Modifier.size(24.dp))
}
Spacer(modifier = Modifier.size(10.dp))
Text(
stringResource(
R.string.translations_show_in_lang_first,
Locale(target).displayName
)
)
}
Divider()
val languageList =
ConfigurationCompat.getLocales(Resources.getSystem().configuration)
for (i in 0 until languageList.size()) {
languageList.get(i)?.let { lang ->
DropdownMenuItem(onClick = {
scope.launch(Dispatchers.IO) {
accountViewModel.dontTranslateFrom(source)
accountViewModel.translateTo(lang)
langSettingsPopupExpanded = false
}
}) {
if (source in accountViewModel.account.dontTranslateFrom) {
if (lang.language in accountViewModel.account.translateTo) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
@ -160,76 +264,12 @@ fun TranslatableRichTextViewer(
Spacer(modifier = Modifier.size(10.dp))
Text(stringResource(R.string.translations_never_translate_from_lang, Locale(source).displayName))
}
Divider()
DropdownMenuItem(onClick = {
scope.launch(Dispatchers.IO) {
accountViewModel.prefer(source, target, source)
langSettingsPopupExpanded = false
}
}) {
if (accountViewModel.account.preferenceBetween(source, target) == source) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
modifier = Modifier.size(24.dp)
Text(
stringResource(
R.string.translations_always_translate_to_lang,
lang.displayName
)
} else {
Spacer(modifier = Modifier.size(24.dp))
}
Spacer(modifier = Modifier.size(10.dp))
Text(stringResource(R.string.translations_show_in_lang_first, Locale(source).displayName))
}
DropdownMenuItem(onClick = {
scope.launch(Dispatchers.IO) {
accountViewModel.prefer(source, target, target)
langSettingsPopupExpanded = false
}
}) {
if (accountViewModel.account.preferenceBetween(source, target) == target) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
} else {
Spacer(modifier = Modifier.size(24.dp))
}
Spacer(modifier = Modifier.size(10.dp))
Text(stringResource(R.string.translations_show_in_lang_first, Locale(target).displayName))
}
Divider()
val languageList =
ConfigurationCompat.getLocales(Resources.getSystem().configuration)
for (i in 0 until languageList.size()) {
languageList.get(i)?.let { lang ->
DropdownMenuItem(onClick = {
scope.launch(Dispatchers.IO) {
accountViewModel.translateTo(lang)
langSettingsPopupExpanded = false
}
}) {
if (lang.language in accountViewModel.account.translateTo) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
} else {
Spacer(modifier = Modifier.size(24.dp))
}
Spacer(modifier = Modifier.size(10.dp))
Text(stringResource(R.string.translations_always_translate_to_lang, lang.displayName))
}
}
)
}
}
}