From c84dad4e6ffc2061c916ae79bff1d06d026c0a5c Mon Sep 17 00:00:00 2001
From: Konstantin Ivanov <54908981+konstantiniiv@users.noreply.github.com>
Date: Thu, 5 Dec 2024 12:28:57 +0100
Subject: [PATCH] DROID-2794 Date as an object | Slash menu, select date
(#1870)
---
.../features/editor/base/EditorTestSetup.kt | 2 +-
.../anytypeio/anytype/di/feature/EditorDI.kt | 19 --
.../features/editor/EditorDatePicker.kt | 49 ++-
.../editor/slash/holders/ActionMenuHolder.kt | 5 +
.../layout/item_slash_widget_object_type.xml | 63 ++--
.../anytype/core_utils/const/SlashConst.kt | 2 +
.../domain/page/CreateBlockLinkWithObject.kt | 2 -
.../page/CreateBlockLinkWithObjectTest.kt | 15 +-
.../presentation/editor/EditorViewModel.kt | 308 ++++++++++++------
.../editor/editor/slash/SlashExtensions.kt | 5 +-
.../editor/editor/slash/SlashItem.kt | 8 +
.../editor/model/EditorDatePicker.kt | 25 +-
.../editor/EditorSlashWidgetClicksTest.kt | 1 +
.../editor/EditorSlashWidgetFilterTest.kt | 2 +
14 files changed, 329 insertions(+), 177 deletions(-)
diff --git a/app/src/androidTest/java/com/anytypeio/anytype/features/editor/base/EditorTestSetup.kt b/app/src/androidTest/java/com/anytypeio/anytype/features/editor/base/EditorTestSetup.kt
index 3ec9cae52a..a7bdc61e6a 100644
--- a/app/src/androidTest/java/com/anytypeio/anytype/features/editor/base/EditorTestSetup.kt
+++ b/app/src/androidTest/java/com/anytypeio/anytype/features/editor/base/EditorTestSetup.kt
@@ -347,7 +347,7 @@ open class EditorTestSetup {
setupBookmark = SetupBookmark(repo)
updateAlignment = UpdateAlignment(repo)
uploadBlock = UploadBlock(repo)
- createBlockLinkWithObject = CreateBlockLinkWithObject(repo, getTemplates, dispatchers)
+ createBlockLinkWithObject = CreateBlockLinkWithObject(repo, dispatchers)
setRelationKey = SetRelationKey(repo)
turnIntoDocument = TurnIntoDocument(repo)
updateFields = UpdateFields(repo)
diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/EditorDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/EditorDI.kt
index 3a8d8f6d9a..a6c2a3244b 100644
--- a/app/src/main/java/com/anytypeio/anytype/di/feature/EditorDI.kt
+++ b/app/src/main/java/com/anytypeio/anytype/di/feature/EditorDI.kt
@@ -96,7 +96,6 @@ import com.anytypeio.anytype.domain.table.MoveTableColumn
import com.anytypeio.anytype.domain.table.MoveTableRow
import com.anytypeio.anytype.domain.table.SetTableRowHeader
import com.anytypeio.anytype.domain.templates.ApplyTemplate
-import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.domain.unsplash.UnsplashRepository
import com.anytypeio.anytype.domain.workspace.FileLimitsEventChannel
@@ -722,12 +721,10 @@ object EditorUseCaseModule {
@PerScreen
fun provideCreateObjectUseCase(
repo: BlockRepository,
- getTemplates: GetTemplates,
dispatchers: AppCoroutineDispatchers
): CreateBlockLinkWithObject =
CreateBlockLinkWithObject(
repo = repo,
- getTemplates = getTemplates,
dispatchers = dispatchers
)
@@ -736,8 +733,6 @@ object EditorUseCaseModule {
@PerScreen
fun provideCreateObjectAsMentionOrLink(
repo: BlockRepository,
- getDefaultObjectType: GetDefaultObjectType,
- getTemplates: GetTemplates,
dispatchers: AppCoroutineDispatchers,
spaceManager: SpaceManager
): CreateObjectAsMentionOrLink = CreateObjectAsMentionOrLink(
@@ -1012,20 +1007,6 @@ object EditorUseCaseModule {
repo: BlockRepository
): SetDocumentImageIcon = SetDocumentImageIcon(repo)
- @JvmStatic
- @Provides
- @PerScreen
- fun getTemplates(
- repo: BlockRepository,
- spaceManager: SpaceManager,
- dispatchers: AppCoroutineDispatchers
- ): GetTemplates =
- GetTemplates(
- repo = repo,
- spaceManager = spaceManager,
- dispatchers = dispatchers
- )
-
@JvmStatic
@Provides
@PerScreen
diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/EditorDatePicker.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/EditorDatePicker.kt
index 254eb57ce2..31b345fdb1 100644
--- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/EditorDatePicker.kt
+++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/EditorDatePicker.kt
@@ -13,9 +13,14 @@ import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.relations.DatePickerContent
import com.anytypeio.anytype.presentation.editor.model.EditorDatePickerState
+import com.anytypeio.anytype.presentation.editor.model.EditorDatePickerState.Visible
import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent
+import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent.OnDateSelected
+import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent.OnTodayClick
+import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent.OnTomorrowClick
import com.anytypeio.anytype.presentation.sets.DateValueView
+
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditorDatePicker(
@@ -24,7 +29,7 @@ fun EditorDatePicker(
onEvent: (OnEditorDatePickerEvent) -> Unit
) {
- if (uiState !is EditorDatePickerState.Visible) return
+ if (uiState !is Visible) return
val bottomSheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
@@ -47,16 +52,40 @@ fun EditorDatePicker(
DatePickerContent(
state = DateValueView(timeInMillis = null),
showHeader = false,
- onDateSelected = {
- onEvent(
- OnEditorDatePickerEvent.OnDateSelected(
- timeInMillis = it
- )
- )
- },
- onTodayClicked = { onEvent(OnEditorDatePickerEvent.OnTodayClick) },
- onTomorrowClicked = { onEvent(OnEditorDatePickerEvent.OnTomorrowClick) }
+ onDateSelected = { onEvent(getDateSelectedEvent(uiState, it)) },
+ onTodayClicked = { onEvent(getTodayEvent(uiState)) },
+ onTomorrowClicked = { onEvent(getTomorrowEvent(uiState)) }
)
},
)
+}
+
+private fun getTodayEvent(uiState: Visible): OnTodayClick {
+ return when (uiState) {
+ is Visible.Link -> OnTodayClick.Link(uiState.targetId)
+ is Visible.Mention -> OnTodayClick.Mention(uiState.targetId)
+ }
+}
+
+private fun getTomorrowEvent(uiState: Visible): OnTomorrowClick {
+ return when (uiState) {
+ is Visible.Link -> OnTomorrowClick.Link(uiState.targetId)
+ is Visible.Mention -> OnTomorrowClick.Mention(uiState.targetId)
+ }
+}
+
+private fun getDateSelectedEvent(
+ uiState: Visible,
+ timeInMillis: Long?
+): OnDateSelected {
+ return when (uiState) {
+ is Visible.Link -> OnDateSelected.Link(
+ timeInMillis = timeInMillis,
+ targetId = uiState.targetId
+ )
+ is Visible.Mention -> OnDateSelected.Mention(
+ timeInMillis = timeInMillis,
+ targetId = uiState.targetId
+ )
+ }
}
\ No newline at end of file
diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/slash/holders/ActionMenuHolder.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/slash/holders/ActionMenuHolder.kt
index e6e7aaa61d..1bd354144d 100644
--- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/slash/holders/ActionMenuHolder.kt
+++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/slash/holders/ActionMenuHolder.kt
@@ -50,6 +50,11 @@ class ActionMenuHolder(val binding: ItemSlashWidgetStyleBinding) : RecyclerView.
ivIcon.setImageResource(R.drawable.ic_slash_actions_link_to)
tvSubtitle.setText(R.string.slash_widget_actions_link_to_subtitle)
}
+ SlashItem.Actions.SelectDate -> {
+ tvTitle.setText(R.string.select_date)
+ ivIcon.setImageResource(R.drawable.ic_obj_date_24)
+ tvSubtitle.gone()
+ }
}
}
}
\ No newline at end of file
diff --git a/core-ui/src/main/res/layout/item_slash_widget_object_type.xml b/core-ui/src/main/res/layout/item_slash_widget_object_type.xml
index 9001056006..bbe4fc127e 100644
--- a/core-ui/src/main/res/layout/item_slash_widget_object_type.xml
+++ b/core-ui/src/main/res/layout/item_slash_widget_object_type.xml
@@ -2,10 +2,10 @@
+ android:layout_height="56dp"
+ android:paddingStart="@dimen/dp_20"
+ android:paddingEnd="@dimen/dp_20">
-
+ android:layout_marginEnd="@dimen/dp_16"
+ android:layout_gravity="center_vertical"
+ android:orientation="vertical">
-
+
+
+
+
+ android:background="@drawable/divider_relations" />
\ No newline at end of file
diff --git a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/const/SlashConst.kt b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/const/SlashConst.kt
index 4238d18738..c359c957cf 100644
--- a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/const/SlashConst.kt
+++ b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/const/SlashConst.kt
@@ -24,6 +24,8 @@ object SlashConst {
const val SLASH_ACTION_COPY = "Copy"
const val SLASH_ACTION_PASTE = "Paste"
const val SLASH_ACTION_LINK_TO = "Link to object"
+ const val SLASH_ACTION_SELECT_DATE = "Select date"
+ const val SLASH_ACTION_SELECT_DATE_ABBREVIATION = "date"
const val SLASH_ACTION_MOVE = "Move"
const val SLASH_ACTION_MOVE_TO = "Move to"
const val SLASH_ACTION_CLEAN_STYLE = "Clean style"
diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/page/CreateBlockLinkWithObject.kt b/domain/src/main/java/com/anytypeio/anytype/domain/page/CreateBlockLinkWithObject.kt
index a540b58678..e88a4538ca 100644
--- a/domain/src/main/java/com/anytypeio/anytype/domain/page/CreateBlockLinkWithObject.kt
+++ b/domain/src/main/java/com/anytypeio/anytype/domain/page/CreateBlockLinkWithObject.kt
@@ -6,7 +6,6 @@ import com.anytypeio.anytype.core_models.primitives.TypeKey
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
-import com.anytypeio.anytype.domain.templates.GetTemplates
/**
* UseCase for creating a new Object with block linked to this Object
@@ -14,7 +13,6 @@ import com.anytypeio.anytype.domain.templates.GetTemplates
class CreateBlockLinkWithObject(
private val repo: BlockRepository,
- private val getTemplates: GetTemplates,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor(dispatchers.io) {
diff --git a/domain/src/test/java/com/anytypeio/anytype/domain/page/CreateBlockLinkWithObjectTest.kt b/domain/src/test/java/com/anytypeio/anytype/domain/page/CreateBlockLinkWithObjectTest.kt
index bcadb147c5..4d302fa365 100644
--- a/domain/src/test/java/com/anytypeio/anytype/domain/page/CreateBlockLinkWithObjectTest.kt
+++ b/domain/src/test/java/com/anytypeio/anytype/domain/page/CreateBlockLinkWithObjectTest.kt
@@ -4,14 +4,11 @@ import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.CoroutineTestRule
import com.anytypeio.anytype.core_models.CreateBlockLinkWithObjectResult
import com.anytypeio.anytype.core_models.InternalFlags
-import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Position
-import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.primitives.TypeId
import com.anytypeio.anytype.core_models.primitives.TypeKey
import com.anytypeio.anytype.domain.block.repo.BlockRepository
-import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.util.dispatchers
import com.anytypeio.anytype.test_utils.MockDataFactory
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -39,14 +36,11 @@ class CreateBlockLinkWithObjectTest {
@Mock
lateinit var repo: BlockRepository
- @Mock
- lateinit var getTemplates: GetTemplates
-
lateinit var createBlockLinkWithObject: CreateBlockLinkWithObject
@Before
fun setup() {
- createBlockLinkWithObject = CreateBlockLinkWithObject(repo, getTemplates, dispatchers)
+ createBlockLinkWithObject = CreateBlockLinkWithObject(repo, dispatchers)
}
@Test
@@ -60,7 +54,6 @@ class CreateBlockLinkWithObjectTest {
val typeId = MockDataFactory.randomString()
val type = MockDataFactory.randomString()
stubCreateBlockLinkWithObject()
- givenGetTemplates(listOf())
//TESTING
val params = CreateBlockLinkWithObject.Params(
@@ -166,12 +159,6 @@ class CreateBlockLinkWithObjectTest {
verifyBlocking(repo, times(1)) { createBlockLinkWithObject(commands) }
}
- private fun givenGetTemplates(objects: List = listOf()) {
- getTemplates.stub {
- onBlocking { run(any()) } doReturn objects
- }
- }
-
private fun stubCreateBlockLinkWithObject() {
repo.stub {
onBlocking { createBlockLinkWithObject(any()) } doReturn CreateBlockLinkWithObjectResult(
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt
index 61168173d3..cee9c5deb8 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt
@@ -250,6 +250,12 @@ import com.anytypeio.anytype.presentation.navigation.SupportNavigation
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.objects.ObjectTypeView
import com.anytypeio.anytype.core_models.SupportedLayouts
+import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Event.SAM.*
+import com.anytypeio.anytype.presentation.editor.editor.Intent.Clipboard.*
+import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent.OnDatePickerDismiss
+import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent.OnDateSelected
+import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent.OnTodayClick
+import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent.OnTomorrowClick
import com.anytypeio.anytype.presentation.objects.getCreateObjectParams
import com.anytypeio.anytype.presentation.objects.getObjectTypeViewsForSBPage
import com.anytypeio.anytype.presentation.objects.getProperType
@@ -3273,6 +3279,16 @@ class EditorViewModel(
)
}
}
+ ObjectType.Layout.DATE -> {
+ navigate(
+ EventWrapper(
+ OpenDateObject(
+ objectId = target,
+ space = vmParams.space.id
+ )
+ )
+ )
+ }
else -> {
sendToast("Cannot open object with layout: ${wrapper.layout}")
}
@@ -5392,7 +5408,7 @@ class EditorViewModel(
}
SlashItem.Actions.Copy -> {
val block = blocks.first { it.id == targetId }
- val intent = Intent.Clipboard.Copy(
+ val intent = Copy(
context = context,
range = null,
blocks = listOf(block)
@@ -5404,7 +5420,7 @@ class EditorViewModel(
SlashItem.Actions.Paste -> {
viewModelScope.launch {
orchestrator.proxies.intents.send(
- Intent.Clipboard.Paste(
+ Paste(
context = context,
focus = targetId,
range = IntRange(slashStartIndex, slashStartIndex),
@@ -5431,7 +5447,7 @@ class EditorViewModel(
orchestrator.stores.views.update(updated)
renderCommand.send(Unit)
controlPanelInteractor.onEvent(
- ControlPanelMachine.Event.SAM.OnQuickStart(
+ OnQuickStart(
currentSelection().size
)
)
@@ -5449,6 +5465,11 @@ class EditorViewModel(
onHideKeyboardClicked()
proceedWithLinkToButtonClicked(block = targetId, position = slashStartIndex)
}
+ SlashItem.Actions.SelectDate -> {
+ mentionDatePicker.value = EditorDatePickerState.Visible.Link(
+ targetId = targetId
+ )
+ }
}
}
@@ -6156,100 +6177,12 @@ class EditorViewModel(
onCreateMentionInText(id = mention.id, name = mention.name, mentionTrigger = mentionTrigger)
}
if (mention is SelectDateItem) {
- mentionDatePicker.value = EditorDatePickerState.Visible
- }
- }
-
- private enum class EditorCalendarDateShortcuts {
- TODAY,
- TOMORROW
- }
-
- fun onEditorDatePickerEvent(event: OnEditorDatePickerEvent) {
- Timber.d("onEditorDatePickerEvent, event:[$event]")
-
- when (event) {
- is OnEditorDatePickerEvent.OnDateSelected -> {
- handleDatePickerDismiss()
- dispatch(Command.ShowKeyboard)
- handleDateSelected(event.timeInMillis)
+ val targetId = orchestrator.stores.focus.current().targetOrNull()
+ if (targetId == null) {
+ Timber.e("Error while getting targetId from focus")
+ return
}
- is OnEditorDatePickerEvent.OnDatePickerDismiss -> {
- dispatch(Command.ShowKeyboard)
- handleDatePickerDismiss()
- }
- OnEditorDatePickerEvent.OnTodayClick -> {
- handleDatePickerDismiss()
- dispatch(Command.ShowKeyboard)
- handlePredefinedDateClick(editorCalendarDateShortcuts = EditorCalendarDateShortcuts.TODAY)
- }
- OnEditorDatePickerEvent.OnTomorrowClick -> {
- handleDatePickerDismiss()
- dispatch(Command.ShowKeyboard)
- handlePredefinedDateClick(editorCalendarDateShortcuts = EditorCalendarDateShortcuts.TOMORROW)
- }
- }
- }
-
- private fun handleDateSelected(timeInMillis: Long?) {
- if (timeInMillis == null) {
- sendToast("Selected time is invalid.")
- Timber.w("OnDateSelected received null timeInMillis")
- return
- }
-
- val adjustedTimestamp = dateProvider.adjustFromStartOfDayInUserTimeZoneToUTC(timeInMillis)
- handleTimestamp(adjustedTimestamp)
- }
-
- private fun handlePredefinedDateClick(editorCalendarDateShortcuts: EditorCalendarDateShortcuts) {
- val timestamp = when (editorCalendarDateShortcuts) {
- EditorCalendarDateShortcuts.TODAY -> dateProvider.getTimestampForTodayAtStartOfDay()
- EditorCalendarDateShortcuts.TOMORROW -> dateProvider.getTimestampForTomorrowAtStartOfDay()
- }
- handleTimestamp(timestamp)
- }
-
- private fun handleTimestamp(timestamp: Long) {
- proceedWithGettingDateByTimestamp(timestamp) { dateObject ->
- Timber.d("handleTimestamp, dateObject:[$dateObject]")
- val id = dateObject?.getSingleValue(Relations.ID)
- val name = dateObject?.getSingleValue(Relations.NAME)
-
- if (id != null && name != null) {
- onCreateMentionInText(
- id = id,
- name = name,
- mentionTrigger = mentionFilter.value
- )
- } else {
- sendToast("Error while creating mention, date object is null")
- Timber.e("Date object missing ID or name.")
- }
- }
- }
-
- private fun handleDatePickerDismiss() {
- mentionDatePicker.value = EditorDatePickerState.Hidden
- }
-
- private fun proceedWithGettingDateByTimestamp(timestamp: Long, action: (Struct?) -> Unit) {
- val params = GetDateObjectByTimestamp.Params(
- space = vmParams.space,
- timestamp = timestamp
- )
- Timber.d("Start ObjectDateByTimestamp with params: [$params]")
- viewModelScope.launch {
- getDateObjectByTimestamp.async(params).fold(
- onSuccess = { dateObject ->
- Timber.d("ObjectDateByTimestamp Success, dateObject: [$dateObject]")
- action(dateObject)
- },
- onFailure = { e ->
- Timber.e(e, "ObjectDateByTimestamp Error")
- sendToast("Failed to retrieve date object.")
- }
- )
+ mentionDatePicker.value = EditorDatePickerState.Visible.Mention(targetId = targetId)
}
}
@@ -7678,6 +7611,189 @@ class EditorViewModel(
}
//endregion
+ //region CALENDAR
+ private enum class EditorCalendarDateShortcuts {
+ TODAY,
+ TOMORROW
+ }
+
+ private enum class EditorCalendarActionType {
+ MENTION,
+ LINK
+ }
+
+ fun onEditorDatePickerEvent(event: OnEditorDatePickerEvent) {
+ Timber.d("onEditorDatePickerEvent, event:[$event]")
+
+ when (event) {
+ is OnDateSelected.Mention -> {
+ handleDatePickerDismiss()
+ dispatch(Command.ShowKeyboard)
+ handleDateSelected(
+ timeInMillis = event.timeInMillis,
+ actionType = EditorCalendarActionType.MENTION,
+ targetId = event.targetId
+ )
+ }
+ is OnDateSelected.Link -> {
+ cutSlashFilter(targetId = event.targetId)
+ controlPanelInteractor.onEvent(ControlPanelMachine.Event.Slash.OnStop)
+ handleDatePickerDismiss()
+ handleDateSelected(
+ timeInMillis = event.timeInMillis,
+ actionType = EditorCalendarActionType.LINK,
+ targetId = event.targetId
+ )
+ proceedWithClearingFocus()
+ }
+ is OnTodayClick.Mention -> {
+ handleDatePickerDismiss()
+ dispatch(Command.ShowKeyboard)
+ handlePredefinedDateClick(
+ predefinedDate = EditorCalendarDateShortcuts.TODAY,
+ actionType = EditorCalendarActionType.MENTION,
+ targetId = event.targetId
+ )
+ }
+ is OnTodayClick.Link -> {
+ handleDatePickerDismiss()
+ cutSlashFilter(targetId = event.targetId)
+ controlPanelInteractor.onEvent(ControlPanelMachine.Event.Slash.OnStop)
+ handlePredefinedDateClick(
+ predefinedDate = EditorCalendarDateShortcuts.TODAY,
+ actionType = EditorCalendarActionType.LINK,
+ targetId = event.targetId
+ )
+ proceedWithClearingFocus()
+ }
+ is OnTomorrowClick.Mention -> {
+ handleDatePickerDismiss()
+ dispatch(Command.ShowKeyboard)
+ handlePredefinedDateClick(
+ predefinedDate = EditorCalendarDateShortcuts.TOMORROW,
+ actionType = EditorCalendarActionType.MENTION,
+ targetId = event.targetId
+ )
+ }
+ is OnTomorrowClick.Link -> {
+ handleDatePickerDismiss()
+ cutSlashFilter(targetId = event.targetId)
+ controlPanelInteractor.onEvent(ControlPanelMachine.Event.Slash.OnStop)
+ handlePredefinedDateClick(
+ predefinedDate = EditorCalendarDateShortcuts.TOMORROW,
+ actionType = EditorCalendarActionType.LINK,
+ targetId = event.targetId
+ )
+ proceedWithClearingFocus()
+ }
+ is OnDatePickerDismiss -> handleDatePickerDismiss()
+ }
+ }
+
+ private fun handleDateSelected(
+ timeInMillis: Long?,
+ actionType: EditorCalendarActionType,
+ targetId: Id
+ ) {
+ if (timeInMillis == null) {
+ sendToast("Selected time is invalid.")
+ Timber.w("OnDateSelected received null timeInMillis")
+ return
+ }
+
+ val adjustedTimestamp = dateProvider.adjustFromStartOfDayInUserTimeZoneToUTC(timeInMillis)
+ handleTimestamp(adjustedTimestamp, actionType, targetId)
+ }
+
+ private fun handlePredefinedDateClick(
+ predefinedDate: EditorCalendarDateShortcuts,
+ actionType: EditorCalendarActionType,
+ targetId: Id
+ ) {
+ val timestamp = when (predefinedDate) {
+ EditorCalendarDateShortcuts.TODAY -> dateProvider.getTimestampForTodayAtStartOfDay()
+ EditorCalendarDateShortcuts.TOMORROW -> dateProvider.getTimestampForTomorrowAtStartOfDay()
+ }
+ handleTimestamp(timestamp, actionType, targetId)
+ }
+
+ private fun handleTimestamp(timestamp: Long, actionType: EditorCalendarActionType, targetId: Id) {
+ proceedWithGettingDateByTimestamp(timestamp) { dateObject ->
+ Timber.d("handleTimestamp, dateObject:[$dateObject]")
+ val id = dateObject?.getSingleValue(Relations.ID)
+ val name = dateObject?.getSingleValue(Relations.NAME)
+
+ if (id != null && name != null) {
+ when (actionType) {
+ EditorCalendarActionType.MENTION -> onCreateMentionInText(
+ id = id,
+ name = name,
+ mentionTrigger = mentionFilter.value
+ )
+ EditorCalendarActionType.LINK -> onCreateDateLink(
+ linkId = id,
+ targetId = targetId
+ )
+ }
+ } else {
+ sendToast("Error while creating ${actionType.name.lowercase()}, date object is null")
+ Timber.e("Date object missing ID or name.")
+ }
+ }
+ }
+
+ private fun handleDatePickerDismiss() {
+ mentionDatePicker.value = EditorDatePickerState.Hidden
+ }
+
+ private fun proceedWithGettingDateByTimestamp(timestamp: Long, action: (Struct?) -> Unit) {
+ val params = GetDateObjectByTimestamp.Params(
+ space = vmParams.space,
+ timestamp = timestamp
+ )
+ Timber.d("Start ObjectDateByTimestamp with params: [$params]")
+ viewModelScope.launch {
+ getDateObjectByTimestamp.async(params).fold(
+ onSuccess = { dateObject ->
+ Timber.d("ObjectDateByTimestamp Success, dateObject: [$dateObject]")
+ action(dateObject)
+ },
+ onFailure = { e ->
+ Timber.e(e, "ObjectDateByTimestamp Error")
+ sendToast("Failed to retrieve date object.")
+ }
+ )
+ }
+ }
+
+ private fun onCreateDateLink(linkId: String, targetId: Id) {
+ Timber.d("Link created with id: $linkId, targetId: $targetId")
+ val targetBlock = blocks.firstOrNull { it.id == targetId }
+ if (targetBlock != null) {
+ val targetContent = targetBlock.content
+ val position = if (targetContent is Content.Text && targetContent.text.isEmpty()) {
+ Position.REPLACE
+ } else {
+ Position.BOTTOM
+ }
+ viewModelScope.launch{
+ orchestrator.proxies.intents.send(
+ Intent.CRUD.Create(
+ context = context,
+ target = targetId,
+ position = position,
+ prototype = Prototype.Link(target = linkId),
+ onSuccess = {}
+ )
+ )
+ }
+ } else {
+ Timber.e("Can't find target block for link")
+ sendToast("Error while creating link")
+ }
+ }
+ //endregion
+
data class Params(
val ctx: Id,
val space: SpaceId
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/slash/SlashExtensions.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/slash/SlashExtensions.kt
index 4bc12665c7..ea7cb45b55 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/slash/SlashExtensions.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/slash/SlashExtensions.kt
@@ -132,7 +132,8 @@ object SlashExtensions {
fun getSlashWidgetObjectTypeItems(objectTypes: List): List =
listOf(
SlashItem.Subheader.ObjectTypeWithBlack,
- SlashItem.Actions.LinkTo
+ SlashItem.Actions.LinkTo,
+ SlashItem.Actions.SelectDate
) + objectTypes.toSlashItemView()
fun getSlashWidgetRelationItems(relations: List): List =
@@ -202,7 +203,7 @@ object SlashExtensions {
)
val filteredObjects = filterObjectTypes(
filter = filter,
- items = listOf(SlashItem.Actions.LinkTo) + objectTypes
+ items = listOf(SlashItem.Actions.LinkTo, SlashItem.Actions.SelectDate) + objectTypes
)
val filteredRelations = filterRelations(
filter = filter,
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/slash/SlashItem.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/slash/SlashItem.kt
index daea13f0ce..ffd610706c 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/slash/SlashItem.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/slash/SlashItem.kt
@@ -3,6 +3,7 @@ package com.anytypeio.anytype.presentation.editor.editor.slash
import com.anytypeio.anytype.core_utils.const.SlashConst
import com.anytypeio.anytype.core_utils.const.SlashConst.SLASH_OTHER_TOC_ABBREVIATION
import com.anytypeio.anytype.core_models.ThemeColor
+import com.anytypeio.anytype.core_utils.const.SlashConst.SLASH_ACTION_SELECT_DATE_ABBREVIATION
import com.anytypeio.anytype.presentation.objects.ObjectTypeView
sealed class SlashWidgetState {
@@ -395,6 +396,13 @@ sealed class SlashItem {
override fun getSearchName(): String = SlashConst.SLASH_ACTION_LINK_TO
override fun getAbbreviation(): List? = null
}
+
+ object SelectDate : Actions() {
+ override fun getSearchName(): String = SlashConst.SLASH_ACTION_SELECT_DATE
+ override fun getAbbreviation(): List? = listOf(
+ SLASH_ACTION_SELECT_DATE_ABBREVIATION
+ )
+ }
}
//endregion
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/model/EditorDatePicker.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/model/EditorDatePicker.kt
index f8ecacd152..ff6b9f5c1e 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/model/EditorDatePicker.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/model/EditorDatePicker.kt
@@ -1,13 +1,30 @@
package com.anytypeio.anytype.presentation.editor.model
+import com.anytypeio.anytype.core_models.Id
+
sealed class EditorDatePickerState {
data object Hidden : EditorDatePickerState()
- data object Visible : EditorDatePickerState()
+ sealed class Visible(val targetId: Id) : EditorDatePickerState() {
+ class Mention(targetId: Id) : Visible(targetId)
+ class Link(targetId: Id) : Visible(targetId)
+ }
}
sealed class OnEditorDatePickerEvent {
+ sealed class OnDateSelected : OnEditorDatePickerEvent() {
+ data class Mention(val timeInMillis: Long?, val targetId: Id) : OnDateSelected()
+ data class Link(val timeInMillis: Long?, val targetId: Id) : OnDateSelected()
+ }
+
object OnDatePickerDismiss : OnEditorDatePickerEvent()
- data class OnDateSelected(val timeInMillis: Long?) : OnEditorDatePickerEvent()
- object OnTodayClick : OnEditorDatePickerEvent()
- object OnTomorrowClick : OnEditorDatePickerEvent()
+
+ sealed class OnTodayClick : OnEditorDatePickerEvent() {
+ data class Mention(val targetId: Id) : OnTodayClick()
+ data class Link(val targetId: Id) : OnTodayClick()
+ }
+
+ sealed class OnTomorrowClick : OnEditorDatePickerEvent() {
+ data class Mention(val targetId: Id) : OnTomorrowClick()
+ data class Link(val targetId: Id) : OnTomorrowClick()
+ }
}
\ No newline at end of file
diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorSlashWidgetClicksTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorSlashWidgetClicksTest.kt
index c1f3ed0de8..5a3b0b6943 100644
--- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorSlashWidgetClicksTest.kt
+++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorSlashWidgetClicksTest.kt
@@ -360,6 +360,7 @@ class EditorSlashWidgetClicksTest: EditorPresentationTestSetup() {
val expectedObjectItems = listOf(
SlashItem.Subheader.ObjectTypeWithBlack,
SlashItem.Actions.LinkTo,
+ SlashItem.Actions.SelectDate,
SlashItem.ObjectType(
objectTypeView = ObjectTypeView(
id = type1.id,
diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorSlashWidgetFilterTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorSlashWidgetFilterTest.kt
index 7a9cfc98cd..d209e1c6e5 100644
--- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorSlashWidgetFilterTest.kt
+++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorSlashWidgetFilterTest.kt
@@ -478,6 +478,7 @@ class EditorSlashWidgetFilterTest : EditorPresentationTestSetup() {
val expectedItems = listOf(
SlashItem.Subheader.Actions,
SlashItem.Actions.LinkTo,
+ SlashItem.Actions.SelectDate,
SlashItem.ObjectType(
objectTypeView = ObjectTypeView(
id = type1.id,
@@ -1561,6 +1562,7 @@ class EditorSlashWidgetFilterTest : EditorPresentationTestSetup() {
val expectedItems = listOf(
SlashItem.Subheader.Actions,
SlashItem.Actions.LinkTo,
+ SlashItem.Actions.SelectDate,
SlashItem.ObjectType(
objectTypeView = ObjectTypeView(
id = type1.id,