Some changes.
This commit is contained in:
@@ -27,8 +27,19 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IWinoRequestDelegator _winoRequestDelegator;
|
||||
private readonly INavigationService _navigationService;
|
||||
private readonly IUnderlyingThemeService _underlyingThemeService;
|
||||
|
||||
public CalendarSettings CurrentSettings { get; }
|
||||
public INativeAppService NativeAppService => _nativeAppService;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsDarkWebviewRenderer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the current event has attachments.
|
||||
/// Currently always returns false until attachments are implemented.
|
||||
/// </summary>
|
||||
public bool HasAttachments => false; // TODO: Implement when CalendarItem attachments are added
|
||||
|
||||
#region Details
|
||||
|
||||
@@ -40,6 +51,12 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
[NotifyPropertyChangedFor(nameof(CurrentRsvpStatus))]
|
||||
public partial CalendarItemViewModel CurrentEvent { get; set; }
|
||||
|
||||
partial void OnCurrentEventChanged(CalendarItemViewModel value)
|
||||
{
|
||||
// Notify the view to re-render the description
|
||||
Messenger.Send(new CalendarDescriptionRenderingRequested());
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial CalendarItemViewModel SeriesParent { get; set; }
|
||||
[ObservableProperty]
|
||||
@@ -127,7 +144,8 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
IPreferencesService preferencesService,
|
||||
IMailDialogService dialogService,
|
||||
IWinoRequestDelegator winoRequestDelegator,
|
||||
INavigationService navigationService)
|
||||
INavigationService navigationService,
|
||||
IUnderlyingThemeService underlyingThemeService)
|
||||
{
|
||||
_calendarService = calendarService;
|
||||
_nativeAppService = nativeAppService;
|
||||
@@ -135,8 +153,10 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
||||
_dialogService = dialogService;
|
||||
_winoRequestDelegator = winoRequestDelegator;
|
||||
_navigationService = navigationService;
|
||||
_underlyingThemeService = underlyingThemeService;
|
||||
|
||||
CurrentSettings = _preferencesService.GetCurrentCalendarSettings();
|
||||
IsDarkWebviewRenderer = _underlyingThemeService.IsUnderlyingThemeDark();
|
||||
|
||||
// Initialize RSVP status options
|
||||
RsvpStatusOptions.Add(new RsvpStatusOption(CalendarItemStatus.Accepted));
|
||||
|
||||
@@ -97,6 +97,10 @@
|
||||
"CalendarEventRsvpPanel_SendReplyMessage": "Send a reply message",
|
||||
"CalendarEventRsvpPanel_Tentative": "Tentative",
|
||||
"CalendarEventRsvpPanel_Title": "Response Options",
|
||||
"CalendarAttendeeStatus_Accepted": "Accepted",
|
||||
"CalendarAttendeeStatus_Declined": "Declined",
|
||||
"CalendarAttendeeStatus_NeedsAction": "Needs Action",
|
||||
"CalendarAttendeeStatus_Tentative": "Tentative",
|
||||
"CalendarEventDetails_Attachments": "Attachments",
|
||||
"CalendarEventDetails_Details": "Details",
|
||||
"CalendarEventDetails_EditSeries": "Edit Series",
|
||||
|
||||
@@ -110,4 +110,31 @@ public static class CalendarXamlHelpers
|
||||
/// </summary>
|
||||
public static bool HasOnlineMeetingLink(CalendarItemViewModel calendarItemViewModel)
|
||||
=> calendarItemViewModel != null && !string.IsNullOrEmpty(calendarItemViewModel.CalendarItem?.HtmlLink);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the text representation of an attendee's status.
|
||||
/// </summary>
|
||||
public static string GetAttendeeStatusText(AttendeeStatus status)
|
||||
{
|
||||
return status switch
|
||||
{
|
||||
AttendeeStatus.Accepted => Translator.CalendarAttendeeStatus_Accepted,
|
||||
AttendeeStatus.Declined => Translator.CalendarAttendeeStatus_Declined,
|
||||
AttendeeStatus.Tentative => Translator.CalendarAttendeeStatus_Tentative,
|
||||
AttendeeStatus.NeedsAction => Translator.CalendarAttendeeStatus_NeedsAction,
|
||||
_ => string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns visibility for attendee status badge.
|
||||
/// Only shows status for non-organizers and when status is not NeedsAction.
|
||||
/// </summary>
|
||||
public static Microsoft.UI.Xaml.Visibility GetAttendeeStatusVisibility(AttendeeStatus status)
|
||||
{
|
||||
// Don't show "Needs Action" status as it's the default
|
||||
return status == AttendeeStatus.NeedsAction
|
||||
? Microsoft.UI.Xaml.Visibility.Collapsed
|
||||
: Microsoft.UI.Xaml.Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,10 @@
|
||||
Background="{ThemeResource DividerStrokeColorDefaultBrush}" />
|
||||
|
||||
<!-- RSVP Button -->
|
||||
<Button Command="{x:Bind ViewModel.ToggleRsvpPanelCommand}" Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<Button
|
||||
Background="{ThemeResource CardBackgroundFillColorDefault}"
|
||||
Command="{x:Bind ViewModel.ToggleRsvpPanelCommand}"
|
||||
Style="{StaticResource TransparentActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<ContentControl Content="{x:Bind ViewModel.CurrentRsvpStatus, Mode=OneWay}" ContentTemplateSelector="{StaticResource RsvpStatusIconSelector}" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.CurrentRsvpText, Mode=OneWay}" />
|
||||
@@ -360,9 +363,9 @@
|
||||
Visibility="{x:Bind ReadOnlyToggle.IsChecked, Mode=OneWay}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="16" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title -->
|
||||
@@ -373,13 +376,13 @@
|
||||
|
||||
<!-- Date and Duration -->
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Row="1"
|
||||
Text="{x:Bind calendarHelpers:CalendarXamlHelpers.GetEventDetailsDateString(ViewModel.CurrentEvent, ViewModel.CurrentSettings), Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- Recurrence Info -->
|
||||
<Grid
|
||||
Grid.Row="3"
|
||||
Grid.Row="2"
|
||||
ColumnSpacing="6"
|
||||
Visibility="{x:Bind ViewModel.CurrentEvent.IsRecurringParent, Mode=OneWay}">
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -395,6 +398,8 @@
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
|
||||
<!-- WebView -->
|
||||
<WebView2 x:Name="EventDetailsWebView" Grid.Row="3" />
|
||||
</Grid>
|
||||
|
||||
<!-- Editable Event -->
|
||||
@@ -459,19 +464,29 @@
|
||||
FontSize="13"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind Email}" />
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Padding="6,2"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||
CornerRadius="4"
|
||||
Visibility="{x:Bind IsOrganizer}">
|
||||
<TextBlock
|
||||
FontSize="11"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Text="{x:Bind domain:Translator.CalendarEventDetails_Organizer}" />
|
||||
</Border>
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" Spacing="6">
|
||||
<Border
|
||||
Padding="6,2"
|
||||
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||
CornerRadius="4"
|
||||
Visibility="{x:Bind IsOrganizer}">
|
||||
<TextBlock
|
||||
FontSize="11"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Text="{x:Bind domain:Translator.CalendarEventDetails_Organizer}" />
|
||||
</Border>
|
||||
<Border
|
||||
Padding="6,2"
|
||||
Background="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
CornerRadius="4"
|
||||
Visibility="{x:Bind calendarHelpers:CalendarXamlHelpers.GetAttendeeStatusVisibility(AttendenceStatus)}">
|
||||
<TextBlock
|
||||
FontSize="11"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind calendarHelpers:CalendarXamlHelpers.GetAttendeeStatusText(AttendenceStatus)}" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
@@ -484,7 +499,8 @@
|
||||
<Grid
|
||||
x:Name="AttachmentsGrid"
|
||||
Grid.Column="2"
|
||||
Style="{StaticResource EventDetailsPanelGridStyle}">
|
||||
Style="{StaticResource EventDetailsPanelGridStyle}"
|
||||
Visibility="{x:Bind ViewModel.HasAttachments, Mode=OneWay}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
@@ -516,6 +532,8 @@
|
||||
<Setter Target="PeopleGrid.(Grid.Row)" Value="0" />
|
||||
<Setter Target="PeopleGrid.(Grid.Column)" Value="1" />
|
||||
<Setter Target="PeopleGrid.(Grid.ColumnSpan)" Value="2" />
|
||||
<!-- People grid spans both rows when attachments are hidden -->
|
||||
<Setter Target="PeopleGrid.(Grid.RowSpan)" Value="2" />
|
||||
<Setter Target="AttachmentsGrid.(Grid.Row)" Value="1" />
|
||||
<Setter Target="AttachmentsGrid.(Grid.Column)" Value="1" />
|
||||
<Setter Target="AttachmentsGrid.(Grid.ColumnSpan)" Value="2" />
|
||||
|
||||
@@ -1,11 +1,206 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Windows.System;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Mail.WinUI;
|
||||
using Wino.Mail.WinUI.Extensions;
|
||||
using Wino.Mail.WinUI.Views.Abstract;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
using Wino.Messaging.Client.Shell;
|
||||
|
||||
namespace Wino.Calendar.Views;
|
||||
|
||||
public sealed partial class EventDetailsPage : EventDetailsPageAbstract
|
||||
public sealed partial class EventDetailsPage : EventDetailsPageAbstract,
|
||||
IRecipient<ApplicationThemeChanged>,
|
||||
IRecipient<CalendarDescriptionRenderingRequested>
|
||||
{
|
||||
private readonly IPreferencesService _preferencesService = App.Current.Services.GetService<IPreferencesService>()!;
|
||||
private TaskCompletionSource<bool> DOMLoadedTask = new TaskCompletionSource<bool>();
|
||||
private bool isChromiumDisposed = false;
|
||||
|
||||
public EventDetailsPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
InitializeComponent();
|
||||
|
||||
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
|
||||
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,msWebView2CodeCache");
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
EventDetailsWebView.CoreWebView2Initialized -= CoreWebViewInitialized;
|
||||
EventDetailsWebView.CoreWebView2Initialized += CoreWebViewInitialized;
|
||||
|
||||
_ = EventDetailsWebView.EnsureCoreWebView2Async();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedFrom(e);
|
||||
|
||||
DisposeWebView2();
|
||||
}
|
||||
|
||||
private async void CoreWebViewInitialized(Microsoft.UI.Xaml.Controls.WebView2 sender, CoreWebView2InitializedEventArgs args)
|
||||
{
|
||||
if (EventDetailsWebView.CoreWebView2 == null) return;
|
||||
|
||||
var editorBundlePath = (await ViewModel.NativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty);
|
||||
|
||||
EventDetailsWebView.CoreWebView2.SetVirtualHostNameToFolderMapping("wino.mail", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
|
||||
|
||||
EventDetailsWebView.CoreWebView2.DOMContentLoaded -= DOMContentLoaded;
|
||||
EventDetailsWebView.CoreWebView2.DOMContentLoaded += DOMContentLoaded;
|
||||
|
||||
EventDetailsWebView.CoreWebView2.NewWindowRequested -= WindowRequested;
|
||||
EventDetailsWebView.CoreWebView2.NewWindowRequested += WindowRequested;
|
||||
|
||||
EventDetailsWebView.NavigationStarting -= WebViewNavigationStarting;
|
||||
EventDetailsWebView.NavigationStarting += WebViewNavigationStarting;
|
||||
|
||||
EventDetailsWebView.Source = new Uri("https://wino.mail/reader.html");
|
||||
}
|
||||
|
||||
private void DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args)
|
||||
{
|
||||
DOMLoadedTask.TrySetResult(true);
|
||||
_ = RenderDescriptionAsync();
|
||||
}
|
||||
|
||||
private async Task RenderDescriptionAsync()
|
||||
{
|
||||
if (ViewModel?.CurrentEvent?.CalendarItem == null)
|
||||
return;
|
||||
|
||||
await DOMLoadedTask.Task;
|
||||
|
||||
await UpdateEditorThemeAsync();
|
||||
await UpdateReaderFontPropertiesAsync();
|
||||
|
||||
var description = ViewModel.CurrentEvent.CalendarItem.Description ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(description))
|
||||
{
|
||||
await EventDetailsWebView.ExecuteScriptFunctionAsync("RenderHTML", isChromiumDisposed, JsonSerializer.Serialize(" ", BasicTypesJsonContext.Default.String));
|
||||
}
|
||||
else
|
||||
{
|
||||
await EventDetailsWebView.ExecuteScriptFunctionAsync("RenderHTML", isChromiumDisposed,
|
||||
JsonSerializer.Serialize(description, BasicTypesJsonContext.Default.String),
|
||||
JsonSerializer.Serialize(true, BasicTypesJsonContext.Default.Boolean));
|
||||
}
|
||||
}
|
||||
|
||||
private async void WindowRequested(CoreWebView2 sender, CoreWebView2NewWindowRequestedEventArgs args)
|
||||
{
|
||||
args.Handled = true;
|
||||
|
||||
try
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri(args.Uri));
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
private async void WebViewNavigationStarting(Microsoft.UI.Xaml.Controls.WebView2 sender, CoreWebView2NavigationStartingEventArgs args)
|
||||
{
|
||||
if (args.Uri == "https://wino.mail/reader.html")
|
||||
return;
|
||||
|
||||
args.Cancel = !args.Uri.StartsWith("data:text/html");
|
||||
|
||||
if (args.Cancel && Uri.TryCreate(args.Uri, UriKind.Absolute, out Uri? newUri) && newUri != null)
|
||||
{
|
||||
await Launcher.LaunchUriAsync(newUri);
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposeWebView2()
|
||||
{
|
||||
if (EventDetailsWebView == null) return;
|
||||
|
||||
EventDetailsWebView.CoreWebView2Initialized -= CoreWebViewInitialized;
|
||||
EventDetailsWebView.NavigationStarting -= WebViewNavigationStarting;
|
||||
|
||||
if (EventDetailsWebView.CoreWebView2 != null)
|
||||
{
|
||||
EventDetailsWebView.CoreWebView2.DOMContentLoaded -= DOMContentLoaded;
|
||||
EventDetailsWebView.CoreWebView2.NewWindowRequested -= WindowRequested;
|
||||
}
|
||||
|
||||
isChromiumDisposed = true;
|
||||
|
||||
EventDetailsWebView.Close();
|
||||
}
|
||||
|
||||
private async Task UpdateEditorThemeAsync()
|
||||
{
|
||||
await DOMLoadedTask.Task;
|
||||
|
||||
if (ViewModel.IsDarkWebviewRenderer)
|
||||
{
|
||||
EventDetailsWebView.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Dark;
|
||||
await InvokeScriptSafeAsync("SetDarkEditor();");
|
||||
}
|
||||
else
|
||||
{
|
||||
EventDetailsWebView.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Light;
|
||||
await InvokeScriptSafeAsync("SetLightEditor();");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateReaderFontPropertiesAsync()
|
||||
{
|
||||
await EventDetailsWebView.ExecuteScriptFunctionAsync("ChangeFontSize", isChromiumDisposed, JsonSerializer.Serialize(_preferencesService.ReaderFontSize, BasicTypesJsonContext.Default.Int32));
|
||||
|
||||
var fontName = _preferencesService.ReaderFont;
|
||||
fontName += ", sans-serif";
|
||||
|
||||
await EventDetailsWebView.ExecuteScriptFunctionAsync("ChangeFontFamily", isChromiumDisposed, JsonSerializer.Serialize(fontName, BasicTypesJsonContext.Default.String));
|
||||
}
|
||||
|
||||
private async Task<string> InvokeScriptSafeAsync(string function)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await EventDetailsWebView.ExecuteScriptAsync(function);
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
void IRecipient<ApplicationThemeChanged>.Receive(ApplicationThemeChanged message)
|
||||
{
|
||||
ViewModel.IsDarkWebviewRenderer = message.IsUnderlyingThemeDark;
|
||||
_ = UpdateEditorThemeAsync();
|
||||
}
|
||||
|
||||
async void IRecipient<CalendarDescriptionRenderingRequested>.Receive(CalendarDescriptionRenderingRequested message)
|
||||
{
|
||||
await RenderDescriptionAsync();
|
||||
}
|
||||
|
||||
protected override void RegisterRecipients()
|
||||
{
|
||||
base.RegisterRecipients();
|
||||
WeakReferenceMessenger.Default.Register<ApplicationThemeChanged>(this);
|
||||
WeakReferenceMessenger.Default.Register<CalendarDescriptionRenderingRequested>(this);
|
||||
}
|
||||
|
||||
protected override void UnregisterRecipients()
|
||||
{
|
||||
base.UnregisterRecipients();
|
||||
WeakReferenceMessenger.Default.Unregister<ApplicationThemeChanged>(this);
|
||||
WeakReferenceMessenger.Default.Unregister<CalendarDescriptionRenderingRequested>(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Wino.Messaging.Client.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Message to trigger rendering of calendar event description in WebView2.
|
||||
/// </summary>
|
||||
public record CalendarDescriptionRenderingRequested();
|
||||
Reference in New Issue
Block a user