Merge pull request #2070 from vitorpamplona/claude/add-post-action-buttons-KsoZk
Add floating action buttons for media and poll creation
This commit is contained in:
@@ -105,6 +105,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.newUser.ImportFollowListSel
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NotificationScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NotificationScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.publicMessages.NewPublicMessageScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.publicMessages.NewPublicMessageScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures.PicturesScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures.PicturesScreen
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.polls.PollPostScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.polls.PollsScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.polls.PollsScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.privacy.PrivacyOptionsScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.privacy.PrivacyOptionsScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ProfileScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ProfileScreen
|
||||||
@@ -374,6 +375,26 @@ fun BuildNavigation(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composableFromBottomArgs<Route.NewPoll> {
|
||||||
|
PollPostScreen(
|
||||||
|
isZapPoll = false,
|
||||||
|
message = it.message,
|
||||||
|
draftId = it.draft,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
composableFromBottomArgs<Route.NewZapPoll> {
|
||||||
|
PollPostScreen(
|
||||||
|
isZapPoll = true,
|
||||||
|
message = it.message,
|
||||||
|
draftId = it.draft,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
composableFromBottomArgs<Route.VoiceReply> {
|
composableFromBottomArgs<Route.VoiceReply> {
|
||||||
VoiceReplyScreen(
|
VoiceReplyScreen(
|
||||||
replyToNoteId = it.replyToNoteId,
|
replyToNoteId = it.replyToNoteId,
|
||||||
|
|||||||
@@ -362,6 +362,18 @@ sealed class Route {
|
|||||||
val draft: String? = null,
|
val draft: String? = null,
|
||||||
) : Route()
|
) : Route()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class NewPoll(
|
||||||
|
val message: String? = null,
|
||||||
|
val draft: String? = null,
|
||||||
|
) : Route()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class NewZapPoll(
|
||||||
|
val message: String? = null,
|
||||||
|
val draft: String? = null,
|
||||||
|
) : Route()
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class VoiceReply(
|
data class VoiceReply(
|
||||||
val replyToNoteId: String,
|
val replyToNoteId: String,
|
||||||
|
|||||||
+1
-1
@@ -186,7 +186,7 @@ fun ShortNotePostScreen(
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun NewPostScreenInner(
|
internal fun NewPostScreenInner(
|
||||||
postViewModel: ShortNotePostViewModel,
|
postViewModel: ShortNotePostViewModel,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: Nav,
|
nav: Nav,
|
||||||
|
|||||||
+3
@@ -76,6 +76,9 @@ fun LongsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
floatingButton = {
|
||||||
|
NewLongVideoButton(accountViewModel, nav, longsFeedContentState::sendToTop)
|
||||||
|
},
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Column(Modifier.padding(paddingValues)) {
|
Column(Modifier.padding(paddingValues)) {
|
||||||
|
|||||||
+195
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.longs
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.AddPhotoAlternate
|
||||||
|
import androidx.compose.material.icons.filled.Videocam
|
||||||
|
import androidx.compose.material.icons.outlined.Close
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewMediaModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewMediaView
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelect
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.uploads.TakeVideo
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
|
import com.vitorpamplona.amethyst.ui.painterRes
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size26Modifier
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NewLongVideoButton(
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: INav,
|
||||||
|
navScrollToTop: () -> Unit,
|
||||||
|
) {
|
||||||
|
var isOpen by remember { mutableStateOf(false) }
|
||||||
|
var wantsToRecordVideo by remember { mutableStateOf(false) }
|
||||||
|
var wantsToPostFromGallery by remember { mutableStateOf(false) }
|
||||||
|
var pickedURIs by remember { mutableStateOf<ImmutableList<SelectedMedia>>(persistentListOf()) }
|
||||||
|
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val postViewModel: NewMediaModel = viewModel()
|
||||||
|
postViewModel.onceUploaded {
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
delay(500)
|
||||||
|
withContext(Dispatchers.Main) { navScrollToTop() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantsToRecordVideo) {
|
||||||
|
TakeVideo { uri ->
|
||||||
|
wantsToRecordVideo = false
|
||||||
|
pickedURIs = uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantsToPostFromGallery) {
|
||||||
|
GallerySelect(
|
||||||
|
onImageUri = { uri ->
|
||||||
|
wantsToPostFromGallery = false
|
||||||
|
pickedURIs = uri
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pickedURIs.isNotEmpty()) {
|
||||||
|
NewMediaView(
|
||||||
|
uris = pickedURIs,
|
||||||
|
onClose = { pickedURIs = persistentListOf() },
|
||||||
|
postViewModel = postViewModel,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isOpen,
|
||||||
|
enter = slideInVertically(initialOffsetY = { it / 2 }) + fadeIn(),
|
||||||
|
exit = slideOutVertically(targetOffsetY = { it / 2 }) + fadeOut(),
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
wantsToRecordVideo = true
|
||||||
|
isOpen = false
|
||||||
|
},
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Videocam,
|
||||||
|
contentDescription = stringRes(id = R.string.record_a_video),
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
wantsToPostFromGallery = true
|
||||||
|
isOpen = false
|
||||||
|
},
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.AddPhotoAlternate,
|
||||||
|
contentDescription = stringRes(id = R.string.upload_image),
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = { isOpen = !isOpen },
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isOpen,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Close,
|
||||||
|
contentDescription = stringRes(id = R.string.new_long_video),
|
||||||
|
modifier = Size26Modifier,
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = !isOpen,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterRes(R.drawable.ic_compose, 5),
|
||||||
|
contentDescription = stringRes(id = R.string.new_long_video),
|
||||||
|
modifier = Size26Modifier,
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+195
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.AddPhotoAlternate
|
||||||
|
import androidx.compose.material.icons.filled.CameraAlt
|
||||||
|
import androidx.compose.material.icons.outlined.Close
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewMediaModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewMediaView
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelect
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.uploads.TakePicture
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
|
import com.vitorpamplona.amethyst.ui.painterRes
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size26Modifier
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NewPictureButton(
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: INav,
|
||||||
|
navScrollToTop: () -> Unit,
|
||||||
|
) {
|
||||||
|
var isOpen by remember { mutableStateOf(false) }
|
||||||
|
var wantsToPostFromCamera by remember { mutableStateOf(false) }
|
||||||
|
var wantsToPostFromGallery by remember { mutableStateOf(false) }
|
||||||
|
var pickedURIs by remember { mutableStateOf<ImmutableList<SelectedMedia>>(persistentListOf()) }
|
||||||
|
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val postViewModel: NewMediaModel = viewModel()
|
||||||
|
postViewModel.onceUploaded {
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
delay(500)
|
||||||
|
withContext(Dispatchers.Main) { navScrollToTop() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantsToPostFromCamera) {
|
||||||
|
TakePicture { uri ->
|
||||||
|
wantsToPostFromCamera = false
|
||||||
|
pickedURIs = uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantsToPostFromGallery) {
|
||||||
|
GallerySelect(
|
||||||
|
onImageUri = { uri ->
|
||||||
|
wantsToPostFromGallery = false
|
||||||
|
pickedURIs = uri
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pickedURIs.isNotEmpty()) {
|
||||||
|
NewMediaView(
|
||||||
|
uris = pickedURIs,
|
||||||
|
onClose = { pickedURIs = persistentListOf() },
|
||||||
|
postViewModel = postViewModel,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isOpen,
|
||||||
|
enter = slideInVertically(initialOffsetY = { it / 2 }) + fadeIn(),
|
||||||
|
exit = slideOutVertically(targetOffsetY = { it / 2 }) + fadeOut(),
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
wantsToPostFromCamera = true
|
||||||
|
isOpen = false
|
||||||
|
},
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.CameraAlt,
|
||||||
|
contentDescription = stringRes(id = R.string.take_a_picture),
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
wantsToPostFromGallery = true
|
||||||
|
isOpen = false
|
||||||
|
},
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.AddPhotoAlternate,
|
||||||
|
contentDescription = stringRes(id = R.string.upload_image),
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = { isOpen = !isOpen },
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isOpen,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Close,
|
||||||
|
contentDescription = stringRes(id = R.string.new_picture),
|
||||||
|
modifier = Size26Modifier,
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = !isOpen,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterRes(R.drawable.ic_compose, 5),
|
||||||
|
contentDescription = stringRes(id = R.string.new_picture),
|
||||||
|
modifier = Size26Modifier,
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -76,6 +76,9 @@ fun PicturesScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
floatingButton = {
|
||||||
|
NewPictureButton(accountViewModel, nav, picturesFeedContentState::sendToTop)
|
||||||
|
},
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Column(Modifier.padding(paddingValues)) {
|
Column(Modifier.padding(paddingValues)) {
|
||||||
|
|||||||
+138
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.polls
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Close
|
||||||
|
import androidx.compose.material.icons.outlined.Poll
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||||
|
import com.vitorpamplona.amethyst.ui.painterRes
|
||||||
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size26Modifier
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NewPollButton(nav: INav) {
|
||||||
|
var isOpen by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
Column {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isOpen,
|
||||||
|
enter = slideInVertically(initialOffsetY = { it / 2 }) + fadeIn(),
|
||||||
|
exit = slideOutVertically(targetOffsetY = { it / 2 }) + fadeOut(),
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
nav.nav(Route.NewPoll())
|
||||||
|
isOpen = false
|
||||||
|
},
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Poll,
|
||||||
|
contentDescription = stringRes(id = R.string.new_regular_poll),
|
||||||
|
modifier = Size26Modifier,
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
nav.nav(Route.NewZapPoll())
|
||||||
|
isOpen = false
|
||||||
|
},
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterRes(R.drawable.ic_poll, 1),
|
||||||
|
contentDescription = stringRes(id = R.string.new_zap_poll),
|
||||||
|
modifier = Size26Modifier,
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = { isOpen = !isOpen },
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isOpen,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Close,
|
||||||
|
contentDescription = stringRes(id = R.string.new_poll),
|
||||||
|
modifier = Size26Modifier,
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = !isOpen,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterRes(R.drawable.ic_compose, 4),
|
||||||
|
contentDescription = stringRes(id = R.string.new_poll),
|
||||||
|
modifier = Size26Modifier,
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.polls
|
||||||
|
|
||||||
|
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.navs.Nav
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.NewPostScreenInner
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.ShortNotePostViewModel
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import kotlinx.coroutines.FlowPreview
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class, FlowPreview::class)
|
||||||
|
@Composable
|
||||||
|
fun PollPostScreen(
|
||||||
|
isZapPoll: Boolean,
|
||||||
|
message: String? = null,
|
||||||
|
draftId: HexKey? = null,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: Nav,
|
||||||
|
) {
|
||||||
|
val postViewModel: ShortNotePostViewModel = viewModel()
|
||||||
|
postViewModel.init(accountViewModel)
|
||||||
|
|
||||||
|
LaunchedEffect(postViewModel, accountViewModel) {
|
||||||
|
val draft = draftId?.let { accountViewModel.getNoteIfExists(it) }
|
||||||
|
postViewModel.load(null, null, null, null, draft)
|
||||||
|
message?.ifBlank { null }?.let {
|
||||||
|
postViewModel.message.setTextAndPlaceCursorAtEnd(it)
|
||||||
|
postViewModel.onMessageChanged()
|
||||||
|
}
|
||||||
|
if (isZapPoll) {
|
||||||
|
postViewModel.wantsZapPoll = true
|
||||||
|
} else {
|
||||||
|
postViewModel.wantsPoll = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewPostScreenInner(postViewModel, accountViewModel, nav)
|
||||||
|
}
|
||||||
+3
@@ -76,6 +76,9 @@ fun PollsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
floatingButton = {
|
||||||
|
NewPollButton(nav)
|
||||||
|
},
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Column(Modifier.padding(paddingValues)) {
|
Column(Modifier.padding(paddingValues)) {
|
||||||
|
|||||||
+195
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.shorts
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.AddPhotoAlternate
|
||||||
|
import androidx.compose.material.icons.filled.Videocam
|
||||||
|
import androidx.compose.material.icons.outlined.Close
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewMediaModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewMediaView
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.uploads.GallerySelect
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.uploads.TakeVideo
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
|
import com.vitorpamplona.amethyst.ui.painterRes
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size26Modifier
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NewShortVideoButton(
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: INav,
|
||||||
|
navScrollToTop: () -> Unit,
|
||||||
|
) {
|
||||||
|
var isOpen by remember { mutableStateOf(false) }
|
||||||
|
var wantsToRecordVideo by remember { mutableStateOf(false) }
|
||||||
|
var wantsToPostFromGallery by remember { mutableStateOf(false) }
|
||||||
|
var pickedURIs by remember { mutableStateOf<ImmutableList<SelectedMedia>>(persistentListOf()) }
|
||||||
|
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val postViewModel: NewMediaModel = viewModel()
|
||||||
|
postViewModel.onceUploaded {
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
delay(500)
|
||||||
|
withContext(Dispatchers.Main) { navScrollToTop() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantsToRecordVideo) {
|
||||||
|
TakeVideo { uri ->
|
||||||
|
wantsToRecordVideo = false
|
||||||
|
pickedURIs = uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantsToPostFromGallery) {
|
||||||
|
GallerySelect(
|
||||||
|
onImageUri = { uri ->
|
||||||
|
wantsToPostFromGallery = false
|
||||||
|
pickedURIs = uri
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pickedURIs.isNotEmpty()) {
|
||||||
|
NewMediaView(
|
||||||
|
uris = pickedURIs,
|
||||||
|
onClose = { pickedURIs = persistentListOf() },
|
||||||
|
postViewModel = postViewModel,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isOpen,
|
||||||
|
enter = slideInVertically(initialOffsetY = { it / 2 }) + fadeIn(),
|
||||||
|
exit = slideOutVertically(targetOffsetY = { it / 2 }) + fadeOut(),
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
wantsToRecordVideo = true
|
||||||
|
isOpen = false
|
||||||
|
},
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Videocam,
|
||||||
|
contentDescription = stringRes(id = R.string.record_a_video),
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
wantsToPostFromGallery = true
|
||||||
|
isOpen = false
|
||||||
|
},
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.AddPhotoAlternate,
|
||||||
|
contentDescription = stringRes(id = R.string.upload_image),
|
||||||
|
modifier = Modifier.size(26.dp),
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = { isOpen = !isOpen },
|
||||||
|
modifier = Size55Modifier,
|
||||||
|
shape = CircleShape,
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isOpen,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Close,
|
||||||
|
contentDescription = stringRes(id = R.string.new_short_video),
|
||||||
|
modifier = Size26Modifier,
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = !isOpen,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterRes(R.drawable.ic_compose, 5),
|
||||||
|
contentDescription = stringRes(id = R.string.new_short_video),
|
||||||
|
modifier = Size26Modifier,
|
||||||
|
tint = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -76,6 +76,9 @@ fun ShortsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
floatingButton = {
|
||||||
|
NewShortVideoButton(accountViewModel, nav, shortsFeedContentState::sendToTop)
|
||||||
|
},
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Column(Modifier.padding(paddingValues)) {
|
Column(Modifier.padding(paddingValues)) {
|
||||||
|
|||||||
@@ -1343,6 +1343,12 @@
|
|||||||
<string name="new_product">New Product</string>
|
<string name="new_product">New Product</string>
|
||||||
<string name="new_exclusive_geo_note">New Geo-Exclusive Post</string>
|
<string name="new_exclusive_geo_note">New Geo-Exclusive Post</string>
|
||||||
<string name="new_long_form_post">New Article</string>
|
<string name="new_long_form_post">New Article</string>
|
||||||
|
<string name="new_poll">New Poll</string>
|
||||||
|
<string name="new_zap_poll">New Zap Poll</string>
|
||||||
|
<string name="new_regular_poll">New Regular Poll</string>
|
||||||
|
<string name="new_picture">New Picture</string>
|
||||||
|
<string name="new_short_video">New Short Video</string>
|
||||||
|
<string name="new_long_video">New Long Video</string>
|
||||||
|
|
||||||
<string name="article_title">Title</string>
|
<string name="article_title">Title</string>
|
||||||
<string name="article_summary">Summary (optional)</string>
|
<string name="article_summary">Summary (optional)</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user