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

DROID-3108 Date as an Object | Add the “Open selected date” option to the Date Picker. (#1899)

This commit is contained in:
Konstantin Ivanov 2024-12-10 15:47:44 +01:00 committed by konstantiniiv
parent a03f37129b
commit 98952de76b
13 changed files with 182 additions and 26 deletions

View file

@ -52,6 +52,7 @@ import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.core_models.TimeInMillis
import com.anytypeio.anytype.core_models.Url
import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState
import com.anytypeio.anytype.core_models.primitives.SpaceId
@ -2120,6 +2121,10 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
)
}
override fun onOpenDateObject(timeInMillis: TimeInMillis) {
vm.onOpenDateObjectByTimeInMillis(timeInMillis)
}
override fun onMoveTo(
target: Id,
space: Id,

View file

@ -13,6 +13,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_models.TimeInMillis
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_ui.features.relations.DocumentRelationAdapter
import com.anytypeio.anytype.core_ui.reactive.textChanges
@ -283,6 +284,10 @@ open class ObjectRelationListFragment : BaseBottomSheetFragment<FragmentRelation
)
}
override fun onOpenDateObject(timeInMillis: TimeInMillis) {
vm.onOpenDateObjectByTimeInMillis(timeInMillis)
}
override fun injectDependencies() {
val param = DefaultComponentParam(
ctx = ctx,

View file

@ -13,6 +13,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_models.TimeInMillis
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_ui.relations.DatePickerContent
import com.anytypeio.anytype.core_ui.views.Title3
@ -53,11 +54,14 @@ open class RelationDateValueFragment : BaseBottomSheetComposeFragment() {
typography = MaterialTheme.typography.copy(bodyLarge = Title3)
) {
DatePickerContent(
showHeader = true,
showOpenSelectDate = true,
state = vm.views.collectAsStateWithLifecycle().value,
onDateSelected = vm::onDateSelected,
onClear = vm::onClearClicked,
onTodayClicked = vm::onTodayClicked,
onTomorrowClicked = vm::onTomorrowClicked
onTomorrowClicked = vm::onTomorrowClicked,
openSelectedDate = vm::openSelectedDateClicked
)
}
}
@ -84,6 +88,14 @@ open class RelationDateValueFragment : BaseBottomSheetComposeFragment() {
DatePickerFragment.new(command.timeInSeconds)
.showChildFragment()
}
is DateValueCommand.OpenDateObject -> {
withParent<DateValueEditReceiver> {
onOpenDateObject(
timeInMillis = command.timeInMillis
)
}
dismiss()
}
}
}
@ -169,5 +181,9 @@ open class RelationDateValueFragment : BaseBottomSheetComposeFragment() {
objectId: Id,
relationKey: Key
)
fun onOpenDateObject(
timeInMillis: TimeInMillis
)
}
}

View file

@ -44,6 +44,7 @@ import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.TimeInMillis
import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_ui.extensions.setEmojiOrNull
@ -1410,6 +1411,10 @@ open class ObjectSetFragment :
)
}
override fun onOpenDateObject(timeInMillis: TimeInMillis) {
vm.onOpenDateObjectByTimeInMillis(timeInMillis)
}
override fun onProceedWithSelectSource(id: Id) {
vm.onObjectSetQueryPicked(query = id)
}

View file

