mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-2794 Date as an object | Slash menu, select date (#1870)
This commit is contained in:
parent
9060dedb86
commit
c84dad4e6f
14 changed files with 329 additions and 177 deletions
|
@ -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<String>(Relations.ID)
|
||||
val name = dateObject?.getSingleValue<String>(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<String>(Relations.ID)
|
||||
val name = dateObject?.getSingleValue<String>(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
|
||||
|
|
|
@ -132,7 +132,8 @@ object SlashExtensions {
|
|||
fun getSlashWidgetObjectTypeItems(objectTypes: List<ObjectTypeView>): List<SlashItem> =
|
||||
listOf(
|
||||
SlashItem.Subheader.ObjectTypeWithBlack,
|
||||
SlashItem.Actions.LinkTo
|
||||
SlashItem.Actions.LinkTo,
|
||||
SlashItem.Actions.SelectDate
|
||||
) + objectTypes.toSlashItemView()
|
||||
|
||||
fun getSlashWidgetRelationItems(relations: List<SlashRelationView>): List<SlashRelationView> =
|
||||
|
@ -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,
|
||||
|
|
|
@ -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<String>? = null
|
||||
}
|
||||
|
||||
object SelectDate : Actions() {
|
||||
override fun getSearchName(): String = SlashConst.SLASH_ACTION_SELECT_DATE
|
||||
override fun getAbbreviation(): List<String>? = listOf(
|
||||
SLASH_ACTION_SELECT_DATE_ABBREVIATION
|
||||
)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue