diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/DateObjectDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/DateObjectDI.kt index 11df94fca3..267957d429 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/DateObjectDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/DateObjectDI.kt @@ -63,10 +63,12 @@ object DateObjectModule { @PerScreen fun getObject( repo: BlockRepository, - dispatchers: AppCoroutineDispatchers + dispatchers: AppCoroutineDispatchers, + settings: UserSettingsRepository, ): GetObject = GetObject( repo = repo, - dispatchers = dispatchers + dispatchers = dispatchers, + settings = settings ) @JvmStatic diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/home/HomescreenDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/home/HomescreenDI.kt index 9615103091..b5906337dc 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/home/HomescreenDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/home/HomescreenDI.kt @@ -145,10 +145,12 @@ object HomeScreenModule { @PerScreen fun getObject( repo: BlockRepository, - dispatchers: AppCoroutineDispatchers + dispatchers: AppCoroutineDispatchers, + settings: UserSettingsRepository, ): GetObject = GetObject( repo = repo, - dispatchers = dispatchers + dispatchers = dispatchers, + settings = settings ) @JvmStatic diff --git a/app/src/main/java/com/anytypeio/anytype/ui/splash/SplashFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/splash/SplashFragment.kt index 4e69dbb765..52d97a42a8 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/splash/SplashFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/splash/SplashFragment.kt @@ -24,6 +24,7 @@ import com.anytypeio.anytype.di.common.componentManager import com.anytypeio.anytype.other.DefaultDeepLinkResolver import com.anytypeio.anytype.presentation.splash.SplashViewModel import com.anytypeio.anytype.presentation.splash.SplashViewModelFactory +import com.anytypeio.anytype.ui.date.DateObjectFragment import com.anytypeio.anytype.ui.editor.EditorFragment import com.anytypeio.anytype.ui.home.HomeScreenFragment import com.anytypeio.anytype.ui.onboarding.OnboardingFragment @@ -171,6 +172,27 @@ class SplashFragment : BaseFragment(R.layout.fragment_spl Timber.e(it, "Error while navigating to set-or-collection from splash") } } + is SplashViewModel.Command.NavigateToDateObject -> { + runCatching { + findNavController().navigate(R.id.actionOpenVaultFromSplash) + findNavController().navigate( + R.id.actionOpenSpaceFromVault, + args = HomeScreenFragment.args( + space = command.space, + deeplink = null + ) + ) + findNavController().navigate( + resId = R.id.dateObjectScreen, + args = DateObjectFragment.args( + objectId = command.id, + space = command.space + ) + ) + }.onFailure { + Timber.e(it, "Error while navigating to date object from splash") + } + } is SplashViewModel.Command.NavigateToAuthStart -> { val intent = activity?.intent val deepLink: String? diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/RelativeDate.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/RelativeDate.kt index 557f08b260..bd2c44f4dc 100644 --- a/core-models/src/main/java/com/anytypeio/anytype/core_models/RelativeDate.kt +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/RelativeDate.kt @@ -2,13 +2,43 @@ package com.anytypeio.anytype.core_models sealed class RelativeDate { abstract val initialTimeInMillis: TimeInMillis + abstract val dayOfWeek: DayOfWeekCustom + + /** Represents an unset date (timestamp = 0.0) */ + data object Empty : RelativeDate() { + override val initialTimeInMillis: TimeInMillis = 0 + override val dayOfWeek: DayOfWeekCustom = DayOfWeekCustom.MONDAY // Default value + } + + data class Today( + override val initialTimeInMillis: TimeInMillis, + override val dayOfWeek: DayOfWeekCustom + ) : RelativeDate() + + data class Tomorrow( + override val initialTimeInMillis: TimeInMillis, + override val dayOfWeek: DayOfWeekCustom + ) : RelativeDate() + + data class Yesterday( + override val initialTimeInMillis: TimeInMillis, + override val dayOfWeek: DayOfWeekCustom + ) : RelativeDate() - data class Today(override val initialTimeInMillis: TimeInMillis) : RelativeDate() - data class Tomorrow(override val initialTimeInMillis: TimeInMillis) : RelativeDate() - data class Yesterday(override val initialTimeInMillis: TimeInMillis) : RelativeDate() data class Other( override val initialTimeInMillis: TimeInMillis, + override val dayOfWeek: DayOfWeekCustom, val formattedDate: String, val formattedTime: String ) : RelativeDate() +} + +enum class DayOfWeekCustom { + MONDAY, + TUESDAY, + WEDNESDAY, + THURSDAY, + FRIDAY, + SATURDAY, + SUNDAY } \ No newline at end of file diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/SupportedLayouts.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/SupportedLayouts.kt index bd443d83ab..b398c56d9b 100644 --- a/core-models/src/main/java/com/anytypeio/anytype/core_models/SupportedLayouts.kt +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/SupportedLayouts.kt @@ -66,6 +66,8 @@ object SupportedLayouts { val widgetsLayouts = layouts + dateLayouts + val lastOpenObjectLayouts = layouts + dateLayouts + fun isSupported(layout: ObjectType.Layout?) : Boolean { return layouts.contains(layout) } diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/ComposableExtensions.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/ComposableExtensions.kt index 54f38c1250..7cd5605154 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/ComposableExtensions.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/ComposableExtensions.kt @@ -15,6 +15,9 @@ import androidx.compose.ui.composed import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.stringResource +import com.anytypeio.anytype.core_models.DayOfWeekCustom +import com.anytypeio.anytype.core_models.RelativeDate import com.anytypeio.anytype.core_models.ThemeColor import com.anytypeio.anytype.core_ui.R @@ -96,3 +99,27 @@ fun SnapshotStateList.swapList(newList: List){ clear() addAll(newList) } + +@Composable +fun getLocalizedDayName(dayOfWeek: DayOfWeekCustom): String { + return when (dayOfWeek) { + DayOfWeekCustom.MONDAY -> stringResource(id = R.string.day_of_week_monday) + DayOfWeekCustom.TUESDAY -> stringResource(id = R.string.day_of_week_tuesday) + DayOfWeekCustom.WEDNESDAY -> stringResource(id = R.string.day_of_week_wednesday) + DayOfWeekCustom.THURSDAY -> stringResource(id = R.string.day_of_week_thursday) + DayOfWeekCustom.FRIDAY -> stringResource(id = R.string.day_of_week_friday) + DayOfWeekCustom.SATURDAY -> stringResource(id = R.string.day_of_week_saturday) + DayOfWeekCustom.SUNDAY -> stringResource(id = R.string.day_of_week_sunday) + } +} + +@Composable +fun RelativeDate.getPrettyName(): String { + return when (this) { + is RelativeDate.Today -> stringResource(id = R.string.today) + is RelativeDate.Tomorrow -> stringResource(id = R.string.tomorrow) + is RelativeDate.Yesterday -> stringResource(id = R.string.yesterday) + is RelativeDate.Other -> this.formattedDate + RelativeDate.Empty -> "" + } +} diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/ResExtension.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/ResExtension.kt index f771839968..daae3134f4 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/ResExtension.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/extensions/ResExtension.kt @@ -309,4 +309,5 @@ fun RelativeDate.getPrettyName( is RelativeDate.Today -> resources.getString(R.string.today) is RelativeDate.Tomorrow -> resources.getString(R.string.tomorrow) is RelativeDate.Yesterday -> resources.getString(R.string.yesterday) + RelativeDate.Empty -> "" } \ No newline at end of file diff --git a/core-ui/src/main/res/drawable/ic_dot_3.xml b/core-ui/src/main/res/drawable/ic_dot_3.xml new file mode 100644 index 0000000000..f33a28d98e --- /dev/null +++ b/core-ui/src/main/res/drawable/ic_dot_3.xml @@ -0,0 +1,9 @@ + + + diff --git a/device/src/main/java/com/anytypeio/anytype/device/providers/DateProviderImpl.kt b/device/src/main/java/com/anytypeio/anytype/device/providers/DateProviderImpl.kt index 9268e8a9d6..cf84267842 100644 --- a/device/src/main/java/com/anytypeio/anytype/device/providers/DateProviderImpl.kt +++ b/device/src/main/java/com/anytypeio/anytype/device/providers/DateProviderImpl.kt @@ -1,6 +1,7 @@ package com.anytypeio.anytype.device.providers import android.text.format.DateUtils +import com.anytypeio.anytype.core_models.DayOfWeekCustom import com.anytypeio.anytype.core_models.RelativeDate import com.anytypeio.anytype.core_models.TimeInMillis import com.anytypeio.anytype.core_models.TimeInSeconds @@ -185,9 +186,9 @@ class DateProviderImpl @Inject constructor( return truncatedDateTime1 == truncatedDateTime2 } - override fun calculateRelativeDates(dateInSeconds: TimeInSeconds?): RelativeDate? { + override fun calculateRelativeDates(dateInSeconds: TimeInSeconds?): RelativeDate { - if (dateInSeconds == null || dateInSeconds == 0L) return null + if (dateInSeconds == null || dateInSeconds == 0L) return RelativeDate.Empty val initialTimeInMillis = dateInSeconds * 1000 val zoneId = defaultZoneId val dateInstant = Instant.ofEpochSecond(dateInSeconds) @@ -196,10 +197,33 @@ class DateProviderImpl @Inject constructor( val daysDifference = ChronoUnit.DAYS.between(today, givenDate) + // Convert java.time.DayOfWeek to DayOfWeekCustom + val dayOfWeekCustom = when (givenDate.dayOfWeek) { + java.time.DayOfWeek.MONDAY -> DayOfWeekCustom.MONDAY + java.time.DayOfWeek.TUESDAY -> DayOfWeekCustom.TUESDAY + java.time.DayOfWeek.WEDNESDAY -> DayOfWeekCustom.WEDNESDAY + java.time.DayOfWeek.THURSDAY -> DayOfWeekCustom.THURSDAY + java.time.DayOfWeek.FRIDAY -> DayOfWeekCustom.FRIDAY + java.time.DayOfWeek.SATURDAY -> DayOfWeekCustom.SATURDAY + java.time.DayOfWeek.SUNDAY -> DayOfWeekCustom.SUNDAY + } + return when (daysDifference) { - 0L -> RelativeDate.Today(initialTimeInMillis = initialTimeInMillis) - 1L -> RelativeDate.Tomorrow(initialTimeInMillis = initialTimeInMillis) - -1L -> RelativeDate.Yesterday(initialTimeInMillis = initialTimeInMillis) + 0L -> RelativeDate.Today( + initialTimeInMillis = initialTimeInMillis, + dayOfWeek = dayOfWeekCustom + ) + + 1L -> RelativeDate.Tomorrow( + initialTimeInMillis = initialTimeInMillis, + dayOfWeek = dayOfWeekCustom + ) + + -1L -> RelativeDate.Yesterday( + initialTimeInMillis = initialTimeInMillis, + dayOfWeek = dayOfWeekCustom + ) + else -> { val timestampMillis = TimeUnit.SECONDS.toMillis(dateInSeconds) @@ -207,7 +231,8 @@ class DateProviderImpl @Inject constructor( RelativeDate.Other( initialTimeInMillis = initialTimeInMillis, formattedDate = dateString, - formattedTime = timeString + formattedTime = timeString, + dayOfWeek = dayOfWeekCustom ) } } diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/misc/DateProvider.kt b/domain/src/main/java/com/anytypeio/anytype/domain/misc/DateProvider.kt index 119f24df35..767a4a47dd 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/misc/DateProvider.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/misc/DateProvider.kt @@ -26,7 +26,7 @@ interface DateProvider { timestamp: TimeInMillis, timeStyle: Int = DEFAULT_DATE_FORMAT_STYLE ): Pair - fun calculateRelativeDates(dateInSeconds: TimeInSeconds?): RelativeDate? + fun calculateRelativeDates(dateInSeconds: TimeInSeconds?): RelativeDate fun isSameMinute(timestamp1: Long, timestamp2: Long): Boolean fun getLocalDateOfTime(epochMilli: Long): LocalDate fun isTimestampWithinYearRange(timeStampInMillis: Long, yearRange: IntRange): Boolean diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/object/GetObject.kt b/domain/src/main/java/com/anytypeio/anytype/domain/object/GetObject.kt index edb9889d1e..5c5eefe4dd 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/object/GetObject.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/object/GetObject.kt @@ -6,6 +6,7 @@ import com.anytypeio.anytype.core_models.primitives.SpaceId 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.config.UserSettingsRepository /** * Use-case for opening an object as preview — without subscribing to its subsequent changes. @@ -13,15 +14,24 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository */ class GetObject( private val repo: BlockRepository, + private val settings: UserSettingsRepository, dispatchers: AppCoroutineDispatchers ) : ResultInteractor(dispatchers.io) { override suspend fun doWork(params: Params): ObjectView = repo.getObject( id = params.target, space = params.space - ) + ).also { + if (params.saveAsLastOpened) { + settings.setLastOpenedObject( + id = params.target, + space = params.space + ) + } + } data class Params( val target: Id, - val space: SpaceId + val space: SpaceId, + val saveAsLastOpened: Boolean = false ) } \ No newline at end of file diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/primitives/FieldParser.kt b/domain/src/main/java/com/anytypeio/anytype/domain/primitives/FieldParser.kt index 0c02d1d44b..22a150d2dd 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/primitives/FieldParser.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/primitives/FieldParser.kt @@ -4,6 +4,7 @@ import com.anytypeio.anytype.core_models.MAX_SNIPPET_SIZE import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.Relations +import com.anytypeio.anytype.core_models.RelativeDate import com.anytypeio.anytype.core_models.SupportedLayouts import com.anytypeio.anytype.core_models.TimeInSeconds import com.anytypeio.anytype.core_models.primitives.Field @@ -91,7 +92,7 @@ class FieldParserImpl @Inject constructor( private fun calculateFieldDate(value: Value.Single?): Field.Date? { val dateInSeconds = value?.single ?: return null val relativeDate = dateProvider.calculateRelativeDates(dateInSeconds) - if (relativeDate == null) { + if (relativeDate is RelativeDate.Empty) { return null } return Field.Date( diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/FieldsScreen.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/FieldsScreen.kt index 34a9987226..d7d0b457d3 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/FieldsScreen.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/FieldsScreen.kt @@ -43,6 +43,7 @@ import com.anytypeio.anytype.feature_date.viewmodel.UiFieldsState @Composable fun FieldsScreen( + modifier: Modifier, uiState: UiFieldsState, onDateEvent: (DateEvent) -> Unit ) { @@ -66,9 +67,9 @@ fun FieldsScreen( LazyRow( state = lazyFieldsListState, - modifier = Modifier.fillMaxWidth(), + modifier = modifier, horizontalArrangement = Arrangement.spacedBy(8.dp), - contentPadding = PaddingValues(start = 16.dp, end = 16.dp, top = 24.dp) + contentPadding = PaddingValues(start = 16.dp, end = 16.dp) ) { items( count = items.size, @@ -181,6 +182,7 @@ fun FieldsScreen( @DefaultPreviews fun FieldsScreenPreview() { FieldsScreen( + modifier = Modifier.fillMaxWidth().height(40.dp), uiState = UiFieldsState( items = StubHorizontalItems, selectedRelationKey = RelationKey("1") @@ -193,6 +195,7 @@ fun FieldsScreenPreview() { @DefaultPreviews fun LoadingPreview() { FieldsScreen( + modifier = Modifier.fillMaxWidth().height(40.dp), uiState = UiFieldsState.LoadingState, onDateEvent = {} ) diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/HeaderScreen.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/HeaderScreen.kt index 70e933b4e8..de05ece99d 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/HeaderScreen.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/HeaderScreen.kt @@ -2,11 +2,15 @@ package com.anytypeio.anytype.feature_date.ui import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -18,13 +22,18 @@ import androidx.compose.ui.res.painterResource 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.DayOfWeekCustom +import com.anytypeio.anytype.core_models.RelativeDate import com.anytypeio.anytype.core_ui.common.DefaultPreviews import com.anytypeio.anytype.core_ui.common.ShimmerEffect +import com.anytypeio.anytype.core_ui.extensions.getLocalizedDayName +import com.anytypeio.anytype.core_ui.extensions.getPrettyName import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable import com.anytypeio.anytype.core_ui.views.HeadlineTitle +import com.anytypeio.anytype.core_ui.views.Relations2 import com.anytypeio.anytype.feature_date.R -import com.anytypeio.anytype.feature_date.viewmodel.UiHeaderState import com.anytypeio.anytype.feature_date.ui.models.DateEvent +import com.anytypeio.anytype.feature_date.viewmodel.UiHeaderState @Composable fun HeaderScreen( @@ -32,91 +41,155 @@ fun HeaderScreen( uiState: UiHeaderState, onDateEvent: (DateEvent) -> Unit ) { - Row( - modifier = modifier + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally ) { - Image( + RelativeDateAndDayOfWeek( modifier = Modifier - .height(48.dp) - .width(52.dp) - .rotate(180f) - .noRippleThrottledClickable { - onDateEvent(DateEvent.Header.OnPreviousClick) - }, - contentDescription = "Previous day", - painter = painterResource(id = R.drawable.ic_arrow_disclosure_18), - contentScale = ContentScale.None + .wrapContentSize(), + uiState = uiState ) - when (uiState) { - is UiHeaderState.Content -> { - Text( - textAlign = TextAlign.Center, - text = uiState.title, - modifier = Modifier - .fillMaxWidth() - .weight(1f) - .align(Alignment.CenterVertically), - style = HeadlineTitle, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - color = colorResource(id = R.color.text_primary) - ) - } - - UiHeaderState.Loading -> { - Box( - modifier = Modifier - .fillMaxWidth() - .weight(1f) - .align(Alignment.CenterVertically), - contentAlignment = Alignment.Center - ) { - ShimmerEffect( + Spacer(modifier = Modifier.height(4.dp)) + Row( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + modifier = Modifier + .height(32.dp) + .width(52.dp) + .rotate(180f) + .noRippleThrottledClickable { + onDateEvent(DateEvent.Header.OnPreviousClick) + }, + contentDescription = "Previous day", + painter = painterResource(id = R.drawable.ic_arrow_disclosure_18), + contentScale = ContentScale.None + ) + when (uiState) { + is UiHeaderState.Content -> { + Text( + textAlign = TextAlign.Center, + text = uiState.title, modifier = Modifier - .width(200.dp) - .height(30.dp) + .wrapContentHeight() + .fillMaxWidth() + .weight(1f) + .noRippleThrottledClickable { + onDateEvent( + DateEvent.Header.OnHeaderClick( + timeInMillis = uiState.relativeDate.initialTimeInMillis + ) + ) + }, + style = HeadlineTitle, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = colorResource(id = R.color.text_primary) ) } - } - UiHeaderState.Empty -> { - Box( - modifier = Modifier - .fillMaxWidth() - .weight(1f) - .align(Alignment.CenterVertically), - contentAlignment = Alignment.Center - ) { - Spacer( + UiHeaderState.Loading -> { + Box( modifier = Modifier - .width(200.dp) - .height(30.dp) - ) + .fillMaxWidth() + .weight(1f) + .align(Alignment.CenterVertically), + contentAlignment = Alignment.Center + ) { + ShimmerEffect( + modifier = Modifier + .width(200.dp) + .height(30.dp) + ) + } + } + + UiHeaderState.Empty -> { + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + .align(Alignment.CenterVertically), + contentAlignment = Alignment.Center + ) { + Spacer( + modifier = Modifier + .width(200.dp) + .height(30.dp) + ) + } } } + Image( + modifier = Modifier + .height(32.dp) + .width(52.dp) + .noRippleThrottledClickable { + onDateEvent(DateEvent.Header.OnNextClick) + }, + contentDescription = "Next day", + painter = painterResource(id = R.drawable.ic_arrow_disclosure_18), + contentScale = ContentScale.None + ) } - Image( - modifier = Modifier - .height(48.dp) - .width(52.dp) - .noRippleThrottledClickable { - onDateEvent(DateEvent.Header.OnNextClick) - }, - contentDescription = "Next day", - painter = painterResource(id = R.drawable.ic_arrow_disclosure_18), - contentScale = ContentScale.None - ) } } +@Composable +fun RelativeDateAndDayOfWeek( + modifier: Modifier = Modifier, + uiState: UiHeaderState +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically + ) { + if (uiState is UiHeaderState.Content) { + uiState.relativeDate.takeIf { it is RelativeDate.Today || it is RelativeDate.Tomorrow || it is RelativeDate.Yesterday } + ?.let { relativeDate -> + DateWithDot(relativeDate.getPrettyName()) + } + + val dayOfWeek = getLocalizedDayName(uiState.relativeDate.dayOfWeek) + Text( + text = dayOfWeek, + style = Relations2, + color = colorResource(id = R.color.text_secondary), + modifier = Modifier.wrapContentSize() + ) + } + } +} + +@Composable +fun DateWithDot(dateText: String) { + Text( + text = dateText, + style = Relations2, + color = colorResource(id = R.color.text_secondary), + modifier = Modifier.wrapContentSize() + ) + Image( + painter = painterResource(id = R.drawable.ic_dot_3), + contentDescription = "dot", + contentScale = ContentScale.Fit, + modifier = Modifier + .padding(horizontal = 8.dp) + .wrapContentSize() + ) +} + @Composable @DefaultPreviews fun DateLayoutHeaderEmptyPreview() { val state = UiHeaderState.Empty HeaderScreen( modifier = Modifier - .fillMaxWidth() - .height(48.dp), uiState = state + .fillMaxWidth(), uiState = state ) {} } @@ -126,18 +199,22 @@ fun DateLayoutHeaderLoadingPreview() { val state = UiHeaderState.Loading HeaderScreen( Modifier - .fillMaxWidth() - .height(48.dp), state + .fillMaxWidth(), state ) {} } @Composable @DefaultPreviews fun DateLayoutHeaderPreview() { - val state = UiHeaderState.Content("Tue, 12 Oct") + val state = UiHeaderState.Content( + title = "Tue, 12 Oct", + relativeDate = RelativeDate.Today( + initialTimeInMillis = 1634016000000, + dayOfWeek = DayOfWeekCustom.MONDAY + ) + ) HeaderScreen( Modifier - .fillMaxWidth() - .height(48.dp), state + .fillMaxWidth(), state ) {} } \ No newline at end of file diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/MainScreen.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/MainScreen.kt index de4aab9f2f..08c5cb8170 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/MainScreen.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/MainScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.windowInsetsTopHeight +import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable @@ -83,16 +84,20 @@ fun DateMainScreen( onDateEvent = onDateEvent ) Spacer( - modifier = Modifier.height(24.dp) + modifier = Modifier.height(16.dp) ) HeaderScreen( modifier = Modifier .fillMaxWidth() - .height(48.dp), + .height(54.dp), uiState = uiHeaderState, onDateEvent = onDateEvent ) + Spacer( + modifier = Modifier.height(32.dp) + ) FieldsScreen( + modifier = Modifier.fillMaxWidth().height(40.dp), uiState = uiFieldsState, onDateEvent = onDateEvent ) diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/models/DateEvent.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/models/DateEvent.kt index 6eb1967080..5903aff660 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/models/DateEvent.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/ui/models/DateEvent.kt @@ -1,5 +1,6 @@ package com.anytypeio.anytype.feature_date.ui.models +import com.anytypeio.anytype.core_models.TimeInMillis import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.core_models.primitives.TimestampInSeconds import com.anytypeio.anytype.feature_date.viewmodel.UiFieldsItem @@ -15,6 +16,7 @@ sealed class DateEvent { sealed class Header : DateEvent() { data object OnNextClick : Header() data object OnPreviousClick : Header() + data class OnHeaderClick(val timeInMillis: TimeInMillis): Header() } sealed class FieldsSheet : DateEvent() { diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateModels.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateModels.kt index b4e8964802..a9740019b9 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateModels.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateModels.kt @@ -4,6 +4,7 @@ import com.anytypeio.anytype.core_models.DVSortType import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.RelationFormat +import com.anytypeio.anytype.core_models.RelativeDate import com.anytypeio.anytype.core_models.TimeInMillis import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState import com.anytypeio.anytype.core_models.primitives.RelationKey @@ -29,7 +30,8 @@ sealed class UiHeaderState { data object Empty : UiHeaderState() data object Loading : UiHeaderState() data class Content( - val title: String + val title: String, + val relativeDate: RelativeDate ) : UiHeaderState() } diff --git a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectViewModel.kt b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectViewModel.kt index ac4947ad3b..57350e8029 100644 --- a/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectViewModel.kt +++ b/feature-date/src/main/java/com/anytypeio/anytype/feature_date/viewmodel/DateObjectViewModel.kt @@ -293,7 +293,8 @@ class DateObjectViewModel( .collect { id -> val params = GetObject.Params( target = id, - space = vmParams.spaceId + space = vmParams.spaceId, + saveAsLastOpened = true ) Timber.d("Start GetObject with params: $params") getObject.async(params).fold( @@ -312,7 +313,10 @@ class DateObjectViewModel( timestampInSeconds = TimestampInSeconds(timestampInSeconds) ) uiHeaderState.value = UiHeaderState.Content( - title = formattedDate + title = formattedDate, + relativeDate = dateProvider.calculateRelativeDates( + dateInSeconds = timestampInSeconds + ) ) } }, @@ -668,21 +672,7 @@ class DateObjectViewModel( private fun onTopToolbarEvent(event: DateEvent.TopToolbar) { when (event) { is DateEvent.TopToolbar.OnCalendarClick -> { - val timestampInSeconds = event.timestampInSeconds - val timeInMillis = dateProvider.adjustToStartOfDayInUserTimeZone( - timestamp = timestampInSeconds.time - ) - val isValid = dateProvider.isTimestampWithinYearRange( - timeStampInMillis = timeInMillis, - yearRange = DATE_PICKER_YEAR_RANGE - ) - if (isValid) { - uiCalendarState.value = UiCalendarState.Calendar( - timeInMillis = timeInMillis - ) - } else { - showDateOutOfRangeError() - } + proceedWithShowCalendar(timestampInSeconds = event.timestampInSeconds) } is DateEvent.TopToolbar.OnSyncStatusClick -> { @@ -694,10 +684,33 @@ class DateObjectViewModel( } } + private fun proceedWithShowCalendar(timestampInSeconds: TimestampInSeconds) { + val timeInMillis = dateProvider.adjustToStartOfDayInUserTimeZone( + timestamp = timestampInSeconds.time + ) + val isValid = dateProvider.isTimestampWithinYearRange( + timeStampInMillis = timeInMillis, + yearRange = DATE_PICKER_YEAR_RANGE + ) + if (isValid) { + uiCalendarState.value = UiCalendarState.Calendar( + timeInMillis = timeInMillis + ) + } else { + showDateOutOfRangeError() + } + } + private fun onHeaderEvent(event: DateEvent.Header) { when (event) { DateEvent.Header.OnNextClick -> proceedWithReopeningDate(offset = SECONDS_IN_DAY) DateEvent.Header.OnPreviousClick -> proceedWithReopeningDate(offset = -SECONDS_IN_DAY) + is DateEvent.Header.OnHeaderClick -> { + val timestampInSeconds = TimestampInSeconds( + time = event.timeInMillis / 1000 + ) + proceedWithShowCalendar(timestampInSeconds) + } } } diff --git a/localization/src/main/res/values/strings.xml b/localization/src/main/res/values/strings.xml index 72982ac0e9..424b91ecf7 100644 --- a/localization/src/main/res/values/strings.xml +++ b/localization/src/main/res/values/strings.xml @@ -1838,4 +1838,12 @@ Please provide specific details of your needs here. Upload Reply + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + Sunday + \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModel.kt index 2480309680..8a43252892 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/splash/SplashViewModel.kt @@ -241,7 +241,7 @@ class SplashViewModel( success = { response -> when (response) { is GetLastOpenedObject.Response.Success -> { - if (SupportedLayouts.layouts.contains(response.obj.layout)) { + if (SupportedLayouts.lastOpenObjectLayouts.contains(response.obj.layout)) { val id = response.obj.id val space = requireNotNull(response.obj.spaceId) when (response.obj.layout) { @@ -252,6 +252,14 @@ class SplashViewModel( space = space ) ) + ObjectType.Layout.DATE -> { + commands.emit( + Command.NavigateToDateObject( + id = id, + space = space + ) + ) + } else -> commands.emit( Command.NavigateToObject( @@ -319,6 +327,7 @@ class SplashViewModel( data object CheckAppStartIntent : Command() data class NavigateToObject(val id: Id, val space: Id) : Command() data class NavigateToObjectSet(val id: Id, val space: Id) : Command() + data class NavigateToDateObject(val id: Id, val space: Id) : Command() data class Toast(val message: String) : Command() }