From 32e2f7893212f2a9ef7902add71ae07a013c5474 Mon Sep 17 00:00:00 2001 From: keyldev Date: Sun, 27 Sep 2026 21:13:17 +0300 Subject: [PATCH] fix(mobile): polish the redesign on phone-width screens - Cameras: compact header (actions beside the title, full-width search, stretched group filter); fixes the clipped search box - Sheets: pad the bottom by the safe area plus a small gap so actions clear the gesture bar - Recordings: phone calendar is a full-width card with a week strip that expands to the month - Player: clock row, centred transport and one row of equal pills (sound, speed menu, clip) --- src/OpenIPC.Viewer.App/Services/Localizer.cs | 4 + .../Services/OverlayDialogPresenter.cs | 20 +- .../ViewModels/ArchiveCalendarViewModel.cs | 56 +++- .../Views/Pages/ArchiveCalendarView.axaml | 296 +++++++++++++----- .../Views/Pages/ArchiveCalendarView.axaml.cs | 20 ++ .../Views/Pages/CameraLibraryPage.axaml | 201 +++++++----- .../Views/Pages/RecordingPlayerPage.axaml | 117 ++++++- .../Views/Pages/RecordingPlayerPage.axaml.cs | 20 ++ .../Views/Pages/RecordingsPage.axaml | 15 +- 9 files changed, 571 insertions(+), 178 deletions(-) diff --git a/src/OpenIPC.Viewer.App/Services/Localizer.cs b/src/OpenIPC.Viewer.App/Services/Localizer.cs index bf9c1ae..daf8c7d 100644 --- a/src/OpenIPC.Viewer.App/Services/Localizer.cs +++ b/src/OpenIPC.Viewer.App/Services/Localizer.cs @@ -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", @@ -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", @@ -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"] = "В сети", @@ -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"] = "Предыдущая", diff --git a/src/OpenIPC.Viewer.App/Services/OverlayDialogPresenter.cs b/src/OpenIPC.Viewer.App/Services/OverlayDialogPresenter.cs index 5dd5a66..1bdc899 100644 --- a/src/OpenIPC.Viewer.App/Services/OverlayDialogPresenter.cs +++ b/src/OpenIPC.Viewer.App/Services/OverlayDialogPresenter.cs @@ -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 @@ -118,14 +121,15 @@ public static async Task ShowAsync(Control content, Task 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 { diff --git a/src/OpenIPC.Viewer.App/ViewModels/ArchiveCalendarViewModel.cs b/src/OpenIPC.Viewer.App/ViewModels/ArchiveCalendarViewModel.cs index 4dd444c..632fab8 100644 --- a/src/OpenIPC.Viewer.App/ViewModels/ArchiveCalendarViewModel.cs +++ b/src/OpenIPC.Viewer.App/ViewModels/ArchiveCalendarViewModel.cs @@ -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 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 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)); @@ -191,6 +211,37 @@ private void BuildGrid(IReadOnlyDictionary activity, int }; Days.Add(cell); } + UpdateWeek(); + } + + private void UpdateWeek() + { + WeekDays.Clear(); + if (Days.Count < 7) return; + + var today = DateTime.Now.Date; + int WeekOf(Func 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] @@ -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(); DaySelected?.Invoke(cell.Date.Date); } @@ -236,6 +289,7 @@ private void ShowAll() { SelectedDate = null; foreach (var c in Days) c.IsSelected = false; + UpdateWeek(); DaySelected?.Invoke(null); } } diff --git a/src/OpenIPC.Viewer.App/Views/Pages/ArchiveCalendarView.axaml b/src/OpenIPC.Viewer.App/Views/Pages/ArchiveCalendarView.axaml index fc42db3..1080012 100644 --- a/src/OpenIPC.Viewer.App/Views/Pages/ArchiveCalendarView.axaml +++ b/src/OpenIPC.Viewer.App/Views/Pages/ArchiveCalendarView.axaml @@ -2,9 +2,40 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="using:OpenIPC.Viewer.App.ViewModels" xmlns:svc="using:OpenIPC.Viewer.App.Services" + xmlns:pages="using:OpenIPC.Viewer.App.Views.Pages" x:Class="OpenIPC.Viewer.App.Views.Pages.ArchiveCalendarView" x:DataType="vm:ArchiveCalendarViewModel"> + + + + + + + + + + + + + + + + + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +