Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/OpenIPC.Viewer.App/Services/Localizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ private static LangCode DetectSystem()
["Library.RowLayouts"] = "Layouts",
["Library.SearchPlaceholder"] = "Name or IP",
["Library.FindOnNetwork"] = "Find on network",
["Library.AddShort"] = "Add",
["Library.NoMatches"] = "No cameras match these filters",
["Library.Filter.All"] = "All",
["Library.Filter.Online"] = "Online",
Expand Down Expand Up @@ -630,6 +631,7 @@ private static LangCode DetectSystem()
["Calendar.MonthSummaryFormat"] = "Recordings this month: {0}",
["Calendar.DayTooltipFormat"] = "Recordings: {0} · Events: {1}",
["Calendar.NearestFormat"] = "Nearest recordings: {0} →",
["Calendar.ToggleMonth"] = "Whole month",
["Recordings.ExportTitle"] = "Export clip",
["Player.Back"] = "Back to recordings",
["Player.Previous"] = "Previous",
Expand Down Expand Up @@ -807,6 +809,7 @@ private static LangCode DetectSystem()
["Library.RowLayouts"] = "Раскладки",
["Library.SearchPlaceholder"] = "Имя или IP",
["Library.FindOnNetwork"] = "Найти в сети",
["Library.AddShort"] = "Добавить",
["Library.NoMatches"] = "Под эти фильтры камер нет",
["Library.Filter.All"] = "Все",
["Library.Filter.Online"] = "В сети",
Expand Down Expand Up @@ -1262,6 +1265,7 @@ private static LangCode DetectSystem()
["Calendar.MonthSummaryFormat"] = "Записей в месяце: {0}",
["Calendar.DayTooltipFormat"] = "Записей: {0} · Событий: {1}",
["Calendar.NearestFormat"] = "Ближайшие записи: {0} →",
["Calendar.ToggleMonth"] = "Весь месяц",
["Recordings.ExportTitle"] = "Экспорт клипа",
["Player.Back"] = "К списку записей",
["Player.Previous"] = "Предыдущая",
Expand Down
20 changes: 12 additions & 8 deletions src/OpenIPC.Viewer.App/Services/OverlayDialogPresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public static class OverlayDialogPresenter
// keeps the card's top edge clear of the status bar / notch.
private const double TopPeek = 56;

// Air between a sheet's last row and the navigation bar / gesture pill.
private const double SheetBottomGap = 14;

// Number of overlay dialogs currently on screen. Mobile dialogs live in the
// TopLevel.OverlayLayer; the dim Border does not reliably intercept taps on
// the bottom nav, so the shell gates navigation on this instead. Desktop
Expand Down Expand Up @@ -118,14 +121,15 @@ public static async Task<TResult> ShowAsync<TResult>(Control content, Task<TResu
},
};

// Full-screen pages cover the whole TopLevel, including under the status
// bar — inset the top so the title clears the clock/notch. Use the real
// safe-area when the platform exposes it, else a sensible default.
if (fullScreen)
{
var safeTop = top?.InsetsManager?.SafeAreaPadding.Top ?? 0;
card.Padding = new Thickness(0, safeTop > 0 ? safeTop : 28, 0, 0);
}
// The overlay layer sits above the shell, so it doesn't inherit the
// safe-area padding MainView applies to itself. Full-screen pages cover
// the status bar — inset the top so the title clears the clock/notch.
// A sheet reaches the bottom edge: lift its action row clear of the
// navigation bar / gesture pill, plus a little air above it.
var safe = top?.InsetsManager?.SafeAreaPadding ?? default;
card.Padding = fullScreen
? new Thickness(0, safe.Top > 0 ? safe.Top : 28, 0, 0)
: new Thickness(0, 0, 0, safe.Bottom + SheetBottomGap);

var dim = new Border
{
Expand Down
56 changes: 55 additions & 1 deletion src/OpenIPC.Viewer.App/ViewModels/ArchiveCalendarViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,30 @@ public ArchiveCalendarViewModel(
[NotifyPropertyChangedFor(nameof(MonthLabel))]
private int _month;

[ObservableProperty] private DateTime? _selectedDate;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HasSelectedDate))]
[NotifyPropertyChangedFor(nameof(SelectedDateLabel))]
private DateTime? _selectedDate;

public bool HasSelectedDate => SelectedDate is not null;

// "27 сентября" — the compact calendar's reset chip.
public string SelectedDateLabel => SelectedDate is { } d ? d.ToString("d MMMM", UiCulture) : "";

public ObservableCollection<CalendarDayCell> Days { get; } = new();

// One row of Days (same cell instances) for the collapsed phone calendar:
// the selected day's week, else this week if it has recordings, else the
// latest week with recordings, else this week / the month's first week.
public ObservableCollection<CalendarDayCell> WeekDays { get; } = new();

// Phone calendar: week strip (false) or the whole month (true). Picking a
// day folds it back so the list gets the screen again.
[ObservableProperty] private bool _isExpanded;

[RelayCommand]
private void ToggleExpanded() => IsExpanded = !IsExpanded;

public string MonthLabel =>
UiCulture.TextInfo.ToTitleCase(new DateTime(Year, Month, 1).ToString("MMMM yyyy", UiCulture));

Expand Down Expand Up @@ -191,6 +211,37 @@ private void BuildGrid(IReadOnlyDictionary<DateTime, DayActivity> activity, int
};
Days.Add(cell);
}
UpdateWeek();
}

private void UpdateWeek()
{
WeekDays.Clear();
if (Days.Count < 7) return;

var today = DateTime.Now.Date;
int WeekOf(Func<CalendarDayCell, bool> match)
{
for (var i = 0; i < Days.Count; i++)
if (match(Days[i])) return i / 7;
return -1;
}
bool WeekHasRecordings(int week) =>
week >= 0 && Enumerable.Range(week * 7, 7).Any(i => Days[i].InMonth && Days[i].HasRecordings);

var thisWeek = WeekOf(c => c.InMonth && c.Date == today);
var lastRecordingWeek = -1;
for (var i = Days.Count - 1; i >= 0 && lastRecordingWeek < 0; i--)
if (Days[i].InMonth && Days[i].HasRecordings) lastRecordingWeek = i / 7;

var week = SelectedDate is { } sel ? WeekOf(c => c.Date == sel.Date) : -1;
if (week < 0 && WeekHasRecordings(thisWeek)) week = thisWeek;
if (week < 0) week = lastRecordingWeek;
if (week < 0) week = thisWeek;
if (week < 0) week = WeekOf(c => c.InMonth);

for (var i = 0; i < 7; i++)
WeekDays.Add(Days[week * 7 + i]);
}

[RelayCommand]
Expand Down Expand Up @@ -228,6 +279,8 @@ private void SelectDay(CalendarDayCell? cell)
}
SelectedDate = cell.Date.Date;
foreach (var c in Days) c.IsSelected = c.Date.Date == cell.Date.Date;
IsExpanded = false;
UpdateWeek();
Comment on lines +282 to +283

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Clearing a day leaves the week strip stale 🐞 Bug ≡ Correctness

SelectDay clears SelectedDate in its same-day toggle-off branch but returns before setting
IsExpanded = false or calling UpdateWeek(). Tapping the selected day clears the recording filter
while the collapsed strip retains the old week, and doing so while expanded leaves the month open.
Agent Prompt
## Issue description
Toggling off an already-selected day clears the recording filter but bypasses the compact calendar state updates, leaving the old week visible or the month expanded.

## Fix Focus Areas
- src/OpenIPC.Viewer.App/ViewModels/ArchiveCalendarViewModel.cs[267-285]

## Recommended Fix
In the same-day toggle-off branch, after clearing `SelectedDate` and cell selection and before raising `DaySelected(null)` and returning, set `IsExpanded = false` and call `UpdateWeek()`. This aligns date clearing with the existing `ShowAll()` behavior and rebuilds the collapsed week strip.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

DaySelected?.Invoke(cell.Date.Date);
}

Expand All @@ -236,6 +289,7 @@ private void ShowAll()
{
SelectedDate = null;
foreach (var c in Days) c.IsSelected = false;
UpdateWeek();
DaySelected?.Invoke(null);
}
}
Expand Down
Loading
Loading