@ -13,7 +13,7 @@ import androidx.compose.ui.tooling.preview.Preview
showSystemUi = true
)
@Preview(
backgroundColor = 0x000000,
backgroundColor = 0xFF121212,
showBackground = true,
uiMode = UI_MODE_NIGHT_YES,
name = "Dark Mode",

View file

@ -51,7 +51,6 @@ fun EditorDatePicker(
content = {
DatePickerContent(
state = DateValueView(timeInMillis = null),
showHeader = false,
onDateSelected = { onEvent(getDateSelectedEvent(uiState, it)) },
onTodayClicked = { onEvent(getTodayEvent(uiState)) },
onTomorrowClicked = { onEvent(getTomorrowEvent(uiState)) }

View file

@ -1,18 +1,23 @@
package com.anytypeio.anytype.core_ui.relations
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.DatePicker
import androidx.compose.material3.DatePickerDefaults
import androidx.compose.material3.DatePickerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SelectableDates
import androidx.compose.material3.Text
@ -25,15 +30,18 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_models.DATE_PICKER_YEAR_RANGE
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.foundation.Dragger
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.core_ui.views.UXBody
import com.anytypeio.anytype.presentation.sets.DateValueView
@ -42,11 +50,13 @@ import com.anytypeio.anytype.presentation.sets.DateValueView
@Composable
fun DatePickerContent(
state: DateValueView,
showHeader: Boolean = true,
showHeader: Boolean = false,
showOpenSelectDate: Boolean = false,
onDateSelected: (Long?) -> Unit,
onClear: () -> Unit = {},
onTodayClicked: () -> Unit,
onTomorrowClicked: () -> Unit
onTomorrowClicked: () -> Unit,
openSelectedDate: (Long) -> Unit = {}
) {
val datePickerState = rememberDatePickerState(
@ -123,30 +133,82 @@ fun DatePickerContent(
colors = datePickerColors,
)
if (state.isEditable) {
Divider(paddingStart = 0.dp, paddingEnd = 0.dp)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, top = 11.dp, bottom = 11.dp)
.noRippleClickable { onTodayClicked() },
color = colorResource(id = R.color.text_primary),
text = stringResource(id = R.string.today),
style = UXBody
)
Divider(paddingStart = 0.dp, paddingEnd = 0.dp)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, top = 11.dp, bottom = 11.dp)
.noRippleClickable { onTomorrowClicked() },
color = colorResource(id = R.color.text_primary),
text = stringResource(id = R.string.tomorrow),
style = UXBody
CalendarShortcuts(
showOpenSelectDate = showOpenSelectDate,
datePickerState = datePickerState,
onTodayClicked = onTodayClicked,
onTomorrowClicked = onTomorrowClicked,
openSelectedDate = openSelectedDate
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ColumnScope.CalendarShortcuts(
showOpenSelectDate: Boolean,
datePickerState: DatePickerState,
onTodayClicked: () -> Unit,
onTomorrowClicked: () -> Unit,
openSelectedDate: (Long) -> Unit
) {
Divider(paddingStart = 16.dp, paddingEnd = 0.dp)
Text(
modifier = Modifier
.height(44.dp)
.fillMaxWidth()
.padding(start = 16.dp, top = 11.dp, bottom = 11.dp)
.noRippleClickable { onTodayClicked() },
color = colorResource(id = R.color.text_primary),
text = stringResource(id = R.string.today),
style = UXBody
)
Divider(paddingStart = 16.dp, paddingEnd = 0.dp)
Text(
modifier = Modifier
.height(44.dp)
.fillMaxWidth()
.padding(start = 16.dp, top = 11.dp, bottom = 11.dp)
.noRippleClickable { onTomorrowClicked() },
color = colorResource(id = R.color.text_primary),
text = stringResource(id = R.string.tomorrow),
style = UXBody
)
if (datePickerState.selectedDateMillis != null && showOpenSelectDate) {
Divider(paddingStart = 16.dp, paddingEnd = 0.dp)
Row(
modifier = Modifier
.height(44.dp)
.noRippleThrottledClickable {
val timeInMillis = datePickerState.selectedDateMillis
if (timeInMillis != null) {
openSelectedDate(timeInMillis)
}
},
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(start = 16.dp),
color = colorResource(id = R.color.text_primary),
text = stringResource(id = R.string.open_selected_date),
style = UXBody
)
Image(
modifier = Modifier
.wrapContentSize()
.padding(end = 16.dp),
painter = painterResource(id = R.drawable.ic_arrow_forward),
contentDescription = "Open selected date"
)
}
Divider(paddingStart = 16.dp, paddingEnd = 0.dp)
}
}
@Composable
private fun CalendarDragger(modifier: Modifier = Modifier) {
Box(
@ -198,4 +260,19 @@ private fun Header(state: DateValueView, onClear: () -> Unit) {
maxLines = 1
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@DefaultPreviews
@Composable
private fun CalendarShortcutsPreview() {
Column {
CalendarShortcuts(
showOpenSelectDate = true,
onTodayClicked = {},
onTomorrowClicked = {},
openSelectedDate = {},
datePickerState = rememberDatePickerState()
)
}
}

View file

@ -35,10 +35,9 @@ fun CalendarScreen(
is UiCalendarState.Calendar -> {
DatePickerContent(
state = DateValueView(timeInMillis = uiState.timeInMillis),
showHeader = false,
onDateSelected = { onDateEvent(DateEvent.Calendar.OnCalendarDateSelected(it)) },
onTodayClicked = { onDateEvent(DateEvent.Calendar.OnTodayClick) },
onTomorrowClicked = { onDateEvent(DateEvent.Calendar.OnTomorrowClick) }
onTomorrowClicked = { onDateEvent(DateEvent.Calendar.OnTomorrowClick) },
)
}

View file

@ -749,6 +749,7 @@
<string name="today">Today</string>
<string name="yesterday">Yesterday</string>
<string name="tomorrow">Tomorrow</string>
<string name="open_selected_date">Open selected date</string>
<string name="no_date">No date</string>
<string name="exact_day">Exact day</string>
<string name="reorder">Reorder</string>

View file

@ -7752,6 +7752,21 @@ class EditorViewModel(
}
}
fun onOpenDateObjectByTimeInMillis(timeInMillis: TimeInMillis) {
Timber.d("onOpenDateObjectByTimeInMillis, timeInMillis:[$timeInMillis]")
viewModelScope.launch {
fieldParser.getDateObjectByTimeInSeconds(
timeInSeconds = timeInMillis / 1000,
spaceId = vmParams.space,
actionSuccess = { obj -> navigateToDateObject(obj.id) },
actionFailure = {
sendToast("Error while opening date object")
Timber.e(it, "Error while opening date object")
}
)
}
}
private fun handleDateSelected(
timeInMillis: TimeInMillis?,
actionType: EditorCalendarActionType,

View file

@ -14,6 +14,7 @@ import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.RelationFormat
import com.anytypeio.anytype.core_models.RelationLink
import com.anytypeio.anytype.core_models.TimeInMillis
import com.anytypeio.anytype.core_models.ext.mapToObjectWrapperType
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_utils.diff.DefaultObjectDiffIdentifier
@ -566,6 +567,23 @@ class RelationListViewModel(
}
}
fun onOpenDateObjectByTimeInMillis(timeInMillis: TimeInMillis) {
Timber.d("onOpenDateObjectByTimeInMillis, timeInMillis: $timeInMillis")
viewModelScope.launch {
fieldParser.getDateObjectByTimeInSeconds(
timeInSeconds = timeInMillis / 1000,
spaceId = vmParams.spaceId,
actionSuccess = { obj ->
commands.emit(Command.NavigateToDateObject(obj.id))
},
actionFailure = {
Timber.e(it, "Failed to get date object by timestamp")
_toasts.emit("Failed to get date object by timestamp")
}
)
}
}
sealed class Model : DefaultObjectDiffIdentifier {
sealed class Section : Model() {
object Featured : Section() {

View file

@ -2870,6 +2870,13 @@ class ObjectSetViewModel(
toast("Date is not set")
}
}
fun onOpenDateObjectByTimeInMillis(timeInMillis: TimeInMillis) {
Timber.d("onOpenDateObjectByTimeInMillis, timeInMillis:[$timeInMillis]")
viewModelScope.launch {
handleReadOnlyValue(timeInMillis = timeInMillis)
}
}
//endregion
companion object {

View file

@ -81,6 +81,14 @@ class RelationDateValueViewModel(
}
}
fun openSelectedDateClicked(timeInMillis: TimeInMillis) {
viewModelScope.launch {
commands.emit(
DateValueCommand.OpenDateObject(timeInMillis)
)
}
}
fun onYesterdayClicked() {
setDate(timeInSeconds = dateProvider.getTimestampForYesterdayAtStartOfDay())
}
@ -165,6 +173,7 @@ class RelationDateValueViewModel(
sealed class DateValueCommand {
data class DispatchResult(val timeInSeconds: Double?, val dismiss: Boolean = false) : DateValueCommand()
data class OpenDatePicker(val timeInSeconds: Long?) : DateValueCommand()
data class OpenDateObject(val timeInMillis: TimeInMillis) : DateValueCommand()
}
data class DateValueView(