1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-3098 Chats | Fix | Object attachements for space-level chats (#1840)

This commit is contained in:
Evgenii Kozlov 2024-11-23 17:07:13 +01:00 committed by GitHub
parent 69a6819814
commit cdc99cd206
Signed by: github
GPG key ID: B5690EEEBB952194
5 changed files with 298 additions and 248 deletions

View file

@ -35,7 +35,6 @@ import com.anytypeio.anytype.ext.daggerViewModel
import com.anytypeio.anytype.feature_discussions.presentation.DiscussionViewModel
import com.anytypeio.anytype.feature_discussions.presentation.DiscussionViewModelFactory
import com.anytypeio.anytype.feature_discussions.ui.DiscussionScreenWrapper
import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
import com.anytypeio.anytype.presentation.search.GlobalSearchViewModel
import com.anytypeio.anytype.ui.editor.EditorFragment
@ -73,7 +72,7 @@ class DiscussionFragment : BaseComposeFragment() {
DiscussionScreenWrapper(
vm = vm,
onAttachClicked = {
onAttachObjectClicked = {
showBottomSheet = true
},
onBackButtonClicked = {

View file

@ -13,8 +13,16 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
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.platform.ComposeView
import androidx.compose.ui.platform.LocalFocusManager
@ -33,6 +41,7 @@ import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.primitives.Space
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_ui.extensions.throttledClick
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.core_utils.ext.argOrNull
@ -49,6 +58,7 @@ import com.anytypeio.anytype.other.DefaultDeepLinkResolver
import com.anytypeio.anytype.presentation.home.Command
import com.anytypeio.anytype.presentation.home.HomeScreenViewModel
import com.anytypeio.anytype.presentation.home.HomeScreenViewModel.Navigation
import com.anytypeio.anytype.presentation.search.GlobalSearchViewModel
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
import com.anytypeio.anytype.presentation.widgets.DropDownMenuAction
import com.anytypeio.anytype.presentation.widgets.WidgetView
@ -63,6 +73,7 @@ import com.anytypeio.anytype.ui.objects.types.pickers.ObjectTypeSelectionListene
import com.anytypeio.anytype.ui.objects.types.pickers.WidgetObjectTypeListener
import com.anytypeio.anytype.ui.objects.types.pickers.WidgetSourceTypeListener
import com.anytypeio.anytype.ui.payments.MembershipFragment
import com.anytypeio.anytype.ui.search.GlobalSearchScreen
import com.anytypeio.anytype.ui.settings.space.SpaceSettingsFragment
import com.anytypeio.anytype.ui.settings.typography
import com.anytypeio.anytype.ui.widgets.SelectWidgetSourceFragment
@ -93,6 +104,7 @@ class HomeScreenFragment : BaseComposeFragment(),
private val vm by viewModels<HomeScreenViewModel> { factory }
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -108,10 +120,15 @@ class HomeScreenFragment : BaseComposeFragment(),
)
) {
if (featureToggles.isNewSpaceHomeEnabled) {
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = false)
var showGlobalSearchBottomSheet by remember { mutableStateOf(false) }
val view = (vm.views.collectAsStateWithLifecycle().value.find {
it is WidgetView.SpaceWidget.View
} as? WidgetView.SpaceWidget.View)
val focus = LocalFocusManager.current
val component = componentManager().spaceLevelChatComponent
val spaceLevelChatViewModel = daggerViewModel {
component.get(
@ -123,6 +140,7 @@ class HomeScreenFragment : BaseComposeFragment(),
}
val pagerState = rememberPagerState { 2 }
val coroutineScope = rememberCoroutineScope()
Box(
Modifier.fillMaxSize()
) {
@ -150,100 +168,57 @@ class HomeScreenFragment : BaseComposeFragment(),
) { page ->
if (page == 0) {
focus.clearFocus(force = true)
HomeScreen(
modifier = Modifier,
widgets = vm.views.collectAsState().value,
mode = vm.mode.collectAsState().value,
onExpand = { path -> vm.onExpand(path) },
onCreateWidget = vm::onCreateWidgetClicked,
onEditWidgets = vm::onEditWidgets,
onExitEditMode = vm::onExitEditMode,
onWidgetMenuAction = { widget: Id, action: DropDownMenuAction ->
vm.onDropDownMenuAction(widget, action)
},
onWidgetObjectClicked = vm::onWidgetObjectClicked,
onWidgetSourceClicked = vm::onWidgetSourceClicked,
onChangeWidgetView = vm::onChangeCurrentWidgetView,
onToggleExpandedWidgetState = vm::onToggleCollapsedWidgetState,
onSearchClicked = vm::onSearchIconClicked,
onCreateNewObjectClicked = throttledClick(
onClick = { vm.onCreateNewObjectClicked() }
),
onCreateNewObjectLongClicked = throttledClick(
onClick = { vm.onCreateNewObjectLongClicked() }
),
onBackClicked = throttledClick(
onClick = vm::onBackClicked
),
onSpaceWidgetClicked = throttledClick(
onClick = vm::onSpaceSettingsClicked
),
onBundledWidgetClicked = vm::onBundledWidgetClicked,
onMove = vm::onMove,
onObjectCheckboxClicked = vm::onObjectCheckboxClicked,
onSpaceShareIconClicked = vm::onSpaceShareIconClicked,
onSeeAllObjectsClicked = vm::onSeeAllObjectsClicked,
onCreateObjectInsideWidget = vm::onCreateObjectInsideWidget,
onCreateDataViewObject = vm::onCreateDataViewObject,
onBackLongClicked = vm::onBackLongClicked
)
PageWithWidgets()
} else {
DiscussionScreenWrapper(
isSpaceLevelChat = true,
vm = spaceLevelChatViewModel,
onAttachClicked = {
// TODO
onAttachObjectClicked = {
showGlobalSearchBottomSheet = true
},
onBackButtonClicked = {
findNavController().popBackStack()
},
onMarkupLinkClicked = {
proceedWithAction(
SystemAction.OpenUrl(it)
)
proceedWithAction(SystemAction.OpenUrl(it))
}
)
}
}
}
if (showGlobalSearchBottomSheet) {
ModalBottomSheet(
onDismissRequest = {
showGlobalSearchBottomSheet = false
},
sheetState = sheetState,
containerColor = colorResource(id = R.color.background_secondary),
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
dragHandle = null
) {
val component = componentManager().globalSearchComponent
val searchViewModel = daggerViewModel {
component
.get(GlobalSearchViewModel.VmParams(space = SpaceId(space)))
.getViewModel()
}
GlobalSearchScreen(
modifier = Modifier.padding(top = 12.dp),
state = searchViewModel.state
.collectAsStateWithLifecycle()
.value
,
onQueryChanged = searchViewModel::onQueryChanged,
onObjectClicked = {
spaceLevelChatViewModel.onAttachObject(it)
showGlobalSearchBottomSheet = false
},
focusOnStart = false
)
}
}
} else {
HomeScreen(
modifier = Modifier,
widgets = vm.views.collectAsState().value,
mode = vm.mode.collectAsState().value,
onExpand = { path -> vm.onExpand(path) },
onCreateWidget = vm::onCreateWidgetClicked,
onEditWidgets = vm::onEditWidgets,
onExitEditMode = vm::onExitEditMode,
onWidgetMenuAction = { widget: Id, action: DropDownMenuAction ->
vm.onDropDownMenuAction(widget, action)
},
onWidgetObjectClicked = vm::onWidgetObjectClicked,
onWidgetSourceClicked = vm::onWidgetSourceClicked,
onChangeWidgetView = vm::onChangeCurrentWidgetView,
onToggleExpandedWidgetState = vm::onToggleCollapsedWidgetState,
onSearchClicked = vm::onSearchIconClicked,
onCreateNewObjectClicked = throttledClick(
onClick = { vm.onCreateNewObjectClicked() }
),
onCreateNewObjectLongClicked = throttledClick(
onClick = { vm.onCreateNewObjectLongClicked() }
),
onBackClicked = throttledClick(
onClick = vm::onBackClicked
),
onSpaceWidgetClicked = throttledClick(
onClick = vm::onSpaceSettingsClicked
),
onBundledWidgetClicked = vm::onBundledWidgetClicked,
onMove = vm::onMove,
onObjectCheckboxClicked = vm::onObjectCheckboxClicked,
onSpaceShareIconClicked = vm::onSpaceShareIconClicked,
onSeeAllObjectsClicked = vm::onSeeAllObjectsClicked,
onCreateObjectInsideWidget = vm::onCreateObjectInsideWidget,
onCreateDataViewObject = vm::onCreateDataViewObject,
onBackLongClicked = vm::onBackLongClicked
)
PageWithWidgets()
}
}
@ -253,6 +228,47 @@ class HomeScreenFragment : BaseComposeFragment(),
}
}
@Composable
fun PageWithWidgets() {
HomeScreen(
modifier = Modifier,
widgets = vm.views.collectAsState().value,
mode = vm.mode.collectAsState().value,
onExpand = { path -> vm.onExpand(path) },
onCreateWidget = vm::onCreateWidgetClicked,
onEditWidgets = vm::onEditWidgets,
onExitEditMode = vm::onExitEditMode,
onWidgetMenuAction = { widget: Id, action: DropDownMenuAction ->
vm.onDropDownMenuAction(widget, action)
},
onWidgetObjectClicked = vm::onWidgetObjectClicked,
onWidgetSourceClicked = vm::onWidgetSourceClicked,
onChangeWidgetView = vm::onChangeCurrentWidgetView,
onToggleExpandedWidgetState = vm::onToggleCollapsedWidgetState,
onSearchClicked = vm::onSearchIconClicked,
onCreateNewObjectClicked = throttledClick(
onClick = { vm.onCreateNewObjectClicked() }
),
onCreateNewObjectLongClicked = throttledClick(
onClick = { vm.onCreateNewObjectLongClicked() }
),
onBackClicked = throttledClick(
onClick = vm::onBackClicked
),
onSpaceWidgetClicked = throttledClick(
onClick = vm::onSpaceSettingsClicked
),
onBundledWidgetClicked = vm::onBundledWidgetClicked,
onMove = vm::onMove,
onObjectCheckboxClicked = vm::onObjectCheckboxClicked,
onSpaceShareIconClicked = vm::onSpaceShareIconClicked,
onSeeAllObjectsClicked = vm::onSeeAllObjectsClicked,
onCreateObjectInsideWidget = vm::onCreateObjectInsideWidget,
onCreateDataViewObject = vm::onCreateDataViewObject,
onBackLongClicked = vm::onBackLongClicked
)
}
override fun onStart() {
Timber.d("onStart")
super.onStart()

View file

@ -105,8 +105,8 @@ fun GlobalSearchScreen(
state: GlobalSearchViewModel.ViewState,
onQueryChanged: (String) -> Unit,
onObjectClicked: (GlobalSearchItemView) -> Unit,
onShowRelatedClicked: (GlobalSearchItemView) -> Unit,
onClearRelatedClicked: () -> Unit,
onShowRelatedClicked: (GlobalSearchItemView) -> Unit = {},
onClearRelatedClicked: () -> Unit = {},
focusOnStart: Boolean = true
) {

View file

@ -102,7 +102,11 @@ fun DiscussionScreenPreview() {
onExitEditMessageMode = {},
isSpaceLevelChat = true,
onBackButtonClicked = {},
onMarkupLinkClicked = {}
onMarkupLinkClicked = {},
onAttachFileClicked = {},
onUploadAttachmentClicked = {},
onAttachMediaClicked = {},
onAttachObjectClicked = {}
)
}

View file

@ -128,9 +128,9 @@ fun DiscussionScreenWrapper(
isSpaceLevelChat: Boolean = false,
vm: DiscussionViewModel,
// TODO move to view model
onAttachClicked: () -> Unit,
onAttachObjectClicked: () -> Unit,
onBackButtonClicked: () -> Unit,
onMarkupLinkClicked: (String) -> Unit
onMarkupLinkClicked: (String) -> Unit,
) {
NavHost(
navController = rememberNavController(),
@ -161,7 +161,7 @@ fun DiscussionScreenWrapper(
attachments = vm.attachments.collectAsState().value,
onMessageSent = vm::onMessageSent,
onTitleChanged = vm::onTitleChanged,
onAttachClicked = onAttachClicked,
onAttachClicked = onAttachObjectClicked,
onClearAttachmentClicked = vm::onClearAttachmentClicked,
lazyListState = lazyListState,
onReacted = vm::onReacted,
@ -176,7 +176,17 @@ fun DiscussionScreenWrapper(
isInEditMessageMode = vm.chatBoxMode.collectAsState().value is ChatBoxMode.EditMessage,
onExitEditMessageMode = vm::onExitEditMessageMode,
onBackButtonClicked = onBackButtonClicked,
onMarkupLinkClicked = onMarkupLinkClicked
onMarkupLinkClicked = onMarkupLinkClicked,
onAttachObjectClicked = onAttachObjectClicked,
onAttachMediaClicked = {
},
onAttachFileClicked = {
},
onUploadAttachmentClicked = {
}
)
LaunchedEffect(Unit) {
vm.commands.collect { command ->
@ -217,7 +227,11 @@ fun DiscussionScreen(
onEditMessage: (DiscussionView.Message) -> Unit,
onAttachmentClicked: (Chat.Message.Attachment) -> Unit,
onExitEditMessageMode: () -> Unit,
onMarkupLinkClicked: (String) -> Unit
onMarkupLinkClicked: (String) -> Unit,
onAttachObjectClicked: () -> Unit,
onAttachMediaClicked: () -> Unit,
onAttachFileClicked: () -> Unit,
onUploadAttachmentClicked: () -> Unit
) {
var textState by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue(""))
@ -298,38 +312,6 @@ fun DiscussionScreen(
enabled = jumpToBottomButtonEnabled
)
}
attachments.forEach {
Box {
Attachment(
modifier = Modifier.padding(
top = 12.dp,
start = 16.dp,
end = 16.dp
),
title = it.title,
type = it.type,
icon = it.icon,
onAttachmentClicked = {
// TODO
}
)
Image(
painter = painterResource(id = R.drawable.ic_clear_18),
contentDescription = "Close icon",
modifier = Modifier
.align(
Alignment.TopEnd
)
.padding(
top = 6.dp,
end = 10.dp
)
.noRippleClickable {
onClearAttachmentClicked()
}
)
}
}
if (isInEditMessageMode) {
EditMessageToolbar(
onExitClicked = {
@ -361,7 +343,12 @@ fun DiscussionScreen(
clearText = {
textState = TextFieldValue()
},
onBackButtonClicked = onBackButtonClicked
onBackButtonClicked = onBackButtonClicked,
onAttachFileClicked = onAttachFileClicked,
onAttachMediaClicked = onAttachMediaClicked,
onUploadAttachmentClicked = onUploadAttachmentClicked,
onAttachObjectClicked = onAttachObjectClicked,
onClearAttachmentClicked = onClearAttachmentClicked
)
}
}
@ -504,7 +491,12 @@ private fun ChatBox(
isTitleFocused: Boolean,
attachments: List<GlobalSearchItemView>,
clearText: () -> Unit,
updateValue: (TextFieldValue) -> Unit
updateValue: (TextFieldValue) -> Unit,
onAttachObjectClicked: () -> Unit,
onAttachMediaClicked: () -> Unit,
onAttachFileClicked: () -> Unit,
onUploadAttachmentClicked: () -> Unit,
onClearAttachmentClicked: () -> Unit
) {
var showDropdownMenu by remember { mutableStateOf(false) }
@ -513,7 +505,7 @@ private fun ChatBox(
val focus = LocalFocusManager.current
Row(
Column(
modifier = modifier
.fillMaxWidth()
.defaultMinSize(minHeight = 56.dp)
@ -527,138 +519,177 @@ private fun ChatBox(
shape = RoundedCornerShape(16.dp)
)
) {
Box(
modifier = Modifier
.padding(horizontal = 4.dp, vertical = 8.dp)
.clip(CircleShape)
.align(Alignment.Bottom)
.clickable {
scope.launch {
focus.clearFocus(force = true)
onBackButtonClicked()
attachments.forEach {
Box {
Attachment(
modifier = Modifier.padding(
top = 12.dp,
start = 16.dp,
end = 16.dp
),
title = it.title,
type = it.type,
icon = it.icon,
onAttachmentClicked = {
// TODO
}
}
) {
Image(
painter = painterResource(id = R.drawable.ic_nav_panel_back),
contentDescription = "Back button",
modifier = Modifier
.align(Alignment.Center)
.padding(horizontal = 4.dp, vertical = 4.dp)
)
}
ChatBoxUserInput(
textState = textState,
onMessageSent = {
onMessageSent(it)
clearText()
resetScroll()
},
onTextChanged = { value ->
updateValue(value)
},
modifier = Modifier
.weight(1f)
.align(Alignment.Bottom)
.focusRequester(chatBoxFocusRequester)
)
Box(
modifier = Modifier
.padding(horizontal = 4.dp, vertical = 8.dp)
.clip(CircleShape)
.align(Alignment.Bottom)
.clickable {
scope.launch {
focus.clearFocus(force = true)
showDropdownMenu = true
}
}
) {
Image(
painter = painterResource(id = R.drawable.ic_nav_panel_plus),
contentDescription = "Plus button",
modifier = Modifier
.align(Alignment.Center)
.padding(horizontal = 4.dp, vertical = 4.dp)
)
MaterialTheme(
shapes = MaterialTheme.shapes.copy(
medium = RoundedCornerShape(
12.dp
)
),
colors = MaterialTheme.colors.copy(
surface = colorResource(id = R.color.background_secondary)
)
) {
DropdownMenu(
offset = DpOffset(8.dp, 40.dp),
expanded = showDropdownMenu,
onDismissRequest = {
showDropdownMenu = false
},
Image(
painter = painterResource(id = R.drawable.ic_clear_18),
contentDescription = "Close icon",
modifier = Modifier
.align(Alignment.BottomEnd)
.defaultMinSize(
minWidth = 252.dp
.align(
Alignment.TopEnd
)
.padding(
top = 6.dp,
end = 10.dp
)
.noRippleClickable {
onClearAttachmentClicked()
}
)
}
}
Row(
) {
Box(
modifier = Modifier
.padding(horizontal = 4.dp, vertical = 8.dp)
.clip(CircleShape)
.align(Alignment.Bottom)
.clickable {
scope.launch {
focus.clearFocus(force = true)
onBackButtonClicked()
}
}
) {
Image(
painter = painterResource(id = R.drawable.ic_nav_panel_back),
contentDescription = "Back button",
modifier = Modifier
.align(Alignment.Center)
.padding(horizontal = 4.dp, vertical = 4.dp)
)
}
ChatBoxUserInput(
textState = textState,
onMessageSent = {
onMessageSent(it)
clearText()
resetScroll()
},
onTextChanged = { value ->
updateValue(value)
},
modifier = Modifier
.weight(1f)
.align(Alignment.Bottom)
.focusRequester(chatBoxFocusRequester)
)
Box(
modifier = Modifier
.padding(horizontal = 4.dp, vertical = 8.dp)
.clip(CircleShape)
.align(Alignment.Bottom)
.clickable {
scope.launch {
focus.clearFocus(force = true)
showDropdownMenu = true
}
}
) {
Image(
painter = painterResource(id = R.drawable.ic_nav_panel_plus),
contentDescription = "Plus button",
modifier = Modifier
.align(Alignment.Center)
.padding(horizontal = 4.dp, vertical = 4.dp)
)
MaterialTheme(
shapes = MaterialTheme.shapes.copy(
medium = RoundedCornerShape(
12.dp
)
),
colors = MaterialTheme.colors.copy(
surface = colorResource(id = R.color.background_secondary)
)
) {
DropdownMenuItem(
text = {
Text(
text = stringResource(R.string.chat_attachment_object),
color = colorResource(id = R.color.text_primary)
)
},
onClick = {
DropdownMenu(
offset = DpOffset(8.dp, 40.dp),
expanded = showDropdownMenu,
onDismissRequest = {
showDropdownMenu = false
}
)
Divider(
paddingStart = 0.dp,
paddingEnd = 0.dp
)
DropdownMenuItem(
text = {
Text(
text = stringResource(R.string.chat_attachment_media),
color = colorResource(id = R.color.text_primary)
)
},
onClick = {
showDropdownMenu = false
}
)
Divider(
paddingStart = 0.dp,
paddingEnd = 0.dp
)
DropdownMenuItem(
text = {
Text(
text = stringResource(R.string.chat_attachment_file),
color = colorResource(id = R.color.text_primary)
modifier = Modifier
.align(Alignment.BottomEnd)
.defaultMinSize(
minWidth = 252.dp
)
},
onClick = {
showDropdownMenu = false
}
)
Divider(
paddingStart = 0.dp,
paddingEnd = 0.dp
)
DropdownMenuItem(
text = {
Text(
text = stringResource(R.string.chat_attachment_upload),
color = colorResource(id = R.color.text_primary)
)
},
onClick = {
showDropdownMenu = false
}
)
) {
DropdownMenuItem(
text = {
Text(
text = stringResource(R.string.chat_attachment_object),
color = colorResource(id = R.color.text_primary)
)
},
onClick = {
showDropdownMenu = false
onAttachObjectClicked()
}
)
Divider(
paddingStart = 0.dp,
paddingEnd = 0.dp
)
DropdownMenuItem(
text = {
Text(
text = stringResource(R.string.chat_attachment_media),
color = colorResource(id = R.color.text_primary)
)
},
onClick = {
showDropdownMenu = false
onAttachMediaClicked()
}
)
Divider(
paddingStart = 0.dp,
paddingEnd = 0.dp
)
DropdownMenuItem(
text = {
Text(
text = stringResource(R.string.chat_attachment_file),
color = colorResource(id = R.color.text_primary)
)
},
onClick = {
showDropdownMenu = false
onAttachFileClicked()
}
)
Divider(
paddingStart = 0.dp,
paddingEnd = 0.dp
)
DropdownMenuItem(
text = {
Text(
text = stringResource(R.string.chat_attachment_upload),
color = colorResource(id = R.color.text_primary)
)
},
onClick = {
showDropdownMenu = false
onUploadAttachmentClicked()
}
)
}
}
}
}