diff --git a/Wino.Core.Domain/Interfaces/IStatePersistenceService.cs b/Wino.Core.Domain/Interfaces/IStatePersistenceService.cs
index 12e0be84..834415d0 100644
--- a/Wino.Core.Domain/Interfaces/IStatePersistenceService.cs
+++ b/Wino.Core.Domain/Interfaces/IStatePersistenceService.cs
@@ -28,11 +28,6 @@ public interface IStatePersistanceService : INotifyPropertyChanged
///
bool IsReaderNarrowed { get; set; }
- ///
- /// Should display back button on the shell title bar.
- ///
- bool IsBackButtonVisible { get; }
-
///
/// Current application mode (Mail or Calendar).
/// Not persisted to configuration, only kept in memory.
@@ -44,11 +39,6 @@ public interface IStatePersistanceService : INotifyPropertyChanged
///
bool IsEventDetailsVisible { get; set; }
- ///
- /// Whether the current application mode has an active backstack that can be navigated.
- ///
- bool HasCurrentModeBackStack { get; set; }
-
///
/// Setting: Opened pane length for the navigation view.
///
diff --git a/Wino.Core.Domain/Interfaces/IWinoNavigationService.cs b/Wino.Core.Domain/Interfaces/IWinoNavigationService.cs
index 3a1b2b5a..328f08ac 100644
--- a/Wino.Core.Domain/Interfaces/IWinoNavigationService.cs
+++ b/Wino.Core.Domain/Interfaces/IWinoNavigationService.cs
@@ -13,5 +13,6 @@ public interface INavigationService
Type GetPageType(WinoPage winoPage);
bool ChangeApplicationMode(WinoApplicationMode mode);
+ bool CanGoBack();
void GoBack(NavigationTransitionEffect slideEffect = NavigationTransitionEffect.FromRight);
}
diff --git a/Wino.Core.Domain/Models/Calendar/CalendarRangeTextFormatter.cs b/Wino.Core.Domain/Models/Calendar/CalendarRangeTextFormatter.cs
index 8d0bade7..598ab3e5 100644
--- a/Wino.Core.Domain/Models/Calendar/CalendarRangeTextFormatter.cs
+++ b/Wino.Core.Domain/Models/Calendar/CalendarRangeTextFormatter.cs
@@ -20,12 +20,20 @@ public sealed class CalendarRangeTextFormatter : ICalendarRangeTextFormatter
return FormatDate(range.StartDate, culture);
}
+ if (range.SpansSingleMonth)
+ {
+ return $"{FormatDate(range.StartDate, culture)} - {FormatDay(range.EndDate, culture)}";
+ }
+
return $"{FormatDate(range.StartDate, culture)} - {FormatDate(range.EndDate, culture)}";
}
private static string FormatDate(DateOnly date, CultureInfo culture)
=> date.ToString(culture.DateTimeFormat.MonthDayPattern, culture);
+ private static string FormatDay(DateOnly date, CultureInfo culture)
+ => date.Day.ToString(culture);
+
private static string FormatMonth(DateOnly date, CultureInfo culture)
=> date.ToString(culture.DateTimeFormat.YearMonthPattern, culture);
}
diff --git a/Wino.Core.Tests/CalendarRangeTextFormatterTests.cs b/Wino.Core.Tests/CalendarRangeTextFormatterTests.cs
index d98b9427..8c302e64 100644
--- a/Wino.Core.Tests/CalendarRangeTextFormatterTests.cs
+++ b/Wino.Core.Tests/CalendarRangeTextFormatterTests.cs
@@ -32,7 +32,7 @@ public class CalendarRangeTextFormatterTests
startDate: new DateOnly(2026, 3, 3),
endDate: new DateOnly(2026, 3, 10));
- Formatter.Format(range, DateContextProvider).Should().Be("March 3 - March 10");
+ Formatter.Format(range, DateContextProvider).Should().Be("March 3 - 10");
}
[Fact]
diff --git a/Wino.Mail.WinUI/Services/NavigationService.cs b/Wino.Mail.WinUI/Services/NavigationService.cs
index cb204daf..994b3e5c 100644
--- a/Wino.Mail.WinUI/Services/NavigationService.cs
+++ b/Wino.Mail.WinUI/Services/NavigationService.cs
@@ -219,6 +219,9 @@ public class NavigationService : NavigationServiceBase, INavigationService
public bool ChangeApplicationMode(WinoApplicationMode mode)
=> ExecuteOnNavigationThread(() => ChangeApplicationModeInternal(mode));
+ public bool CanGoBack()
+ => ExecuteOnNavigationThread(CanGoBackInternal);
+
private bool ChangeApplicationModeInternal(WinoApplicationMode mode, object? activationParameter = null)
{
var coreFrame = GetCoreFrameInternal(NavigationReferenceFrame.ShellFrame);
@@ -333,7 +336,6 @@ public class NavigationService : NavigationServiceBase, INavigationService
if (innerShellFrame.CanGoBack && lastBackStackEntry?.SourcePageType == pageType)
{
innerShellFrame.GoBack();
- UpdateCurrentModeBackStackState(innerShellFrame);
WeakReferenceMessenger.Default.Send(loadCalendarMessage);
return true;
}
@@ -495,7 +497,7 @@ public class NavigationService : NavigationServiceBase, INavigationService
if (navigationResult)
{
- UpdateCurrentModeBackStackState(frame);
+ return true;
}
return navigationResult;
@@ -522,15 +524,13 @@ public class NavigationService : NavigationServiceBase, INavigationService
var currentApplicationMode = _statePersistanceService.ApplicationMode;
if (currentApplicationMode == WinoApplicationMode.Settings &&
- _statePersistanceService.HasCurrentModeBackStack)
+ innerShellFrame?.Content is SettingsPage settingsPage)
{
- WeakReferenceMessenger.Default.Send(new BackBreadcrumNavigationRequested(slideEffect));
- return;
- }
+ if (settingsPage.CanNavigateBack)
+ {
+ WeakReferenceMessenger.Default.Send(new BackBreadcrumNavigationRequested(slideEffect));
+ }
- if (currentApplicationMode == WinoApplicationMode.Settings &&
- innerShellFrame?.Content is SettingsPage)
- {
return;
}
@@ -544,7 +544,6 @@ public class NavigationService : NavigationServiceBase, INavigationService
if (innerShellFrame?.CanGoBack == true)
{
innerShellFrame.GoBack();
- UpdateCurrentModeBackStackState(innerShellFrame);
}
else if (innerShellFrame != null && innerShellFrame.Content?.GetType() != typeof(CalendarPage))
{
@@ -560,16 +559,11 @@ public class NavigationService : NavigationServiceBase, INavigationService
{
// Mail mode: Clear selections and dispose rendering frame
_statePersistanceService.IsReadingMail = false;
- _statePersistanceService.HasCurrentModeBackStack = false;
WeakReferenceMessenger.Default.Send(new ClearMailSelectionsRequested());
WeakReferenceMessenger.Default.Send(new DisposeRenderingFrameRequested());
}
}
- else
- {
- UpdateCurrentModeBackStackState(innerShellFrame);
- }
}
private void ResetCurrentModeBackStackState()
@@ -581,17 +575,6 @@ public class NavigationService : NavigationServiceBase, INavigationService
innerShellFrame.BackStack.Clear();
innerShellFrame.ForwardStack.Clear();
}
-
- _statePersistanceService.HasCurrentModeBackStack = false;
- }
-
- private void UpdateCurrentModeBackStackState(Frame? innerShellFrame)
- {
- if (_statePersistanceService.ApplicationMode == WinoApplicationMode.Settings)
- return;
-
- _statePersistanceService.HasCurrentModeBackStack = _statePersistanceService.ApplicationMode == WinoApplicationMode.Calendar &&
- innerShellFrame?.CanGoBack == true;
}
private void PruneInnerShellBackStackForMode(Frame frame, WinoApplicationMode mode)
@@ -636,7 +619,33 @@ public class NavigationService : NavigationServiceBase, INavigationService
frame.BackStack.Clear();
frame.ForwardStack.Clear();
- UpdateCurrentModeBackStackState(frame);
+ }
+
+ private bool CanGoBackInternal()
+ {
+ var innerShellFrame = GetCoreFrameInternal(NavigationReferenceFrame.InnerShellFrame);
+
+ return _statePersistanceService.ApplicationMode switch
+ {
+ WinoApplicationMode.Mail => _statePersistanceService.IsReadingMail && _statePersistanceService.IsReaderNarrowed,
+ WinoApplicationMode.Settings => innerShellFrame?.Content is SettingsPage settingsPage && settingsPage.CanNavigateBack,
+ WinoApplicationMode.Calendar or WinoApplicationMode.Contacts => HasModeScopedBackStack(innerShellFrame, _statePersistanceService.ApplicationMode),
+ _ => false
+ };
+ }
+
+ private bool HasModeScopedBackStack(Frame? innerShellFrame, WinoApplicationMode mode)
+ {
+ if (innerShellFrame == null || innerShellFrame.BackStack.Count == 0)
+ return false;
+
+ for (int i = innerShellFrame.BackStack.Count - 1; i >= 0; i--)
+ {
+ if (IsPageTypeAllowedInMode(mode, innerShellFrame.BackStack[i].SourcePageType))
+ return true;
+ }
+
+ return false;
}
// Standalone EML viewer.
diff --git a/Wino.Mail.WinUI/Services/StatePersistenceService.cs b/Wino.Mail.WinUI/Services/StatePersistenceService.cs
index b1edfab7..a14528ff 100644
--- a/Wino.Mail.WinUI/Services/StatePersistenceService.cs
+++ b/Wino.Mail.WinUI/Services/StatePersistenceService.cs
@@ -32,23 +32,12 @@ public class StatePersistenceService : ObservableObject, IStatePersistanceServic
private void ServicePropertyChanged(object? sender, PropertyChangedEventArgs e) => StatePropertyChanged?.Invoke(this, e?.PropertyName ?? string.Empty);
- public bool IsBackButtonVisible =>
- ApplicationMode == WinoApplicationMode.Mail
- ? (IsReadingMail && IsReaderNarrowed) || HasCurrentModeBackStack
- : HasCurrentModeBackStack;
-
private WinoApplicationMode applicationMode = WinoApplicationMode.Mail;
public WinoApplicationMode ApplicationMode
{
get => applicationMode;
- set
- {
- if (SetProperty(ref applicationMode, value))
- {
- OnPropertyChanged(nameof(IsBackButtonVisible));
- }
- }
+ set => SetProperty(ref applicationMode, value);
}
private bool isEventDetailsVisible;
@@ -60,40 +49,18 @@ public class StatePersistenceService : ObservableObject, IStatePersistanceServic
{
if (SetProperty(ref isEventDetailsVisible, value))
{
- OnPropertyChanged(nameof(IsBackButtonVisible));
-
IsReaderNarrowed = value;
IsReadingMail = value;
}
}
}
- private bool hasCurrentModeBackStack;
-
- public bool HasCurrentModeBackStack
- {
- get => hasCurrentModeBackStack;
- set
- {
- if (SetProperty(ref hasCurrentModeBackStack, value))
- {
- OnPropertyChanged(nameof(IsBackButtonVisible));
- }
- }
- }
-
private bool isReadingMail;
public bool IsReadingMail
{
get => isReadingMail;
- set
- {
- if (SetProperty(ref isReadingMail, value))
- {
- OnPropertyChanged(nameof(IsBackButtonVisible));
- }
- }
+ set => SetProperty(ref isReadingMail, value);
}
private bool shouldShiftMailRenderingDesign;
@@ -109,13 +76,7 @@ public class StatePersistenceService : ObservableObject, IStatePersistanceServic
public bool IsReaderNarrowed
{
get => isReaderNarrowed;
- set
- {
- if (SetProperty(ref isReaderNarrowed, value))
- {
- OnPropertyChanged(nameof(IsBackButtonVisible));
- }
- }
+ set => SetProperty(ref isReaderNarrowed, value);
}
private string coreWindowTitle = string.Empty;
diff --git a/Wino.Mail.WinUI/ShellWindow.xaml b/Wino.Mail.WinUI/ShellWindow.xaml
index 0c4945f0..6952db29 100644
--- a/Wino.Mail.WinUI/ShellWindow.xaml
+++ b/Wino.Mail.WinUI/ShellWindow.xaml
@@ -31,10 +31,13 @@
VerticalContentAlignment="Stretch"
BackRequested="BackButtonClicked"
Background="Transparent"
- IsBackButtonVisible="{x:Bind StatePersistanceService.IsBackButtonVisible, Mode=OneWay}"
IsPaneToggleButtonVisible="True"
PaneToggleRequested="PaneButtonClicked"
Subtitle="{x:Bind StatePersistanceService.CoreWindowTitle, Mode=OneWay}">
+
+ Stretch
+ Stretch
+