From f8def03c60cc222d9562967dddfad125a235ee41 Mon Sep 17 00:00:00 2001 From: JohnOberhauser <14130581+JohnOberhauser@users.noreply.github.com> Date: Wed, 15 May 2024 15:35:58 -0500 Subject: [PATCH] Adding language option when creating a new post (#526) - Adding a option to specify which language you are using when creating a new post - Updating account info when starting the app - Adding default language to a user's prefs --------- Co-authored-by: John Oberhauser --- .../kotlin/social/firefly/MainViewModel.kt | 3 + .../datastore/UserPreferencesDatastore.kt | 9 +++ .../UserPreferencesDatastoreManager.kt | 2 + .../core/datastore/user_preferences.proto | 1 + .../firefly/core/designsystem/icon/FfIcons.kt | 3 + .../src/main/res/drawable/globe.xml | 9 +++ .../core/ui/common/dropdown/FfDropDown.kt | 3 +- .../ui/common/src/main/res/values/strings.xml | 4 -- .../core/usecase/mastodon/auth/Login.kt | 2 + .../auth/UpdateAllLoggedInAccounts.kt | 2 + .../usecase/mastodon/status/PostStatus.kt | 2 + .../usecase/mastodon/status/PostStatusTest.kt | 2 + .../feature/post/NewPostInteractions.kt | 4 +- .../firefly/feature/post/NewPostModule.kt | 3 +- .../firefly/feature/post/NewPostScreen.kt | 19 ++++-- .../firefly/feature/post/NewPostViewModel.kt | 57 +++++++++++++++++- .../feature/post/bottombar/BottomBar.kt | 58 ++++-------------- .../feature/post/bottombar/BottomBarState.kt | 7 +++ .../post/bottombar/LanguageDropDown.kt | 60 +++++++++++++++++++ .../post/bottombar}/VisibilityDropDown.kt | 8 ++- .../firefly/feature/post/poll/PollUi.kt | 6 +- feature/post/src/main/res/values/strings.xml | 5 ++ 22 files changed, 201 insertions(+), 68 deletions(-) create mode 100644 core/designsystem/src/main/res/drawable/globe.xml create mode 100644 feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/LanguageDropDown.kt rename {core/ui/common/src/main/kotlin/social/firefly/core/ui/common/dropdown => feature/post/src/main/kotlin/social/firefly/feature/post/bottombar}/VisibilityDropDown.kt (95%) diff --git a/app/src/main/kotlin/social/firefly/MainViewModel.kt b/app/src/main/kotlin/social/firefly/MainViewModel.kt index 490730773..208dd8aae 100644 --- a/app/src/main/kotlin/social/firefly/MainViewModel.kt +++ b/app/src/main/kotlin/social/firefly/MainViewModel.kt @@ -18,6 +18,7 @@ import social.firefly.core.navigation.NavigationDestination import social.firefly.core.navigation.usecases.NavigateTo import social.firefly.core.repository.mastodon.TimelineRepository import social.firefly.core.share.ShareInfo +import social.firefly.core.usecase.mastodon.auth.UpdateAllLoggedInAccounts import social.firefly.ui.AppState class MainViewModel( @@ -25,6 +26,7 @@ class MainViewModel( private val userPreferencesDatastoreManager: UserPreferencesDatastoreManager, private val timelineRepository: TimelineRepository, private val eventRelay: EventRelay, + private val updateAllLoggedInAccounts: UpdateAllLoggedInAccounts, appPreferencesDatastore: AppPreferencesDatastore, ) : ViewModel() { @@ -47,6 +49,7 @@ class MainViewModel( if (userPreferencesDatastoreManager.isLoggedInToAtLeastOneAccount) { navigateTo(NavigationDestination.Tabs) handleIntent(intent) + updateAllLoggedInAccounts() } else { navigateTo(NavigationDestination.Auth) } diff --git a/core/datastore/src/main/kotlin/social/firefly/core/datastore/UserPreferencesDatastore.kt b/core/datastore/src/main/kotlin/social/firefly/core/datastore/UserPreferencesDatastore.kt index c178897c1..a79c6058c 100644 --- a/core/datastore/src/main/kotlin/social/firefly/core/datastore/UserPreferencesDatastore.kt +++ b/core/datastore/src/main/kotlin/social/firefly/core/datastore/UserPreferencesDatastore.kt @@ -52,6 +52,7 @@ class UserPreferencesDatastore internal constructor( val serializedPushKeys: Flow = dataStore.data.mapLatest { it.serializedPushKeys } val lastSeenHomeStatusId: Flow = dataStore.data.mapLatest { it.lastSeenHomeStatusId } val threadType: Flow = dataStore.data.mapLatest { it.threadType }.distinctUntilChanged() + val defaultLanguage: Flow = dataStore.data.mapLatest { it.defaultLanguage } suspend fun saveAvatarUrl(url: String) { dataStore.updateData { @@ -93,6 +94,14 @@ class UserPreferencesDatastore internal constructor( } } + suspend fun saveDefaultLanguage(language: String) { + dataStore.updateData { + it.toBuilder() + .setDefaultLanguage(language) + .build() + } + } + companion object { @Suppress("MaxLineLength") const val HOST_NAME_REGEX = "[a-zA-Z0-9]+(\\.[a-zA-Z0-9]+)+" diff --git a/core/datastore/src/main/kotlin/social/firefly/core/datastore/UserPreferencesDatastoreManager.kt b/core/datastore/src/main/kotlin/social/firefly/core/datastore/UserPreferencesDatastoreManager.kt index 5671dd07d..b7978493f 100644 --- a/core/datastore/src/main/kotlin/social/firefly/core/datastore/UserPreferencesDatastoreManager.kt +++ b/core/datastore/src/main/kotlin/social/firefly/core/datastore/UserPreferencesDatastoreManager.kt @@ -64,6 +64,7 @@ class UserPreferencesDatastoreManager( accountId: String, userName: String, avatarUrl: String, + defaultLanguage: String, ) { require(UserPreferencesDatastore.HOST_NAME_REGEX.toRegex().matches(domain)) val fileName = "$domain-$accountId-$counter-prefs.pb" @@ -83,6 +84,7 @@ class UserPreferencesDatastoreManager( ).apply { saveUserName(userName) saveAvatarUrl(avatarUrl) + saveDefaultLanguage(defaultLanguage) } _dataStores.update { it + newDataStore } diff --git a/core/datastore/src/main/proto/social/firefly/core/datastore/user_preferences.proto b/core/datastore/src/main/proto/social/firefly/core/datastore/user_preferences.proto index 585680d46..e7fa00e4c 100644 --- a/core/datastore/src/main/proto/social/firefly/core/datastore/user_preferences.proto +++ b/core/datastore/src/main/proto/social/firefly/core/datastore/user_preferences.proto @@ -20,4 +20,5 @@ message UserPreferences { ThreadType thread_type = 9; string avatar_url = 11; string user_name = 12; + string default_language = 13; } \ No newline at end of file diff --git a/core/designsystem/src/main/kotlin/social/firefly/core/designsystem/icon/FfIcons.kt b/core/designsystem/src/main/kotlin/social/firefly/core/designsystem/icon/FfIcons.kt index cf6bbd5e2..178b889e2 100644 --- a/core/designsystem/src/main/kotlin/social/firefly/core/designsystem/icon/FfIcons.kt +++ b/core/designsystem/src/main/kotlin/social/firefly/core/designsystem/icon/FfIcons.kt @@ -85,6 +85,9 @@ object FfIcons { @Composable fun gear() = painterResource(id = R.drawable.gear) + @Composable + fun globe() = painterResource(id = R.drawable.globe) + @Composable fun globeHemisphereWest() = painterResource(id = R.drawable.globe_hemisphere_west) diff --git a/core/designsystem/src/main/res/drawable/globe.xml b/core/designsystem/src/main/res/drawable/globe.xml new file mode 100644 index 000000000..7883eace3 --- /dev/null +++ b/core/designsystem/src/main/res/drawable/globe.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/common/src/main/kotlin/social/firefly/core/ui/common/dropdown/FfDropDown.kt b/core/ui/common/src/main/kotlin/social/firefly/core/ui/common/dropdown/FfDropDown.kt index 16a39cd1c..3335df56b 100644 --- a/core/ui/common/src/main/kotlin/social/firefly/core/ui/common/dropdown/FfDropDown.kt +++ b/core/ui/common/src/main/kotlin/social/firefly/core/ui/common/dropdown/FfDropDown.kt @@ -110,7 +110,8 @@ fun FfDropDownMenu( Box( modifier = Modifier .clip(RoundedCornerShape(8.dp)) // rounded corner is for the ripple - .clickable { if (canExpand) expanded.value = true }, + .clickable { if (canExpand) expanded.value = true } + .padding(8.dp), ) { // padding is for the ripple Row(modifier = Modifier.padding(padding)) { diff --git a/core/ui/common/src/main/res/values/strings.xml b/core/ui/common/src/main/res/values/strings.xml index 30aafe2f8..f32f740fd 100644 --- a/core/ui/common/src/main/res/values/strings.xml +++ b/core/ui/common/src/main/res/values/strings.xml @@ -22,10 +22,6 @@ more info Discover more - Public - Unlisted - Private - Direct There\'s nothing here Follow diff --git a/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/auth/Login.kt b/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/auth/Login.kt index 5e19914be..97ef33731 100644 --- a/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/auth/Login.kt +++ b/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/auth/Login.kt @@ -84,12 +84,14 @@ class Login( accessToken = accessToken, baseUrl = host, ) + val defaultLanguage = account.source?.defaultLanguage ?: "" userPreferencesDatastoreManager.createNewUserDatastore( domain = host, accessToken = accessToken, accountId = account.accountId, userName = account.displayName, avatarUrl = account.avatarUrl, + defaultLanguage = defaultLanguage, ) withContext(Dispatchers.IO) { databaseDelegate.clearAllTables() diff --git a/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/auth/UpdateAllLoggedInAccounts.kt b/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/auth/UpdateAllLoggedInAccounts.kt index 9192cc4d4..a904481e5 100644 --- a/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/auth/UpdateAllLoggedInAccounts.kt +++ b/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/auth/UpdateAllLoggedInAccounts.kt @@ -29,6 +29,8 @@ class UpdateAllLoggedInAccounts( }?.apply { saveAvatarUrl(account.avatarUrl) saveUserName(account.username) + val defaultLanguage = account.source?.defaultLanguage ?: "" + saveDefaultLanguage(defaultLanguage) } } } diff --git a/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/status/PostStatus.kt b/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/status/PostStatus.kt index 6bcef59f9..4ca427ee0 100644 --- a/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/status/PostStatus.kt +++ b/core/usecase/mastodon/src/main/kotlin/social/firefly/core/usecase/mastodon/status/PostStatus.kt @@ -37,6 +37,7 @@ class PostStatus internal constructor( pollCreate: PollCreate?, contentWarningText: String?, inReplyToId: String?, + languageCode: String?, ) = externalScope.async(dispatcherIo) { try { // asynchronously update all attachment descriptions before sending post @@ -76,6 +77,7 @@ class PostStatus internal constructor( contentWarningText }, inReplyToId = inReplyToId, + language = languageCode, ), ) saveStatusToDatabase(status) diff --git a/core/usecase/mastodon/src/test/kotlin/social/firefly/core/usecase/mastodon/status/PostStatusTest.kt b/core/usecase/mastodon/src/test/kotlin/social/firefly/core/usecase/mastodon/status/PostStatusTest.kt index 487dd11c9..bc544079f 100644 --- a/core/usecase/mastodon/src/test/kotlin/social/firefly/core/usecase/mastodon/status/PostStatusTest.kt +++ b/core/usecase/mastodon/src/test/kotlin/social/firefly/core/usecase/mastodon/status/PostStatusTest.kt @@ -46,6 +46,7 @@ class PostStatusTest : BaseUseCaseTest() { pollCreate = null, contentWarningText = null, inReplyToId = null, + languageCode = null, ) }, verifyBlock = { @@ -68,6 +69,7 @@ class PostStatusTest : BaseUseCaseTest() { pollCreate = null, contentWarningText = null, inReplyToId = null, + languageCode = null ) }, verifyBlock = { diff --git a/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostInteractions.kt b/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostInteractions.kt index b75a32e94..c7c31d6fa 100644 --- a/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostInteractions.kt +++ b/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostInteractions.kt @@ -9,13 +9,15 @@ interface NewPostInteractions { fun onPostClicked() fun onEditClicked() fun onVisibilitySelected(statusVisibility: StatusVisibility) + fun onLanguageSelected(code: String) } -object NewPostInteractionsNoOp : social.firefly.feature.post.NewPostInteractions { +object NewPostInteractionsNoOp : NewPostInteractions { override fun onScreenViewed() = Unit override fun onUploadImageClicked() = Unit override fun onUploadMediaClicked() = Unit override fun onPostClicked() = Unit override fun onEditClicked() = Unit override fun onVisibilitySelected(statusVisibility: StatusVisibility) = Unit + override fun onLanguageSelected(code: String) = Unit } diff --git a/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostModule.kt b/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostModule.kt index c05c85077..36d639088 100644 --- a/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostModule.kt +++ b/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostModule.kt @@ -24,7 +24,7 @@ val newPostModule = module { ) viewModel { parametersHolder -> - social.firefly.feature.post.NewPostViewModel( + NewPostViewModel( analytics = get(), replyStatusId = parametersHolder[0], editStatusId = parametersHolder[1], @@ -34,6 +34,7 @@ val newPostModule = module { showSnackbar = get(), getLoggedInUserAccountId = get(), accountRepository = get(), + userPreferencesDatastoreManager = get(), ) } diff --git a/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostScreen.kt b/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostScreen.kt index c1c640712..cecbdf0dd 100644 --- a/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostScreen.kt +++ b/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.CircleShape @@ -71,6 +72,7 @@ import social.firefly.core.ui.common.transparentTextFieldColors import social.firefly.core.ui.common.utils.getWindowHeightClass import social.firefly.feature.post.bottombar.BottomBar import social.firefly.feature.post.bottombar.BottomBarState +import social.firefly.feature.post.bottombar.VisibilityDropDownButton import social.firefly.feature.post.media.MediaInteractions import social.firefly.feature.post.poll.Poll import social.firefly.feature.post.poll.PollBar @@ -127,7 +129,7 @@ private fun NewPostScreen( mediaInteractions: MediaInteractions, pollInteractions: PollInteractions, contentWarningInteractions: ContentWarningInteractions, - newPostInteractions: social.firefly.feature.post.NewPostInteractions, + newPostInteractions: NewPostInteractions, ) { Box( modifier = @@ -183,7 +185,7 @@ private fun CompactNewPostScreenContent( mediaInteractions: MediaInteractions, pollInteractions: PollInteractions, contentWarningInteractions: ContentWarningInteractions, - newPostInteractions: social.firefly.feature.post.NewPostInteractions, + newPostInteractions: NewPostInteractions, ) { Row { Box( @@ -231,6 +233,8 @@ private fun NewPostScreenContent( onPostClicked = newPostInteractions::onPostClicked, onEditClicked = newPostInteractions::onEditClicked, sendButtonEnabled = newPostUiState.sendButtonEnabled, + visibility = newPostUiState.visibility, + newPostInteractions = newPostInteractions, ) Spacer(modifier = Modifier.height(FfSpacing.sm)) @@ -279,8 +283,7 @@ private fun NewPostScreenContent( contentWarningInteractions = contentWarningInteractions, onUploadImageClicked = newPostInteractions::onUploadImageClicked, onUploadMediaClicked = newPostInteractions::onUploadMediaClicked, - visibility = newPostUiState.visibility, - onVisibilitySelected = newPostInteractions::onVisibilitySelected, + onLanguageSelected = newPostInteractions::onLanguageSelected, ) } } @@ -306,9 +309,16 @@ private fun TopBar( onPostClicked: () -> Unit, onEditClicked: () -> Unit, sendButtonEnabled: Boolean, + visibility: StatusVisibility, + newPostInteractions: NewPostInteractions, ) { FfCloseableTopAppBar( actions = { + VisibilityDropDownButton( + visibility = visibility, + onVisibilitySelected = newPostInteractions::onVisibilitySelected, + ) + Spacer(modifier = Modifier.width(8.dp)) SubmitButton( modifier = Modifier.padding(end = 16.dp), onPostClicked = if (!statusUiState.editStatusId.isNullOrBlank()) @@ -339,7 +349,6 @@ private fun SubmitButton( } } -@OptIn(ExperimentalComposeUiApi::class) @Composable private fun MainBox( statusUiState: StatusUiState, diff --git a/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostViewModel.kt b/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostViewModel.kt index 306cbc2bb..e3cec4f3e 100644 --- a/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostViewModel.kt +++ b/feature/post/src/main/kotlin/social/firefly/feature/post/NewPostViewModel.kt @@ -2,9 +2,12 @@ package social.firefly.feature.post import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent @@ -16,6 +19,7 @@ import social.firefly.common.utils.FileType import social.firefly.common.utils.StringFactory import social.firefly.common.utils.edit import social.firefly.core.analytics.NewPostAnalytics +import social.firefly.core.datastore.UserPreferencesDatastoreManager import social.firefly.core.model.StatusVisibility import social.firefly.core.model.request.PollCreate import social.firefly.core.navigation.usecases.PopNavBackstack @@ -24,13 +28,15 @@ import social.firefly.core.repository.mastodon.AccountRepository import social.firefly.core.usecase.mastodon.account.GetLoggedInUserAccountId import social.firefly.core.usecase.mastodon.status.EditStatus import social.firefly.core.usecase.mastodon.status.PostStatus -import social.firefly.feature.post.bottombar.BottomBarState +import social.firefly.feature.post.bottombar.LocaleUiState import social.firefly.feature.post.media.MediaDelegate import social.firefly.feature.post.poll.PollDelegate import social.firefly.feature.post.poll.PollStyle import social.firefly.feature.post.status.StatusDelegate import timber.log.Timber +import java.util.Locale +@OptIn(ExperimentalCoroutinesApi::class) class NewPostViewModel( private val analytics: NewPostAnalytics, private val replyStatusId: String?, @@ -41,7 +47,8 @@ class NewPostViewModel( private val editStatus: EditStatus, private val popNavBackstack: PopNavBackstack, private val showSnackbar: ShowSnackbar, -) : ViewModel(), social.firefly.feature.post.NewPostInteractions, KoinComponent { + private val userPreferencesDatastoreManager: UserPreferencesDatastoreManager, +) : ViewModel(), NewPostInteractions, KoinComponent { val statusDelegate: StatusDelegate by inject { parametersOf( @@ -77,7 +84,7 @@ class NewPostViewModel( ) { imagesStates, pollUiState, statusUiState -> val images = imagesStates.filter { it.fileType == FileType.IMAGE } val videos = imagesStates.filter { it.fileType == FileType.VIDEO } - BottomBarState( + newPostUiState.value.bottomBarState.copy( imageButtonEnabled = videos.isEmpty() && images.size < MAX_IMAGES && pollUiState == null, videoButtonEnabled = images.isEmpty() && pollUiState == null, pollButtonEnabled = images.isEmpty() && videos.isEmpty(), @@ -136,6 +143,49 @@ class NewPostViewModel( } } } + + viewModelScope.launch { + val defaultLanguageCode = userPreferencesDatastoreManager + .activeUserDatastore + .flatMapLatest { it.defaultLanguage } + .first() + .ifBlank { + Locale.getDefault().language + } + + val availableLocales = Locale.getAvailableLocales() + .filter { locale -> + locale.country.isNullOrBlank() && + locale.script.isNullOrBlank() && + locale.variant.isNullOrBlank() + }.sortedBy { locale -> + locale.displayName + }.map { locale -> + LocaleUiState( + displayName = "${locale.displayName} (${locale.getDisplayName(locale)})", + code = locale.language + ) + } + + _newPostUiState.edit { + copy( + bottomBarState = newPostUiState.value.bottomBarState.copy( + language = defaultLanguageCode, + availableLocales = availableLocales, + ) + ) + } + } + } + + override fun onLanguageSelected(code: String) { + _newPostUiState.edit { + copy( + bottomBarState = newPostUiState.value.bottomBarState.copy( + language = code, + ) + ) + } } override fun onVisibilitySelected(statusVisibility: StatusVisibility) { @@ -169,6 +219,7 @@ class NewPostViewModel( }, contentWarningText = statusDelegate.uiState.value.contentWarningText, inReplyToId = replyStatusId, + languageCode = newPostUiState.value.bottomBarState.language, ) onStatusPosted() diff --git a/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/BottomBar.kt b/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/BottomBar.kt index 37f0fce8d..2eff0aa5e 100644 --- a/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/BottomBar.kt +++ b/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/BottomBar.kt @@ -30,9 +30,7 @@ import social.firefly.common.utils.toFile import social.firefly.core.designsystem.icon.FfIcons import social.firefly.core.designsystem.theme.FfSpacing import social.firefly.core.designsystem.theme.FfTheme -import social.firefly.core.model.StatusVisibility import social.firefly.core.ui.common.divider.FfDivider -import social.firefly.core.ui.common.dropdown.VisibilityDropDownButton import social.firefly.feature.post.R import social.firefly.feature.post.NewPostViewModel import social.firefly.feature.post.poll.PollInteractions @@ -47,8 +45,7 @@ internal fun BottomBar( onMediaInserted: (Uri, File, FileType) -> Unit, onUploadImageClicked: () -> Unit, onUploadMediaClicked: () -> Unit, - visibility: StatusVisibility, - onVisibilitySelected: (StatusVisibility) -> Unit, + onLanguageSelected: (code: String) -> Unit, ) { val context = LocalContext.current @@ -97,49 +94,18 @@ internal fun BottomBar( }, pollInteractions = pollInteractions, contentWarningInteractions = contentWarningInteractions, - visibility = visibility, - onVisibilitySelected = onVisibilitySelected, + onLanguageSelected = onLanguageSelected, ) } @Composable -internal fun BottomBar( +private fun BottomBar( bottomBarState: BottomBarState, onUploadImageClicked: () -> Unit, onUploadVideoClicked: () -> Unit, pollInteractions: PollInteractions, contentWarningInteractions: ContentWarningInteractions, - visibility: StatusVisibility, - onVisibilitySelected: (StatusVisibility) -> Unit, -) { - BottomBar( - onUploadImageClicked = onUploadImageClicked, - onUploadVideoClicked = onUploadVideoClicked, - imageButtonEnabled = bottomBarState.imageButtonEnabled, - videoButtonEnabled = bottomBarState.videoButtonEnabled, - pollButtonEnabled = bottomBarState.pollButtonEnabled, - contentWarningText = bottomBarState.contentWarningText, - characterCountText = bottomBarState.characterCountText, - pollInteractions = pollInteractions, - contentWarningInteractions = contentWarningInteractions, - visibility = visibility, - onVisibilitySelected = onVisibilitySelected, - ) -} - -@Composable -internal fun BottomBar( - onUploadImageClicked: () -> Unit, - onUploadVideoClicked: () -> Unit, - imageButtonEnabled: Boolean, - contentWarningText: String?, - pollInteractions: PollInteractions, - pollButtonEnabled: Boolean, - contentWarningInteractions: ContentWarningInteractions, - characterCountText: String, - videoButtonEnabled: Boolean, - visibility: StatusVisibility, - onVisibilitySelected: (StatusVisibility) -> Unit, + onLanguageSelected: (code: String) -> Unit, ) { Column { FfDivider( @@ -155,7 +121,7 @@ internal fun BottomBar( ) { IconButton( onClick = onUploadImageClicked, - enabled = imageButtonEnabled, + enabled = bottomBarState.imageButtonEnabled, ) { Icon( FfIcons.image(), @@ -165,7 +131,7 @@ internal fun BottomBar( IconButton( onClick = onUploadVideoClicked, - enabled = videoButtonEnabled, + enabled = bottomBarState.videoButtonEnabled, ) { Icon( FfIcons.monitorPlay(), @@ -175,22 +141,22 @@ internal fun BottomBar( AddPollButton( pollInteractions = pollInteractions, - pollButtonEnabled = pollButtonEnabled, + pollButtonEnabled = bottomBarState.pollButtonEnabled, ) ContentWarningButton( contentWarningInteractions = contentWarningInteractions, - contentWarningText = contentWarningText, + contentWarningText = bottomBarState.contentWarningText, ) Spacer(modifier = Modifier.weight(1f)) - VisibilityDropDownButton( - visibility = visibility, - onVisibilitySelected = onVisibilitySelected, + LanguageDropDown( + bottomBarState = bottomBarState, + onLanguageClicked = onLanguageSelected, ) - CharacterCountLabel(characterCountText = characterCountText) + CharacterCountLabel(characterCountText = bottomBarState.characterCountText) } } } diff --git a/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/BottomBarState.kt b/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/BottomBarState.kt index ca229fac7..500b59cbb 100644 --- a/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/BottomBarState.kt +++ b/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/BottomBarState.kt @@ -9,4 +9,11 @@ data class BottomBarState( val contentWarningText: String? = null, val characterCountText: String = "", val maxImages: Int = NewPostViewModel.MAX_IMAGES, + val language: String = "", + val availableLocales: List = emptyList(), +) + +data class LocaleUiState( + val displayName: String, + val code: String, ) \ No newline at end of file diff --git a/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/LanguageDropDown.kt b/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/LanguageDropDown.kt new file mode 100644 index 000000000..0a9854144 --- /dev/null +++ b/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/LanguageDropDown.kt @@ -0,0 +1,60 @@ +package social.firefly.feature.post.bottombar + +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import social.firefly.core.designsystem.icon.FfIcons +import social.firefly.core.designsystem.theme.FfTheme +import social.firefly.core.ui.common.dropdown.FfDropDownMenu +import social.firefly.core.ui.common.dropdown.FfDropdownMenuItem +import social.firefly.core.ui.common.text.MediumTextLabel + +@Composable +internal fun LanguageDropDown( + modifier: Modifier = Modifier, + bottomBarState: BottomBarState, + onLanguageClicked: (code: String) -> Unit, +) { + val expanded = remember { mutableStateOf(false) } + + FfDropDownMenu( + modifier = modifier, + expanded = expanded, + dropDownMenuContent = { + bottomBarState.availableLocales.forEach { locale -> + FfDropdownMenuItem( + text = { + Text( + text = locale.displayName + ) + }, + onClick = { + expanded.value = false + onLanguageClicked(locale.code) + } + ) + } + } + ) { + Icon( + painter = FfIcons.globe(), + contentDescription = null, + modifier = Modifier + .size(FfIcons.Sizes.small) + .align(Alignment.CenterVertically), + tint = FfTheme.colors.iconPrimary, + ) + Spacer(modifier = Modifier.padding(start = 8.dp)) + MediumTextLabel( + text = bottomBarState.language + ) + } +} \ No newline at end of file diff --git a/core/ui/common/src/main/kotlin/social/firefly/core/ui/common/dropdown/VisibilityDropDown.kt b/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/VisibilityDropDown.kt similarity index 95% rename from core/ui/common/src/main/kotlin/social/firefly/core/ui/common/dropdown/VisibilityDropDown.kt rename to feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/VisibilityDropDown.kt index 138493a7a..1db1300f5 100644 --- a/core/ui/common/src/main/kotlin/social/firefly/core/ui/common/dropdown/VisibilityDropDown.kt +++ b/feature/post/src/main/kotlin/social/firefly/feature/post/bottombar/VisibilityDropDown.kt @@ -1,4 +1,4 @@ -package social.firefly.core.ui.common.dropdown +package social.firefly.feature.post.bottombar import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope @@ -21,10 +21,12 @@ import social.firefly.core.designsystem.icon.FfIcons import social.firefly.core.designsystem.theme.FfTheme import social.firefly.core.designsystem.theme.ThemeOption import social.firefly.core.model.StatusVisibility -import social.firefly.core.ui.common.R +import social.firefly.core.ui.common.dropdown.FfDropDownMenu +import social.firefly.core.ui.common.dropdown.FfDropdownMenuItem +import social.firefly.feature.post.R @Composable -fun VisibilityDropDownButton( +internal fun VisibilityDropDownButton( modifier: Modifier = Modifier, visibility: StatusVisibility, onVisibilitySelected: (StatusVisibility) -> Unit, diff --git a/feature/post/src/main/kotlin/social/firefly/feature/post/poll/PollUi.kt b/feature/post/src/main/kotlin/social/firefly/feature/post/poll/PollUi.kt index ddd5175c0..2528890f1 100644 --- a/feature/post/src/main/kotlin/social/firefly/feature/post/poll/PollUi.kt +++ b/feature/post/src/main/kotlin/social/firefly/feature/post/poll/PollUi.kt @@ -155,21 +155,19 @@ internal fun PollBar( ) } - Spacer(modifier = Modifier.width(24.dp)) + Spacer(modifier = Modifier.width(4.dp)) PollDurationDropDown( pollUiState = pollUiState, pollInteractions = pollInteractions, ) - Spacer(modifier = Modifier.width(32.dp)) - PollStyleDropDown( pollUiState = pollUiState, pollInteractions = pollInteractions, ) - Spacer(modifier = Modifier.width(32.dp)) + Spacer(modifier = Modifier.width(8.dp)) HideUntilEndCheckbox(pollUiState = pollUiState, pollInteractions = pollInteractions) diff --git a/feature/post/src/main/res/values/strings.xml b/feature/post/src/main/res/values/strings.xml index 034044806..378b859d9 100644 --- a/feature/post/src/main/res/values/strings.xml +++ b/feature/post/src/main/res/values/strings.xml @@ -45,4 +45,9 @@ Your post was published Your edit was published + + Public + Unlisted + Private + Direct \ No newline at end of file